From 69aa47a468bbe983bf2fd799b6dbc25e3461be8e Mon Sep 17 00:00:00 2001 From: dengwei1 Date: Wed, 22 Jul 2020 18:32:31 +0800 Subject: [PATCH] guam: input add touch ilitek support input add touch ilitek support NO_PROP_NEEDED:only for guam/guamp/guamna Change-Id: I7cd9be63ad90f6c67d8df810799308b5a9fa7b26 Signed-off-by: dengwei1 Reviewed-on: https://gerrit.mot.com/1702165 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- .../input/touchscreen/ili9882_mmi/Android.mk | 20 + drivers/input/touchscreen/ili9882_mmi/Kbuild | 28 + .../input/touchscreen/ili9882_mmi/Makefile | 10 + .../ili9882_mmi/firmware/ili9882_fw.h | 97 + .../input/touchscreen/ili9882_mmi/ili9882.c | 1056 +++++ .../input/touchscreen/ili9882_mmi/ili9882.h | 1151 +++++ .../touchscreen/ili9882_mmi/ili9882_flash.c | 1477 +++++++ .../touchscreen/ili9882_mmi/ili9882_hostdl.c | 917 ++++ .../touchscreen/ili9882_mmi/ili9882_i2c.c | 470 ++ .../touchscreen/ili9882_mmi/ili9882_ic.c | 1017 +++++ .../touchscreen/ili9882_mmi/ili9882_mp.c | 3822 +++++++++++++++++ .../touchscreen/ili9882_mmi/ili9882_mtk.c | 459 ++ .../touchscreen/ili9882_mmi/ili9882_node.c | 2587 +++++++++++ .../touchscreen/ili9882_mmi/ili9882_qcom.c | 758 ++++ .../touchscreen/ili9882_mmi/ili9882_spi.c | 655 +++ .../touchscreen/ili9882_mmi/ili9882_touch.c | 1301 ++++++ 16 files changed, 15825 insertions(+) create mode 100644 drivers/input/touchscreen/ili9882_mmi/Android.mk create mode 100644 drivers/input/touchscreen/ili9882_mmi/Kbuild create mode 100644 drivers/input/touchscreen/ili9882_mmi/Makefile create mode 100755 drivers/input/touchscreen/ili9882_mmi/firmware/ili9882_fw.h create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882.h create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_flash.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_hostdl.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_i2c.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_ic.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_mp.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_mtk.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_node.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_qcom.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_spi.c create mode 100755 drivers/input/touchscreen/ili9882_mmi/ili9882_touch.c diff --git a/drivers/input/touchscreen/ili9882_mmi/Android.mk b/drivers/input/touchscreen/ili9882_mmi/Android.mk new file mode 100644 index 000000000000..a2a388d1ed25 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/Android.mk @@ -0,0 +1,20 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_ILI_0FLASH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),) + KERNEL_CFLAGS += CONFIG_INPUT_ILI_PANEL_NOTIFICATIONS=y +endif + +ifneq ($(DL_FW_BY_DISPLAY),) + KERNEL_CFLAGS += CONFIG_ILITEK_RESUME_BY_DDI=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := ili9882_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/ili9882_mmi/Kbuild b/drivers/input/touchscreen/ili9882_mmi/Kbuild new file mode 100644 index 000000000000..54d1be97a0f0 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/Kbuild @@ -0,0 +1,28 @@ +# 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/ili9882_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/ili9882_mmi/firmware + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_ILITEK_CHIPSET)),) + obj-m += ili9882_mmi.o +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_ILI_0FLASH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DILI_SENSOR_EN +endif +ifneq ($(filter m y,$(CONFIG_INPUT_ILI_PANEL_NOTIFICATIONS)),) +EXTRA_CFLAGS += -DILI_CONFIG_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_RESUME_BY_DDI)),) +EXTRA_CFLAGS += -DILI_CONFIG_RESUME_BY_DDI +endif + +ili9882_mmi-objs += ili9882.o +ili9882_mmi-objs += ili9882_spi.o +ili9882_mmi-objs += ili9882_qcom.o +ili9882_mmi-objs += ili9882_ic.o +ili9882_mmi-objs += ili9882_touch.o +ili9882_mmi-objs += ili9882_mp.o +ili9882_mmi-objs += ili9882_hostdl.o +ili9882_mmi-objs += ili9882_node.o diff --git a/drivers/input/touchscreen/ili9882_mmi/Makefile b/drivers/input/touchscreen/ili9882_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_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/ili9882_mmi/firmware/ili9882_fw.h b/drivers/input/touchscreen/ili9882_mmi/firmware/ili9882_fw.h new file mode 100755 index 000000000000..cc6f34a3863c --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/firmware/ili9882_fw.h @@ -0,0 +1,97 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ILI9881X_FW_H +#define __ILI9881X_FW_H + +/* define names and paths for the variety of tp modules */ +#define DEF_INI_NAME_PATH "/sdcard/mp.ini" +#define DEF_FW_FILP_PATH "/sdcard/ILITEK_FW" +#define DEF_INI_REQUEST_PATH "mp.ini" +#define DEF_FW_REQUEST_PATH "ilitek_fw.bin" +static unsigned char CTPM_FW_DEF[] = { +}; + +#define CSOT_INI_NAME_PATH "/sdcard/mp_csot.ini" +#define CSOT_FW_FILP_PATH "/sdcard/ILITEK_FW_CSOT" +#define CSOT_INI_REQUEST_PATH "mp_csot.ini" +#define CSOT_FW_REQUEST_PATH "ILITEK_FW_CSOT" +static unsigned char CTPM_FW_CSOT[] = { + 0xFF, +}; + +#define AUO_INI_NAME_PATH "/sdcard/mp_auo.ini" +#define AUO_FW_FILP_PATH "/sdcard/ILITEK_FW_AUO" +#define AUO_INI_REQUEST_PATH "mp_auo.ini" +#define AUO_FW_REQUEST_PATH "ILITEK_FW_AUO" +static unsigned char CTPM_FW_AUO[] = { + 0xFF, +}; + +#define BOE_INI_NAME_PATH "/sdcard/mp_boe.ini" +#define BOE_FW_FILP_PATH "/sdcard/ILITEK_FW_BOE" +#define BOE_INI_REQUEST_PATH "mp_boe.ini" +#define BOE_FW_REQUEST_PATH "ILITEK_FW_BOE" +static unsigned char CTPM_FW_BOE[] = { + 0xFF, +}; + +#define INX_INI_NAME_PATH "/sdcard/mp_inx.ini" +#define INX_FW_FILP_PATH "/sdcard/ILITEK_FW_INX" +#define INX_INI_REQUEST_PATH "mp_inx.ini" +#define INX_FW_REQUEST_PATH "ILITEK_FW_INX" +static unsigned char CTPM_FW_INX[] = { + 0xFF, +}; + +#define DJ_INI_NAME_PATH "/sdcard/mp_dj.ini" +#define DJ_FW_FILP_PATH "/sdcard/ILITEK_FW_DJ" +#define DJ_INI_REQUEST_PATH "mp_dj.ini" +#define DJ_FW_REQUEST_PATH "ILITEK_FW_DJ" +static unsigned char CTPM_FW_DJ[] = { + 0xFF, +}; + +#define TXD_INI_NAME_PATH "/sdcard/mp_txd.ini" +#define TXD_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD" +#define TXD_INI_REQUEST_PATH "mp_txd.ini" +#define TXD_FW_REQUEST_PATH "ILITEK_FW_TXD" +static unsigned char CTPM_FW_TXD[] = { + 0xFF, +}; + +#define TM_INI_NAME_PATH "/sdcard/mp_tm.ini" +#define TM_FW_FILP_PATH "/sdcard/ILITEK_FW_TM" +#define TM_INI_REQUEST_PATH "mp_tm.ini" +#define TM_FW_REQUEST_PATH "ILITEK_FW_TM" +static unsigned char CTPM_FW_TM[] = { + 0xFF, +}; + +#define HLT_INI_NAME_PATH "/sdcard/mp_hlt.ini" +#define HLT_FW_FILP_PATH "/sdcard/ILITEK_FW_HLT.hex" +#define HLT_INI_REQUEST_PATH "mp_hlt.ini" +#define HLT_FW_REQUEST_PATH "ILITEK_FW_HLT.hex" +static unsigned char CTPM_FW_HLT[] = { +}; + +#endif \ No newline at end of file diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882.c b/drivers/input/touchscreen/ili9882_mmi/ili9882.c new file mode 100755 index 000000000000..5d9619a2bf4d --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882.c @@ -0,0 +1,1056 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "firmware/ili9882_fw.h" +#include "ili9882.h" + +/* Debug level */ +bool debug_en = DEBUG_OUTPUT; +EXPORT_SYMBOL(debug_en); + +static struct workqueue_struct *esd_wq; +static struct workqueue_struct *bat_wq; +static struct delayed_work esd_work; +static struct delayed_work bat_work; + +#if RESUME_BY_DDI +static struct workqueue_struct *resume_by_ddi_wq; +static struct work_struct resume_by_ddi_work; + +static void ilitek_resume_by_ddi_work(struct work_struct *work) +{ + mutex_lock(&ilits->touch_mutex); + + if (ilits->gesture) + disable_irq_wake(ilits->irq_num); + + /* Set tp as demo mode and reload code if it's iram. */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) + ili_fw_upgrade_handler(NULL); + else + ili_reset_ctrl(ilits->reset); + + ili_irq_enable(); + ILI_INFO("TP resume end by wq\n"); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ilits->tp_suspend = false; + mutex_unlock(&ilits->touch_mutex); +} + +void ili_resume_by_ddi(void) +{ + if (!resume_by_ddi_wq) { + ILI_INFO("resume_by_ddi_wq is null\n"); + return; + } + + mutex_lock(&ilits->touch_mutex); + + ILI_INFO("TP resume start called by ddi\n"); + + /* + * To match the timing of sleep out, the first of mipi cmd must be sent within 10ms + * after TP reset. We then create a wq doing host download before resume. + */ + atomic_set(&ilits->fw_stat, ENABLE); + ili_reset_ctrl(ilits->reset); + ili_ice_mode_ctrl(ENABLE, OFF); + ilits->ddi_rest_done = true; + mdelay(5); + queue_work(resume_by_ddi_wq, &(resume_by_ddi_work)); + + mutex_unlock(&ilits->touch_mutex); +} +#endif + +int ili_mp_test_handler(char *apk, bool lcm_on) +{ + int ret = 0; + + if (atomic_read(&ilits->fw_stat)) { + ILI_ERR("fw upgrade processing, ignore\n"); + return -EMP_FW_PROC; + } + + atomic_set(&ilits->mp_stat, ENABLE); + + if (ilits->actual_tp_mode != P5_X_FW_TEST_MODE) { + ret = ili_switch_tp_mode(P5_X_FW_TEST_MODE); + if (ret < 0) { + ILI_ERR("Switch MP mode failed\n"); + ret = -EMP_MODE; + goto out; + } + } + + ret = ili_mp_test_main(apk, lcm_on); + +out: + /* + * If there's running mp test with lcm off, we suspose that + * users will soon call resume from suspend. TP mode will be changed + * from MP to AP mode until resume finished. + */ + if (!lcm_on) { + atomic_set(&ilits->mp_stat, DISABLE); + return ret; + } + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed during mp test\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during mp test\n"); + } + + atomic_set(&ilits->mp_stat, DISABLE); + return ret; +} + +int ili_switch_tp_mode(u8 mode) +{ + int ret = 0; + bool ges_dbg = false; + + atomic_set(&ilits->tp_sw_mode, START); + + ilits->actual_tp_mode = mode; + + /* able to see cdc data in gesture mode */ + if (ilits->tp_data_format == DATA_FORMAT_DEBUG && + ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) + ges_dbg = true; + + switch (ilits->actual_tp_mode) { + case P5_X_FW_AP_MODE: + ILI_INFO("Switch to AP mode\n"); + ilits->wait_int_timeout = AP_INT_TIMEOUT; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed\n"); + } else { + ret = ili_reset_ctrl(ilits->reset); + } + if (ret < 0) + ILI_ERR("TP Reset failed\n"); + + break; + case P5_X_FW_GESTURE_MODE: + ILI_INFO("Switch to Gesture mode\n"); + ilits->wait_int_timeout = AP_INT_TIMEOUT; + ret = ilits->gesture_move_code(ilits->gesture_mode); + if (ret < 0) + ILI_ERR("Move gesture code failed\n"); + if (ges_dbg) { + ILI_INFO("Enable gesture debug func\n"); + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL); + } + break; + case P5_X_FW_TEST_MODE: + ILI_INFO("Switch to Test mode\n"); + ilits->wait_int_timeout = MP_INT_TIMEOUT; + ret = ilits->mp_move_code(); + break; + default: + ILI_ERR("Unknown TP mode: %x\n", mode); + ret = -1; + break; + } + + if (ret < 0) + ILI_ERR("Switch TP mode (%d) failed \n", mode); + + ILI_DBG("Actual TP mode = %d\n", ilits->actual_tp_mode); + atomic_set(&ilits->tp_sw_mode, END); + return ret; +} + +int ili_gesture_recovery(void) +{ + int ret = 0; + + atomic_set(&ilits->esd_stat, START); + + ILI_INFO("Doing gesture recovery\n"); + ret = ilits->ges_recover(); + + atomic_set(&ilits->esd_stat, END); + return ret; +} + +void ili_spi_recovery(void) +{ + atomic_set(&ilits->esd_stat, START); + + ILI_INFO("Doing spi recovery\n"); + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed\n"); + + atomic_set(&ilits->esd_stat, END); +} + +int ili_wq_esd_spi_check(void) +{ + int ret = 0; + u8 tx = SPI_WRITE, rx = 0; + + ret = ilits->spi_write_then_read(ilits->spi, &tx, 1, &rx, 1); + ILI_DBG("spi esd check = 0x%x\n", ret); + if (ret == DO_SPI_RECOVER) { + ILI_ERR("ret = 0x%x\n", ret); + return -1; + } + return 0; +} + +int ili_wq_esd_i2c_check(void) +{ + ILI_DBG(""); + return 0; +} + +static void ilitek_tddi_wq_esd_check(struct work_struct *work) +{ + mutex_lock(&ilits->touch_mutex); + if (ilits->esd_recover() < 0) { + ILI_ERR("SPI ACK failed, doing spi recovery\n"); + ili_spi_recovery(); + } + mutex_unlock(&ilits->touch_mutex); + complete_all(&ilits->esd_done); + ili_wq_ctrl(WQ_ESD, ENABLE); +} + +static int read_power_status(u8 *buf) +{ + struct file *f = NULL; + mm_segment_t old_fs; + ssize_t byte = 0; + + old_fs = get_fs(); + set_fs(get_ds()); + + f = filp_open(POWER_STATUS_PATH, O_RDONLY, 0); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open %s\n", POWER_STATUS_PATH); + return -1; + } + + f->f_op->llseek(f, 0, SEEK_SET); + byte = f->f_op->read(f, buf, 20, &f->f_pos); + + ILI_DBG("Read %d bytes\n", (int)byte); + + set_fs(old_fs); + filp_close(f, NULL); + return 0; +} + +static void ilitek_tddi_wq_bat_check(struct work_struct *work) +{ + u8 str[20] = {0}; + static int charge_mode; + + if (read_power_status(str) < 0) + ILI_ERR("Read power status failed\n"); + + ILI_DBG("Batter Status: %s\n", str); + + if (strstr(str, "Charging") != NULL || strstr(str, "Full") != NULL + || strstr(str, "Fully charged") != NULL) { + if (charge_mode != 1) { + ILI_DBG("Charging mode\n"); + if (ili_ic_func_ctrl("plug", DISABLE) < 0) // plug in + ILI_ERR("Write plug in failed\n"); + charge_mode = 1; + } + } else { + if (charge_mode != 2) { + ILI_DBG("Not charging mode\n"); + if (ili_ic_func_ctrl("plug", ENABLE) < 0) // plug out + ILI_ERR("Write plug out failed\n"); + charge_mode = 2; + } + } + ili_wq_ctrl(WQ_BAT, ENABLE); +} + +void ili_wq_ctrl(int type, int ctrl) +{ + switch (type) { + case WQ_ESD: + if (ENABLE_WQ_ESD || ilits->wq_ctrl) { + if (!esd_wq) { + ILI_ERR("wq esd is null\n"); + break; + } + ilits->wq_esd_ctrl = ctrl; + if (ctrl == ENABLE) { + ILI_DBG("execute esd check\n"); + if (!queue_delayed_work(esd_wq, &esd_work, msecs_to_jiffies(WQ_ESD_DELAY))) + ILI_DBG("esd check was already on queue\n"); + } else { + cancel_delayed_work_sync(&esd_work); + flush_workqueue(esd_wq); + ILI_DBG("cancel esd wq\n"); + } + } + break; + case WQ_BAT: + if (ENABLE_WQ_BAT || ilits->wq_ctrl) { + if (!bat_wq) { + ILI_ERR("WQ BAT is null\n"); + break; + } + ilits->wq_bat_ctrl = ctrl; + if (ctrl == ENABLE) { + ILI_DBG("execute bat check\n"); + if (!queue_delayed_work(bat_wq, &bat_work, msecs_to_jiffies(WQ_BAT_DELAY))) + ILI_DBG("bat check was already on queue\n"); + } else { + cancel_delayed_work_sync(&bat_work); + flush_workqueue(bat_wq); + ILI_DBG("cancel bat wq\n"); + } + } + break; + default: + ILI_ERR("Unknown WQ type, %d\n", type); + break; + } +} + +static void ilitek_tddi_wq_init(void) +{ + esd_wq = alloc_workqueue("esd_check", WQ_MEM_RECLAIM, 0); + bat_wq = alloc_workqueue("bat_check", WQ_MEM_RECLAIM, 0); + + WARN_ON(!esd_wq); + WARN_ON(!bat_wq); + + INIT_DELAYED_WORK(&esd_work, ilitek_tddi_wq_esd_check); + INIT_DELAYED_WORK(&bat_work, ilitek_tddi_wq_bat_check); + +#if RESUME_BY_DDI + resume_by_ddi_wq = create_singlethread_workqueue("resume_by_ddi_wq"); + WARN_ON(!resume_by_ddi_wq); + INIT_WORK(&resume_by_ddi_work, ilitek_resume_by_ddi_work); +#endif +} + +int ili_sleep_handler(int mode) +{ + int ret = 0; + bool sense_stop = true; + + mutex_lock(&ilits->touch_mutex); + atomic_set(&ilits->tp_sleep, START); + + if (atomic_read(&ilits->fw_stat) || + atomic_read(&ilits->mp_stat)) { + ILI_INFO("fw upgrade or mp still running, ignore sleep requst\n"); + atomic_set(&ilits->tp_sleep, END); + mutex_unlock(&ilits->touch_mutex); + return 0; + } + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + ili_irq_disable(); + + ILI_INFO("Sleep Mode = %d\n", mode); + + if (ilits->ss_ctrl) + sense_stop = true; + else if ((ilits->chip->core_ver >= CORE_VER_1430)) + sense_stop = false; + else + sense_stop = true; + + switch (mode) { + case TP_SUSPEND: + ILI_INFO("TP suspend start\n"); + if (sense_stop) { + if (ili_ic_func_ctrl("sense", DISABLE) < 0) + ILI_ERR("Write sense stop cmd failed\n"); + + if (ili_ic_check_busy(50, 20) < 0) + ILI_ERR("Check busy timeout during suspend\n"); + } + + if (ilits->gesture) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + } else { + if (ili_ic_func_ctrl("sleep", SLEEP_IN) < 0) + ILI_ERR("Write sleep in cmd failed\n"); + } + ILI_INFO("TP suspend end\n"); + ilits->tp_suspend = true; + break; + case TP_DEEP_SLEEP: + ILI_INFO("TP deep suspend start\n"); + if (sense_stop) { + if (ili_ic_func_ctrl("sense", DISABLE) < 0) + ILI_ERR("Write sense stop cmd failed\n"); + + if (ili_ic_check_busy(50, 20) < 0) + ILI_ERR("Check busy timeout during deep suspend\n"); + } + + if (ilits->gesture) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + } else { + if (ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN) < 0) + ILI_ERR("Write deep sleep in cmd failed\n"); + } + ILI_INFO("TP deep suspend end\n"); + ilits->tp_suspend = true; + break; + case TP_RESUME: +#if !RESUME_BY_DDI + ILI_INFO("TP resume start\n"); + + if (ilits->gesture) + disable_irq_wake(ilits->irq_num); + + /* Set tp as demo mode and reload code if it's iram. */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed during resume\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during resume\n"); + ili_ic_func_ctrl_reset(); + } + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ilits->tp_suspend = false; + ILI_INFO("TP resume end\n"); +#endif + ili_irq_enable(); + break; + default: + ILI_ERR("Unknown sleep mode, %d\n", mode); + ret = -EINVAL; + break; + } + + ili_touch_release_all_point(); + atomic_set(&ilits->tp_sleep, END); + mutex_unlock(&ilits->touch_mutex); + return ret; +} + +int ili_fw_upgrade_handler(void *data) +{ + int ret = 0; + + atomic_set(&ilits->fw_stat, START); + + ilits->fw_update_stat = FW_STAT_INIT; + ret = ili_fw_upgrade(ilits->fw_open); + if (ret != 0) { + ILI_INFO("FW upgrade fail\n"); + ilits->fw_update_stat = FW_UPDATE_FAIL; + } else { + ILI_INFO("FW upgrade pass\n"); +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE + /* add_for_charger_start */ + if((ilits->usb_plug_status)&&(ilits->actual_tp_mode !=P5_X_FW_TEST_MODE)) { + ret = ili_ic_func_ctrl("plug", !ilits->usb_plug_status);// plug in + if(ret<0) { + ILI_ERR("Write plug in failed\n"); + } + } + /* add_for_charger_end */ +#endif +#endif + ilits->fw_update_stat = FW_UPDATE_PASS; + } + + if (!ilits->boot) { + ilits->boot = true; + ILI_INFO("Registre touch to input subsystem\n"); + ili_input_register(); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + } + + atomic_set(&ilits->fw_stat, END); + return ret; +} + +int ili_set_tp_data_len(int format, bool send, u8* data) +{ + u8 cmd[10] = {0}, ctrl = 0, debug_ctrl = 0; + u16 self_key = 2; + int ret = 0, tp_mode = ilits->actual_tp_mode, len = 0; + + switch (format) { + case DATA_FORMAT_DEMO: + case DATA_FORMAT_GESTURE_DEMO: + len = P5_X_DEMO_MODE_PACKET_LEN; + ctrl = DATA_FORMAT_DEMO_CMD; + break; + case DATA_FORMAT_DEBUG: + case DATA_FORMAT_GESTURE_DEBUG: + len = (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2); + len += 2 * self_key + (8 * 2) + 1 + 35; + ctrl = DATA_FORMAT_DEBUG_CMD; + break; + case DATA_FORMAT_DEMO_DEBUG_INFO: + /*only suport SPI interface now, so defult use size 1024 buffer*/ + len = P5_X_DEMO_MODE_PACKET_LEN + + P5_X_DEMO_DEBUG_INFO_ID0_LENGTH + P5_X_INFO_HEADER_LENGTH; + ctrl = DATA_FORMAT_DEMO_DEBUG_INFO_CMD; + break; + case DATA_FORMAT_GESTURE_INFO: + len = P5_X_GESTURE_INFO_LENGTH; + ctrl = DATA_FORMAT_GESTURE_INFO_CMD; + break; + case DATA_FORMAT_GESTURE_NORMAL: + len = P5_X_GESTURE_NORMAL_LENGTH; + ctrl = DATA_FORMAT_GESTURE_NORMAL_CMD; + break; + case DATA_FORMAT_GESTURE_SPECIAL_DEMO: + if (ilits->gesture_demo_ctrl == ENABLE) { + if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) + len = P5_X_GESTURE_INFO_LENGTH + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; + else + len = P5_X_DEMO_MODE_PACKET_LEN + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; + } else { + if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) + len = P5_X_GESTURE_INFO_LENGTH; + else + len = P5_X_GESTURE_NORMAL_LENGTH; + } + ILI_INFO("Gesture demo mode control = %d\n", ilits->gesture_demo_ctrl); + ili_ic_func_ctrl("gesture_demo_en", ilits->gesture_demo_ctrl); + ILI_INFO("knock_en setting\n"); + ili_ic_func_ctrl("knock_en", 0x8); + break; + case DATA_FORMAT_DEBUG_LITE_ROI: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_ROI_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_WINDOW: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_WINDOW_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_AREA: + if(cmd == NULL) { + ILI_ERR("DATA_FORMAT_DEBUG_LITE_AREA error cmd\n"); + return -1; + } + debug_ctrl = DATA_FORMAT_DEBUG_LITE_AREA_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + cmd[3] = data[0]; + cmd[4] = data[1]; + cmd[5] = data[2]; + cmd[6] = data[3]; + break; + default: + ILI_ERR("Unknow TP data format\n"); + return -1; + } + + if (ctrl == DATA_FORMAT_DEBUG_LITE_CMD) { + len = P5_X_DEBUG_LITE_LENGTH; + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = ctrl; + cmd[2] = debug_ctrl; + ret = ilits->wrapper(cmd, 10, NULL, 0, ON, OFF); + + if (ret < 0) { + ILI_ERR("switch to format %d failed\n", format); + ili_switch_tp_mode(P5_X_FW_AP_MODE); + } + + } else if (tp_mode == P5_X_FW_AP_MODE || + format == DATA_FORMAT_GESTURE_DEMO || + format == DATA_FORMAT_GESTURE_DEBUG) { + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = ctrl; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + + if (ret < 0) { + ILI_ERR("switch to format %d failed\n", format); + ili_switch_tp_mode(P5_X_FW_AP_MODE); + } + } else if (tp_mode == P5_X_FW_GESTURE_MODE) { + + /*set gesture symbol*/ + ili_set_gesture_symbol(); + + if (send) { + ret = ili_ic_func_ctrl("lpwg", ctrl); + if (ret < 0) + ILI_ERR("write gesture mode failed\n"); + } + } + + ilits->tp_data_format = format; + ilits->tp_data_len = len; + ILI_INFO("TP mode = %d, format = %d, len = %d\n", + tp_mode, ilits->tp_data_format, ilits->tp_data_len); + + return ret; +} + +int ili_report_handler(void) +{ + int ret = 0, pid = 0; + u8 checksum = 0, pack_checksum = 0; + u8 *trdata = NULL; + int rlen = 0; + int tmp = debug_en; + + /* Just in case these stats couldn't be blocked in top half context */ + if (!ilits->report || atomic_read(&ilits->tp_reset) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep)) { + ILI_INFO("ignore report request\n"); + return -EINVAL; + } + + if (ilits->irq_after_recovery) { + ILI_INFO("ignore int triggered by recovery\n"); + ilits->irq_after_recovery = false; + return -EINVAL; + } + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) { + __pm_stay_awake(ilits->ws); + + if (ilits->pm_suspend) { + /* Waiting for pm resume completed */ + ret = wait_for_completion_timeout(&ilits->pm_completion, msecs_to_jiffies(700)); + if (!ret) { + ILI_ERR("system(spi) can't finished resuming procedure."); + } + } + } + + rlen = ilits->tp_data_len; + ILI_DBG("Packget length = %d\n", rlen); + + if (!rlen || rlen > TR_BUF_SIZE) { + ILI_ERR("Length of packet is invaild\n"); + goto out; + } + + memset(ilits->tr_buf, 0x0, TR_BUF_SIZE); + + ret = ilits->wrapper(NULL, 0, ilits->tr_buf, rlen, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read report packet failed, ret = %d\n", ret); + if (ret == DO_SPI_RECOVER) { + ili_ic_get_pc_counter(DO_SPI_RECOVER); + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE && ilits->gesture && !ilits->prox_near) { + ILI_ERR("Gesture failed, doing gesture recovery\n"); + if (ili_gesture_recovery() < 0) + ILI_ERR("Failed to recover gesture\n"); + ilits->irq_after_recovery = true; + } else { + ILI_ERR("SPI ACK failed, doing spi recovery\n"); + ili_spi_recovery(); + ilits->irq_after_recovery = true; + } + } + goto out; + } + + ili_dump_data(ilits->tr_buf, 8, rlen, 0, "finger report"); + + checksum = ili_calc_packet_checksum(ilits->tr_buf, rlen - 1); + pack_checksum = ilits->tr_buf[rlen-1]; + trdata = ilits->tr_buf; + pid = trdata[0]; + ILI_DBG("Packet ID = %x\n", pid); + + if (checksum != pack_checksum && pid != P5_X_I2CUART_PACKET_ID) { + ILI_ERR("Checksum Error (0x%X)! Pack = 0x%X, len = %d\n", checksum, pack_checksum, rlen); + debug_en = DEBUG_ALL; + ili_dump_data(trdata, 8, rlen, 0, "finger report with wrong"); + debug_en = tmp; + ret = -EINVAL; + goto out; + } + + if (pid == P5_X_INFO_HEADER_PACKET_ID) { + trdata = ilits->tr_buf + P5_X_INFO_HEADER_LENGTH; + pid = trdata[0]; + } + + switch (pid) { + case P5_X_DEMO_PACKET_ID: + ili_report_ap_mode(trdata, rlen); + break; + case P5_X_DEBUG_PACKET_ID: + ili_report_debug_mode(trdata, rlen); + break; + case P5_X_DEBUG_LITE_PACKET_ID: + ili_report_debug_lite_mode(trdata, rlen); + break; + case P5_X_I2CUART_PACKET_ID: + ili_report_i2cuart_mode(trdata, rlen); + break; + case P5_X_GESTURE_PACKET_ID: + ili_report_gesture_mode(trdata, rlen); + break; + case P5_X_GESTURE_FAIL_ID: + ILI_INFO("gesture fail reason code = 0x%02x", trdata[1]); + break; + case P5_X_DEMO_DEBUG_INFO_PACKET_ID: + ili_demo_debug_info_mode(trdata, rlen); + break; + default: + ILI_ERR("Unknown packet id, %x\n", pid); +#if ( TDDI_INTERFACE == BUS_I2C ) + ili_ic_get_pc_counter(DO_I2C_RECOVER); + if (ilits->fw_latch !=0){ + msleep(100); + ili_ic_func_ctrl_reset(); + ILI_ERR("I2C func_ctrl_reset\n"); + } + if ((ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) && ilits->fw_latch !=0){ + msleep(100); + ili_set_gesture_symbol(); + ILI_ERR("I2C gesture_symbol\n"); + } +#endif + break; + } + +out: + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + } + + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) + __pm_relax(ilits->ws); + return ret; +} + +int ili_reset_ctrl(int mode) +{ + int ret = 0; + + atomic_set(&ilits->tp_reset, START); + + switch (mode) { + case TP_IC_CODE_RST: + ILI_INFO("TP IC Code RST \n"); + ret = ili_ic_code_reset(); + if (ret < 0) + ILI_ERR("IC Code reset failed\n"); + break; + case TP_IC_WHOLE_RST: + ILI_INFO("TP IC whole RST\n"); + ret = ili_ic_whole_reset(); + if (ret < 0) + ILI_ERR("IC whole reset failed\n"); + break; + case TP_HW_RST_ONLY: + ILI_INFO("TP HW RST\n"); + ili_tp_reset(); + break; + default: + ILI_ERR("Unknown reset mode, %d\n", mode); + ret = -EINVAL; + break; + } + + /* + * Since OTP must be folloing with reset, except for code rest, + * the stat of ice mode should be set as 0. + */ + if (mode != TP_IC_CODE_RST) + atomic_set(&ilits->ice_stat, DISABLE); + + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN; + ilits->pll_clk_wakeup = true; + atomic_set(&ilits->tp_reset, END); + return ret; +} + +static int ilitek_get_tp_module(void) +{ + /* + * TODO: users should implement this function + * if there are various tp modules been used in projects. + */ + + return 0; + //return MODEL_HLT; +} + +static void ili_update_tp_module_info(void) +{ + int module; + + module = ilitek_get_tp_module(); + + switch (module) { + case MODEL_CSOT: + ilits->md_name = "CSOT"; + ilits->md_fw_filp_path = CSOT_FW_FILP_PATH; + ilits->md_fw_rq_path = CSOT_FW_REQUEST_PATH; + ilits->md_ini_path = CSOT_INI_NAME_PATH; + ilits->md_ini_rq_path = CSOT_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_CSOT; + ilits->md_fw_ili_size = sizeof(CTPM_FW_CSOT); + break; + case MODEL_AUO: + ilits->md_name = "AUO"; + ilits->md_fw_filp_path = AUO_FW_FILP_PATH; + ilits->md_fw_rq_path = AUO_FW_REQUEST_PATH; + ilits->md_ini_path = AUO_INI_NAME_PATH; + ilits->md_ini_rq_path = AUO_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_AUO; + ilits->md_fw_ili_size = sizeof(CTPM_FW_AUO); + break; + case MODEL_BOE: + ilits->md_name = "BOE"; + ilits->md_fw_filp_path = BOE_FW_FILP_PATH; + ilits->md_fw_rq_path = BOE_FW_REQUEST_PATH; + ilits->md_ini_path = BOE_INI_NAME_PATH; + ilits->md_ini_rq_path = BOE_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_BOE; + ilits->md_fw_ili_size = sizeof(CTPM_FW_BOE); + break; + case MODEL_INX: + ilits->md_name = "INX"; + ilits->md_fw_filp_path = INX_FW_FILP_PATH; + ilits->md_fw_rq_path = INX_FW_REQUEST_PATH; + ilits->md_ini_path = INX_INI_NAME_PATH; + ilits->md_ini_rq_path = INX_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_INX; + ilits->md_fw_ili_size = sizeof(CTPM_FW_INX); + break; + case MODEL_DJ: + ilits->md_name = "DJ"; + ilits->md_fw_filp_path = DJ_FW_FILP_PATH; + ilits->md_fw_rq_path = DJ_FW_REQUEST_PATH; + ilits->md_ini_path = DJ_INI_NAME_PATH; + ilits->md_ini_rq_path = DJ_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_DJ; + ilits->md_fw_ili_size = sizeof(CTPM_FW_DJ); + break; + case MODEL_TXD: + ilits->md_name = "TXD"; + ilits->md_fw_filp_path = TXD_FW_FILP_PATH; + ilits->md_fw_rq_path = TXD_FW_REQUEST_PATH; + ilits->md_ini_path = TXD_INI_NAME_PATH; + ilits->md_ini_rq_path = TXD_FW_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TXD; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TXD); + break; + case MODEL_TM: + ilits->md_name = "TM"; + ilits->md_fw_filp_path = TM_FW_REQUEST_PATH; + ilits->md_fw_rq_path = TM_FW_REQUEST_PATH; + ilits->md_ini_path = TM_INI_NAME_PATH; + ilits->md_ini_rq_path = TM_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TM; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TM); + break; + case MODEL_HLT: + ilits->md_name = "HLT"; + ilits->md_fw_filp_path = HLT_FW_REQUEST_PATH; + ilits->md_fw_rq_path = HLT_FW_REQUEST_PATH; + ilits->md_ini_path = HLT_INI_NAME_PATH; + ilits->md_ini_rq_path = HLT_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_HLT; + ilits->md_fw_ili_size = sizeof(CTPM_FW_HLT); + break; + default: + break; + } + + if (module == 0 || ilits->md_fw_ili_size < ILI_FILE_HEADER) { + ILI_ERR("Couldn't find any tp modules, applying default settings\n"); + ilits->md_name = "DEF"; + ilits->md_fw_filp_path = DEF_FW_FILP_PATH; + ilits->md_fw_rq_path = DEF_FW_REQUEST_PATH; + ilits->md_ini_path = DEF_INI_NAME_PATH; + ilits->md_ini_rq_path = DEF_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_DEF; + ilits->md_fw_ili_size = sizeof(CTPM_FW_DEF); + } + + ILI_INFO("Found %s module: ini path = %s, fw path = (%s, %s, %d)\n", + ilits->md_name, + ilits->md_ini_path, + ilits->md_fw_filp_path, + ilits->md_fw_rq_path, + ilits->md_fw_ili_size); + + ilits->tp_module = module; +} + +int ili_tddi_init(void) +{ +#if (BOOT_FW_UPDATE | HOST_DOWN_LOAD) + struct task_struct *fw_boot_th; +#endif + + ILI_INFO("driver version = %s\n", DRIVER_VERSION); + + mutex_init(&ilits->touch_mutex); + mutex_init(&ilits->debug_mutex); + mutex_init(&ilits->debug_read_mutex); + init_waitqueue_head(&(ilits->inq)); + spin_lock_init(&ilits->irq_spin); + init_completion(&ilits->esd_done); + + atomic_set(&ilits->irq_stat, DISABLE); + atomic_set(&ilits->ice_stat, DISABLE); + atomic_set(&ilits->tp_reset, END); + atomic_set(&ilits->fw_stat, END); + atomic_set(&ilits->mp_stat, DISABLE); + atomic_set(&ilits->tp_sleep, END); + atomic_set(&ilits->cmd_int_check, DISABLE); + atomic_set(&ilits->esd_stat, END); + + ili_ic_init(); + ilitek_tddi_wq_init(); + + /* Must do hw reset once in first time for work normally if tp reset is avaliable */ +#if !TDDI_RST_BIND + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during init\n"); +#endif + + ilits->demo_debug_info[0] = ili_demo_debug_info_id0; + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->boot = false; + + /* + * This status of ice enable will be reset until process of fw upgrade runs. + * it might cause unknown problems if we disable ice mode without any + * codes inside touch ic. + */ + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Failed to enable ice mode during ili_tddi_init\n"); + + if ( ili_ic_dummy_check() < 0 ){ + ILI_ERR("Not found ilitek chip\n"); + return -ENODEV; + } + + if (ili_ic_get_info() < 0) + ILI_ERR("Chip info is incorrect\n"); + + ili_update_tp_module_info(); + + ili_node_init(); + + ili_fw_read_flash_info(); + +#if (BOOT_FW_UPDATE | HOST_DOWN_LOAD) + fw_boot_th = kthread_run(ili_fw_upgrade_handler, NULL, "ili_fw_boot"); + if (fw_boot_th == (struct task_struct *)ERR_PTR) { + fw_boot_th = NULL; + WARN_ON(!fw_boot_th); + ILI_ERR("Failed to create fw upgrade thread\n"); + } +#else + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Failed to disable ice mode failed during init\n"); + +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = DISABLE; +#endif + + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = ENABLE; +#endif + + ILI_INFO("Registre touch to input subsystem\n"); + ili_input_register(); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ilits->boot = true; +#endif + + ilits->ws = wakeup_source_register("ili_wakelock"); + if (!ilits->ws) + ILI_ERR("wakeup source request failed\n"); + + return 0; +} + +void ili_dev_remove(void) +{ + ILI_INFO("remove ilitek dev\n"); + + if (!ilits) + return; + + gpio_free(ilits->tp_int); + gpio_free(ilits->tp_rst); + + if (esd_wq != NULL) { + cancel_delayed_work_sync(&esd_work); + flush_workqueue(esd_wq); + destroy_workqueue(esd_wq); + } + if (bat_wq != NULL) { + cancel_delayed_work_sync(&bat_work); + flush_workqueue(bat_wq); + destroy_workqueue(bat_wq); + } + + if (ilits->ws) + wakeup_source_unregister(ilits->ws); + + kfree(ilits->tr_buf); + kfree(ilits->gcoord); + ili_interface_dev_exit(ilits); +} + +int ili_dev_init(struct ilitek_hwif_info *hwif) +{ + ILI_INFO("TP Interface: %s\n", (hwif->bus_type == BUS_I2C) ? "I2C" : "SPI"); + return ili_interface_dev_init(hwif); +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882.h b/drivers/input/touchscreen/ili9882_mmi/ili9882.h new file mode 100755 index 000000000000..90ba594b7737 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882.h @@ -0,0 +1,1151 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __ILI9882_H +#define __ILI9882_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_COMPAT +#include +#endif +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#include +#include +#include +#include +#endif + +#ifdef CONFIG_FB +#include +#include +#else +#include +#endif + +#ifdef CONFIG_DRM_MSM +#include +#endif + +#ifdef CONFIG_DRM +#include +#endif + +#ifdef CONFIG_MTK_SPI +#include "mt_spi.h" +#include "sync_write.h" +#endif + +#define DRIVER_VERSION "3.0.3.0.200610" + +/* Options */ +#define TDDI_INTERFACE BUS_SPI /* BUS_I2C(0x18) or BUS_SPI(0x1C) */ +#define VDD_VOLTAGE 1800000 +#define VCC_VOLTAGE 1800000 +#define SPI_CLK 9 /* follow by clk list */ +#define SPI_RETRY 5 +#define IRQ_GPIO_NUM 66 +#define TR_BUF_SIZE (2*K) /* Buffer size of touch report */ +#define TR_BUF_LIST_SIZE (1*K) /* Buffer size of touch report for debug data */ +#define SPI_TX_BUF_SIZE 4096 +#define SPI_RX_BUF_SIZE 4096 +#define WQ_ESD_DELAY 4000 +#define WQ_BAT_DELAY 2000 +#define AP_INT_TIMEOUT 600 /*600ms*/ +#define MP_INT_TIMEOUT 5000 /*5s*/ +#define MT_B_TYPE ENABLE +#define TDDI_RST_BIND DISABLE +#define MT_PRESSURE DISABLE +#define ENABLE_WQ_ESD DISABLE +#define ENABLE_WQ_BAT DISABLE +#define ENABLE_GESTURE DISABLE +#define REGULATOR_POWER DISABLE +#define TP_SUSPEND_PRIO ENABLE +#define RESUME_BY_DDI DISABLE +#define BOOT_FW_UPDATE ENABLE +#define MP_INT_LEVEL DISABLE +#define PLL_CLK_WAKEUP_TP_RESUME DISABLE +#define CHARGER_NOTIFIER_CALLBACK DISABLE + +/*if current interface is spi, must to hostdownload */ +#if (TDDI_INTERFACE == BUS_SPI) +#define HOST_DOWN_LOAD ENABLE +#else +#define HOST_DOWN_LOAD DISABLE +#endif + +/* Plaform compatibility */ +#define CONFIG_PLAT_SPRD DISABLE +#define I2C_DMA_TRANSFER DISABLE +#define SPI_DMA_TRANSFER_SPLIT DISABLE +#define SPRD_SYSFS_SUSPEND_RESUME DISABLE + +/* Path */ +#define DEBUG_DATA_FILE_SIZE (10*K) +#define DEBUG_DATA_FILE_PATH "/sdcard/ILITEK_log.csv" +#define CSV_LCM_ON_PATH "/sdcard/ilitek_mp_lcm_on_log" +#define CSV_LCM_OFF_PATH "/sdcard/ilitek_mp_lcm_off_log" +#define POWER_STATUS_PATH "/sys/class/power_supply/battery/status" +#define DUMP_FLASH_PATH "/sdcard/flash_dump" +#define DUMP_IRAM_PATH "/sdcard/iram_dump" + +/* Debug messages */ +#define DEBUG_NONE 0 +#define DEBUG_ALL 1 +#define DEBUG_OUTPUT DEBUG_NONE + +#define ILI_INFO(fmt, arg...) \ +({ \ + pr_info("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +}) \ + +#define ILI_ERR(fmt, arg...) \ +({ \ + pr_err("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +}) \ + +extern bool debug_en; +#define ILI_DBG(fmt, arg...) \ +do { \ + if (debug_en) \ + pr_info("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +} while (0) + +#define ERR_ALLOC_MEM(X) ((IS_ERR(X) || X == NULL) ? 1 : 0) +#define K (1024) +#define M (K * K) +#define ENABLE 1 +#define START 1 +#define ON 1 +#define ILI_WRITE 1 +#define ILI_READ 0 +#define DISABLE 0 +#define END 0 +#define OFF 0 +#define NONE -1 +#define DO_SPI_RECOVER -2 +#define DO_I2C_RECOVER -3 + +enum TP_SPI_CLK_LIST { + TP_SPI_CLK_1M = 1000000, + TP_SPI_CLK_2M = 2000000, + TP_SPI_CLK_3M = 3000000, + TP_SPI_CLK_4M = 4000000, + TP_SPI_CLK_5M = 5000000, + TP_SPI_CLK_6M = 6000000, + TP_SPI_CLK_7M = 7000000, + TP_SPI_CLK_8M = 8000000, + TP_SPI_CLK_9M = 9000000, + TP_SPI_CLK_10M = 10000000, + TP_SPI_CLK_11M = 11000000, + TP_SPI_CLK_12M = 12000000, + TP_SPI_CLK_13M = 13000000, + TP_SPI_CLK_14M = 14000000, + TP_SPI_CLK_15M = 15000000, +}; + +enum TP_PLAT_TYPE { + TP_PLAT_MTK = 0, + TP_PLAT_QCOM +}; + +enum TP_RST_METHOD { + TP_IC_WHOLE_RST = 0, + TP_IC_CODE_RST, + TP_HW_RST_ONLY, +}; + +enum TP_FW_UPGRADE_TYPE { + UPGRADE_FLASH = 0, + UPGRADE_IRAM +}; + +enum TP_FW_UPGRADE_STATUS { + FW_STAT_INIT = 0, + FW_UPDATING = 90, + FW_UPDATE_PASS = 100, + FW_UPDATE_FAIL = -1 +}; + +enum TP_FW_OPEN_METHOD { + REQUEST_FIRMWARE = 0, + FILP_OPEN +}; + +enum TP_SLEEP_STATUS { + TP_SUSPEND = 0, + TP_DEEP_SLEEP = 1, + TP_RESUME = 2 +}; + +enum TP_PROXIMITY_STATUS { + DDI_POWER_OFF = 0, + DDI_POWER_ON = 1, + WAKE_UP_GESTURE_RECOVERY = 2, + WAKE_UP_SWITCH_GESTURE_MODE = 3 +}; + +enum TP_SLEEP_CTRL { + SLEEP_IN = 0x0, + SLEEP_OUT = 0x1, + DEEP_SLEEP_IN = 0x3 +}; + +enum TP_FW_BLOCK_NUM { + AP = 1, + DATA = 2, + TUNING = 3, + GESTURE = 4, + MP = 5, + DDI = 6, + TAG = 7, + PARA_BACKUP = 8, + RESERVE_BLOCK3 = 9, + RESERVE_BLOCK4 = 10, + RESERVE_BLOCK5 = 11, + RESERVE_BLOCK6 = 12, + RESERVE_BLOCK7 = 13, + RESERVE_BLOCK8 = 14, + RESERVE_BLOCK9 = 15, + RESERVE_BLOCK10 = 16, +}; + +enum TP_FW_BLOCK_TAG { + BLOCK_TAG_AF = 0xAF, + BLOCK_TAG_B0 = 0xB0 +}; + +enum TP_WQ_TYPE { + WQ_ESD = 0, + WQ_BAT, +}; + +enum TP_RECORD_DATA { + DISABLE_RECORD = 0, + ENABLE_RECORD, + DATA_RECORD +}; + +enum TP_DATA_FORMAT { + DATA_FORMAT_DEMO = 0, + DATA_FORMAT_DEBUG, + DATA_FORMAT_DEMO_DEBUG_INFO, + DATA_FORMAT_GESTURE_SPECIAL_DEMO, + DATA_FORMAT_GESTURE_INFO, + DATA_FORMAT_GESTURE_NORMAL, + DATA_FORMAT_GESTURE_DEMO, + DATA_FORMAT_GESTURE_DEBUG, + DATA_FORMAT_DEBUG_LITE_ROI, + DATA_FORMAT_DEBUG_LITE_WINDOW, + DATA_FORMAT_DEBUG_LITE_AREA +}; + +enum NODE_MODE_SWITCH { + AP_MODE = 0, + TEST_MODE, + DEBUG_MODE, + DEBUG_LITE_ROI, + DEBUG_LITE_WINDOW, + DEBUG_LITE_AREA +}; + +enum TP_MODEL { + MODEL_DEF = 0, + MODEL_CSOT, + MODEL_AUO, + MODEL_BOE, + MODEL_INX, + MODEL_DJ, + MODEL_TXD, + MODEL_TM, + MODEL_HLT +}; + +enum TP_ERR_CODE { + EMP_CMD = 100, + EMP_PROTOCOL, + EMP_FILE, + EMP_INI, + EMP_TIMING_INFO, + EMP_INVAL, + EMP_PARSE, + EMP_NOMEM, + EMP_GET_CDC, + EMP_INT, + EMP_CHECK_BUY, + EMP_MODE, + EMP_FW_PROC, + EMP_FORMUL_NULL, + EMP_PARA_NULL, + EFW_CONVERT_FILE, + EFW_ICE_MODE, + EFW_CRC, + EFW_REST, + EFW_ERASE, + EFW_PROGRAM, + EFW_INTERFACE, +}; + +enum TP_IC_TYPE { + ILI_A = 0x0A, + ILI_B, + ILI_C, + ILI_D, + ILI_E, + ILI_F, + ILI_G, + ILI_H, + ILI_I, + ILI_J, + ILI_K, + ILI_L, + ILI_M, + ILI_N, + ILI_O, + ILI_P, + ILI_Q, + ILI_R, + ILI_S, + ILI_T, + ILI_U, + ILI_V, + ILI_W, + ILI_X, + ILI_Y, + ILI_Z, +}; + +struct gesture_symbol { + u8 double_tap :1; + u8 alphabet_line_2_top :1; + u8 alphabet_line_2_bottom :1; + u8 alphabet_line_2_left :1; + u8 alphabet_line_2_right :1; + u8 alphabet_m :1; + u8 alphabet_w :1; + u8 alphabet_c :1; + u8 alphabet_E :1; + u8 alphabet_V :1; + u8 alphabet_O :1; + u8 alphabet_S :1; + u8 alphabet_Z :1; + u8 alphabet_V_down :1; + u8 alphabet_V_left :1; + u8 alphabet_V_right :1; + u8 alphabet_two_line_2_bottom :1; + u8 alphabet_F :1; + u8 alphabet_AT :1; + u8 reserve0 :5; +}; + +struct report_info_block { + u8 nReportByPixel :1; + u8 nIsHostDownload :1; + u8 nIsSPIICE :1; + u8 nIsSPISLAVE :1; + u8 nIsI2C :1; + u8 nReserved00 :3; + u8 nReserved01 :8; + u8 nReserved02 :8; + u8 nReserved03 :8; +}; + +#define TDDI_I2C_ADDR 0x41 +#define TDDI_DEV_ID "ILITEK_TDDI" + + /* define the width and heigth of a screen. */ +#define TOUCH_SCREEN_X_MIN 0 +#define TOUCH_SCREEN_Y_MIN 0 +#define TOUCH_SCREEN_X_MAX 720 +#define TOUCH_SCREEN_Y_MAX 1600 +#define MAX_TOUCH_NUM 5 + +/* define the range on panel */ +#define TPD_HEIGHT 2048 +#define TPD_WIDTH 2048 + +/* Firmware upgrade */ +#define CORE_VER_1410 0x01040100 +#define CORE_VER_1420 0x01040200 +#define CORE_VER_1430 0x01040300 +#define CORE_VER_1460 0x01040600 +#define CORE_VER_1470 0x01040700 +#define MAX_HEX_FILE_SIZE (160*K) +#define ILI_FILE_HEADER 256 +#define DLM_START_ADDRESS 0x20610 +#define DLM_HEX_ADDRESS 0x10000 +#define MP_HEX_ADDRESS 0x13000 +#define DDI_RSV_BK_ST_ADDR 0x1E000 +#define DDI_RSV_BK_END_ADDR 0x1FFFF +#define DDI_RSV_BK_SIZE (1*K) +#define RSV_BK_ST_ADDR 0x1E000 +#define RSV_BK_END_ADDR 0x1E3FF +#define FW_VER_ADDR 0xFFE0 +#define FW_BLOCK_INFO_NUM 17 +#define SPI_UPGRADE_LEN 2048 +#define SPI_BUF_SIZE MAX_HEX_FILE_SIZE +#define INFO_HEX_ST_ADDR 0x4F +#define INFO_MP_HEX_ADDR 0x1F + +/* DMA Control Registers */ +#define DMA_BASED_ADDR 0x72000 +#define DMA48_ADDR (DMA_BASED_ADDR + 0xC0) +#define DMA48_reg_dma_ch0_busy_flag DMA48_ADDR +#define DMA48_reserved_0 0xFFFE +#define DMA48_reg_dma_ch0_trigger_sel (BIT(16)|BIT(17)|BIT(18)|BIT(19)) +#define DMA48_reserved_1 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define DMA48_reg_dma_ch0_start_set BIT(24) +#define DMA48_reg_dma_ch0_start_clear BIT(25) +#define DMA48_reg_dma_ch0_trigger_src_mask BIT(26) +#define DMA48_reserved_2 BIT(27) + +#define DMA49_ADDR (DMA_BASED_ADDR + 0xC4) +#define DMA49_reg_dma_ch0_src1_addr DMA49_ADDR +#define DMA49_reserved_0 BIT(20) + +#define DMA50_ADDR (DMA_BASED_ADDR + 0xC8) +#define DMA50_reg_dma_ch0_src1_step_inc DMA50_ADDR +#define DMA50_reserved_0 (DMA50_ADDR + 0x01) +#define DMA50_reg_dma_ch0_src1_format (BIT(24)|BIT(25)) +#define DMA50_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA50_reg_dma_ch0_src1_en BIT(31) + +#define DMA52_ADDR (DMA_BASED_ADDR + 0xD0) +#define DMA52_reg_dma_ch0_src2_step_inc DMA52_ADDR +#define DMA52_reserved_0 (DMA52_ADDR + 0x01) +#define DMA52_reg_dma_ch0_src2_format (BIT(24)|BIT(25)) +#define DMA52_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA52_reg_dma_ch0_src2_en BIT(31) + +#define DMA53_ADDR (DMA_BASED_ADDR + 0xD4) +#define DMA53_reg_dma_ch0_dest_addr DMA53_ADDR +#define DMA53_reserved_0 BIT(20) + +#define DMA54_ADDR (DMA_BASED_ADDR + 0xD8) +#define DMA54_reg_dma_ch0_dest_step_inc DMA54_ADDR +#define DMA54_reserved_0 (DMA54_ADDR + 0x01) +#define DMA54_reg_dma_ch0_dest_format (BIT(24)|BIT(25)) +#define DMA54_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA54_reg_dma_ch0_dest_en BIT(31) + +#define DMA55_ADDR (DMA_BASED_ADDR + 0xDC) +#define DMA55_reg_dma_ch0_trafer_counts DMA55_ADDR +#define DMA55_reserved_0 (BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define DMA55_reg_dma_ch0_trafer_mode (BIT(24)|BIT(25)|BIT(26)|BIT(27)) +#define DMA55_reserved_1 (BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* INT Function Registers */ +#define INTR_BASED_ADDR 0x48000 +#define INTR1_ADDR (INTR_BASED_ADDR + 0x4) +#define INTR1_reg_uart_tx_int_flag INTR1_ADDR +#define INTR1_reserved_0 (BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR1_reg_wdt_alarm_int_flag BIT(8) +#define INTR1_reserved_1 (BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define INTR1_reg_dma_ch1_int_flag BIT(16) +#define INTR1_reg_dma_ch0_int_flag BIT(17) +#define INTR1_reg_dma_frame_done_int_flag BIT(18) +#define INTR1_reg_dma_tdi_done_int_flag BIT(19) +#define INTR1_reserved_2 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define INTR1_reg_flash_error_flag BIT(24) +#define INTR1_reg_flash_int_flag BIT(25) +#define INTR1_reserved_3 BIT(26) + +#define INTR2_ADDR (INTR_BASED_ADDR + 0x8) +#define INTR2_td_int_flag_clear INTR2_ADDR +#define INTR2_td_timeout_int_flag_clear BIT(1) +#define INTR2_td_debug_frame_done_int_flag_clear BIT(2) +#define INTR2_td_frame_start_scan_int_flag_clear BIT(3) +#define INTR2_log_int_flag_clear BIT(4) +#define INTR2_d2t_crc_err_int_flag_clear BIT(8) +#define INTR2_d2t_flash_req_int_flag_clear BIT(9) +#define INTR2_d2t_ddi_int_flag_clear BIT(10) +#define INTR2_wr_done_int_flag_clear BIT(16) +#define INTR2_rd_done_int_flag_clear BIT(17) +#define INTR2_tdi_err_int_flag_clear BIT(18) +#define INTR2_d2t_slpout_rise_flag_clear BIT(24) +#define INTR2_d2t_slpout_fall_flag_clear BIT(25) +#define INTR2_d2t_dstby_flag_clear BIT(26) +#define INTR2_ddi_pwr_rdy_flag_clear BIT(27) + +#define INTR32_ADDR (INTR_BASED_ADDR + 0x80) +#define INTR32_reg_ice_sw_int_en INTR32_ADDR +#define INTR32_reg_ice_apb_conflict_int_en BIT(1) +#define INTR32_reg_ice_ilm_conflict_int_en BIT(2) +#define INTR32_reg_ice_dlm_conflict_int_en BIT(3) +#define INTR32_reserved_0 (BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR32_reg_spi_sr_int_en BIT(8) +#define INTR32_reg_spi_sp_int_en BIT(9) +#define INTR32_reg_spi_trx_int_en BIT(10) +#define INTR32_reg_spi_cmd_int_en BIT(11) +#define INTR32_reg_spi_rw_int_en BIT(12) +#define INTR32_reserved_1 (BIT(13)|BIT(14)|BIT(15)) +#define INTR32_reg_i2c_start_int_en BIT(16) +#define INTR32_reg_i2c_addr_match_int_en BIT(17) +#define INTR32_reg_i2c_cmd_int_en BIT(18) +#define INTR32_reg_i2c_sr_int_en BIT(19) +#define INTR32_reg_i2c_trx_int_en BIT(20) +#define INTR32_reg_i2c_rx_stop_int_en BIT(21) +#define INTR32_reg_i2c_tx_stop_int_en BIT(22) +#define INTR32_reserved_2 BIT(23) +#define INTR32_reg_t0_int_en BIT(24) +#define INTR32_reg_t1_int_en BIT(25) +#define INTR32_reserved_3 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +#define INTR33_ADDR (INTR_BASED_ADDR + 0x84) +#define INTR33_reg_uart_tx_int_en INTR33_ADDR +#define INTR33_reserved_0 (BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR33_reg_wdt_alarm_int_en BIT(8) +#define INTR33_reserved_1 (BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define INTR33_reg_dma_ch1_int_en BIT(16) +#define INTR33_reg_dma_ch0_int_en BIT(17) +#define INTR33_reg_dma_frame_done_int_en BIT(18) +#define INTR33_reg_dma_tdi_done_int_en BIT(19) +#define INTR33_reserved_2 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define INTR33_reg_flash_error_en BIT(24) +#define INTR33_reg_flash_int_en BIT(25) +#define INTR33_reserved_3 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* Flash */ +#define FLASH_BASED_ADDR 0x41000 +#define FLASH0_ADDR (FLASH_BASED_ADDR + 0x0) +#define FLASH0_reg_flash_csb FLASH0_ADDR +#define FLASH0_reserved_0 (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define FLASH0_reg_preclk_sel (BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define FLASH0_reg_rx_dual BIT(24) +#define FLASH0_reg_tx_dual BIT(25) +#define FLASH0_reserved_26 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) +#define FLASH1_ADDR (FLASH_BASED_ADDR + 0x4) +#define FLASH1_reg_flash_key1 FLASH1_ADDR +#define FLASH1_reg_flash_key2 (FLASH1_ADDR + 0x01) +#define FLASH1_reg_flash_key3 (FLASH1_ADDR + 0x02) +#define FLASH1_reserved_0 (FLASH1_ADDR + 0x03) +#define FLASH2_ADDR (FLASH_BASED_ADDR + 0x8) +#define FLASH2_reg_tx_data FLASH2_ADDR +#define FLASH3_ADDR (FLASH_BASED_ADDR + 0xC) +#define FLASH3_reg_rcv_cnt FLASH3_ADDR +#define FLASH4_ADDR (FLASH_BASED_ADDR + 0x10) +#define FLASH4_reg_rcv_data FLASH4_ADDR +#define FLASH4_reg_rcv_dly BIT(8) +#define FLASH4_reg_sutrg_en BIT(9) +#define FLASH4_reserved_1 (BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define FLASH4_reg_rcv_data_valid_state BIT(16) +#define FLASH4_reg_flash_rd_finish_state BIT(17) +#define FLASH4_reserved_2 (BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define FLASH4_reg_flash_dma_trigger_en (BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* Dummy Registers */ +#define WDT_DUMMY_BASED_ADDR 0x5101C +#define WDT7_DUMMY0 WDT_DUMMY_BASED_ADDR +#define WDT8_DUMMY1 (WDT_DUMMY_BASED_ADDR + 0x04) +#define WDT9_DUMMY2 (WDT_DUMMY_BASED_ADDR + 0x08) + +/* The example for the gesture virtual keys */ +#define GESTURE_DOUBLECLICK 0x58 +#define GESTURE_UP 0x60 +#define GESTURE_DOWN 0x61 +#define GESTURE_LEFT 0x62 +#define GESTURE_RIGHT 0x63 +#define GESTURE_M 0x64 +#define GESTURE_W 0x65 +#define GESTURE_C 0x66 +#define GESTURE_E 0x67 +#define GESTURE_V 0x68 +#define GESTURE_O 0x69 +#define GESTURE_S 0x6A +#define GESTURE_Z 0x6B +#define KEY_GESTURE_POWER KEY_POWER +#define KEY_GESTURE_UP KEY_UP +#define KEY_GESTURE_DOWN KEY_DOWN +#define KEY_GESTURE_LEFT KEY_LEFT +#define KEY_GESTURE_RIGHT KEY_RIGHT +#define KEY_GESTURE_O KEY_O +#define KEY_GESTURE_E KEY_E +#define KEY_GESTURE_M KEY_M +#define KEY_GESTURE_W KEY_W +#define KEY_GESTURE_S KEY_S +#define KEY_GESTURE_V KEY_V +#define KEY_GESTURE_C KEY_C +#define KEY_GESTURE_Z KEY_Z +#define KEY_GESTURE_F KEY_F +#define GESTURE_V_DOWN 0x6C +#define GESTURE_V_LEFT 0x6D +#define GESTURE_V_RIGHT 0x6E +#define GESTURE_TWOLINE_DOWN 0x6F +#define GESTURE_F 0x70 +#define GESTURE_AT 0x71 +#define ESD_GESTURE_PWD 0xF38A94EF +#define SPI_ESD_GESTURE_RUN 0xA67C9DFE +#define I2C_ESD_GESTURE_RUN 0xA67C9DFE +#define SPI_ESD_GESTURE_PWD_ADDR 0x25FF8 +#define I2C_ESD_GESTURE_PWD_ADDR 0x40054 + +#define ESD_GESTURE_CORE146_PWD 0xF38A +#define SPI_ESD_GESTURE_CORE146_RUN 0xA67C +#define I2C_ESD_GESTURE_CORE146_RUN 0xA67C +#define SPI_ESD_GESTURE_CORE146_PWD_ADDR 0x4005C +#define I2C_ESD_GESTURE_CORE146_PWD_ADDR 0x4005C + +#define DOUBLE_TAP ( ON )//BIT0 +#define ALPHABET_LINE_2_TOP ( ON )//BIT1 +#define ALPHABET_LINE_2_BOTTOM ( ON )//BIT2 +#define ALPHABET_LINE_2_LEFT ( ON )//BIT3 +#define ALPHABET_LINE_2_RIGHT ( ON )//BIT4 +#define ALPHABET_M ( ON )//BIT5 +#define ALPHABET_W ( ON )//BIT6 +#define ALPHABET_C ( ON )//BIT7 +#define ALPHABET_E ( ON )//BIT8 +#define ALPHABET_V ( ON )//BIT9 +#define ALPHABET_O ( ON )//BIT10 +#define ALPHABET_S ( ON )//BIT11 +#define ALPHABET_Z ( ON )//BIT12 +#define ALPHABET_V_DOWN ( ON )//BIT13 +#define ALPHABET_V_LEFT ( ON )//BIT14 +#define ALPHABET_V_RIGHT ( ON )//BIT15 +#define ALPHABET_TWO_LINE_2_BOTTOM ( ON )//BIT16 +#define ALPHABET_F ( ON )//BIT17 +#define ALPHABET_AT ( ON )//BIT18 + +/* FW data format */ +#define DATA_FORMAT_DEMO_CMD 0x00 +#define DATA_FORMAT_DEBUG_CMD 0x02 +#define DATA_FORMAT_DEMO_DEBUG_INFO_CMD 0x04 +#define DATA_FORMAT_GESTURE_NORMAL_CMD 0x01 +#define DATA_FORMAT_GESTURE_INFO_CMD 0x02 +#define DATA_FORMAT_DEBUG_LITE_CMD 0x05 +#define DATA_FORMAT_DEBUG_LITE_ROI_CMD 0x01 +#define DATA_FORMAT_DEBUG_LITE_WINDOW_CMD 0x02 +#define DATA_FORMAT_DEBUG_LITE_AREA_CMD 0x03 +#define P5_X_DEMO_MODE_PACKET_LEN 43 +#define P5_X_INFO_HEADER_LENGTH 3 +#define P5_X_INFO_CHECKSUM_LENGTH 1 +#define P5_X_DEMO_DEBUG_INFO_ID0_LENGTH 14 +#define P5_X_DEBUG_MODE_PACKET_LENGTH 1280 +#define P5_X_TEST_MODE_PACKET_LENGTH 1180 +#define P5_X_GESTURE_NORMAL_LENGTH 8 +#define P5_X_GESTURE_INFO_LENGTH 170 +#define P5_X_DEBUG_LITE_LENGTH 300 +#define P5_X_CORE_VER_THREE_LENGTH 5 +#define P5_X_CORE_VER_FOUR_LENGTH 6 + + +/* Protocol */ +#define PROTOCOL_VER_500 0x050000 +#define PROTOCOL_VER_510 0x050100 +#define PROTOCOL_VER_520 0x050200 +#define PROTOCOL_VER_530 0x050300 +#define PROTOCOL_VER_540 0x050400 +#define PROTOCOL_VER_550 0x050500 +#define PROTOCOL_VER_560 0x050600 +#define PROTOCOL_VER_570 0x050700 +#define P5_X_READ_DATA_CTRL 0xF6 +#define P5_X_GET_TP_INFORMATION 0x20 +#define P5_X_GET_KEY_INFORMATION 0x27 +#define P5_X_GET_PANEL_INFORMATION 0x29 +#define P5_X_GET_FW_VERSION 0x21 +#define P5_X_GET_PROTOCOL_VERSION 0x22 +#define P5_X_GET_CORE_VERSION 0x23 +#define P5_X_GET_CORE_VERSION_NEW 0x24 +#define P5_X_MODE_CONTROL 0xF0 +#define P5_X_SET_CDC_INIT 0xF1 +#define P5_X_GET_CDC_DATA 0xF2 +#define P5_X_CDC_BUSY_STATE 0xF3 +#define P5_X_MP_TEST_MODE_INFO 0xFE +#define P5_X_I2C_UART 0x40 +#define CMD_GET_FLASH_DATA 0x41 +#define CMD_CTRL_INT_ACTION 0x1B +#define P5_X_FW_UNKNOWN_MODE 0xFF +#define P5_X_FW_AP_MODE 0x00 +#define P5_X_FW_TEST_MODE 0x01 +#define P5_X_FW_GESTURE_MODE 0x0F +#define P5_X_FW_DELTA_DATA_MODE 0x03 +#define P5_X_FW_RAW_DATA_MODE 0x08 +#define P5_X_DEMO_PACKET_ID 0x5A +#define P5_X_DEBUG_PACKET_ID 0xA7 +#define P5_X_TEST_PACKET_ID 0xF2 +#define P5_X_GESTURE_PACKET_ID 0xAA +#define P5_X_GESTURE_FAIL_ID 0xAE +#define P5_X_I2CUART_PACKET_ID 0x7A +#define P5_X_DEBUG_LITE_PACKET_ID 0x9A +#define P5_X_SLAVE_MODE_CMD_ID 0x5F +#define P5_X_INFO_HEADER_PACKET_ID 0xB7 +#define P5_X_DEMO_DEBUG_INFO_PACKET_ID 0x5C +#define P5_X_EDGE_PLAM_CTRL_1 0x01 +#define P5_X_EDGE_PLAM_CTRL_2 0x12 +#define SPI_WRITE 0x82 +#define SPI_READ 0x83 +#define SPI_ACK 0xA3 + +/* Chips */ +#define ILI9881_CHIP 0x9881 +#define ILI7807_CHIP 0x7807 +#define ILI9881N_AA 0x98811700 +#define ILI9881O_AA 0x98811800 +#define ILI9882_CHIP 0x9882 +#define TDDI_PID_ADDR 0x4009C +#define TDDI_OTP_ID_ADDR 0x400A0 +#define TDDI_ANA_ID_ADDR 0x400A4 +#define TDDI_PC_COUNTER_ADDR 0x44008 +#define TDDI_PC_LATCH_ADDR 0x51010 +#define TDDI_CHIP_RESET_ADDR 0x40050 +#define RAWDATA_NO_BK_SHIFT 8192 + +struct ilitek_ts_data { + struct i2c_client *i2c; + struct spi_device *spi; + struct input_dev *input; + struct device *dev; + struct wakeup_source *ws; + + struct ilitek_hwif_info *hwif; + struct ilitek_ic_info *chip; + struct ilitek_protocol_info *protocol; + struct gesture_coordinate *gcoord; + struct regulator *vdd; + struct regulator *vcc; + +#ifdef CONFIG_FB + struct notifier_block notifier_fb; +#else + struct early_suspend early_suspend; +#endif +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE +/* add_for_charger_start */ + struct notifier_block notifier_charger; + struct workqueue_struct *charger_notify_wq; + struct work_struct update_charger; + int usb_plug_status; +/* add_for_charger_end */ +#endif +#endif + struct mutex touch_mutex; + struct mutex debug_mutex; + struct mutex debug_read_mutex; + spinlock_t irq_spin; + + struct completion pm_completion; + bool pm_suspend; + + /* physical path to the input device in the system hierarchy */ + const char *phys; + + bool boot; + u32 fw_pc; + u32 fw_latch; + + u16 max_x; + u16 max_y; + u16 min_x; + u16 min_y; + u16 panel_wid; + u16 panel_hei; + u8 xch_num; + u8 ych_num; + u8 stx; + u8 srx; + u8 *update_buf; + u8 *tr_buf; + u8 *spi_tx; + u8 *spi_rx; + struct firmware tp_fw; + + int actual_tp_mode; + int tp_data_format; + int tp_data_len; + + int irq_num; + int irq_gpio; + int irq_tirgger_type; + int tp_rst; + int tp_int; + int wait_int_timeout; + + int finger; + int curt_touch[MAX_TOUCH_NUM]; + int prev_touch[MAX_TOUCH_NUM]; + int last_touch; + + int fw_retry; + int fw_update_stat; + int fw_open; + u8 fw_info[75]; + u8 fw_mp_ver[4]; + bool wq_ctrl; + bool wq_esd_ctrl; + bool wq_bat_ctrl; + + bool netlink; + bool report; + bool gesture; + bool mp_retry; + int gesture_mode; + int gesture_demo_ctrl; + struct gesture_symbol ges_sym; + struct report_info_block rib; + + u16 flash_mid; + u16 flash_devid; + int program_page; + int flash_sector; + + /* Sending report data to users for the debug */ + bool dnp; //debug node open + int dbf; //debug data frame + int odi; //out data index + wait_queue_head_t inq; + struct debug_buf_list *dbl; + int raw_count; + int delta_count; + int bg_count; + + int reset; + int rst_edge_delay; + int fw_upgrade_mode; + int mp_ret_len; + bool wtd_ctrl; + bool force_fw_update; + bool irq_after_recovery; + bool ddi_rest_done; + bool resume_by_ddi; + bool tp_suspend; + bool info_from_hex; + bool prox_near; + bool gesture_load_code; + bool trans_xy; + bool ss_ctrl; + bool node_update; + bool int_pulse; + bool pll_clk_wakeup; + + /* module info */ + int tp_module; + int md_fw_ili_size; + char *md_name; + char *md_fw_filp_path; + char *md_fw_rq_path; + char *md_ini_path; + char *md_ini_rq_path; + u8 *md_fw_ili; + + atomic_t irq_stat; + atomic_t tp_reset; + atomic_t ice_stat; + atomic_t fw_stat; + atomic_t mp_stat; + atomic_t tp_sleep; + atomic_t tp_sw_mode; + atomic_t cmd_int_check; + atomic_t esd_stat; + + /* Event for driver test */ + struct completion esd_done; + + int (*spi_write_then_read)(struct spi_device *spi, const void *txbuf, + unsigned n_tx, void *rxbuf, unsigned n_rx); + int (*wrapper)(u8* wdata, u32 wlen, u8* rdata, u32 rlen, bool spi_irq, bool i2c_irq); + int (*mp_move_code)(void); + int (*gesture_move_code)(int mode); + int (*esd_recover)(void); + int (*ges_recover)(void); + void (*demo_debug_info[5])(u8 *, size_t); + int (*detect_int_stat)(bool status); +}; +extern struct ilitek_ts_data *ilits; + +struct debug_buf_list { + bool mark; + unsigned char *data; +}; + +struct ilitek_touch_info { + u16 id; + u16 x; + u16 y; + u16 pressure; +}; + +struct gesture_coordinate { + u16 code; + u8 clockwise; + int type; + struct ilitek_touch_info pos_start; + struct ilitek_touch_info pos_end; + struct ilitek_touch_info pos_1st; + struct ilitek_touch_info pos_2nd; + struct ilitek_touch_info pos_3rd; + struct ilitek_touch_info pos_4th; +}; + +struct ilitek_protocol_info { + u32 ver; + int fw_ver_len; + int pro_ver_len; + int tp_info_len; + int key_info_len; + int panel_info_len; + int core_ver_len; + int func_ctrl_len; + int window_len; + int cdc_len; + int mp_info_len; +}; + +struct ilitek_ic_func_ctrl { + const char *name; + u8 cmd[6]; + int len; + u8 def_cmd; + u8 rec_state; //0:disable, 1: enable, 2: ignore record + u8 rec_cmd; +}; + +struct ilitek_ic_info { + u8 type; + u8 ver; + u16 id; + u32 pid; + u32 pid_addr; + u32 pc_counter_addr; + u32 pc_latch_addr; + u32 reset_addr; + u32 otp_addr; + u32 ana_addr; + u32 otp_id; + u32 ana_id; + u32 fw_ver; + u32 core_ver; + u32 fw_mp_ver; + u32 max_count; + u32 reset_key; + u16 wtd_key; + int no_bk_shift; + bool dma_reset; + int (*dma_crc)(u32 start_addr, u32 block_size); +}; + +struct ilitek_hwif_info { + u8 bus_type; + u8 plat_type; + const char *name; + struct module *owner; + const struct of_device_id *of_match_table; + const struct dev_pm_ops *pm; + int (*plat_probe)(void); + int (*plat_remove)(void); + void *info; +}; + +/* Prototypes for tddi firmware/flash functions */ +extern void ili_flash_dma_write(u32 start, u32 end, u32 len); +extern void ili_flash_clear_dma(void); +extern void ili_fw_read_flash_info(void); +extern u32 ili_fw_read_hw_crc(u32 start, u32 end, u32 *flash_crc); +extern int ili_fw_read_flash(u32 start, u32 end, u8 *data, int len); +extern int ili_fw_dump_iram_data(u32 start, u32 end, bool save); +extern int ili_fw_dump_flash_data(u32 start, u32 end, bool user); +extern int ili_fw_upgrade(int op); + +/* Prototypes for tddi mp test */ +extern int ili_mp_test_main(char *apk, bool lcm_on); + +/* Prototypes for tddi core functions */ +extern int ili_touch_esd_gesture_flash(void); +extern int ili_touch_esd_gesture_iram(void); +extern void ili_set_gesture_symbol(void); +extern int ili_move_gesture_code_flash(int mode); +extern int ili_move_gesture_code_iram(int mode); +extern int ili_move_mp_code_flash(void); +extern int ili_move_mp_code_iram(void); +extern void ili_touch_press(u16 x, u16 y, u16 pressure, u16 id); +extern void ili_touch_release(u16 x, u16 y, u16 id); +extern void ili_touch_release_all_point(void); +extern void ili_report_ap_mode(u8 *buf, int len); +extern void ili_report_debug_mode(u8 *buf, int rlen); +extern void ili_report_debug_lite_mode(u8 *buf, int rlen); +extern void ili_report_gesture_mode(u8 *buf, int rlen); +extern void ili_report_i2cuart_mode(u8 *buf, int rlen); +extern void ili_ic_set_ddi_reg_onepage(u8 page, u8 reg, u8 data); +extern void ili_ic_get_ddi_reg_onepage(u8 page, u8 reg, u8 *data); +extern int ili_ic_whole_reset(void); +extern int ili_ic_code_reset(void); +extern int ili_ic_func_ctrl(const char *name, int ctrl); +extern void ili_ic_func_ctrl_reset(void); +extern int ili_ic_int_trigger_ctrl(bool pulse); +extern void ili_ic_get_pc_counter(int stat); +extern int ili_ic_check_int_level(bool level); +extern int ili_ic_check_int_pulse(bool pulse); +extern int ili_ic_check_busy(int count, int delay); +extern int ili_ic_get_project_id(u8 *pdata, int size); +extern int ili_ic_get_panel_info(void); +extern int ili_ic_get_tp_info(void); +extern int ili_ic_get_core_ver(void); +extern int ili_ic_get_protocl_ver(void); +extern int ili_ic_get_fw_ver(void); +extern int ili_ic_get_info(void); +extern int ili_ic_dummy_check(void); +extern int ili_ice_mode_bit_mask_write(u32 addr, u32 mask, u32 value); +extern int ili_ice_mode_write(u32 addr, u32 data, int len); +extern int ili_ice_mode_read(u32 addr, u32 *data, int len); +extern int ili_ice_mode_ctrl(bool enable, bool mcu); +extern void ili_ic_init(void); +extern void ili_fw_uart_ctrl(u8 ctrl); + +/* Prototypes for tddi events */ +#if RESUME_BY_DDI +extern void ili_resume_by_ddi(void); +#endif +extern int ili_proximity_far(int mode); +extern int ili_proximity_near(int mode); +extern int ili_switch_tp_mode(u8 data); +extern int ili_set_tp_data_len(int format, bool send, u8* data); +extern int ili_fw_upgrade_handler(void *data); +extern int ili_wq_esd_i2c_check(void); +extern int ili_wq_esd_spi_check(void); +extern int ili_gesture_recovery(void); +extern void ili_spi_recovery(void); +extern void ili_wq_ctrl(int type, int ctrl); +extern int ili_mp_test_handler(char *apk, bool lcm_on); +extern int ili_report_handler(void); +extern int ili_sleep_handler(int mode); +extern int ili_reset_ctrl(int mode); +extern int ili_tddi_init(void); +extern int ili_dev_init(struct ilitek_hwif_info *hwif); +extern void ili_dev_remove(void); + +/* Prototypes for i2c/spi interface */ +extern int ili_core_spi_setup(int num); +extern int ili_interface_dev_init(struct ilitek_hwif_info *hwif); +extern void ili_interface_dev_exit(struct ilitek_ts_data *ts); +extern int ili_spi_write_then_read_split(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx); +extern int ili_spi_write_then_read_direct(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx); + +/* Prototypes for platform level */ +extern void ili_plat_regulator_power_on(bool status); +extern void ili_input_register(void); +extern void ili_irq_disable(void); +extern void ili_irq_enable(void); +extern void ili_tp_reset(void); +extern void ili_irq_unregister(void); +extern int ili_irq_register(int type); + +/* Prototypes for miscs */ +extern void ili_node_init(void); +extern void ili_dump_data(void *data, int type, int len, int row_len, const char *name); +extern u8 ili_calc_packet_checksum(u8 *packet, int len); +extern void ili_netlink_reply_msg(void *raw, int size); +extern int ili_katoi(char *str); +extern int ili_str2hex(char *str); + +extern int ili_mp_read_write(u8* wdata, u32 wlen, u8* rdata, u32 rlen); +/* Prototypes for additional functionalities */ +extern void ili_gesture_fail_reason(bool enable); +extern int ili_get_tp_recore_ctrl(int data); +extern int ili_get_tp_recore_data(void); +extern void ili_demo_debug_info_mode(u8 *buf, size_t rlen); +extern void ili_demo_debug_info_id0(u8 *buf, size_t len); + +static inline void ipio_kfree(void **mem) +{ + if (*mem != NULL) { + kfree(*mem); + *mem = NULL; + } +} + +static inline void ipio_vfree(void **mem) +{ + if (*mem != NULL) { + vfree(*mem); + *mem = NULL; + } +} + +static inline void *ipio_memcpy(void *dest, const void *src, int n, int dest_size) +{ + if (n > dest_size) + n = dest_size; + + return memcpy(dest, src, n); +} + +static inline int ipio_strcmp(const char *s1, const char *s2) +{ + return (strlen(s1) != strlen(s2)) ? -1 : strncmp(s1, s2, strlen(s1)); +} + +#endif /* __ILI9882_H */ diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_flash.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_flash.c new file mode 100755 index 000000000000..c0da3e181fda --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_flash.c @@ -0,0 +1,1477 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define UPDATE_PASS 0 +#define UPDATE_FAIL -1 +#define TIMEOUT_SECTOR 500 +#define TIMEOUT_PAGE 3500 +#define TIMEOUT_PROGRAM 10 + +static struct touch_fw_data { + u8 block_number; + u32 start_addr; + u32 end_addr; + u32 new_fw_cb; + int delay_after_upgrade; + bool isCRC; + bool isboot; + bool is80k; + int hex_tag; +} tfd; + +static struct flash_block_info { + char *name; + u32 start; + u32 end; + u32 len; + u32 mem_start; + u32 fix_mem_start; + u8 mode; +} fbi[FW_BLOCK_INFO_NUM]; + +static u8 fw_flash_tmp[K + 7]; +static u8 *pfw; +static u8 *CTPM_FW; + +static u32 HexToDec(char *phex, s32 len) +{ + u32 ret = 0, temp = 0, i; + s32 shift = (len - 1) * 4; + + for (i = 0; i < len; shift -= 4, i++) { + if ((phex[i] >= '0') && (phex[i] <= '9')) + temp = phex[i] - '0'; + else if ((phex[i] >= 'a') && (phex[i] <= 'f')) + temp = (phex[i] - 'a') + 10; + else if ((phex[i] >= 'A') && (phex[i] <= 'F')) + temp = (phex[i] - 'A') + 10; + + ret |= (temp << shift); + } + return ret; +} + +static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw) +{ + int i = 0, j = 0; + int crc_poly = 0x04C11DB7; + int tmp_crc = 0xFFFFFFFF; + + for (i = start_addr; i < start_addr + len; i++) { + tmp_crc ^= (pfw[i] << 24); + + for (j = 0; j < 8; j++) { + if ((tmp_crc & 0x80000000) != 0) + tmp_crc = tmp_crc << 1 ^ crc_poly; + else + tmp_crc = tmp_crc << 1; + } + } + return tmp_crc; +} + +static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len) +{ + int limit = 4*K; + int addr = 0, loop = 0, tmp_len = len, cnt = 0; + u8 cmd[4] = {0}; + + if (!buf) { + ILI_ERR("buf is null\n"); + return -ENOMEM; + } + + if (len % limit) + loop = (len / limit) + 1; + else + loop = len / limit; + + for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) { + if (tmp_len > limit) + tmp_len = limit; + + cmd[0] = 0x25; + cmd[3] = (char)((addr & 0x00FF0000) >> 16); + cmd[2] = (char)((addr & 0x0000FF00) >> 8); + cmd[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + tmp_len = len - cnt * limit; + ilits->fw_update_stat = ((len - tmp_len) * 100) / len; + ILI_DBG("Reading iram data .... %d%c", ilits->fw_update_stat, '%'); + } + return 0; +} + +int ili_fw_dump_iram_data(u32 start, u32 end, bool save) +{ + struct file *f = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + int i, ret, len; + u8 *fw_buf = NULL; + + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) { + ILI_ERR("Enable ice mode failed\n"); + ret = -1; + goto out; + } + + len = end - start + 1; + + if (len > MAX_HEX_FILE_SIZE) { + ILI_ERR("len is larger than buffer, abort\n"); + ret = -EINVAL; + goto out; + } + + fw_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL); + if (ERR_ALLOC_MEM(fw_buf)) { + ILI_ERR("Failed to allocate update_buf\n"); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + fw_buf[i] = 0xFF; + + ret = ilitek_tddi_fw_iram_read(fw_buf, start, len); + if (ret < 0) + goto out; + + f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, fw_buf, len, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH); + +out: + ili_ice_mode_ctrl(DISABLE, OFF); + ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + ipio_kfree((void **)&fw_buf); + return ret; +} + +static int ilitek_tddi_flash_poll_busy(int timer) +{ + int ret = UPDATE_PASS, retry = timer; + u8 cmd = 0x5; + u32 temp = 0; + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) + ILI_ERR("Write 0x5 cmd failed\n"); + + do { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + mdelay(1); + + if (ili_ice_mode_read(FLASH4_ADDR, &temp, sizeof(u8)) < 0) + ILI_ERR("Read flash busy error\n"); + + if ((temp & 0x3) == 0) + break; + } while (--retry >= 0); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Pull cs high failed\n"); + + if (retry <= 0) { + ILI_ERR("Flash polling busy timeout ! tmp = %x\n", temp); + ret = UPDATE_FAIL; + } + + return ret; +} + +void ili_flash_clear_dma(void) +{ + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_preclk_sel, (2 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_preclk_sel, FLASH0_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH4_ADDR, FLASH4_reg_flash_dma_trigger_en, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH4_reg_flash_dma_trigger_en, FLASH4_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH3_reg_rcv_cnt, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH3_reg_rcv_cnt); + + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Write 0xFF at %x failed\n", FLASH4_reg_rcv_data); +} + +static int ilitek_tddi_flash_read_int_flag(void) +{ + int retry = 100; + u32 data = 0; + + do { + if (ili_ice_mode_read(INTR1_ADDR & BIT(25), &data, sizeof(u32)) < 0) + ILI_ERR("Read flash int flag error\n"); + + ILI_DBG("int flag = %x\n", data); + if (data) + break; + mdelay(2); + } while (--retry >= 0); + + if (retry <= 0) { + ILI_ERR("Read Flash INT flag timeout !, flag = 0x%x\n", data); + return -1; + } + return 0; +} + +void ili_flash_dma_write(u32 start, u32 end, u32 len) +{ + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_preclk_sel, 1 << 16) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_preclk_sel, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x00, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_reg_flash_key1, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, 0x0b, 1) < 0) + ILI_ERR("Write 0x0b at %x failed\n", FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write 0xb timeout \n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0xFF0000), FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr1 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0x00FF00) >> 8, FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr2 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0x0000FF), FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr3 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, 0 << 24) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, 0x00, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write dummy timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH3_reg_rcv_cnt, len, 4) < 0) + ILI_ERR("Write length failed\n"); +} + +static void ilitek_tddi_flash_write_enable(void) +{ + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull CS low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x6, 1) < 0) + ILI_ERR("Write 0x6 failed\n"); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Pull CS high failed\n"); +} + +u32 ili_fw_read_hw_crc(u32 start, u32 end, u32 *flash_crc) +{ + int retry = 100; + u32 busy = 0; + u32 write_len = end; + + if (write_len > ilits->chip->max_count) { + ILI_ERR("The length (%x) written into firmware is greater than max count (%x)\n", + write_len, ilits->chip->max_count); + return -1; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull CS low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x3b, 1) < 0) + ILI_ERR("Write 0x3b failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(0x041003, 0x01, 1) < 0) + ILI_ERR("Write enable Dio_Rx_dual failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_write(0x04100C, write_len, 3) < 0) + ILI_ERR("Write Set Receive count failed\n"); + + if (ili_ice_mode_write(0x048007, 0x02, 1) < 0) + ILI_ERR("Write clearing int flag failed\n"); + + if (ili_ice_mode_write(0x041016, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at 0x041016 failed\n"); + + if (ili_ice_mode_write(0x041016, 0x01, 1) < 0) + ILI_ERR("Write Checksum_En failed\n"); + + if (ili_ice_mode_write(FLASH4_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write start to receive failed\n"); + + do { + if (ili_ice_mode_read(0x048007, &busy, sizeof(u8)) < 0) + ILI_ERR("Read busy error\n"); + + ILI_DBG("busy = %x\n", busy); + if (((busy >> 1) & 0x01) == 0x01) + break; + mdelay(2); + } while (--retry >= 0); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write CS high failed\n"); + + if (retry <= 0) { + ILI_ERR("Read HW CRC timeout !, busy = 0x%x\n", busy); + return -1; + } + + if (ili_ice_mode_write(0x041003, 0x0, 1) < 0) + ILI_ERR("Write disable dio_Rx_dual failed\n"); + + if (ili_ice_mode_read(0x04101C, flash_crc, sizeof(u32)) < 0) { + ILI_ERR("Read hw crc error\n"); + return -1; + } + + return 0; +} + +static int ilitek_tddi_fw_read_flash_data(u32 start, u32 end, u8 *data, int len) +{ + u32 i, j, index = 0; + u32 tmp; + + if (end - start > len) { + ILI_ERR("the length (%d) reading crc is over than len(%d)\n", end - start, len); + return -1; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x03, 1) < 0) + ILI_ERR("Write 0x3 failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + for (i = start, j = 0; i <= end; i++, j++) { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_read(FLASH4_ADDR, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read flash data error!\n"); + + data[index] = tmp; + index++; + ilits->fw_update_stat = (j * 100) / len; + ILI_DBG("Reading flash data .... %d%c", ilits->fw_update_stat, '%'); + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + return 0; +} + +int ili_fw_dump_flash_data(u32 start, u32 end, bool user) +{ + struct file *f = NULL; + u8 *buf = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + u32 start_addr, end_addr; + int ret, length; + + f = filp_open(DUMP_FLASH_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + goto out; + + if (user) { + start_addr = 0x0; + end_addr = 0x1FFFF; + } else { + start_addr = start; + end_addr = end; + } + + length = end_addr - start_addr + 1; + ILI_INFO("len = %d\n", length); + + buf = vmalloc(length * sizeof(u8)); + if (ERR_ALLOC_MEM(buf)) { + ILI_ERR("Failed to allocate buf memory, %ld\n", PTR_ERR(buf)); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ret = ilitek_tddi_fw_read_flash_data(start_addr, end_addr, buf, length); + if (ret < 0) + goto out; + + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, buf, length, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ipio_vfree((void **)&buf); + +out: + ili_ice_mode_ctrl(DISABLE, OFF); + ILI_INFO("Dump flash %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + return ret; +} + +static void ilitek_tddi_flash_protect(bool enable) +{ + ILI_INFO("%s flash protection\n", enable ? "Enable" : "Disable"); + + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x1, 1) < 0) + ILI_ERR("Write 0x1 failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x0, 1) < 0) + ILI_ERR("Write 0x0 failed\n"); + + switch (ilits->flash_mid) { + case 0xEF: + if (ilits->flash_devid == 0x6012 || ilits->flash_devid == 0x6011) { + if (enable) { + if (ili_ice_mode_write(FLASH2_ADDR, 0x7E, 1) < 0) + ILI_ERR("Write 0x7E at %x failed\n", FLASH2_ADDR); + } else { + if (ili_ice_mode_write(FLASH2_ADDR, 0x0, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH2_ADDR); + } + } + break; + case 0xC8: + if (ilits->flash_devid == 0x6012 || ilits->flash_devid == 0x6013) { + if (enable) { + if (ili_ice_mode_write(FLASH2_ADDR, 0x7A, 1) < 0) + ILI_ERR("Write 0x7A at %x failed\n", FLASH2_ADDR); + } else { + if (ili_ice_mode_write(FLASH2_ADDR, 0x0, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH2_ADDR); + } + + } + break; + default: + ILI_ERR("Can't find flash id(0x%x), ignore protection\n", ilits->flash_mid); + break; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); +} + +static int ilitek_fw_check_ddi_chunk(u8 *pfw) +{ + int ret = 0; + u8 cmd[7] = {0x0}; + u32 start = 0x0, end = 0x0, len = 0x0; + bool hex_info = ilits->info_from_hex; + + if (!fbi[DDI].start || !fbi[DDI].end) + return UPDATE_PASS; + + /* To make sure if flash has data. */ + if (hex_info) + ilits->info_from_hex = DISABLE; + + if (ili_ic_get_fw_ver() < 0) { + ILI_INFO("Failed to get fw ver, reading flash by dma instead\n"); + goto dma; + } + + /* Command for getting ddi info from firmware. */ + cmd[0] = CMD_GET_FLASH_DATA; //cmd + cmd[1] = DDI_RSV_BK_SIZE & 0xFF; //len L byte + cmd[2] = (DDI_RSV_BK_SIZE >> 8) & 0xFF; //len H byte + + /* Read customer reserved block */ + start = DDI_RSV_BK_ST_ADDR; + cmd[3] = (start & 0xFF); //addr L byte + cmd[4] = (start >> 8) & 0xFF ; //addr M byte + cmd[5] = (start >> 16) & 0xFF; //addr H byte + cmd[6] = ili_calc_packet_checksum(cmd, sizeof(cmd) - 1); + ILI_INFO("Read customer reserved block: start addr = 0x%X, len = 0x%X ", start, DDI_RSV_BK_SIZE); + if (ilits->wrapper(cmd, sizeof(cmd), fw_flash_tmp, sizeof(fw_flash_tmp), OFF, ON) < 0) { + ILI_ERR("Failed to read customer reserved block data from fw\n"); + goto dma; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + /* Read mp data block */ + start = DDI_RSV_BK_END_ADDR - DDI_RSV_BK_SIZE + 1; + cmd[3] = (start & 0xFF); //addr L byte + cmd[4] = (start >> 8) & 0xFF ; //addr M byte + cmd[5] = (start >> 16) & 0xFF; //addr H byte + cmd[6] = ili_calc_packet_checksum(cmd, sizeof(cmd) - 1); + ILI_INFO("Read mp data block: start addr = 0x%X, len = 0x%X ", start, DDI_RSV_BK_SIZE); + if (ilits->wrapper(cmd, sizeof(cmd), fw_flash_tmp, sizeof(fw_flash_tmp), OFF, ON) < 0) { + ILI_ERR("Failed to read mp data block from fw\n"); + goto dma; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE- start); + + goto out; + +dma: + ILI_INFO("Obtain block data from dma\n"); + + ili_ice_mode_ctrl(ENABLE, OFF); + + /* Read customer reserved block */ + start = DDI_RSV_BK_ST_ADDR; + end = DDI_RSV_BK_ST_ADDR + DDI_RSV_BK_SIZE - 1; + len = DDI_RSV_BK_SIZE; + if (ilitek_tddi_fw_read_flash_data(start, end, fw_flash_tmp, len) < 0) { + ILI_ERR("Failed to read customer reserved block data from dma\\n"); + ret = UPDATE_FAIL; + goto out; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + /* Read mp data block */ + start = DDI_RSV_BK_END_ADDR - DDI_RSV_BK_SIZE + 1; + end = DDI_RSV_BK_END_ADDR; + len = DDI_RSV_BK_SIZE; + if (ilitek_tddi_fw_read_flash_data(start, end, fw_flash_tmp, len) < 0) { + ILI_ERR("Failed to read mp data block from dma\n"); + ret = UPDATE_FAIL; + goto out; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + ili_ice_mode_ctrl(DISABLE, OFF); +out: + /* + * Since there're two blocks combined with DDI chunk togeter, + * we need to extend the addresses where are going to upgrade. + */ + fbi[DDI].start = DDI_RSV_BK_ST_ADDR; + fbi[DDI].end = DDI_RSV_BK_END_ADDR; + tfd.end_addr = DDI_RSV_BK_END_ADDR; + + if (hex_info) + ilits->info_from_hex = ENABLE; + + atomic_set(&ilits->cmd_int_check, DISABLE); + return ret; +} + +static int ilitek_tddi_flash_fw_crc_check(u8 *pfw, bool check_fw_ver) +{ + int i, len = 0, crc_byte_len = 4; + u8 flash_crc[4] = {0}; + u32 start_addr = 0, end_addr = 0; + u32 hw_crc = 0; + u32 flash_crc_cb = 0, hex_crc = 0; + + /* Check Flash and HW CRC */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + start_addr = fbi[i].start; + end_addr = fbi[i].end; + + /* Invaild end address */ + if (end_addr == 0) + continue; + + if (ilitek_tddi_fw_read_flash_data(end_addr - crc_byte_len + 1, end_addr, + flash_crc, sizeof(flash_crc)) < 0) { + ILI_ERR("Read Flash failed\n"); + return UPDATE_FAIL; + } + + flash_crc_cb = flash_crc[0] << 24 | flash_crc[1] << 16 | flash_crc[2] << 8 | flash_crc[3]; + + if (ili_fw_read_hw_crc(start_addr, end_addr - start_addr - crc_byte_len + 1, &hw_crc) < 0) { + ILI_ERR("Read HW CRC failed\n"); + return UPDATE_FAIL; + } + + ILI_INFO("Block = %d, HW CRC = 0x%06x, Flash CRC = 0x%06x\n", i, hw_crc, flash_crc_cb); + + /* Compare Flash CRC with HW CRC */ + if (flash_crc_cb != hw_crc) { + ILI_INFO("HW and Flash CRC not matched\n"); + return UPDATE_FAIL; + } + memset(flash_crc, 0, sizeof(flash_crc)); + } + + if (check_fw_ver) { + /* Check FW version */ + ILI_INFO("New FW ver = 0x%x, Current FW ver = 0x%x\n", tfd.new_fw_cb, ilits->chip->fw_ver); + if (tfd.new_fw_cb != ilits->chip->fw_ver) { + ILI_INFO("FW version not matched\n"); + return UPDATE_FAIL; + } + } + + + /* Check Hex and HW CRC */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + start_addr = fbi[i].start; + end_addr = fbi[i].end; + + len = fbi[i].end - fbi[i].start + 1 - 4; + hex_crc = CalculateCRC32(fbi[i].start, len, pfw); + + if (ili_fw_read_hw_crc(start_addr, end_addr - start_addr - crc_byte_len + 1, &hw_crc) < 0) { + ILI_ERR("Read HW CRC failed\n"); + return UPDATE_FAIL; + } + + ILI_INFO("Block = %d, HW CRC = 0x%06x, Hex CRC = 0x%06x\n", i, hw_crc, hex_crc); + if (hex_crc != hw_crc) { + ILI_ERR("Hex and HW CRC not matched\n"); + return UPDATE_FAIL; + } + } + + ILI_INFO("Flash FW is the same as targe file FW\n"); + return UPDATE_PASS; +} + +static int ilitek_tddi_fw_flash_program(u8 *pfw) +{ + u8 buf[512] = {0}; + u32 i = 0, j = 0, addr = 0, k = 0, recv_addr = 0; + int page = ilits->program_page; + bool skip = true; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + ILI_INFO("Block[%d]: Programing from (0x%x) to (0x%x), tfd.end_addr = 0x%x\n", + i, fbi[i].start, fbi[i].end, tfd.end_addr); + + for (addr = fbi[i].start; addr < fbi[i].end; addr += page) { + buf[0] = 0x25; + buf[3] = 0x04; + buf[2] = 0x10; + buf[1] = 0x08; + + for (k = 0; k < page; k++) { + if (addr + k <= tfd.end_addr) + buf[4 + k] = pfw[addr + k]; + else + buf[4 + k] = 0xFF; + + if (buf[4 + k] != 0xFF) + skip = false; + } + + if (skip) { + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + return UPDATE_FAIL; + } + + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x2, 1) < 0) + ILI_ERR("Write 0x2 failed\n"); + + recv_addr = ((addr & 0xFF0000) >> 16) | (addr & 0x00FF00) | ((addr & 0x0000FF) << 16); + if (ili_ice_mode_write(FLASH2_ADDR, recv_addr, 3) < 0) + ILI_ERR("Write address failed\n"); + + if (ilits->wrapper(buf, page + 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to program data at start_addr = 0x%X, k = 0x%X, addr = 0x%x\n", + addr, k, addr + k); + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + return UPDATE_FAIL; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + if (ilits->flash_mid == 0xEF) { + mdelay(1); + } else { + if (ilitek_tddi_flash_poll_busy(TIMEOUT_PROGRAM) < 0) + return UPDATE_FAIL; + } + if (ilits->fw_update_stat != ((j * 100) / tfd.end_addr)) { + ilits->fw_update_stat = (j * 100) / tfd.end_addr; + ILI_DBG("Program flahse data .... %d%c", ilits->fw_update_stat, '%'); + } + } + } + return UPDATE_PASS; +} + +static int ilitek_tddi_fw_flash_erase(void) +{ + int ret = 0; + u32 i = 0, addr = 0, recv_addr = 0; + bool bk_erase = false; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + ILI_INFO("Block[%d]: Erasing from (0x%x) to (0x%x) \n", i, fbi[i].start, fbi[i].end); + + for (addr = fbi[i].start; addr <= fbi[i].end; addr += ilits->flash_sector) { + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (addr == fbi[AP].start) { + /* block erase with 64k bytes. */ + if (ili_ice_mode_write(FLASH2_ADDR, 0xD8, 1) < 0) + ILI_ERR("Write 0xB at %x failed\n", FLASH2_ADDR); + bk_erase = true; + } else { + /* sector erase with 4k bytes. */ + if (ili_ice_mode_write(FLASH2_ADDR, 0x20, 1) < 0) + ILI_ERR("Write 0x20 at %x failed\n", FLASH2_ADDR); + } + + recv_addr = ((addr & 0xFF0000) >> 16) | (addr & 0x00FF00) | ((addr & 0x0000FF) << 16); + if (ili_ice_mode_write(FLASH2_ADDR, recv_addr, 3) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + /* Waitint for flash setting ready */ + mdelay(1); + + if (addr == fbi[AP].start) + ret = ilitek_tddi_flash_poll_busy(TIMEOUT_PAGE); + else + ret = ilitek_tddi_flash_poll_busy(TIMEOUT_SECTOR); + + if (ret < 0) + return UPDATE_FAIL; + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + if (i == AP && tfd.is80k && bk_erase) { + addr = 0x10000 - 0x1000; + bk_erase = false; + } + } + } + return UPDATE_PASS; +} + +static int ilitek_fw_flash_upgrade(u8 *pfw) +{ + int ret = UPDATE_PASS; + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + + ilitek_fw_check_ddi_chunk(pfw); + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + return -EFW_ICE_MODE; + + ret = ilitek_tddi_flash_fw_crc_check(pfw, true); + if (ret == UPDATE_PASS) { + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) { + ILI_ERR("Disable ice mode failed, call reset instead\n"); + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + return UPDATE_PASS; + } + return UPDATE_PASS; + } else { + ILI_INFO("Flash FW and target file FW are different, do upgrade\n"); + } + + ret = ilitek_tddi_fw_flash_erase(); + if (ret == UPDATE_FAIL) + return -EFW_ERASE; + + ret = ilitek_tddi_fw_flash_program(pfw); + if (ret == UPDATE_FAIL) + return -EFW_PROGRAM; + + ret = ilitek_tddi_flash_fw_crc_check(pfw, false); + if (ret == UPDATE_FAIL) + return -EFW_CRC; + + /* We do have to reset chip in order to move new code from flash to iram. */ + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed after flash progam\n"); + ret = -EFW_REST; + } + return ret; +} + +static int ilitek_fw_calc_file_crc(u8 *pfw) +{ + int i; + u32 ex_addr, data_crc, file_crc; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + ex_addr = fbi[i].end; + data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw); + file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr]; + ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc); + if (data_crc != file_crc) { + ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n", + i, data_crc, file_crc); + return -1; + } + } + + ILI_INFO("Content of fw file is correct\n"); + return 0; +} + +static void ilitek_tddi_fw_update_block_info(u8 *pfw) +{ + u32 fw_info_addr = 0, fw_mp_ver_addr = 0; + + fbi[AP].name = "AP"; + fbi[DATA].name = "DATA"; + fbi[TUNING].name = "TUNING"; + fbi[MP].name = "MP"; + fbi[GESTURE].name = "GESTURE"; + + /* upgrade mode define */ + fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP; + fbi[MP].mode = MP; + fbi[GESTURE].mode = GESTURE; + + if (fbi[AP].end > (64*K)) + tfd.is80k = true; + + /* Copy fw info */ + fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR; + ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info)); + + /* copy fw mp ver */ + fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR; + ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr); + ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver)); + + /* copy fw core ver */ + ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) | + (ilits->fw_info[70] << 8) | ilits->fw_info[71]; + ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver); + + /* Get hex fw vers */ + tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) | + (ilits->fw_info[50] << 8) | ilits->fw_info[51]; + + /* Get hex report info block*/ + ipio_memcpy(&ilits->rib, ilits->fw_info, sizeof(ilits->rib), sizeof(ilits->rib)); + ILI_INFO("report_info_block : nReportByPixel = %d, nIsHostDownload = %d, nIsSPIICE = %d, nIsSPISLAVE = %d\n", + ilits->rib.nReportByPixel, ilits->rib.nIsHostDownload, ilits->rib.nIsSPIICE, ilits->rib.nIsSPISLAVE); + ILI_INFO("report_info_block : nIsI2C = %d, nReserved00 = %d, nReserved01 = %x, nReserved02 = %x, nReserved03 = %x\n", + ilits->rib.nIsI2C, ilits->rib.nReserved00, ilits->rib.nReserved01, ilits->rib.nReserved02, ilits->rib.nReserved03); + + /* Calculate update address */ + ILI_INFO("New FW ver = 0x%x\n", tfd.new_fw_cb); + ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number); +} + +static int ilitek_tddi_fw_ili_convert(u8 *pfw) +{ + int i, size, blk_num = 0, blk_map = 0, num = 0; + int b0_addr = 0, b0_num = 0; + + if (ERR_ALLOC_MEM(ilits->md_fw_ili)) + return -ENOMEM; + + CTPM_FW = ilits->md_fw_ili; + size = ilits->md_fw_ili_size; + + if (size < ILI_FILE_HEADER || size > MAX_HEX_FILE_SIZE) { + ILI_ERR("size of ILI file is invalid\n"); + return -EINVAL; + } + + /* Check if it's old version of ILI format. */ + if (CTPM_FW[22] == 0xFF && CTPM_FW[23] == 0xFF && + CTPM_FW[24] == 0xFF && CTPM_FW[25] == 0xFF) { + ILI_ERR("Invaild ILI format, abort!\n"); + return -EINVAL; + } + + blk_num = CTPM_FW[131]; + blk_map = (CTPM_FW[129] << 8) | CTPM_FW[130]; + ILI_INFO("Parsing ILI file, block num = %d, block mapping = %x\n", blk_num, blk_map); + + if (blk_num > (FW_BLOCK_INFO_NUM - 1) || !blk_num || !blk_map) { + ILI_ERR("Number of block or block mapping is invalid, abort!\n"); + return -EINVAL; + } + + memset(fbi, 0x0, sizeof(fbi)); + tfd.start_addr = 0; + tfd.end_addr = 0; + tfd.hex_tag = BLOCK_TAG_AF; + + /* Parsing block info */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + /* B0 tag */ + b0_addr = (CTPM_FW[4 + i * 4] << 16) | (CTPM_FW[5 + i * 4] << 8) | (CTPM_FW[6 + i * 4]); + b0_num = CTPM_FW[7 + i * 4]; + if ((b0_num != 0) && (b0_addr != 0x000000)) + fbi[b0_num].fix_mem_start = b0_addr; + + /* AF tag */ + num = i + 1; + if (((blk_map >> i) & 0x01) == 0x01) { + fbi[num].start = (CTPM_FW[132 + i * 6] << 16) | (CTPM_FW[133 + i * 6] << 8) | CTPM_FW[134 + i * 6]; + fbi[num].end = (CTPM_FW[135 + i * 6] << 16) | (CTPM_FW[136 + i * 6] << 8) | CTPM_FW[137 + i * 6]; + + if (fbi[num].fix_mem_start == 0) + fbi[num].fix_mem_start = INT_MAX; + + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_INFO("Block[%d]: start_addr = %x, end = %x, fix_mem_start = 0x%x\n", num, fbi[num].start, + fbi[num].end, fbi[num].fix_mem_start); + } + } + + memcpy(pfw, CTPM_FW + ILI_FILE_HEADER, size - ILI_FILE_HEADER); + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.block_number = blk_num; + tfd.end_addr = size - ILI_FILE_HEADER; + return 0; +} + +static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw) +{ + int block = 0; + u32 i = 0, j = 0, k = 0, num = 0; + u32 len = 0, addr = 0, type = 0; + u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0; + u32 offset; + + memset(fbi, 0x0, sizeof(fbi)); + + /* Parsing HEX file */ + for (; i < size;) { + len = HexToDec(&phex[i + 1], 2); + addr = HexToDec(&phex[i + 3], 4); + type = HexToDec(&phex[i + 7], 2); + + if (type == 0x04) { + ex_addr = HexToDec(&phex[i + 9], 4); + } else if (type == 0x02) { + ex_addr = HexToDec(&phex[i + 9], 4); + ex_addr = ex_addr >> 12; + } else if (type == BLOCK_TAG_AF) { + /* insert block info extracted from hex */ + tfd.hex_tag = type; + if (tfd.hex_tag == BLOCK_TAG_AF) + num = HexToDec(&phex[i + 9 + 6 + 6], 2); + else + num = 0xFF; + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].start = HexToDec(&phex[i + 9], 6); + fbi[num].end = HexToDec(&phex[i + 9 + 6], 6); + fbi[num].fix_mem_start = INT_MAX; + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_INFO("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end); + + block++; + } else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) { + num = HexToDec(&phex[i + 9 + 6], 2); + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6); + ILI_INFO("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start); + } + + addr = addr + (ex_addr << 16); + + if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D) + offset = 2; + else + offset = 1; + + if (addr > MAX_HEX_FILE_SIZE) { + ILI_ERR("Invalid hex format %d\n", addr); + return -1; + } + + if (type == 0x00) { + end_addr = addr + len; + if (addr < start_addr) + start_addr = addr; + /* fill data */ + for (j = 0, k = 0; j < (len * 2); j += 2, k++) + pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2); + } + i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset; + } + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.start_addr = start_addr; + tfd.end_addr = end_addr; + tfd.block_number = block; + return 0; +} + +static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw) +{ + int ret =0, fsize = 0; + const struct firmware *fw = NULL; + struct file *f = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + + ILI_INFO("Open file method = %s, path = %s\n", + op ? "FILP_OPEN" : "REQUEST_FIRMWARE", + op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path); + + switch (op) { + case REQUEST_FIRMWARE: + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed, try again\n"); + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed after retry\n"); + ret = -1; + goto out; + } + } + + fsize = fw->size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is zero\n"); + release_firmware(fw); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + release_firmware(fw); + ret = -ENOMEM; + goto out; + } + } + + /* Copy fw data got from request_firmware to global */ + ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize); + ilits->tp_fw.size = fsize; + release_firmware(fw); + break; + case FILP_OPEN: + f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + fsize = f->f_inode->i_size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ipio_vfree((void **)&(ilits->tp_fw.data)); + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + filp_close(f, NULL); + ret = -ENOMEM; + goto out; + } + } + + /* ready to map user's memory to obtain data by reading files */ + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ilits->tp_fw.size = fsize; + break; + default: + ILI_ERR("Unknown open file method, %d\n", op); + break; + } + + if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) { + ILI_ERR("fw data/size is invaild\n"); + ret = -1; + goto out; + } + + /* Convert hex and copy data from tp_fw.data to pfw */ + if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) { + ILI_ERR("Convert hex file failed\n"); + ret = -1; + } + +out: + ipio_vfree((void **)&(ilits->tp_fw.data)); + return ret; +} + +int ili_fw_upgrade(int op) +{ + int i, ret = 0, retry = 3; + + if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) { + if (ERR_ALLOC_MEM(pfw)) { + ipio_vfree((void **)&pfw); + pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8)); + if (ERR_ALLOC_MEM(pfw)) { + ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw)); + ipio_vfree((void **)&pfw); + ret = -ENOMEM; + goto out; + } + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + pfw[i] = 0xFF; + + if (ilitek_tdd_fw_hex_open(op, pfw) < 0) { + ILI_ERR("Open hex file fail, try upgrade from ILI file\n"); + + /* + * Users might not be aware of a broken hex file when recovering + * fw from ILI file. We should force them to check + * hex files they attempt to update via device node. + */ + if (ilits->node_update) { + ILI_ERR("Ignore update from ILI file\n"); + ipio_vfree((void **)&pfw); + return -EFW_CONVERT_FILE; + } + + if (ilitek_tddi_fw_ili_convert(pfw) < 0) { + ILI_ERR("Convert ILI file error\n"); + ret = -EFW_CONVERT_FILE; + goto out; + } + } + ilitek_tddi_fw_update_block_info(pfw); + + if (ilits->chip->core_ver >= CORE_VER_1470 && ilits->rib.nIsHostDownload == 1) { + ILI_ERR("hex file interface no match error\n"); + ret = -EFW_INTERFACE; + goto out; + } + } + + do { + ret = ilitek_fw_flash_upgrade(pfw); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing flash\n", retry); + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while erasing flash\n"); + if (ilitek_tddi_fw_flash_erase() < 0) + ILI_ERR("Failed to erase flash\n"); + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed after erase flash\n"); + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP reset failed after erase flash\n"); + } + +out: + if (ret < 0) + ilits->info_from_hex = DISABLE; + + if (atomic_read(&ilits->ice_stat)) + ili_ice_mode_ctrl(DISABLE, OFF); + + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ili_ic_func_ctrl_reset(); + + if (!ilits->info_from_hex) + ilits->info_from_hex = ENABLE; + + ipio_vfree((void **)&pfw); + return ret; +} + +struct flash_table { + u16 mid; + u16 dev_id; + int mem_size; + int program_page; + int sector; +} flash_t[] = { + [0] = {0x00, 0x0000, (256 * K), 256, (4 * K)}, /* Default */ + [1] = {0xEF, 0x6011, (128 * K), 256, (4 * K)}, /* W25Q10EW */ + [2] = {0xEF, 0x6012, (256 * K), 256, (4 * K)}, /* W25Q20EW */ + [3] = {0xC8, 0x6012, (256 * K), 256, (4 * K)}, /* GD25LQ20B */ + [4] = {0xC8, 0x6013, (512 * K), 256, (4 * K)}, /* GD25LQ40 */ + [5] = {0x85, 0x6013, (4 * M), 256, (4 * K)}, + [6] = {0xC2, 0x2812, (256 * K), 256, (4 * K)}, + [7] = {0x1C, 0x3812, (256 * K), 256, (4 * K)}, +}; + +void ili_fw_read_flash_info(void) +{ + int i = 0; + u8 buf[4] = {0}; + u8 cmd = 0x9F; + u32 tmp = 0; + u16 flash_id = 0, flash_mid = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while reading flash info\n"); + } + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) + ILI_ERR("Write 0x9F failed\n"); + + for (i = 0; i < ARRAY_SIZE(buf); i++) { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_read(FLASH4_ADDR, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read flash info error\n"); + + buf[i] = tmp; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); /* CS high */ + + flash_mid = buf[0]; + flash_id = buf[1] << 8 | buf[2]; + + for (i = 0; i < ARRAY_SIZE(flash_t); i++) { + if (flash_mid == flash_t[i].mid && flash_id == flash_t[i].dev_id) { + ilits->flash_mid = flash_t[i].mid; + ilits->flash_devid = flash_t[i].dev_id; + ilits->program_page = flash_t[i].program_page; + ilits->flash_sector = flash_t[i].sector; + break; + } + } + + if (i >= ARRAY_SIZE(flash_t)) { + ILI_INFO("Not found flash id in tab, use default\n"); + ilits->flash_mid = flash_t[0].mid; + ilits->flash_devid = flash_t[0].dev_id; + ilits->program_page = flash_t[0].program_page; + ilits->flash_sector = flash_t[0].sector; + } + + ILI_INFO("Flash MID = %x, Flash DEV_ID = %x\n", ilits->flash_mid, ilits->flash_devid); + ILI_INFO("Flash program page = %d\n", ilits->program_page); + ILI_INFO("Flash sector = %d\n", ilits->flash_sector); + + ilitek_tddi_flash_protect(DISABLE); + + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed while reading flash info\n"); + } +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_hostdl.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_hostdl.c new file mode 100755 index 000000000000..8ec3d557b673 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_hostdl.c @@ -0,0 +1,917 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define UPDATE_PASS 0 +#define UPDATE_FAIL -1 +#define TIMEOUT_SECTOR 500 +#define TIMEOUT_PAGE 3500 +#define TIMEOUT_PROGRAM 10 + +static struct touch_fw_data { + u8 block_number; + u32 start_addr; + u32 end_addr; + u32 new_fw_cb; + int delay_after_upgrade; + bool isCRC; + bool isboot; + bool is80k; + int hex_tag; +} tfd; + +static struct flash_block_info { + char *name; + u32 start; + u32 end; + u32 len; + u32 mem_start; + u32 fix_mem_start; + u8 mode; +} fbi[FW_BLOCK_INFO_NUM]; + +static u8 *pfw; +static u8 *CTPM_FW; + +static u32 HexToDec(char *phex, s32 len) +{ + u32 ret = 0, temp = 0, i; + s32 shift = (len - 1) * 4; + + for (i = 0; i < len; shift -= 4, i++) { + if ((phex[i] >= '0') && (phex[i] <= '9')) + temp = phex[i] - '0'; + else if ((phex[i] >= 'a') && (phex[i] <= 'f')) + temp = (phex[i] - 'a') + 10; + else if ((phex[i] >= 'A') && (phex[i] <= 'F')) + temp = (phex[i] - 'A') + 10; + + ret |= (temp << shift); + } + return ret; +} + +static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw) +{ + int i = 0, j = 0; + int crc_poly = 0x04C11DB7; + int tmp_crc = 0xFFFFFFFF; + + for (i = start_addr; i < start_addr + len; i++) { + tmp_crc ^= (pfw[i] << 24); + + for (j = 0; j < 8; j++) { + if ((tmp_crc & 0x80000000) != 0) + tmp_crc = tmp_crc << 1 ^ crc_poly; + else + tmp_crc = tmp_crc << 1; + } + } + return tmp_crc; +} + +static int calc_hw_dma_crc(u32 start_addr, u32 block_size) +{ + int count = 50; + u32 busy = 0; + + /* dma1 src1 address */ + if (ili_ice_mode_write(0x072104, start_addr, 4) < 0) + ILI_ERR("Write dma1 src1 address failed\n"); + /* dma1 src1 format */ + if (ili_ice_mode_write(0x072108, 0x80000001, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest address */ + if (ili_ice_mode_write(0x072114, 0x0002725C, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest format */ + if (ili_ice_mode_write(0x072118, 0x80000000, 4) < 0) + ILI_ERR("Write dma1 dest format failed\n"); + /* Block size*/ + if (ili_ice_mode_write(0x07211C, block_size, 4) < 0) + ILI_ERR("Write block size (%d) failed\n", block_size); + /* crc off */ + if (ili_ice_mode_write(0x041016, 0x00, 1) < 0) + ILI_INFO("Write crc of failed\n"); + /* dma crc */ + if (ili_ice_mode_write(0x041017, 0x03, 1) < 0) + ILI_ERR("Write dma 1 crc failed\n"); + /* Dma1 stop */ + if (ili_ice_mode_write(0x072100, 0x02000000, 4) < 0) + ILI_ERR("Write dma1 stop failed\n"); + /* crc on */ + if (ili_ice_mode_write(0x041016, 0x01, 1) < 0) + ILI_ERR("Write crc on failed\n"); + /* clr int */ + if (ili_ice_mode_write(0x048006, 0x2, 1) < 0) + ILI_ERR("Write clr int failed\n"); + /* Dma1 start */ + if (ili_ice_mode_write(0x072100, 0x01000000, 4) < 0) + ILI_ERR("Write dma1 start failed\n"); + + /* Polling BIT0 */ + while (count > 0) { + mdelay(1); + if (ili_ice_mode_read(0x048006, &busy, sizeof(u8)) < 0) + ILI_ERR("Read busy error\n"); + ILI_DBG("busy = %x\n", busy); + if ((busy & 0x02) == 2) + break; + count--; + } + + if (count <= 0) { + ILI_ERR("BIT0 is busy\n"); + return -1; + } + + if (ili_ice_mode_read(0x04101C, &busy, sizeof(u32)) < 0) { + ILI_ERR("Read dma crc error\n"); + return -1; + } + return busy; +} + +static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len) +{ + int limit = SPI_RX_BUF_SIZE; + int addr = 0, loop = 0, tmp_len = len, cnt = 0; + u8 cmd[4] = {0}; + + if (!buf) { + ILI_ERR("buf is null\n"); + return -ENOMEM; + } + + if (len % limit) + loop = (len / limit) + 1; + else + loop = len / limit; + + for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) { + if (tmp_len > limit) + tmp_len = limit; + + cmd[0] = 0x25; + cmd[3] = (char)((addr & 0x00FF0000) >> 16); + cmd[2] = (char)((addr & 0x0000FF00) >> 8); + cmd[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + tmp_len = len - cnt * limit; + ilits->fw_update_stat = ((len - tmp_len) * 100) / len; + ILI_INFO("Reading iram data .... %d%c", ilits->fw_update_stat, '%'); + } + return 0; +} + +int ili_fw_dump_iram_data(u32 start, u32 end, bool save) +{ + struct file *f = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + int i, ret = 0; + int len, tmp = debug_en; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) { + ILI_ERR("Enable ice mode failed\n"); + return ret; + } + } + + len = end - start + 1; + + if (len > MAX_HEX_FILE_SIZE) { + ILI_ERR("len is larger than buffer, abort\n"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + ilits->update_buf[i] = 0xFF; + + ret = ilitek_tddi_fw_iram_read(ilits->update_buf, start, len); + if (ret < 0) { + ILI_ERR("Read IRAM data failed\n"); + goto out; + } + + if (save) { + f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -ENOMEM; + goto out; + } + + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, ilits->update_buf, len, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH); + } else { + debug_en = DEBUG_ALL; + ili_dump_data(ilits->update_buf, 8, len, 0, "IRAM"); + debug_en = tmp; + } + +out: + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed after code reset\n"); + } + + ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + return ret; +} + +static int ilitek_tddi_fw_iram_program(u32 start, u8 *w_buf, u32 w_len, u32 split_len) +{ + int i = 0, j = 0, addr = 0; + u32 end = start + w_len; + bool fix_4_alignment = false; + + if (split_len % 4 > 0) + ILI_ERR("Since split_len must be four-aligned, it must be a multiple of four"); + + if (split_len != 0) { + for (addr = start, i = 0; addr < end; addr += split_len, i += split_len) { + if ((addr + split_len) > end) { + split_len = end - addr; + if (split_len % 4 != 0) + fix_4_alignment = true; + } + + ilits->update_buf[0] = SPI_WRITE; + ilits->update_buf[1] = 0x25; + ilits->update_buf[2] = (char)((addr & 0x000000FF)); + ilits->update_buf[3] = (char)((addr & 0x0000FF00) >> 8); + ilits->update_buf[4] = (char)((addr & 0x00FF0000) >> 16); + + for (j = 0; j < split_len; j++) + ilits->update_buf[5 + j] = w_buf[i + j]; + + if (fix_4_alignment) { + ILI_INFO("org split_len = 0x%X\n", split_len); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 4, ilits->update_buf[5 + split_len - 4]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 3, ilits->update_buf[5 + split_len - 3]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 2, ilits->update_buf[5 + split_len - 2]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 1, ilits->update_buf[5 + split_len - 1]); + for (j = 0; j < (4 - (split_len % 4)); j++) { + ilits->update_buf[5 + j + split_len] = 0xFF; + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n",j + split_len, ilits->update_buf[5 + j + split_len]); + } + + ILI_INFO("split_len %% 4 = %d\n", split_len % 4); + split_len = split_len + (4 - (split_len % 4)); + ILI_INFO("fix split_len = 0x%X\n", split_len); + } + if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, split_len + 5, NULL, 0)) { + ILI_ERR("Failed to write data via SPI in host download (%x)\n", split_len + 5); + return -EIO; + } + ilits->fw_update_stat = (i * 100) / w_len; + } + } else { + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + ilits->update_buf[i] = 0xFF; + + ilits->update_buf[0] = SPI_WRITE; + ilits->update_buf[1] = 0x25; + ilits->update_buf[2] = (char)((start & 0x000000FF)); + ilits->update_buf[3] = (char)((start & 0x0000FF00) >> 8); + ilits->update_buf[4] = (char)((start & 0x00FF0000) >> 16); + + memcpy(&ilits->update_buf[5], w_buf, w_len); + if (w_len % 4 != 0) { + ILI_INFO("org w_len = %d\n", w_len); + w_len = w_len + (4 - (w_len % 4)); + ILI_INFO("w_len = %d w_len %% 4 = %d\n", w_len, w_len % 4); + } + /* It must be supported by platforms that have the ability to transfer all data at once. */ + if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, w_len + 5, NULL, 0) < 0) { + ILI_ERR("Failed to write data via SPI in host download (%x)\n", w_len + 5); + return -EIO; + } + } + return 0; +} + +static int ilitek_tddi_fw_iram_upgrade(u8 *pfw) +{ + int i, ret = UPDATE_PASS; + u32 mode, crc, dma, iram_crc = 0; + u8 *fw_ptr = NULL, crc_temp[4], crc_len = 4; + bool iram_crc_err = false; + + if (!ilits->ddi_rest_done) { + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) + ili_reset_ctrl(ilits->reset); + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + return -EFW_ICE_MODE; + } else { + /* Restore it if the wq of load_fw_ddi has been called. */ + ilits->ddi_rest_done = false; + } + + if (ilits->chip->dma_reset) { + ILI_INFO("operate dma reset in reg after tp reset\n"); + if (ili_ice_mode_write(0x40040, 0x00800000, 4) < 0) + ILI_INFO("Failed to open DMA reset\n"); + if (ili_ice_mode_write(0x40040, 0x00000000, 4) < 0) + ILI_INFO("Failed to close DMA reset\n"); + } + + /* Point to pfw with different addresses for getting its block data. */ + fw_ptr = pfw; + if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) { + + mode = MP; + } else if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) { + mode = GESTURE; + crc_len = 0; + } else { + mode = AP; + } + + /* Program data to iram acorrding to each block */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].mode == mode && fbi[i].len != 0) { + ILI_DBG("Download %s code from hex 0x%x to IRAM 0x%x, len = 0x%x\n", + fbi[i].name, fbi[i].start, fbi[i].mem_start, fbi[i].len); + +#if SPI_DMA_TRANSFER_SPLIT + if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, SPI_UPGRADE_LEN) < 0) + ILI_ERR("IRAM program failed\n"); +#else + if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, 0) < 0) + ILI_ERR("IRAM program failed\n"); +#endif + crc = CalculateCRC32(fbi[i].start, fbi[i].len - crc_len, fw_ptr); + dma = calc_hw_dma_crc(fbi[i].mem_start, fbi[i].len - crc_len); + + if (mode != GESTURE) { + ilitek_tddi_fw_iram_read(crc_temp, (fbi[i].mem_start + fbi[i].len - crc_len), sizeof(crc_temp)); + iram_crc = crc_temp[0] << 24 | crc_temp[1] << 16 |crc_temp[2] << 8 | crc_temp[3]; + if (iram_crc != dma) + iram_crc_err = true; + } + + ILI_INFO("%s CRC is %s hex(%x) : dma(%x) : iram(%x), calculation len is 0x%x\n", + fbi[i].name,((crc != dma)||(iram_crc_err)) ? "Invalid !" : "Correct !", crc, dma, iram_crc, fbi[i].len - crc_len); + + if ((crc != dma)|| iram_crc_err) { + ILI_ERR("CRC Error! print iram data with first 16 bytes\n"); + ili_fw_dump_iram_data(0x0, 0xF, false); + return -EFW_CRC; + } + } + } + + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { + if (ili_reset_ctrl(TP_IC_CODE_RST) < 0) { + ILI_ERR("TP Code reset failed during iram programming\n"); + ret = -EFW_REST; + return ret; + } + } + + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) { + ILI_ERR("Disable ice mode failed after code reset\n"); + ret = -EFW_ICE_MODE; + } + + /* Waiting for fw ready sending first cmd */ + if (!ilits->info_from_hex || (ilits->chip->core_ver < CORE_VER_1410)) + mdelay(100); + + return ret; +} + +static int ilitek_fw_calc_file_crc(u8 *pfw) +{ + int i; + u32 ex_addr, data_crc, file_crc; + + for (i = 0; i < ARRAY_SIZE(fbi); i++) { + if (fbi[i].end == 0) + continue; + ex_addr = fbi[i].end; + data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw); + file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr]; + ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc); + if (data_crc != file_crc) { + ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n", + i, data_crc, file_crc); + return -1; + } + } + + ILI_INFO("Content of fw file is correct\n"); + return 0; +} + +static int ilitek_tddi_fw_update_block_info(u8 *pfw) +{ + u32 ges_area_section = 0, ges_info_addr = 0, ges_fw_start = 0, ges_fw_end = 0; + u32 ap_end = 0, ap_len = 0; + u32 fw_info_addr = 0, fw_mp_ver_addr = 0; + + if (tfd.hex_tag != BLOCK_TAG_AF) { + ILI_ERR("HEX TAG is invalid (0x%X)\n", tfd.hex_tag); + return -EINVAL; + } + + fbi[AP].mem_start = (fbi[AP].fix_mem_start != INT_MAX) ? fbi[AP].fix_mem_start : 0; + fbi[DATA].mem_start = (fbi[DATA].fix_mem_start != INT_MAX) ? fbi[DATA].fix_mem_start : DLM_START_ADDRESS; + fbi[TUNING].mem_start = (fbi[TUNING].fix_mem_start != INT_MAX) ? fbi[TUNING].fix_mem_start : fbi[DATA].mem_start + fbi[DATA].len; + fbi[MP].mem_start = (fbi[MP].fix_mem_start != INT_MAX) ? fbi[MP].fix_mem_start : 0; + fbi[GESTURE].mem_start = (fbi[GESTURE].fix_mem_start != INT_MAX) ? fbi[GESTURE].fix_mem_start : 0; + fbi[TAG].mem_start = (fbi[TAG].fix_mem_start != INT_MAX) ? fbi[TAG].fix_mem_start : 0; + fbi[PARA_BACKUP].mem_start = (fbi[PARA_BACKUP].fix_mem_start != INT_MAX) ? fbi[PARA_BACKUP].fix_mem_start : 0; + fbi[DDI].mem_start = (fbi[DDI].fix_mem_start != INT_MAX) ? fbi[DDI].fix_mem_start : 0; + + /* Parsing gesture info form AP code */ + ges_info_addr = (fbi[AP].end + 1 - 60); + ges_area_section = (pfw[ges_info_addr + 3] << 24) + (pfw[ges_info_addr + 2] << 16) + (pfw[ges_info_addr + 1] << 8) + pfw[ges_info_addr]; + fbi[GESTURE].mem_start = (pfw[ges_info_addr + 7] << 24) + (pfw[ges_info_addr + 6] << 16) + (pfw[ges_info_addr + 5] << 8) + pfw[ges_info_addr + 4]; + ap_end = (pfw[ges_info_addr + 11] << 24) + (pfw[ges_info_addr + 10] << 16) + (pfw[ges_info_addr + 9] << 8) + pfw[ges_info_addr + 8]; + + if (ap_end != fbi[GESTURE].mem_start) + ap_len = ap_end - fbi[GESTURE].mem_start + 1; + + ges_fw_start = (pfw[ges_info_addr + 15] << 24) + (pfw[ges_info_addr + 14] << 16) + (pfw[ges_info_addr + 13] << 8) + pfw[ges_info_addr + 12]; + ges_fw_end = (pfw[ges_info_addr + 19] << 24) + (pfw[ges_info_addr + 18] << 16) + (pfw[ges_info_addr + 17] << 8) + pfw[ges_info_addr + 16]; + + if (ges_fw_end != ges_fw_start) + fbi[GESTURE].len = ges_fw_end - ges_fw_start; + + /* update gesture address */ + fbi[GESTURE].start = ges_fw_start; + + ILI_INFO("==== Gesture loader info ====\n"); + ILI_INFO("gesture move to ap addr => start = 0x%x, ap_end = 0x%x, ap_len = 0x%x\n", fbi[GESTURE].mem_start, ap_end, ap_len); + ILI_INFO("gesture hex addr => start = 0x%x, gesture_end = 0x%x, gesture_len = 0x%x\n", ges_fw_start, ges_fw_end, fbi[GESTURE].len); + ILI_INFO("=============================\n"); + + fbi[AP].name = "AP"; + fbi[DATA].name = "DATA"; + fbi[TUNING].name = "TUNING"; + fbi[MP].name = "MP"; + fbi[GESTURE].name = "GESTURE"; + fbi[TAG].name = "TAG"; + fbi[PARA_BACKUP].name = "PARA_BACKUP"; + fbi[DDI].name = "DDI"; + + /* upgrade mode define */ + fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP; + fbi[MP].mode = MP; + fbi[GESTURE].mode = GESTURE; + + if (fbi[AP].end > (64*K)) + tfd.is80k = true; + + /* Copy fw info */ + fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR; + ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info)); + + /* copy fw mp ver */ + fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR; + ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr); + ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver)); + + /* copy fw core ver */ + ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) | + (ilits->fw_info[70] << 8) | ilits->fw_info[71]; + ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver); + + /* Get hex fw vers */ + tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) | + (ilits->fw_info[50] << 8) | ilits->fw_info[51]; + + /* Get hex report info block*/ + ipio_memcpy(&ilits->rib, ilits->fw_info, sizeof(ilits->rib), sizeof(ilits->rib)); + ILI_INFO("report_info_block : nReportByPixel = %d, nIsHostDownload = %d, nIsSPIICE = %d, nIsSPISLAVE = %d\n", + ilits->rib.nReportByPixel, ilits->rib.nIsHostDownload, ilits->rib.nIsSPIICE, ilits->rib.nIsSPISLAVE); + ILI_INFO("report_info_block : nIsI2C = %d, nReserved00 = %d, nReserved01 = %x, nReserved02 = %x, nReserved03 = %x\n", + ilits->rib.nIsI2C, ilits->rib.nReserved00, ilits->rib.nReserved01, ilits->rib.nReserved02, ilits->rib.nReserved03); + + /* Calculate update address */ + ILI_INFO("New FW ver = 0x%x\n", tfd.new_fw_cb); + ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number); + + return 0; +} + +static int ilitek_tddi_fw_ili_convert(u8 *pfw) +{ + int i, size, blk_num = 0, blk_map = 0, num; + int b0_addr = 0, b0_num = 0; + + if (ERR_ALLOC_MEM(ilits->md_fw_ili)) + return -ENOMEM; + + CTPM_FW = ilits->md_fw_ili; + size = ilits->md_fw_ili_size; + + if (size < ILI_FILE_HEADER || size > MAX_HEX_FILE_SIZE) { + ILI_ERR("size of ILI file is invalid\n"); + return -EINVAL; + } + + /* Check if it's old version of ILI format. */ + if (CTPM_FW[22] == 0xFF && CTPM_FW[23] == 0xFF && + CTPM_FW[24] == 0xFF && CTPM_FW[25] == 0xFF) { + ILI_ERR("Invaild ILI format, abort!\n"); + return -EINVAL; + } + + blk_num = CTPM_FW[131]; + blk_map = (CTPM_FW[129] << 8) | CTPM_FW[130]; + ILI_INFO("Parsing ILI file, block num = %d, block mapping = %x\n", blk_num, blk_map); + + if (blk_num > (FW_BLOCK_INFO_NUM - 1) || !blk_num || !blk_map) { + ILI_ERR("Number of block or block mapping is invalid, abort!\n"); + return -EINVAL; + } + + memset(fbi, 0x0, sizeof(fbi)); + + tfd.start_addr = 0; + tfd.end_addr = 0; + tfd.hex_tag = BLOCK_TAG_AF; + + /* Parsing block info */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + /* B0 tag */ + b0_addr = (CTPM_FW[4 + i * 4] << 16) | (CTPM_FW[5 + i * 4] << 8) | (CTPM_FW[6 + i * 4]); + b0_num = CTPM_FW[7 + i * 4]; + if ((b0_num != 0) && (b0_addr != 0x000000)) + fbi[b0_num].fix_mem_start = b0_addr; + + /* AF tag */ + num = i + 1; + if (((blk_map >> i) & 0x01) == 0x01) { + fbi[num].start = (CTPM_FW[132 + i * 6] << 16) | (CTPM_FW[133 + i * 6] << 8) | CTPM_FW[134 + i * 6]; + fbi[num].end = (CTPM_FW[135 + i * 6] << 16) | (CTPM_FW[136 + i * 6] << 8) | CTPM_FW[137 + i * 6]; + + if (fbi[num].fix_mem_start == 0) + fbi[num].fix_mem_start = INT_MAX; + + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_DBG("Block[%d]: start_addr = %x, end = %x, fix_mem_start = 0x%x\n", num, fbi[num].start, + fbi[num].end, fbi[num].fix_mem_start); + if (num == GESTURE) + ilits->gesture_load_code = true; + } + } + + memcpy(pfw, CTPM_FW + ILI_FILE_HEADER, size - ILI_FILE_HEADER); + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.block_number = blk_num; + tfd.end_addr = size - ILI_FILE_HEADER; + return 0; +} + +static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw) +{ + int block = 0; + u32 i = 0, j = 0, k = 0, num = 0; + u32 len = 0, addr = 0, type = 0; + u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0; + u32 offset; + + memset(fbi, 0x0, sizeof(fbi)); + + /* Parsing HEX file */ + for (; i < size;) { + len = HexToDec(&phex[i + 1], 2); + addr = HexToDec(&phex[i + 3], 4); + type = HexToDec(&phex[i + 7], 2); + + if (type == 0x04) { + ex_addr = HexToDec(&phex[i + 9], 4); + } else if (type == 0x02) { + ex_addr = HexToDec(&phex[i + 9], 4); + ex_addr = ex_addr >> 12; + } else if (type == BLOCK_TAG_AF) { + /* insert block info extracted from hex */ + tfd.hex_tag = type; + if (tfd.hex_tag == BLOCK_TAG_AF) + num = HexToDec(&phex[i + 9 + 6 + 6], 2); + else + num = 0xFF; + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].start = HexToDec(&phex[i + 9], 6); + fbi[num].end = HexToDec(&phex[i + 9 + 6], 6); + fbi[num].fix_mem_start = INT_MAX; + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_DBG("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end); + + if (num == GESTURE) + ilits->gesture_load_code = true; + + block++; + } else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) { + num = HexToDec(&phex[i + 9 + 6], 2); + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6); + ILI_DBG("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start); + } + + addr = addr + (ex_addr << 16); + + if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D) + offset = 2; + else + offset = 1; + + if (addr > MAX_HEX_FILE_SIZE) { + ILI_ERR("Invalid hex format %d\n", addr); + return -1; + } + + if (type == 0x00) { + end_addr = addr + len; + if (addr < start_addr) + start_addr = addr; + /* fill data */ + for (j = 0, k = 0; j < (len * 2); j += 2, k++) + pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2); + } + i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset; + } + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.start_addr = start_addr; + tfd.end_addr = end_addr; + tfd.block_number = block; + return 0; +} + +static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw) +{ + int ret =0, fsize = 0; + const struct firmware *fw = NULL; + struct file *f = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + + ILI_INFO("Open file method = %s, path = %s\n", + op ? "FILP_OPEN" : "REQUEST_FIRMWARE", + op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path); + + switch (op) { + case REQUEST_FIRMWARE: + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed, try again\n"); + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed after retry\n"); + ret = -1; + goto out; + } + } + + fsize = fw->size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + release_firmware(fw); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + release_firmware(fw); + ret = -ENOMEM; + goto out; + } + } + + /* Copy fw data got from request_firmware to global */ + ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize); + ilits->tp_fw.size = fsize; + release_firmware(fw); + break; + case FILP_OPEN: + f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file, %ld\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + fsize = f->f_inode->i_size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + filp_close(f, NULL); + ret = -ENOMEM; + goto out; + } + } + + /* ready to map user's memory to obtain data by reading files */ + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ilits->tp_fw.size = fsize; + break; + default: + ILI_ERR("Unknown open file method, %d\n", op); + break; + } + + if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) { + ILI_ERR("fw data/size is invaild\n"); + ret = -1; + goto out; + } + + /* Convert hex and copy data from tp_fw.data to pfw */ + if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) { + ILI_ERR("Convert hex file failed\n"); + ret = -1; + } + +out: + ipio_vfree((void **)&(ilits->tp_fw.data)); + return ret; +} + +int ili_fw_upgrade(int op) +{ + int i, ret = 0, retry = 3; + + if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) { + ilits->gesture_load_code = false; + + if (ERR_ALLOC_MEM(pfw)) { + ipio_vfree((void **)&pfw); + pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8)); + if (ERR_ALLOC_MEM(pfw)) { + ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw)); + ipio_vfree((void **)&pfw); + ret = -ENOMEM; + goto out; + } + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + pfw[i] = 0xFF; + + if (ilitek_tdd_fw_hex_open(op, pfw) < 0) { + ILI_ERR("Open hex file fail, try upgrade from ILI file\n"); + + /* + * Users might not be aware of a broken hex file when recovering + * fw from ILI file. We should force them to check + * hex files if they attempt to update via device node. + */ + if (ilits->node_update) { + ILI_ERR("Ignore update from ILI file\n"); + ipio_vfree((void **)&pfw); + return -EFW_CONVERT_FILE; + } + + if (ilitek_tddi_fw_ili_convert(pfw) < 0) { + ILI_ERR("Convert ILI file error\n"); + ret = -EFW_CONVERT_FILE; + goto out; + } + } + + if (ilitek_tddi_fw_update_block_info(pfw) < 0) { + ret = -EFW_CONVERT_FILE; + goto out; + } + + if (ilits->chip->core_ver >= CORE_VER_1470 && ilits->rib.nIsHostDownload == 0) { + ILI_ERR("hex file interface no match error\n"); + return -EFW_INTERFACE; + } + } + + do { + ret = ilitek_tddi_fw_iram_upgrade(pfw); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing iram\n", retry); + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP reset failed while erasing data\n"); + ilits->xch_num = 0; + ilits->ych_num = 0; + return ret; + } + +out: + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ili_ic_func_ctrl_reset(); + return ret; +} + +void ili_fw_read_flash_info(void) +{ + return; +} + +void ili_flash_clear_dma(void) +{ + return; +} + +void ili_flash_dma_write(u32 start, u32 end, u32 len) +{ + return; +} + +int ili_fw_dump_flash_data(u32 start, u32 end, bool user) +{ + return 0; +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_i2c.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_i2c.c new file mode 100755 index 000000000000..6f3b01bd4714 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_i2c.c @@ -0,0 +1,470 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +struct touch_bus_info { + struct i2c_driver bus_driver; + struct ilitek_hwif_info *hwif; +}; + +struct ilitek_ts_data *ilits; + +#define RW_SYNC 0 +#define R_ONLY 1 +#define W_ONLY 2 + +#if I2C_DMA_TRANSFER +static unsigned char *ilitek_dma_va; +static dma_addr_t ilitek_dma_pa; + +#define DMA_VA_BUFFER 4096 + +static int dma_i2c_alloc(struct i2c_client *client) +{ + if (client != NULL) { + client->dev.coherent_dma_mask = DMA_BIT_MASK(32); + ilitek_dma_va = (u8 *)dmam_alloc_coherent(client->dev, DMA_VA_BUFFER, &ilitek_dma_pa, GFP_KERNEL); + if (ERR_ALLOC_MEM(ilitek_dma_va)) { + ILI_ERR("Allocate DMA I2C Buffer failed\n"); + return -ENOMEM; + } + + memset(ilitek_dma_va, 0, DMA_VA_BUFFER); + client->ext_flag |= I2C_DMA_FLAG; + return 0; + } + + ILI_ERR("client is NULL, allocated dma i2c failed\n"); + return -ENODEV; +} +#endif + +static int core_i2c_write(void *buf, int len) +{ + int ret = 0; + u8 *txbuf = (u8 *)buf; + u8 check_sum = 0; + u8 *mpbuf = NULL; + + struct i2c_msg msgs[] = { + { + .addr = ilits->i2c->addr, + .flags = 0, /* write flag. */ + .len = len, + .buf = txbuf, + }, + }; + +#if I2C_DMA_TRANSFER + if (len > 8) { + msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + memcpy(ilitek_dma_va, txbuf, len); + msgs[0].buf = (u8 *)ilitek_dma_pa; + } +#endif + + + /* + * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum + * to the last index and plus 1 with size. + */ + if (atomic_read(&ilits->mp_stat) && txbuf[0] == P5_X_SET_CDC_INIT) { + check_sum = ili_calc_packet_checksum(txbuf, len); + mpbuf = kcalloc(len + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(mpbuf)) { + ILI_ERR("Failed to allocate mpbuf mem\n"); + return -ENOMEM; + } + ipio_memcpy(mpbuf, txbuf, len, msgs[0].len); + mpbuf[len] = check_sum; + msgs[0].buf = mpbuf; + ili_dump_data(mpbuf, 8, len+1, 0, "mp cdc cmd"); + msgs[0].len = len + 1; + } + + if (i2c_transfer(ilits->i2c->adapter, msgs, 1) != 1) + ret = -1; + + ipio_kfree((void **)&mpbuf); + return ret; +} + +static int core_i2c_read(void *buf, int len) +{ + int ret; + u8 *rxbuf = (u8 *)buf; + + struct i2c_msg msgs[] = { + { + .addr = ilits->i2c->addr, + .flags = I2C_M_RD, /* read flag. */ + .len = len, + .buf = rxbuf, + }, + }; + + +#if I2C_DMA_TRANSFER + if (len > 8) { + msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + msgs[0].buf = (u8 *)ilitek_dma_pa; + } else { + msgs[0].buf = rxbuf; + } +#endif + + + ret = i2c_transfer(ilits->i2c->adapter, msgs, 1); + +#if I2C_DMA_TRANSFER + if (len > 8) + memcpy(rxbuf, ilitek_dma_va, len); +#endif + + /* + * If i2c_transfer is ok (must return 1 because only sends one msg), + * return #bytes transferred, else error code. + */ + return (ret == 1) ? len : ret; +} + +static int ilitek_i2c_write(void *buf, int len) +{ + int ret = 0; + + if (len == 0) { + ILI_ERR("i2c write len is invalid\n"); + return -EINVAL; + } + + ret = core_i2c_write(buf, len); + if (ret < 0) { + if (atomic_read(&ilits->tp_reset) == START) { + ret = 0; + goto out; + } + ILI_ERR("i2c write error, ret = %d\n", ret); + } + +out: + return ret; +} + +static int ilitek_i2c_read(void *buf, int len) +{ + int ret = 0; + + if (len == 0) { + ILI_ERR("i2c read len is invalid\n"); + return -EINVAL; + } + + ret = core_i2c_read(buf, len); + if (ret < 0) { + if (atomic_read(&ilits->tp_reset) == START) { + ret = 0; + goto out; + } + ILI_ERR("i2c read error, ret = %d\n", ret); + } + +out: + return ret; +} + +static int ili_i2c_pll_clk_wakeup(void) +{ + int ret = 0; + u8 wakeup = 0xA3; +#if (PLL_CLK_WAKEUP_TP_RESUME == ENABLE) + if (ilits->pll_clk_wakeup == true) { +#else + if ((ilits->pll_clk_wakeup == true) && (ilits->tp_suspend == true)) { +#endif + ret = ilitek_i2c_write(&wakeup, sizeof(wakeup)); + if (ret < 0) + ILI_ERR("i2c write dummy cmd error\n"); + } + return ret; +} + +static int ili_i2c_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq) +{ + int ret = 0, operate = -1; + + if (wlen > 0) { + if (ERR_ALLOC_MEM(txbuf)) { + ILI_ERR("txbuf is null\n"); + return -ENOMEM; + } + } + + if (rlen > 0) { + if (ERR_ALLOC_MEM(rxbuf)) { + ILI_ERR("rxbuf is null\n"); + return -ENOMEM; + } + } + + if (wlen > 0 && rlen > 0) + operate = RW_SYNC; + else if (wlen > 0 && !rlen) + operate = W_ONLY; + else + operate = R_ONLY; + + if (ilits->int_pulse) + ilits->detect_int_stat = ili_ic_check_int_pulse; + else + ilits->detect_int_stat = ili_ic_check_int_level; + + if (i2c_irq) + atomic_set(&ilits->cmd_int_check, ENABLE); + + switch (operate) { + case RW_SYNC: + ret = ilitek_i2c_write(txbuf, wlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper write error\n"); + break; + } + + if (i2c_irq) { + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } else { + msleep(1); + } + + ret = ilitek_i2c_read(rxbuf, rlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper read error\n"); + break; + } + break; + case W_ONLY: + ret = ili_i2c_pll_clk_wakeup(); + if (ret < 0) { + ILI_ERR("i2c-wrapper dummy cmd write error\n"); + } + ret = ilitek_i2c_write(txbuf, wlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper write error\n"); + break; + } + break; + case R_ONLY: + if (i2c_irq) { + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } + + ret = ilitek_i2c_read(rxbuf, rlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper read error\n"); + break; + } + break; + default: + ILI_ERR("Unknown ts-i2c operation\n"); + ret = -EINVAL; + break; + } + + if (i2c_irq) + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +int ili_core_spi_setup(int num) +{ + ILI_ERR("Not support this interface\n"); + return 0; +} + +static int ilitek_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct touch_bus_info *info = + container_of(to_i2c_driver(i2c->dev.driver), + struct touch_bus_info, bus_driver); + + ILI_INFO("ilitek i2c probe\n"); + + if (!i2c) { + ILI_ERR("i2c client is NULL\n"); + return -ENODEV; + } + + if (i2c->addr != TDDI_I2C_ADDR) { + i2c->addr = TDDI_I2C_ADDR; + ILI_INFO("i2c addr doesn't be set up, use default : 0x%x\n", i2c->addr); + } + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + ILI_ERR("i2c functions are not supported!\n"); + return -ENODEV; + } + + ilits = devm_kzalloc(&i2c->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits)) { + ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits)); + return -ENOMEM; + } + + /* Used for receiving touch data only, do not mix up with others. */ + ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC); + if (ERR_ALLOC_MEM(ilits->tr_buf)) { + ILI_ERR("failed to allocate touch report buffer\n"); + return -ENOMEM; + } + + ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->gcoord)) { + ILI_ERR("Failed to allocate gresture coordinate buffer\n"); + return -ENOMEM; + } + + ilits->i2c = i2c; + ilits->spi = NULL; + ilits->dev = &i2c->dev; + ilits->hwif = info->hwif; + ilits->phys = "I2C"; + ilits->wrapper = ili_i2c_wrapper; + ilits->detect_int_stat = ili_ic_check_int_pulse; + ilits->int_pulse = true; + ilits->mp_retry = false; + +#if I2C_DMA_TRANSFER + if (dma_i2c_alloc(ilits->i2c) < 0) + ILI_ERR("Failed to alllocate DMA mem %ld\n", PTR_ERR(ilits->i2c)); +#endif + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + + if (TDDI_RST_BIND) + ilits->reset = TP_IC_WHOLE_RST; + else + ilits->reset = TP_HW_RST_ONLY; + + ilits->rst_edge_delay = 100; + ilits->fw_open = FILP_OPEN; + ilits->fw_upgrade_mode = UPGRADE_FLASH; + ilits->mp_move_code = ili_move_mp_code_flash; + ilits->gesture_move_code = ili_move_gesture_code_flash; + ilits->esd_recover = ili_wq_esd_i2c_check; + ilits->ges_recover = ili_touch_esd_gesture_flash; + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ilits->gesture_demo_ctrl = DISABLE; + ilits->wtd_ctrl = OFF; + ilits->report = ENABLE; + ilits->netlink = DISABLE; + ilits->dnp = DISABLE; + ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING; + ilits->info_from_hex = DISABLE; + ilits->wait_int_timeout = AP_INT_TIMEOUT; + +#if ENABLE_GESTURE + ilits->gesture = DISABLE; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP; + ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT; + ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT; + ilits->ges_sym.alphabet_m = ALPHABET_M; + ilits->ges_sym.alphabet_w = ALPHABET_W; + ilits->ges_sym.alphabet_c = ALPHABET_C; + ilits->ges_sym.alphabet_E = ALPHABET_E; + ilits->ges_sym.alphabet_V = ALPHABET_V; + ilits->ges_sym.alphabet_O = ALPHABET_O; + ilits->ges_sym.alphabet_S = ALPHABET_S; + ilits->ges_sym.alphabet_Z = ALPHABET_Z; + ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN; + ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT; + ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT; + ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_F = ALPHABET_F; + ilits->ges_sym.alphabet_AT = ALPHABET_AT; +#endif + + return info->hwif->plat_probe(); +} + +static int ilitek_i2c_remove(struct i2c_client *i2c) +{ + ILI_INFO(); + return 0; +} + +static const struct i2c_device_id tp_i2c_id[] = { + {TDDI_DEV_ID, 0}, + {}, +}; + +int ili_interface_dev_init(struct ilitek_hwif_info *hwif) +{ + struct touch_bus_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ILI_ERR("faied to allocate i2c_driver\n"); + return -ENOMEM; + } + + if (hwif->bus_type != BUS_I2C) { + ILI_ERR("Not I2C dev\n"); + ipio_kfree((void **)&info); + return -EINVAL; + } + + hwif->info = info; + + info->bus_driver.driver.name = hwif->name; + info->bus_driver.driver.owner = hwif->owner; + info->bus_driver.driver.of_match_table = hwif->of_match_table; + info->bus_driver.driver.pm = hwif->pm; + + info->bus_driver.probe = ilitek_i2c_probe; + info->bus_driver.remove = ilitek_i2c_remove; + info->bus_driver.id_table = tp_i2c_id; + + info->hwif = hwif; + return i2c_add_driver(&info->bus_driver); +} + +void ili_interface_dev_exit(struct ilitek_ts_data *ts) +{ + struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info; + + ILI_INFO("remove i2c dev\n"); + i2c_del_driver(&info->bus_driver); + ipio_kfree((void **)&info); +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_ic.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_ic.c new file mode 100755 index 000000000000..22dd3101e7d8 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_ic.c @@ -0,0 +1,1017 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define PROTOCL_VER_NUM 8 +static struct ilitek_protocol_info protocol_info[PROTOCL_VER_NUM] = { + /* length -> fw, protocol, tp, key, panel, core, func, window, cdc, mp_info */ + [0] = {PROTOCOL_VER_500, 4, 4, 14, 30, 5, 5, 2, 8, 3, 8}, + [1] = {PROTOCOL_VER_510, 4, 3, 14, 30, 5, 5, 3, 8, 3, 8}, + [2] = {PROTOCOL_VER_520, 4, 4, 14, 30, 5, 5, 3, 8, 3, 8}, + [3] = {PROTOCOL_VER_530, 9, 4, 14, 30, 5, 5, 3, 8, 3, 8}, + [4] = {PROTOCOL_VER_540, 9, 4, 14, 30, 5, 5, 3, 8, 15, 8}, + [5] = {PROTOCOL_VER_550, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, + [6] = {PROTOCOL_VER_560, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, + [7] = {PROTOCOL_VER_570, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, +}; + +#define FUNC_CTRL_NUM 16 +static struct ilitek_ic_func_ctrl func_ctrl[FUNC_CTRL_NUM] = { + /* cmd[3] = cmd, func, ctrl */ + // rec_state 0:disable, 1: enable, 2: ignore record + [0] = {"sense", {0x1, 0x1, 0x0}, 3, 0x0, 2, 0xFF}, + [1] = {"sleep", {0x1, 0x2, 0x0}, 3, 0x0, 2, 0xFF}, + [2] = {"glove", {0x1, 0x6, 0x0}, 3, 0x0, 0, 0xFF}, + [3] = {"stylus", {0x1, 0x7, 0x0}, 3, 0x0, 0, 0xFF}, + [4] = {"lpwg", {0x1, 0xA, 0x0}, 3, 0x0, 2, 0xFF}, + [5] = {"proximity", {0x1, 0x10, 0x0}, 3, 0x0, 2, 0xFF}, + [6] = {"plug", {0x1, 0x11, 0x0}, 3, 0x1, 0, 0xFF}, + [7] = {"edge_palm", {0x1, 0x12, 0x0}, 3, 0x1, 0, 0xFF}, + [8] = {"lock_point", {0x1, 0x13, 0x0}, 3, 0x0, 0, 0xFF}, + [9] = {"active", {0x1, 0x14, 0x0}, 3, 0x0, 2, 0xFF}, + [10] = {"idle", {0x1, 0x19, 0x0}, 3, 0x1, 0, 0xFF}, + [11] = {"gesture_demo_en", {0x1, 0x16, 0x0}, 3, 0x0, 0, 0xFF}, + [12] = {"tp_recore", {0x1, 0x18, 0x0}, 3, 0x0, 2, 0xFF}, + [13] = {"knock_en", {0x1, 0xA, 0x8, 0x03, 0x0, 0x0}, 6, 0xFF, 0, 0xFF}, + [14] = {"int_trigger", {0x1, 0x1B, 0x0}, 3, 0x0, 2, 0xFF}, + [15] = {"ear_phone", {0x1, 0x17, 0x0}, 3, 0x0, 0, 0xFF}, +}; + +#define CHIP_SUP_NUM 5 +static u32 ic_sup_list[CHIP_SUP_NUM] = { + [0] = ILI9881_CHIP, + [1] = ILI7807_CHIP, + [2] = ILI9881N_AA, + [3] = ILI9881O_AA, + [4] = ILI9882_CHIP +}; + +static int ilitek_tddi_ic_check_support(u32 pid, u16 id) +{ + int i = 0; + + for (i = 0; i < CHIP_SUP_NUM; i++) { + if ((pid == ic_sup_list[i]) || (id == ic_sup_list[i])) + break; + } + + if (i >= CHIP_SUP_NUM) { + ILI_INFO("ERROR, ILITEK CHIP(0x%x) Not found !!\n", pid); + } + + ILI_INFO("ILITEK CHIP ILI9881x found.\n"); + + ilits->chip->pid = pid; + + ilits->chip->reset_key = 0x00019878; + ilits->chip->wtd_key = 0x9881; + + if (((pid & 0xFFFFFF00) == ILI9881N_AA) || ((pid & 0xFFFFFF00) == ILI9881O_AA)){ + ilits->chip->dma_reset = ENABLE; + } else { + ilits->chip->dma_reset = DISABLE; + } + + ilits->chip->no_bk_shift = RAWDATA_NO_BK_SHIFT; + ilits->chip->max_count = 0x1FFFF; + return 0; +} + +int ili_ice_mode_bit_mask_write(u32 addr, u32 mask, u32 value) +{ + int ret = 0; + u32 data = 0; + + if (ili_ice_mode_read(addr, &data, sizeof(u32)) < 0) { + ILI_ERR("Read data error\n"); + return -1; + } + + data &= (~mask); + data |= (value & mask); + + ILI_DBG("mask value data = %x\n", data); + + ret = ili_ice_mode_write(addr, data, sizeof(u32)); + if (ret < 0) + ILI_ERR("Failed to re-write data in ICE mode, ret = %d\n", ret); + + return ret; +} + +int ili_ice_mode_write(u32 addr, u32 data, int len) +{ + int ret = 0, i; + u8 txbuf[64] = {0}; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode not enabled\n"); + return -1; + } + + txbuf[0] = 0x25; + txbuf[1] = (char)((addr & 0x000000FF) >> 0); + txbuf[2] = (char)((addr & 0x0000FF00) >> 8); + txbuf[3] = (char)((addr & 0x00FF0000) >> 16); + + for (i = 0; i < len; i++) + txbuf[i + 4] = (char)(data >> (8 * i)); + + ret = ilits->wrapper(txbuf, len + 4, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Failed to write data in ice mode, ret = %d\n", ret); + + return ret; +} + +int ili_ice_mode_read(u32 addr, u32 *data, int len) +{ + int ret = 0; + u8 *rxbuf = NULL; + u8 txbuf[4] = {0}; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode not enabled\n"); + return -1; + } + + txbuf[0] = 0x25; + txbuf[1] = (char)((addr & 0x000000FF) >> 0); + txbuf[2] = (char)((addr & 0x0000FF00) >> 8); + txbuf[3] = (char)((addr & 0x00FF0000) >> 16); + + ret = ilits->wrapper(txbuf, sizeof(txbuf), NULL, 0, OFF, OFF); + if (ret < 0) + goto out; + + rxbuf = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(rxbuf)) { + ILI_ERR("Failed to allocate rxbuf, %ld\n", PTR_ERR(rxbuf)); + ret = -ENOMEM; + goto out; + } + + ret = ilits->wrapper(NULL, 0, rxbuf, len, OFF, OFF); + if (ret < 0) + goto out; + + if (len == sizeof(u8)) + *data = rxbuf[0]; + else + *data = (rxbuf[0] | rxbuf[1] << 8 | rxbuf[2] << 16 | rxbuf[3] << 24); + +out: + if (ret < 0) + ILI_ERR("Failed to read data in ice mode, ret = %d\n", ret); + + ipio_kfree((void **)&rxbuf); + return ret; +} + +int ili_ice_mode_ctrl(bool enable, bool mcu) +{ + int ret = 0; + u8 cmd_open[4] = {0x25, 0x62, 0x10, 0x18}; + u8 cmd_close[4] = {0x1B, 0x62, 0x10, 0x18}; + + ILI_INFO("%s ICE mode, mcu on = %d\n", (enable ? "Enable" : "Disable"), mcu); + + if (enable) { + if (atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already enabled\n"); + return 0; + } + + if (mcu) + cmd_open[0] = 0x1F; + + atomic_set(&ilits->ice_stat, ENABLE); + + if (ilits->wrapper(cmd_open, sizeof(cmd_open), NULL, 0, OFF, OFF) < 0){ + ILI_ERR("write ice mode cmd error\n"); + atomic_set(&ilits->ice_stat, DISABLE); + } + ilits->pll_clk_wakeup = false; + +#if (TDDI_INTERFACE == BUS_I2C) + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); /* CS high */ +#endif + } else { + if (!atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already disabled\n"); + return 0; + } + + ret = ilits->wrapper(cmd_close, sizeof(cmd_close), NULL, 0, OFF, OFF); + if (ret < 0){ + ILI_ERR("Exit to ICE Mode failed !!\n"); + atomic_set(&ilits->ice_stat, ENABLE); + } else { + atomic_set(&ilits->ice_stat, DISABLE); + ilits->pll_clk_wakeup = true; + } + } + + return ret; +} + +int ili_ic_func_ctrl(const char *name, int ctrl) +{ + int i = 0, ret; + + for (i = 0; i < FUNC_CTRL_NUM; i++) { + if (ipio_strcmp(name, func_ctrl[i].name) == 0) { + if (strlen(name) != strlen(func_ctrl[i].name)) + continue; + break; + } + } + + if (i >= FUNC_CTRL_NUM) { + ILI_ERR("Not found function ctrl, %s\n", name); + ret = -1; + goto out; + } + + if (ilits->protocol->ver == PROTOCOL_VER_500) { + ILI_ERR("Non support function ctrl with protocol v5.0\n"); + ret = -1; + goto out; + } + + if (ilits->protocol->ver >= PROTOCOL_VER_560) { + if (ipio_strcmp(func_ctrl[i].name, "gesture") == 0 || + ipio_strcmp(func_ctrl[i].name, "phone_cover_window") == 0) { + ILI_INFO("Non support %s function ctrl\n", func_ctrl[i].name); + ret = -1; + goto out; + } + } + + func_ctrl[i].cmd[2] = ctrl; + + ILI_INFO("func = %s, len = %d, cmd = 0x%x, 0%x, 0x%x\n", func_ctrl[i].name, func_ctrl[i].len, + func_ctrl[i].cmd[0], func_ctrl[i].cmd[1], func_ctrl[i].cmd[2]); + + ret = ilits->wrapper(func_ctrl[i].cmd, func_ctrl[i].len, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Write TP function failed\n"); + + if (func_ctrl[i].rec_state < 2) { + if (ctrl == func_ctrl[i].def_cmd) + func_ctrl[i].rec_state = DISABLE; + else + func_ctrl[i].rec_state = ENABLE; + + func_ctrl[i].rec_cmd = ctrl; + } + + ILI_DBG("record %s func cmd %d, rec_state %d\n", func_ctrl[i].name, func_ctrl[i].rec_cmd, func_ctrl[i].rec_state); +out: + return ret; +} + +void ili_ic_func_ctrl_reset(void) { + int i = 0; + for (i = 0; i < FUNC_CTRL_NUM; i++) { + if (func_ctrl[i].rec_state == ENABLE) { + ILI_DBG("reset func ctrl %s, record status = %d, cmd = %d\n", func_ctrl[i].name, + func_ctrl[i].rec_state, func_ctrl[i].rec_cmd); + if (ili_ic_func_ctrl(func_ctrl[i].name, func_ctrl[i].rec_cmd) < 0) + ILI_ERR("reset ic func ctrl %s failed\n", func_ctrl[i].name); + } + } +} + +int ili_ic_code_reset(void) +{ + int ret; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before code reset\n"); + + ret = ili_ice_mode_write(0x40040, 0xAE, 1); + if (ret < 0) + ILI_ERR("ic code reset failed\n"); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed after code reset\n"); + return ret; +} + +int ili_ic_whole_reset(void) +{ + int ret = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before chip reset\n"); + + ILI_INFO("ic whole reset key = 0x%x, edge_delay = %d\n", + ilits->chip->reset_key, ilits->rst_edge_delay); + + ret = ili_ice_mode_write(ilits->chip->reset_addr, ilits->chip->reset_key, sizeof(u32)); + if (ret < 0) { + ILI_ERR("ic whole reset failed\n"); + goto out; + } + + /* Need accurate power sequence, do not change it to msleep */ + mdelay(ilits->rst_edge_delay); + +out: + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed after chip reset\n"); + + return ret; +} + +static void ilitek_tddi_ic_wr_pack(int packet) +{ + int retry = 100; + u32 reg_data = 0; + + while (retry--) { + if (ili_ice_mode_read(0x73010, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73010 error\n"); + + if ((reg_data & 0x02) == 0) { + ILI_INFO("check ok 0x73010 read 0x%X retry = %d\n", reg_data, retry); + break; + } + mdelay(10); + } + + if (retry <= 0) + ILI_INFO("check 0x73010 error read 0x%X\n", reg_data); + + if (ili_ice_mode_write(0x73000, packet, 4) < 0) + ILI_ERR("Write %x at 0x73000\n", packet); +} + +static u32 ilitek_tddi_ic_rd_pack(int packet) +{ + int retry = 100; + u32 reg_data = 0; + + ilitek_tddi_ic_wr_pack(packet); + + while (retry--) { + if (ili_ice_mode_read(0x4800A, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x4800A error\n"); + + if ((reg_data & 0x02) == 0x02) { + ILI_INFO("check ok 0x4800A read 0x%X retry = %d\n", reg_data, retry); + break; + } + mdelay(10); + } + if (retry <= 0) + ILI_INFO("check 0x4800A error read 0x%X\n", reg_data); + + if (ili_ice_mode_write(0x4800A, 0x02, 1) < 0) + ILI_ERR("Write 0x2 at 0x4800A\n"); + + if (ili_ice_mode_read(0x73016, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73016 error\n"); + + return reg_data; +} + +void ili_ic_set_ddi_reg_onepage(u8 page, u8 reg, u8 data) +{ + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x1F000100 | (reg << 16) | data; + bool ice = atomic_read(&ilits->ice_stat); + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before writing ddi reg\n"); + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9527); + /*Switch to Page*/ + ilitek_tddi_ic_wr_pack(setpage); + /* Page*/ + ilitek_tddi_ic_wr_pack(setreg); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9500); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed after writing ddi reg\n"); +} + +void ili_ic_get_ddi_reg_onepage(u8 page, u8 reg, u8 *data) +{ + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x2F000100 | (reg << 16); + bool ice = atomic_read(&ilits->ice_stat); + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before reading ddi reg\n"); + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9527); + /*Set Read Page reg*/ + ilitek_tddi_ic_wr_pack(setpage); + + /*TDI_RD_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9487); + /*( *( __IO uint8 *) (0x4800A) ) =0x2*/ + if (ili_ice_mode_write(0x4800A, 0x02, 1) < 0) + ILI_ERR("Write 0x2 at 0x4800A\n"); + + *data = ilitek_tddi_ic_rd_pack(setreg); + ILI_INFO("check page = 0x%X, reg = 0x%X, read 0x%X\n", page, reg, *data); + + /*TDI_RD_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9400); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9500); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed after reading ddi reg\n"); +} + + +void ili_ic_get_pc_counter(int stat) +{ + bool ice = atomic_read(&ilits->ice_stat); + u32 pc = 0, pc_addr = ilits->chip->pc_counter_addr; + u32 latch = 0, latch_addr = ilits->chip->pc_latch_addr; + int ret = 0; + + ILI_DBG("stat = %d\n", stat); + + if (!ice) { + if (stat == DO_SPI_RECOVER || stat == DO_I2C_RECOVER) + ret = ili_ice_mode_ctrl(ENABLE, OFF); + else + ret = ili_ice_mode_ctrl(ENABLE, ON); + + if (ret < 0) + ILI_ERR("Enable ice mode failed while reading pc counter\n"); + } + + if (ili_ice_mode_read(ilits->chip->pc_counter_addr, &pc, sizeof(u32)) < 0) + ILI_ERR("Read pc conter error\n"); + + if (ili_ice_mode_read(ilits->chip->pc_latch_addr, &latch, sizeof(u32)) < 0) + ILI_ERR("Read pc latch error\n"); + + ilits->fw_pc = pc; + ilits->fw_latch = latch; + ILI_ERR("Read counter (addr: 0x%x) = 0x%x, latch (addr: 0x%x) = 0x%x\n", + pc_addr, ilits->fw_pc, latch_addr, ilits->fw_latch); + + /* Avoid screen abnormal. */ + if (stat == DO_SPI_RECOVER) { + atomic_set(&ilits->ice_stat, DISABLE); + return; + } + + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, ON) < 0) + ILI_ERR("Disable ice mode failed while reading pc counter\n"); + } +} + +int ili_ic_int_trigger_ctrl(bool pulse) +{ + /* It's supported by fw, and the level will be kept at high until data was already prepared. */ + if (ili_ic_func_ctrl("int_trigger", pulse) < 0) { + ILI_ERR("Write CMD error, set back to <%s> trigger\n", ilits->int_pulse ? "Level" : "Pulse"); + return -1; + } + + ilits->int_pulse = pulse; + ILI_INFO("INT Trigger = %s\n", ilits->int_pulse ? "Level" : "Pulse"); + return 0; +} + +int ili_ic_check_int_level(bool level) +{ + int timer = 3000; + int gpio = IRQ_GPIO_NUM; //ilits->tp_int + + /* + * If callers have a trouble to use the gpio that is passed by vendors, + * please utlises a physical gpio number instead or call a help from them. + */ + + while (--timer > 0) { + if(level) { + if (gpio_get_value(gpio)) { + ILI_DBG("INT high detected.\n"); + return 0; + } + } else { + if (!gpio_get_value(gpio)) { + ILI_DBG("INT low detected.\n");; + return 0; + } + } + mdelay(1); + } + ILI_ERR("Error! INT level no detected.\n"); + return -1; +} + +int ili_ic_check_int_pulse(bool pulse) +{ + if (!wait_event_interruptible_timeout(ilits->inq, !atomic_read(&ilits->cmd_int_check), msecs_to_jiffies(ilits->wait_int_timeout))) { + ILI_ERR("Error! INT pulse no detected. Timeout = %d ms\n", ilits->wait_int_timeout); + atomic_set(&ilits->cmd_int_check, DISABLE); + return -1; + } + ILI_DBG("INT pulse detected.\n"); + return 0; +} + +int ili_ic_check_busy(int count, int delay) +{ + u8 cmd[2] = {0}; + u8 busy = 0, rby = 0; + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_CDC_BUSY_STATE; + + if (ilits->actual_tp_mode == P5_X_FW_AP_MODE) + rby = 0x41; + else if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) + rby = 0x51; + else { + ILI_ERR("Unknown TP mode (0x%x)\n", ilits->actual_tp_mode); + return -EINVAL; + } + + ILI_INFO("read byte = %x, delay = %d\n", rby, delay); + + do { + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) + ILI_ERR("Write check busy cmd failed\n"); + + if (ilits->wrapper(&cmd[1], sizeof(u8), &busy, sizeof(u8), ON, OFF) < 0) + ILI_ERR("Read check busy failed\n"); + + ILI_DBG("busy = 0x%x\n", busy); + + if (busy == rby) { + ILI_INFO("Check busy free\n"); + return 0; + } + + mdelay(delay); + } while (--count > 0); + + ILI_ERR("Check busy (0x%x) timeout !\n", busy); + ili_ic_get_pc_counter(0); + return -1; +} + +int ili_ic_get_project_id(u8 *pdata, int size) +{ + int i; + u32 tmp; + bool ice = atomic_read(&ilits->ice_stat); + + if (!pdata) { + ILI_ERR("pdata is null\n"); + return -ENOMEM; + } + + ILI_INFO("Read size = %d\n", size); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while reading project id\n"); + + if (ili_ice_mode_write(0x041000, 0x0, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + if (ili_ice_mode_write(0x041004, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(0x041008, 0x03, 1) < 0) + ILI_ERR("Write 0x03 at 0x041008\n"); + + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + for (i = 0; i < size; i++) { + if (ili_ice_mode_write(0x041008, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + if (ili_ice_mode_read(0x41010, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read project id error\n"); + pdata[i] = tmp; + ILI_INFO("project_id[%d] = 0x%x\n", i, pdata[i]); + } + + ili_flash_clear_dma(); + + if (ili_ice_mode_write(0x041000, 0x1, 1) < 0) + ILI_ERR("Pull cs high\n"); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed while reading project id\n"); + + return 0; +} + +int ili_ic_get_core_ver(void) +{ + int i = 0, ret = 0; + u8 cmd[2] = {0}, buf[10] = {0}; + + ilits->protocol->core_ver_len = P5_X_CORE_VER_FOUR_LENGTH; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[68]; + buf[2] = ilits->fw_info[69]; + buf[3] = ilits->fw_info[70]; + buf[4] = ilits->fw_info[71]; + goto out; + } + + do { + if (i == 0) { + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_CORE_VERSION_NEW; + } else { + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_CORE_VERSION; + ilits->protocol->core_ver_len = P5_X_CORE_VER_THREE_LENGTH; + } + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) + ILI_ERR("Write core ver cmd failed\n"); + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->core_ver_len, ON, OFF) < 0) + ILI_ERR("Write core ver (0x%x) failed\n", cmd[1]); + + ILI_DBG("header = 0x%x\n", buf[0]); + + if (buf[0] == P5_X_GET_CORE_VERSION || + buf[0] == P5_X_GET_CORE_VERSION_NEW) + break; + + } while (++i < 2); + + if (buf[0] == P5_X_GET_CORE_VERSION) + buf[4] = 0; + + if (i >= 2) { + ILI_ERR("Invalid header (0x%x)\n", buf[0]); + ret = -EINVAL; + } + +out: + ILI_INFO("Core version = %d.%d.%d.%d\n", buf[1], buf[2], buf[3], buf[4]); + ilits->chip->core_ver = buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | buf[4]; + return ret; +} + +void ili_fw_uart_ctrl(u8 ctrl) +{ + u8 cmd[4] = {0}; + + if (ctrl > 1) { + ILI_INFO("Unknown cmd, ignore\n"); + return; + } + + ILI_INFO("%s UART mode\n", ctrl ? "Enable" : "Disable"); + + cmd[0] = P5_X_I2C_UART; + cmd[1] = 0x3; + cmd[2] = 0; + cmd[3] = ctrl; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_INFO("Write fw uart cmd failed\n"); + return; + } +} + +int ili_ic_get_fw_ver(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[10] = {0}; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[48]; + buf[2] = ilits->fw_info[49]; + buf[3] = ilits->fw_info[50]; + buf[4] = ilits->fw_info[51]; + buf[5] = ilits->fw_mp_ver[0]; + buf[6] = ilits->fw_mp_ver[1]; + buf[7] = ilits->fw_mp_ver[2]; + buf[8] = ilits->fw_mp_ver[3]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_FW_VERSION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->fw_ver_len, ON, OFF)) { + ILI_ERR("Write fw version cmd failed\n"); + ret = -EINVAL; + goto out; + } + + if (buf[0] != P5_X_GET_FW_VERSION) { + ILI_ERR("Invalid firmware ver\n"); + ret = -1; + } + +out: + ILI_INFO("Firmware version = %d.%d.%d.%d\n", buf[1], buf[2], buf[3], buf[4]); + ILI_INFO("Firmware MP version = %d.%d.%d.%d\n", buf[5], buf[6], buf[7], buf[8]); + ilits->chip->fw_ver = buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | buf[4]; + ilits->chip->fw_mp_ver = buf[5] << 24 | buf[6] << 16 | buf[7] << 8 | buf[8]; + return ret; +} + +int ili_ic_get_panel_info(void) +{ + int ret = 0; + u8 cmd = P5_X_GET_PANEL_INFORMATION; + u8 buf[10] = {0}; + u8 len = ilits->protocol->panel_info_len; + + if (ilits->info_from_hex && (ilits->chip->core_ver >= CORE_VER_1410)) { + buf[1] = ilits->fw_info[16]; + buf[2] = ilits->fw_info[17]; + buf[3] = ilits->fw_info[18]; + buf[4] = ilits->fw_info[19]; + ilits->panel_wid = buf[2] << 8 | buf[1]; + ilits->panel_hei = buf[4] << 8 | buf[3]; + ilits->trans_xy = (ilits->chip->core_ver >= CORE_VER_1430 + && (ilits->rib.nReportByPixel > 0)) ? ON : OFF; + goto out; + } + + len = (ilits->chip->core_ver >= CORE_VER_1430) ? 6 : len; + + ret = ilits->wrapper(&cmd, sizeof(cmd), buf, len, ON, OFF); + if (ret < 0) + ILI_ERR("Read panel info error\n"); + + if (buf[0] != cmd) { + ILI_INFO("Invalid panel info, use default resolution\n"); + ilits->panel_wid = TOUCH_SCREEN_X_MAX; + ilits->panel_hei = TOUCH_SCREEN_Y_MAX; + ilits->trans_xy = OFF; + } else { + ilits->panel_wid = buf[1] << 8 | buf[2]; + ilits->panel_hei = buf[3] << 8 | buf[4]; + ilits->trans_xy = (ilits->chip->core_ver >= CORE_VER_1430) ? buf[5] : OFF; + } + +out: + ILI_INFO("Panel info: width = %d, height = %d\n", ilits->panel_wid, ilits->panel_hei); + ILI_INFO("Transfer touch coordinate = %s\n", ilits->trans_xy ? "ON" : "OFF"); + return ret; +} + +int ili_ic_get_tp_info(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[20] = {0}; + + if (ilits->info_from_hex && (ilits->chip->core_ver >= CORE_VER_1410)) { + buf[1] = ilits->fw_info[5]; + buf[2] = ilits->fw_info[7]; + buf[3] = ilits->fw_info[8]; + buf[4] = ilits->fw_info[9]; + buf[5] = ilits->fw_info[10]; + buf[6] = ilits->fw_info[11]; + buf[7] = ilits->fw_info[12]; + buf[8] = ilits->fw_info[14]; + buf[11] = buf[7]; + buf[12] = buf[8]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_TP_INFORMATION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write tp info pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + ret = ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->tp_info_len, ON, OFF); + if (ret < 0) { + ILI_ERR("Read tp info error\n"); + goto out; + } + + if (buf[0] != P5_X_GET_TP_INFORMATION) { + ILI_ERR("Invalid tp info\n"); + ret = -1; + goto out; + } + +out: + ilits->min_x = buf[1]; + ilits->min_y = buf[2]; + ilits->max_x = buf[4] << 8 | buf[3]; + ilits->max_y = buf[6] << 8 | buf[5]; + ilits->xch_num = buf[7]; + ilits->ych_num = buf[8]; + ilits->stx = buf[11]; + ilits->srx = buf[12]; + + ILI_INFO("TP Info: min_x = %d, min_y = %d, max_x = %d, max_y = %d\n", ilits->min_x, ilits->min_y, ilits->max_x, ilits->max_y); + ILI_INFO("TP Info: xch = %d, ych = %d, stx = %d, srx = %d\n", ilits->xch_num, ilits->ych_num, ilits->stx, ilits->srx); + return ret; +} + +static void ilitek_tddi_ic_check_protocol_ver(u32 pver) +{ + int i = 0; + + if (ilits->protocol->ver == pver) { + ILI_DBG("same procotol version, do nothing\n"); + return; + } + + for (i = 0; i < PROTOCL_VER_NUM - 1; i++) { + if (protocol_info[i].ver == pver) { + ilits->protocol = &protocol_info[i]; + ILI_INFO("update protocol version = %x\n", ilits->protocol->ver); + return; + } + } + + ILI_ERR("Not found a correct protocol version in list, use newest version\n"); + ilits->protocol = &protocol_info[PROTOCL_VER_NUM - 1]; +} + +int ili_ic_get_protocl_ver(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[10] = {0}; + u32 ver; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[72]; + buf[2] = ilits->fw_info[73]; + buf[3] = ilits->fw_info[74]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_PROTOCOL_VERSION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write protocol ver pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->pro_ver_len, ON, OFF)) { + ILI_ERR("Read protocol version error\n"); + ret = -EINVAL; + goto out; + } + + if (buf[0] != P5_X_GET_PROTOCOL_VERSION) { + ILI_ERR("Invalid protocol ver\n"); + ret = -1; + goto out; + } + +out: + ver = buf[1] << 16 | buf[2] << 8 | buf[3]; + + ilitek_tddi_ic_check_protocol_ver(ver); + + ILI_INFO("Protocol version = %d.%d.%d\n", ilits->protocol->ver >> 16, + (ilits->protocol->ver >> 8) & 0xFF, ilits->protocol->ver & 0xFF); + return ret; +} + +int ili_ic_get_info(void) +{ + int ret = 0; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode doesn't enable\n"); + return -1; + } + + + if (ili_ice_mode_read(ilits->chip->pid_addr, &ilits->chip->pid, sizeof(u32)) < 0) + ILI_ERR("Read chip pid error\n"); + + if (ili_ice_mode_read(ilits->chip->otp_addr, &ilits->chip->otp_id, sizeof(u32)) < 0) + ILI_ERR("Read otp id error\n"); + if (ili_ice_mode_read(ilits->chip->ana_addr, &ilits->chip->ana_id, sizeof(u32)) < 0) + ILI_ERR("Read ana id error\n"); + + ilits->chip->pid = ilits->chip->pid; + ilits->chip->id = ilits->chip->pid >> 16; + ilits->chip->type = (ilits->chip->pid & 0x0000FF00) >> 8; + ilits->chip->ver = ilits->chip->pid & 0xFF; + ilits->chip->otp_id &= 0xFF; + ilits->chip->ana_id &= 0xFF; + + ILI_INFO("CHIP: PID = %x\n",(ilits->chip->pid >> 8)); + + ret = ilitek_tddi_ic_check_support(ilits->chip->pid, ilits->chip->id); + return ret; +} + +int ili_ic_dummy_check(void) +{ + int ret = 0; + u32 wdata = 0xA55A5AA5; + u32 rdata = 0; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode doesn't enable\n"); + return -1; + } + + if (ili_ice_mode_write(WDT9_DUMMY2, wdata, sizeof(u32)) < 0) + ILI_ERR("Write dummy error\n"); + + + if (ili_ice_mode_read(WDT9_DUMMY2, &rdata, sizeof(u32)) < 0) + ILI_ERR("Read dummy error\n"); + + if (rdata != wdata){ + ILI_ERR("Dummy check incorrect, rdata = %x wdata = %x \n", rdata, wdata); + return -1; + } + ILI_INFO("Ilitek IC check successe\n"); + + return ret; +} + +static struct ilitek_ic_info chip; + +void ili_ic_init(void) +{ + chip.pid_addr = TDDI_PID_ADDR; + chip.pc_counter_addr = TDDI_PC_COUNTER_ADDR; + chip.pc_latch_addr = TDDI_PC_LATCH_ADDR; + chip.otp_addr = TDDI_OTP_ID_ADDR; + chip.ana_addr = TDDI_ANA_ID_ADDR; + chip.reset_addr = TDDI_CHIP_RESET_ADDR; + + ilits->protocol = &protocol_info[PROTOCL_VER_NUM - 1]; + ilits->chip = &chip; +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_mp.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_mp.c new file mode 100755 index 000000000000..00392f893f63 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_mp.c @@ -0,0 +1,3822 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define VALUE 0 +#define RETRY_COUNT 3 +#define INT_CHECK 0 +#define POLL_CHECK 1 + +#define BENCHMARK 1 +#define NODETYPE 1 + +#define TYPE_BENCHMARK 0 +#define TYPE_NO_JUGE 1 +#define TYPE_JUGE 2 + +#define NORMAL_CSV_PASS_NAME "mp_pass" +#define NORMAL_CSV_FAIL_NAME "mp_fail" +#define NORMAL_CSV_WARNING_NAME "mp_warning" + +#define CSV_FILE_SIZE (1 * M) + +#define PARSER_MAX_CFG_BUF (512 * 3) +#define PARSER_MAX_KEY_NUM (600 * 3) +#define PARSER_MAX_KEY_NAME_LEN 100 +#define PARSER_MAX_KEY_VALUE_LEN 2000 +#define MAX_SECTION_NUM 100 +#define BENCHMARK_KEY_NAME "benchmark_data" +#define NODE_TYPE_KEY_NAME "node type" +#define INI_ERR_OUT_OF_LINE -1 + +#define CMD_MUTUAL_DAC 0x1 +#define CMD_MUTUAL_BG 0x2 +#define CMD_MUTUAL_SIGNAL 0x3 +#define CMD_MUTUAL_NO_BK 0x5 +#define CMD_MUTUAL_HAVE_BK 0x8 +#define CMD_MUTUAL_BK_DAC 0x10 +#define CMD_SELF_DAC 0xC +#define CMD_SELF_BG 0xF +#define CMD_SELF_SIGNAL 0xD +#define CMD_SELF_NO_BK 0xE +#define CMD_SELF_HAVE_BK 0xB +#define CMD_SELF_BK_DAC 0x11 +#define CMD_KEY_DAC 0x14 +#define CMD_KEY_BG 0x16 +#define CMD_KEY_NO_BK 0x7 +#define CMD_KEY_HAVE_BK 0x15 +#define CMD_KEY_OPEN 0x12 +#define CMD_KEY_SHORT 0x13 +#define CMD_ST_DAC 0x1A +#define CMD_ST_BG 0x1C +#define CMD_ST_NO_BK 0x17 +#define CMD_ST_HAVE_BK 0x1B +#define CMD_ST_OPEN 0x18 +#define CMD_TX_SHORT 0x19 +#define CMD_RX_SHORT 0x4 +#define CMD_RX_OPEN 0x6 +#define CMD_TX_RX_DELTA 0x1E +#define CMD_CM_DATA 0x9 +#define CMD_CS_DATA 0xA +#define CMD_TRCRQ_PIN 0x20 +#define CMD_RESX2_PIN 0x21 +#define CMD_MUTUAL_INTEGRA_TIME 0x22 +#define CMD_SELF_INTEGRA_TIME 0x23 +#define CMD_KEY_INTERGRA_TIME 0x24 +#define CMD_ST_INTERGRA_TIME 0x25 +#define CMD_PEAK_TO_PEAK 0x1D +#define CMD_GET_TIMING_INFO 0x30 +#define CMD_DOZE_P2P 0x32 +#define CMD_DOZE_RAW 0x33 +#define CMD_PIN_TEST 0x61 + +#define MP_DATA_PASS 0 +#define MP_DATA_FAIL -1 + +#define Mathabs(x) ({ \ + long ret; \ + if (sizeof(x) == sizeof(long)) { \ + long __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } else { \ + int __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } \ + ret; \ + }) + +#define DUMP(fmt, arg...) \ + do { \ + if (debug_en) \ + pr_cont(fmt, ##arg); \ + } while (0) + +static struct ini_file_data { + char section_name[PARSER_MAX_KEY_NAME_LEN]; + char key_name[PARSER_MAX_KEY_NAME_LEN]; + char key_value[PARSER_MAX_KEY_VALUE_LEN]; + int section_len; + int key_name_len; + int key_val_len; +} *ini_info; + +enum open_test_node_type { + NO_COMPARE = 0x00, /* Not A Area, No Compare */ + AA_Area = 0x01, /* AA Area, Compare using Charge_AA */ + Border_Area = 0x02, /* Border Area, Compare using Charge_Border */ + Notch = 0x04, /* Notch Area, Compare using Charge_Notch */ + Round_Corner = 0x08, /* Round Corner, No Compare */ + Skip_Micro = 0x10 /* Skip_Micro, No Compare */ +}; + +enum mp_test_catalog { + MUTUAL_TEST = 0, + SELF_TEST = 1, + KEY_TEST = 2, + ST_TEST = 3, + TX_RX_DELTA = 4, + UNTOUCH_P2P = 5, + PIXEL = 6, + OPEN_TEST = 7, + PEAK_TO_PEAK_TEST = 8, + SHORT_TEST = 9, + PIN_TEST = 10, +}; + +struct mp_test_P540_open { + s32 *tdf_700; + s32 *tdf_250; + s32 *tdf_200; + s32 *cbk_700; + s32 *cbk_250; + s32 *cbk_200; + s32 *charg_rate; + s32 *full_Open; + s32 *dac; +}; + +struct mp_test_open_c { + s32 *cap_dac; + s32 *cap_raw; + s32 *dcl_cap; +}; + +struct open_test_para { + int tvch; + int tvcl; + int gain; + int cbk_step; + int cint; +} open_para; + +struct shor_test_para { + int tvch; + int tvcl; + int variation; + int rinternal; + int cint; +} short_para; + +static struct core_mp_test_data { + u32 chip_pid; + u16 chip_id; + u8 chip_type; + u8 chip_ver; + u32 fw_ver; + u32 protocol_ver; + u32 core_ver; + char ini_date[128]; + char ini_ver[64]; + int no_bk_shift; + bool retry; + bool m_signal; + bool m_dac; + bool s_signal; + bool s_dac; + bool key_dac; + bool st_dac; + bool p_no_bk; + bool p_has_bk; + bool open_integ; + bool open_cap; + bool isLongV; + bool td_retry; + bool all_pass; + + int cdc_len; + int xch_len; + int ych_len; + int stx_len; + int srx_len; + int key_len; + int st_len; + int frame_len; + int mp_items; + int final_result; + int short_varia; + + u32 overlay_start_addr; + u32 overlay_end_addr; + u32 mp_flash_addr; + u32 mp_size; + u8 dma_trigger_enable; + + /* Tx/Rx threshold & buffer */ + int TxDeltaMax; + int TxDeltaMin; + int RxDeltaMax; + int RxDeltaMin; + s32 *tx_delta_buf; + s32 *rx_delta_buf; + s32 *tx_max_buf; + s32 *tx_min_buf; + s32 *rx_max_buf; + s32 *rx_min_buf; + + int tdf; + int busy_cdc; + bool ctrl_lcm; + bool lost_benchmark; + bool lost_parameter; +} core_mp = {0}; + +struct mp_test_items { + /* The description must be the same as ini's section name */ + char *desp; + char *result; + int catalog; + u8 cmd; + u8 spec_option; + u8 type_option; + bool run; + bool lcm; + int bch_mrk_multi; + int max; + int max_res; + int item_result; + int min; + int min_res; + int frame_count; + int trimmed_mean; + int lowest_percentage; + int highest_percentage; + int v_tdf_1; + int v_tdf_2; + int h_tdf_1; + int h_tdf_2; + int goldenmode; + int max_min_mode; + int bch_mrk_frm_num; + int retry_cnt; + u8 delay_time; + u8 test_int_pin; + u8 int_pulse_test; + s32 *result_buf; + s32 *buf; + s32 *max_buf; + s32 *min_buf; + s32 *bench_mark_max; + s32 *bench_mark_min; + s32 **bch_mrk_max; + s32 **bch_mrk_min; + s32 *node_type; + int (*do_test)(int index); +}; + +#define MP_TEST_ITEM 50 +static struct mp_test_items tItems[MP_TEST_ITEM] = { + {.desp = "baseline data(bg)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_BG, .lcm = ON}, + {.desp = "untouch signal data(bg-raw-4096) - mutual", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_SIGNAL, .lcm = ON}, + {.desp = "manual bk data(mutual)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_BK_DAC, .lcm = ON}, + {.desp = "calibration data(dac) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_DAC, .lcm = ON}, + {.desp = "baselin data(bg,self_tx,self_r)", .catalog = SELF_TEST, .cmd = CMD_SELF_BG, .lcm = ON}, + {.desp = "untouch signal data(bg-raw-4096) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_SIGNAL, .lcm = ON}, + {.desp = "raw data(no bk) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_NO_BK, .lcm = ON}, + {.desp = "raw data(have bk) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_HAVE_BK, .lcm = ON}, + {.desp = "manual bk dac data(self_tx,self_rx)", .catalog = SELF_TEST, .cmd = CMD_SELF_BK_DAC, .lcm = ON}, + {.desp = "calibration data(dac/icon)", .catalog = KEY_TEST, .cmd = CMD_KEY_DAC, .lcm = ON}, + {.desp = "key baseline data", .catalog = KEY_TEST, .cmd = CMD_KEY_BG, .lcm = ON}, + {.desp = "key raw data", .catalog = KEY_TEST, .cmd = CMD_KEY_NO_BK, .lcm = ON}, + {.desp = "key raw bk dac", .catalog = KEY_TEST, .cmd = CMD_KEY_HAVE_BK, .lcm = ON}, + {.desp = "key raw open test", .catalog = KEY_TEST, .cmd = CMD_KEY_OPEN, .lcm = ON}, + {.desp = "key raw short test", .catalog = KEY_TEST, .cmd = CMD_KEY_SHORT, .lcm = ON}, + {.desp = "st calibration data(dac)", .catalog = ST_TEST, .cmd = CMD_ST_DAC, .lcm = ON}, + {.desp = "st baseline data(bg)", .catalog = ST_TEST, .cmd = CMD_ST_BG, .lcm = ON}, + {.desp = "st raw data(no bk)", .catalog = ST_TEST, .cmd = CMD_ST_NO_BK, .lcm = ON}, + {.desp = "st raw(have bk)", .catalog = ST_TEST, .cmd = CMD_ST_HAVE_BK, .lcm = ON}, + {.desp = "st open data", .catalog = ST_TEST, .cmd = CMD_ST_OPEN, .lcm = ON}, + {.desp = "tx short test", .catalog = MUTUAL_TEST, .cmd = CMD_TX_SHORT, .lcm = ON}, + {.desp = "rx open", .catalog = MUTUAL_TEST, .cmd = CMD_RX_OPEN, .lcm = ON}, + {.desp = "untouch cm data", .catalog = MUTUAL_TEST, .cmd = CMD_CM_DATA, .lcm = ON}, + {.desp = "untouch cs data", .catalog = MUTUAL_TEST, .cmd = CMD_CS_DATA, .lcm = ON}, + {.desp = "tx/rx delta", .catalog = TX_RX_DELTA, .cmd = CMD_TX_RX_DELTA, .lcm = ON}, + {.desp = "untouch peak to peak", .catalog = UNTOUCH_P2P, .cmd = CMD_MUTUAL_SIGNAL, .lcm = ON}, + {.desp = "pixel raw (no bk)", .catalog = PIXEL, .cmd = CMD_MUTUAL_NO_BK, .lcm = ON}, + {.desp = "pixel raw (have bk)", .catalog = PIXEL, .cmd = CMD_MUTUAL_HAVE_BK, .lcm = ON}, + {.desp = "noise peak to peak(cut panel)", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "open test(integration)", .catalog = OPEN_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + {.desp = "open test(cap)", .catalog = OPEN_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + /* Following is the new test items for protocol 5.4.0 above */ + {.desp = "pin test ( int and rst )", .catalog = PIN_TEST, .cmd = CMD_PIN_TEST, .lcm = ON}, + {.desp = "noise peak to peak(with panel)", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "noise peak to peak(ic only)", .catalog = PEAK_TO_PEAK_TEST, .cmd = CMD_PEAK_TO_PEAK, .lcm = ON}, + {.desp = "open test(integration)_sp", .catalog = OPEN_TEST, .lcm = ON}, + {.desp = "raw data(no bk)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_NO_BK, .lcm = ON}, + {.desp = "raw data(have bk)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_HAVE_BK, .lcm = ON}, + {.desp = "calibration data(dac)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_DAC, .lcm = ON}, + {.desp = "short test -ili9881", .catalog = SHORT_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + {.desp = "short test", .catalog = SHORT_TEST, .lcm = ON}, + {.desp = "doze raw data", .catalog = MUTUAL_TEST, .lcm = ON}, + {.desp = "doze peak to peak", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "open test_c", .catalog = OPEN_TEST, .lcm = ON}, + {.desp = "touch deltac", .catalog = MUTUAL_TEST, .lcm = ON}, + /* LCM OFF TEST */ + {.desp = "raw data(have bk) (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "raw data(no bk) (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "noise peak to peak(with panel) (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, + {.desp = "noise peak to peak(ic only) (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, + {.desp = "raw data_td (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "peak to peak_td (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, +}; + +static struct run_index { + int count; + int index[MP_TEST_ITEM]; +} ri; + +static s32 *frame_buf; +static s32 **frm_buf; +static s32 *key_buf; +static s32 *frame1_cbk700, *frame1_cbk250, *frame1_cbk200; +static s32 *cap_dac, *cap_raw; +static int g_ini_items = 0; +static char csv_name[128] = {0}; +static char seq_item[MAX_SECTION_NUM][PARSER_MAX_KEY_NAME_LEN] = {{0}}; + +static int isspace_t(int x) +{ + if (x == ' ' || x == '\t' || x == '\n' || + x == '\f' || x == '\b' || x == '\r') + return 1; + else + return 0; +} + +static void dump_benchmark_data(s32 *max_ptr, s32 *min_ptr) +{ + ili_dump_data(max_ptr, 32, core_mp.frame_len, 0, "Dump Benchmark Max : "); + ili_dump_data(min_ptr, 32, core_mp.frame_len, 0, "Dump Benchmark Min : "); +} + +static void dump_node_type_buffer(s32 *node_ptr, u8 *name) +{ + ili_dump_data(node_ptr, 32, core_mp.frame_len, 0, "Dump NodeType : "); +} + +static int parser_get_ini_key_value(char *section, char *key, char *value) +{ + int i = 0; + int ret = -2; + + for (i = 0; i < g_ini_items; i++) { + if (ipio_strcmp(section, ini_info[i].section_name) != 0) + continue; + + if (ipio_strcmp(key, ini_info[i].key_name) == 0) { + ipio_memcpy(value, ini_info[i].key_value, ini_info[i].key_val_len, PARSER_MAX_KEY_VALUE_LEN); + ILI_DBG("(key: %s, value:%s) => (ini key: %s, val: %s)\n", + key, + value, + ini_info[i].key_name, + ini_info[i].key_value); + ret = 0; + break; + } + } + return ret; +} + +static void parser_ini_nodetype(s32 *type_ptr, char *desp, int frame_len) +{ + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = {0}, record = ','; + + for (i = 0; i < g_ini_items; i++) { + if (!strstr(ini_info[i].section_name, desp) || + ipio_strcmp(ini_info[i].key_name, NODE_TYPE_KEY_NAME) != 0) { + continue; + } + + record = ','; + for (j = 0, index1 = 0; j <= ini_info[i].key_val_len; j++) { + if (ini_info[i].key_value[j] == ';' || j == ini_info[i].key_val_len) { + if (record != '.') { + memset(str, 0, sizeof(str)); + ipio_memcpy(str, &ini_info[i].key_value[index1], (j - index1), sizeof(str)); + temp = ili_katoi(str); + + /* Over boundary, end to calculate. */ + if (count >= frame_len) { + ILI_ERR("count(%d) is larger than frame length, break\n", count); + break; + } + type_ptr[count] = temp; + count++; + } + record = ini_info[i].key_value[j]; + index1 = j + 1; + } + } + } +} + +static void parser_ini_benchmark(s32 *max_ptr, s32 *min_ptr, int8_t type, char *desp, int frame_len, char *bchmrk_name) +{ + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = {0}, record = ','; + s32 data[4]; + char benchmark_str[256] = {0}; + + /* format complete string from the name of section "_Benchmark_Data". */ + snprintf(benchmark_str,sizeof(benchmark_str), "%s%s%s", desp, "_", bchmrk_name); + ILI_DBG("benchmark_str = %s\n", benchmark_str); + + for (i = 0; i < g_ini_items; i++) { + if ((ipio_strcmp(ini_info[i].section_name, benchmark_str) != 0)) + continue; + record = ','; + for (j = 0, index1 = 0; j <= ini_info[i].key_val_len; j++) { + if (ini_info[i].key_value[j] == ',' || ini_info[i].key_value[j] == ';' || + ini_info[i].key_value[j] == '.' || j == ini_info[i].key_val_len) { + + if (record != '.') { + memset(str, 0, sizeof(str)); + ipio_memcpy(str, &ini_info[i].key_value[index1], (j - index1), sizeof(str)); + temp = ili_katoi(str); + data[(count % 4)] = temp; + + /* Over boundary, end to calculate. */ + if ((count / 4) >= frame_len) { + ILI_ERR("count (%d) is larger than frame length, break\n", (count / 4)); + break; + } + + if ((count % 4) == 3) { + if (data[0] == 1) { + if (type == VALUE) { + max_ptr[count/4] = data[1] + data[2]; + min_ptr[count/4] = data[1] - data[3]; + } else { + max_ptr[count/4] = data[1] + (data[1] * data[2]) / 100; + min_ptr[count/4] = data[1] - (data[1] * data[3]) / 100; + } + } else { + max_ptr[count/4] = INT_MAX; + min_ptr[count/4] = INT_MIN; + } + } + count++; + } + record = ini_info[i].key_value[j]; + index1 = j + 1; + } + } + } +} + +static int parser_get_tdf_value(char *str, int catalog) +{ + u32 i, ans, index = 0, flag = 0, count = 0, size = 0; + char s[10] = {0}; + + if (!str) { + ILI_ERR("String is null\n"); + return -1; + } + + size = strlen(str); + for (i = 0, count = 0; i < size; i++) { + if (str[i] == '.') { + flag = 1; + continue; + } + s[index++] = str[i]; + if (flag) + count++; + } + ans = ili_katoi(s); + + /* Multiply by 100 to shift out of decimal point */ + if (catalog == SHORT_TEST) { + if (count == 0) + ans = ans * 100; + else if (count == 1) + ans = ans * 10; + } + + return ans; +} + +static int parser_get_u8_array(char *key, u8 *buf, u16 base, int len) +{ + char *s = key; + char *pToken; + int ret, conut = 0; + long s_to_long = 0; + + if (strlen(s) == 0 || len <= 0) { + ILI_ERR("Can't find any characters inside buffer\n"); + return -1; + } + + /* + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + */ + if (isspace_t((int)(unsigned char)*s) == 0) { + while ((pToken = strsep(&s, ",")) != NULL) { + ret = kstrtol(pToken, base, &s_to_long); + if (ret == 0) + buf[conut] = s_to_long; + else + ILI_INFO("convert string too long, ret = %d\n", ret); + conut++; + + if (conut >= len) + break; + } + } + + return conut; +} + +static int parser_get_int_data(char *section, char *keyname, char *rv, int rv_len) +{ + int len = 0; + char value[512] = { 0 }; + + if (rv == NULL || section == NULL || keyname == NULL) { + ILI_ERR("Parameters are invalid\n"); + return -EINVAL; + } + + /* return a white-space string if get nothing */ + if (parser_get_ini_key_value(section, keyname, value) < 0) { + snprintf(rv, rv_len, "%s", value); + return 0; + } + + len = snprintf(rv, rv_len, "%s", value); + return len; +} + +/* Count the number of each line and assign the content to tmp buffer */ +static int parser_get_ini_phy_line(char *data, char *buffer, int maxlen) +{ + int i = 0; + int j = 0; + int iRetNum = -1; + char ch1 = '\0'; + + for (i = 0, j = 0; i < maxlen; j++) { + ch1 = data[j]; + iRetNum = j + 1; + if (ch1 == '\n' || ch1 == '\r') { /* line end */ + ch1 = data[j + 1]; + if (ch1 == '\n' || ch1 == '\r') + iRetNum++; + break; + } else if (ch1 == 0x00) { + //iRetNum = -1; + break; /* file end */ + } + + buffer[i++] = ch1; + } + + buffer[i] = '\0'; + return iRetNum; +} + +static char *parser_ini_str_trim_r(char *buf) +{ + int len, i; + char *tmp = NULL; + char x[512] = {0}; + char *y = NULL; + char *empty = ""; + + len = strlen(buf); + + if (len < sizeof(x)) { + tmp = x; + goto copy; + } + + y = kzalloc(len, GFP_KERNEL); + if (ERR_ALLOC_MEM(y)) { + ILI_ERR("Failed to allocate tmp buf\n"); + return empty; + } + tmp = y; + +copy: + for (i = 0; i < len; i++) { + if (buf[i] != ' ') + break; + } + + if (i < len) + strncpy(tmp, (buf + i), (len - i)); + + strncpy(buf, tmp, len); + ipio_kfree((void **)&y); + return buf; +} + +static int parser_get_ini_phy_data(char *data, int fsize) +{ + int i, n = 0, ret = 0, banchmark_flag = 0, empty_section, nodetype_flag = 0; + int offset = 0, isEqualSign = 0, scount = 0; + char *ini_buf = NULL, *tmpSectionName = NULL; + char M_CFG_SSL = '['; + char M_CFG_SSR = ']'; +/* char M_CFG_NIS = ':'; */ + char M_CFG_NTS = '#'; + char M_CFG_EQS = '='; + + if (data == NULL) { + ILI_ERR("INI data is NULL\n"); + ret = -EINVAL; + goto out; + } + + ini_buf = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ini_buf)) { + ILI_ERR("Failed to allocate ini_buf memory, %ld\n", PTR_ERR(ini_buf)); + ret = -ENOMEM; + goto out; + } + + tmpSectionName = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(tmpSectionName)) { + ILI_ERR("Failed to allocate tmpSectionName memory, %ld\n", PTR_ERR(tmpSectionName)); + ret = -ENOMEM; + goto out; + } + + memset(seq_item, 0, MP_TEST_ITEM * PARSER_MAX_KEY_NAME_LEN * sizeof(char)); + + while (true) { + banchmark_flag = 0; + empty_section = 0; + nodetype_flag = 0; + if (g_ini_items > PARSER_MAX_KEY_NUM) { + ILI_ERR("MAX_KEY_NUM: Out of length\n"); + goto out; + } + + if (offset >= fsize) + goto out;/*over size*/ + + n = parser_get_ini_phy_line(data + offset, ini_buf, PARSER_MAX_CFG_BUF); + if (n < 0) { + ILI_ERR("End of Line\n"); + goto out; + } + + offset += n; + + n = strlen(parser_ini_str_trim_r(ini_buf)); + + if (n == 0 || ini_buf[0] == M_CFG_NTS) + continue; + + /* Get section names */ + if (n > 2 && ((ini_buf[0] == M_CFG_SSL && ini_buf[n - 1] != M_CFG_SSR))) { + ILI_ERR("Bad Section: %s\n", ini_buf); + ret = -EINVAL; + goto out; + } else { + if (ini_buf[0] == M_CFG_SSL) { + ini_info[g_ini_items].section_len = n - 2; + if (ini_info[g_ini_items].section_len > PARSER_MAX_KEY_NAME_LEN) { + ILI_ERR("MAX_KEY_NAME_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + if (scount > MAX_SECTION_NUM) { + ILI_ERR("seq_item is over than its define (%d), abort\n", scount); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ini_buf[n - 1] = 0x00; + strncpy((char *)tmpSectionName, ini_buf + 1, (PARSER_MAX_CFG_BUF + 1) * sizeof(char)); + banchmark_flag = 0; + nodetype_flag = 0; + strncpy(seq_item[scount], tmpSectionName, PARSER_MAX_KEY_NAME_LEN); + scount++; + ILI_DBG("Section Name: %s, Len: %d, offset = %d\n", seq_item[scount], n - 2, offset); + continue; + } + } + + /* copy section's name without square brackets to its real buffer */ + strncpy(ini_info[g_ini_items].section_name, tmpSectionName, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].section_len = strlen(tmpSectionName); + + isEqualSign = 0; + for (i = 0; i < n; i++) { + if (ini_buf[i] == M_CFG_EQS) { + isEqualSign = i; + break; + } + if (ini_buf[i] == M_CFG_SSL || ini_buf[i] == M_CFG_SSR) { + empty_section = 1; + break; + } + } + + if (isEqualSign == 0) { + if (empty_section) + continue; + + if (strstr(ini_info[g_ini_items].section_name, BENCHMARK_KEY_NAME) != NULL) { + isEqualSign = -1; + ini_info[g_ini_items].key_name_len = strlen(ini_info[g_ini_items].section_name); + strncpy(ini_info[g_ini_items].key_name, ini_info[g_ini_items].section_name, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].key_val_len = n; + } else if (strstr(ini_info[g_ini_items].section_name, NODE_TYPE_KEY_NAME) != NULL) { + isEqualSign = -1; + ini_info[g_ini_items].key_name_len = strlen(NODE_TYPE_KEY_NAME); + strncpy(ini_info[g_ini_items].key_name, NODE_TYPE_KEY_NAME, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].key_val_len = n; + } else { + continue; + } + } else { + ini_info[g_ini_items].key_name_len = isEqualSign; + if (ini_info[g_ini_items].key_name_len > PARSER_MAX_KEY_NAME_LEN) { + /* ret = CFG_ERR_OUT_OF_LEN; */ + ILI_ERR("MAX_KEY_NAME_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ipio_memcpy(ini_info[g_ini_items].key_name, ini_buf, + ini_info[g_ini_items].key_name_len, PARSER_MAX_KEY_NAME_LEN); + ini_info[g_ini_items].key_val_len = n - isEqualSign - 1; + } + + if (ini_info[g_ini_items].key_val_len > PARSER_MAX_KEY_VALUE_LEN) { + ILI_ERR("MAX_KEY_VALUE_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ipio_memcpy(ini_info[g_ini_items].key_value, + ini_buf + isEqualSign + 1, ini_info[g_ini_items].key_val_len, PARSER_MAX_KEY_VALUE_LEN); + + ILI_DBG("%s = %s\n", ini_info[g_ini_items].key_name, + ini_info[g_ini_items].key_value); + + g_ini_items++; + } +out: + ipio_kfree((void **)&ini_buf); + ipio_kfree((void **)&tmpSectionName); + return ret; +} + +static int ilitek_tddi_mp_ini_parser(const char *path) +{ + int i, ret = 0, fsize = 0; + char *tmp = NULL; + const struct firmware *ini = NULL; + struct file *f = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + + ILI_INFO("ini file path = %s\n", path); + + f = filp_open(path, O_RDONLY, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open ini file at %ld, try to request_firmware\n", PTR_ERR(f)); + f = NULL; + path = ilits->md_ini_rq_path; + ILI_INFO("request path = %s\n", path); + if (request_firmware(&ini, path, ilits->dev) < 0) { + ILI_ERR("Request ini file failed\n"); + return -EINVAL; + } + } + + if (f != NULL) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) + fsize = f->f_inode->i_size; +#else + struct inode *inode; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) + inode = f->f_dentry->d_inode; +#else + inode = f->f_path.dentry->d_inode; +#endif + fsize = inode->i_size; +#endif + } else { + fsize = ini->size; + } + + ILI_INFO("ini file size = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + ret = -EINVAL; + goto out; + } + + tmp = vmalloc(fsize+1); + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("Failed to allocate tmp memory, %ld\n", PTR_ERR(tmp)); + ret = -ENOMEM; + goto out; + } + + if (f != NULL) { + old_fs = get_fs(); + set_fs(get_ds()); + vfs_read(f, tmp, fsize, &pos); + set_fs(old_fs); + tmp[fsize] = 0x0; + } else { + memcpy(tmp, ini->data, fsize); + } + + g_ini_items = 0; + + /* Initialise ini strcture */ + for (i = 0; i < PARSER_MAX_KEY_NUM; i++) { + memset(ini_info[i].section_name, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ini_info[i].key_name, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ini_info[i].key_value, 0, PARSER_MAX_KEY_VALUE_LEN); + ini_info[i].section_len = 0; + ini_info[i].key_name_len = 0; + ini_info[i].key_val_len = 0; + } + + /* change all characters to lower case */ + for (i = 0; i < fsize; i++) + tmp[i] = tolower(tmp[i]); + + ret = parser_get_ini_phy_data(tmp, fsize); + if (ret < 0) { + ILI_ERR("Failed to get physical ini data, ret = %d\n", ret); + goto out; + } + + ILI_INFO("Parsed ini file done\n"); +out: + ipio_vfree((void **)&tmp); + + if (f != NULL) + filp_close(f, NULL); + else + release_firmware(ini); + + return ret; +} + +static void run_pixel_test(int index) +{ + int i, x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int tmp[4] = { 0 }, max = 0; + int shift = y * core_mp.xch_len; + int centre = p_comb[shift + x]; + + /* + * if its position is in corner, the number of point + * we have to minus is around 2 to 3. + */ + if (y == 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == (core_mp.ych_len - 1) && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == 0 && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == (core_mp.ych_len - 1) && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == 0 && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + + } else if (y == (core_mp.ych_len - 1) && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + } else { + /* middle minus four directions */ + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[2] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[3] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } + + max = tmp[0]; + + for (i = 0; i < 4; i++) { + if (tmp[i] > max) + max = tmp[i]; + } + + tItems[index].buf[shift + x] = max; + } + } +} + +static void run_untouch_p2p_test(int index) +{ + int x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + if (p_comb[shift + x] > tItems[index].max_buf[shift + x]) + tItems[index].max_buf[shift + x] = p_comb[shift + x]; + + if (p_comb[shift + x] < tItems[index].min_buf[shift + x]) + tItems[index].min_buf[shift + x] = p_comb[shift + x]; + + tItems[index].buf[shift + x] = + tItems[index].max_buf[shift + x] - tItems[index].min_buf[shift + x]; + } + } +} + +static int run_open_test(int index) +{ + int i, x, y, k, ret = 0; + int border_x[] = {-1, 0, 1, 1, 1, 0, -1, -1}; + int border_y[] = {-1, -1, -1, 0, 1, 1, 1, 0}; + s32 *p_comb = frame_buf; + + if (ipio_strcmp(tItems[index].desp, "open test(integration)") == 0) { + for (i = 0; i < core_mp.frame_len; i++) + tItems[index].buf[i] = p_comb[i]; + } else if (ipio_strcmp(tItems[index].desp, "open test(cap)") == 0) { + /* + * Each result is getting from a 3 by 3 grid depending on where the centre location is. + * So if the centre is at corner, the number of node grabbed from a grid will be different. + */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int sum = 0, avg = 0, count = 0; + int shift = y * core_mp.xch_len; + int centre = p_comb[shift + x]; + + for (k = 0; k < 8; k++) { + if (((y + border_y[k] >= 0) && (y + border_y[k] < core_mp.ych_len)) && + ((x + border_x[k] >= 0) && (x + border_x[k] < core_mp.xch_len))) { + count++; + sum += p_comb[(y + border_y[k]) * core_mp.xch_len + (x + border_x[k])]; + } + } + + avg = (sum + centre) / (count + 1); /* plus 1 because of centre */ + tItems[index].buf[shift + x] = (centre * 100) / avg; + } + } + } + return ret; +} + +static void run_tx_rx_delta_test(int index) +{ + int x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + /* Tx Delta */ + if (y != (core_mp.ych_len - 1)) + core_mp.tx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[(shift + 1) + x]); + + /* Rx Delta */ + if (x != (core_mp.xch_len - 1)) + core_mp.rx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[shift + (x + 1)]); + } + } +} + +static char *get_date_time_str(void) +{ + struct timespec now_time; + struct rtc_time rtc_now_time; + static char time_data_buf[128] = { 0 }; + + getnstimeofday(&now_time); + rtc_time_to_tm(now_time.tv_sec, &rtc_now_time); + snprintf(time_data_buf, sizeof(time_data_buf), "%04d%02d%02d-%02d%02d%02d", + (rtc_now_time.tm_year + 1900), rtc_now_time.tm_mon + 1, + rtc_now_time.tm_mday, rtc_now_time.tm_hour, rtc_now_time.tm_min, + rtc_now_time.tm_sec); + + return time_data_buf; +} + +static void mp_print_csv_header(char *csv, int *csv_len, int *csv_line, int file_size) +{ + int i, seq, tmp_len = *csv_len, tmp_line = *csv_line; + + /* header must has 19 line*/ + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "ILITek C-TP Utility V%s %x : Driver Sensor Test\n", DRIVER_VERSION, core_mp.chip_pid); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Confidentiality Notice:\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Any information of this tool is confidential and privileged.\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "@ ILI TECHNOLOGY CORP. All Rights Reserved.\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Firmware Version ,0x%x\n", core_mp.fw_ver); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Panel information ,XCH=%d, YCH=%d\n", core_mp.xch_len, core_mp.ych_len); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "INI Release Version ,%s\n", core_mp.ini_ver); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "INI Release Date ,%s\n", core_mp.ini_date); + tmp_line++; + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Test Item:\n"); + tmp_line++; + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " ---%s\n", tItems[i].desp); + tmp_line++; + } + + while (tmp_line < 19) { + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "\n"); + tmp_line++; + } + + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + + *csv_len = tmp_len; + *csv_line = tmp_line; +} + +static void mp_print_csv_tail(char *csv, int *csv_len, int file_size) +{ + int i, seq, tmp_len = *csv_len; + + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Result_Summary \n"); + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + if (tItems[i].item_result == MP_DATA_PASS) + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,OK\n", tItems[i].desp); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,NG\n", tItems[i].desp); + } + + *csv_len = tmp_len; +} + +static void mp_print_csv_cdc_cmd(char *csv, int *csv_len, int index, int file_size) +{ + int i, slen = 0, tmp_len = *csv_len, size; + char str[128] = {0}; + char *open_sp_cmd[] = {"open dac", "open raw1", "open raw2", "open raw3"}; + char *open_c_cmd[] = {"open cap1 dac", "open cap1 raw"}; + char *name = tItems[index].desp; + + if (ipio_strcmp(name, "open test(integration)_sp") == 0) { + size = ARRAY_SIZE(open_sp_cmd); + for (i = 0; i < size; i++) { + slen = parser_get_int_data("pv5_4 command", open_sp_cmd[i], str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", open_sp_cmd[i]); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "%s = ,%s\n", open_sp_cmd[i], str); + } + } else if (ipio_strcmp(name, "open test_c") == 0) { + size = ARRAY_SIZE(open_c_cmd); + for (i = 0; i < size; i++) { + slen = parser_get_int_data("pv5_4 command", open_c_cmd[i], str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", open_sp_cmd[i]); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "%s = ,%s\n", open_c_cmd[i], str); + } + } else { + slen = parser_get_int_data("pv5_4 command", name, str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", name); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "CDC command = ,%s\n", str); + + /* Print short parameters */ + if (ipio_strcmp(name, "short test") == 0) + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Variation = ,%d\n", short_para.variation); + + /* Print td second command */ + if (core_mp.td_retry && (ipio_strcmp(name, "peak to peak_td (lcm off)") == 0)) { + name = "peak to peak_td (lcm off)_2"; + parser_get_int_data("pv5_4 command", name, str, sizeof(str)); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "CDC command 2 = ,%s\n", str); + } + } + *csv_len = tmp_len; +} + +static void mp_compare_cdc_show_result(int index, s32 *tmp, char *csv, + int *csv_len, int type, s32 *max_ts, + s32 *min_ts, const char *desp, int file_zise) +{ + int x, y, tmp_len = *csv_len; + int mp_result = MP_DATA_PASS; + + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("The data of test item is null (%p)\n", tmp); + mp_result = -EMP_INVAL; + goto out; + } + + /* print X raw only */ + for (x = 0; x < core_mp.xch_len; x++) { + if (x == 0) { + DUMP("\n %s ", desp); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n %s ,", desp); + } + DUMP(" X_%d ,", (x+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " X_%d ,", (x+1)); + } + + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + + for (y = 0; y < core_mp.ych_len; y++) { + DUMP(" Y_%d ,", (y+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " Y_%d ,", (y+1)); + + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len + x; + + /* In Short teset, we only identify if its value is low than min threshold. */ + if (tItems[index].catalog == SHORT_TEST) { + if (tmp[shift] < min_ts[shift]) { + DUMP(" #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "#%7d,", tmp[shift]); + mp_result = MP_DATA_FAIL; + } else { + DUMP(" %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " %7d, ", tmp[shift]); + } + continue; + } + + if ((tmp[shift] <= max_ts[shift] && tmp[shift] >= min_ts[shift]) || (type != TYPE_JUGE)) { + if ((tmp[shift] == INT_MAX || tmp[shift] == INT_MIN) && (type == TYPE_BENCHMARK)) { + DUMP("%s", "BYPASS,"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "BYPASS,"); + } else { + DUMP(" %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " %7d, ", tmp[shift]); + } + } else { + if (tmp[shift] > max_ts[shift]) { + DUMP(" *%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "*%7d,", tmp[shift]); + } else { + DUMP(" #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "#%7d,", tmp[shift]); + } + mp_result = MP_DATA_FAIL; + } + } + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + } + +out: + if (type == TYPE_JUGE) { + if (mp_result == MP_DATA_PASS) { + pr_info("\n Result : PASS\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : PASS\n"); + } else { + pr_info("\n Result : FAIL\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : FAIL\n"); + } + } + *csv_len = tmp_len; +} + +#define ABS(a, b) ((a > b) ? (a - b) : (b - a)) +#define ADDR(x, y) ((y * core_mp.xch_len) + (x)) + +static int compare_charge(s32 *charge_rate, int x, int y, s32 *inNodeType, + int Charge_AA, int Charge_Border, int Charge_Notch) +{ + int OpenThreadhold, tempY, tempX, ret, k; + int sx[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; + int sy[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; + + ret = charge_rate[ADDR(x, y)]; + + /*Setting Threadhold from node type */ + if (charge_rate[ADDR(x, y)] == 0) + return ret; + else if ((inNodeType[ADDR(x, y)] & AA_Area) == AA_Area) + OpenThreadhold = Charge_AA; + else if ((inNodeType[ADDR(x, y)] & Border_Area) == Border_Area) + OpenThreadhold = Charge_Border; + else if ((inNodeType[ADDR(x, y)] & Notch) == Notch) + OpenThreadhold = Charge_Notch; + else + return ret; + + /* compare carge rate with 3*3 node */ + /* by pass => 1.no compare 2.corner 3.Skip_Micro 4.full open fail node */ + for (k = 0; k < 8; k++) { + tempX = x + sx[k]; + tempY = y + sy[k]; + + /*out of range */ + if ((tempX < 0) || (tempX >= core_mp.xch_len) || (tempY < 0) || (tempY >= core_mp.ych_len)) + continue; + + if ((inNodeType[ADDR(tempX, tempY)] == NO_COMPARE) || ((inNodeType[ADDR(tempX, tempY)] & Round_Corner) == Round_Corner) || + ((inNodeType[ADDR(tempX, tempY)] & Skip_Micro) == Skip_Micro) || charge_rate[ADDR(tempX, tempY)] == 0) + continue; + + if ((charge_rate[ADDR(tempX, tempY)] - charge_rate[ADDR(x, y)]) > OpenThreadhold) + return OpenThreadhold; + } + return ret; +} + +static int full_open_rate_compare(s32 *full_open, s32 *cbk, int x, int y, s32 inNodeType, int full_open_rate) +{ + int ret = true; + + if ((inNodeType == NO_COMPARE) || ((inNodeType & Round_Corner) == Round_Corner)) + return true; + + if (full_open[ADDR(x, y)] < (cbk[ADDR(x, y)] * full_open_rate / 100)) + ret = false; + + return ret; +} + +static s32 open_sp_formula(int dac, int raw, int tvch, int tvcl) +{ + s32 ret = 0; + u16 id = core_mp.chip_id; + + if (id == ILI9881_CHIP) { + ret = ((dac * 10000 * 161 / 100) - (16384 / 2 - raw) * 20000 * 7 / 16384 * 36 / 10) / (tvch - tvcl) / 2; + } else { + ret = ((dac * 10000 * 131 / 100) - (16384 / 2 - raw) * 20000 * 7 / 16384 * 36 / 10) / (tvch - tvcl) / 2; + } + return ret; +} + +static s32 open_c_formula(int inCap1DAC, int inCap1Raw, int accuracy) +{ + s32 inCap1Value = 0; + + u16 id = core_mp.chip_id; + u8 type = core_mp.chip_type; + + int inFoutRange = 16384; + int inFoutRange_half = 8192; + int inVadc_range = 36; + int inVbk = 39; + int inCbk_step = open_para.cbk_step;/* from mp.ini */ + int inCint = open_para.cint;/* from mp.ini */ + int inVdrv = open_para.tvch - open_para.tvcl;/* from mp.ini */ + int inGain = open_para.gain;/* from mp.ini */ + int inMagnification = 10; + int inPartDac = 0; + int inPartRaw = 0; + + if ((inCbk_step == 0) || (inCint == 0)) { + if (id == ILI9881_CHIP) { + if ((type == ILI_N) || (type == ILI_O)){ + inCbk_step = 32; + inCint = 70; + } + } else if (id == ILI9882_CHIP) { + if (type == ILI_N) { + inCbk_step = 42; + inCint = 70; + } else if (type == ILI_H) { + inCbk_step = 42; + inCint = 69; + } + } else if (id == ILI7807_CHIP){ + if (type == ILI_Q) { + inCbk_step = 28; + inCint= 70; + } else if (type == ILI_S) { + inCbk_step = 38; + inCint= 66; + inVbk = 42; + inVdrv = inVdrv / 10; + inGain = inGain / 10; + + } else if (type == ILI_V) { + inCbk_step = 28; + inCint= 70; + } + } + } + + inPartDac = (inCap1DAC * inCbk_step * inVbk / 2); + inPartRaw = ((inCap1Raw - inFoutRange_half) * inVadc_range * inCint * 10 / inFoutRange); + + if (accuracy) { + inCap1Value = ((inPartDac + inPartRaw) * 10) / inVdrv / inMagnification / inGain; + } else { + inCap1Value = (inPartDac + inPartRaw) / inVdrv / inMagnification / inGain; + } + + return inCap1Value; +} + +static void allnode_open_cdc_result(int index, int *buf, int *dac, int *raw) +{ + int i; + char *desp = tItems[index].desp; + char str[32] = {0}; + int accuracy = 0; + + if (ipio_strcmp(desp, "open test(integration)_sp") == 0) { + for (i = 0; i < core_mp.frame_len; i++) + buf[i] = open_sp_formula(dac[i], raw[i], open_para.tvch, open_para.tvcl); + } else if (ipio_strcmp(desp, "open test_c") == 0) { + + if (parser_get_int_data(desp, "accuracy", str, sizeof(str)) > 0) + accuracy = ili_katoi(str); + + for (i = 0; i < core_mp.frame_len; i++) + buf[i] = open_c_formula(dac[i], raw[i], accuracy); + } +} + +static int codeToOhm(s32* ohm, s32* Code, u16 *v_tdf, u16 *h_tdf) +{ + u16 id = core_mp.chip_id; + u8 type = core_mp.chip_type; + + int inTVCH = short_para.tvch; + int inTVCL = short_para.tvcl; + int inVariation = short_para.variation; + int inTDF1 = 0; + int inTDF2 = 0; + int inCint = short_para.cint; + int inRinternal = short_para.rinternal; + s32 temp = 0; + int j = 0; + + if (core_mp.isLongV) { + inTDF1 = *v_tdf; + inTDF2 = *(v_tdf + 1); + } else { + inTDF1 = *h_tdf; + inTDF2 = *(h_tdf + 1); + } + + if (inVariation == 0) { + inVariation = 100; + } + + if ((inCint == 0) || (inRinternal == 0)) { + if (id == ILI9881_CHIP) { + if ((type == ILI_N) || (type == ILI_O)){ + inRinternal = 1915; + inCint = 70; + } + } else if (id == ILI9882_CHIP) { + if (type == ILI_N) { + inRinternal = 1354; + inCint = 70; + } else if (type == ILI_H) { + inRinternal = 1354; + inCint = 69; + } + } else if (id == ILI7807_CHIP){ + if (type == ILI_Q) { + inRinternal = 1500; + inCint= 70; + } else if (type == ILI_S) { + inRinternal = 1500; + inCint= 66; + inTVCH = inTVCH/10; + inTVCL = inTVCL/10; + } else if (type == ILI_V) { + inRinternal = 1500; + inCint= 70; + } + } + } + for (j = 0; j < core_mp.frame_len; j++) { + if (Code[j] == 0) { + ILI_ERR("code is invalid\n"); + } else { + temp = ((inTVCH - inTVCL) * inVariation * (inTDF1 - inTDF2) * (1 << 12) / (9 * Code[j] * inCint)) * 1000; + temp = (temp - inRinternal) / 1000; + } + ohm[j] = temp; + } + + /* Unit = M Ohm */ + return temp; + +} + +static int short_test(int index, int frame_index) +{ + u32 pid = core_mp.chip_pid >> 8; + int j = 0, ret = 0; + u16 v_tdf[2] = {0}; + u16 h_tdf[2] = {0}; + char str[32] = {0}; + + v_tdf[0] = tItems[index].v_tdf_1; + v_tdf[1] = tItems[index].v_tdf_2; + h_tdf[0] = tItems[index].h_tdf_1; + h_tdf[1] = tItems[index].h_tdf_2; + + parser_get_int_data("short test", "tvch", str, sizeof(str)); + short_para.tvch = ili_katoi(str); + + parser_get_int_data("short test", "tvcl", str, sizeof(str)); + short_para.tvcl = ili_katoi(str); + + parser_get_int_data("short test", "variation", str, sizeof(str)); + short_para.variation = ili_katoi(str); + + parser_get_int_data("short test", "cint", str, sizeof(str)); + short_para.cint = ili_katoi(str); + + parser_get_int_data("short test", "rinternal", str, sizeof(str)); + short_para.rinternal = ili_katoi(str); + + /* 9881N, 9881O, 7807Q, 7807S not support cbk_step and cint read from mp.ini */ + if ((pid != 0x988117) && (pid != 0x988118) && (pid != 0x78071A) && (pid != 0x78071C)) { + if ((short_para.cint == 0) || (short_para.rinternal == 0)) { + ILI_ERR("Failed to get short parameter"); + core_mp.lost_parameter = true; + return -1; + } + } + + ILI_INFO("TVCH = %d, TVCL = %d, Variation = %d, V_TDF1 = %d, V_TDF2 = %d, H_TDF1 = %d, H_TDF2 = %d, Cint = %d, Rinternal = %d\n" + , short_para.tvch, short_para.tvcl, short_para.variation, tItems[index].v_tdf_1, tItems[index].v_tdf_2, tItems[index].h_tdf_1, tItems[index].h_tdf_2, short_para.cint, short_para.rinternal); + + if (core_mp.protocol_ver >= PROTOCOL_VER_540) { + /* Calculate code to ohm and save to tItems[index].buf */ + ili_dump_data(frame_buf, 10, core_mp.frame_len, core_mp.xch_len, "Short Raw 1"); + codeToOhm(&tItems[index].buf[frame_index * core_mp.frame_len], frame_buf, v_tdf, h_tdf); + ili_dump_data(&tItems[index].buf[frame_index * core_mp.frame_len], 10, core_mp.frame_len, core_mp.xch_len, "Short Ohm 1"); + } else { + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[frame_index * core_mp.frame_len + j] = frame_buf[j]; + } + + return ret; +} + +static int allnode_key_cdc_data(int index) +{ + int i, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[3] = {0}; + u8 *ori = NULL; + + len = core_mp.key_len * 2; + + ILI_DBG("Read key's length = %d\n", len); + ILI_DBG("core_mp.key_len = %d\n", core_mp.key_len); + + if (len <= 0) { + ILI_ERR("Length is invalid\n"); + ret = -1; + goto out; + } + + /* CDC init */ + cmd[0] = P5_X_SET_CDC_INIT; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + // /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori mem (%ld)\n", PTR_ERR(ori)); + ret = -1; + goto out; + } + ret = ilits->wrapper(cmd, sizeof(cmd), ori, len, ON, ON); + if (ret < 0) { + ILI_ERR(" fail to get cdc data\n"); + goto out; + } + ili_dump_data(ori, 8, len, 0, "Key CDC original"); + + if (key_buf == NULL) { + key_buf = kcalloc(core_mp.key_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(key_buf)) { + ILI_ERR("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(key_buf)); + goto out; + } + } else { + memset(key_buf, 0x0, core_mp.key_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if (tItems[index].cmd == CMD_KEY_DAC) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + key_buf[i] = (inDACp + inDACn) / 2; + } + } + ili_dump_data(key_buf, 32, core_mp.frame_len, core_mp.xch_len, "Key CDC combined data"); + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static int mp_cdc_get_pv5_4_command(u8 *cmd, int len, int index) +{ + int slen = 0; + char str[128] = {0}; + char *key = tItems[index].desp; + + if (core_mp.td_retry && (ipio_strcmp(key, "peak to peak_td (lcm off)") == 0)) + key = "peak to peak_td (lcm off)_2"; + + slen = parser_get_int_data("pv5_4 command", key, str, sizeof(str)); + if (slen < 0) + return -1; + + if (parser_get_u8_array(str, cmd, 16, len) < 0) + return -1; + + return 0; +} + +static int mp_cdc_init_cmd_common(u8 *cmd, int len, int index) +{ + int ret = 0; + + if (core_mp.protocol_ver >= PROTOCOL_VER_540) { + core_mp.cdc_len = 15; + return mp_cdc_get_pv5_4_command(cmd, len, index); + } + + cmd[0] = P5_X_SET_CDC_INIT; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + core_mp.cdc_len = 3; + + if (ipio_strcmp(tItems[index].desp, "open test(integration)") == 0) + cmd[2] = 0x2; + if (ipio_strcmp(tItems[index].desp, "open test(cap)") == 0) + cmd[2] = 0x3; + + if (tItems[index].catalog == PEAK_TO_PEAK_TEST) { + cmd[2] = ((tItems[index].frame_count & 0xff00) >> 8); + cmd[3] = tItems[index].frame_count & 0xff; + cmd[4] = 0; + + core_mp.cdc_len = 5; + + if (ipio_strcmp(tItems[index].desp, "noise peak to peak(cut panel)") == 0) + cmd[4] = 0x1; + + ILI_DBG("P2P CMD: %d,%d,%d,%d,%d\n", + cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]); + } + + return ret; +} + +static int allnode_open_cdc_data(int mode, int *buf) +{ + int i = 0, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[15] = {0}; + u8 *ori = NULL; + char str[128] = {0}; + char *key[] = {"open dac", "open raw1", "open raw2", "open raw3", + "open cap1 dac", "open cap1 raw"}; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp.xch_len * core_mp.ych_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d, mode = %d\n", len, mode); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + /* CDC init. Read command from ini file */ + if (parser_get_int_data("pv5_4 command", key[mode], str, sizeof(str)) < 0) { + ILI_ERR("Failed to parse PV54 command, ret = %d\n", ret); + ret = -EMP_PARSE; + goto out; + } + + parser_get_u8_array(str, cmd, 16, sizeof(cmd)); + + ili_dump_data(cmd, 8, sizeof(cmd), 0, "Open SP command"); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + /* Get original frame(cdc) data */ + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + + ili_dump_data(ori, 8, len, 0, "Open SP CDC original"); + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if ((mode == 0) || (mode == 4)) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + buf[i] = inDACp + inDACn; + } else { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + if ((tmp & 0x8000) == 0x8000) + buf[i] = tmp - 65536; + else + buf[i] = tmp; + + } + } + ili_dump_data(buf, 10, core_mp.frame_len, core_mp.xch_len, "Open SP CDC combined"); +out: + + ipio_kfree((void **)&ori); + return ret; +} + +static int allnode_peak_to_peak_cdc_data(int index) +{ + int i, k, ret = 0, len = 0, rd_frame_num = 1, cmd_len = 0; + u8 cmd[15] = {0}; + u8 *ori = NULL; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp.frame_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d\n", len); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + memset(cmd, 0xFF, sizeof(cmd)); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + if (tItems[index].bch_mrk_multi) { + rd_frame_num = tItems[index].bch_mrk_frm_num - 1; + } + + if (frm_buf == NULL) { + frm_buf = (s32 **)kzalloc(tItems[index].bch_mrk_frm_num * sizeof(s32 *), GFP_KERNEL); + if (ERR_ALLOC_MEM(frm_buf)) { + ILI_ERR("Failed to allocate frm_buf mem (%ld)\n", PTR_ERR(frm_buf)); + ret = -EMP_NOMEM; + goto out; + } + for (i = 0 ; i < tItems[index].bch_mrk_frm_num ; i++) { + frm_buf[i] = (s32*)kzalloc(core_mp.frame_len * sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frm_buf)) { + ILI_ERR("Failed to allocate frm_buf[%d] mem (%ld)\n", i, PTR_ERR(frm_buf)); + ret = -EMP_NOMEM; + goto out; + } + } + } + + /* CDC init */ + if (mp_cdc_init_cmd_common(cmd, sizeof(cmd), index) < 0) { + ILI_ERR("Failed to get cdc command\n"); + ret = -EMP_CMD; + goto out; + } + + ili_dump_data(cmd, 8, core_mp.cdc_len, 0, "Mutual CDC command"); + + for (k = 0; k < rd_frame_num; k++) { + if (k == 0) + cmd_len = core_mp.cdc_len; + else + cmd_len = 0; + memset(ori, 0, len); + /* Get original frame(cdc) data */ + if (ilits->wrapper(cmd, cmd_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + + ili_dump_data(ori, 8, len, 0, "Mutual CDC original"); + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + + if ((tmp & 0x8000) == 0x8000) + frm_buf[k][i] = tmp - 65536; + else + frm_buf[k][i] = tmp; + + // multiple case frame3 = frame1 - frame2 + if (tItems[index].bch_mrk_multi && rd_frame_num == k + 1) + frm_buf[k+1][i] = frm_buf[k-1][i] - frm_buf[k][i]; + } + } + + if (tItems[index].bch_mrk_multi) { + ili_dump_data(frm_buf[0], 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined[0]/frame1"); + ili_dump_data(frm_buf[1], 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined[1]/frame2"); + ili_dump_data(frm_buf[2], 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined[2]/frame3"); + } else { + ili_dump_data(frm_buf[0], 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined[0]/frame1"); + } + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static int allnode_mutual_cdc_data(int index) +{ + int i, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[15] = {0}; + u8 *ori = NULL; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp.frame_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d\n", len); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + memset(cmd, 0xFF, sizeof(cmd)); + + /* CDC init */ + if (mp_cdc_init_cmd_common(cmd, sizeof(cmd), index) < 0) { + ILI_ERR("Failed to get cdc command\n"); + ret = -EMP_CMD; + goto out; + } + + ili_dump_data(cmd, 8, core_mp.cdc_len, 0, "Mutual CDC command"); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + /* Get original frame(cdc) data */ + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + + ili_dump_data(ori, 8, len, 0, "Mutual CDC original"); + + if (frame_buf == NULL) { + frame_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame_buf)) { + ILI_ERR("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(frame_buf)); + ret = -EMP_NOMEM; + goto out; + } + } else { + memset(frame_buf, 0x0, core_mp.frame_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if (ipio_strcmp(tItems[index].desp, "calibration data(dac)") == 0) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + frame_buf[i] = (inDACp + inDACn) / 2; + } else { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + + if ((tmp & 0x8000) == 0x8000) + frame_buf[i] = tmp - 65536; + else + frame_buf[i] = tmp; + + if (ipio_strcmp(tItems[index].desp, "raw data(no bk)") == 0 || + ipio_strcmp(tItems[index].desp, "raw data(no bk) (lcm off)") == 0) { + frame_buf[i] -= core_mp.no_bk_shift; + } + } + } + + ili_dump_data(frame_buf, 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined"); + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static void compare_MaxMin_result(int index, s32 *data) +{ + int x, y; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + if (tItems[index].catalog == UNTOUCH_P2P) + return; + else if (tItems[index].catalog == TX_RX_DELTA) { + /* Tx max/min comparison */ + if (core_mp.tx_delta_buf[shift + x] < data[shift + x]) + core_mp.tx_max_buf[shift + x] = data[shift + x]; + + if (core_mp.tx_delta_buf[shift + x] > data[shift + x]) + core_mp.tx_min_buf[shift + x] = data[shift + x]; + + /* Rx max/min comparison */ + if (core_mp.rx_delta_buf[shift + x] < data[shift + x]) + core_mp.rx_max_buf[shift + x] = data[shift + x]; + + if (core_mp.rx_delta_buf[shift + x] > data[shift + x]) + core_mp.rx_min_buf[shift + x] = data[shift + x]; + } else { + if (tItems[index].max_buf[shift + x] < data[shift + x]) + tItems[index].max_buf[shift + x] = data[shift + x]; + + if (tItems[index].min_buf[shift + x] > data[shift + x]) + tItems[index].min_buf[shift + x] = data[shift + x]; + } + } + } +} + +static int create_mp_test_frame_buffer(int index, int frame_count) +{ + int i; + + ILI_DBG("Create MP frame buffers (index = %d), count = %d\n", + index, frame_count); + + if (tItems[index].catalog == TX_RX_DELTA) { + if (core_mp.tx_delta_buf == NULL) { + core_mp.tx_delta_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("Failed to allocate tx_delta_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_delta_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_delta_buf == NULL) { + core_mp.rx_delta_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf)) { + ILI_ERR("Failed to allocate rx_delta_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_delta_buf); + return -ENOMEM; + } + } + + if (core_mp.tx_max_buf == NULL) { + core_mp.tx_max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_max_buf)) { + ILI_ERR("Failed to allocate tx_max_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_max_buf); + return -ENOMEM; + } + } + + if (core_mp.tx_min_buf == NULL) { + core_mp.tx_min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_min_buf)) { + ILI_ERR("Failed to allocate tx_min_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_min_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_max_buf == NULL) { + core_mp.rx_max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_max_buf)) { + ILI_ERR("Failed to allocate rx_max_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_max_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_min_buf == NULL) { + core_mp.rx_min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_min_buf)) { + ILI_ERR("Failed to allocate rx_min_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_min_buf); + return -ENOMEM; + } + } + } else { + if (tItems[index].buf == NULL) { + tItems[index].buf = vmalloc(frame_count * core_mp.frame_len * sizeof(s32)); + if (ERR_ALLOC_MEM(tItems[index].buf)) { + ILI_ERR("Failed to allocate buf mem\n"); + ipio_kfree((void **)&tItems[index].buf); + return -ENOMEM; + } + } + + if (tItems[index].result_buf == NULL) { + tItems[index].result_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].result_buf)) { + ILI_ERR("Failed to allocate result_buf mem\n"); + ipio_kfree((void **)&tItems[index].result_buf); + return -ENOMEM; + } + } + + if (tItems[index].max_buf == NULL) { + tItems[index].max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].max_buf)) { + ILI_ERR("Failed to allocate max_buf mem\n"); + ipio_kfree((void **)&tItems[index].max_buf); + return -ENOMEM; + } + } + + if (tItems[index].min_buf == NULL) { + tItems[index].min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].min_buf)) { + ILI_ERR("Failed to allocate min_buf mem\n"); + ipio_kfree((void **)&tItems[index].min_buf); + return -ENOMEM; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + if (tItems[index].bch_mrk_max == NULL) { + tItems[index].bch_mrk_max = (s32 **)kzalloc(tItems[index].bch_mrk_frm_num * sizeof(s32 *), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_max)) { + ILI_ERR("Failed to allocate bch_mrk_max mem\n"); + ipio_kfree((void **)&tItems[index].bch_mrk_max); + return -ENOMEM; + } + + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + tItems[index].bch_mrk_max[i] = (s32 *)kzalloc(core_mp.frame_len * sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_max[i])) { + ILI_ERR("Failed to allocate bch_mrk_max[%d] mem\n", i); + ipio_kfree((void **)&tItems[index].bch_mrk_max[i]); + return -ENOMEM; + } + } + } + + if (tItems[index].bch_mrk_min == NULL) { + tItems[index].bch_mrk_min = (s32 **)kzalloc(tItems[index].bch_mrk_frm_num * sizeof(s32 *), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_min)) { + ILI_ERR("Failed to allocate bch_mrk_min mem\n"); + ipio_kfree((void **)&tItems[index].bch_mrk_min); + return -ENOMEM; + } + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + tItems[index].bch_mrk_min[i] = (s32 *)kzalloc(core_mp.frame_len * sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_min[i])) { + ILI_ERR("Failed to allocate bch_mrk_min[%d] mem\n", i); + ipio_kfree((void **)&tItems[index].bch_mrk_min[i]); + return -ENOMEM; + } + } + } + + if (tItems[index].bench_mark_max == NULL) { + tItems[index].bench_mark_max = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bench_mark_max)) { + ILI_ERR("Failed to allocate bench_mark_max mem\n"); + ipio_kfree((void **)&tItems[index].bench_mark_max); + return -ENOMEM; + } + } + if (tItems[index].bench_mark_min == NULL) { + tItems[index].bench_mark_min = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bench_mark_min)) { + ILI_ERR("Failed to allocate bench_mark_min mem\n"); + ipio_kfree((void **)&tItems[index].bench_mark_min); + return -ENOMEM; + } + } + } + } + return 0; +} + +static int pin_test(int index) +{ + int ret = 0; + u8 cmd[5] = {0}; + + ILI_INFO("PIN test start"); + ILI_INFO("test_int_pin = 0x%x\n", tItems[index].test_int_pin); + ILI_INFO("int_pulse_test = 0x%x\n", tItems[index].int_pulse_test); + ILI_INFO("delay_time = 0x%x\n", tItems[index].delay_time); + + if (tItems[index].test_int_pin == ENABLE) { + /* test HIGH level*/ + cmd[0] = tItems[index].cmd; + cmd[1] = 0x1; + ret = ilits->wrapper(cmd, 2, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + /* test LOW level*/ + cmd[1] = 0x0; + ret = ilits->wrapper(cmd, 2, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + } + + if (tItems[index].int_pulse_test == ENABLE) { + + ILI_INFO("MP IRQ Rising Trigger Test\n"); + cmd[1] = 0x2; + cmd[2] = tItems[index].delay_time; + + ili_irq_unregister(); + ret = ili_irq_register(IRQF_TRIGGER_RISING); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + atomic_set(&ilits->cmd_int_check, ENABLE); + ret = ilits->wrapper(cmd, 3, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + ILI_INFO("MP IRQ Falling Trigger Test\n"); + ili_irq_unregister(); + ret = ili_irq_register(IRQF_TRIGGER_FALLING); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + atomic_set(&ilits->cmd_int_check, ENABLE); + + ret = ilits->wrapper(cmd, 3, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + } + + tItems[index].item_result = MP_DATA_PASS; + +out: + if (ret < 0) + tItems[index].item_result = MP_DATA_FAIL; + + ILI_INFO("Change to defualt IRQ trigger type\n"); + ili_irq_unregister(); + ili_irq_register(ilits->irq_tirgger_type); + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +static int mutual_test(int index) +{ + int i = 0, j = 0, x = 0, y = 0, ret = 0, get_frame_cont = 1; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + if (tItems[i].catalog == TX_RX_DELTA) { + core_mp.tx_max_buf[y * core_mp.xch_len + x] = INT_MIN; + core_mp.rx_max_buf[y * core_mp.xch_len + x] = INT_MIN; + core_mp.tx_min_buf[y * core_mp.xch_len + x] = INT_MAX; + core_mp.rx_min_buf[y * core_mp.xch_len + x] = INT_MAX; + } else { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + } + + if (tItems[index].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[index].frame_count; + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, BENCHMARK_KEY_NAME); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + for (i = 0; i < get_frame_cont; i++) { + ret = allnode_mutual_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + switch (tItems[index].catalog) { + case PIXEL: + run_pixel_test(index); + break; + case UNTOUCH_P2P: + run_untouch_p2p_test(index); + break; + case OPEN_TEST: + run_open_test(index); + break; + case TX_RX_DELTA: + run_tx_rx_delta_test(index); + break; + case SHORT_TEST: + ret = short_test(index, i); + if (ret < 0) { + ret = -EMP_PARA_NULL; + goto out; + } + break; + default: + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[i * core_mp.frame_len + j] = frame_buf[j]; + break; + } + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + } + +out: + return ret; +} + +static int peak_to_peak_test(int index) +{ + int i = 0, j = 0, x = 0, y = 0, ret = 0; + char benchmark_str[128] = {0}; + + ILI_DBG("index = %d, desp = %s bch_mrk_frm_num = %d\n" + , index, tItems[index].desp, tItems[index].bch_mrk_frm_num); + + ret = create_mp_test_frame_buffer(index, tItems[index].bch_mrk_frm_num); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (tItems[index].spec_option == BENCHMARK) { + for ( i = 0 ; i < tItems[index].bch_mrk_frm_num ; i++ ) { + if (tItems[index].bch_mrk_multi) + snprintf(benchmark_str, sizeof(benchmark_str), "%s%d", BENCHMARK_KEY_NAME, i+1); + else + snprintf(benchmark_str, sizeof(benchmark_str), "%s", BENCHMARK_KEY_NAME); + parser_ini_benchmark(tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i], + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, benchmark_str); + + dump_benchmark_data(tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i]); + } + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + ret = allnode_peak_to_peak_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++){ + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[i * core_mp.frame_len + j] = frm_buf[i][j]; + + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + } +out: + return ret; +} + + +static int open_test_sp(int index) +{ + struct mp_test_P540_open *open; + int i = 0, x = 0, y = 0, ret = 0, addr = 0; + int Charge_AA = 0, Charge_Border = 0, Charge_Notch = 0, full_open_rate = 0; + char str[512] = {0}; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + open = kzalloc(tItems[index].frame_count * sizeof(struct mp_test_P540_open), GFP_KERNEL); + if (ERR_ALLOC_MEM(open)) { + ILI_ERR("Failed to allocate open mem (%ld)\n", PTR_ERR(open)); + ret = -EMP_NOMEM; + goto out; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (frame1_cbk700 == NULL) { + frame1_cbk700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk700)) { + ILI_ERR("Failed to allocate frame1_cbk700 buffer\n"); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk700, 0x0, core_mp.frame_len); + } + + if (frame1_cbk250 == NULL) { + frame1_cbk250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk250)) { + ILI_ERR("Failed to allocate frame1_cbk250 buffer\n"); + ipio_kfree((void **)&frame1_cbk700); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk250, 0x0, core_mp.frame_len); + } + + if (frame1_cbk200 == NULL) { + frame1_cbk200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk200)) { + ILI_ERR("Failed to allocate cbk buffer\n"); + ipio_kfree((void **)&frame1_cbk700); + ipio_kfree((void **)&frame1_cbk250); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk200, 0x0, core_mp.frame_len); + } + + tItems[index].node_type = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].node_type)) { + ILI_ERR("Failed to allocate node_type FRAME buffer\n"); + return -EMP_NOMEM; + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, BENCHMARK_KEY_NAME); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + parser_ini_nodetype(tItems[index].node_type, NODE_TYPE_KEY_NAME, core_mp.frame_len); + dump_node_type_buffer(tItems[index].node_type, "node type"); + + parser_get_int_data(tItems[index].desp, "charge_aa", str, sizeof(str)); + Charge_AA = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "charge_border", str, sizeof(str)); + Charge_Border = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "charge_notch", str, sizeof(str)); + Charge_Notch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "full open", str, sizeof(str)); + full_open_rate = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvch", str, sizeof(str)); + open_para.tvch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvcl", str, sizeof(str)); + open_para.tvcl = ili_katoi(str); + + ILI_DBG("AA = %d, Border = %d, Notch = %d, full_open_rate = %d, tvch = %d, tvcl = %d\n", + Charge_AA, Charge_Border, Charge_Notch, + full_open_rate, open_para.tvch, open_para.tvcl); + + for (i = 0; i < tItems[index].frame_count; i++) { + open[i].tdf_700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].tdf_250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].tdf_200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].charg_rate = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].full_Open = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + } + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_open_cdc_data(0, open[i].dac); + if (ret < 0) { + ILI_ERR("Failed to get Open SP DAC data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(1, open[i].tdf_700); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw1 data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(2, open[i].tdf_250); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw2 data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(3, open[i].tdf_200); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw3 data, %d\n", ret); + goto out; + } + allnode_open_cdc_result(index, open[i].cbk_700, open[i].dac, open[i].tdf_700); + allnode_open_cdc_result(index, open[i].cbk_250, open[i].dac, open[i].tdf_250); + allnode_open_cdc_result(index, open[i].cbk_200, open[i].dac, open[i].tdf_200); + + addr = 0; + + /* record fist frame for debug */ + if (i == 0) { + ipio_memcpy(frame1_cbk700, open[i].cbk_700, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(frame1_cbk250, open[i].cbk_250, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(frame1_cbk200, open[i].cbk_200, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + } + + ili_dump_data(open[i].cbk_700, 10, core_mp.frame_len, core_mp.xch_len, "cbk 700"); + ili_dump_data(open[i].cbk_250, 10, core_mp.frame_len, core_mp.xch_len, "cbk 250"); + ili_dump_data(open[i].cbk_200, 10, core_mp.frame_len, core_mp.xch_len, "cbk 200"); + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + open[i].charg_rate[addr] = open[i].cbk_250[addr] * 100 / open[i].cbk_700[addr]; + open[i].full_Open[addr] = open[i].cbk_700[addr] - open[i].cbk_200[addr]; + addr++; + } + } + + ili_dump_data(open[i].charg_rate, 10, core_mp.frame_len, core_mp.xch_len, "origin charge rate"); + ili_dump_data(open[i].full_Open, 10, core_mp.frame_len, core_mp.xch_len, "origin full open"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + if (full_open_rate_compare(open[i].full_Open, open[i].cbk_700, x, y, tItems[index].node_type[addr], full_open_rate) == false) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = 0; + open[i].charg_rate[addr] = 0; + } + addr++; + } + } + + ili_dump_data(&tItems[index].buf[(i * core_mp.frame_len)], 10, core_mp.frame_len, core_mp.xch_len, "after full_open_rate_compare"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = compare_charge(open[i].charg_rate, x, y, tItems[index].node_type, Charge_AA, Charge_Border, Charge_Notch); + addr++; + } + } + + ili_dump_data(&tItems[index].buf[(i * core_mp.frame_len)], 10, core_mp.frame_len, core_mp.xch_len, "after compare charge rate"); + + compare_MaxMin_result(index, &tItems[index].buf[(i * core_mp.frame_len)]); + } + +out: + ipio_kfree((void **)&tItems[index].node_type); + + if (open != NULL) { + for (i = 0; i < tItems[index].frame_count; i++) { + ipio_kfree((void **)&open[i].tdf_700); + ipio_kfree((void **)&open[i].tdf_250); + ipio_kfree((void **)&open[i].tdf_200); + ipio_kfree((void **)&open[i].cbk_700); + ipio_kfree((void **)&open[i].cbk_250); + ipio_kfree((void **)&open[i].cbk_200); + ipio_kfree((void **)&open[i].charg_rate); + ipio_kfree((void **)&open[i].full_Open); + ipio_kfree((void **)&open[i].dac); + } + kfree(open); + open = NULL; + } + + return ret; +} + +static int open_test_cap(int index) +{ + u32 pid = core_mp.chip_pid >> 8; + struct mp_test_open_c *open; + int i = 0, x = 0, y = 0, ret = 0, addr = 0; + char str[512] = {0}; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + open = kzalloc(tItems[index].frame_count * sizeof(struct mp_test_open_c), GFP_KERNEL); + if (ERR_ALLOC_MEM(open)) { + ILI_ERR("Failed to allocate open mem (%ld)\n", PTR_ERR(open)); + ret = -EMP_NOMEM; + goto out; + } + + if (create_mp_test_frame_buffer(index, tItems[index].frame_count) < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (cap_dac == NULL) { + cap_dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(cap_dac)) { + ILI_ERR("Failed to allocate cap_dac buffer\n"); + return -EMP_NOMEM; + } + } else { + memset(cap_dac, 0x0, core_mp.frame_len); + } + + if (cap_raw == NULL) { + cap_raw = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(cap_raw)) { + ILI_ERR("Failed to allocate cap_raw buffer\n"); + ipio_kfree((void **)&cap_dac); + return -EMP_NOMEM; + } + } else { + memset(cap_raw, 0x0, core_mp.frame_len); + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, BENCHMARK_KEY_NAME); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + parser_get_int_data(tItems[index].desp, "gain", str, sizeof(str)); + open_para.gain = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvch", str, sizeof(str)); + open_para.tvch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvcl", str, sizeof(str)); + open_para.tvcl = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "cbk_step", str, sizeof(str)); + open_para.cbk_step = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "cint", str, sizeof(str)); + open_para.cint = ili_katoi(str); + + /* 9881N, 9881O, 7807Q, 7807S not support cbk_step and cint read from mp.ini */ + if ((pid != 0x988117) && (pid != 0x988118) && (pid != 0x78071A) && (pid != 0x78071C)) { + if ((open_para.cbk_step == 0) || (open_para.cint == 0)) { + ILI_ERR("Failed to get open parameter"); + ret = -EMP_PARA_NULL; + core_mp.lost_parameter = true; + goto out; + } + } + + ILI_INFO("gain = %d, tvch = %d, tvcl = %d, cbk_step = %d, cint = %d\n", open_para.gain, open_para.tvch, open_para.tvcl, open_para.cbk_step, open_para.cint ); + for (i = 0; i < tItems[index].frame_count; i++) { + open[i].cap_dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cap_raw = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].dcl_cap = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + } + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_open_cdc_data(4, open[i].cap_dac); + if (ret < 0) { + ILI_ERR("Failed to get Open CAP DAC data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(5, open[i].cap_raw); + if (ret < 0) { + ILI_ERR("Failed to get Open CAP RAW data, %d\n", ret); + goto out; + } + + allnode_open_cdc_result(index, open[i].dcl_cap, open[i].cap_dac, open[i].cap_raw); + + /* record fist frame for debug */ + if (i == 0) { + ipio_memcpy(cap_dac, open[i].cap_dac, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(cap_raw, open[i].cap_raw, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + } + + ili_dump_data(open[i].dcl_cap, 10, core_mp.frame_len, core_mp.xch_len, "DCL_Cap"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = open[i].dcl_cap[addr]; + addr++; + } + } + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + } + +out: + if (open != NULL) { + for (i = 0; i < tItems[index].frame_count; i++) { + ipio_kfree((void **)&open[i].cap_dac); + ipio_kfree((void **)&open[i].cap_raw); + ipio_kfree((void **)&open[i].dcl_cap); + } + kfree(open); + open = NULL; + } + + return ret; +} + +static int key_test(int index) +{ + int i, j = 0, ret = 0; + + ILI_DBG("Item = %s, Frame Count = %d\n", + tItems[index].desp, tItems[index].frame_count); + + if (tItems[index].frame_count == 0) { + ILI_ERR("Frame count is zero, which at least sets as 1\n"); + ret = -EINVAL; + goto out; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) + goto out; + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_key_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + + for (j = 0; j < core_mp.key_len; j++) + tItems[index].buf[j] = key_buf[j]; + } + + compare_MaxMin_result(index, tItems[index].buf); + +out: + return ret; +} + +static int self_test(int index) +{ + ILI_ERR("TDDI has no self to be tested currently\n"); + return -1; +} + +static int st_test(int index) +{ + ILI_ERR("ST Test is not supported by the driver\n"); + return -1; +} + +static int mp_get_timing_info(void) +{ + int slen = 0; + char str[256] = {0}; + u8 info[64] = {0}; + char *key = "timing_info_raw"; + + core_mp.isLongV = 0; + + slen = parser_get_int_data("pv5_4 command", key, str, sizeof(str)); + if (slen < 0) + return -1; + + if (parser_get_u8_array(str, info, 16, slen) < 0) + return -1; + + core_mp.isLongV = info[6]; + + ILI_INFO("DDI Mode = %s\n", (core_mp.isLongV ? "Long V" : "Long H")); + + return 0; +} + +static int mp_test_data_sort_average(s32 *oringin_data, int index, s32 *avg_result) +{ + int i, j, k, x, y, len = 5, size; + s32 u32temp; + int u32up_frame, u32down_frame; + s32 *u32sum_raw_data = NULL; + s32 *u32data_buff = NULL; + int ret = 0; + + if (tItems[index].frame_count <= 1) { + ret = 0; + goto out; + } + + if (ERR_ALLOC_MEM(oringin_data)) { + ILI_ERR("Input wrong address\n"); + ret = -ENOMEM; + goto out; + } + + u32data_buff = kcalloc(core_mp.frame_len * tItems[index].frame_count, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(u32data_buff)) { + ILI_ERR("Failed to allocate u32data_buff FRAME buffer\n"); + ret = -ENOMEM; + goto out; + } + + u32sum_raw_data = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(u32data_buff)) { + ILI_ERR("Failed to allocate u32sum_raw_data FRAME buffer\n"); + ret = -ENOMEM; + goto out; + } + + size = core_mp.frame_len * tItems[index].frame_count; + for (i = 0; i < size; i++) { + u32data_buff[i] = oringin_data[i]; + } + + u32up_frame = tItems[index].frame_count * tItems[index].highest_percentage / 100; + u32down_frame = tItems[index].frame_count * tItems[index].lowest_percentage / 100; + ILI_DBG("Up=%d, Down=%d -%s\n", u32up_frame, u32down_frame, tItems[index].desp); + + if (debug_en) { + pr_cont("\n[Show Original frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + for (j = 0 ; j < tItems[index].frame_count ; j++) { + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("%d,", u32data_buff[j * core_mp.frame_len + i]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + } + + for (i = 0; i < core_mp.frame_len; i++) { + for (j = 0; j < tItems[index].frame_count-1; j++) { + for (k = 0; k < (tItems[index].frame_count-1-j); k++) { + x = i+k*core_mp.frame_len; + y = i+(k+1)*core_mp.frame_len; + if (*(u32data_buff+x) > *(u32data_buff+y)) { + u32temp = *(u32data_buff+x); + *(u32data_buff+x) = *(u32data_buff+y); + *(u32data_buff+y) = u32temp; + } + } + } + } + + if (debug_en) { + pr_cont("\n[After sorting frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + for (j = u32down_frame; j < tItems[index].frame_count - u32up_frame; j++) { + if ((i < len) || (i >= (core_mp.frame_len - len))) + pr_cont("%d,", u32data_buff[i + j * core_mp.frame_len]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + } + + for (i = 0 ; i < core_mp.frame_len ; i++) { + u32sum_raw_data[i] = 0; + for (j = u32down_frame; j < tItems[index].frame_count - u32up_frame; j++) + u32sum_raw_data[i] += u32data_buff[i + j * core_mp.frame_len]; + + avg_result[i] = u32sum_raw_data[i] / (tItems[index].frame_count - u32down_frame - u32up_frame); + } + + if (debug_en) { + pr_cont("\n[Average result frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("%d,", avg_result[i]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + +out: + ipio_kfree((void **)&u32data_buff); + ipio_kfree((void **)&u32sum_raw_data); + return ret; +} + +static void mp_compare_cdc_result(int index, s32 *tmp, s32 *max_ts, s32 *min_ts, int *result) +{ + int i; + + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("The data of test item is null (%p)\n", tmp); + *result = MP_DATA_FAIL; + return; + } + + if (tItems[index].catalog == SHORT_TEST) { + for (i = 0; i < core_mp.frame_len; i++) { + if (tmp[i] < min_ts[i]) { + *result = MP_DATA_FAIL; + return; + } + } + } else { + for (i = 0; i < core_mp.frame_len; i++) { + if (tmp[i] > max_ts[i] || tmp[i] < min_ts[i]) { + ILI_DBG("Fail No.%d: max=%d, val=%d, min=%d\n", i, max_ts[i], tmp[i], min_ts[i]); + *result = MP_DATA_FAIL; + return; + } + } + } +} + +static int mp_compare_test_result(int index) +{ + int i, test_result = MP_DATA_PASS; + s32 *max_threshold = NULL, *min_threshold = NULL; + + if (tItems[index].catalog == PIN_TEST) + return tItems[index].item_result; + + max_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + test_result = MP_DATA_FAIL; + goto out; + } + + min_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(min_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + test_result = MP_DATA_FAIL; + goto out; + } + + /* Show test result as below */ + if (tItems[index].catalog == TX_RX_DELTA) { + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf) || ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[index].desp); + test_result = MP_DATA_FAIL; + goto out; + } + + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = core_mp.TxDeltaMax; + min_threshold[i] = core_mp.TxDeltaMin; + } + mp_compare_cdc_result(index, core_mp.tx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, core_mp.tx_min_buf, max_threshold, min_threshold, &test_result); + + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = core_mp.RxDeltaMax; + min_threshold[i] = core_mp.RxDeltaMin; + } + + mp_compare_cdc_result(index, core_mp.rx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, core_mp.rx_min_buf, max_threshold, min_threshold, &test_result); + } else { + if (ERR_ALLOC_MEM(tItems[index].buf) || ERR_ALLOC_MEM(tItems[index].max_buf) || + ERR_ALLOC_MEM(tItems[index].min_buf) || ERR_ALLOC_MEM(tItems[index].result_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[index].desp); + test_result = MP_DATA_FAIL; + goto out; + } + + if (tItems[index].spec_option == BENCHMARK) { + if (tItems[index].catalog == PEAK_TO_PEAK_TEST) { + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = tItems[index].bch_mrk_max[0][i]; + min_threshold[i] = tItems[index].bch_mrk_min[0][i]; + } + } else { + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = tItems[index].bench_mark_max[i]; + min_threshold[i] = tItems[index].bench_mark_min[i]; + } + } + } else { + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = tItems[index].max; + min_threshold[i] = tItems[index].min; + } + } + + /* general result */ + if (tItems[index].trimmed_mean && tItems[index].catalog != PEAK_TO_PEAK_TEST) { + mp_test_data_sort_average(tItems[index].buf, index, tItems[index].result_buf); + mp_compare_cdc_result(index, tItems[index].result_buf, max_threshold, min_threshold, &test_result); + } else { + if (tItems[index].bch_mrk_multi) { + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + mp_compare_cdc_result(index, frm_buf[i], tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i], &test_result); + } + } else { + mp_compare_cdc_result(index, tItems[index].max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, tItems[index].min_buf, max_threshold, min_threshold, &test_result); + } + } + } + +out: + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); + if (frm_buf != NULL) { + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) + if (frm_buf[i] != NULL) { + kfree(frm_buf[i]); + frm_buf[i] = NULL; + } + kfree(frm_buf); + frm_buf = NULL; + } + if ( core_mp.all_pass == true ){ + test_result = MP_DATA_PASS; + } + tItems[index].item_result = test_result; + return test_result; +} + +static void mp_do_retry(int index, int count) +{ + if (count == 0) { + ILI_INFO("Finish retry action\n"); + return; + } + + ILI_INFO("retry = %d, item = %s\n", count, tItems[index].desp); + + tItems[index].do_test(index); + + if (mp_compare_test_result(index) < 0) + return mp_do_retry(index, count - 1); +} + +static int mp_show_result(bool lcm_on) +{ + int ret = MP_DATA_PASS, seq; + int i, x, y, j, csv_len = 0, pass_item_count = 0, line_count = 0, get_frame_cont = 1; + s32 *max_threshold = NULL, *min_threshold = NULL; + char *csv = NULL; + char *ret_pass_name = NULL, *ret_fail_name = NULL; + struct file *f = NULL; + mm_segment_t fs; + loff_t pos; + + csv = vmalloc(CSV_FILE_SIZE); + if (ERR_ALLOC_MEM(csv)) { + ILI_ERR("Failed to allocate CSV mem\n"); + ret = -EMP_NOMEM; + goto fail_open; + } + + max_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + min_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold) || ERR_ALLOC_MEM(min_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + ret = -EMP_NOMEM; + goto fail_open; + } + + if (parser_get_ini_key_value("pv5_4 command", "date", core_mp.ini_date) < 0) + ipio_memcpy(core_mp.ini_date, "Unknown", strlen("Unknown"), strlen("Unknown")); + if (parser_get_ini_key_value("pv5_4 command", "version", core_mp.ini_ver) < 0) + ipio_memcpy(core_mp.ini_ver, "Unknown", strlen("Unknown"), strlen("Unknown")); + + mp_print_csv_header(csv, &csv_len, &line_count, CSV_FILE_SIZE); + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + get_frame_cont = 1; + + if (tItems[i].item_result == MP_DATA_PASS) { + pr_info("\n[%s],OK \n\n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n[%s],OK\n\n", tItems[i].desp); + } else { + pr_info("\n[%s],NG \n\n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n[%s],NG\n\n", tItems[i].desp); + } + + if (tItems[i].catalog == PIN_TEST) { + pr_info("Test INT Pin = %d\n", tItems[i].test_int_pin); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Test INT Pin = %d\n", tItems[i].test_int_pin); + pr_info("Pulse Test = %d\n", tItems[i].int_pulse_test); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Pulse Test = %d\n", tItems[i].int_pulse_test); + pr_info("Delay Time = %d\n", tItems[i].delay_time); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Delay Time = %d\n", tItems[i].delay_time); + continue; + } + + mp_print_csv_cdc_cmd(csv, &csv_len, i, CSV_FILE_SIZE); + + pr_info("Frame count = %d\n", tItems[i].frame_count); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Frame count = %d\n", tItems[i].frame_count); + + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + pr_info("lowest percentage = %d\n", tItems[i].lowest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "lowest percentage = %d\n", tItems[i].lowest_percentage); + + pr_info("highest percentage = %d\n", tItems[i].highest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "highest percentage = %d\n", tItems[i].highest_percentage); + } + + /* Show result of benchmark max and min */ + if (tItems[i].spec_option == BENCHMARK) { + if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].bch_mrk_max[0][j]; + min_threshold[j] = tItems[i].bch_mrk_min[0][j]; + } + + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) { + char bench_name[128] = {0}; + snprintf(bench_name, (CSV_FILE_SIZE - csv_len), "Max_Bench%d", (j+1)); + mp_compare_cdc_show_result(i, tItems[i].bch_mrk_max[j], csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, bench_name, CSV_FILE_SIZE); + snprintf(bench_name, (CSV_FILE_SIZE - csv_len), "Min_Bench%d", (j+1)); + mp_compare_cdc_show_result(i, tItems[i].bch_mrk_min[j], csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, bench_name, CSV_FILE_SIZE); + } + } else { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].bench_mark_max[j]; + min_threshold[j] = tItems[i].bench_mark_min[j]; + } + mp_compare_cdc_show_result(i, tItems[i].bench_mark_max, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Max_Bench", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, tItems[i].bench_mark_min, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Min_Bench", CSV_FILE_SIZE); + } + + } else { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].max; + min_threshold[j] = tItems[i].min; + } + + pr_info("Max = %d\n", tItems[i].max); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Max = %d\n", tItems[i].max); + + pr_info("Min = %d\n", tItems[i].min); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Min = %d\n", tItems[i].min); + } + + if (ipio_strcmp(tItems[i].desp, "open test(integration)_sp") == 0) { + mp_compare_cdc_show_result(i, frame1_cbk700, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk700", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, frame1_cbk250, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk250", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, frame1_cbk200, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk200", CSV_FILE_SIZE); + } + + if (ipio_strcmp(tItems[i].desp, "open test_c") == 0) { + mp_compare_cdc_show_result(i, cap_dac, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_DAC", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, cap_raw, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_RAW", CSV_FILE_SIZE); + } + + if (tItems[i].catalog == TX_RX_DELTA) { + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf) || ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } else { + if (ERR_ALLOC_MEM(tItems[i].buf) || ERR_ALLOC_MEM(tItems[i].max_buf) || + ERR_ALLOC_MEM(tItems[i].min_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } + + /* Show test result as below */ + if (tItems[i].catalog == KEY_TEST) { + for (x = 0; x < core_mp.key_len; x++) { + DUMP("KEY_%02d ", x); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "KEY_%02d,", x); + } + + DUMP("\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n"); + + for (y = 0; y < core_mp.key_len; y++) { + DUMP(" %3d ", tItems[i].buf[y]); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), " %3d, ", tItems[i].buf[y]); + } + + DUMP("\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n"); + } else if (tItems[i].catalog == TX_RX_DELTA) { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = core_mp.TxDeltaMax; + min_threshold[j] = core_mp.TxDeltaMin; + } + mp_compare_cdc_show_result(i, core_mp.tx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, core_mp.tx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Min Hold", CSV_FILE_SIZE); + + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = core_mp.RxDeltaMax; + min_threshold[j] = core_mp.RxDeltaMin; + } + mp_compare_cdc_show_result(i, core_mp.rx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, core_mp.rx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Min Hold", CSV_FILE_SIZE); + } else { + /* general result */ + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + mp_compare_cdc_show_result(i, tItems[i].result_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Mean result", CSV_FILE_SIZE); + } else { + if (!tItems[i].bch_mrk_multi) { + mp_compare_cdc_show_result(i, tItems[i].max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, tItems[i].min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Min Hold", CSV_FILE_SIZE); + } + + } + if (tItems[i].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[i].frame_count; + + if (tItems[i].bch_mrk_multi) + get_frame_cont = tItems[i].bch_mrk_frm_num; + + /* result of each frame */ + for (j = 0; j < get_frame_cont; j++) { + char frame_name[128] = {0}; + snprintf(frame_name, (CSV_FILE_SIZE - csv_len), "Frame %d", (j+1)); + mp_compare_cdc_show_result(i, &tItems[i].buf[(j*core_mp.frame_len)], csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, frame_name, CSV_FILE_SIZE); + } + } + } + + memset(csv_name, 0, 128 * sizeof(char)); + + mp_print_csv_tail(csv, &csv_len, CSV_FILE_SIZE); + + for (i = 0; i < MP_TEST_ITEM; i++) { + if (tItems[i].run) { + if (tItems[i].item_result < 0) { + pass_item_count = 0; + break; + } + pass_item_count++; + } + } + + /* define csv file name */ + ret_pass_name = NORMAL_CSV_PASS_NAME; + ret_fail_name = NORMAL_CSV_FAIL_NAME; + + if (pass_item_count == 0) { + core_mp.final_result = MP_DATA_FAIL; + ret = MP_DATA_FAIL; + + if(core_mp.lost_parameter){ + ret_fail_name = NULL; + ret_fail_name = NORMAL_CSV_WARNING_NAME; + ILI_ERR("WARNING! OPEN or SHORT parameter not found in ini file!!\n"); + } + + if (lcm_on) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_ON_PATH, get_date_time_str(), ret_fail_name); + else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_OFF_PATH, get_date_time_str(), ret_fail_name); + } else { + core_mp.final_result = MP_DATA_PASS; + ret = MP_DATA_PASS; + + if (core_mp.lost_benchmark) { + ret_pass_name = NULL; + ret_pass_name = NORMAL_CSV_WARNING_NAME; + ILI_ERR("WARNING! Golden and SPEC in ini file aren't matched!!\n"); + } + + if (lcm_on) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_ON_PATH, get_date_time_str(), ret_pass_name); + else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_OFF_PATH, get_date_time_str(), ret_pass_name); + } + + ILI_INFO("Open CSV : %s\n", csv_name); + + if (f == NULL) + f = filp_open(csv_name, O_WRONLY | O_CREAT | O_TRUNC, 644); + + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open CSV file"); + ret = -EMP_NOMEM; + goto fail_open; + } + + ILI_INFO("Open CSV succeed, its length = %d\n ", csv_len); + + if (csv_len >= CSV_FILE_SIZE) { + ILI_ERR("The length saved to CSV is too long !\n"); + ret = -EMP_INVAL; + goto fail_open; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, csv, csv_len, &pos); + set_fs(fs); + filp_close(f, NULL); + + ILI_INFO("Writing Data into CSV succeed\n"); + +fail_open: + ipio_vfree((void **)&csv); + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); + return ret; +} + +static void ilitek_tddi_mp_init_item(void) +{ + int i = 0; + + memset(&core_mp, 0, sizeof(core_mp)); + memset(&open_para, 0, sizeof(open_para)); + memset(&short_para, 0, sizeof(short_para)); + + core_mp.chip_pid = ilits->chip->pid; + core_mp.chip_id = ilits->chip->id; + core_mp.chip_type = ilits->chip->type; + core_mp.chip_ver = ilits->chip->ver; + core_mp.fw_ver = ilits->chip->fw_ver; + core_mp.protocol_ver = ilits->protocol->ver; + core_mp.core_ver = ilits->chip->core_ver; + core_mp.cdc_len = ilits->protocol->cdc_len; + core_mp.no_bk_shift = ilits->chip->no_bk_shift; + core_mp.xch_len = ilits->xch_num; + core_mp.ych_len = ilits->ych_num; + core_mp.frame_len = core_mp.xch_len * core_mp.ych_len; + core_mp.stx_len = 0; + core_mp.srx_len = 0; + core_mp.key_len = 0; + core_mp.st_len = 0; + core_mp.tdf = 240; + core_mp.busy_cdc = INT_CHECK; + core_mp.retry = ilits->mp_retry; + core_mp.td_retry = false; + core_mp.final_result = MP_DATA_FAIL; + core_mp.lost_benchmark = false; + core_mp.lost_parameter = false; + core_mp.all_pass = false; + + ILI_INFO("============== TP & Panel info ================\n"); + ILI_INFO("Driver version = %s\n", DRIVER_VERSION); + ILI_INFO("TP Module = %s\n", ilits->md_name); + ILI_INFO("CHIP = 0x%x\n", core_mp.chip_pid); + ILI_INFO("Firmware version = %x\n", core_mp.fw_ver); + ILI_INFO("Protocol version = %x\n", core_mp.protocol_ver); + ILI_INFO("Core version = %x\n", core_mp.core_ver); + ILI_INFO("Read CDC Length = %d\n", core_mp.cdc_len); + ILI_INFO("X length = %d, Y length = %d\n", core_mp.xch_len, core_mp.ych_len); + ILI_INFO("Frame length = %d\n", core_mp.frame_len); + ILI_INFO("Check busy method = %s\n", (core_mp.busy_cdc ? "Polling" : "Interrupt")); + ILI_INFO("===============================================\n"); + + for (i = 0; i < MP_TEST_ITEM; i++) { + tItems[i].spec_option = 0; + tItems[i].type_option = 0; + tItems[i].run = false; + tItems[i].max = 0; + tItems[i].max_res = MP_DATA_FAIL; + tItems[i].item_result = MP_DATA_PASS; + tItems[i].min = 0; + tItems[i].min_res = MP_DATA_FAIL; + tItems[i].frame_count = 0; + tItems[i].trimmed_mean = 0; + tItems[i].lowest_percentage = 0; + tItems[i].highest_percentage = 0; + tItems[i].v_tdf_1 = 0; + tItems[i].v_tdf_2 = 0; + tItems[i].h_tdf_1 = 0; + tItems[i].h_tdf_2 = 0; + tItems[i].max_min_mode = 0; + tItems[i].bch_mrk_multi = false; + tItems[i].bch_mrk_frm_num = 1; + tItems[i].goldenmode = 0; + tItems[i].retry_cnt = RETRY_COUNT; + tItems[i].result_buf = NULL; + tItems[i].buf = NULL; + tItems[i].max_buf = NULL; + tItems[i].min_buf = NULL; + tItems[i].bench_mark_max = NULL; + tItems[i].bench_mark_min = NULL; + tItems[i].bch_mrk_max = NULL; + tItems[i].bch_mrk_min = NULL; + tItems[i].node_type = NULL; + tItems[i].delay_time = 0; + tItems[i].test_int_pin = 0; + tItems[i].int_pulse_test = 0; + + if (tItems[i].catalog == MUTUAL_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == TX_RX_DELTA) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == UNTOUCH_P2P) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == PIXEL) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == OPEN_TEST) { + if (ipio_strcmp(tItems[i].desp, "open test(integration)_sp") == 0) + tItems[i].do_test = open_test_sp; + else if (ipio_strcmp(tItems[i].desp, "open test_c") == 0) + tItems[i].do_test = open_test_cap; + else + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == KEY_TEST) { + tItems[i].do_test = key_test; + } else if (tItems[i].catalog == SELF_TEST) { + tItems[i].do_test = self_test; + } else if (tItems[i].catalog == ST_TEST) { + tItems[i].do_test = st_test; + } else if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + tItems[i].do_test = peak_to_peak_test; + } else if (tItems[i].catalog == SHORT_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == PIN_TEST) { + tItems[i].do_test = pin_test; + } + + tItems[i].result = kmalloc(16, GFP_KERNEL); + snprintf(tItems[i].result, 16, "%s", "FAIL"); + } +} + +static void mp_p2p_td_retry_after_ra_fail(int p2p_td) +{ + int i; + + for (i = 0; i < MP_TEST_ITEM; i++) { + if (ipio_strcmp(tItems[i].desp, "noise peak to peak(with panel) (lcm off)") == 0) + break; + } + + if (i >= MP_TEST_ITEM) + return; + + ILI_DBG("i = %d, p2p_noise_ret = %d, p2p_noise_run = %d\n", + i, tItems[i].item_result, tItems[i].run); + + if (tItems[i].item_result == MP_DATA_PASS && tItems[i].run == 1) + tItems[p2p_td].do_test(p2p_td); +} + +static void mp_test_run(int index) +{ + int i = index, j; + char str[512] = {0}; + + + /* Get parameters from ini */ + parser_get_int_data(tItems[i].desp, "spec option", str, sizeof(str)); + tItems[i].spec_option = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "type option", str, sizeof(str)); + tItems[i].type_option = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "frame count", str, sizeof(str)); + tItems[i].frame_count = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "trimmed mean", str, sizeof(str)); + tItems[i].trimmed_mean = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "lowest percentage", str, sizeof(str)); + tItems[i].lowest_percentage = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "highest percentage", str, sizeof(str)); + tItems[i].highest_percentage = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "goldenmode", str, sizeof(str)); + tItems[i].goldenmode = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "max min mode", str, sizeof(str)); + tItems[i].max_min_mode = ili_katoi(str); + + parser_get_int_data(tItems[i].desp, "retry count", str, sizeof(str)); + tItems[i].retry_cnt = ili_katoi(str); + + if (tItems[i].goldenmode && (tItems[i].spec_option != tItems[i].goldenmode)) + core_mp.lost_benchmark = true; + + /* Get pin test delay time */ + if (tItems[i].catalog == PIN_TEST) { + parser_get_int_data(tItems[i].desp, "test int pin", str, sizeof(str)); + tItems[i].test_int_pin = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "int pulse test", str, sizeof(str)); + tItems[i].int_pulse_test = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "delay time", str, sizeof(str)); + tItems[i].delay_time = ili_katoi(str); + } + + /* Get TDF value from ini */ + if (tItems[i].catalog == SHORT_TEST) { + parser_get_int_data(tItems[i].desp, "v_tdf_1", str, sizeof(str)); + tItems[i].v_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "v_tdf_2", str, sizeof(str)); + tItems[i].v_tdf_2 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf_1", str, sizeof(str)); + tItems[i].h_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf_2", str, sizeof(str)); + tItems[i].h_tdf_2 = parser_get_tdf_value(str, tItems[i].catalog); + } else { + parser_get_int_data(tItems[i].desp, "v_tdf", str, sizeof(str)); + tItems[i].v_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf", str, sizeof(str)); + tItems[i].h_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + } + + /* Get threshold from ini structure in parser */ + if (ipio_strcmp(tItems[i].desp, "tx/rx delta") == 0) { + parser_get_int_data(tItems[i].desp, "tx max", str, sizeof(str)); + core_mp.TxDeltaMax = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "tx min", str, sizeof(str)); + core_mp.TxDeltaMin = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "rx max", str, sizeof(str)); + core_mp.RxDeltaMax = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "rx min", str, sizeof(str)); + core_mp.RxDeltaMin = ili_katoi(str); + ILI_DBG("%s: Tx Max = %d, Tx Min = %d, Rx Max = %d, Rx Min = %d\n", + tItems[i].desp, core_mp.TxDeltaMax, core_mp.TxDeltaMin, + core_mp.RxDeltaMax, core_mp.RxDeltaMin); + } else { + parser_get_int_data(tItems[i].desp, "max", str, sizeof(str)); + tItems[i].max = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "min", str, sizeof(str)); + tItems[i].min = ili_katoi(str); + } + + + /* Get pin test delay time */ + if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + if (tItems[i].max_min_mode == 1 && tItems[i].spec_option == 1) { + tItems[i].bch_mrk_multi = true; + for (j = 1; j < 11; j++) { + char tmp[8] = {0}; + /* format complete string from the name of section "_Benchmark_Data". */ + snprintf(str, sizeof(str), "%s%s%s%d", tItems[index].desp, "_", BENCHMARK_KEY_NAME, j); + if (parser_get_int_data(str, str, tmp, sizeof(tmp)) <= 0) + break; + + tItems[i].bch_mrk_frm_num = j; + } + ILI_DBG("set bch_mrk_frm_num = %d\n", tItems[i].bch_mrk_frm_num); + } + } + + ILI_DBG("%s: run = %d, max = %d, min = %d, frame_count = %d\n", tItems[i].desp, + tItems[i].run, tItems[i].max, tItems[i].min, tItems[i].frame_count); + + ILI_DBG("v_tdf_1 = %d, v_tdf_2 = %d, h_tdf_1 = %d, h_tdf_2 = %d", tItems[i].v_tdf_1, + tItems[i].v_tdf_2, tItems[i].h_tdf_1, tItems[i].h_tdf_2); + + ILI_INFO("Run MP Test Item : %s\n", tItems[i].desp); + tItems[i].do_test(i); + + mp_compare_test_result(i); + + /* P2P TD retry after RA sample failed. */ + if (ipio_strcmp(tItems[i].desp, "peak to peak_td (lcm off)") == 0 && + tItems[i].item_result == MP_DATA_FAIL) { + parser_get_int_data(tItems[i].desp, "recheck ptop lcm off", str, sizeof(str)); + ILI_INFO("Peak to Peak TD retry = %d\n", ili_katoi(str)); + core_mp.td_retry = ili_katoi(str); + if (core_mp.td_retry) + mp_p2p_td_retry_after_ra_fail(i); + } + + if (core_mp.retry && tItems[i].item_result == MP_DATA_FAIL) { + ILI_INFO("MP failed, doing retry %d times\n", tItems[i].retry_cnt); + mp_do_retry(i, tItems[i].retry_cnt); + } + +} + +static void mp_test_free(void) +{ + int i, j; + + ILI_INFO("Free all allocated mem for MP\n"); + + core_mp.final_result = MP_DATA_FAIL; + core_mp.td_retry = false; + + for (i = 0; i < MP_TEST_ITEM; i++) { + tItems[i].run = false; + tItems[i].max_res = MP_DATA_FAIL; + tItems[i].min_res = MP_DATA_FAIL; + tItems[i].item_result = MP_DATA_PASS; + + if (tItems[i].catalog == TX_RX_DELTA) { + ipio_kfree((void **)&core_mp.rx_delta_buf); + ipio_kfree((void **)&core_mp.tx_delta_buf); + ipio_kfree((void **)&core_mp.tx_max_buf); + ipio_kfree((void **)&core_mp.tx_min_buf); + ipio_kfree((void **)&core_mp.rx_max_buf); + ipio_kfree((void **)&core_mp.rx_min_buf); + } else { + if (tItems[i].spec_option == BENCHMARK) { + ipio_kfree((void **)&tItems[i].bench_mark_max); + ipio_kfree((void **)&tItems[i].bench_mark_min); + if (tItems[i].bch_mrk_max != NULL) { + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) + if (tItems[i].bch_mrk_max[j] != NULL) { + kfree(tItems[i].bch_mrk_max[j]); + tItems[i].bch_mrk_max[j] = NULL; + } + kfree(tItems[i].bch_mrk_max); + tItems[i].bch_mrk_max = NULL; + } + if (tItems[i].bch_mrk_min != NULL) { + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) + if (tItems[i].bch_mrk_min[j] != NULL) { + kfree(tItems[i].bch_mrk_min[j]); + tItems[i].bch_mrk_min[j] = NULL; + } + kfree(tItems[i].bch_mrk_min); + tItems[i].bch_mrk_min = NULL; + } + } + ipio_kfree((void **)&tItems[i].node_type); + ipio_kfree((void **)&tItems[i].result); + ipio_kfree((void **)&tItems[i].result_buf); + ipio_kfree((void **)&tItems[i].max_buf); + ipio_kfree((void **)&tItems[i].min_buf); + ipio_vfree((void **)&tItems[i].buf); + } + } + + ipio_kfree((void **)&frame1_cbk700); + ipio_kfree((void **)&frame1_cbk250); + ipio_kfree((void **)&frame1_cbk200); + ipio_kfree((void **)&frame_buf); + ipio_kfree((void **)&key_buf); + if (frm_buf != NULL) { + for (j = 0; j < 3; j++) + if (frm_buf[j] != NULL) { + kfree(frm_buf[j]); + frm_buf[j] = NULL; + } + kfree(frm_buf); + frm_buf = NULL; + } +} + +/* The method to copy results to user depends on what APK needs */ +static void mp_copy_ret_to_apk(char *buf) +{ + int i, seq, len = 2; + + if (!buf) { + ILI_ERR("apk buffer is null\n"); + return; + } + + len += snprintf(buf + len, PAGE_SIZE - len, "CSV path: %s\n\n", csv_name); + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + if (tItems[i].item_result == MP_DATA_FAIL) { + ILI_ERR("[%s] = FAIL\n", tItems[i].desp); + len += snprintf(buf + len, PAGE_SIZE - len, "[%s] = FAIL\n", tItems[i].desp); + } else { + ILI_INFO("[%s] = PASS\n", tItems[i].desp); + len += snprintf(buf + len, PAGE_SIZE - len, "[%s] = PASS\n", tItems[i].desp); + } + } + ilits->mp_ret_len = len; +} + +static int mp_sort_item(bool lcm_on) +{ + int i, j; + char str[128] = {0}; + + ri.count = 0; + memset(ri.index, 0x0, MP_TEST_ITEM); + + for (i = 0; i < MAX_SECTION_NUM; i++) { + for (j = 0; j < MP_TEST_ITEM; j++) { + if (ipio_strcmp(seq_item[i], tItems[j].desp) != 0) + continue; + + parser_get_int_data(tItems[j].desp, "enable", str, sizeof(str)); + tItems[j].run = ili_katoi(str); + if (tItems[j].run != 1 || tItems[j].lcm != lcm_on) + continue; + + if (ri.count > MP_TEST_ITEM) { + ILI_ERR("Test item(%d) is invaild, abort\n", ri.count); + return -EINVAL; + } + + ri.index[ri.count] = j; + ri.count++; + } + } + return 0; +} + +static void ilitek_tddi_mp_all_pass_check(void) +{ + int ret = 0, retry = 5; + u8 cmd[1] = {0xFD}; + u8 temp[3] = {0}; + + ilits->wait_int_timeout = 20; + do { + ret = ilits->wrapper(cmd, 1, temp, 3, ON, OFF); + if (ret < 0) { + ILI_ERR("Write MP All Pass command failed\n"); + core_mp.all_pass = false; + } else { + if (temp[0] == 0xFD) + break; + } + } while (--retry >= 0); + ilits->wait_int_timeout = MP_INT_TIMEOUT; + if (retry <= 0) { + ILI_ERR("MP All Pass command failed, normal mode\n"); + core_mp.all_pass = false; + return; + } + + if ( temp[2] == ili_calc_packet_checksum(temp, sizeof(temp) - 1) && + temp[0] == 0xFD && temp[1] == 0x02) { //0xFD:back door command header, 0x02:Normal mode + core_mp.all_pass = true; + ILI_ERR("MP mode check: Back door mode, MP All Pass\n"); + } else { + core_mp.all_pass = false; //0xFD:back door command header, 0x01:Normal mode + ILI_ERR("MP mode check: Normal mode \n"); + } + + +} + +int ili_mp_test_main(char *apk, bool lcm_on) +{ + int i, ret = 0; + char str[128] = {0}, ver[128] = {0}; + + if (ilits->xch_num <= 0 || ilits->ych_num <= 0) { + ILI_ERR("Invalid frame length (%d, %d)\n", ilits->xch_num, ilits->ych_num); + ret = -EMP_INVAL; + goto out; + } + + if (ini_info == NULL ) { + ini_info = (struct ini_file_data *)vmalloc(sizeof(struct ini_file_data) * PARSER_MAX_KEY_NUM); + if (ERR_ALLOC_MEM(ini_info)) { + ILI_ERR("Failed to malloc ini_info\n"); + ret = -EMP_NOMEM; + goto out; + } + } + + ilitek_tddi_mp_init_item(); + + ilitek_tddi_mp_all_pass_check(); + + + ret = ilitek_tddi_mp_ini_parser(ilits->md_ini_path); + if (ret < 0) { + ILI_ERR("Failed to parsing INI file\n"); + ret = -EMP_INI; + goto out; + } + + /* Compare both protocol version of ini and firmware */ + parser_get_ini_key_value("pv5_4 command", "protocol", str); + snprintf(ver, sizeof(ver), "0x%s", str); + if ((ili_str2hex(ver)) != (core_mp.protocol_ver >> 8)) { + ILI_ERR("ERROR! MP Protocol version is invaild, 0x%x\n", ili_str2hex(ver)); + ret = -EMP_PROTOCOL; + goto out; + } + + /* Read timing info from ini file */ + if (mp_get_timing_info() < 0) { + ILI_ERR("Failed to get timing info from ini\n"); + ret = -EMP_TIMING_INFO; + goto out; + } + +#if MP_INT_LEVEL + if (ili_ic_int_trigger_ctrl(true) < 0) { + ILI_ERR("Failed to set INT as Level trigger"); + ret = -EMP_CMD; + goto out; + } +#endif + + /* Sort test item by ini file */ + if (mp_sort_item(lcm_on) < 0) { + ILI_ERR("Failed to sort test item\n"); + ret = -EMP_INI; + goto out; + } + + for (i = 0; i < ri.count; i++) + mp_test_run(ri.index[i]); + + ret = mp_show_result(lcm_on); + + mp_copy_ret_to_apk(apk); + +out: + mp_test_free(); + +#if MP_INT_LEVEL + if (ili_ic_int_trigger_ctrl(false) < 0) { + ILI_ERR("Failed to set INT back to pluse trigger"); + ret = -EMP_CMD; + } +#endif + + ipio_vfree((void **)&ini_info); + return ret; +}; diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_mtk.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_mtk.c new file mode 100755 index 000000000000..35823d8f1972 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_mtk.c @@ -0,0 +1,459 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" +#include "tpd.h" + +#define DTS_INT_GPIO "touch,irq-gpio" +#define DTS_RESET_GPIO "touch,reset-gpio" +#define DTS_OF_NAME "mediatek,cap_touch" +#define MTK_RST_GPIO GTP_RST_PORT +#define MTK_INT_GPIO GTP_INT_PORT + +extern struct tpd_device *tpd; + +void ili_tp_reset(void) +{ + ILI_INFO("edge delay = %d\n", ilits->rst_edge_delay); + + /* Need accurate power sequence, do not change it to msleep */ + tpd_gpio_output(ilits->tp_rst, 1); + mdelay(1); + tpd_gpio_output(ilits->tp_rst, 0); + mdelay(5); + tpd_gpio_output(ilits->tp_rst, 1); + mdelay(ilits->rst_edge_delay); +} + +void ili_input_register(void) +{ + int i; + + ilits->input = tpd->dev; + + if (tpd_dts_data.use_tpd_button) { + for (i = 0; i < tpd_dts_data.tpd_key_num; i++) + input_set_capability(ilits->input, EV_KEY, tpd_dts_data.tpd_key_local[i]); + } + + /* set the supported event type for input device */ + set_bit(EV_ABS, ilits->input->evbit); + set_bit(EV_SYN, ilits->input->evbit); + set_bit(EV_KEY, ilits->input->evbit); + set_bit(BTN_TOUCH, ilits->input->keybit); + set_bit(BTN_TOOL_FINGER, ilits->input->keybit); + set_bit(INPUT_PROP_DIRECT, ilits->input->propbit); + + if (MT_PRESSURE) + input_set_abs_params(ilits->input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + if (MT_B_TYPE) { +#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM); +#endif /* LINUX_VERSION_CODE */ + } else { + input_set_abs_params(ilits->input, ABS_MT_TRACKING_ID, 0, MAX_TOUCH_NUM, 0, 0); + } + + /* Gesture keys register */ + input_set_capability(ilits->input, EV_KEY, KEY_POWER); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_UP); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_O); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_E); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_M); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_W); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_S); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_V); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_Z); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_C); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_F); + + __set_bit(KEY_GESTURE_POWER, ilits->input->keybit); + __set_bit(KEY_GESTURE_UP, ilits->input->keybit); + __set_bit(KEY_GESTURE_DOWN, ilits->input->keybit); + __set_bit(KEY_GESTURE_LEFT, ilits->input->keybit); + __set_bit(KEY_GESTURE_RIGHT, ilits->input->keybit); + __set_bit(KEY_GESTURE_O, ilits->input->keybit); + __set_bit(KEY_GESTURE_E, ilits->input->keybit); + __set_bit(KEY_GESTURE_M, ilits->input->keybit); + __set_bit(KEY_GESTURE_W, ilits->input->keybit); + __set_bit(KEY_GESTURE_S, ilits->input->keybit); + __set_bit(KEY_GESTURE_V, ilits->input->keybit); + __set_bit(KEY_GESTURE_Z, ilits->input->keybit); + __set_bit(KEY_GESTURE_C, ilits->input->keybit); + __set_bit(KEY_GESTURE_F, ilits->input->keybit); +} + +#if REGULATOR_POWER +void ili_plat_regulator_power_on(bool status) +{ + ILI_INFO("%s\n", status ? "POWER ON" : "POWER OFF"); + + if (status) { + if (ilits->vdd) { + if (regulator_enable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_enable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } else { + if (ilits->vdd) { + if (regulator_disable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_disable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } + atomic_set(&ilits->ice_stat, DISABLE); + mdelay(5); +} + +static void ilitek_plat_regulator_power_init(void) +{ + const char *vdd_name = "vdd"; + const char *vcc_name = "vcc"; + + ilits->vdd = regulator_get(tpd->tpd_dev, vdd_name); + if (ERR_ALLOC_MEM(ilits->vdd)) { + ILI_ERR("regulator_get VDD fail\n"); + ilits->vdd = NULL; + } + + tpd->reg = ilits->vdd; + + if (regulator_set_voltage(ilits->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < 0) + ILI_ERR("Failed to set VDD %d\n", VDD_VOLTAGE); + + ilits->vcc = regulator_get(ilits->dev, vcc_name); + if (ERR_ALLOC_MEM(ilits->vcc)) { + ILI_ERR("regulator_get VCC fail.\n"); + ilits->vcc = NULL; + } + if (regulator_set_voltage(ilits->vcc, VCC_VOLTAGE, VCC_VOLTAGE) < 0) + ILI_ERR("Failed to set VCC %d\n", VCC_VOLTAGE); + + ili_plat_regulator_power_on(true); +} +#endif + +static int ilitek_plat_gpio_register(void) +{ + int ret = 0; + + ilits->tp_int = MTK_INT_GPIO; + ilits->tp_rst = MTK_RST_GPIO; + + ILI_INFO("TP INT: %d\n", ilits->tp_int); + ILI_INFO("TP RESET: %d\n", ilits->tp_rst); + + if (!gpio_is_valid(ilits->tp_int)) { + ILI_ERR("Invalid INT gpio: %d\n", ilits->tp_int); + return -EBADR; + } + + if (!gpio_is_valid(ilits->tp_rst)) { + ILI_ERR("Invalid RESET gpio: %d\n", ilits->tp_rst); + return -EBADR; + } + + gpio_direction_input(ilits->tp_int); + return ret; +} + +void ili_irq_disable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == DISABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + disable_irq_nosync(ilits->irq_num); + atomic_set(&ilits->irq_stat, DISABLE); + ILI_DBG("Disable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +void ili_irq_enable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == ENABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + enable_irq(ilits->irq_num); + atomic_set(&ilits->irq_stat, ENABLE); + ILI_DBG("Enable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +static irqreturn_t ilitek_plat_isr_top_half(int irq, void *dev_id) +{ + if (irq != ilits->irq_num) { + ILI_ERR("Incorrect irq number (%d)\n", irq); + return IRQ_NONE; + } + + if (atomic_read(&ilits->cmd_int_check) == ENABLE) { + atomic_set(&ilits->cmd_int_check, DISABLE); + ILI_DBG("CMD INT detected, ignore\n"); + wake_up(&(ilits->inq)); + return IRQ_HANDLED; + } + + if (ilits->prox_near) { + ILI_INFO("Proximity event, ignore interrupt!\n"); + return IRQ_HANDLED; + } + + ILI_DBG("report: %d, rst: %d, fw: %d, switch: %d, mp: %d, sleep: %d, esd: %d\n", + ilits->report, + atomic_read(&ilits->tp_reset), + atomic_read(&ilits->fw_stat), + atomic_read(&ilits->tp_sw_mode), + atomic_read(&ilits->mp_stat), + atomic_read(&ilits->tp_sleep), + atomic_read(&ilits->esd_stat)); + + if (!ilits->report || atomic_read(&ilits->tp_reset) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep) || + atomic_read(&ilits->esd_stat)) { + ILI_DBG("ignore interrupt !\n"); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ilitek_plat_isr_bottom_half(int irq, void *dev_id) +{ + if (mutex_is_locked(&ilits->touch_mutex)) { + ILI_DBG("touch is locked, ignore\n"); + return IRQ_HANDLED; + } + mutex_lock(&ilits->touch_mutex); + ili_report_handler(); + mutex_unlock(&ilits->touch_mutex); + return IRQ_HANDLED; +} + +void ili_irq_unregister(void) +{ + devm_free_irq(ilits->dev, ilits->irq_num, NULL); +} + +int ili_irq_register(int type) +{ + int ret = 0; + static bool get_irq_pin; + struct device_node *node; + + atomic_set(&ilits->irq_stat, DISABLE); + + if (get_irq_pin == false) { + node = of_find_matching_node(NULL, touch_of_match); + if (node) + ilits->irq_num = irq_of_parse_and_map(node, 0); + + ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num); + get_irq_pin = true; + } + + ret = devm_request_threaded_irq(ilits->dev, ilits->irq_num, + ilitek_plat_isr_top_half, + ilitek_plat_isr_bottom_half, + type | IRQF_ONESHOT, "ilitek", NULL); + + if (type == IRQF_TRIGGER_FALLING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_FALLING\n"); + if (type == IRQF_TRIGGER_RISING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_RISING\n"); + + if (ret != 0) + ILI_ERR("Failed to register irq handler, irq = %d, ret = %d\n", ilits->irq_num, ret); + + atomic_set(&ilits->irq_stat, ENABLE); + + return ret; +} + +static void tpd_resume(struct device *h) +{ + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); +} + +static void tpd_suspend(struct device *h) +{ + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); +} + +static int ilitek_tp_pm_suspend(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM SUSPEND"); + ilits->pm_suspend = true; + reinit_completion(&ilits->pm_completion); + return 0; +} + +static int ilitek_tp_pm_resume(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM RESUME"); + ilits->pm_suspend = false; + complete(&ilits->pm_completion); + return 0; +} + +static int ilitek_plat_probe(void) +{ + ILI_INFO("platform probe\n"); + +#if REGULATOR_POWER + ilitek_plat_regulator_power_init(); +#endif + + if (ilitek_plat_gpio_register() < 0) + ILI_ERR("Register gpio failed\n"); + + ili_irq_register(ilits->irq_tirgger_type); + + if (ili_tddi_init() < 0) { + ILI_ERR("ILITEK Driver probe failed\n"); + ili_irq_unregister(); + return -ENODEV; + } + tpd_load_status = 1; + ilits->pm_suspend = false; + init_completion(&ilits->pm_completion); + + ILI_INFO("ILITEK Driver loaded successfully!"); + return 0; +} + +static int ilitek_plat_remove(void) +{ + ILI_INFO("remove plat dev\n"); + ili_dev_remove(); + return 0; +} + +static const struct dev_pm_ops tp_pm_ops = { + .suspend = ilitek_tp_pm_suspend, + .resume = ilitek_tp_pm_resume, +}; + +static const struct of_device_id tp_match_table[] = { + {.compatible = DTS_OF_NAME}, + {}, +}; + +static struct ilitek_hwif_info hwif = { + .bus_type = TDDI_INTERFACE, + .plat_type = TP_PLAT_MTK, + .owner = THIS_MODULE, + .name = TDDI_DEV_ID, + .of_match_table = of_match_ptr(tp_match_table), + .plat_probe = ilitek_plat_probe, + .plat_remove = ilitek_plat_remove, + .pm = &tp_pm_ops, +}; + +static int tpd_local_init(void) +{ + ILI_INFO("TPD init device driver\n"); + + if (ili_dev_init(&hwif) < 0) { + ILI_ERR("Failed to register i2c/spi bus driver\n"); + return -ENODEV; + } + if (tpd_load_status == 0) { + ILI_ERR("Add error touch panel driver\n"); + return -1; + } + if (tpd_dts_data.use_tpd_button) { + tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local, + tpd_dts_data.tpd_key_dim_local); + } + tpd_type_cap = 1; + return 0; +} + +static struct tpd_driver_t tpd_device_driver = { + .tpd_device_name = TDDI_DEV_ID, + .tpd_local_init = tpd_local_init, + .suspend = tpd_suspend, + .resume = tpd_resume, +}; + +static int __init ilitek_plat_dev_init(void) +{ + int ret = 0; + + ILI_INFO("ILITEK TP driver init for MTK\n"); + tpd_get_dts_info(); + ret = tpd_driver_add(&tpd_device_driver); + if (ret < 0) { + ILI_ERR("ILITEK add TP driver failed\n"); + tpd_driver_remove(&tpd_device_driver); + return -ENODEV; + } + return 0; +} + +static void __exit ilitek_plat_dev_exit(void) +{ + ILI_INFO("ilitek driver has been removed\n"); + tpd_driver_remove(&tpd_device_driver); +} + +module_init(ilitek_plat_dev_init); +module_exit(ilitek_plat_dev_exit); +MODULE_AUTHOR("ILITEK"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_node.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_node.c new file mode 100755 index 000000000000..dd587f64cba3 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_node.c @@ -0,0 +1,2587 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define USER_STR_BUFF PAGE_SIZE +#define IOCTL_I2C_BUFF PAGE_SIZE +#define ILITEK_IOCTL_MAGIC 100 +#define ILITEK_IOCTL_MAXNR 27 + +#define ILITEK_IOCTL_I2C_WRITE_DATA _IOWR(ILITEK_IOCTL_MAGIC, 0, u8*) +#define ILITEK_IOCTL_I2C_SET_WRITE_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 1, int) +#define ILITEK_IOCTL_I2C_READ_DATA _IOWR(ILITEK_IOCTL_MAGIC, 2, u8*) +#define ILITEK_IOCTL_I2C_SET_READ_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 3, int) + +#define ILITEK_IOCTL_TP_HW_RESET _IOWR(ILITEK_IOCTL_MAGIC, 4, int) +#define ILITEK_IOCTL_TP_POWER_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 5, int) +#define ILITEK_IOCTL_TP_REPORT_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 6, int) +#define ILITEK_IOCTL_TP_IRQ_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 7, int) + +#define ILITEK_IOCTL_TP_DEBUG_LEVEL _IOWR(ILITEK_IOCTL_MAGIC, 8, int) +#define ILITEK_IOCTL_TP_FUNC_MODE _IOWR(ILITEK_IOCTL_MAGIC, 9, int) + +#define ILITEK_IOCTL_TP_FW_VER _IOWR(ILITEK_IOCTL_MAGIC, 10, u8*) +#define ILITEK_IOCTL_TP_PL_VER _IOWR(ILITEK_IOCTL_MAGIC, 11, u8*) +#define ILITEK_IOCTL_TP_CORE_VER _IOWR(ILITEK_IOCTL_MAGIC, 12, u8*) +#define ILITEK_IOCTL_TP_DRV_VER _IOWR(ILITEK_IOCTL_MAGIC, 13, u8*) +#define ILITEK_IOCTL_TP_CHIP_ID _IOWR(ILITEK_IOCTL_MAGIC, 14, u32*) + +#define ILITEK_IOCTL_TP_NETLINK_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 15, int*) +#define ILITEK_IOCTL_TP_NETLINK_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 16, int*) + +#define ILITEK_IOCTL_TP_MODE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 17, u8*) +#define ILITEK_IOCTL_TP_MODE_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 18, int*) +#define ILITEK_IOCTL_ICE_MODE_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 19, int) + +#define ILITEK_IOCTL_TP_INTERFACE_TYPE _IOWR(ILITEK_IOCTL_MAGIC, 20, u8*) +#define ILITEK_IOCTL_TP_DUMP_FLASH _IOWR(ILITEK_IOCTL_MAGIC, 21, int) +#define ILITEK_IOCTL_TP_FW_UART_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 22, u8*) +#define ILITEK_IOCTL_TP_PANEL_INFO _IOWR(ILITEK_IOCTL_MAGIC, 23, u32*) +#define ILITEK_IOCTL_TP_INFO _IOWR(ILITEK_IOCTL_MAGIC, 24, u32*) +#define ILITEK_IOCTL_WRAPPER_RW _IOWR(ILITEK_IOCTL_MAGIC, 25, u8*) +#define ILITEK_IOCTL_DDI_WRITE _IOWR(ILITEK_IOCTL_MAGIC, 26, u8*) +#define ILITEK_IOCTL_DDI_READ _IOWR(ILITEK_IOCTL_MAGIC, 27, u8*) + +#ifdef CONFIG_COMPAT +#define ILITEK_COMPAT_IOCTL_I2C_WRITE_DATA _IOWR(ILITEK_IOCTL_MAGIC, 0, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_SET_WRITE_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 1, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_READ_DATA _IOWR(ILITEK_IOCTL_MAGIC, 2, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_SET_READ_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 3, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_HW_RESET _IOWR(ILITEK_IOCTL_MAGIC, 4, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_POWER_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 5, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_REPORT_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 6, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_IRQ_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 7, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_DEBUG_LEVEL _IOWR(ILITEK_IOCTL_MAGIC, 8, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_FUNC_MODE _IOWR(ILITEK_IOCTL_MAGIC, 9, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_FW_VER _IOWR(ILITEK_IOCTL_MAGIC, 10, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_PL_VER _IOWR(ILITEK_IOCTL_MAGIC, 11, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_CORE_VER _IOWR(ILITEK_IOCTL_MAGIC, 12, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_DRV_VER _IOWR(ILITEK_IOCTL_MAGIC, 13, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_CHIP_ID _IOWR(ILITEK_IOCTL_MAGIC, 14, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_NETLINK_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 15, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_NETLINK_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 16, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_MODE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 17, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_MODE_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 18, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_ICE_MODE_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 19, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_INTERFACE_TYPE _IOWR(ILITEK_IOCTL_MAGIC, 20, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_DUMP_FLASH _IOWR(ILITEK_IOCTL_MAGIC, 21, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_FW_UART_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 22, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_PANEL_INFO _IOWR(ILITEK_IOCTL_MAGIC, 23, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_INFO _IOWR(ILITEK_IOCTL_MAGIC, 24, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_WRAPPER_RW _IOWR(ILITEK_IOCTL_MAGIC, 25, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_DDI_WRITE _IOWR(ILITEK_IOCTL_MAGIC, 26, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_DDI_READ _IOWR(ILITEK_IOCTL_MAGIC, 27, compat_uptr_t) +#endif + +struct record_state { + u8 touch_palm_state_e : 2; + u8 app_an_statu_e : 3; + u8 app_sys_check_bg_abnormal : 1; + u8 g_b_wrong_bg : 1; +}; + +static unsigned char g_user_buf[USER_STR_BUFF] = {0}; +#define ILI_SPI_NAME "ilitek" +static struct class *touchscreen_class; +static struct device *touchscreen_class_dev; + +int ili_str2hex(char *str) +{ + int strlen, result, intermed, intermedtop; + char *s = str; + + while (*s != 0x0) { + s++; + } + + strlen = (int)(s - str); + s = str; + if (*s != 0x30) { + return -1; + } + + s++; + + if (*s != 0x78 && *s != 0x58) { + return -1; + } + s++; + + strlen = strlen - 3; + result = 0; + while (*s != 0x0) { + intermed = *s & 0x0f; + intermedtop = *s & 0xf0; + if (intermedtop == 0x60 || intermedtop == 0x40) { + intermed += 0x09; + } + intermed = intermed << (strlen << 2); + result = result | intermed; + strlen -= 1; + s++; + } + return result; +} + +int ili_katoi(char *str) +{ + int result = 0; + unsigned int digit; + int sign; + + if (*str == '-') { + sign = 1; + str += 1; + } else { + sign = 0; + if (*str == '+') { + str += 1; + } + } + + for (;; str += 1) { + digit = *str - '0'; + if (digit > 9) + break; + result = (10 * result) + digit; + } + + if (sign) { + return -result; + } + return result; +} + +struct file_buffer { + char *ptr; + char fname[128]; + int32_t wlen; + int32_t flen; + int32_t max_size; +}; + +static int file_write(struct file_buffer *file, bool new_open) +{ + struct file *f = NULL; + mm_segment_t fs; + loff_t pos; + + if (file->ptr == NULL) { + ILI_ERR("str is invaild\n"); + return -1; + } + + if (file->fname == NULL) { + ILI_ERR("file name is invaild\n"); + return -1; + } + + if (file->flen >= file->max_size) { + ILI_ERR("The length saved to file is too long !\n"); + return -1; + } + + if (new_open) + f = filp_open(file->fname, O_WRONLY | O_CREAT | O_TRUNC, 644); + else + f = filp_open(file->fname, O_WRONLY | O_CREAT | O_APPEND, 644); + + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open %s file\n", file->fname); + return -1; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, file->ptr, file->flen, &pos); + set_fs(fs); + filp_close(f, NULL); + return 0; +} + +static int ilitek_debug_node_buff_control(bool open) +{ + int i, ret; + + ilits->dnp = open; + + ILI_INFO("Debug buf ctrl = %s\n", ilits->dnp ? "Enabled" : "Disabled"); + + if (open) { + ilits->dbf = 0; + ilits->odi = 0; + ipio_kfree((void **)&ilits->dbl); + ilits->dbl = kzalloc(TR_BUF_LIST_SIZE * sizeof(*ilits->dbl), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbl)) { + ILI_ERR("Failed to allocate ilits->dbl mem, %ld\n", PTR_ERR(ilits->dbl)); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbl[i].mark = false; + ipio_kfree((void **)&ilits->dbl[i].data); + ilits->dbl[i].data = kzalloc(TR_BUF_SIZE * sizeof(unsigned char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbl[i].data)) { + ILI_ERR("Failed to allocate dbl[%d] mem, %ld\n", i, PTR_ERR(ilits->dbl[i].data)); + ret = -ENOMEM; + goto out; + } + } + return 0; + } + +out: + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbl[i].mark = false; + ipio_kfree((void **)&ilits->dbl[i].data); + } + ipio_kfree((void **)&ilits->dbl); + return ret; +} + +static int debug_mode_get_data(struct file_buffer *file, u8 type, u32 frame_count) +{ + int ret; + u8 cmd[2] = { 0 }, row, col; + s16 temp; + unsigned char *ptr; + int j; + u16 write_index = 0; + + ilitek_debug_node_buff_control(DISABLE); + + ilits->dbf = 0; + row = ilits->ych_num; + col = ilits->xch_num; + mutex_lock(&ilits->touch_mutex); + + cmd[0] = 0xFA; + cmd[1] = type; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + + if (ilitek_debug_node_buff_control(ENABLE) < 0) { + ILI_ERR("Failed to allocate debug buf\n"); + ret = -ENOMEM; + goto out; + } + + mutex_unlock(&ilits->touch_mutex); + + if (ret < 0) { + ILI_ERR("Write 0xFA,0x%x failed\n", type); + goto out; + } + + while (write_index < frame_count) { + file->wlen = 0; + ILI_INFO("frame = %d,index = %d,count = %d\n", write_index, ilits->odi, ilits->dbf); + if (!wait_event_interruptible_timeout(ilits->inq, ilits->dbl[ilits->odi].mark, msecs_to_jiffies(3000))) { + ILI_ERR("debug mode get data timeout!\n"); + goto out; + } + + mutex_lock(&ilits->touch_mutex); + + memset(file->ptr, 0, file->max_size); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n\nFrame%d,", write_index); + for (j = 0; j < col; j++) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "[X%d] ,", j); + ptr = &ilits->dbl[ilits->odi].data[35]; + for (j = 0; j < row * col; j++, ptr += 2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j % col == 0) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[Y%d] ,", (j / col)); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "%d, ", temp); + } + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[X] ,"); + for (j = 0; j < row + col; j++, ptr += 2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j == col) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[Y] ,"); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "%d, ", temp); + } + file_write(file, false); + write_index++; + + mutex_unlock(&ilits->touch_mutex); + + ilits->dbl[ilits->odi].mark = false; + ilits->odi = ((ilits->odi + 1) % TR_BUF_LIST_SIZE); + } +out: + ilitek_debug_node_buff_control(DISABLE); + return ret; +} + +static int dev_mkdir(char *name, umode_t mode) +{ +#if 0 + struct dentry *dentry; + struct path path; + int err; + dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + err = vfs_mkdir(path.dentry->d_inode, dentry, mode); + done_path_create(&path, dentry); + return err; +#else + int err; + mm_segment_t fs; + + ILI_INFO("mkdir: %s\n", name); + fs = get_fs(); + set_fs(KERNEL_DS); + err = 0;//ksys_mkdir(name, mode); + set_fs(fs); + + return err; +#endif +} + +static ssize_t ilitek_proc_get_delta_data_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + s16 *delta = NULL; + int row = 0, col = 0, index = 0; + int ret, i, x, y; + int read_length = 0, len = 0; + u8 cmd[2] = {0}; + u8 *data = NULL; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + mutex_lock(&ilits->touch_mutex); + + row = ilits->ych_num; + col = ilits->xch_num; + read_length = 4 + 2 * row * col + 1 ; + + ILI_INFO("read length = %d\n", read_length); + + data = kcalloc(read_length + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + goto out; + } + + delta = kcalloc(P5_X_DEBUG_MODE_PACKET_LENGTH, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(delta)) { + ILI_ERR("Failed to allocate delta mem\n"); + goto out; + } + + cmd[0] = 0xB7; + cmd[1] = 0x1; //get delta + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x1 command, %d\n", ret); + goto out; + } + + msleep(120); + + /* read debug packet header */ + ret = ilits->wrapper(NULL, 0, data, read_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read debug packet header failed, %d\n", ret); + goto out; + } + + cmd[1] = 0x03; //switch to normal mode + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, ON, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x3 command, %d\n", ret); + goto out; + } + + for (i = 4, index = 0; i < row * col * 2 + 4; i += 2, index++) + delta[index] = (data[i] << 8) + data[i + 1]; + + len = snprintf(g_user_buf + size, PAGE_SIZE - len, "======== Deltadata ========\n"); + ILI_INFO("======== Deltadata ========\n"); + + len += snprintf(g_user_buf + len, PAGE_SIZE - len, + "Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + ILI_INFO("Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + + // print delta data + for (y = 0; y < row; y++) { + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "[%2d] ", (y+1)); + ILI_INFO("[%2d] ", (y+1)); + + for (x = 0; x < col; x++) { + int shift = y * col + x; + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "%5d", delta[shift]); + printk(KERN_CONT "%5d", delta[shift]); + } + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "\n"); + printk(KERN_CONT "\n"); + } + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + +out: + mutex_unlock(&ilits->touch_mutex); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ipio_kfree((void **)&data); + ipio_kfree((void **)&delta); + return len; +} + +static ssize_t ilitek_proc_fw_get_raw_data_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + s16 *rawdata = NULL; + int row = 0, col = 0, index = 0; + int ret, i, x, y; + int read_length = 0, len = 0; + u8 cmd[2] = {0}; + u8 *data = NULL; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + mutex_lock(&ilits->touch_mutex); + + row = ilits->ych_num; + col = ilits->xch_num; + read_length = 4 + 2 * row * col + 1 ; + + ILI_INFO("read length = %d\n", read_length); + + data = kcalloc(read_length + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + goto out; + } + + rawdata = kcalloc(P5_X_DEBUG_MODE_PACKET_LENGTH, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(rawdata)) { + ILI_ERR("Failed to allocate rawdata mem\n"); + goto out; + } + + cmd[0] = 0xB7; + cmd[1] = 0x2; //get rawdata + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x2 command, %d\n", ret); + goto out; + } + + msleep(120); + + /* read debug packet header */ + ret = ilits->wrapper(NULL, 0, data, read_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read debug packet header failed, %d\n", ret); + goto out; + } + + cmd[1] = 0x03; //switch to normal mode + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, ON, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x3 command, %d\n", ret); + goto out; + } + + for (i = 4, index = 0; i < row * col * 2 + 4; i += 2, index++) + rawdata[index] = (data[i] << 8) + data[i + 1]; + + len = snprintf(g_user_buf, PAGE_SIZE, "======== RawData ========\n"); + ILI_INFO("======== RawData ========\n"); + + len += snprintf(g_user_buf + len, PAGE_SIZE - len, + "Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + ILI_INFO("Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + + // print raw data + for (y = 0; y < row; y++) { + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "[%2d] ", (y+1)); + ILI_INFO("[%2d] ", (y+1)); + + for (x = 0; x < col; x++) { + int shift = y * col + x; + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "%5d", rawdata[shift]); + printk(KERN_CONT "%5d", rawdata[shift]); + } + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "\n"); + printk(KERN_CONT "\n"); + } + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + +out: + mutex_unlock(&ilits->touch_mutex); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ipio_kfree((void **)&data); + ipio_kfree((void **)&rawdata); + return len; +} + +static ssize_t ilitek_proc_fw_pc_counter_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + int len = 0; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_ic_get_pc_counter(0); + len = snprintf(g_user_buf, PAGE_SIZE, "pc = 0x%x, latch = 0x%x\n", ilits->fw_pc, ilits->fw_latch); + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + return len; +} + +static u32 rw_reg[5] = {0}; +static ssize_t ilitek_proc_rw_tp_reg_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + int ret = 0, len = 0; + bool mcu_on = 0, read = 0; + u32 type, addr, read_data, write_data, write_len, stop_mcu; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + stop_mcu = rw_reg[0]; + type = rw_reg[1]; + addr = rw_reg[2]; + write_data = rw_reg[3]; + write_len = rw_reg[4]; + + ILI_INFO("stop_mcu = %d\n", rw_reg[0]); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl(ENABLE, ON); + else + ret = ili_ice_mode_ctrl(ENABLE, OFF); + + if (ret < 0) { + ILI_ERR("Failed to enter ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to enter ICE mode"); + } + + if (type == read) { + if (ili_ice_mode_read(addr, &read_data, sizeof(u32)) < 0) { + ILI_ERR("Read data error\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Read data error"); + } + + ILI_INFO("[READ]:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "READ:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + } else { + if (ili_ice_mode_write(addr, write_data, write_len) < 0) { + ILI_ERR("Write data error\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Write data error"); + } + + ILI_INFO("[WRITE]:addr = 0x%06x, write = 0x%08x, len = %d byte\n", addr, write_data, write_len); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "WRITE:addr = 0x%06x, write = 0x%08x, len =%d byte\n", addr, write_data, write_len); + } + + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl(DISABLE, ON); + else + ret = ili_ice_mode_ctrl(DISABLE, OFF); + + if (ret < 0) { + ILI_ERR("Failed to disable ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to disable ICE mode"); + } + + if (copy_to_user((char *)buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_rw_tp_reg_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char *token = NULL, *cur = NULL; + char cmd[256] = { 0 }; + u32 count = 0; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + size = -1; + goto out; + } + } + token = cur = cmd; + while ((token = strsep(&cur, ",")) != NULL) { + rw_reg[count] = ili_str2hex(token); + ILI_INFO("rw_reg[%d] = 0x%x\n", count, rw_reg[count]); + count++; + } + +out: + mutex_unlock(&ilits->touch_mutex); + return size; +} + +static ssize_t ilitek_proc_debug_switch_read(struct file *pFile, char __user *buff, size_t size, loff_t *pos) +{ + bool open; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + mutex_lock(&ilits->debug_mutex); + + open = !ilits->dnp; + + ilitek_debug_node_buff_control(open); + + size = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "dnp : %s\n", ilits->dnp ? "Enabled" : "Disabled"); + *pos = size; + + if (copy_to_user(buff, g_user_buf, size)) + ILI_ERR("Failed to copy data to user space\n"); + + mutex_unlock(&ilits->debug_mutex); + return size; +} + +static ssize_t ilitek_proc_debug_message_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + unsigned long p = *pos; + int i = 0; + int send_data_len = 0; + int ret = 0; + int data_count = 0; + int one_data_bytes = 0; + int need_read_data_len = 0; + int type = 0; + unsigned char *tmpbuf = NULL; + unsigned char tmpbufback[128] = {0}; + + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + + if (!ilits->dnp) { + ILI_ERR("Debug flag isn't enabled (%d)\n", ilits->dnp); + return -EAGAIN; + } + + mutex_lock(&ilits->debug_read_mutex); + ILI_DBG("f_count= %d, index = %d, mark = %d\n", ilits->dbf, ilits->odi, ilits->dbl[ilits->odi].mark); + if (!wait_event_interruptible_timeout(ilits->inq, ilits->dbl[ilits->odi].mark, msecs_to_jiffies(3000))) { + ILI_ERR("WARNING ! there's no data received.\n"); + mutex_unlock(&ilits->debug_read_mutex); + *pos = send_data_len; + return send_data_len; + } + mutex_lock(&ilits->debug_mutex); + + tmpbuf = vmalloc(4096); /* buf size if even */ + if (ERR_ALLOC_MEM(tmpbuf)) { + ILI_ERR("buffer vmalloc error\n"); + send_data_len += snprintf(tmpbufback + send_data_len, sizeof(tmpbufback), "buffer vmalloc error\n"); + ret = copy_to_user(buff, tmpbufback, send_data_len); /*ilits->dbl[0] */ + goto out; + } + + if (ilits->dbl[ilits->odi].mark) { + if (ilits->dbl[ilits->odi].data[0] == P5_X_DEMO_PACKET_ID) { + need_read_data_len = 43; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_I2CUART_PACKET_ID) { + type = ilits->dbl[ilits->odi].data[3] & 0x0F; + + data_count = ilits->dbl[ilits->odi].data[1] * ilits->dbl[ilits->odi].data[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + need_read_data_len = data_count * one_data_bytes + 1 + 5; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_PACKET_ID || ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_LITE_PACKET_ID) { + send_data_len = 0; /* ilits->dbl[0][1] - 2; */ + need_read_data_len = TR_BUF_SIZE - 8; + } + + for (i = 0; i < need_read_data_len; i++) { + send_data_len += snprintf(tmpbuf + send_data_len, sizeof(tmpbufback), "%02X", ilits->dbl[ilits->odi].data[i]); + if (send_data_len >= 4096) { + ILI_ERR("send_data_len = %d set 4096 i = %d\n", send_data_len, i); + send_data_len = 4096; + break; + } + } + + send_data_len += snprintf(tmpbuf + send_data_len, sizeof(tmpbufback), "\n\n"); + + if (p == 5 || size == 4096 || size == 2048) { + ilits->dbl[ilits->odi].mark = false; + ilits->odi = ((ilits->odi + 1) % TR_BUF_LIST_SIZE); + } + } + + /* Preparing to send debug data to user */ + if (size == 4096) + ret = copy_to_user(buff, tmpbuf, send_data_len); + else + ret = copy_to_user(buff, tmpbuf + p, send_data_len - p); + + /* ILI_ERR("send_data_len = %d\n", send_data_len); */ + if (send_data_len <= 0 || send_data_len > 4096) { + ILI_ERR("send_data_len = %d set 4096\n", send_data_len); + send_data_len = 4096; + } + + if (ret) { + ILI_ERR("copy_to_user err\n"); + ret = -EFAULT; + } else { + *pos += send_data_len; + ret = send_data_len; + ILI_DBG("Read %d bytes(s) from %ld\n", send_data_len, p); + } + +out: + mutex_unlock(&ilits->debug_mutex); + mutex_unlock(&ilits->debug_read_mutex); + ipio_vfree((void **)&tmpbuf); + return send_data_len; +} + +static ssize_t ilitek_proc_get_debug_mode_data_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret; + struct file_buffer csv; + + if (*pos != 0) + return 0; + + /* initialize file */ + memset(csv.fname, 0, sizeof(csv.fname)); + snprintf(csv.fname, sizeof(csv.fname), "%s", DEBUG_DATA_FILE_PATH); + csv.flen = 0; + csv.wlen = 0; + csv.max_size = DEBUG_DATA_FILE_SIZE; + + csv.ptr = vmalloc(csv.max_size); + if (ERR_ALLOC_MEM(csv.ptr)) { + ILI_ERR("Failed to allocate CSV mem\n"); + goto out; + } + + /* save data to csv */ + ILI_INFO("Get Raw data %d frame\n", ilits->raw_count); + ILI_INFO("Get Delta data %d frame\n", ilits->delta_count); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "Get Raw data %d frame\n", ilits->raw_count); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "Get Delta data %d frame\n", ilits->delta_count); + + file_write(&csv, true); + + /* change to debug mode */ + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, NULL) < 0) { + ILI_ERR("Failed to set tp data length\n"); + goto out; + } + + /* get raw data */ + csv.wlen = 0; + memset(csv.ptr, 0, csv.max_size); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "\n\n=======Raw data======="); + file_write(&csv, false); + + ret = debug_mode_get_data(&csv, P5_X_FW_RAW_DATA_MODE, ilits->raw_count); + if (ret < 0) + goto out; + + /* get delta data */ + csv.wlen = 0; + memset(csv.ptr, 0, csv.max_size); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "\n\n=======Delta data======="); + file_write(&csv, false); + + ret = debug_mode_get_data(&csv, P5_X_FW_DELTA_DATA_MODE, ilits->delta_count); + if (ret < 0) + goto out; + + /* change to demo mode */ + if (ili_set_tp_data_len(DATA_FORMAT_DEMO, false, NULL) < 0) + ILI_ERR("Failed to set tp data length\n"); + +out: + ipio_vfree((void **)&csv.ptr); + return 0; +} + +static ssize_t ilitek_proc_get_debug_mode_data_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char *token = NULL, *cur = NULL; + char cmd[256] = {0}; + u8 temp[256] = {0}, count = 0; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + return -1; + } + } + + ILI_INFO("size = %d, cmd = %s\n", (int)size, cmd); + token = cur = cmd; + while ((token = strsep(&cur, ",")) != NULL) { + temp[count] = ili_str2hex(token); + ILI_INFO("temp[%d] = %d\n", count, temp[count]); + count++; + } + + ilits->raw_count = ((temp[0] << 8) | temp[1]); + ilits->delta_count = ((temp[2] << 8) | temp[3]); + ilits->bg_count = ((temp[4] << 8) | temp[5]); + + ILI_INFO("Raw_count = %d, Delta_count = %d, BG_count = %d\n", ilits->raw_count, ilits->delta_count, ilits->bg_count); + return size; +} + +static ssize_t ilitek_node_mp_lcm_on_test_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + ILI_INFO("Run MP test with LCM on\n"); + + mutex_lock(&ilits->touch_mutex); + + /* Create the directory for mp_test result */ + if ((dev_mkdir(CSV_LCM_ON_PATH, S_IRUGO | S_IWUSR)) != 0) + ILI_ERR("Failed to create directory for mp_test\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + ilits->mp_ret_len = 0; + + ret = ili_mp_test_handler(g_user_buf, ON); + ILI_INFO("MP TEST %s, Error code = %d\n", (ret < 0) ? "FAIL" : "PASS", ret); + + g_user_buf[0] = 3; + g_user_buf[1] = (ret < 0) ? -ret : ret; + len += ilits->mp_ret_len; + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "MP TEST %s\n", (ret < 0) ? "FAIL" : "PASS"); + if (g_user_buf[1] == EMP_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to switch MP mode, abort!"); + } else if (g_user_buf[1] == EMP_FW_PROC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "FW still upgrading, abort!"); + } else if (g_user_buf[1] == EMP_FORMUL_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "MP formula is null, abort!"); + } else if (g_user_buf[1] == EMP_INI) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Not found ini file, abort!"); + } else if (g_user_buf[1] == EMP_NOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocated memory, abort!"); + } else if (g_user_buf[1] == EMP_PROTOCOL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Protocol version isn't matched, abort!"); + } else if (g_user_buf[1] == EMP_TIMING_INFO) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get timing info, abort!"); + } else if (g_user_buf[1] == EMP_PARA_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get mp parameter, abort!"); + } + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_mp_lcm_off_test_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + ILI_INFO("Run MP test with LCM off\n"); + + mutex_lock(&ilits->touch_mutex); + + /* Create the directory for mp_test result */ + if ((dev_mkdir(CSV_LCM_OFF_PATH, S_IRUGO | S_IWUSR)) != 0) + ILI_ERR("Failed to create directory for mp_test\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + ilits->mp_ret_len = 0; + + ret = ili_mp_test_handler(g_user_buf, OFF); + ILI_INFO("MP TEST %s, Error code = %d\n", (ret < 0) ? "FAIL" : "PASS", ret); + + g_user_buf[0] = 3; + g_user_buf[1] = (ret < 0) ? -ret : ret; + len += ilits->mp_ret_len; + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "MP TEST %s\n", (ret < 0) ? "FAIL" : "PASS"); + if (g_user_buf[1] == EMP_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to switch MP mode, abort!"); + } else if (g_user_buf[1] == EMP_FW_PROC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "FW still upgrading, abort!"); + } else if (g_user_buf[1] == EMP_FORMUL_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "MP formula is null, abort!"); + } else if (g_user_buf[1] == EMP_INI) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Not found ini file, abort!"); + } else if (g_user_buf[1] == EMP_NOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocated memory, abort!"); + } else if (g_user_buf[1] == EMP_PROTOCOL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Protocol version isn't matched, abort!"); + } else if (g_user_buf[1] == EMP_TIMING_INFO) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get timing info, abort!"); + } else if (g_user_buf[1] == EMP_PARA_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get mp parameter, abort!"); + } + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_ver_info_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (*pos != 0) + return 0; + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "CHIP ID = %x\n", ilits->chip->id); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "FW version = %d.%d.%d.%d\n", + ilits->chip->fw_ver >> 24, (ilits->chip->fw_ver >> 16) & 0xFF, + (ilits->chip->fw_ver >> 8) & 0xFF, ilits->chip->fw_ver & 0xFF); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "FW MP version = %d.%d.%d.%d\n", + ilits->chip->fw_mp_ver >> 24, (ilits->chip->fw_mp_ver >> 16) & 0xFF, + (ilits->chip->fw_mp_ver >> 8) & 0xFF, ilits->chip->fw_mp_ver & 0xFF); + + if (ilits->protocol->core_ver_len == P5_X_CORE_VER_THREE_LENGTH) + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Core version = %d.%d.%d\n", + (ilits->chip->core_ver >> 24) & 0xFF, (ilits->chip->core_ver >> 16) & 0xFF, + (ilits->chip->core_ver >> 8) & 0xFF); + else + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Core version = %d.%d.%d.%d\n", + (ilits->chip->core_ver >> 24) & 0xFF, (ilits->chip->core_ver >> 16) & 0xFF, + (ilits->chip->core_ver >> 8) & 0xFF, ilits->chip->core_ver & 0xFF); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Protocol version = %d.%d.%d\n", + ilits->protocol->ver >> 16, (ilits->protocol->ver >> 8) & 0xFF, + ilits->protocol->ver & 0xFF); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Driver version = %s\n", DRIVER_VERSION); + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_change_list_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (*pos != 0) + return 0; + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "============= Change list ==============\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "[Drive version] = %s\n", DRIVER_VERSION); + // len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "[Patch] 202001-0001\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "========================================\n"); + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_fw_process_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "%02d\n", ilits->fw_update_stat); + + ILI_INFO("update status = %d\n", ilits->fw_update_stat); + + if (copy_to_user((char *)buff, &ilits->fw_update_stat, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos = len; + return len; +} + +static ssize_t ilitek_node_fw_upgrade_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + ILI_INFO("Prepar to upgarde firmware\n"); + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + ilits->force_fw_update = ENABLE; + ilits->node_update = true; + + ret = ili_fw_upgrade_handler(NULL); + + ilits->node_update = false; + ilits->force_fw_update = DISABLE; + + g_user_buf[0] = 0x0; + g_user_buf[1] = (ret < 0) ? -ret : ret; + + if (g_user_buf[1] == 0) + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Upgrade firmware = PASS"); + else + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Upgrade firmware = FAIL"); + + /* Reason for fail */ + if (g_user_buf[1] == EFW_CONVERT_FILE) { + g_user_buf[0] = 0xFF; + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to convert hex/ili file, abort!"); + } else if (g_user_buf[1] == ENOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocate memory, abort!"); + } else if (g_user_buf[1] == EFW_ICE_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to operate ice mode, abort!"); + } else if (g_user_buf[1] == EFW_CRC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "CRC not matched, abort!"); + } else if (g_user_buf[1] == EFW_REST) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to do reset, abort!"); + } else if (g_user_buf[1] == EFW_ERASE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to erase flash, abort!"); + } else if (g_user_buf[1] == EFW_PROGRAM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to program flash, abort!"); + } else if (g_user_buf[1] == EFW_INTERFACE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to hex file interface no match, abort!"); + } + + if (copy_to_user((u32 *) buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_debug_level_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + debug_en = !debug_en; + + ILI_INFO(" %s debug level = %x\n", debug_en ? "Enable" : "Disable", debug_en); + + size = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "debug level : %s\n", debug_en ? "Enable" : "Disable"); + + *pos += size; + + if (copy_to_user((u32 *) buff, g_user_buf, size)) + ILI_ERR("Failed to copy data to user space\n"); + + return size; +} + +int ili_get_tp_recore_ctrl(int data) +{ + int ret = 0; + + switch((int)data) { + + case ENABLE_RECORD: + ILI_INFO("recore enable"); + ret = ili_ic_func_ctrl("tp_recore", 1); + mdelay(200); + break; + case DATA_RECORD: + mdelay(50); + ILI_INFO("Get data"); + ret = ili_ic_func_ctrl("tp_recore", 2); + if (ret < 0) { + ILI_ERR("cmd fail\n"); + goto out; + } + + ret = ili_get_tp_recore_data(); + if (ret < 0) + ILI_ERR("get data fail\n"); + + ILI_INFO("recore reset"); + ret = ili_ic_func_ctrl("tp_recore", 3); + if (ret < 0) { + ILI_ERR("cmd fail\n"); + goto out; + } + break; + case DISABLE_RECORD: + ILI_INFO("recore disable"); + ret = ili_ic_func_ctrl("tp_recore", 0); + break; + + } +out: + return ret; + +} + +int ili_get_tp_recore_data(void) +{ + u8 buf[8] = {0}, record_case = 0; + s8 index; + u16 *raw = NULL, *raw_ptr = NULL, frame_len = 0; + u32 base_addr = 0x20000, addr, len, *ptr, i, fcnt; + struct record_state record_stat; + bool ice = atomic_read(&ilits->ice_stat); + + if (ilits->wrapper(NULL, 0, buf, sizeof(buf), OFF, OFF) < 0) { + ILI_ERR("Get info fail\n"); + return -1; + } + + addr = ((buf[0] << 8) | buf[1]) + base_addr; + len = ((buf[2] << 8) | buf[3]); + index = buf[4]; + fcnt = buf[5]; + record_case = buf[6]; + ipio_memcpy(&record_stat, &buf[7], 1, 1); + ILI_INFO("addr = 0x%x, len = %d, lndex = 0x%d, fram num = %d, record_case = 0x%x\n", + addr, len, index, fcnt, record_case); + ili_dump_data(buf, 8, sizeof(buf), 0, "all record bytes"); + + raw = kcalloc(len, sizeof(u8), GFP_ATOMIC); + if (ERR_ALLOC_MEM(raw)) { + ILI_ERR("Failed to allocate packet memory, %ld\n", PTR_ERR(raw)); + return -1; + } + ptr = (u32*)raw; + + if (!ice) + ili_ice_mode_ctrl(ENABLE, ON); + + buf[0] = 0x25; + buf[3] = (char)((addr & 0x00FF0000) >> 16); + buf[2] = (char)((addr & 0x0000FF00) >> 8); + buf[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(buf, 4, NULL, 0, ON, OFF)) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, (u8 *)raw, len, ON, OFF)) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + frame_len = (len / (fcnt * 2)); + for (i = 0; i < fcnt; i ++) { + raw_ptr = raw + (index * frame_len); + + ili_dump_data(raw_ptr, 16, frame_len, ilits->xch_num, "recore_data"); + index--; + if(index < 0) + index = fcnt - 1; + } + + if (!ice) + ili_ice_mode_ctrl(DISABLE, ON); + + if (record_case == 2) { + ILI_INFO("tp_palm_stat = %d\n", record_stat.touch_palm_state_e); + ILI_INFO("app_an_stat = %d\n", record_stat.app_an_statu_e); + ILI_INFO("app_check_abnor = %d\n", record_stat.app_sys_check_bg_abnormal); + ILI_INFO("wrong_bg = %d\n", record_stat.g_b_wrong_bg); + } + + ipio_kfree((void **)&raw); + + return 0; +} + +void ili_gesture_fail_reason(bool enable) +{ + + u8 cmd[24] = {0}; + + /* set symbol */ + if (ili_ic_func_ctrl("knock_en", 0x8) < 0) + ILI_ERR("set symbol failed"); + + /* enable gesture fail reason */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x10; + if (enable) + cmd[3] = 0x01; + else + cmd[3] = 0x00; + cmd[4] = 0xFF; + cmd[5] = 0xFF; + if ((ilits->wrapper(cmd, 6, NULL, 0, ON, OFF)) < 0) + ILI_ERR("enable gesture fail reason failed"); + + /* set gesture parameters */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x12; + cmd[3] = 0x01; + memset(cmd + 4, 0xFF, 20); + if ((ilits->wrapper(cmd, 24, NULL, 0, ON, OFF)) < 0) + ILI_ERR("set gesture parameters failed"); + + /* get gesture parameters */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x11; + cmd[3] = 0x01; + if ((ilits->wrapper(cmd, 4, NULL, 0, ON, OFF)) < 0) + ILI_ERR("get gesture parameters failed"); + +} + +int ili_tp_data_mode_ctrl(u8* cmd) +{ + int ret = 0; + switch (cmd[0]) { + case AP_MODE: + if (ilits->tp_suspend) { + if (ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEMO, false, NULL) < 0) { + ILI_ERR("Failed to set demo len from gesture mode\n"); + ret = -ENOTTY; + } + } else { + if (ili_switch_tp_mode(P5_X_FW_AP_MODE) < 0) { + ILI_ERR("Failed to switch demo mode\n"); + ret = -ENOTTY; + } + } + break; + case TEST_MODE: + if (ili_switch_tp_mode(P5_X_FW_TEST_MODE) < 0) { + ILI_ERR("Failed to switch test mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_MODE: + if (!ilits->tp_suspend && (ilits->actual_tp_mode != P5_X_FW_AP_MODE)) { + if (ili_switch_tp_mode(P5_X_FW_AP_MODE) < 0) { + ILI_ERR("Failed to switch demo mode\n"); + ret = -ENOTTY; + break; + } + } + if (ilits->tp_suspend) { + if (ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL) < 0) { + ILI_ERR("Failed to set debug len from gesture mode\n"); + ret = -ENOTTY; + } + } else { + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, NULL) < 0) { + ILI_ERR("Failed to set debug len\n"); + ret = -ENOTTY; + } + } + break; + case DEBUG_LITE_ROI: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_ROI, false, NULL) < 0) { + ILI_ERR("Failed to set debug lite roi from gesture mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_LITE_WINDOW: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_WINDOW, false, NULL) < 0) { + ILI_ERR("Failed to set debug lite window from gesture mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_LITE_AREA: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_AREA, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite area from gesture mode\n"); + ret = -ENOTTY; + } + break; + default: + ILI_ERR("Unknown TP mode ctrl\n"); + ret = -ENOTTY; + break; + } + + return ret; +} + +static ssize_t ilitek_node_ioctl_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + int i, ret = 0, count = 0; + char cmd[512] = {0}; + char *token = NULL, *cur = NULL; + u8 temp[256] = {0}; + u32 *data = NULL; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + return -1; + } + } + + ILI_INFO("size = %d, cmd = %s\n", (int)size, cmd); + + token = cur = cmd; + + data = kcalloc(512, sizeof(u32), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + size = -1; + goto out; + } + + while ((token = strsep(&cur, ",")) != NULL) { + data[count] = ili_str2hex(token); + ILI_INFO("data[%d] = %x\n", count, data[count]); + count++; + } + + ILI_INFO("cmd = %s\n", cmd); + + if (strncmp(cmd, "hwreset", strlen(cmd)) == 0) { + ili_reset_ctrl(TP_HW_RST_ONLY); + } else if (strcmp(cmd, "rawdatarecore") == 0) { + if (data[1] == ENABLE_RECORD) + ili_get_tp_recore_ctrl(ENABLE_RECORD); + else if (data[1] == DATA_RECORD) + ili_get_tp_recore_ctrl(DATA_RECORD); + else if (data[1] == DISABLE_RECORD) + ili_get_tp_recore_ctrl(DISABLE_RECORD); + } else if (strcmp(cmd, "switchdemodebuginfomode") == 0) { + ili_set_tp_data_len(DATA_FORMAT_DEMO_DEBUG_INFO, false, NULL); + } else if (strcmp(cmd, "gesturedemoen") == 0) { + if (data[1] == DISABLE) + ilits->gesture_demo_ctrl = DISABLE; + else + ilits->gesture_demo_ctrl = ENABLE; + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEMO, false, NULL); + } else if (strcmp(cmd, "gesturefailrsn") == 0) { + if (data[1] == DISABLE) + ili_gesture_fail_reason(DISABLE); + else + ili_gesture_fail_reason(ENABLE); + ILI_INFO("%s gesture fail reason\n", data[1] ? "ENABLE" : "DISABLE"); + } else if (strncmp(cmd, "icwholereset", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_reset_ctrl(TP_IC_WHOLE_RST); + } else if (strncmp(cmd, "iccodereset", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_reset_ctrl(TP_IC_CODE_RST); + ili_ice_mode_ctrl(DISABLE, OFF); + } else if (strncmp(cmd, "enableirq", strlen(cmd)) == 0) { + ILI_INFO("Enable IRQ\n"); + ili_irq_enable(); + } else if (strncmp(cmd, "disableirq", strlen(cmd)) == 0) { + ILI_INFO("Disable IRQ\n"); + ili_irq_disable(); + } else if (strncmp(cmd, "infofromhex", strlen(cmd)) == 0) { + ilits->info_from_hex = data[1]; + ILI_INFO("info from hex = %d\n", data[1]); + } else if (strncmp(cmd, "getinfo", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_ic_get_info(); + ili_ice_mode_ctrl(DISABLE, OFF); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_core_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ILI_INFO("Driver version = %s\n", DRIVER_VERSION); + ILI_INFO("TP module = %s\n", ilits->md_name); + } else if (strncmp(cmd, "enableicemode", strlen(cmd)) == 0) { + if (data[1] == ON) + ili_ice_mode_ctrl(ENABLE, ON); + else + ili_ice_mode_ctrl(ENABLE, OFF); + } else if (strncmp(cmd, "wqctrl", strlen(cmd)) == 0) { + ilits->wq_ctrl = !ilits->wq_ctrl; + ILI_INFO("wq_ctrl flag= %d\n", ilits->wq_ctrl); + } else if (strncmp(cmd, "disableicemode", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(DISABLE, OFF); + } else if (strncmp(cmd, "enablewqesd", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_ESD, ENABLE); + } else if (strncmp(cmd, "enablewqbat", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_BAT, ENABLE); + } else if (strncmp(cmd, "disablewqesd", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_ESD, DISABLE); + } else if (strncmp(cmd, "disablewqbat", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_BAT, DISABLE); + } else if (strncmp(cmd, "gesture", strlen(cmd)) == 0) { + ilits->gesture = !ilits->gesture; + ILI_INFO("gesture = %d\n", ilits->gesture); + } else if (strncmp(cmd, "esdgesture", strlen(cmd)) == 0) { + if (ili_gesture_recovery() < 0) { + ILI_ERR("Gesture recovery failed\n"); + size = -1; + } + } else if (strncmp(cmd, "esdspi", strlen(cmd)) == 0) { + ili_spi_recovery(); + } else if (strncmp(cmd, "sleepin", strlen(cmd)) == 0) { + ili_ic_func_ctrl("sleep", SLEEP_IN); + } else if (strncmp(cmd, "deepsleepin", strlen(cmd)) == 0) { + ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN); + } else if (strncmp(cmd, "iceflag", strlen(cmd)) == 0) { + if (data[1] == ENABLE) + atomic_set(&ilits->ice_stat, ENABLE); + else + atomic_set(&ilits->ice_stat, DISABLE); + ILI_INFO("ice mode flag = %d\n", atomic_read(&ilits->ice_stat)); + } else if (strncmp(cmd, "gesturenormal", strlen(cmd)) == 0) { + ilits->gesture_mode = DATA_FORMAT_GESTURE_NORMAL; + ILI_INFO("gesture mode = %d\n", ilits->gesture_mode); + } else if (strncmp(cmd, "gestureinfo", strlen(cmd)) == 0) { + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ILI_INFO("gesture mode = %d\n", ilits->gesture_mode); + } else if (strncmp(cmd, "netlink", strlen(cmd)) == 0) { + ilits->netlink = !ilits->netlink; + ILI_INFO("netlink flag= %d\n", ilits->netlink); + } else if (strncmp(cmd, "switchtestmode", strlen(cmd)) == 0) { + ili_switch_tp_mode(P5_X_FW_TEST_MODE); + } else if (strncmp(cmd, "switchdebugmode", strlen(cmd)) == 0) { + ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, NULL); + } else if (strncmp(cmd, "switchdemomode", strlen(cmd)) == 0) { + ili_set_tp_data_len(DATA_FORMAT_DEMO, false, NULL); + } else if (strncmp(cmd, "switchgesturedebugmode", strlen(cmd)) == 0) { + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL); + } else if (strncmp(cmd, "dbgflag", strlen(cmd)) == 0) { + ilits->dnp = !ilits->dnp; + ILI_INFO("debug flag message = %d\n", ilits->dnp); + } else if (strncmp(cmd, "spiclk", strlen(cmd)) == 0) { + ILI_INFO("spi clk num = %d\n", data[1]); + ili_core_spi_setup(data[1]); + } else if (strncmp(cmd, "ss", strlen(cmd)) == 0) { + ILI_INFO("sense_stop = %d\n", data[1]); + ilits->ss_ctrl = data[1]; + } else if (strncmp(cmd, "iow", strlen(cmd)) == 0) { + int w_len = 0; + w_len = data[1]; + ILI_INFO("w_len = %d\n", w_len); + for (i = 0; i < w_len; i++) { + temp[i] = data[2 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + + ilits->wrapper(temp, w_len, NULL, 0, ON, OFF); + } else if (strncmp(cmd, "ior", strlen(cmd)) == 0) { + int r_len = 0; + r_len = data[1]; + ILI_INFO("r_len = %d\n", r_len); + ilits->wrapper(NULL, 0, temp, r_len, ON, OFF); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strncmp(cmd, "iowr", strlen(cmd)) == 0) { + int delay = 0; + int w_len = 0, r_len = 0; + w_len = data[1]; + r_len = data[2]; + delay = data[3]; + ILI_INFO("w_len = %d, r_len = %d, delay = %d\n", w_len, r_len, delay); + + for (i = 0; i < w_len; i++) { + temp[i] = data[4 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ret = ilits->wrapper(temp, w_len, NULL, 0, ON, OFF); + memset(temp, 0, sizeof(temp)); + mdelay(delay); + ilits->wrapper(NULL, 0, temp, r_len, ON, OFF); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strcmp(cmd, "iowrint") == 0) { + int delay = 0; + int w_len = 0, r_len = 0; + w_len = data[1]; + r_len = data[2]; + delay = data[3]; + ILI_INFO("w_len = %d, r_len = %d, delay = %d\n", w_len, r_len, delay); + for (i = 0; i < w_len; i++) { + temp[i] = data[4 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ret = ilits->wrapper(temp, w_len, temp, r_len, ON, ON); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strncmp(cmd, "getddiregdata", strlen(cmd)) == 0) { + u8 ddi_data = 0; + ILI_INFO("Get ddi reg one page: page = %x, reg = %x\n", data[1], data[2]); + ili_ic_get_ddi_reg_onepage(data[1], data[2], &ddi_data); + ILI_INFO("ddi_data = %x\n", ddi_data); + } else if (strncmp(cmd, "setddiregdata", strlen(cmd)) == 0) { + ILI_INFO("Set ddi reg one page: page = %x, reg = %x, data = %x\n", data[1], data[2], data[3]); + ili_ic_set_ddi_reg_onepage(data[1], data[2], data[3]); + } else if (strncmp(cmd, "dumpflashdata", strlen(cmd)) == 0) { + ilits->fw_update_stat = FW_STAT_INIT; + ILI_INFO("Start = 0x%x, End = 0x%x, Dump Hex path = %s\n", data[1], data[2], DUMP_FLASH_PATH); + ret = ili_fw_dump_flash_data(data[1], data[2], false); + ilits->fw_update_stat = (ret < 0) ? FW_UPDATE_FAIL : FW_UPDATE_PASS; + ILI_INFO("ilits->fw_update_stat = %d\n", ilits->fw_update_stat); + } else if (strncmp(cmd, "dumpiramdata", strlen(cmd)) == 0) { + ILI_INFO("Start = 0x%x, End = 0x%x, Dump IRAM path = %s\n", data[1], data[2], DUMP_IRAM_PATH); + ilits->fw_update_stat = FW_STAT_INIT; + ret = ili_fw_dump_iram_data(data[1], data[2], true); + ilits->fw_update_stat = (ret < 0) ? FW_UPDATE_FAIL : FW_UPDATE_PASS; + ILI_INFO("ilits->fw_update_stat = %d\n", ilits->fw_update_stat); + } else if (strncmp(cmd, "edge_palm_ctrl", strlen(cmd)) == 0) { + ili_ic_func_ctrl("edge_palm", data[1]); + } else if (strncmp(cmd, "uart_mode_ctrl", strlen(cmd)) == 0) { + ili_fw_uart_ctrl(data[1]); + } else if (strncmp(cmd, "flashesdgesture", strlen(cmd)) == 0) { + ili_touch_esd_gesture_flash(); + } else if (strncmp(cmd, "intact", strlen(cmd)) == 0) { + ILI_INFO("Set INT trigger = %x\n", data[1]); + ili_ic_int_trigger_ctrl(data[1]); + } else if (strncmp(cmd, "spiw", strlen(cmd)) == 0) { + int wlen; + wlen = data[1]; + temp[0] = 0x82; + for (i = 0; i < wlen; i++) { + temp[i] = data[2 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ilits->spi_write_then_read(ilits->spi, temp, wlen, NULL, 0); + } else if (strncmp(cmd, "spir", strlen(cmd)) == 0) { + int rlen; + u8 *rbuf = NULL; + rlen = data[1]; + rbuf = kzalloc(rlen, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(rbuf)) { + ILI_ERR("Failed to allocate dma_rxbuf, %ld\n", PTR_ERR(rbuf)); + kfree(rbuf); + goto out; + } + temp[0] = 0x83; + ilits->spi_write_then_read(ilits->spi, temp, 1, rbuf, rlen); + for (i = 0; i < rlen; i++) + ILI_INFO("read[%d] = %x\n", i, rbuf[i]); + kfree(rbuf); + } else if (strncmp(cmd, "spirw", strlen(cmd)) == 0) { + int wlen, rlen; + u8 *rbuf = NULL; + wlen = data[1]; + rlen = data[2]; + for (i = 0; i < wlen; i++) { + temp[i] = data[3 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + if (rlen != 0) { + rbuf = kzalloc(rlen, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(rbuf)) { + ILI_ERR("Failed to allocate dma_rxbuf, %ld\n", PTR_ERR(rbuf)); + kfree(rbuf); + goto out; + } + } + ilits->spi_write_then_read(ilits->spi, temp, wlen, rbuf, rlen); + if (rlen != 0) { + for (i = 0; i < rlen; i++) + ILI_INFO("read[%d] = %x\n", i, rbuf[i]); + } + kfree(rbuf); + } else if (strncmp(cmd, "ges_sym", strlen(cmd)) == 0) { + struct gesture_symbol *ptr_sym = &ilits->ges_sym; + u8 *ptr; + ptr = (u8*) ptr_sym; + ptr[0] = data[1]; + ptr[1] = data[2]; + ptr[2] = data[3]; + ili_dump_data(ptr, 8, 3, 0, "Gesture symbol"); + } else if (strncmp(cmd, "mode", strlen(cmd)) == 0) { + temp[0] = data[1]; + temp[1] = data[2]; + temp[2] = data[3]; + temp[3] = data[4]; + temp[4] = data[5]; + ret = ili_tp_data_mode_ctrl(temp); + } else if (strncmp(cmd, "switchmpretry", strlen(cmd)) == 0) { + ilits->mp_retry = !ilits->mp_retry; + ILI_INFO("switchmpretry = %d\n", ilits->mp_retry); + } else { + ILI_ERR("Unknown command\n"); + size = -1; + } +out: + ipio_kfree((void **)&data); + mutex_unlock(&ilits->touch_mutex); + return size; +} + +#ifdef CONFIG_COMPAT +static long ilitek_node_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + + if (!filp->f_op || !filp->f_op->unlocked_ioctl) { + ILI_ERR("There's no unlocked_ioctl defined in file\n"); + return -ENOTTY; + } + + ILI_DBG("cmd = %d\n", _IOC_NR(cmd)); + + switch (cmd) { + case ILITEK_COMPAT_IOCTL_I2C_WRITE_DATA: + ILI_DBG("compat_ioctl: convert i2c/spi write\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_WRITE_DATA, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_READ_DATA: + ILI_DBG("compat_ioctl: convert i2c/spi read\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_READ_DATA, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_SET_WRITE_LENGTH: + ILI_DBG("compat_ioctl: convert set write length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_SET_WRITE_LENGTH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_SET_READ_LENGTH: + ILI_DBG("compat_ioctl: convert set read length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_SET_READ_LENGTH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_HW_RESET: + ILI_DBG("compat_ioctl: convert hw reset\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_HW_RESET, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_POWER_SWITCH: + ILI_DBG("compat_ioctl: convert power switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_POWER_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_REPORT_SWITCH: + ILI_DBG("compat_ioctl: convert report switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_REPORT_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_IRQ_SWITCH: + ILI_DBG("compat_ioctl: convert irq switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_IRQ_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DEBUG_LEVEL: + ILI_DBG("compat_ioctl: convert debug level\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DEBUG_LEVEL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FUNC_MODE: + ILI_DBG("compat_ioctl: convert format mode\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FUNC_MODE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FW_VER: + ILI_DBG("compat_ioctl: convert set read length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FW_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_PL_VER: + ILI_DBG("compat_ioctl: convert fw version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_PL_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_CORE_VER: + ILI_DBG("compat_ioctl: convert core version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_CORE_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DRV_VER: + ILI_DBG("compat_ioctl: convert driver version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DRV_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_CHIP_ID: + ILI_DBG("compat_ioctl: convert chip id\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_CHIP_ID, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_NETLINK_CTRL: + ILI_DBG("compat_ioctl: convert netlink ctrl\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_NETLINK_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_NETLINK_STATUS: + ILI_DBG("compat_ioctl: convert netlink status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_NETLINK_STATUS, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_MODE_CTRL: + ILI_DBG("compat_ioctl: convert tp mode ctrl\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_MODE_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_MODE_STATUS: + ILI_DBG("compat_ioctl: convert tp mode status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_MODE_STATUS, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_ICE_MODE_SWITCH: + ILI_DBG("compat_ioctl: convert tp mode switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_ICE_MODE_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_INTERFACE_TYPE: + ILI_DBG("compat_ioctl: convert interface type\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_INTERFACE_TYPE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DUMP_FLASH: + ILI_DBG("compat_ioctl: convert dump flash\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DUMP_FLASH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FW_UART_CTRL: + ILI_DBG("compat_ioctl: convert fw uart\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FW_UART_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_PANEL_INFO: + ILI_DBG("compat_ioctl: convert resolution\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_TP_PANEL_INFO, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_INFO: + ILI_DBG("compat_ioctl: convert tp info\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_TP_INFO, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_WRAPPER_RW: + ILI_DBG("compat_ioctl: convert wrapper\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_WRAPPER_RW, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_DDI_WRITE: + ILI_DBG("compat_ioctl: convert ddi write\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_DDI_WRITE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_DDI_READ: + ILI_DBG("compat_ioctl: convert ddi read\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_DDI_READ, (unsigned long)compat_ptr(arg)); + return ret; + default: + ILI_ERR("no ioctl cmd, return ilitek_node_ioctl\n"); + return -ENOIOCTLCMD; + } +} +#endif + +static long ilitek_node_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0, length = 0; + u8 *szBuf = NULL, if_to_user = 0; + static u16 i2c_rw_length; + u32 id_to_user[64] = {0}; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + u8 *wrap_rbuf = NULL, wrap_int = 0; + u16 wrap_wlen = 0, wrap_rlen = 0; + bool spi_irq, i2c_irq; + + if (_IOC_TYPE(cmd) != ILITEK_IOCTL_MAGIC) { + ILI_ERR("The Magic number doesn't match\n"); + return -ENOTTY; + } + + if (_IOC_NR(cmd) > ILITEK_IOCTL_MAXNR) { + ILI_ERR("The number of ioctl doesn't match\n"); + return -ENOTTY; + } + + ILI_DBG("cmd = %d\n", _IOC_NR(cmd)); + + mutex_lock(&ilits->touch_mutex); + + szBuf = kcalloc(IOCTL_I2C_BUFF, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(szBuf)) { + ILI_ERR("Failed to allocate mem\n"); + ret = -ENOMEM; + goto out; + } + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + switch (cmd) { + case ILITEK_IOCTL_I2C_WRITE_DATA: + ILI_DBG("ioctl: write len = %d\n", i2c_rw_length); + + if (i2c_rw_length > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! write len is largn than ioctl buf (%d, %ld)\n", + i2c_rw_length, IOCTL_I2C_BUFF); + ret = -ENOTTY; + break; + } + + if (copy_from_user(szBuf, (u8 *) arg, i2c_rw_length)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ret = ilits->wrapper(szBuf, i2c_rw_length, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Failed to write data\n"); + break; + case ILITEK_IOCTL_I2C_READ_DATA: + ILI_DBG("ioctl: read len = %d\n", i2c_rw_length); + + if (i2c_rw_length > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! read len is largn than ioctl buf (%d, %ld)\n", + i2c_rw_length, IOCTL_I2C_BUFF); + ret = -ENOTTY; + break; + } + + ret = ilits->wrapper(NULL, 0, szBuf, i2c_rw_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to read data\n"); + break; + } + + if (copy_to_user((u8 *) arg, szBuf, i2c_rw_length)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_I2C_SET_WRITE_LENGTH: + case ILITEK_IOCTL_I2C_SET_READ_LENGTH: + i2c_rw_length = arg; + break; + case ILITEK_IOCTL_TP_HW_RESET: + ILI_DBG("ioctl: hw reset\n"); + ili_reset_ctrl(ilits->reset); + break; + case ILITEK_IOCTL_TP_POWER_SWITCH: + ILI_INFO("Not implemented yet\n"); + break; + case ILITEK_IOCTL_TP_REPORT_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: report switch = %d\n", szBuf[0]); + if (szBuf[0]) { + ilits->report = ENABLE; + ILI_DBG("report is enabled\n"); + } else { + ilits->report = DISABLE; + ILI_DBG("report is disabled\n"); + } + break; + case ILITEK_IOCTL_TP_IRQ_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: irq switch = %d\n", szBuf[0]); + if (szBuf[0]) + ili_irq_enable(); + else + ili_irq_disable(); + break; + case ILITEK_IOCTL_TP_DEBUG_LEVEL: + if (copy_from_user(szBuf, (u8 *) arg, sizeof(u32))) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + debug_en = !debug_en; + ILI_DBG("debug_en = %d", debug_en); + break; + case ILITEK_IOCTL_TP_FUNC_MODE: + if (copy_from_user(szBuf, (u8 *) arg, 3)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: set func mode = %x,%x,%x\n", szBuf[0], szBuf[1], szBuf[2]); + ret = ilits->wrapper(szBuf, 3, NULL, 0, ON, OFF); + break; + case ILITEK_IOCTL_TP_FW_VER: + ILI_DBG("ioctl: get fw and mp version\n"); + szBuf[7] = ilits->chip->fw_mp_ver & 0xFF; + szBuf[6] = (ilits->chip->fw_mp_ver >> 8) & 0xFF; + szBuf[5] = (ilits->chip->fw_mp_ver >> 16) & 0xFF; + szBuf[4] = ilits->chip->fw_mp_ver >> 24; + szBuf[3] = ilits->chip->fw_ver & 0xFF; + szBuf[2] = (ilits->chip->fw_ver >> 8) & 0xFF; + szBuf[1] = (ilits->chip->fw_ver >> 16) & 0xFF; + szBuf[0] = ilits->chip->fw_ver >> 24; + ILI_DBG("Firmware version = %d.%d.%d.%d\n", szBuf[0], szBuf[1], szBuf[2], szBuf[3]); + ILI_DBG("Firmware MP version = %d.%d.%d.%d\n", szBuf[4], szBuf[5], szBuf[6], szBuf[7]); + + if (copy_to_user((u8 *) arg, szBuf, 8)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_PL_VER: + ILI_DBG("ioctl: get protocl version\n"); + szBuf[2] = ilits->protocol->ver & 0xFF; + szBuf[1] = (ilits->protocol->ver >> 8) & 0xFF; + szBuf[0] = ilits->protocol->ver >> 16; + ILI_DBG("Protocol version = %d.%d.%d\n", szBuf[0], szBuf[1], szBuf[2]); + + if (copy_to_user((u8 *) arg, szBuf, 3)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_CORE_VER: + ILI_DBG("ioctl: get core version\n"); + if (ilits->protocol->core_ver_len == P5_X_CORE_VER_THREE_LENGTH) + szBuf[3] = 0xFF; + else + szBuf[3] = ilits->chip->core_ver & 0xFF; + szBuf[2] = (ilits->chip->core_ver >> 8) & 0xFF; + szBuf[1] = (ilits->chip->core_ver >> 16) & 0xFF; + szBuf[0] = ilits->chip->core_ver >> 24; + ILI_DBG("Core version = %d.%d.%d.%d, length = %d\n", + szBuf[0], szBuf[1], szBuf[2], szBuf[3], ilits->protocol->core_ver_len); + + if (copy_to_user((u8 *) arg, szBuf, 4)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_DRV_VER: + ILI_DBG("ioctl: get driver version\n"); + length = snprintf(szBuf, USER_STR_BUFF * sizeof(unsigned char), "%s", DRIVER_VERSION); + + if (copy_to_user((u8 *) arg, szBuf, length)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_CHIP_ID: + ILI_DBG("ioctl: get chip id\n"); + id_to_user[0] = ilits->chip->pid; + id_to_user[1] = ilits->chip->otp_id; + id_to_user[2] = ilits->chip->ana_id; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 3)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_NETLINK_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: netlink ctrl = %d\n", szBuf[0]); + if (szBuf[0]) { + ilits->netlink = ENABLE; + ILI_DBG("ioctl: Netlink is enabled\n"); + } else { + ilits->netlink = DISABLE; + ILI_DBG("ioctl: Netlink is disabled\n"); + } + break; + case ILITEK_IOCTL_TP_NETLINK_STATUS: + ILI_DBG("ioctl: get netlink stat = %d\n", ilits->netlink); + if (copy_to_user((int *)arg, &ilits->netlink, sizeof(int))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_MODE_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 10)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: switch fw format = %d\n", szBuf[0]); + ret = ili_tp_data_mode_ctrl(szBuf); + + break; + case ILITEK_IOCTL_TP_MODE_STATUS: + ILI_DBG("ioctl: current firmware mode = %d", ilits->actual_tp_mode); + if (copy_to_user((int *)arg, &ilits->actual_tp_mode, sizeof(int))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + /* It works for host downloado only */ + case ILITEK_IOCTL_ICE_MODE_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: switch ice mode = %d, mcu on = %d\n", szBuf[0], szBuf[1]); + + if (ili_ice_mode_ctrl(szBuf[0] ? ENABLE : DISABLE, + szBuf[1] ? ENABLE : DISABLE) < 0) + ILI_ERR("ioctl: set ice mode failed\n"); + + break; + case ILITEK_IOCTL_TP_INTERFACE_TYPE: + if_to_user = ilits->hwif->bus_type; + if (copy_to_user((u8 *) arg, &if_to_user, sizeof(if_to_user))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_DUMP_FLASH: + ILI_DBG("ioctl: dump flash data\n"); + ret = ili_fw_dump_flash_data(0, 0, true); + if (ret < 0) { + ILI_ERR("ioctl: Failed to dump flash data\n"); + } + break; + case ILITEK_IOCTL_TP_FW_UART_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: fw UART = %d\n", szBuf[0]); + + ili_fw_uart_ctrl(szBuf[0]); + + if_to_user = szBuf[0]; + + if (copy_to_user((u8 *) arg, &if_to_user, sizeof(if_to_user))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_PANEL_INFO: + ILI_DBG("ioctl: get panel resolution\n"); + id_to_user[0] = ilits->panel_wid; + id_to_user[1] = ilits->panel_hei; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 2)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_INFO: + ILI_DBG("ioctl: get tp info\n"); + id_to_user[0] = ilits->min_x; + id_to_user[1] = ilits->min_y; + id_to_user[2] = ilits->max_x; + id_to_user[3] = ilits->max_y; + id_to_user[4] = ilits->xch_num; + id_to_user[5] = ilits->ych_num; + id_to_user[6] = ilits->stx; + id_to_user[7] = ilits->srx; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 8)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_WRAPPER_RW: + ILI_DBG("ioctl: wrapper rw\n"); + + if (i2c_rw_length > IOCTL_I2C_BUFF || i2c_rw_length == 0) { + ILI_ERR("ERROR! i2c_rw_length is is invalid\n"); + ret = -ENOTTY; + break; + } + + if (copy_from_user(szBuf, (u8 *) arg, i2c_rw_length)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + wrap_int = szBuf[0]; + wrap_rlen = (szBuf[1] << 8) | szBuf[2]; + wrap_wlen = (szBuf[3] << 8) | szBuf[4]; + + ILI_DBG("wrap_int = %d\n", wrap_int); + ILI_DBG("wrap_rlen = %d\n", wrap_rlen); + ILI_DBG("wrap_wlen = %d\n", wrap_wlen); + + if (wrap_wlen > IOCTL_I2C_BUFF || wrap_rlen > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! R/W len is largn than ioctl buf\n"); + ret = -ENOTTY; + break; + } + + if (wrap_rlen > 0) { + wrap_rbuf = kcalloc(IOCTL_I2C_BUFF, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(wrap_rbuf)) { + ILI_ERR("Failed to allocate mem\n"); + ret = -ENOMEM; + break; + } + } + + if (wrap_int == 1) { + i2c_irq = ON; + spi_irq = ON; + } else if (wrap_int == 2) { + i2c_irq = OFF; + spi_irq = OFF; + } else { + i2c_irq= OFF; + spi_irq = (wrap_rlen > 0 ? ON : OFF); + } + + ILI_DBG("i2c_irq = %d, spi_irq = %d\n", i2c_irq, spi_irq); + + ilits->wrapper(szBuf + 5, wrap_wlen, wrap_rbuf, wrap_rlen, spi_irq, i2c_irq); + + ili_dump_data(szBuf + 5, 8, wrap_wlen, 16, "wrap_wbuf:"); + ili_dump_data(wrap_rbuf, 8, wrap_rlen, 16, "wrap_rbuf:"); + if (copy_to_user((u8 *) arg, wrap_rbuf, wrap_rlen)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_DDI_WRITE: + if (copy_from_user(szBuf, (u8 *) arg, 3)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: page = %x, reg = %x, data = %x\n", szBuf[0], szBuf[1], szBuf[2]); + ili_ic_set_ddi_reg_onepage(szBuf[0], szBuf[1], szBuf[2]); + break; + case ILITEK_IOCTL_DDI_READ: + if (copy_from_user(szBuf, (u8 *) arg, 2)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: page = %x, reg = %x\n", szBuf[0], szBuf[1]); + ili_ic_get_ddi_reg_onepage(szBuf[0], szBuf[1], &szBuf[2]); + ILI_DBG("ioctl: data = %x\n", szBuf[2]); + if (copy_to_user((u8 *) arg, szBuf + 2, 1)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + default: + ret = -ENOTTY; + break; + } + + ipio_kfree((void **)&szBuf); + ipio_kfree((void **)&wrap_rbuf); + +out: + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + mutex_unlock(&ilits->touch_mutex); + return ret; +} + +static struct proc_dir_entry *proc_dir_ilitek; + +typedef struct { + char *name; + struct proc_dir_entry *node; + struct file_operations *fops; + bool isCreated; +} proc_node; + +static struct file_operations proc_mp_lcm_on_test_fops = { + .read = ilitek_node_mp_lcm_on_test_read, +}; + +static struct file_operations proc_mp_lcm_off_test_fops = { + .read = ilitek_node_mp_lcm_off_test_read, +}; + +static struct file_operations proc_ver_info_fops = { + .read = ilitek_node_ver_info_read, +}; + +static struct file_operations proc_change_list_fops = { + .read = ilitek_node_change_list_read, +}; + +static struct file_operations proc_debug_message_fops = { + .read = ilitek_proc_debug_message_read, +}; + +static struct file_operations proc_debug_message_switch_fops = { + .read = ilitek_proc_debug_switch_read, +}; + +static struct file_operations proc_ioctl_fops = { + .unlocked_ioctl = ilitek_node_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ilitek_node_compat_ioctl, +#endif + .write = ilitek_node_ioctl_write, +}; + +static struct file_operations proc_fw_upgrade_fops = { + .read = ilitek_node_fw_upgrade_read, +}; + +static struct file_operations proc_fw_process_fops = { + .read = ilitek_proc_fw_process_read, +}; + +static struct file_operations proc_get_delta_data_fops = { + .read = ilitek_proc_get_delta_data_read, +}; + +static struct file_operations proc_get_raw_data_fops = { + .read = ilitek_proc_fw_get_raw_data_read, +}; + +static struct file_operations proc_rw_tp_reg_fops = { + .read = ilitek_proc_rw_tp_reg_read, + .write = ilitek_proc_rw_tp_reg_write, +}; + +static struct file_operations proc_fw_pc_counter_fops = { + .read = ilitek_proc_fw_pc_counter_read, +}; + +static struct file_operations proc_get_debug_mode_data_fops = { + .read = ilitek_proc_get_debug_mode_data_read, + .write = ilitek_proc_get_debug_mode_data_write, +}; + +static struct file_operations proc_debug_level_fops = { + .read = ilitek_proc_debug_level_read, +}; + +proc_node iliproc[] = { + {"ioctl", NULL, &proc_ioctl_fops, false}, + {"fw_process", NULL, &proc_fw_process_fops, false}, + {"fw_upgrade", NULL, &proc_fw_upgrade_fops, false}, + {"debug_level", NULL, &proc_debug_level_fops, false}, + {"mp_lcm_on_test", NULL, &proc_mp_lcm_on_test_fops, false}, + {"mp_lcm_off_test", NULL, &proc_mp_lcm_off_test_fops, false}, + {"debug_message", NULL, &proc_debug_message_fops, false}, + {"debug_message_switch", NULL, &proc_debug_message_switch_fops, false}, + {"fw_pc_counter", NULL, &proc_fw_pc_counter_fops, false}, + {"show_delta_data", NULL, &proc_get_delta_data_fops, false}, + {"show_raw_data", NULL, &proc_get_raw_data_fops, false}, + {"get_debug_mode_data", NULL, &proc_get_debug_mode_data_fops, false}, + {"rw_tp_reg", NULL, &proc_rw_tp_reg_fops, false}, + {"ver_info", NULL, &proc_ver_info_fops, false}, + {"change_list", NULL, &proc_change_list_fops, false}, +}; + +#define NETLINK_USER 21 +static struct sock *netlink_skb; +static struct nlmsghdr *netlink_head; +static struct sk_buff *skb_out; +static int netlink_pid; + +void ili_netlink_reply_msg(void *raw, int size) +{ + int ret; + int msg_size = size; + u8 *data = (u8 *) raw; + + ILI_INFO("The size of data being sent to user = %d\n", msg_size); + ILI_INFO("pid = %d\n", netlink_pid); + ILI_INFO("Netlink is enable = %d\n", ilits->netlink); + + if (ilits->netlink) { + skb_out = nlmsg_new(msg_size, 0); + + if (!skb_out) { + ILI_ERR("Failed to allocate new skb\n"); + return; + } + + netlink_head = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); + NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */ + + /* strncpy(NLMSG_DATA(netlink_head), data, msg_size); */ + ipio_memcpy(nlmsg_data(netlink_head), data, msg_size, size); + + ret = nlmsg_unicast(netlink_skb, skb_out, netlink_pid); + if (ret < 0) + ILI_ERR("Failed to send data back to user\n"); + } +} + +static void netlink_recv_msg(struct sk_buff *skb) +{ + netlink_pid = 0; + + ILI_INFO("Netlink = %d\n", ilits->netlink); + + netlink_head = (struct nlmsghdr *)skb->data; + + ILI_INFO("Received a request from client: %s, %d\n", + (char *)NLMSG_DATA(netlink_head), (int)strlen((char *)NLMSG_DATA(netlink_head))); + + /* pid of sending process */ + netlink_pid = netlink_head->nlmsg_pid; + + ILI_INFO("the pid of sending process = %d\n", netlink_pid); + + /* TODO: may do something if there's not receiving msg from user. */ + if (netlink_pid != 0) { + ILI_ERR("The channel of Netlink has been established successfully !\n"); + ilits->netlink = ENABLE; + } else { + ILI_ERR("Failed to establish the channel between kernel and user space\n"); + ilits->netlink = DISABLE; + } +} + +static int netlink_init(void) +{ + int ret = 0; + +#if KERNEL_VERSION(3, 4, 0) > LINUX_VERSION_CODE + netlink_skb = netlink_kernel_create(&init_net, NETLINK_USER, netlink_recv_msg, NULL, THIS_MODULE); +#else + struct netlink_kernel_cfg cfg = { + .input = netlink_recv_msg, + }; + + netlink_skb = netlink_kernel_create(&init_net, NETLINK_USER, &cfg); +#endif + + ILI_INFO("Initialise Netlink and create its socket\n"); + + if (!netlink_skb) { + ILI_ERR("Failed to create nelink socket\n"); + ret = -EFAULT; + } + return ret; +} + +static ssize_t path_show(struct device *pDevice, struct device_attribute *pAttr, char *pBuf) +{ + ssize_t blen; + const char *path; + + path = kobject_get_path(&ilits->spi->dev.kobj, GFP_KERNEL); + blen = scnprintf(pBuf, PAGE_SIZE, "%s\n", path ? path : "na"); + kfree(path); + + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ILI_INFO("*** %s() vendor = %s ***\n", __func__, "ilitek"); + return scnprintf(buf, PAGE_SIZE, "ilitek"); +} + + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +int ilitek_sys_init(void) +{ + int i; + s32 ret = 0; + dev_t devno; + struct device_attribute *attrs = touchscreen_attributes; + + ret = alloc_chrdev_region(&devno, 0, 1, ILI_SPI_NAME); + if (ret) { + ILI_ERR ("can't allocate chrdev\n"); + return ret; + } else { + + /* set sysfs for firmware */ + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + ret = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + ILI_ERR("Failed to create touchscreen class!\n"); + return ret; + } + + touchscreen_class_dev = device_create(touchscreen_class, NULL, devno, NULL, ILI_SPI_NAME); + pr_info(" touchscreen_class_dev = %p \n", touchscreen_class_dev); + if (IS_ERR(touchscreen_class_dev)) { + ret = PTR_ERR(touchscreen_class_dev); + touchscreen_class_dev = NULL; + ILI_ERR("Failed to create device(touchscreen_class_dev)!\n"); + return ret; + } + ILI_INFO("Succeed to create device(touchscreen_class_dev)!\n"); + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + ret = device_create_file(touchscreen_class_dev, &attrs[i]); + if (ret < 0) + goto device_destroy; + } + } + + return ret; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + + touchscreen_class_dev = NULL; + class_unregister(touchscreen_class); + ILI_ERR("error creating touchscreen class\n"); + + return -ENODEV; +} + +void ili_node_init(void) +{ + int i = 0, ret = 0; + + proc_dir_ilitek = proc_mkdir("ilitek", NULL); + + for (; i < ARRAY_SIZE(iliproc); i++) { + iliproc[i].node = proc_create(iliproc[i].name, 0644, proc_dir_ilitek, iliproc[i].fops); + + if (iliproc[i].node == NULL) { + iliproc[i].isCreated = false; + ILI_ERR("Failed to create %s under /proc\n", iliproc[i].name); + ret = -ENODEV; + } else { + iliproc[i].isCreated = true; + ILI_INFO("Succeed to create %s under /proc\n", iliproc[i].name); + } + } + netlink_init(); + ilitek_sys_init(); +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_qcom.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_qcom.c new file mode 100755 index 000000000000..00810abe6900 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_qcom.c @@ -0,0 +1,758 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define DTS_INT_GPIO "touch,irq-gpio" +#define DTS_RESET_GPIO "touch,reset-gpio" +#define DTS_OF_NAME "tchip,ilitek" + +void ili_tp_reset(void) +{ + ILI_INFO("edge delay = %d\n", ilits->rst_edge_delay); + + /* Need accurate power sequence, do not change it to msleep */ + gpio_direction_output(ilits->tp_rst, 1); + mdelay(1); + gpio_set_value(ilits->tp_rst, 0); + mdelay(5); + gpio_set_value(ilits->tp_rst, 1); + mdelay(ilits->rst_edge_delay); +} + +void ili_input_register(void) +{ + ILI_INFO(); + + ilits->input = input_allocate_device(); + if (ERR_ALLOC_MEM(ilits->input)) { + ILI_ERR("Failed to allocate touch input device\n"); + input_free_device(ilits->input); + return; + } + + ilits->input->name = ilits->hwif->name; + ilits->input->phys = ilits->phys; + ilits->input->dev.parent = ilits->dev; + ilits->input->id.bustype = ilits->hwif->bus_type; + + /* set the supported event type for input device */ + set_bit(EV_ABS, ilits->input->evbit); + set_bit(EV_SYN, ilits->input->evbit); + set_bit(EV_KEY, ilits->input->evbit); + set_bit(BTN_TOUCH, ilits->input->keybit); + set_bit(BTN_TOOL_FINGER, ilits->input->keybit); + set_bit(INPUT_PROP_DIRECT, ilits->input->propbit); + + input_set_abs_params(ilits->input, ABS_MT_POSITION_X, TOUCH_SCREEN_X_MIN, ilits->panel_wid - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_POSITION_Y, TOUCH_SCREEN_Y_MIN, ilits->panel_hei - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + + if (MT_PRESSURE) + input_set_abs_params(ilits->input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + if (MT_B_TYPE) { +#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM); +#endif /* LINUX_VERSION_CODE */ + } else { + input_set_abs_params(ilits->input, ABS_MT_TRACKING_ID, 0, MAX_TOUCH_NUM, 0, 0); + } + + /* Gesture keys register */ + input_set_capability(ilits->input, EV_KEY, KEY_POWER); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_UP); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_O); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_E); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_M); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_W); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_S); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_V); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_Z); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_C); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_F); + + __set_bit(KEY_GESTURE_POWER, ilits->input->keybit); + __set_bit(KEY_GESTURE_UP, ilits->input->keybit); + __set_bit(KEY_GESTURE_DOWN, ilits->input->keybit); + __set_bit(KEY_GESTURE_LEFT, ilits->input->keybit); + __set_bit(KEY_GESTURE_RIGHT, ilits->input->keybit); + __set_bit(KEY_GESTURE_O, ilits->input->keybit); + __set_bit(KEY_GESTURE_E, ilits->input->keybit); + __set_bit(KEY_GESTURE_M, ilits->input->keybit); + __set_bit(KEY_GESTURE_W, ilits->input->keybit); + __set_bit(KEY_GESTURE_S, ilits->input->keybit); + __set_bit(KEY_GESTURE_V, ilits->input->keybit); + __set_bit(KEY_GESTURE_Z, ilits->input->keybit); + __set_bit(KEY_GESTURE_C, ilits->input->keybit); + __set_bit(KEY_GESTURE_F, ilits->input->keybit); + + /* register the input device to input sub-system */ + if (input_register_device(ilits->input) < 0) { + ILI_ERR("Failed to register touch input device\n"); + input_unregister_device(ilits->input); + input_free_device(ilits->input); + } +} + +#if REGULATOR_POWER +void ili_plat_regulator_power_on(bool status) +{ + ILI_INFO("%s\n", status ? "POWER ON" : "POWER OFF"); + + if (status) { + if (ilits->vdd) { + if (regulator_enable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_enable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } else { + if (ilits->vdd) { + if (regulator_disable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_disable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } + atomic_set(&ilits->ice_stat, DISABLE); + mdelay(5); +} + +static void ilitek_plat_regulator_power_init(void) +{ + const char *vdd_name = "vdd"; + const char *vcc_name = "vcc"; + + ilits->vdd = regulator_get(ilits->dev, vdd_name); + if (ERR_ALLOC_MEM(ilits->vdd)) { + ILI_ERR("regulator_get VDD fail\n"); + ilits->vdd = NULL; + } + if (regulator_set_voltage(ilits->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < 0) + ILI_ERR("Failed to set VDD %d\n", VDD_VOLTAGE); + + ilits->vcc = regulator_get(ilits->dev, vcc_name); + if (ERR_ALLOC_MEM(ilits->vcc)) { + ILI_ERR("regulator_get VCC fail.\n"); + ilits->vcc = NULL; + } + if (regulator_set_voltage(ilits->vcc, VCC_VOLTAGE, VCC_VOLTAGE) < 0) + ILI_ERR("Failed to set VCC %d\n", VCC_VOLTAGE); + + ili_plat_regulator_power_on(true); +} +#endif + +static int ilitek_plat_gpio_register(void) +{ + int ret = 0; + u32 flag; + struct device_node *dev_node = ilits->dev->of_node; + + ilits->tp_int = of_get_named_gpio_flags(dev_node, DTS_INT_GPIO, 0, &flag); + ilits->tp_rst = of_get_named_gpio_flags(dev_node, DTS_RESET_GPIO, 0, &flag); + + ILI_INFO("TP INT: %d\n", ilits->tp_int); + ILI_INFO("TP RESET: %d\n", ilits->tp_rst); + + if (!gpio_is_valid(ilits->tp_int)) { + ILI_ERR("Invalid INT gpio: %d\n", ilits->tp_int); + return -EBADR; + } + + if (!gpio_is_valid(ilits->tp_rst)) { + ILI_ERR("Invalid RESET gpio: %d\n", ilits->tp_rst); + return -EBADR; + } + + ret = gpio_request(ilits->tp_int, "TP_INT"); + if (ret < 0) { + ILI_ERR("Request IRQ GPIO failed, ret = %d\n", ret); + gpio_free(ilits->tp_int); + ret = gpio_request(ilits->tp_int, "TP_INT"); + if (ret < 0) { + ILI_ERR("Retrying request INT GPIO still failed , ret = %d\n", ret); + goto out; + } + } + + ret = gpio_request(ilits->tp_rst, "TP_RESET"); + if (ret < 0) { + ILI_ERR("Request RESET GPIO failed, ret = %d\n", ret); + gpio_free(ilits->tp_rst); + ret = gpio_request(ilits->tp_rst, "TP_RESET"); + if (ret < 0) { + ILI_ERR("Retrying request RESET GPIO still failed , ret = %d\n", ret); + goto out; + } + } + +out: + gpio_direction_input(ilits->tp_int); + return ret; +} + +void ili_irq_disable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == DISABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + disable_irq_nosync(ilits->irq_num); + atomic_set(&ilits->irq_stat, DISABLE); + ILI_DBG("Disable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +void ili_irq_enable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == ENABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + enable_irq(ilits->irq_num); + atomic_set(&ilits->irq_stat, ENABLE); + ILI_DBG("Enable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +static irqreturn_t ilitek_plat_isr_top_half(int irq, void *dev_id) +{ + if (irq != ilits->irq_num) { + ILI_ERR("Incorrect irq number (%d)\n", irq); + return IRQ_NONE; + } + + if (atomic_read(&ilits->cmd_int_check) == ENABLE) { + atomic_set(&ilits->cmd_int_check, DISABLE); + ILI_DBG("CMD INT detected, ignore\n"); + wake_up(&(ilits->inq)); + return IRQ_HANDLED; + } + + if (ilits->prox_near) { + ILI_INFO("Proximity event, ignore interrupt!\n"); + return IRQ_HANDLED; + } + + ILI_DBG("report: %d, rst: %d, fw: %d, switch: %d, mp: %d, sleep: %d, esd: %d\n", + ilits->report, + atomic_read(&ilits->tp_reset), + atomic_read(&ilits->fw_stat), + atomic_read(&ilits->tp_sw_mode), + atomic_read(&ilits->mp_stat), + atomic_read(&ilits->tp_sleep), + atomic_read(&ilits->esd_stat)); + + if (!ilits->report || atomic_read(&ilits->tp_reset) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep) || + atomic_read(&ilits->esd_stat)) { + ILI_DBG("ignore interrupt !\n"); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ilitek_plat_isr_bottom_half(int irq, void *dev_id) +{ + if (mutex_is_locked(&ilits->touch_mutex)) { + ILI_DBG("touch is locked, ignore\n"); + return IRQ_HANDLED; + } + mutex_lock(&ilits->touch_mutex); + ili_report_handler(); + mutex_unlock(&ilits->touch_mutex); + return IRQ_HANDLED; +} + +void ili_irq_unregister(void) +{ + devm_free_irq(ilits->dev, ilits->irq_num, NULL); +} + +int ili_irq_register(int type) +{ + int ret = 0; + static bool get_irq_pin; + + atomic_set(&ilits->irq_stat, DISABLE); + + if (get_irq_pin == false) { + ilits->irq_num = gpio_to_irq(ilits->tp_int); + get_irq_pin = true; + } + + ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num); + + ret = devm_request_threaded_irq(ilits->dev, ilits->irq_num, + ilitek_plat_isr_top_half, + ilitek_plat_isr_bottom_half, + type | IRQF_ONESHOT, "ilitek", NULL); + + if (type == IRQF_TRIGGER_FALLING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_FALLING\n"); + if (type == IRQF_TRIGGER_RISING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_RISING\n"); + + if (ret != 0) + ILI_ERR("Failed to register irq handler, irq = %d, ret = %d\n", ilits->irq_num, ret); + + atomic_set(&ilits->irq_stat, ENABLE); + + return ret; +} + +#if SPRD_SYSFS_SUSPEND_RESUME +static ssize_t ts_suspend_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", ilits->tp_suspend ? "true" : "false"); +} + +static ssize_t ts_suspend_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if ((buf[0] == '1') && !ilits->tp_suspend) + ili_sleep_handler(TP_DEEP_SLEEP); + else if ((buf[0] == '0') && ilits->tp_suspend) + ili_sleep_handler(TP_RESUME); + + return count; +} +static DEVICE_ATTR_RW(ts_suspend); + +static struct attribute *ilitek_dev_suspend_atts[] = { + &dev_attr_ts_suspend.attr, + NULL +}; + +static const struct attribute_group ilitek_dev_suspend_atts_group = { + .attrs = ilitek_dev_suspend_atts, +}; + +static const struct attribute_group *ilitek_dev_attr_groups[] = { + &ilitek_dev_suspend_atts_group, + NULL +}; + +int ili_sysfs_add_device(struct device *dev) { + int ret = 0, i; + + for (i = 0; ilitek_dev_attr_groups[i]; i++) { + ret = sysfs_create_group(&dev->kobj, ilitek_dev_attr_groups[i]); + if (ret) { + while (--i >= 0) { + sysfs_remove_group(&dev->kobj, ilitek_dev_attr_groups[i]); + } + break; + } + } + + return ret; +} + +int ili_sysfs_remove_device(struct device *dev) { + int i; + + sysfs_remove_link(NULL, "touchscreen"); + for (i = 0; ilitek_dev_attr_groups[i]; i++) { + sysfs_remove_group(&dev->kobj, ilitek_dev_attr_groups[i]); + } + + return 0; +} +#else + +#ifdef CONFIG_DRM +static int ilitek_plat_notifier_fb(struct notifier_block *self, unsigned long event, void *data) +{ + int *blank; + struct drm_panel_notifier *evdata = data; + + ILI_INFO("Notifier's event = %ld\n", event); + + if (!(evdata && evdata->data)) + return 0; + + blank = evdata->data; + + switch (*blank) { + case DRM_PANEL_BLANK_POWERDOWN: +/* + if (TP_SUSPEND_PRIO) { + if (event != MSM_DRM_EARLY_EVENT_BLANK) + return NOTIFY_DONE; + } else { + if (event != MSM_DRM_EVENT_BLANK) + return NOTIFY_DONE; + } +*/ + if (event == DRM_PANEL_EARLY_EVENT_BLANK) + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); + break; + + case DRM_PANEL_BLANK_UNBLANK: + if (event == DRM_PANEL_EVENT_BLANK) + { + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + + } + break; + default: + ILI_ERR("Unknown event, blank = %d\n", *blank); + break; + } + return NOTIFY_OK; +} +#else + +#if defined(CONFIG_FB) || defined(CONFIG_DRM_MSM) +static int ilitek_plat_notifier_fb(struct notifier_block *self, unsigned long event, void *data) +{ + int *blank; + struct fb_event *evdata = data; + + ILI_INFO("Notifier's event = %ld\n", event); + + /* + * FB_EVENT_BLANK(0x09): A hardware display blank change occurred. + * FB_EARLY_EVENT_BLANK(0x10): A hardware display blank early change occurred. + */ + if (evdata && evdata->data) { + blank = evdata->data; + switch (*blank) { +#ifdef CONFIG_DRM_MSM + case MSM_DRM_BLANK_POWERDOWN: +#else + case FB_BLANK_POWERDOWN: +#endif +#if CONFIG_PLAT_SPRD + case DRM_MODE_DPMS_OFF: +#endif /* CONFIG_PLAT_SPRD */ + if (TP_SUSPEND_PRIO) { +#ifdef CONFIG_DRM_MSM + if (event != MSM_DRM_EARLY_EVENT_BLANK) +#else + if (event != FB_EARLY_EVENT_BLANK) +#endif + return NOTIFY_DONE; + } else { +#ifdef CONFIG_DRM_MSM + if (event != MSM_DRM_EVENT_BLANK) +#else + if (event != FB_EVENT_BLANK) +#endif + return NOTIFY_DONE; + } + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); + break; +#ifdef CONFIG_DRM_MSM + case MSM_DRM_BLANK_UNBLANK: + case MSM_DRM_BLANK_NORMAL: +#else + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: +#endif + +#if CONFIG_PLAT_SPRD + case DRM_MODE_DPMS_ON: +#endif /* CONFIG_PLAT_SPRD */ + +#ifdef CONFIG_DRM_MSM + if (event == MSM_DRM_EVENT_BLANK) +#else + if (event == FB_EVENT_BLANK) +#endif + { + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + + } + break; + default: + ILI_ERR("Unknown event, blank = %d\n", *blank); + break; + } + } + return NOTIFY_OK; +} +#else +static void ilitek_plat_early_suspend(struct early_suspend *h) +{ + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); +} + +static void ilitek_plat_late_resume(struct early_suspend *h) +{ + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); +} +#endif +#endif + +static void ilitek_plat_sleep_init(void) +{ + ILI_INFO("Init notifier_fb struct\n"); + ilits->notifier_fb.notifier_call = ilitek_plat_notifier_fb; +#if defined(CONFIG_DRM) +{ + int ret = 0; + extern struct drm_panel *ili_active_panel; + if (ili_active_panel) { + ret = drm_panel_notifier_register(ili_active_panel, &ilits->notifier_fb); + if(ret) { + ILI_ERR("register drm_notifier failed. ret=%d\n", ret); + } + } +} +#else +#if defined(CONFIG_FB) || defined(CONFIG_DRM_MSM) +#if defined(CONFIG_DRM_MSM) + if (msm_drm_register_client(&ilits->notifier_fb)) { + ILI_ERR("msm_drm_register_client Unable to register fb_notifier\n"); + } +#else +#if CONFIG_PLAT_SPRD + if (adf_register_client(&ilits->notifier_fb)) + ILI_ERR("Unable to register notifier_fb\n"); +#else + if (fb_register_client(&ilits->notifier_fb)) + ILI_ERR("Unable to register notifier_fb\n"); +#endif /* CONFIG_PLAT_SPRD */ +#endif /* CONFIG_DRM_MSM */ +#else + ILI_INFO("Init eqarly_suspend struct\n"); + ilits->early_suspend.suspend = ilitek_plat_early_suspend; + ilits->early_suspend.resume = ilitek_plat_late_resume; + ilits->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + register_early_suspend(&ilits->early_suspend); +#endif +#endif +} +#endif +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE +/* add_for_charger_start */ +static int ilitek_charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) { + int ret = 0; + struct power_supply *psy = NULL; + union power_supply_propval prop; + + + if(ilits->fw_update_stat != 100) + return 0; + + psy= power_supply_get_by_name("usb"); + if (!psy) { + ILI_ERR("Couldn't get usbpsy\n"); + return -EINVAL; + } + if (!strcmp(psy->desc->name, "usb")) { + if (psy && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + } else { + if(ilits->usb_plug_status == 2) + ilits->usb_plug_status = prop.intval; + if(ilits->usb_plug_status != prop.intval) { + ILI_INFO("usb prop.intval =%d\n", prop.intval); + ilits->usb_plug_status = prop.intval; + if(!ilits->tp_suspend && (ilits->charger_notify_wq != NULL)) + queue_work(ilits->charger_notify_wq,&ilits->update_charger); + } + } + } + } + return 0; +} +static void ilitek_update_charger(struct work_struct *work) +{ + int ret = 0; + mutex_lock(&ilits->touch_mutex); + ret = ili_ic_func_ctrl("plug", !ilits->usb_plug_status);// plug in + if(ret<0) { + ILI_ERR("Write plug in failed\n"); + } + mutex_unlock(&ilits->touch_mutex); +} +void ilitek_plat_charger_init(void) +{ + int ret = 0; + ilits->usb_plug_status = 2; + ilits->charger_notify_wq = create_singlethread_workqueue("ili_charger_wq"); + if (!ilits->charger_notify_wq) { + ILI_ERR("allocate ili_charger_notify_wq failed\n"); + return; + } + INIT_WORK(&ilits->update_charger, ilitek_update_charger); + ilits->notifier_charger.notifier_call = ilitek_charger_notifier_callback; + ret = power_supply_reg_notifier(&ilits->notifier_charger); + if (ret < 0) + ILI_ERR("power_supply_reg_notifier failed\n"); +} +/* add_for_charger_end */ +#endif +#endif +static int ilitek_plat_probe(void) +{ + ILI_INFO("platform probe\n"); + +#if REGULATOR_POWER + ilitek_plat_regulator_power_init(); +#endif + + if (ilitek_plat_gpio_register() < 0) + ILI_ERR("Register gpio failed\n"); + + ili_irq_register(ilits->irq_tirgger_type); + + if (ili_tddi_init() < 0) { + ILI_ERR("ILITEK Driver probe failed\n"); + ili_irq_unregister(); + return -ENODEV; + } +#if SPRD_SYSFS_SUSPEND_RESUME + ili_sysfs_add_device(ilits->dev); + if (sysfs_create_link(NULL, &ilits->dev->kobj, "touchscreen") < 0) + ILI_INFO("Failed to create link!\n"); +#else + ilitek_plat_sleep_init(); +#endif + ilits->pm_suspend = false; + init_completion(&ilits->pm_completion); +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE + /* add_for_charger_start */ + ilitek_plat_charger_init(); + /* add_for_charger_end */ +#endif +#endif + ILI_INFO("ILITEK Driver loaded successfully!"); + return 0; +} + +static int ilitek_tp_pm_suspend(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM SUSPEND"); + ilits->pm_suspend = true; + reinit_completion(&ilits->pm_completion); + return 0; +} + +static int ilitek_tp_pm_resume(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM RESUME"); + ilits->pm_suspend = false; + complete(&ilits->pm_completion); + return 0; +} + +static int ilitek_plat_remove(void) +{ + ILI_INFO("remove plat dev\n"); +#if SPRD_SYSFS_SUSPEND_RESUME + ili_sysfs_remove_device(ilits->dev); +#endif + ili_dev_remove(); + return 0; +} + +static const struct dev_pm_ops tp_pm_ops = { + .suspend = ilitek_tp_pm_suspend, + .resume = ilitek_tp_pm_resume, +}; + +static const struct of_device_id tp_match_table[] = { + {.compatible = DTS_OF_NAME}, + {}, +}; + +static struct ilitek_hwif_info hwif = { + .bus_type = TDDI_INTERFACE, + .plat_type = TP_PLAT_QCOM, + .owner = THIS_MODULE, + .name = TDDI_DEV_ID, + .of_match_table = of_match_ptr(tp_match_table), + .plat_probe = ilitek_plat_probe, + .plat_remove = ilitek_plat_remove, + .pm = &tp_pm_ops, +}; + +static int __init ilitek_plat_dev_init(void) +{ + ILI_INFO("ILITEK TP driver init for QCOM\n"); + if (ili_dev_init(&hwif) < 0) { + ILI_ERR("Failed to register i2c/spi bus driver\n"); + return -ENODEV; + } + return 0; +} + +static void __exit ilitek_plat_dev_exit(void) +{ + ILI_INFO("remove plat dev\n"); + ili_dev_remove(); +} + +//module_init(ilitek_plat_dev_init); +late_initcall(ilitek_plat_dev_init); +module_exit(ilitek_plat_dev_exit); +MODULE_AUTHOR("ILITEK"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_spi.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_spi.c new file mode 100755 index 000000000000..9d6075872697 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_spi.c @@ -0,0 +1,655 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" +#ifdef CONFIG_DRM +struct drm_panel *ili_active_panel; +static int check_dt(struct device_node *np); +#endif + +struct touch_bus_info { + struct spi_driver bus_driver; + struct ilitek_hwif_info *hwif; +}; + +struct ilitek_ts_data *ilits; + +#if SPI_DMA_TRANSFER_SPLIT +#define DMA_TRANSFER_MAX_CHUNK 64 // number of chunks to be transferred. +#define DMA_TRANSFER_MAX_LEN 4096 // length of a chunk. + +int ili_spi_write_then_read_split(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status = -1, duplex_len = 0; + int xfercnt = 0, xferlen = 0, xferloop = 0; + int offset = 0; + u8 cmd = 0; + struct spi_message message; + struct spi_transfer *xfer; + + xfer = kzalloc(DMA_TRANSFER_MAX_CHUNK * sizeof(struct spi_transfer), GFP_KERNEL); + + if (n_rx > SPI_RX_BUF_SIZE) { + ILI_ERR("Rx length is greater than spi local buf, abort\n"); + status = -ENOMEM; + goto out; + } + + spi_message_init(&message); + memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); + memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); + + if ((n_tx > 0) && (n_rx > 0)) + cmd = SPI_READ; + else + cmd = SPI_WRITE; + + switch (cmd) { + case SPI_WRITE: + if (n_tx % DMA_TRANSFER_MAX_LEN) + xferloop = (n_tx / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = n_tx / DMA_TRANSFER_MAX_LEN; + + xferlen = n_tx; + memcpy(ilits->spi_tx, (u8 *)txbuf, xferlen); + + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = n_tx - (xfercnt+1) * DMA_TRANSFER_MAX_LEN; + } + status = spi_sync(spi, &message); + break; + case SPI_READ: + if (n_tx > DMA_TRANSFER_MAX_LEN) { + ILI_ERR("Tx length must be lower than dma length (%d).\n", DMA_TRANSFER_MAX_LEN); + status = -EINVAL; + break; + } + + if (!atomic_read(&ilits->ice_stat)) + offset = 2; + + memcpy(ilits->spi_tx, txbuf, n_tx); + duplex_len = n_tx + n_rx + offset; + + if (duplex_len % DMA_TRANSFER_MAX_LEN) + xferloop = (duplex_len / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = duplex_len / DMA_TRANSFER_MAX_LEN; + + xferlen = duplex_len; + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx; + xfer[xfercnt].rx_buf = ilits->spi_rx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = duplex_len - (xfercnt + 1) * DMA_TRANSFER_MAX_LEN; + } + + status = spi_sync(spi, &message); + if (status == 0) { + if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { + status = DO_SPI_RECOVER; + ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); + break; + } + + memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); + } else { + ILI_ERR("spi read fail, status = %d\n", status); + } + break; + default: + ILI_INFO("Unknown command 0x%x\n", cmd); + break; + } + +out: + ipio_kfree((void **)&xfer); + return status; +} +#else +int ili_spi_write_then_read_direct(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status = -1, duplex_len = 0; + int offset = 0; + u8 cmd; + struct spi_message message; + struct spi_transfer xfer; + + if (n_rx > SPI_RX_BUF_SIZE) { + ILI_ERR("Rx length is greater than spi local buf, abort\n"); + status = -ENOMEM; + goto out; + } + + spi_message_init(&message); + memset(&xfer, 0, sizeof(xfer)); + + if ((n_tx > 0) && (n_rx > 0)) + cmd = SPI_READ; + else + cmd = SPI_WRITE; + + switch (cmd) { + case SPI_WRITE: + xfer.len = n_tx; + xfer.tx_buf = txbuf; + spi_message_add_tail(&xfer, &message); + status = spi_sync(spi, &message); + break; + case SPI_READ: + if (!atomic_read(&ilits->ice_stat)) + offset = 2; + + duplex_len = n_tx + n_rx + offset; + if ((duplex_len > SPI_TX_BUF_SIZE) || + (duplex_len > SPI_RX_BUF_SIZE)) { + ILI_ERR("duplex_len is over than dma buf, abort\n"); + status = -ENOMEM; + break; + } + + memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); + memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); + + xfer.len = duplex_len; + memcpy(ilits->spi_tx, txbuf, n_tx); + xfer.tx_buf = ilits->spi_tx; + xfer.rx_buf = ilits->spi_rx; + + spi_message_add_tail(&xfer, &message); + status = spi_sync(spi, &message); + if (status == 0) { + if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { + status = DO_SPI_RECOVER; + ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); + break; + } + + memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); + } else { + ILI_ERR("spi read fail, status = %d\n", status); + } + break; + default: + ILI_INFO("Unknown command 0x%x\n", cmd); + break; + } + +out: + return status; +} +#endif + +static int ili_spi_mp_pre_cmd(u8 cdc) +{ + u8 pre[5] = {0}; + + if (!atomic_read(&ilits->mp_stat) || cdc != P5_X_SET_CDC_INIT || + ilits->chip->core_ver >= CORE_VER_1430) + return 0; + + ILI_DBG("mp test with pre commands\n"); + + pre[0] = SPI_WRITE; + pre[1] = 0x0;// dummy byte + pre[2] = 0x2;// Write len byte + pre[3] = P5_X_READ_DATA_CTRL; + pre[4] = P5_X_GET_CDC_DATA; + if (ilits->spi_write_then_read(ilits->spi, pre, 5, NULL, 0) < 0) { + ILI_ERR("Failed to write pre commands\n"); + return -1; + } + + pre[0] = SPI_WRITE; + pre[1] = 0x0;// dummy byte + pre[2] = 0x1;// Write len byte + pre[3] = P5_X_GET_CDC_DATA; + if (ilits->spi_write_then_read(ilits->spi, pre, 4, NULL, 0) < 0) { + ILI_ERR("Failed to write pre commands\n"); + return -1; + } + return 0; +} + +static int ili_spi_pll_clk_wakeup(void) +{ + int index = 0; + u8 wdata[32] = {0}; + u8 wakeup[9] = {0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3}; + u32 wlen = sizeof(wakeup); + + wdata[0] = SPI_WRITE; + wdata[1] = wlen >> 8; + wdata[2] = wlen & 0xff; + index = 3; + wlen += index; + + ipio_memcpy(&wdata[index], wakeup, wlen, wlen); + + ILI_INFO("Write dummy to wake up spi pll clk\n"); + if (ilits->spi_write_then_read(ilits->spi, wdata, wlen, NULL, 0) < 0) { + ILI_INFO("spi slave write error\n"); + return -1; + } + + return 0; +} + +static int ili_spi_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq) +{ + int ret = 0; + int mode = 0, index = 0; + u8 wdata[32] = {0}; + u8 checksum = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (wlen > 0) { + if (!txbuf) { + ILI_ERR("txbuf is null\n"); + return -ENOMEM; + } + + /* 3 bytes data consist of length and header */ + if ((wlen + 3) > sizeof(wdata)) { + ILI_ERR("WARNING! wlen(%d) > wdata(%d), using wdata length to transfer\n", wlen, (int)sizeof(wdata)); + wlen = sizeof(wdata); + } + } + + if (rlen > 0) { + if (!rxbuf) { + ILI_ERR("rxbuf is null\n"); + return -ENOMEM; + } + } + + if (rlen > 0 && !wlen) + mode = SPI_READ; + else + mode = SPI_WRITE; + + if (ilits->int_pulse) + ilits->detect_int_stat = ili_ic_check_int_pulse; + else + ilits->detect_int_stat = ili_ic_check_int_level; + + if (spi_irq) + atomic_set(&ilits->cmd_int_check, ENABLE); + + switch (mode) { + case SPI_WRITE: +#if ( PLL_CLK_WAKEUP_TP_RESUME == ENABLE ) + if (ilits->pll_clk_wakeup == true) { +#else + if ((ilits->pll_clk_wakeup == true) && (ilits->tp_suspend == true)) { +#endif + ret = ili_spi_pll_clk_wakeup(); + if (ret < 0) { + ILI_ERR("Wakeup pll clk error\n"); + break; + } + } + if (ice) { + wdata[0] = SPI_WRITE; + index = 1; + } else { + wdata[0] = SPI_WRITE; + wdata[1] = wlen >> 8; + wdata[2] = wlen & 0xff; + index = 3; + } + + wlen += index; + + ipio_memcpy(&wdata[index], txbuf, wlen, wlen); + + /* + * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum + * to the last index and plus 1 with size. + */ + if (atomic_read(&ilits->mp_stat) && wdata[index] == P5_X_SET_CDC_INIT) { + checksum = ili_calc_packet_checksum(&wdata[index], wlen - index); + wdata[wlen] = checksum; + wlen++; + wdata[1] = (wlen - index) >> 8; + wdata[2] = (wlen - index) & 0xff; + ili_dump_data(wdata, 8, wlen, 0, "mp cdc cmd with checksum"); + } + + ret = ilits->spi_write_then_read(ilits->spi, wdata, wlen, txbuf, 0); + if (ret < 0) { + ILI_INFO("spi-wrapper write error\n"); + break; + } + + /* Won't break if it needs to read data following with writing. */ + if (!rlen) + break; + case SPI_READ: + if (!ice && spi_irq) { + /* Check INT triggered by FW when sending cmds. */ + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } + + ret = ili_spi_mp_pre_cmd(wdata[3]); + if (ret < 0) + ILI_ERR("spi-wrapper mp pre cmd error\n"); + + wdata[0] = SPI_READ; + + ret = ilits->spi_write_then_read(ilits->spi, wdata, 1, rxbuf, rlen); + if (ret < 0) + ILI_ERR("spi-wrapper read error\n"); + + break; + default: + ILI_ERR("Unknown spi mode (%d)\n", mode); + ret = -EINVAL; + break; + } + + if (spi_irq) + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +int ili_core_spi_setup(int num) +{ + u32 freq[] = { + TP_SPI_CLK_1M, + TP_SPI_CLK_2M, + TP_SPI_CLK_3M, + TP_SPI_CLK_4M, + TP_SPI_CLK_5M, + TP_SPI_CLK_6M, + TP_SPI_CLK_7M, + TP_SPI_CLK_8M, + TP_SPI_CLK_9M, + TP_SPI_CLK_10M, + TP_SPI_CLK_11M, + TP_SPI_CLK_12M, + TP_SPI_CLK_13M, + TP_SPI_CLK_14M, + TP_SPI_CLK_15M + }; + + if (num > sizeof(freq)) { + ILI_ERR("Invaild clk freq, set default clk freq\n"); + num = 7; + } + + ILI_INFO("spi clock = %d\n", freq[num]); + + ilits->spi->mode = SPI_MODE_0; + ilits->spi->bits_per_word = 8; + ilits->spi->max_speed_hz = freq[num]; + ilits->spi->chip_select = 0; + + if (spi_setup(ilits->spi) < 0) { + ILI_ERR("Failed to setup spi device\n"); + return -ENODEV; + } + + ILI_INFO("name = %s, bus_num = %d,cs = %d, mode = %d, speed = %d\n", + ilits->spi->modalias, + ilits->spi->master->bus_num, + ilits->spi->chip_select, + ilits->spi->mode, + ilits->spi->max_speed_hz); + return 0; +} + +#ifdef CONFIG_DRM +static int check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + ili_active_panel = panel; + return 0; + } + } + if (node) + pr_err("%s: %s not actived\n", __func__, node->name); + return -ENODEV; +} +#endif + +static int ilitek_spi_probe(struct spi_device *spi) +{ + struct touch_bus_info *info = + container_of(to_spi_driver(spi->dev.driver), + struct touch_bus_info, bus_driver); + + ILI_INFO("ilitek spi probe\n"); + + if (!spi) { + ILI_ERR("spi device is NULL\n"); + return -ENODEV; + } + +#ifdef CONFIG_DRM +{ + struct device_node *dp = spi->dev.of_node; + if (check_dt(dp)) { + ILI_ERR("%s: %s not actived\n", __func__, dp->name); + return -ENODEV; + } +} +#endif + ilits = devm_kzalloc(&spi->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits)) { + ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits)); + return -ENOMEM; + } + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + ILI_ERR("Full duplex not supported by master\n"); + return -EIO; + } + + ilits->update_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->update_buf)) { + ILI_ERR("fw kzalloc error\n"); + return -ENOMEM; + } + + /* Used for receiving touch data only, do not mix up with others. */ + ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC); + if (ERR_ALLOC_MEM(ilits->tr_buf)) { + ILI_ERR("failed to allocate touch report buffer\n"); + return -ENOMEM; + } + + ilits->spi_tx = kzalloc(SPI_TX_BUF_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->spi_tx)) { + ILI_ERR("Failed to allocate spi tx buffer\n"); + return -ENOMEM; + } + + ilits->spi_rx = kzalloc(SPI_RX_BUF_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->spi_rx)) { + ILI_ERR("Failed to allocate spi rx buffer\n"); + return -ENOMEM; + } + + ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->gcoord)) { + ILI_ERR("Failed to allocate gresture coordinate buffer\n"); + return -ENOMEM; + } + + ilits->i2c = NULL; + ilits->spi = spi; + ilits->dev = &spi->dev; + ilits->hwif = info->hwif; + ilits->phys = "SPI"; + ilits->wrapper = ili_spi_wrapper; + ilits->detect_int_stat = ili_ic_check_int_pulse; + ilits->int_pulse = true; + ilits->mp_retry = false; + +#if SPI_DMA_TRANSFER_SPLIT + ilits->spi_write_then_read = ili_spi_write_then_read_split; +#else + ilits->spi_write_then_read = ili_spi_write_then_read_direct; +#endif + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN; + + if (TDDI_RST_BIND) + ilits->reset = TP_IC_WHOLE_RST; + else + ilits->reset = TP_HW_RST_ONLY; + + ilits->rst_edge_delay = 10; + ilits->fw_open = REQUEST_FIRMWARE; + ilits->fw_upgrade_mode = UPGRADE_IRAM; + ilits->mp_move_code = ili_move_mp_code_iram; + ilits->gesture_move_code = ili_move_gesture_code_iram; + ilits->esd_recover = ili_wq_esd_spi_check; + ilits->ges_recover = ili_touch_esd_gesture_iram; + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ilits->gesture_demo_ctrl = DISABLE; + ilits->wtd_ctrl = OFF; + ilits->report = ENABLE; + ilits->netlink = DISABLE; + ilits->dnp = DISABLE; + ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING; + ilits->info_from_hex = ENABLE; + ilits->wait_int_timeout = AP_INT_TIMEOUT; + +#if ENABLE_GESTURE + ilits->gesture = DISABLE; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP; + ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT; + ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT; + ilits->ges_sym.alphabet_m = ALPHABET_M; + ilits->ges_sym.alphabet_w = ALPHABET_W; + ilits->ges_sym.alphabet_c = ALPHABET_C; + ilits->ges_sym.alphabet_E = ALPHABET_E; + ilits->ges_sym.alphabet_V = ALPHABET_V; + ilits->ges_sym.alphabet_O = ALPHABET_O; + ilits->ges_sym.alphabet_S = ALPHABET_S; + ilits->ges_sym.alphabet_Z = ALPHABET_Z; + ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN; + ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT; + ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT; + ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_F = ALPHABET_F; + ilits->ges_sym.alphabet_AT = ALPHABET_AT; +#endif + + if (ili_core_spi_setup(SPI_CLK) < 0) + return -EINVAL; + + return info->hwif->plat_probe(); +} + +static int ilitek_spi_remove(struct spi_device *spi) +{ + ILI_INFO(); + return 0; +} + +static struct spi_device_id tp_spi_id[] = { + {TDDI_DEV_ID, 0}, + {}, +}; + +int ili_interface_dev_init(struct ilitek_hwif_info *hwif) +{ + struct touch_bus_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ILI_ERR("faied to allocate spi_driver\n"); + return -ENOMEM; + } + + if (hwif->bus_type != BUS_SPI) { + ILI_ERR("Not SPI dev\n"); + ipio_kfree((void **)&info); + return -EINVAL; + } + + hwif->info = info; + + info->bus_driver.driver.name = hwif->name; + info->bus_driver.driver.owner = hwif->owner; + info->bus_driver.driver.of_match_table = hwif->of_match_table; + info->bus_driver.driver.pm = hwif->pm; + + info->bus_driver.probe = ilitek_spi_probe; + info->bus_driver.remove = ilitek_spi_remove; + info->bus_driver.id_table = tp_spi_id; + + info->hwif = hwif; + return spi_register_driver(&info->bus_driver); +} + +void ili_interface_dev_exit(struct ilitek_ts_data *ts) +{ + struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info; + + ILI_INFO("remove spi dev\n"); + kfree(ilits->update_buf); + kfree(ilits->spi_tx); + kfree(ilits->spi_rx); + spi_unregister_driver(&info->bus_driver); + ipio_kfree((void **)&info); +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_touch.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_touch.c new file mode 100755 index 000000000000..998686d6b2f2 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_touch.c @@ -0,0 +1,1301 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +/*gesture info mode*/ +struct ili_demo_debug_info_id0 { + u32 id : 8; + u32 sys_powr_state_e : 3; + u32 sys_state_e : 3; + u32 tp_state_e : 2; + + u32 touch_palm_state : 2; + u32 app_an_statu_e : 3; + u32 app_sys_bg_err : 1; + u32 g_b_wrong_bg : 1; + u32 reserved0 : 1; + + u32 normal_mode : 1; + u32 charger_mode : 1; + u32 glove_mode : 1; + u32 stylus_mode : 1; + u32 multi_mode : 1; + u32 noise_mode : 1; + u32 palm_plus_mode : 1; + u32 floating_mode : 1; + + u32 algo_pt_status0 : 3; + u32 algo_pt_status1 : 3; + u32 algo_pt_status2 : 3; + u32 algo_pt_status3 : 3; + u32 algo_pt_status4 : 3; + u32 algo_pt_status5 : 3; + u32 algo_pt_status6 : 3; + u32 algo_pt_status7 : 3; + u32 algo_pt_status8 : 3; + u32 algo_pt_status9 : 3; + u32 reserved2 : 2; + + u32 hopping_flg : 1; + u32 hopping_index : 5; + u32 frequency_h : 2; + u32 frequency_l : 8; + u32 reserved3 : 8; + u32 reserved4 : 8; + +}; + +void ili_dump_data(void *data, int type, int len, int row_len, const char *name) +{ + int i, row = 31; + u8 *p8 = NULL; + s32 *p32 = NULL; + s16 *p16 = NULL; + + if (!debug_en) + return; + + if (row_len > 0) + row = row_len; + + if (data == NULL) { + ILI_ERR("The data going to dump is NULL\n"); + return; + } + + pr_cont("\n\n"); + pr_cont("ILITEK: Dump %s data\n", name); + pr_cont("ILITEK: "); + + if (type == 8) + p8 = (u8 *) data; + if (type == 32 || type == 10) + p32 = (s32 *) data; + if (type == 16) + p16 = (s16 *) data; + + for (i = 0; i < len; i++) { + if (type == 8) + pr_cont(" %4x ", p8[i]); + else if (type == 32) + pr_cont(" %4x ", p32[i]); + else if (type == 10) + pr_cont(" %4d ", p32[i]); + else if (type == 16) + pr_cont(" %4d ", p16[i]); + + if ((i % row) == row - 1) { + pr_cont("\n"); + pr_cont("ILITEK: "); + } + } + pr_cont("\n\n"); +} + +static void dma_clear_reg_setting(void) +{ + /* 1. interrupt t0/t1 enable flag */ + if (ili_ice_mode_bit_mask_write(INTR32_ADDR, INTR32_reg_t0_int_en | INTR32_reg_t1_int_en, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR32_reg_t0_int_en | INTR32_reg_t1_int_en, INTR32_ADDR); + + /* 2. clear tdi_err_int_flag */ + if (ili_ice_mode_bit_mask_write(INTR2_ADDR, INTR2_tdi_err_int_flag_clear, (1 << 18)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR2_tdi_err_int_flag_clear, INTR2_ADDR); + + /* 3. clear dma channel 0 src1 info */ + if (ili_ice_mode_write(DMA49_reg_dma_ch0_src1_addr, 0x00000000, 4) < 0) + ILI_ERR("Write 0x00000000 at %x failed\n", DMA49_reg_dma_ch0_src1_addr); + if (ili_ice_mode_write(DMA50_reg_dma_ch0_src1_step_inc, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", DMA50_reg_dma_ch0_src1_step_inc); + if (ili_ice_mode_bit_mask_write(DMA50_ADDR, DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, DMA50_ADDR); + + /* 4. clear dma channel 0 trigger select */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_trigger_sel, (0 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_trigger_sel, DMA48_ADDR); + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + /* 5. clear dma flash setting */ + ili_flash_clear_dma(); +} + +static void dma_trigger_reg_setting(u32 reg_dest_addr, u32 flash_start_addr, u32 copy_size) +{ + int retry = 30; + u32 stat = 0; + + /* 1. set dma channel 0 clear */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_start_clear, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_start_clear, DMA48_ADDR); + + /* 2. set dma channel 0 src1 info */ + if (ili_ice_mode_write(DMA49_reg_dma_ch0_src1_addr, 0x00041010, 4) < 0) + ILI_ERR("Write 0x00041010 at %x failed\n", DMA49_reg_dma_ch0_src1_addr); + if (ili_ice_mode_write(DMA50_reg_dma_ch0_src1_step_inc, 0x00, 1) < 0) + ILI_ERR("Write 0x00 at %x failed\n", DMA50_reg_dma_ch0_src1_step_inc); + if (ili_ice_mode_bit_mask_write(DMA50_ADDR, DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, DMA50_ADDR); + + /* 3. set dma channel 0 src2 info */ + if (ili_ice_mode_bit_mask_write(DMA52_ADDR, DMA52_reg_dma_ch0_src2_en, (0 << 31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA52_reg_dma_ch0_src2_en, DMA52_ADDR); + + /* 4. set dma channel 0 dest info */ + if (ili_ice_mode_write(DMA53_reg_dma_ch0_dest_addr, reg_dest_addr, 3) < 0) + ILI_ERR("Write %x at %x failed\n", reg_dest_addr, DMA53_reg_dma_ch0_dest_addr); + if (ili_ice_mode_write(DMA54_reg_dma_ch0_dest_step_inc, 0x01, 1) < 0) + ILI_ERR("Write 0x01 at %x failed\n", DMA54_reg_dma_ch0_dest_step_inc); + + if (ili_ice_mode_write(DMA54_ADDR, DMA54_reg_dma_ch0_dest_format | DMA54_reg_dma_ch0_dest_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA54_reg_dma_ch0_dest_format | DMA54_reg_dma_ch0_dest_en, DMA54_ADDR); + + /* 5. set dma channel 0 trafer info */ + if (ili_ice_mode_write(DMA55_reg_dma_ch0_trafer_counts, copy_size, 4) < 0) + ILI_ERR("Write %x at %x failed\n", copy_size, DMA55_reg_dma_ch0_trafer_counts); + if (ili_ice_mode_bit_mask_write(DMA55_ADDR, DMA55_reg_dma_ch0_trafer_mode, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA55_reg_dma_ch0_trafer_mode, DMA55_ADDR); + + /* 6. set dma channel 0 int info */ + if (ili_ice_mode_bit_mask_write(INTR33_ADDR, INTR33_reg_dma_ch0_int_en, (1 << 17)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR33_reg_dma_ch0_int_en, INTR33_ADDR); + + /* 7. set dma channel 0 trigger select */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_trigger_sel, (1 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_trigger_sel, DMA48_ADDR); + + /* 8. set dma flash setting */ + ili_flash_dma_write(flash_start_addr, (flash_start_addr+copy_size), copy_size); + + /* 9. clear flash and dma ch0 int flag */ + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_dma_ch1_int_flag | INTR1_reg_flash_int_flag, BIT(16) | BIT(25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_dma_ch1_int_flag | INTR1_reg_flash_int_flag, INTR1_ADDR); + if (ili_ice_mode_bit_mask_write(0x041013, BIT(0), 1) < 0) //patch + ILI_ERR("Write %lu at %x failed\n", BIT(0), 0x041013); + + /* DMA Trigger */ + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Trigger DMA failed\n"); + + /* waiting for fw reload code completed. */ + while (retry > 0) { + if (ili_ice_mode_read(INTR1_ADDR, &stat, sizeof(u32)) < 0) { + ILI_ERR("Read 0x%x error\n", INTR1_ADDR); + retry--; + continue; + } + + ILI_DBG("fw dma stat = %x\n", stat); + + if ((stat & BIT(16)) == BIT(16)) + break; + + retry--; + usleep_range(1000, 1000); + } + + if (retry <= 0) + ILI_ERR("DMA fail: Regsiter = 0x%x Flash = 0x%x, Size = %d\n", + reg_dest_addr, flash_start_addr, copy_size); + + /* CS High */ + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x1, 1) < 0) + ILI_ERR("Pull CS High failed\n"); + /* waiting for CS status done */ + mdelay(10); +} + +int ili_move_mp_code_flash(void) +{ + int ret = 0; + u32 mp_text_size = 0, mp_andes_init_size = 0; + u32 mp_flash_addr, mp_size, overlay_start_addr, overlay_end_addr; + bool dma_trigger_enable = 0; + u8 cmd[2] = {0}; + u8 data[16] = {0}; + + cmd[0] = P5_X_MP_TEST_MODE_INFO; + ret = ilits->wrapper(cmd, sizeof(u8), data, ilits->protocol->mp_info_len, ON, OFF); + ili_dump_data(data, 8, ilits->protocol->mp_info_len, 0, "MP overlay info"); + if (ret < 0) { + ILI_ERR("Failed to write info cmd\n"); + goto out; + } + + dma_trigger_enable = 0; + + mp_flash_addr = data[3] + (data[2] << 8) + (data[1] << 16); + mp_size = data[6] + (data[5] << 8) + (data[4] << 16); + overlay_start_addr = data[9] + (data[8] << 8) + (data[7] << 16); + overlay_end_addr = data[12] + (data[11] << 8) + (data[10] << 16); + + if (overlay_start_addr != 0x0 && overlay_end_addr != 0x0 + && data[0] == P5_X_MP_TEST_MODE_INFO) + dma_trigger_enable = 1; + + ILI_INFO("MP info Overlay: Enable = %d, addr = 0x%x ~ 0x%x, flash addr = 0x%x, mp size = 0x%x\n", + dma_trigger_enable, overlay_start_addr, + overlay_end_addr, mp_flash_addr, mp_size); + + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = P5_X_FW_TEST_MODE; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + if (ret < 0) + goto out; + + /* Check if ic is ready switching test mode from demo mode */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_ic_check_busy(50, 50); /* Set busy as 0x41 */ + if (ret < 0) + goto out; + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + goto out; + + if (dma_trigger_enable) { + mp_andes_init_size = overlay_start_addr; + mp_text_size = (mp_size - overlay_end_addr) + 1; + ILI_INFO("MP andes init size = %d , MP text size = %d\n", mp_andes_init_size, mp_text_size); + + dma_clear_reg_setting(); + + ILI_INFO("[Move ANDES.INIT to DRAM]\n"); + dma_trigger_reg_setting(0, mp_flash_addr, mp_andes_init_size); /* DMA ANDES.INIT */ + + dma_clear_reg_setting(); + + ILI_INFO("[Move MP.TEXT to DRAM]\n"); + dma_trigger_reg_setting(overlay_end_addr, (mp_flash_addr + overlay_start_addr), mp_text_size); + + dma_clear_reg_setting(); + } else { + /* DMA Trigger */ + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Trigger DMA failed\n"); + /* waiting for fw reload code completed. */ + mdelay(30); + + /* CS High */ + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x1, 1) < 0) + ILI_ERR("Pull CS High failed\n"); + /* waiting for CS status done */ + mdelay(10); + } + + if (ili_reset_ctrl(TP_IC_CODE_RST) < 0) + ILI_ERR("IC Code reset failed during moving mp code\n"); + + ret = ili_ice_mode_ctrl(DISABLE, OFF); + if (ret < 0) + goto out; + + /* Check if ic is already in test mode */ + ilits->actual_tp_mode = P5_X_FW_TEST_MODE; /* set busy as 0x51 */ + ret = ili_ic_check_busy(300, 50); + if (ret < 0) + ILI_ERR("Check cdc timeout failed after moved mp code\n"); + +out: + return ret; +} + +int ili_move_mp_code_iram(void) +{ + ILI_INFO("Download MP code to iram\n"); + return ili_fw_upgrade_handler(NULL); +} + +int ili_proximity_near(int mode) +{ + int ret = 0; + + ilits->prox_near = true; + + switch (mode) { + case DDI_POWER_ON: + /* + * If the power of VSP and VSN keeps alive when proximity near event + * occures, TP can just go to sleep in. + */ + ret = ili_ic_func_ctrl("sleep", SLEEP_IN); + if (ret < 0) + ILI_ERR("Write sleep in cmd failed\n"); + break; + case DDI_POWER_OFF: + ILI_INFO("DDI POWER OFF, do nothing\n"); + break; + default: + ILI_ERR("Unknown mode (%d)\n", mode); + ret = -EINVAL; + break; + } + return ret; +} + +int ili_proximity_far(int mode) +{ + int ret = 0; + u8 cmd[2] = {0}; + + if (!ilits->prox_near) { + ILI_INFO("No proximity near event, break\n"); + return 0; + } + + switch (mode) { + case WAKE_UP_GESTURE_RECOVERY: + /* + * If the power of VSP and VSN has been shut down previsouly, + * TP should go through gesture recovery to get back. + */ + ili_gesture_recovery(); + break; + case WAKE_UP_SWITCH_GESTURE_MODE: + /* + * If the power of VSP and VSN keeps alive in the event of proximity near, + * TP can be just recovered by switching gesture mode to get back. + */ + cmd[0] = 0xF6; + cmd[1] = 0x0A; + + ILI_INFO("write prepare gesture command 0xF6 0x0A\n"); + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + if (ret < 0) { + ILI_INFO("write prepare gesture command error\n"); + break; + } + + ret = ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + if (ret < 0) + ILI_ERR("Switch to gesture mode failed during proximity far\n"); + break; + default: + ILI_ERR("Unknown mode (%d)\n", mode); + ret = -EINVAL; + break; + } + + ilits->prox_near = false; + + return ret; +} + +int ili_move_gesture_code_flash(int mode) +{ + int ret = 0; + + /* + * NOTE: If functions need to be added during suspend, + * they must be called before gesture cmd reaches to FW. + */ + + ILI_INFO("Gesture mode = %d\n", mode); + ret = ili_set_tp_data_len(mode, true, NULL); + + return ret; +} + +void ili_set_gesture_symbol(void) +{ + u8 cmd[7] = {0}; + struct gesture_symbol *ptr_sym = &ilits->ges_sym; + u8 *ptr; + + ptr = (u8*) ptr_sym; + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0x01; + cmd[2] = 0x0A; + cmd[3] = 0x08; + cmd[4] = ptr[0]; + cmd[5] = ptr[1]; + cmd[6] = ptr[2]; + + ili_dump_data(cmd, 8, sizeof(cmd), 0, "Gesture symbol"); + + if (ilits->wrapper(cmd, 2, NULL, 0, ON, OFF) < 0) { + ILI_ERR("Write pre cmd failed\n"); + return; + + } + + if (ilits->wrapper(&cmd[1], (sizeof(cmd) - 1), NULL, 0, ON, OFF)) { + ILI_ERR("Write gesture symbol fail\n"); + return; + } + + ILI_DBG(" double_tap = %d\n", ilits->ges_sym.double_tap); + ILI_DBG(" alphabet_line_2_top = %d\n", ilits->ges_sym.alphabet_line_2_top); + ILI_DBG(" alphabet_line_2_bottom = %d\n", ilits->ges_sym.alphabet_line_2_bottom); + ILI_DBG(" alphabet_line_2_left = %d\n", ilits->ges_sym.alphabet_line_2_left); + ILI_DBG(" alphabet_line_2_right = %d\n", ilits->ges_sym.alphabet_line_2_right); + ILI_DBG(" alphabet_w = %d\n", ilits->ges_sym.alphabet_w); + ILI_DBG(" alphabet_c = %d\n", ilits->ges_sym.alphabet_c); + ILI_DBG(" alphabet_E = %d\n", ilits->ges_sym.alphabet_E); + ILI_DBG(" alphabet_V = %d\n", ilits->ges_sym.alphabet_V); + ILI_DBG(" alphabet_O = %d\n", ilits->ges_sym.alphabet_O); + ILI_DBG(" alphabet_S = %d\n", ilits->ges_sym.alphabet_S); + ILI_DBG(" alphabet_Z = %d\n", ilits->ges_sym.alphabet_Z); + ILI_DBG(" alphabet_V_down = %d\n", ilits->ges_sym.alphabet_V_down); + ILI_DBG(" alphabet_V_left = %d\n", ilits->ges_sym.alphabet_V_left); + ILI_DBG(" alphabet_V_right = %d\n", ilits->ges_sym.alphabet_V_right); + ILI_DBG(" alphabet_two_line_2_bottom= %d\n", ilits->ges_sym.alphabet_two_line_2_bottom); + ILI_DBG(" alphabet_F= %d\n", ilits->ges_sym.alphabet_F); + ILI_DBG(" alphabet_AT= %d\n", ilits->ges_sym.alphabet_AT); + +} + +int ili_move_gesture_code_iram(int mode) +{ + int i, ret = 0, timeout = 10; + u8 cmd[2] = {0}; + u8 cmd_write[3] = {0x01,0x0A,0x05}; + + /* + * NOTE: If functions need to be added during suspend, + * they must be called before gesture cmd reaches to FW. + */ + + ILI_INFO("Gesture code loaded by %s\n", ilits->gesture_load_code ? "driver" : "firmware" ); + + if (!ilits->gesture_load_code) { + ret = ili_set_tp_data_len(mode, true, NULL); + goto out; + } + + /*pre-command for ili_ic_func_ctrl("lpwg", 0x3)*/ + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0x1; + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write 0xF6,0x1 failed\n"); + goto out; + } + + ret = ili_ic_func_ctrl("lpwg", 0x3); + if (ret < 0) { + ILI_ERR("write gesture flag failed\n"); + goto out; + } + + ret = ili_set_tp_data_len(mode, true, NULL); + if (ret < 0) { + ILI_ERR("Failed to set tp data length\n"); + goto out; + } + + ili_irq_enable(); + /* Prepare Check Ready */ + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0xA; + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write 0xF6,0xA failed\n"); + goto out; + } + + for (i = 0; i < timeout; i++) { + /* Check ready for load code */ + ret = ilits->wrapper(cmd_write, sizeof(cmd_write), cmd, sizeof(u8), ON, OFF); + ILI_DBG("gesture ready byte = 0x%x\n", cmd[0]); + + if (cmd[0] == 0x91) { + ILI_INFO("Ready to load gesture code\n"); + break; + } + } + ili_irq_disable(); + + if (i >= timeout) { + ILI_ERR("Gesture is not ready (0x%x), try to run its recovery\n", cmd[0]); + return ili_gesture_recovery(); + } + + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("FW upgrade failed during moving code\n"); + goto out; + } + + /* Resume gesture loader */ + ret = ili_ic_func_ctrl("lpwg", 0x6); + if (ret < 0) { + ILI_ERR("write resume loader error"); + goto out; + } + +out: + return ret; +} + +u8 ili_calc_packet_checksum(u8 *packet, int len) +{ + int i; + s32 sum = 0; + + for (i = 0; i < len; i++) + sum += packet[i]; + + return (u8) ((-sum) & 0xFF); +} + +int ili_touch_esd_gesture_flash(void) +{ + int ret = 0, retry = 100; + u32 answer = 0; + int ges_pwd_addr = I2C_ESD_GESTURE_CORE146_PWD_ADDR; + int ges_pwd = ESD_GESTURE_CORE146_PWD; + int ges_run = I2C_ESD_GESTURE_CORE146_RUN; + int pwd_len = 2; + + if (ilits->chip->core_ver < CORE_VER_1460) { + ges_pwd_addr = I2C_ESD_GESTURE_PWD_ADDR; + ges_pwd = ESD_GESTURE_PWD; + ges_run = I2C_ESD_GESTURE_RUN; + pwd_len = 4; + } + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + return ret; + } + + ILI_INFO("ESD Gesture PWD Addr = 0x%X, PWD = 0x%X, GES_RUN = 0%X, core_ver = 0x%X\n", + ges_pwd_addr, ges_pwd, ges_run, ilits->chip->core_ver); + + /* write a special password to inform FW go back into gesture mode */ + ret = ili_ice_mode_write(ges_pwd_addr, ges_pwd, pwd_len); + if (ret < 0) { + ILI_ERR("write password failed\n"); + goto out; + } + + /* HW reset gives effect to FW receives password successed */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_reset_ctrl(ilits->reset); + if (ret < 0) { + ILI_ERR("TP Reset failed during gesture recovery\n"); + goto out; + } + + ret = ili_ice_mode_ctrl(ENABLE, ON); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto out; + } + + /* polling another specific register to see if gesutre is enabled properly */ + do { + ret = ili_ice_mode_read(ges_pwd_addr, &answer, pwd_len); + if (ret < 0) { + ILI_ERR("Read gesture answer error\n"); + goto out; + } + + if (answer != ges_run) + ILI_INFO("ret = 0x%X, answer = 0x%X\n", answer, ges_run); + + mdelay(2); + } while (answer != ges_run && --retry > 0); + + if (retry <= 0) { + ILI_ERR("Enter gesture failed\n"); + ret = -1; + goto out; + } + + ILI_INFO("0x%X Enter gesture successfully\n", answer); + +out: + if (ili_ice_mode_ctrl(DISABLE, ON) < 0) { + ILI_ERR("Disable ice mode failed during gesture recovery\n"); + } + + if (ret >= 0) { + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, NULL); + } + + return ret; +} + +int ili_touch_esd_gesture_iram(void) +{ + int ret = 0, retry = 100; + u32 answer = 0; + int ges_pwd_addr = SPI_ESD_GESTURE_CORE146_PWD_ADDR; + int ges_pwd = ESD_GESTURE_CORE146_PWD; + int ges_run = SPI_ESD_GESTURE_CORE146_RUN; + int pwd_len = 2; + + if (ilits->chip->core_ver < CORE_VER_1460) { + if (ilits->chip->core_ver >= CORE_VER_1420) + ges_pwd_addr = I2C_ESD_GESTURE_PWD_ADDR; + else + ges_pwd_addr = SPI_ESD_GESTURE_PWD_ADDR; + ges_pwd = ESD_GESTURE_PWD; + ges_run = SPI_ESD_GESTURE_RUN; + pwd_len = 4; + } + + ILI_INFO("ESD Gesture PWD Addr = 0x%X, PWD = 0x%X, GES_RUN = 0%X, core_ver = 0x%X\n", + ges_pwd_addr, ges_pwd, ges_run, ilits->chip->core_ver); + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto fail; + } + + /* write a special password to inform FW go back into gesture mode */ + ret = ili_ice_mode_write(ges_pwd_addr, ges_pwd, pwd_len); + if (ret < 0) { + ILI_ERR("write password failed\n"); + goto fail; + } + + /* Host download gives effect to FW receives password successed */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("FW upgrade failed during gesture recovery\n"); + goto fail; + } + + /* Wait for fw running code finished. */ + if (ilits->info_from_hex || (ilits->chip->core_ver >= CORE_VER_1410)) + msleep(50); + + + ret = ili_ice_mode_ctrl(ENABLE, ON); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto fail; + } + + /* polling another specific register to see if gesutre is enabled properly */ + do { + ret = ili_ice_mode_read(ges_pwd_addr, &answer, pwd_len); + if (ret < 0) { + ILI_ERR("Read gesture answer error\n"); + goto fail; + } + + if (answer != ges_run) + ILI_INFO("ret = 0x%X, answer = 0x%X\n", answer, ges_run); + + mdelay(2); + } while (answer != ges_run && --retry > 0); + + if (retry <= 0) { + ILI_ERR("Enter gesture failed\n"); + ret = -1; + goto fail; + } + + ILI_INFO("Enter gesture successfully\n"); + + ret = ili_ice_mode_ctrl(DISABLE, ON); + if (ret < 0) { + ILI_ERR("Disable ice mode failed during gesture recovery\n"); + goto fail; + } + + ILI_INFO("Gesture code loaded by %s\n", ilits->gesture_load_code ? "driver" : "firmware"); + + if (!ilits->gesture_load_code) { + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, NULL); + goto out; + } + + /* Load gesture code */ + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, NULL); + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("Failed to load code during gesture recovery\n"); + goto fail; + } + + /* Resume gesture loader */ + ret = ili_ic_func_ctrl("lpwg", 0x6); + if (ret < 0) { + ILI_ERR("write resume loader error"); + goto fail; + } + +out: + return ret; + +fail: + ili_ice_mode_ctrl(DISABLE, ON); + return ret; +} + +void ili_demo_debug_info_id0(u8 *buf, size_t len) +{ + struct ili_demo_debug_info_id0 id0; + + ipio_memcpy(&id0, buf, sizeof(id0), len); + ILI_INFO("id0 len = %d,strucy len = %d", (int)len, (int)sizeof(id0)); + + ILI_INFO("id = %d\n", id0.id); + ILI_INFO("app_sys_powr_state_e = %d\n", id0.sys_powr_state_e); + ILI_INFO("app_sys_state_e = %d\n", id0.sys_state_e); + ILI_INFO("tp_state_e = %d\n", id0.tp_state_e); + + ILI_INFO("touch_palm_state_e = %d\n", id0.touch_palm_state); + ILI_INFO("app_an_statu_e = %d\n", id0.app_an_statu_e); + ILI_INFO("app_sys_bg_err = %d\n", id0.app_sys_bg_err); + ILI_INFO("g_b_wrong_bg = %d\n", id0.g_b_wrong_bg); + ILI_INFO("reserved0 = %d\n", id0.reserved0); + + ILI_INFO("normal_mode = %d\n", id0.normal_mode); + ILI_INFO("charger_mode = %d\n", id0.charger_mode); + ILI_INFO("glove_mode = %d\n", id0.glove_mode); + ILI_INFO("stylus_mode = %d\n", id0.stylus_mode); + ILI_INFO("multi_mode = %d\n", id0.multi_mode); + ILI_INFO("noise_mode = %d\n", id0.noise_mode); + ILI_INFO("palm_plus_mode = %d\n", id0.palm_plus_mode); + ILI_INFO("floating_mode = %d\n", id0.floating_mode); + + ILI_INFO("algo_pt_status0 = %d\n", id0.algo_pt_status0); + ILI_INFO("algo_pt_status1 = %d\n", id0.algo_pt_status1); + ILI_INFO("algo_pt_status2 = %d\n", id0.algo_pt_status2); + ILI_INFO("algo_pt_status3 = %d\n", id0.algo_pt_status3); + ILI_INFO("algo_pt_status4 = %d\n", id0.algo_pt_status4); + ILI_INFO("algo_pt_status5 = %d\n", id0.algo_pt_status5); + ILI_INFO("algo_pt_status6 = %d\n", id0.algo_pt_status6); + ILI_INFO("algo_pt_status7 = %d\n", id0.algo_pt_status7); + ILI_INFO("algo_pt_status8 = %d\n", id0.algo_pt_status8); + ILI_INFO("algo_pt_status9 = %d\n", id0.algo_pt_status9); + ILI_INFO("reserved2 = %d\n", id0.reserved2); + + ILI_INFO("hopping_flg = %d\n", id0.hopping_flg); + ILI_INFO("hopping_index = %d\n", id0.hopping_index); + ILI_INFO("frequency = %d\n", (id0.frequency_h << 8 | id0.frequency_l)); + ILI_INFO("reserved3 = %d\n", id0.reserved3); + ILI_INFO("reserved4 = %d\n", id0.reserved4); + +} + +void ili_demo_debug_info_mode(u8 *buf, size_t len) +{ + u8 *info_ptr; + u8 info_id, info_len; + + ili_report_ap_mode(buf, P5_X_DEMO_MODE_PACKET_LEN); + info_ptr = buf + P5_X_DEMO_MODE_PACKET_LEN; + info_len = info_ptr[0]; + info_id = info_ptr[1]; + + ILI_INFO("info len = %d ,id = %d\n", info_len, info_id); + + ilits->demo_debug_info[info_id](&info_ptr[1] , info_len); +} + +static void ilitek_tddi_touch_send_debug_data(u8 *buf, int len) +{ + int index; + + mutex_lock(&ilits->debug_mutex); + + if (!ilits->netlink && !ilits->dnp) + goto out; + + /* Send data to netlink */ + if (ilits->netlink) { + ili_netlink_reply_msg(buf, len); + goto out; + } + + /* Sending data to apk via the node of debug_message node */ + if (ilits->dnp) { + index = ilits->dbf; + if (!ilits->dbl[ilits->dbf].mark) { + ilits->dbf = ((ilits->dbf + 1) % TR_BUF_LIST_SIZE); + } else { + if (ilits->dbf == 0) + index = TR_BUF_LIST_SIZE - 1; + else + index = ilits->dbf - 1; + } + if (ilits->dbl[index].data == NULL) { + ILI_ERR("BUFFER %d error\n", index); + goto out; + } + ipio_memcpy(ilits->dbl[index].data, buf, len, 2048); + ilits->dbl[index].mark = true; + wake_up(&(ilits->inq)); + goto out; + } + +out: + mutex_unlock(&ilits->debug_mutex); +} + +void ili_touch_press(u16 x, u16 y, u16 pressure, u16 id) +{ + ILI_DBG("Touch Press: id = %d, x = %d, y = %d, p = %d\n", id, x, y, pressure); + + if (MT_B_TYPE) { + input_mt_slot(ilits->input, id); + input_mt_report_slot_state(ilits->input, MT_TOOL_FINGER, true); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + } else { + input_report_key(ilits->input, BTN_TOUCH, 1); + input_report_abs(ilits->input, ABS_MT_TRACKING_ID, id); + input_report_abs(ilits->input, ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_WIDTH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + + input_mt_sync(ilits->input); + } +} + +void ili_touch_release(u16 x, u16 y, u16 id) +{ + ILI_DBG("Touch Release: id = %d, x = %d, y = %d\n", id, x, y); + + if (MT_B_TYPE) { + input_mt_slot(ilits->input, id); + input_mt_report_slot_state(ilits->input, MT_TOOL_FINGER, false); + } else { + input_report_key(ilits->input, BTN_TOUCH, 0); + input_mt_sync(ilits->input); + } +} + +void ili_touch_release_all_point(void) +{ + int i; + + if (MT_B_TYPE) { + for (i = 0 ; i < MAX_TOUCH_NUM; i++) + ili_touch_release(0, 0, i); + + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); +} + +static struct ilitek_touch_info touch_info[MAX_TOUCH_NUM]; + +void ili_report_ap_mode(u8 *buf, int len) +{ + int i = 0; + u32 xop = 0, yop = 0; + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if ((buf[(4 * i) + 1] == 0xFF) && (buf[(4 * i) + 2] == 0xFF) + && (buf[(4 * i) + 3] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + + xop = (((buf[(4 * i) + 1] & 0xF0) << 4) | (buf[(4 * i) + 2])); + yop = (((buf[(4 * i) + 1] & 0x0F) << 8) | (buf[(4 * i) + 3])); + + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } + + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) + touch_info[ilits->finger].pressure = buf[(4 * i) + 4]; + else + touch_info[ilits->finger].pressure = 1; + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->finger) { + if (MT_B_TYPE) { + for (i = 0; i < ilits->finger; i++) { + input_report_key(ilits->input, BTN_TOUCH, 1); + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; + } + } + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_debug_mode_report_point(u8 *buf, int len) +{ + int i = 0; + u32 xop = 0, yop = 0; + static u8 p[MAX_TOUCH_NUM]; + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if ((buf[(3 * i)] == 0xFF) && (buf[(3 * i) + 1] == 0xFF) + && (buf[(3 * i) + 2] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + + xop = (((buf[(3 * i)] & 0xF0) << 4) | (buf[(3 * i) + 1])); + yop = (((buf[(3 * i)] & 0x0F) << 8) | (buf[(3 * i) + 2])); + + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } + + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) { + /* + * Since there's no pressure data in debug mode, we make fake values + * for android system if pressure needs to be reported. + */ + if (p[ilits->finger] == 1) + touch_info[ilits->finger].pressure = p[ilits->finger] = 2; + else + touch_info[ilits->finger].pressure = p[ilits->finger] = 1; + } else { + touch_info[ilits->finger].pressure = 1; + } + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->finger) { + if (MT_B_TYPE) { + for (i = 0; i < ilits->finger; i++) { + input_report_key(ilits->input, BTN_TOUCH, 1); + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; + } + } +} + +void ili_report_debug_mode(u8 *buf, int len) +{ + ili_debug_mode_report_point(buf + 5, len); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_debug_lite_mode(u8 *buf, int len) +{ + ili_debug_mode_report_point(buf + 4, len); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_gesture_mode(u8 *buf, int len) +{ + int i, lu_x = 0, lu_y = 0, rd_x = 0, rd_y = 0, score = 0; + u8 ges[P5_X_GESTURE_INFO_LENGTH] = {0}; + struct gesture_coordinate *gc = ilits->gcoord; + struct input_dev *input = ilits->input; + bool transfer = ilits->trans_xy; + + for (i = 0; i < len; i++) + ges[i] = buf[i]; + + memset(gc, 0x0, sizeof(struct gesture_coordinate)); + + gc->code = ges[1]; + score = ges[36]; + ILI_INFO("gesture code = 0x%x, score = %d\n", gc->code, score); + + /* Parsing gesture coordinate */ + gc->pos_start.x = ((ges[4] & 0xF0) << 4) | ges[5]; + gc->pos_start.y = ((ges[4] & 0x0F) << 8) | ges[6]; + gc->pos_end.x = ((ges[7] & 0xF0) << 4) | ges[8]; + gc->pos_end.y = ((ges[7] & 0x0F) << 8) | ges[9]; + gc->pos_1st.x = ((ges[16] & 0xF0) << 4) | ges[17]; + gc->pos_1st.y = ((ges[16] & 0x0F) << 8) | ges[18]; + gc->pos_2nd.x = ((ges[19] & 0xF0) << 4) | ges[20]; + gc->pos_2nd.y = ((ges[19] & 0x0F) << 8) | ges[21]; + gc->pos_3rd.x = ((ges[22] & 0xF0) << 4) | ges[23]; + gc->pos_3rd.y = ((ges[22] & 0x0F) << 8) | ges[24]; + gc->pos_4th.x = ((ges[25] & 0xF0) << 4) | ges[26]; + gc->pos_4th.y = ((ges[25] & 0x0F) << 8) | ges[27]; + + switch (gc->code) { + case GESTURE_DOUBLECLICK: + ILI_INFO("Double Click key event\n"); + input_report_key(input, KEY_GESTURE_POWER, 1); + input_sync(input); + input_report_key(input, KEY_GESTURE_POWER, 0); + input_sync(input); + gc->type = GESTURE_DOUBLECLICK; + gc->clockwise = 1; + gc->pos_end.x = gc->pos_start.x; + gc->pos_end.y = gc->pos_start.y; + break; + case GESTURE_LEFT: + gc->type = GESTURE_LEFT; + gc->clockwise = 1; + break; + case GESTURE_RIGHT: + gc->type = GESTURE_RIGHT; + gc->clockwise = 1; + break; + case GESTURE_UP: + gc->type = GESTURE_UP; + gc->clockwise = 1; + break; + case GESTURE_DOWN: + gc->type = GESTURE_DOWN; + gc->clockwise = 1; + break; + case GESTURE_O: + gc->type = GESTURE_O; + gc->clockwise = (ges[34] > 1) ? 0 : ges[34]; + + lu_x = (((ges[28] & 0xF0) << 4) | (ges[29])); + lu_y = (((ges[28] & 0x0F) << 8) | (ges[30])); + rd_x = (((ges[31] & 0xF0) << 4) | (ges[32])); + rd_y = (((ges[31] & 0x0F) << 8) | (ges[33])); + + gc->pos_1st.x = ((rd_x + lu_x) / 2); + gc->pos_1st.y = lu_y; + gc->pos_2nd.x = lu_x; + gc->pos_2nd.y = ((rd_y + lu_y) / 2); + gc->pos_3rd.x = ((rd_x + lu_x) / 2); + gc->pos_3rd.y = rd_y; + gc->pos_4th.x = rd_x; + gc->pos_4th.y = ((rd_y + lu_y) / 2); + break; + case GESTURE_W: + gc->type = GESTURE_W; + gc->clockwise = 1; + break; + case GESTURE_M: + gc->type = GESTURE_M; + gc->clockwise = 1; + break; + case GESTURE_V: + gc->type = GESTURE_V; + gc->clockwise = 1; + break; + case GESTURE_C: + gc->type = GESTURE_C; + gc->clockwise = 1; + break; + case GESTURE_E: + gc->type = GESTURE_E; + gc->clockwise = 1; + break; + case GESTURE_S: + gc->type = GESTURE_S; + gc->clockwise = 1; + break; + case GESTURE_Z: + gc->type = GESTURE_Z; + gc->clockwise = 1; + break; + case GESTURE_TWOLINE_DOWN: + gc->type = GESTURE_TWOLINE_DOWN; + gc->clockwise = 1; + gc->pos_1st.x = (((ges[10] & 0xF0) << 4) | (ges[11])); + gc->pos_1st.y = (((ges[10] & 0x0F) << 8) | (ges[12])); + gc->pos_2nd.x = (((ges[13] & 0xF0) << 4) | (ges[14])); + gc->pos_2nd.y = (((ges[13] & 0x0F) << 8) | (ges[15])); + break; + default: + ILI_ERR("Unknown gesture code\n"); + break; + } + + if (!transfer) { + gc->pos_start.x = gc->pos_start.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_start.y = gc->pos_start.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_end.x = gc->pos_end.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_end.y = gc->pos_end.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_1st.x = gc->pos_1st.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_1st.y = gc->pos_1st.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_2nd.x = gc->pos_2nd.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_2nd.y = gc->pos_2nd.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_3rd.x = gc->pos_3rd.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_3rd.y = gc->pos_3rd.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_4th.x = gc->pos_4th.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_4th.y = gc->pos_4th.y * ilits->panel_hei / TPD_HEIGHT; + } + + ILI_INFO("Transfer = %d, Type = %d, clockwise = %d\n", transfer, gc->type, gc->clockwise); + ILI_INFO("Gesture Points: (%d, %d)(%d, %d)(%d, %d)(%d, %d)(%d, %d)(%d, %d)\n", + gc->pos_start.x, gc->pos_start.y, + gc->pos_end.x, gc->pos_end.y, + gc->pos_1st.x, gc->pos_1st.y, + gc->pos_2nd.x, gc->pos_2nd.y, + gc->pos_3rd.x, gc->pos_3rd.y, + gc->pos_4th.x, gc->pos_4th.y); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_i2cuart_mode(u8 *buf, int len) +{ + int type = buf[3] & 0x0F; + int need_read_len = 0, one_data_bytes = 0; + int actual_len = len - 5; + int uart_len; + u8 *uart_buf = NULL, *total_buf = NULL; + + ILI_DBG("data[3] = %x, type = %x, actual_len = %d\n", + buf[3], type, actual_len); + + need_read_len = buf[1] * buf[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + + need_read_len = need_read_len * one_data_bytes + 1; + ILI_DBG("need_read_len = %d one_data_bytes = %d\n", need_read_len, one_data_bytes); + + if (need_read_len <= actual_len) { + ilitek_tddi_touch_send_debug_data(buf, len); + goto out; + } + + uart_len = need_read_len - actual_len; + ILI_DBG("uart len = %d\n", uart_len); + + uart_buf = kcalloc(uart_len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(uart_buf)) { + ILI_ERR("Failed to allocate uart_buf memory %ld\n", PTR_ERR(uart_buf)); + goto out; + } + + if (ilits->wrapper(NULL, 0, uart_buf, uart_len, OFF, OFF) < 0) { + ILI_ERR("i2cuart read data failed\n"); + goto out; + } + + total_buf = kcalloc(len + uart_len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(total_buf)) { + ILI_ERR("Failed to allocate total_buf memory %ld\n", PTR_ERR(total_buf)); + goto out; + } + + memcpy(total_buf, buf, len); + memcpy(total_buf + len, uart_buf, uart_len); + ilitek_tddi_touch_send_debug_data(total_buf, len + uart_len); + +out: + ipio_kfree((void **)&uart_buf); + ipio_kfree((void **)&total_buf); + return; +}