diff --git a/drivers/input/touchscreen/focaltech_mmi/Android.mk b/drivers/input/touchscreen/focaltech_mmi/Android.mk new file mode 100644 index 000000000000..f3299646f0cb --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := focaltech_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/focaltech_mmi/Kbuild b/drivers/input/touchscreen/focaltech_mmi/Kbuild new file mode 100644 index 000000000000..9f1eb6df5053 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/Kbuild @@ -0,0 +1,12 @@ +# 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/focaltech_mmi + +obj-m := focaltech_mmi.o +focaltech_mmi-objs += focaltech_ts.o +focaltech_mmi-objs += focaltech_flash.o +focaltech_mmi-objs += focaltech_upgrade_ft8716.o +focaltech_mmi-objs += focaltech_upgrade_idc.o +focaltech_mmi-objs += focaltech_upgrade_ft5x46.o +focaltech_mmi-objs += focaltech_upgrade_ft8006u.o diff --git a/drivers/input/touchscreen/focaltech_mmi/Makefile b/drivers/input/touchscreen/focaltech_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_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/focaltech_mmi/focaltech_flash.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.c new file mode 100644 index 000000000000..79ecbf5e3ce9 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.c @@ -0,0 +1,2003 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include "focaltech_flash.h" +#include "focaltech_upgrade_common.h" + +static struct fts_upgrade_fun *fts_updatefun_curr; +static struct ft_chip_t *fts_chip_type_curr; +static const struct ft_ts_platform_data *fts_pdata_curr; +static const char *fts_fw_name_curr; + +int fts_i2c_read(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +int fts_i2c_write_reg(struct i2c_client *client, u8 addr, const u8 val) +{ + u8 buf[2] = {0}; + + buf[0] = addr; + buf[1] = val; + + return fts_i2c_write(client, buf, sizeof(buf)); +} + +int fts_i2c_read_reg(struct i2c_client *client, u8 addr, u8 *val) +{ + return fts_i2c_read(client, &addr, 1, val, 1); +} + +/************************************************************************ +* Name: fts_ctpm_i2c_hid2std +* Brief: HID to I2C +* Input: i2c info +* Output: no +* Return: fail =0 +***********************************************************************/ +int fts_ctpm_i2c_hid2std(struct i2c_client *client) +{ + u8 buf[5] = {0}; + int bRet = 0; + + if (FTS_CHIP_IDC(fts_chip_type_curr->chip_type)) + return 0; + + buf[0] = 0xeb; + buf[1] = 0xaa; + buf[2] = 0x09; + bRet = fts_i2c_write(client, buf, 3); + msleep(20); + buf[0] = buf[1] = buf[2] = 0; + fts_i2c_read(client, buf, 0, buf, 3); + + if ((buf[0] == 0xeb) && (buf[1] == 0xaa) && (buf[2] == 0x08)) { + FTS_DEBUG("hidi2c change to stdi2c successful\n"); + bRet = 1; + } else { + FTS_ERROR("hidi2c change to stdi2c error\n"); + bRet = 0; + } + + return bRet; +} + +/************************************************************************ +* Name: fts_ctpm_rom_or_pram_reset +* Brief: RST CMD(07), reset to romboot(maybe->bootloader) +* Input: +* Output: +* Return: +***********************************************************************/ +void fts_ctpm_rom_or_pram_reset(struct i2c_client *client) +{ + u8 rst_cmd = FTS_REG_RESET_FW; + + FTS_INFO("[UPGRADE] Reset to romboot/bootloader\n"); + fts_i2c_write(client, &rst_cmd, 1); + /* The delay can't be changed */ + msleep(300); +} + +/************************************************************************ +* Name: fts_ctpm_get_pram_or_rom_id +* Brief: 0 +* Input: 0 +* Output: 0 +* Return: 0 +***********************************************************************/ +enum FW_STATUS fts_ctpm_get_pram_or_rom_id(struct i2c_client *client) +{ + u8 buf[4]; + u8 reg_val[2] = {0}; + enum FW_STATUS inRomBoot = FTS_RUN_IN_ERROR; + + fts_ctpm_i2c_hid2std(client); + + /*Enter upgrade mode*/ + /*send 0x55 in time windows*/ + buf[0] = FTS_UPGRADE_55; + buf[1] = FTS_UPGRADE_AA; + fts_i2c_write(client, buf, 2); + + msleep(20); + + buf[0] = 0x90; + buf[1] = buf[2] = buf[3] = 0x00; + fts_i2c_read(client, buf, 4, reg_val, 2); + + FTS_DEBUG("[UPGRADE] ROM/PRAM/Bootloader id:0x%02x%02x\n", + reg_val[0], reg_val[1]); + if ((reg_val[0] == 0x00) || (reg_val[0] == 0xFF)) + inRomBoot = FTS_RUN_IN_ERROR; + else if ((reg_val[0] == fts_chip_type_curr->pramboot_idh) && + (reg_val[1] == fts_chip_type_curr->pramboot_idl)) + inRomBoot = FTS_RUN_IN_PRAM; + else if ((reg_val[0] == fts_chip_type_curr->rom_idh) && + (reg_val[1] == fts_chip_type_curr->rom_idl)) + inRomBoot = FTS_RUN_IN_ROM; + else if ((reg_val[0] == fts_chip_type_curr->bootloader_idh) && + (reg_val[1] == fts_chip_type_curr->bootloader_idl)) + inRomBoot = FTS_RUN_IN_BOOTLOADER; + + return inRomBoot; +} + +/************************************************************************ +* Name: fts_ctpm_get_app_ver +* Brief: get app file version +* Input: +* Output: +* Return: fw version +***********************************************************************/ +int fts_ctpm_get_app_ver(void) +{ + int i_ret = 0; + + if (fts_updatefun_curr->get_app_bin_file_ver) + i_ret = fts_updatefun_curr->get_app_bin_file_ver + (fts_fw_name_curr); + + if (i_ret < 0) + i_ret = 0; + + return i_ret; +} + +/************************************************************************ +* Name: fts_ctpm_fw_upgrade +* Brief: fw upgrade entry funciotn +* Input: +* Output: +* Return: 0 - upgrade successfully +* <0 - upgrade failed +***********************************************************************/ +int fts_ctpm_fw_upgrade(struct i2c_client *client) +{ + int i_ret = 0; + + if (fts_updatefun_curr->upgrade_with_app_bin_file) + i_ret = fts_updatefun_curr->upgrade_with_app_bin_file(client, + fts_fw_name_curr); + + return i_ret; +} + +/************************************************************************ +* Name: fts_ctpm_check_fw_status +* Brief: Check App is valid or not +* Input: +* Output: +* Return: -EIO - I2C communication error +* FTS_RUN_IN_APP - APP valid +* 0 - APP invalid +***********************************************************************/ +static int fts_ctpm_check_fw_status(struct i2c_client *client) +{ + u8 chip_id1 = 0; + u8 chip_id2 = 0; + int fw_status = FTS_RUN_IN_ERROR; + int i = 0; + int ret = 0; + int i2c_noack_retry = 0; + + for (i = 0; i < 5; i++) { + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &chip_id1); + if (ret < 0) { + i2c_noack_retry++; + continue; + } + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID2, &chip_id2); + if (ret < 0) { + i2c_noack_retry++; + continue; + } + if (chip_id1 != fts_chip_type_curr->chip_idh) + continue; + if (FTS_CHIP_IDC(fts_chip_type_curr->chip_type) && + (chip_id2 != fts_chip_type_curr->chip_idl)) + continue; + fw_status = FTS_RUN_IN_APP; + break; + } + + FTS_DEBUG("[UPGRADE] chip_id=%02x%02x, chip_types.chip_idh=%02x%02x\n", + chip_id1, chip_id2, + fts_chip_type_curr->chip_idh, fts_chip_type_curr->chip_idl); + + /* I2C No ACK 5 times, then return -EIO */ + if (i2c_noack_retry >= 5) + return -EIO; + + /* not get correct ID, need check pram/rom/bootloader */ + if (i >= 5) + fw_status = fts_ctpm_get_pram_or_rom_id(client); + + return fw_status; +} + +/************************************************************************ +* Name: fts_ctpm_check_fw_ver +* Brief: Check vendor id is valid or not +* Input: +* Output: +* Return: 1 - vendor id valid +* 0 - vendor id invalid +***********************************************************************/ +static int fts_ctpm_check_fw_ver(struct i2c_client *client) +{ + /* FW version check is done in moto FW upgrade script */ + return 1; +} + +/************************************************************************ +* Name: fts_ctpm_check_need_upgrade +* Brief: +* Input: +* Output: +* Return: 1 - Need upgrade +* 0 - No upgrade +***********************************************************************/ +static int fts_ctpm_check_need_upgrade(struct i2c_client *client) +{ + int fw_status = 0; + int bUpgradeFlag = 0; + + FTS_FUNC_ENTER(); + + /* 1. veriry FW APP is valid or not */ + fw_status = fts_ctpm_check_fw_status(client); + FTS_DEBUG("[UPGRADE] fw_status = %d\n", fw_status); + if (fw_status < 0) { + /* I2C no ACK, return immediately */ + FTS_ERROR("[UPGRADE] I2C NO ACK,exit upgrade\n"); + return -EIO; + } else if (fw_status == FTS_RUN_IN_ERROR) + FTS_ERROR("[UPGRADE] IC Type Fail\n"); + else if (fw_status == FTS_RUN_IN_APP) { + if (fts_ctpm_check_fw_ver(client) == 1) { + FTS_INFO("[UPGRADE] need upgrade fw\n"); + bUpgradeFlag = 1; + } else { + FTS_INFO("[UPGRADE] Don't need upgrade fw\n"); + bUpgradeFlag = 0; + } + } else { + /* if app is invalid, reset to run ROM */ + FTS_INFO("[UPGRADE] FW APP invalid\n"); + fts_ctpm_rom_or_pram_reset(client); + + bUpgradeFlag = 1; + } + + FTS_FUNC_EXIT(); + + return bUpgradeFlag; +} + +/************************************************************************ +* Name: fts_ctpm_auto_upgrade +* Brief: auto upgrade +* Input: +* Output: +* Return: 0 - no upgrade +***********************************************************************/ +int fts_ctpm_auto_upgrade(struct i2c_client *client, + const char *fw_name, + const struct ft_ts_platform_data *pdata) +{ + int i_ret = 0; + int bUpgradeFlag = 0; + u8 uc_upgrade_times = 0; + + FTS_DEBUG("[UPGRADE] set update function and type by ID\n"); + + if (pdata->family_id == FT8716_ID) { + ft8716_set_upgrade_function(&fts_updatefun_curr); + ft8716_set_chip_id(&fts_chip_type_curr); + } + + if (pdata->family_id == FT5X46_ID) { + ft5x46_set_upgrade_function(&fts_updatefun_curr); + ft5x46_set_chip_id(&fts_chip_type_curr); + } + + if (pdata->family_id == FT8006U_ID) { + return fts_fwupg_do_upgrade(fw_name); + } + + /* no IC upgrade function enabled, return error */ + if ((fts_updatefun_curr == NULL) || (fts_chip_type_curr == NULL)) { + FTS_ERROR("[UPGRADE] ID(0x%02x) not support\n", + pdata->family_id); + return -EINVAL; + } + fts_pdata_curr = pdata; + fts_fw_name_curr = fw_name; + + FTS_DEBUG("[UPGRADE] check upgrade need or not\n"); + bUpgradeFlag = fts_ctpm_check_need_upgrade(client); + FTS_DEBUG("[UPGRADE] bUpgradeFlag = 0x%x\n", bUpgradeFlag); + if (bUpgradeFlag <= 0) { + FTS_INFO("[UPGRADE] No Upgrade, exit(%d)\n", bUpgradeFlag); + return 0; + } + + /* FW Upgrade */ + do { + FTS_DEBUG("[UPGRADE] star upgrade(%d)\n", uc_upgrade_times); + + i_ret = fts_ctpm_fw_upgrade(client); + if (i_ret != 0) { + /* upgrade fail, reset to run ROM BOOT.. + * if app in flash is ok, TP will work success + */ + FTS_ERROR("[UPGRADE] upgrade fail, reset now\n"); + fts_ctpm_rom_or_pram_reset(client); + continue; + } +; FTS_INFO("[UPGRADE] Upgrade success, reset now\n"); + fts_ctpm_rom_or_pram_reset(client); + break; + } while (++uc_upgrade_times < 2); + + return i_ret; +} + +struct fts_upgrade g_upgrade; +struct fts_upgrade *fwupgrade; + +struct ft_chip_t ft8006u_fct = { + .type = 0x0B, + .chip_idh = 0xF0, + .chip_idl = 0x06, + .rom_idh = 0xF0, + .rom_idl = 0x06, + .pramboot_idh = 0xF0, + .pramboot_idl = 0xA6, + .bootloader_idh = 0x00, + .bootloader_idl = 0x00, + .chip_type = 0x8006D80B, +}; + +struct fts_ts_data g_fts_data; +struct fts_ts_data *fts_data; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static u16 fts_pram_ecc_calc_host(u8 *pbuf, u16 length) +{ + u16 ecc = 0; + u16 i = 0; + u16 j = 0; + + for ( i = 0; i < length; i += 2 ) { + ecc ^= ((pbuf[i] << 8) | (pbuf[i + 1])); + for (j = 0; j < 16; j ++) { + if (ecc & 0x01) + ecc = (u16)((ecc >> 1) ^ AL2_FCS_COEF_8006); + else + ecc >>= 1; + } + } + + return ecc; +} + +/************************************************************************ + * fts_pram_ecc_cal - Calculate and get pramboot ecc + * + * return pramboot ecc of tp if success, otherwise return error code + ***********************************************************************/ +static int fts_pram_ecc_cal_algo( + struct i2c_client *client, + u32 start_addr, + u32 ecc_length) +{ + int ret = 0; + int i = 0; + int ecc = 0; + u8 val[2] = { 0 }; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + + FTS_DEBUG("[UPGRADE]read out pramboot checksum\n"); + cmd[0] = FTS_ROMBOOT_CMD_ECC; + cmd[1] = BYTE_OFF_16(start_addr); + cmd[2] = BYTE_OFF_8(start_addr); + cmd[3] = BYTE_OFF_0(start_addr); + cmd[4] = BYTE_OFF_16(ecc_length); + cmd[5] = BYTE_OFF_8(ecc_length); + cmd[6] = BYTE_OFF_0(ecc_length); + ret = fts_i2c_write(client, cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write pramboot ecc cal cmd fail\n"); + return ret; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < 100; i++) { + msleep(1); + ret = fts_i2c_read(client, cmd, 1, val, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]ecc_finish read cmd fail\n"); + return ret; + } + if (0 == val[0]) + break; + } + if (i >= 100) { + FTS_ERROR("[UPGRADE]wait ecc finish fail\n"); + return -EIO; + } + + cmd[0] = FTS_CMD_READ_ECC; + ret = fts_i2c_read(client, cmd, 1, val, 2); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read pramboot ecc fail\n"); + return ret; + } + + ecc = ((u16)(val[0] << 8) + val[1]) & 0x0000FFFF; + return ecc; +} + +static int fts_pram_ecc_cal_xor(struct i2c_client *client) +{ + int ret = 0; + u8 reg_val = 0; + + FTS_DEBUG("[UPGRADE]read out pramboot checksum\n"); + + ret = fts_i2c_read_reg(client, FTS_ROMBOOT_CMD_ECC, ®_val); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read pramboot ecc fail\n"); + return ret; + } + + return (int)reg_val; +} + +int fts_pram_ecc_cal(struct i2c_client *client, u32 saddr, u32 len) +{ + if ((NULL == fwupgrade) && (NULL == fwupgrade->func)) { + FTS_ERROR("[UPGRADE]fwupgrade/func is null\n"); + return -EINVAL; + } + + if (fwupgrade->func->newmode) { + return fts_pram_ecc_cal_algo(client, saddr, len); + } else { + return fts_pram_ecc_cal_xor(client); + } +} + +/************************************************************************ + * fts_pram_write_buf - write pramboot data and calculate ecc + * + * return pramboot ecc of host if success, otherwise return error code + ***********************************************************************/ +int fts_pram_write_buf(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 j = 0; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number; + u32 packet_len = 0; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_tmp = 0; + int ecc_in_host = 0; + + FTS_DEBUG("[UPGRADE]write pramboot to pram\n"); + if ((NULL == fwupgrade) && (NULL == fwupgrade->func)) { + FTS_ERROR("[UPGRADE]fwupgrade/func is null\n"); + return -EINVAL; + } + + if (NULL == buf) { + FTS_ERROR("[UPGRADE]pramboot buf is null\n"); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]pramboot len=%d\n", len); + if ((len < PRAMBOOT_MIN_SIZE) || (len > PRAMBOOT_MAX_SIZE)) { + FTS_ERROR("[UPGRADE]pramboot length(%d) fail\n", len); + return -EINVAL; + } + + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) + packet_number++; + packet_len = FTS_FLASH_PACKET_LENGTH; + + packet_buf[0] = FTS_ROMBOOT_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + packet_buf[1] = BYTE_OFF_16(offset); + packet_buf[2] = BYTE_OFF_8(offset); + packet_buf[3] = BYTE_OFF_0(offset); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + if (!fwupgrade->func->newmode) { + ecc_tmp ^= packet_buf[FTS_CMD_WRITE_LEN + j]; + } + } + + ret = fts_i2c_write(client, packet_buf, packet_len + FTS_CMD_WRITE_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pramboot write data(%d) fail\n", i); + return ret; + } + } + + if (fwupgrade->func->newmode) { + ecc_in_host = (int)fts_pram_ecc_calc_host(buf, len); + } else { + ecc_in_host = (int)ecc_tmp; + } + + return ecc_in_host; +} + +/************************************************************************ + * fts_pram_start - remap to start pramboot + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_pram_start(struct i2c_client *client) +{ + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + int ret = 0; + + FTS_DEBUG("[UPGRADE]remap to start pramboot\n"); + + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write start pram cmd fail\n"); + return ret; + } + msleep(FTS_DELAY_PRAMBOOT_START); + + return 0; +} + +/************************************************************************ + * fts_pram_init - initialize pramboot + * + * return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_pram_init(struct i2c_client *client) +{ + int ret = 0; + u8 reg_val = 0; + u8 wbuf[3] = { 0 }; + + FTS_DEBUG("[UPGRADE]pramboot initialization\n"); + + /* read flash ID */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + ret = fts_i2c_read(client, wbuf, 1, ®_val, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read flash type fail\n"); + return ret; + } + + /* set flash clk */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + wbuf[1] = reg_val; + wbuf[2] = 0x00; + ret = fts_i2c_write(client, wbuf, 3); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write flash type fail\n"); + return ret; + } + + return 0; +} + +/************************************************************************ +* Name: fts_pram_write_init +* Brief: wirte pramboot to pram and initialize +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_pram_write_init(struct i2c_client *client) +{ + int ret = 0; + bool state = 0; + enum FW_STATUS status = FTS_RUN_IN_ERROR; + struct fts_upgrade *upg = fwupgrade; + + FTS_DEBUG("[UPGRADE]**********pram write and init**********\n"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upgrade/func is null\n"); + return -EINVAL; + } + + if (!upg->func->pramboot_supported) { + FTS_ERROR("[UPGRADE]ic not support pram\n"); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]check whether tp is in romboot or not \n"); + /* need reset to romboot when non-romboot state */ + ret = fts_fwupg_get_boot_state(client, &status); + if (status != FTS_RUN_IN_ROM) { + if (FTS_RUN_IN_PRAM == status) { + FTS_INFO("[UPGRADE]tp is in pramboot, need send reset cmd before upgrade\n"); + ret = fts_pram_init(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pramboot(before) init fail\n"); + return ret; + } + } + + FTS_INFO("[UPGRADE]tp isn't in romboot, need send reset to romboot\n"); + ret = fts_fwupg_reset_to_romboot(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]reset to romboot fail\n"); + return ret; + } + } + + /* check the length of the pramboot */ + if (upg->func->fts_8006u) + ret = fts_ft8006u_pram_write_remap(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram write fail, ret=%d\n", ret); + return ret; + } + + FTS_DEBUG("[UPGRADE]after write pramboot, confirm run in pramboot\n"); + state = fts_fwupg_check_state(client, FTS_RUN_IN_PRAM); + if (!state) { + FTS_ERROR("[UPGRADE]not in pramboot\n"); + return -EIO; + } + + ret = fts_pram_init(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pramboot init fail\n"); + return ret; + } + + return 0; +} +/***************************************************************************** +* Name: fts_wait_tp_to_valid +* Brief: Read chip id until TP FW become valid(Timeout: TIMEOUT_READ_REG), +* need call when reset/power on/resume... +* Input: +* Output: +* Return: return 0 if tp valid, otherwise return error code +*****************************************************************************/ +int fts_wait_tp_to_valid(struct i2c_client *client) +{ + int ret = 0; + int cnt = 0; + u8 reg_value = 0; + u8 chip_id = fts_data->ic_info.ids.chip_idh; + + do { + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, ®_value); + if ((ret < 0) || (reg_value != chip_id)) { + FTS_DEBUG("[UPGRADE]TP Not Ready, ReadData = 0x%x\n", reg_value); + FTS_DEBUG("[UPGRADE]TP Not Ready, chip_id = 0x%x\n", chip_id); + } else if (reg_value == chip_id) { + FTS_INFO("[UPGRADE]TP Ready, Device ID = 0x%x\n", reg_value); + return 0; + } + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + return -EIO; +} +/************************************************************************ +* Name: fts_fwupg_check_fw_valid +* Brief: check fw in tp is valid or not +* Input: +* Output: +* Return: return true if fw is valid, otherwise return false +***********************************************************************/ +bool fts_fwupg_check_fw_valid(struct i2c_client *client) +{ + int ret = 0; + + ret = fts_wait_tp_to_valid(client); + if (ret < 0) { + FTS_INFO("[UPGRADE]tp fw invaild\n"); + return false; + } + + FTS_DEBUG("[UPGRADE]tp fw vaild\n"); + return true; +} +/************************************************************************ +* HID to standard I2C +***********************************************************************/ +void fts_i2c_hid2std(struct i2c_client *client) +{ + int ret = 0; + u8 buf[3] = {0xeb, 0xaa, 0x09}; + + ret = fts_i2c_write(client, buf, 3); + if (ret < 0) + FTS_ERROR("[UPGRADE]hid2std cmd write fail\n"); + else { + msleep(10); + buf[0] = buf[1] = buf[2] = 0; + ret = fts_i2c_read(client, NULL, 0, buf, 3); + if (ret < 0) + FTS_ERROR("[UPGRADE]hid2std cmd read fail\n"); + else if ((0xeb == buf[0]) && (0xaa == buf[1]) && (0x08 == buf[2])) { + FTS_DEBUG("[UPGRADE]hidi2c change to stdi2c successful\n"); + } else { + FTS_ERROR("[UPGRADE]hidi2c change to stdi2c fail\n"); + } + } +} +/************************************************************************ +* Name: fts_fwupg_get_boot_state +* Brief: read boot id(rom/pram/bootloader), confirm boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_get_boot_state(struct i2c_client *client, enum FW_STATUS *fw_sts) +{ + int ret = 0; + u8 cmd[4] = { 0 }; + u32 cmd_len = 0; + u8 val[2] = { 0 }; + struct ft_chip_t ids = fts_data->ic_info.ids; + struct fts_upgrade *upg = fwupgrade; + + FTS_DEBUG("[UPGRADE]**********read boot id**********\n"); + if ((NULL == fw_sts) || (NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upgrade/func/fw_sts is null\n"); + return -EINVAL; + } + + if (upg->func->hid_supported) + fts_i2c_hid2std(client); + + cmd[0] = FTS_CMD_START1; + cmd[1] = FTS_CMD_START2; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write 55 aa cmd fail\n"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + cmd[0] = FTS_CMD_READ_ID; + cmd[1] = cmd[2] = cmd[3] = 0x00; + if (fts_data->ic_info.is_incell) + cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_i2c_read(client, cmd, cmd_len, val, 2); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write 90 cmd fail\n"); + return ret; + } + + FTS_DEBUG("[UPGRADE]read boot id:0x%02x%02x\n", val[0], val[1]); + if ((val[0] == ids.rom_idh) && (val[1] == ids.rom_idl)) { + FTS_INFO("[UPGRADE]tp run in romboot\n"); + *fw_sts = FTS_RUN_IN_ROM; + } else if ((val[0] == ids.pramboot_idh) && (val[1] == ids.pramboot_idl)) { + FTS_INFO("[UPGRADE]tp run in pramboot\n"); + *fw_sts = FTS_RUN_IN_PRAM; + } else if ((val[0] == ids.bootloader_idh) && (val[1] == ids.bootloader_idl)) { + FTS_INFO("[UPGRADE]tp run in bootloader\n"); + *fw_sts = FTS_RUN_IN_BOOTLOADER; + } + + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_check_state +* Brief: confirm tp run in romboot/pramboot/bootloader +* Input: +* Output: +* Return: return true if state is match, otherwise return false +***********************************************************************/ +bool fts_fwupg_check_state(struct i2c_client *client, enum FW_STATUS rstate) +{ + int ret = 0; + int i = 0; + enum FW_STATUS cstate = FTS_RUN_IN_ERROR; + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + ret = fts_fwupg_get_boot_state(client, &cstate); + if (cstate == rstate) + return true; + msleep(FTS_DELAY_READ_ID); + } + + return false; +} + +/************************************************************************ +* Name: fts_fwupg_reset_in_boot +* Brief: RST CMD(07), reset to romboot(bootloader) in boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_in_boot(struct i2c_client *client) +{ + int ret = 0; + u8 cmd = FTS_CMD_RESET; + + FTS_DEBUG("[UPGRADE]reset in boot environment\n"); + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram/rom/bootloader reset cmd write fail\n"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_reset_to_boot +* Brief: reset to boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_to_boot(struct i2c_client *client) +{ + int ret = 0; + + FTS_DEBUG("[UPGRADE]send 0xAA and 0x55 to FW, reset to boot environment\n"); + + ret = fts_i2c_write_reg(client, FTS_REG_UPGRADE, FTS_UPGRADE_AA); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write FC=0xAA fail\n"); + return ret; + } + msleep(FTS_DELAY_FC_AA); + + ret = fts_i2c_write_reg(client, FTS_REG_UPGRADE, FTS_UPGRADE_55); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write FC=0x55 fail\n"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_reset_to_romboot +* Brief: reset to romboot, to load pramboot +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_to_romboot(struct i2c_client *client) +{ + int ret = 0; + int i = 0; + u8 cmd = FTS_CMD_RESET; + enum FW_STATUS state = FTS_RUN_IN_ERROR; + + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram/rom/bootloader reset cmd write fail\n"); + return ret; + } + mdelay(10); + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + ret = fts_fwupg_get_boot_state(client, &state); + if (FTS_RUN_IN_ROM == state) + break; + mdelay(5); + } + if (i >= FTS_UPGRADE_LOOP) { + FTS_ERROR("[UPGRADE]reset to romboot fail\n"); + return -EIO; + } + + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_enter_into_boot +* Brief: enter into boot environment, ready for upgrade +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_enter_into_boot(struct i2c_client *client) +{ + int ret = 0; + bool fwvalid = false; + bool state = false; + struct fts_upgrade *upg = fwupgrade; + + FTS_DEBUG("[UPGRADE]***********enter into pramboot/bootloader***********\n"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upgrade/func is null\n"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_fwupg_reset_to_boot(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]enter into romboot/bootloader fail\n"); + return ret; + } + } + + if (upg->func->pramboot_supported) { + FTS_DEBUG("[UPGRADE]pram supported, write pramboot and init\n"); + /* pramboot */ + ret = fts_pram_write_init(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram write_init fail\n"); + return ret; + } + } else { + FTS_DEBUG("[UPGRADE]pram not supported, confirm in bootloader\n"); + /* bootloader */ + state = fts_fwupg_check_state(client, FTS_RUN_IN_BOOTLOADER); + if (!state) { + FTS_ERROR("[UPGRADE]fw not in bootloader, fail\n"); + return -EIO; + } + } + + return 0; +} + +/************************************************************************ + * Name: fts_fwupg_check_flash_status + * Brief: read status from tp + * Input: flash_status: correct value from tp + * retries: read retry times + * retries_delay: retry delay + * Output: + * Return: return true if flash status check pass, otherwise return false +***********************************************************************/ +static bool fts_fwupg_check_flash_status( + struct i2c_client *client, + u16 flash_status, + int retries, + int retries_delay) +{ + int ret = 0; + int i = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + + for (i = 0; i < retries; i++) { + cmd = FTS_CMD_FLASH_STATUS; + ret = fts_i2c_read(client, &cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + if (flash_status == read_status) + return true; + msleep(retries_delay); + } + + return false; +} + +/************************************************************************ + * Name: fts_fwupg_erase + * Brief: erase flash area + * Input: delay - delay after erase + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_erase(struct i2c_client *client, u32 delay) +{ + int ret = 0; + u8 cmd = 0; + bool flag = false; + + FTS_DEBUG("[UPGRADE]**********erase now**********\n"); + + /*send to erase flash*/ + cmd = FTS_CMD_ERASE_APP; + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]erase cmd fail\n"); + return ret; + } + msleep(delay); + + /* read status 0xF0AA: success */ + flag = fts_fwupg_check_flash_status(client, FTS_CMD_FLASH_STATUS_ERASE_OK, + FTS_RETRIES_REASE, FTS_RETRIES_DELAY_REASE); + if (!flag) { + FTS_ERROR("[UPGRADE]ecc flash status check fail\n"); + return -EIO; + } + + return 0; +} + +/************************************************************************ + * Name: fts_fwupg_ecc_cal + * Brief: calculate and get ecc from tp + * Input: saddr - start address need calculate ecc + * len - length need calculate ecc + * Output: + * Return: return data ecc of tp if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_ecc_cal(struct i2c_client *client, u32 saddr, u32 len) +{ + int ret = 0; + u32 i = 0; + u8 wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 }; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u32 packet_num = 0; + u32 packet_len = 0; + u32 remainder = 0; + u32 addr = 0; + u32 offset = 0; + + FTS_DEBUG( "**********read out checksum**********"); + + /* check sum init */ + wbuf[0] = FTS_CMD_ECC_INIT; + ret = fts_i2c_write(client, wbuf, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]ecc init cmd write fail\n"); + return ret; + } + + packet_num = len / FTS_MAX_LEN_ECC_CALC; + remainder = len % FTS_MAX_LEN_ECC_CALC; + if (remainder) + packet_num++; + packet_len = FTS_MAX_LEN_ECC_CALC; + FTS_DEBUG("[UPGRADE]ecc calc num:%d, remainder:%d\n", packet_num, remainder); + + /* send commond to start checksum */ + wbuf[0] = FTS_CMD_ECC_CAL; + for (i = 0; i < packet_num; i++) { + offset = FTS_MAX_LEN_ECC_CALC * i; + addr = saddr + offset; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + + if ((i == (packet_num - 1)) && remainder) + packet_len = remainder; + wbuf[4] = BYTE_OFF_8(packet_len); + wbuf[5] = BYTE_OFF_0(packet_len); + + FTS_DEBUG("[UPGRADE]ecc calc startaddr:0x%04x, len:%d\n", addr, packet_len); + ret = fts_i2c_write(client, wbuf, FTS_CMD_ECC_CAL_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]ecc calc cmd write fail\n"); + return ret; + } + + msleep(packet_len / 256); + + /* read status if check sum is finished */ + ret = fts_fwupg_check_flash_status(client, FTS_CMD_FLASH_STATUS_ECC_OK, + FTS_RETRIES_ECC_CAL, FTS_RETRIES_DELAY_ECC_CAL); + if (ret < 0) { + FTS_ERROR("[UPGRADE]ecc flash status read fail\n"); + return ret; + } + } + + /* read out check sum */ + wbuf[0] = FTS_CMD_ECC_READ; + ret = fts_i2c_read(client, wbuf, 1, val, 1); + if (ret < 0) { + FTS_ERROR( "ecc read cmd write fail"); + return ret; + } + + return val[0]; +} + +/************************************************************************ + * Name: fts_flash_write_buf + * Brief: write buf data to flash address + * Input: saddr - start address data write to flash + * buf - data buffer + * len - data length + * delay - delay after write + * Output: + * Return: return data ecc of host if success, otherwise return error code + ***********************************************************************/ +int fts_flash_write_buf( + struct i2c_client *client, + u32 saddr, + u8 *buf, + u32 len, + u32 delay) +{ + int ret = 0; + u32 i = 0; + u32 j = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_in_host = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + u16 wr_ok = 0; + + FTS_DEBUG( "**********write data to flash**********"); + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("[UPGRADE]buf is NULL or len is 0\n"); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]data buf start addr=0x%x, len=0x%x\n", saddr, len); + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) + packet_number++; + packet_len = FTS_FLASH_PACKET_LENGTH; + FTS_DEBUG("[UPGRADE]write data, num:%d remainder:%d\n", packet_number, remainder); + + packet_buf[0] = FTS_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + packet_buf[1] = BYTE_OFF_16(addr); + packet_buf[2] = BYTE_OFF_8(addr); + packet_buf[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + ecc_in_host ^= packet_buf[FTS_CMD_WRITE_LEN + j]; + } + + ret = fts_i2c_write(client, packet_buf, packet_len + FTS_CMD_WRITE_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]app write fail\n"); + return ret; + } + mdelay(delay); + + /* read status */ + wr_ok = FTS_CMD_FLASH_STATUS_WRITE_OK + addr / packet_len; + for (j = 0; j < FTS_RETRIES_WRITE; j++) { + cmd = FTS_CMD_FLASH_STATUS; + ret = fts_i2c_read(client, &cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + if (wr_ok == read_status) { + break; + } + mdelay(FTS_RETRIES_DELAY_WRITE); + } + } + + return (int)ecc_in_host; +} + +/************************************************************************ + * Name: fts_flash_read_buf + * Brief: read data from flash + * Input: saddr - start address data write to flash + * buf - buffer to store data read from flash + * len - read length + * Output: + * Return: return 0 if success, otherwise return error code + * + * Warning: can't call this function directly, need call in boot environment + ***********************************************************************/ +int fts_flash_read_buf(struct i2c_client *client, u32 saddr, u8 *buf, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u8 wbuf[FTS_CMD_READ_LEN]; + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("[UPGRADE]buf is NULL or len is 0\n"); + return -EINVAL; + } + + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) + packet_number++; + packet_len = FTS_FLASH_PACKET_LENGTH; + FTS_DEBUG("[UPGRADE]read packet_number:%d, remainder:%d\n", packet_number, remainder); + + wbuf[0] = FTS_CMD_READ; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + ret = fts_i2c_write(client, wbuf, FTS_CMD_READ_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram/bootloader write 03 command fail\n"); + return ret; + } + + msleep(FTS_CMD_READ_DELAY); /* must wait, otherwise read wrong data */ + ret = fts_i2c_read(client, NULL, 0, buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram/bootloader read 03 command fail\n"); + return ret; + } + } + + return 0; +} + +/************************************************************************ + * Name: fts_flash_read + * Brief: + * Input: addr - address of flash + * len - length of read + * Output: buf - data read from flash + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_flash_read(struct i2c_client *client, u32 addr, u8 *buf, u32 len) +{ + int ret = 0; + + FTS_DEBUG("[UPGRADE]***********read flash***********\n"); + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("[UPGRADE]buf is NULL or len is 0\n"); + return -EINVAL; + } + + ret = fts_fwupg_enter_into_boot(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]enter into pramboot/bootloader fail\n"); + goto read_flash_err; + } + + ret = fts_flash_read_buf(client, addr, buf, len); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read flash fail\n"); + goto read_flash_err; + } + +read_flash_err: + /* reset to normal boot */ + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) + FTS_ERROR("[UPGRADE]reset to normal boot fail\n"); + return ret; +} + +/************************************************************************ + * Name: fts_read_file + * Brief: read file + * Input: file name + * Output: + * Return: return file len if succuss, otherwise return error code + ***********************************************************************/ +int fts_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + char file_path[FILE_NAME_LENGTH] = { 0 }; + struct file *filp = NULL; + struct inode *inode; + mm_segment_t old_fs; + loff_t pos; + loff_t file_len = 0; + + if ((NULL == file_name) || (NULL == file_buf)) { + FTS_ERROR("[UPGRADE]filename/filebuf is NULL\n"); + return -EINVAL; + } + + snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name); + filp = filp_open(file_path, O_RDONLY, 0); + if (IS_ERR(filp)) { + FTS_ERROR("[UPGRADE]open %s file fail\n", file_path); + return -ENOENT; + } + + inode = filp->f_inode; + file_len = inode->i_size; + *file_buf = (u8 *)vmalloc(file_len); + if (NULL == *file_buf) { + FTS_ERROR("[UPGRADE]file buf malloc fail\n"); + filp_close(filp, NULL); + return -ENOMEM; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + ret = vfs_read(filp, *file_buf, file_len , &pos); + if (ret < 0) + FTS_ERROR("[UPGRADE]read file fail\n"); + FTS_DEBUG("[UPGRADE]file len:%x read len:%x pos:%x\n", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); + set_fs(old_fs); + + return ret; +} +#define FTS_USE_FIRMWARE 1 +/************************************************************************ +* Name: fts_upgrade_bin +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_upgrade_bin(struct i2c_client *client, char *fw_name, bool force) +{ + int ret = 0; + u32 fw_file_len = 0; + u8 *fw_file_buf; + struct fts_upgrade *upg = fwupgrade; +#if FTS_USE_FIRMWARE + const struct firmware *fw; +#endif + + FTS_DEBUG("[UPGRADE]start upgrade with fw bin\n"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upgrade/func is null\n"); + return -EINVAL; + } + +#if FTS_USE_FIRMWARE + ret = request_firmware(&fw, fw_name, &client->dev); + if (ret < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + fw_name, ret); + return ret; + } + fw_file_buf = (u8*)fw->data; + fw_file_len = (u32)fw->size; +#else + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN) || (ret > FTS_MAX_LEN_FILE)) { + FTS_ERROR("[UPGRADE]read fw bin file(sdcard) fail, len:%d\n", ret); + goto err_bin; + } + fw_file_len = ret; +#endif + FTS_DEBUG("[UPGRADE]fw bin file len:%x\n", fw_file_len); + if (force) { + if (upg->func->force_upgrade) { + ret = upg->func->force_upgrade(client, fw_file_buf, fw_file_len); + } else { + FTS_INFO("[UPGRADE]force_upgrade function is null, no upgrade\n"); + goto err_bin; + } + } else { + if (FTS_AUTO_LIC_UPGRADE_EN) { + if (upg->func->lic_upgrade) + ret = upg->func->lic_upgrade(client, fw_file_buf, fw_file_len); + else + FTS_INFO("[UPGRADE]lic_upgrade function is null, no upgrade\n"); + } + if (upg->func->upgrade) + ret = upg->func->upgrade(client, fw_file_buf, fw_file_len); + else + FTS_INFO("[UPGRADE]upgrade function is null, no upgrade\n"); + } + + if (ret < 0) { + FTS_ERROR("[UPGRADE]upgrade fw bin failed\n"); + fts_fwupg_reset_in_boot(client); + goto err_bin; + } + + FTS_INFO("[UPGRADE]upgrade fw bin success\n"); + +err_bin: +#if FTS_USE_FIRMWARE + release_firmware(fw); +#else + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } +#endif + return ret; +} + +static int fts_lic_get_vid_in_tp(struct i2c_client *client, u16 *vid) +{ + int ret = 0; + u8 val[2] = { 0 }; + + if (NULL == vid) { + FTS_ERROR("[UPGRADE]vid is NULL\n"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &val[0]); + if (fts_data->ic_info.is_incell) + ret = fts_i2c_read_reg(client, FTS_REG_MODULE_ID, &val[1]); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read vid from tp fail\n"); + return ret; + } + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_vid_in_host(u16 *vid) +{ + u8 val[2] = { 0 }; + u8 *licbuf = NULL; + u32 conf_saddr = 0; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !upg->func || !upg->lic || !vid) { + FTS_ERROR("[UPGRADE]upgrade/func/get_hlic_ver/lic/vid is null\n"); + return -EINVAL; + } + + if (upg->lic_length < FTS_MAX_LEN_SECTOR) { + FTS_ERROR("[UPGRADE]lic length(%x) fail\n", upg->lic_length); + return -EINVAL; + } + + licbuf = upg->lic; + conf_saddr = upg->func->fwcfgoff; + val[0] = licbuf[conf_saddr + FTS_CONIFG_VENDORID_OFF]; + if (fts_data->ic_info.is_incell) + val[1] = licbuf[conf_saddr + FTS_CONIFG_MODULEID_OFF]; + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("[UPGRADE]ver is NULL\n"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_LIC_VER, ver); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read lcd initcode ver from tp fail\n"); + return ret; + } + + return 0; +} + +static int fts_lic_get_ver_in_host(u8 *ver) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !upg->func || !upg->func->get_hlic_ver || !upg->lic) { + FTS_ERROR("[UPGRADE]upgrade/func/get_hlic_ver/lic is null\n"); + return -EINVAL; + } + + ret = upg->func->get_hlic_ver(upg->lic); + if (ret < 0) { + FTS_ERROR("[UPGRADE]get host lcd initial code version fail\n"); + return ret; + } + + *ver = (u8)ret; + return ret; +} + +/* check if lcd init code need upgrade +* true-need false-no need +*/ +static bool fts_lic_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + u8 initcode_ver_in_tp = 0; + u8 initcode_ver_in_host = 0; + u16 vid_in_tp = 0; + u16 vid_in_host = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(client); + if ( !fwvalid) { + FTS_INFO("[UPGRADE]fw is invalid, no upgrade lcd init code\n"); + return false; + } + + ret = fts_lic_get_vid_in_host(&vid_in_host); + if (ret < 0) { + FTS_ERROR("[UPGRADE]vendor id in host invalid\n"); + return false; + } + + ret = fts_lic_get_vid_in_tp(client, &vid_in_tp); + if (ret < 0) { + FTS_ERROR("[UPGRADE]vendor id in tp invalid\n"); + return false; + } + + FTS_DEBUG("[UPGRADE]vid in tp:%x, host:%x\n", vid_in_tp, vid_in_host); + if (vid_in_tp != vid_in_host) { + FTS_INFO("[UPGRADE]vendor id in tp&host are different, no upgrade lic\n"); + return false; + } + + ret = fts_lic_get_ver_in_host(&initcode_ver_in_host); + if (ret < 0) { + FTS_ERROR("[UPGRADE]init code in host invalid\n"); + return false; + } + + ret = fts_lic_get_ver_in_tp(client, &initcode_ver_in_tp); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read reg0xE4 fail\n"); + return false; + } + + FTS_DEBUG("[UPGRADE]lcd initial code version in tp:%x, host:%x\n", + initcode_ver_in_tp, initcode_ver_in_host); + if (0xA5 == initcode_ver_in_tp) { + FTS_INFO("[UPGRADE]lcd init code ver is 0xA5, don't upgade init code\n"); + return false; + } else if (0xFF == initcode_ver_in_tp) { + FTS_DEBUG("[UPGRADE]lcd init code in tp is invalid, need upgrade init code\n"); + return true; + } else if (initcode_ver_in_tp < initcode_ver_in_host) + return true; + else + return false; +} + +int fts_lic_upgrade(struct i2c_client *client, struct fts_upgrade *upg) +{ + int ret = 0; + bool hlic_upgrade = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_DEBUG("[UPGRADE]lcd initial code auto upgrade function\n"); + if ((!upg) || (!upg->func) || (!upg->func->lic_upgrade)) { + FTS_ERROR("[UPGRADE]lcd upgrade function is null\n"); + return -EINVAL; + } + + hlic_upgrade = fts_lic_need_upgrade(client); + FTS_DEBUG("[UPGRADE]lcd init code upgrade flag:%d\n", hlic_upgrade); + if (hlic_upgrade) { + FTS_INFO("[UPGRADE]lcd initial code need upgrade, upgrade begin...\n"); + do { + FTS_INFO("[UPGRADE]lcd initial code upgrade times:%d\n", upgrade_count); + upgrade_count++; + + ret = upg->func->lic_upgrade(client, upg->lic, upg->lic_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_lic_get_ver_in_tp(client, &ver); + FTS_INFO("[UPGRADE]success upgrade to lcd initcode ver:%02x\n", ver); + break; + } + } while (upgrade_count < 2); + } else { + FTS_INFO("[UPGRADE]lcd initial code don't need upgrade\n"); + } + + return ret; +} + + +static int fts_param_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("[UPGRADE]ver is NULL\n"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_IDE_PARA_VER_ID, ver); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read fw param ver from tp fail\n"); + return ret; + } + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("[UPGRADE]param version in tp invalid\n"); + return -EIO; + } + + return 0; +} + +static int fts_param_get_ver_in_host(u8 *ver) +{ + struct fts_upgrade *upg = fwupgrade; + + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("[UPGRADE]fts_data/upgrade/func/fw/ver is NULL\n"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->paramcfgveroff) { + FTS_ERROR("[UPGRADE]fw len(%x) < paramcfg ver offset(%x)\n", + upg->fw_length, upg->func->paramcfgveroff); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]fw paramcfg version offset:%x\n", upg->func->paramcfgveroff); + *ver = upg->fw[upg->func->paramcfgveroff]; + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("[UPGRADE]param version in host invalid\n"); + return -EIO; + } + + return 0; +} + +/************************************************************************ + * fts_param_need_upgrade - check fw paramcfg need upgrade or not + * + * Return: return true if paramcfg need upgrade + ***********************************************************************/ +static bool fts_param_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + u8 val = 0; + u8 ver_in_host = 0; + u8 ver_in_tp = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(client); + if ( !fwvalid) { + FTS_INFO("[UPGRADE]fw is invalid, no upgrade paramcfg\n"); + return false; + } + + ret = fts_param_get_ver_in_host(&ver_in_host); + if (ret < 0) { + FTS_ERROR("[UPGRADE]param version in host invalid\n"); + return false; + } + + ret = fts_i2c_read_reg(client, FTS_REG_IDE_PARA_STATUS, &val); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read IDE PARAM STATUS in tp fail\n"); + return false; + } + + if ((val & 0x80) != 0x80) { + FTS_INFO("[UPGRADE]no IDE VER in tp\n"); + return false; + } else if ((val & 0x7F) != 0x00) { + FTS_INFO("[UPGRADE]IDE VER, param invalid, need upgrade param\n"); + return true; + } + + ret = fts_param_get_ver_in_tp(client, &ver_in_tp); + if (ret < 0) { + FTS_ERROR("[UPGRADE]get IDE param ver in tp fail\n"); + return false; + } + + FTS_INFO("[UPGRADE]fw paramcfg version in tp:%x, host:%x\n", ver_in_tp, ver_in_host); + if (ver_in_tp < ver_in_host) + return true; + + return false; +} + +/************************************************************************ + * fts_fwupg_get_ver_in_tp - read fw ver from tp register + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_fwupg_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("[UPGRADE]ver is NULL\n"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_FW_VER, ver); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read fw ver from tp fail\n"); + return ret; + } + + return 0; +} + +/************************************************************************ + * fts_fwupg_get_ver_in_host - read fw ver in host fw image + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_fwupg_get_ver_in_host(u8 *ver) +{ + struct fts_upgrade *upg = fwupgrade; + + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("[UPGRADE]fts_data/upgrade/func/fw/ver is NULL\n"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->fwveroff) { + FTS_ERROR("[UPGRADE]fw len(%x) < fw ver offset(%x)\n", + upg->fw_length, upg->func->fwveroff); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]fw version offset:%x\n", upg->func->fwveroff); + *ver = upg->fw[upg->func->fwveroff]; + return 0; +} + +/************************************************************************ + * fts_fwupg_need_upgrade - check fw need upgrade or not + * + * Return: return true if fw need upgrade + ***********************************************************************/ +static bool fts_fwupg_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + bool fwvalid = false; + u8 fw_ver_in_host = 0; + u8 fw_ver_in_tp = 0; + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_fwupg_get_ver_in_host(&fw_ver_in_host); + if (ret < 0) { + FTS_ERROR("[UPGRADE]get fw ver in host fail\n"); + return false; + } + + ret = fts_fwupg_get_ver_in_tp(client, &fw_ver_in_tp); + if (ret < 0) { + FTS_ERROR("[UPGRADE]get fw ver in tp fail\n"); + return false; + } + + FTS_INFO("[UPGRADE]fw version in tp:%x, host:%x\n", fw_ver_in_tp, fw_ver_in_host); + if (fw_ver_in_tp < fw_ver_in_host) + return true; + } else { + FTS_INFO("[UPGRADE]fw invalid, need upgrade fw\n"); + return true; + } + + return false; +} + +/************************************************************************ + * Name: fts_fw_upgrade + * Brief: fw upgrade main entry + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_upgrade(struct i2c_client *client, struct fts_upgrade *upg) +{ + int ret = 0; + bool upgrade_flag = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_DEBUG("[UPGRADE]fw auto upgrade function\n"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upg/upg->func is null\n"); + return -EINVAL; + } + + upgrade_flag = fts_fwupg_need_upgrade(client); + FTS_DEBUG("[UPGRADE]fw upgrade flag:%d\n", upgrade_flag); + do { + upgrade_count++; + if (upgrade_flag) { + FTS_INFO("[UPGRADE]upgrade fw app(times:%d)\n", upgrade_count); + if (upg->func->upgrade) { + ret = upg->func->upgrade(client, upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_fwupg_get_ver_in_tp(client, &ver); + FTS_INFO("[UPGRADE]success upgrade to fw version %02x\n", ver); + break; + } + } else { + FTS_ERROR("[UPGRADE]upgrade func/upgrade is null, return immediately\n"); + ret = -ENODATA; + break; + } + } else { + FTS_INFO("[UPGRADE]fw don't need upgrade\n"); + if (upg->func->param_upgrade) { + if (fts_param_need_upgrade(client)) { + FTS_INFO("[UPGRADE]upgrade param area(times:%d)\n", upgrade_count); + ret = upg->func->param_upgrade(client, upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_param_get_ver_in_tp(client, &ver); + FTS_INFO("[UPGRADE]success upgrade to fw param version %02x\n", ver); + break; + } + } else { + FTS_INFO("[UPGRADE]param don't need upgrade\n"); + break; + } + } else + break; + } + } while (upgrade_count < 2); + + return ret; +} +bool FTS_AUTO_LIC_UPGRADE_EN = false; + +/* + * fts_fwupg_init_ic_detail - for ic detail initialaztion + */ +static void fts_fwupg_init_ic_detail(void) +{ + struct fts_upgrade *upg = fwupgrade; + + if (upg && upg->func && upg->func->init) + upg->func->init(); +} +/* + * fts_fwupg_do_upgrade - fw upgrade work function + * 1. get fw image/file + * 2. call upgrade main function(fts_fwupg_auto_upgrade) + */ +int fts_fwupg_do_upgrade(const char *fwname) +{ + struct fts_ts_data *ts_data = fts_data; + fts_fwupg_init_ic_detail(); + return fts_upgrade_bin(ts_data->client, (char*)fwname, false); +} + +int fts_extra_exit(void) +{ + return 0; +} + +int fts_extra_init(struct i2c_client *client, struct input_dev *input_dev, struct ft_ts_platform_data *pdata) +{ + int type = pdata->family_id; + + fts_data = &g_fts_data; + fwupgrade = &g_upgrade; + fts_data->client = client; + fts_data->input_dev = input_dev; + fts_data->pdata = pdata; + + if (FT8006U_ID == type) { + FTS_AUTO_LIC_UPGRADE_EN = true; + fwupgrade->func = &upgrade_func_ft8006u; + fts_data->ic_info.ids = ft8006u_fct; + fts_data->ic_info.is_incell = true; + fts_data->ic_info.hid_supported = false; + } + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.h b/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.h new file mode 100644 index 000000000000..14a610ff53db --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.h @@ -0,0 +1,230 @@ +#ifndef __LINUX_FOCALTECH_FLASH_H__ +#define __LINUX_FOCALTECH_FLASH_H__ + +#include +#include +#include +#include "focaltech_upgrade_common.h" + +extern int fts_ctpm_auto_upgrade(struct i2c_client *client, + const char *fw_name, + const struct ft_ts_platform_data *pdata); + +int fts_ctpm_i2c_hid2std(struct i2c_client *client); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern bool FTS_AUTO_LIC_UPGRADE_EN; + +struct ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; +}; + +struct fts_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct ts_ic_info ic_info; + struct workqueue_struct *ts_workqueue; + struct delayed_work esdcheck_work; + struct ft_ts_platform_data *pdata; +}; +extern struct fts_ts_data *fts_data; + +#define INTERVAL_READ_REG 100 /* unit:ms */ +#define TIMEOUT_READ_REG 1000 /* unit:ms */ +#define FTS_GET_VENDOR_ID_NUM 0 + +#define FTS_REG_LIC_VER 0xE4 +#define BYTE_OFF_0(x) (u8)((x) & 0xFF) +#define BYTE_OFF_8(x) (u8)((x >> 8) & 0xFF) +#define BYTE_OFF_16(x) (u8)((x >> 16) & 0xFF) +#define BYTE_OFF_24(x) (u8)((x >> 24) & 0xFF) +#define FTS_CMD_START1 0x55 +#define FTS_CMD_START2 0xAA +#define FTS_CMD_READ_ID 0x90 +#define INTERVAL_READ_REG 100 //unit:ms +#define TIMEOUT_READ_REG 1000 //unit:ms + +#define FTS_REG_IDE_PARA_VER_ID 0xB5 +#define FTS_REG_IDE_PARA_STATUS 0xB6 +#define FTS_REG_VENDOR_ID 0xA8 +#define FTS_REG_MODULE_ID 0xE3 + +#define FTS_CMD_RESET 0x07 +#define FTS_ROMBOOT_CMD_WRITE 0xAE +#define FTS_ROMBOOT_CMD_START_APP 0x08 +#define FTS_DELAY_PRAMBOOT_START 10 +#define FTS_ROMBOOT_CMD_ECC 0xCC + +#define FTS_CMD_READ 0x03 +#define FTS_CMD_READ_DELAY 1 +#define FTS_CMD_READ_LEN 4 +#define FTS_CMD_FLASH_TYPE 0x05 +#define FTS_CMD_FLASH_MODE 0x09 +#define FLASH_MODE_WRITE_FLASH_VALUE 0x0A +#define FLASH_MODE_UPGRADE_VALUE 0x0B +#define FLASH_MODE_LIC_VALUE 0x0C +#define FLASH_MODE_PARAM_VALUE 0x0D +#define FTS_CMD_ERASE_APP 0x61 +#define FTS_REASE_APP_DELAY 1350 +#define FTS_ERASE_SECTOR_DELAY 60 +#define FTS_RETRIES_REASE 50 +#define FTS_RETRIES_DELAY_REASE 200 +#define FTS_CMD_FLASH_STATUS 0x6A +#define FTS_CMD_FLASH_STATUS_LEN 2 +#define FTS_CMD_FLASH_STATUS_NOP 0x0000 +#define FTS_CMD_FLASH_STATUS_ECC_OK 0xF055 +#define FTS_CMD_FLASH_STATUS_ERASE_OK 0xF0AA +#define FTS_CMD_FLASH_STATUS_WRITE_OK 0x1000 +#define FTS_CMD_ECC_INIT 0x64 +#define FTS_CMD_ECC_CAL 0x65 +#define FTS_CMD_ECC_CAL_LEN 6 +#define FTS_RETRIES_ECC_CAL 10 +#define FTS_RETRIES_DELAY_ECC_CAL 50 +#define FTS_CMD_ECC_READ 0x66 +#define FTS_CMD_DATA_LEN 0xB0 +#define FTS_CMD_DATA_LEN_LEN 4 +#define FTS_CMD_WRITE 0xBF +#define FTS_RETRIES_WRITE 100 +#define FTS_RETRIES_DELAY_WRITE 1 +#define FTS_CMD_WRITE_LEN 6 +#define FTS_DELAY_READ_ID 20 +#define FTS_DELAY_UPGRADE_RESET 80 +#define PRAMBOOT_MIN_SIZE 0x120 +#define PRAMBOOT_MAX_SIZE (64*1024) +#define FTS_FLASH_PACKET_LENGTH 32 /* max=128 */ +#define FTS_MAX_LEN_ECC_CALC 0xFFFE /* must be even */ +#define FTS_MIN_LEN 0x120 +#define FTS_MAX_LEN_FILE (128 * 1024) +#define FTS_MAX_LEN_APP (64 * 1024) +#define FTS_MAX_LEN_SECTOR (4 * 1024) +#define FTS_CONIFG_VENDORID_OFF 0x04 +#define FTS_CONIFG_MODULEID_OFF 0x1E +#define FTS_CONIFG_PROJECTID_OFF 0x20 +#define FTS_APPINFO_OFF 0x100 +#define FTS_APPINFO_APPLEN_OFF 0x00 +#define FTS_APPINFO_APPLEN2_OFF 0x12 +#define FTS_REG_UPGRADE 0xFC +#define FTS_UPGRADE_AA 0xAA +#define FTS_UPGRADE_55 0x55 +#define FTS_DELAY_FC_AA 10 +#define FTS_UPGRADE_LOOP 30 +#define FTS_HEADER_LEN 32 +#define FTS_FW_BIN_FILEPATH "/sdcard/" + +#define FTX_MAX_COMPATIBLE_TYPE 4 + +#define FTS_ROMBOOT_CMD_ECC_NEW_LEN 7 +#define FTS_ROMBOOT_CMD_ECC_FINISH 0xCE +#define FTS_CMD_READ_ECC 0xCD +#define AL2_FCS_COEF_8006 ((1 << 15) + (1 << 10) + (1 << 3)) + +#define I2C_BUFFER_LENGTH_MAXINUM 256 +#define FILE_NAME_LENGTH 128 +#define ENABLE 1 +#define DISABLE 0 +#define VALID 1 +#define INVALID 0 +#define FTS_CMD_START1 0x55 +#define FTS_CMD_START2 0xAA +#define FTS_CMD_START_DELAY 10 +#define FTS_CMD_READ_ID 0x90 +#define FTS_CMD_READ_ID_LEN 4 +#define FTS_CMD_READ_ID_LEN_INCELL 1 + +#define FTS_REG_ESD_SATURATE 0xED +#define FTS_REG_ESDCHECK_DISABLE 0x8D + +#define FTS_SYSFS_ECHO_ON(buf) (buf[0] == '1') +#define FTS_SYSFS_ECHO_OFF(buf) (buf[0] == '0') + +enum FW_FLASH_MODE { + FLASH_MODE_APP, + FLASH_MODE_LIC, + FLASH_MODE_PARAM, + FLASH_MODE_ALL, +}; + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* IC info */ +struct upgrade_func { + u64 ctype[FTX_MAX_COMPATIBLE_TYPE]; + int newmode; + u32 fwveroff; + u32 fwcfgoff; + u32 appoff; + u32 licoff; + u32 paramcfgoff; + u32 paramcfgveroff; + u32 paramcfg2off; + bool hid_supported; + bool pramboot_supported; + bool fts_8006u; + u8 *pramboot; + u32 pb_length; + int (*init)(void); + int (*upgrade)(struct i2c_client *, u8 *, u32); + int (*get_hlic_ver)(u8 *); + int (*lic_upgrade)(struct i2c_client *, u8 *, u32); + int (*param_upgrade)(struct i2c_client *, u8 *, u32); + int (*force_upgrade)(struct i2c_client *, u8 *, u32); +}; + +struct fts_upgrade { + u8 *fw; + u32 fw_length; + u8 *lic; + u32 lic_length; + struct upgrade_func *func; +}; + +struct upgrade_fw { + u16 vendor_id; + u8 *fw_file; + u32 fw_len; +}; + + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct fts_upgrade *fwupgrade; +extern struct upgrade_func upgrade_func_ft8006; +extern struct upgrade_func upgrade_func_ft8006u; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +bool fts_fwupg_check_fw_valid(struct i2c_client *client); +int fts_fwupg_get_boot_state(struct i2c_client *client, enum FW_STATUS *fw_sts); +bool fts_fwupg_check_state(struct i2c_client *client, enum FW_STATUS rstate); +int fts_fwupg_reset_in_boot(struct i2c_client *client); +int fts_fwupg_reset_to_boot(struct i2c_client *client); +int fts_fwupg_reset_to_romboot(struct i2c_client *client); +int fts_fwupg_enter_into_boot(struct i2c_client *client); +int fts_fwupg_erase(struct i2c_client *client, u32 delay); +int fts_fwupg_ecc_cal(struct i2c_client *client, u32 saddr, u32 len); +int fts_flash_write_buf(struct i2c_client *client, u32 saddr, u8 *buf, u32 len, u32 delay); +void fts_fwupg_auto_upgrade(struct fts_ts_data *ts_data); +void fts_i2c_hid2std(struct i2c_client *client); +int fts_ft8006u_pram_write_remap(struct i2c_client *client); +int fts_pram_write_buf(struct i2c_client *client, u8 *buf, u32 len); +int fts_pram_ecc_cal(struct i2c_client *client, u32 saddr, u32 len); +int fts_pram_start(struct i2c_client *client); + +int fts_fwupg_do_upgrade(const char *fwname); + +int fts_extra_init(struct i2c_client *client, struct input_dev *input_dev, struct ft_ts_platform_data *pdata); +int fts_extra_exit(void); +#endif diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_ts.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_ts.c new file mode 100644 index 000000000000..e5625426b21c --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_ts.c @@ -0,0 +1,3893 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010 Focal tech Ltd. + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_flash.h" + + +#if defined(CONFIG_FB) +#include +#include + +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +/* Early-suspend level */ +#define FT_SUSPEND_LEVEL 1 +#endif + +#if defined(CONFIG_TOUCHSCREEN_FOCALTECH_SECURE_TOUCH_MMI) +#include +#include +#include +#include +static irqreturn_t ft_ts_interrupt(int irq, void *data); +#endif + +#include +#include +#include + +#define FT_DRIVER_VERSION 0x02 + +#define FT_META_REGS 3 +#define FT_ONE_TCH_LEN 6 +#define FT_TCH_LEN(x) (FT_META_REGS + FT_ONE_TCH_LEN * x) + +#define FT_PRESS 0x7F +#define FT_MAX_ID 0x0F +#define FT_TOUCH_X_H_POS 3 +#define FT_TOUCH_X_L_POS 4 +#define FT_TOUCH_Y_H_POS 5 +#define FT_TOUCH_Y_L_POS 6 +#define FT_TD_STATUS 2 +#define FT_TOUCH_EVENT_POS 3 +#define FT_TOUCH_ID_POS 5 +#define FT_TOUCH_DOWN 0 +#define FT_TOUCH_CONTACT 2 + +/* register address*/ +#define FT_REG_DEV_MODE 0x00 +#define FT_DEV_MODE_REG_CAL 0x02 +#define FT_REG_FW_ID 0xA1 +#define FT_REG_ID 0xA3 +#define FT_REG_PMODE 0xA5 +#define FT_REG_FW_VER 0xA6 +#define FT_REG_FW_VENDOR_ID 0xA8 +#define FT_REG_POINT_RATE 0x88 +#define FT_REG_THGROUP 0x80 +#define FT_REG_ECC 0xCC +#define FT_REG_RESET_FW 0x07 +#define FT_REG_FW_MIN_VER 0xB2 +#define FT_REG_FW_SUB_MIN_VER 0xB3 +#define FT_REG_I2C_MODE 0xEB +#define FT_REG_FW_LEN 0xB0 +#define FT_REG_CHG_STATE 0x8B + +/* i2c mode register value */ +#define FT_VAL_I2C_MODE 0xAA +#define FT_VAL_I2C_MODE_STD 0x09 +#define FT_VAL_I2C_MODE_STD_CFM 0x08 + +/* gesture register address*/ +#define FT_REG_GESTURE_ENABLE 0xD0 +#define FT_REG_GESTURE_OUTPUT 0xD3 + +/* gesture register bits*/ +#define FT_GESTURE_DOUBLECLICK_COORD_X 100 +#define FT_GESTURE_DOUBLECLICK_COORD_Y 100 +#define FT_GESTURE_WAKEUP_TIMEOUT 500 +#define FT_GESTURE_DEFAULT_TRACKING_ID 0x0A +#define FT_GESTURE_DOUBLECLICK_ID 0x24 +#define FT_GESTURE_POINTER_NUM_MAX 128 +#define FT_GESTURE_POINTER_SIZEOF 4 +#define FT_GESTURE_ID_FLAG_SIZE 1 +#define FT_GESTURE_POINTER_NUM_FLAG_SIZE 1 +/* 6 bytes are taken to mark which gesture is supported in firmware */ +#define FT_GESTURE_SET_FLAG_SIZE 6 +#define I2C_TRANSFER_MAX_BYTE 255 +#define FT_GESTURE_DATA_HEADER (FT_GESTURE_ID_FLAG_SIZE + \ + FT_GESTURE_POINTER_NUM_FLAG_SIZE + \ + FT_GESTURE_SET_FLAG_SIZE) + +/* power register bits*/ +#define FT_PMODE_ACTIVE 0x00 +#define FT_PMODE_MONITOR 0x01 +#define FT_PMODE_STANDBY 0x02 +#define FT_PMODE_HIBERNATE 0x03 +#define FT_FACTORYMODE_VALUE 0x40 +#define FT_WORKMODE_VALUE 0x00 +#define FT_RST_CMD_REG1 0xFC +#define FT_RST_CMD_REG2 0xBC +#define FT_READ_ID_REG 0x90 +#define FT_ERASE_APP_REG 0x61 +#define FT_ERASE_PANEL_REG 0x63 +#define FT_FW_START_REG 0xBF + +#define FT_STATUS_NUM_TP_MASK 0x0F + +#define FT_VTG_MIN_UV 2600000 +#define FT_VTG_MAX_UV 3300000 +#define FT_I2C_VTG_MIN_UV 1800000 +#define FT_I2C_VTG_MAX_UV 1800000 + +#define FT_COORDS_ARR_SIZE 4 +#define MAX_BUTTONS 4 + +#define FT_8BIT_SHIFT 8 +#define FT_4BIT_SHIFT 4 +#define FT_FW_NAME_MAX_LEN 50 + +#define FT5316_ID 0x0A +#define FT5306I_ID 0x55 +#define FT6X06_ID 0x06 +#define FT6X36_ID 0x36 + +#define FT_UPGRADE_AA 0xAA +#define FT_UPGRADE_55 0x55 + +#define FT_FW_MIN_SIZE 8 +#define FT_FW_MAX_SIZE 65536 + +/* Firmware file is not supporting minor and sub minor so use 0 */ +#define FT_FW_FILE_MAJ_VER(x) ((x)->data[(x)->size - 2]) +#define FT_FW_FILE_MIN_VER(x) 0 +#define FT_FW_FILE_SUB_MIN_VER(x) 0 +#define FT_FW_FILE_VENDOR_ID(x) ((x)->data[(x)->size - 1]) + +#define FT_FW_FILE_MAJ_VER_FT6X36(x) ((x)->data[0x10a]) +#define FT_FW_FILE_VENDOR_ID_FT6X36(x) ((x)->data[0x108]) + +/** +* Application data verification will be run before upgrade flow. +* Firmware image stores some flags with negative and positive value +* in corresponding addresses, we need pick them out do some check to +* make sure the application data is valid. +*/ +#define FT_FW_CHECK(x, ts_data) \ + (ts_data->family_id == FT6X36_ID ? \ + (((x)->data[0x104] ^ (x)->data[0x105]) == 0xFF \ + && ((x)->data[0x106] ^ (x)->data[0x107]) == 0xFF) : \ + (((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \ + && ((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \ + && ((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF)) + +#define FT_MAX_TRIES 5 +#define FT_RETRY_DLY 20 + +#define FT_MAX_WR_BUF 10 +#define FT_MAX_RD_BUF 3 +#define FT_FW_PKT_LEN 128 +#define FT_FW_PKT_META_LEN 6 +#define FT_FW_PKT_DLY_MS 20 +#define FT_FW_LAST_PKT 0x6ffa +#define FT_EARSE_DLY_MS 100 +#define FT_55_AA_DLY_NS 5000 + +#define FT_UPGRADE_LOOP 30 +#define FT_CAL_START 0x04 +#define FT_CAL_FIN 0x00 +#define FT_CAL_STORE 0x05 +#define FT_CAL_RETRY 100 +#define FT_REG_CAL 0x00 +#define FT_CAL_MASK 0x70 + +#define FT_INFO_MAX_LEN 512 + +#define FT_BLOADER_SIZE_OFF 12 +#define FT_BLOADER_NEW_SIZE 30 +#define FT_DATA_LEN_OFF_OLD_FW 8 +#define FT_DATA_LEN_OFF_NEW_FW 14 +#define FT_FINISHING_PKT_LEN_OLD_FW 6 +#define FT_FINISHING_PKT_LEN_NEW_FW 12 +#define FT_MAGIC_BLOADER_Z7 0x7bfa +#define FT_MAGIC_BLOADER_LZ4 0x6ffa +#define FT_MAGIC_BLOADER_GZF_30 0x7ff4 +#define FT_MAGIC_BLOADER_GZF 0x7bf4 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +#define EXP_FN_DET_INTERVAL 1000 /* ms */ + +static irqreturn_t ft_ts_interrupt(int irq, void *data); + +enum { + FT_BLOADER_VERSION_LZ4 = 0, + FT_BLOADER_VERSION_Z7 = 1, + FT_BLOADER_VERSION_GZF = 2, +}; + +enum { + FT_FT5336_FAMILY_ID_0x11 = 0x11, + FT_FT5336_FAMILY_ID_0x12 = 0x12, + FT_FT5336_FAMILY_ID_0x13 = 0x13, + FT_FT5336_FAMILY_ID_0x14 = 0x14, +}; + +#define FT_STORE_TS_INFO(buf, id, fw_maj, fw_min, fw_sub_min) \ + snprintf(buf, FT_INFO_MAX_LEN, \ + "vendor name = Focaltech\n" \ + "model = 0x%x\n" \ + "fw_version = %d.%d.%d\n", \ + id, fw_maj, fw_min, fw_sub_min) +#define FT_TS_INFO_SYSFS_DIR_NAME "ts_info" +static char *ts_info_buff; + +#define FT_STORE_TS_DBG_INFO(buf, id, name, max_tch, group_id, \ + fw_vkey_support, fw_name, fw_maj, fw_min, fw_sub_min) \ + snprintf(buf, FT_INFO_MAX_LEN, \ + "controller\t= focaltech\n" \ + "model\t\t= 0x%x\n" \ + "name\t\t= %s\n" \ + "max_touches\t= %d\n" \ + "drv_ver\t\t= 0x%x\n" \ + "group_id\t= 0x%x\n" \ + "fw_vkey_support\t= %s\n" \ + "fw_name\t\t= %s\n" \ + "fw_ver\t\t= %d.%d.%d\n", id, name, \ + max_tch, FT_DRIVER_VERSION, group_id, \ + fw_vkey_support, fw_name, fw_maj, fw_min, \ + fw_sub_min) + +#define FT_DEBUG_DIR_NAME "ts_debug" +#define FT_FW_ID_LEN 2 + +#define FT_PROC_DIR_NAME "ftxxxx-debug" + +enum { + PROC_UPGRADE = 0, + PROC_READ_REGISTER = 1, + PROC_WRITE_REGISTER = 2, + PROC_WRITE_DATA = 6, + PROC_READ_DATA = 7 +}; + +struct ft_clip_area { + unsigned xul_clip, yul_clip, xbr_clip, ybr_clip; + unsigned inversion; /* clip inside (when 1) or ouside otherwise */ +}; + +enum { + FT_MOD_CHARGER, + FT_MOD_FPS, + FT_MOD_MAX +}; + +struct ft_modifiers { + int mods_num; + struct semaphore list_sema; + struct list_head mod_head; + +}; + +struct config_modifier { + const char *name; + int id; + bool effective; + struct ft_clip_area *clipa; + struct list_head link; +}; + +struct ft_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct ft_ts_platform_data *pdata; + struct ft_gesture_platform_data *gesture_pdata; + struct regulator *vdd; + struct regulator *vcc_i2c; + bool regulator_en; + struct mutex ft_clk_io_ctrl_mutex; + char fw_name[FT_FW_NAME_MAX_LEN]; + bool loading_fw; + u8 family_id; + struct dentry *dir; + u16 addr; + bool suspended; + char *ts_info; + u8 *tch_data; + u32 tch_data_len; + u8 fw_ver[3]; + u8 fw_vendor_id; + const char *panel_supplier; + struct kobject *ts_info_kobj; +#if defined(CONFIG_FB) + struct work_struct fb_notify_work; + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; +#if defined(CONFIG_TOUCHSCREEN_FOCALTECH_SECURE_TOUCH_MMI) + atomic_t st_enabled; + atomic_t st_pending_irqs; + struct completion st_powerdown; + struct completion st_irq_processed; + bool st_initialized; + struct clk *core_clk; + struct clk *iface_clk; +#endif + bool flash_enabled; + bool force_reflash; + u8 fw_id[FT_FW_ID_LEN]; + bool irq_enabled; + struct proc_dir_entry *proc_entry; + u8 proc_operate_mode; + struct notifier_block ft_reboot; + /* moto TP modifiers */ + bool patching_enabled; + struct ft_modifiers modifiers; + bool charger_detection_enabled; + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + + bool fps_detection_enabled; + bool is_fps_registered; /* FPS notif registration might be delayed */ + struct notifier_block fps_notif; + + bool clipping_on; + struct ft_clip_area *clipa; +}; + +struct ft_exp_fn { + bool inserted; + int (*func_init)(struct ft_ts_data *data); + void (*func_remove)(struct ft_ts_data *data); + void (*func_attn)(struct ft_ts_data *data, unsigned char intr_mask); + struct list_head link; +}; + +struct ft_exp_fn_ctrl { + bool inited; + struct mutex list_mutex; + struct list_head fn_list; + struct delayed_work det_work; + struct workqueue_struct *det_workqueue; + struct ft_ts_data *data_ptr; +}; +static DEFINE_MUTEX(exp_fn_ctrl_mutex); +static struct ft_exp_fn_ctrl exp_fn_ctrl; + + +struct touch_up_down { + int mismatch; + unsigned char up_down; + unsigned int counter; +}; + +struct touch_area_stats { + struct touch_up_down *ud; + ssize_t ud_len; + ssize_t ud_id; + ssize_t unknown_counter; + const char *name; +}; + +static struct touch_up_down display_ud[20]; +static struct touch_area_stats display_ud_stats = { + .ud = display_ud, + .ud_len = ARRAY_SIZE(display_ud), + .name = "ts" +}; + +__attribute__((weak)) +int FPS_register_notifier(struct notifier_block *nb, unsigned long stype, + bool report) +{ + return -ENODEV; +} + +__attribute__((weak)) +int FPS_unregister_notifier(struct notifier_block *nb, unsigned long stype) +{ + return -ENODEV; +} + +static void ud_set_id(struct touch_area_stats *tas, int id) +{ + tas->ud_id = id; +} + +static void ud_log_status(struct touch_area_stats *tas, bool down) +{ + struct touch_up_down *ud = tas->ud; + ssize_t id = tas->ud_id; + + if (id >= tas->ud_len) + tas->unknown_counter++; + + if (!down) { /* up */ + if (ud[id].up_down == 0x10) { + pr_debug("%s UP[%zu]\n", tas->name, id); + ud[id].up_down |= 1; + ud[id].mismatch--; + } + } else if (down) { /* down */ + if (ud[id].up_down == 0) { + ud[id].up_down |= (1 << 4); + pr_debug("%s DOWN[%zu]\n", tas->name, id); + ud[id].mismatch++; + } else if (ud[id].up_down == 0x10) + return; + } + + if (ud[id].up_down == 0x11) { + pr_debug("%s CLEAR[%zu]\n", tas->name, id); + ud[id].up_down = 0; + ud[id].counter++; + } +} + +static void TSI_state(struct input_dev *dev, unsigned int tool, bool status) +{ + ud_log_status(&display_ud_stats, status); + input_mt_report_slot_state(dev, tool, status); +} + +static void TSI_id(struct input_dev *dev, int id) +{ + ud_set_id(&display_ud_stats, id); + input_mt_slot(dev, id); +} + +#define input_mt_report_slot_state TSI_state +#define input_mt_slot TSI_id + +static int ft_ts_start(struct device *dev); +static int ft_ts_stop(struct device *dev); +static void ft_resume_ps_chg_cm_state(struct ft_ts_data *data); +static struct config_modifier *ft_modifier_by_id( + struct ft_ts_data *data, int id); + +#if defined(CONFIG_TOUCHSCREEN_FOCALTECH_SECURE_TOUCH_MMI) +static void ft_secure_touch_init(struct ft_ts_data *data) +{ + data->st_initialized = 0; + + init_completion(&data->st_powerdown); + init_completion(&data->st_irq_processed); + + /* Get clocks */ + data->core_clk = devm_clk_get(&data->client->dev, "core_clk"); + if (IS_ERR(data->core_clk)) { + data->core_clk = NULL; + dev_warn(&data->client->dev, + "%s: core_clk is not defined\n", __func__); + } + + data->iface_clk = devm_clk_get(&data->client->dev, "iface_clk"); + if (IS_ERR(data->iface_clk)) { + data->iface_clk = NULL; + dev_warn(&data->client->dev, + "%s: iface_clk is not defined", __func__); + } + data->st_initialized = 1; +} + +static void ft_secure_touch_notify(struct ft_ts_data *data) +{ + sysfs_notify(&data->input_dev->dev.kobj, NULL, "secure_touch"); +} + +static irqreturn_t ft_filter_interrupt(struct ft_ts_data *data) +{ + if (atomic_read(&data->st_enabled)) { + if (atomic_cmpxchg(&data->st_pending_irqs, 0, 1) == 0) { + reinit_completion(&data->st_irq_processed); + ft_secure_touch_notify(data); + wait_for_completion_interruptible( + &data->st_irq_processed); + } + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/* + * 'blocking' variable will have value 'true' when we want to prevent the driver + * from accessing the xPU/SMMU protected HW resources while the session is + * active. + */ +static void ft_secure_touch_stop(struct ft_ts_data *data, bool blocking) +{ + if (atomic_read(&data->st_enabled)) { + atomic_set(&data->st_pending_irqs, -1); + ft_secure_touch_notify(data); + if (blocking) + wait_for_completion_interruptible( + &data->st_powerdown); + } +} + +static int ft_clk_prepare_enable(struct ft_ts_data *data) +{ + int ret; + + ret = clk_prepare_enable(data->iface_clk); + if (ret) { + dev_err(&data->client->dev, + "error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(data->core_clk); + if (ret) { + clk_disable_unprepare(data->iface_clk); + dev_err(&data->client->dev, + "error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void ft_clk_disable_unprepare(struct ft_ts_data *data) +{ + clk_disable_unprepare(data->core_clk); + clk_disable_unprepare(data->iface_clk); +} + +static int ft_bus_get(struct ft_ts_data *data) +{ + int retval; + + mutex_lock(&data->ft_clk_io_ctrl_mutex); + retval = pm_runtime_get_sync(data->client->adapter->dev.parent); + if (retval >= 0 && data->core_clk != NULL && data->iface_clk != NULL) { + retval = ft_clk_prepare_enable(data); + if (retval) + pm_runtime_put_sync(data->client->adapter->dev.parent); + } + mutex_unlock(&data->ft_clk_io_ctrl_mutex); + return retval; +} + +static void ft_bus_put(struct ft_ts_data *data) +{ + mutex_lock(&data->ft_clk_io_ctrl_mutex); + if (data->core_clk != NULL && data->iface_clk != NULL) + ft_clk_disable_unprepare(data); + pm_runtime_put_sync(data->client->adapter->dev.parent); + mutex_unlock(&data->ft_clk_io_ctrl_mutex); +} + +static ssize_t ft_secure_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&data->st_enabled)); +} + +/* + * Accept only "0" and "1" valid values. + * "0" will reset the st_enabled flag, then wake up the reading process and + * the interrupt handler. + * The bus driver is notified via pm_runtime that it is not required to stay + * awake anymore. + * It will also make sure the queue of events is emptied in the controller, + * in case a touch happened in between the secure touch being disabled and + * the local ISR being ungated. + * "1" will set the st_enabled flag and clear the st_pending_irqs flag. + * The bus driver is requested via pm_runtime to stay awake. + */ +static ssize_t ft_secure_touch_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!data->st_initialized) + return -EIO; + + err = count; + switch (value) { + case 0: + if (atomic_read(&data->st_enabled) == 0) + break; + ft_bus_put(data); + atomic_set(&data->st_enabled, 0); + ft_secure_touch_notify(data); + complete(&data->st_irq_processed); + ft_ts_interrupt(data->client->irq, data); + complete(&data->st_powerdown); + break; + + case 1: + if (atomic_read(&data->st_enabled)) { + err = -EBUSY; + break; + } + synchronize_irq(data->client->irq); + if (ft_bus_get(data) < 0) { + dev_err(&data->client->dev, "ft_bus_get failed\n"); + err = -EIO; + break; + } + reinit_completion(&data->st_powerdown); + reinit_completion(&data->st_irq_processed); + atomic_set(&data->st_enabled, 1); + atomic_set(&data->st_pending_irqs, 0); + break; + + default: + dev_err(&data->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} + +/* + * This function returns whether there are pending interrupts, or + * other error conditions that need to be signaled to the userspace library, + * according tot he following logic: + * - st_enabled is 0 if secure touch is not enabled, returning -EBADF + * - st_pending_irqs is -1 to signal that secure touch is in being stopped, + * returning -EINVAL + * - st_pending_irqs is 1 to signal that there is a pending irq, returning + * the value "1" to the sysfs read operation + * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt + * has been processed, so the interrupt handler can be allowed to continue. + */ +static ssize_t ft_secure_touch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + int val = 0; + + if (atomic_read(&data->st_enabled) == 0) + return -EBADF; + if (atomic_cmpxchg(&data->st_pending_irqs, -1, 0) == -1) + return -EINVAL; + if (atomic_cmpxchg(&data->st_pending_irqs, 1, 0) == 1) + val = 1; + else + complete(&data->st_irq_processed); + return scnprintf(buf, PAGE_SIZE, "%u", val); +} +#else +static void ft_secure_touch_init(struct ft_ts_data *data) +{ +} +static irqreturn_t ft_filter_interrupt(struct ft_ts_data *data) +{ + return IRQ_NONE; +} +static void ft_secure_touch_stop(struct ft_ts_data *data, bool blocking) +{ +} +#endif + +static struct device_attribute st_attrs[] = { +#if defined(CONFIG_TOUCHSCREEN_FOCALTECH_SECURE_TOUCH_MMI) + __ATTR(secure_touch_enable, (S_IRUGO | S_IWUSR | S_IWGRP), + ft_secure_touch_enable_show, + ft_secure_touch_enable_store), + __ATTR(secure_touch, S_IRUGO, + ft_secure_touch_show, NULL), +#endif +}; + +static int ft_i2c_read(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +static int ft_i2c_write(struct i2c_client *client, char *writebuf, + int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +static int ft_write_reg(struct i2c_client *client, u8 addr, const u8 val) +{ + u8 buf[2] = {0}; + + buf[0] = addr; + buf[1] = val; + + return ft_i2c_write(client, buf, sizeof(buf)); +} + +static int ft_read_reg(struct i2c_client *client, u8 addr, u8 *val) +{ + return ft_i2c_read(client, &addr, 1, val, 1); +} + +static void ft_irq_disable(struct ft_ts_data *data) +{ + if (data->irq_enabled) { + disable_irq(data->client->irq); + data->irq_enabled = false; + } +} + +static void ft_irq_enable(struct ft_ts_data *data) +{ + if (!data->irq_enabled) { + enable_irq(data->client->irq); + data->irq_enabled = true; + } +} + +static const char *ft_find_vendor_name( + const struct ft_ts_platform_data *pdata, u8 id) +{ + int i; + + if (pdata->num_vendor_ids == 0) + return NULL; + + for (i = 0; i < pdata->num_vendor_ids; i++) + if (id == pdata->vendor_ids[i]) { + pr_info("vendor id 0x%02x panel supplier is %s\n", + id, pdata->vendor_names[i]); + return pdata->vendor_names[i]; + } + + return NULL; +} + +static void ft_update_fw_vendor_id(struct ft_ts_data *data) +{ + struct i2c_client *client = data->client; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_VENDOR_ID; + err = ft_i2c_read(client, ®_addr, 1, &data->fw_vendor_id, 1); + if (err < 0) + dev_err(&client->dev, "fw vendor id read failed"); + data->panel_supplier = ft_find_vendor_name(data->pdata, + data->fw_vendor_id); +} + +static void ft_update_fw_ver(struct ft_ts_data *data) +{ + struct i2c_client *client = data->client; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_VER; + err = ft_i2c_read(client, ®_addr, 1, &data->fw_ver[0], 1); + if (err < 0) + dev_err(&client->dev, "fw major version read failed"); + + reg_addr = FT_REG_FW_MIN_VER; + err = ft_i2c_read(client, ®_addr, 1, &data->fw_ver[1], 1); + if (err < 0) + dev_err(&client->dev, "fw minor version read failed"); + + reg_addr = FT_REG_FW_SUB_MIN_VER; + err = ft_i2c_read(client, ®_addr, 1, &data->fw_ver[2], 1); + if (err < 0) + dev_err(&client->dev, "fw sub minor version read failed"); + + dev_info(&client->dev, "Firmware version = %d.%d.%d\n", + data->fw_ver[0], data->fw_ver[1], data->fw_ver[2]); +} + +static void ft_update_fw_id(struct ft_ts_data *data) +{ + struct i2c_client *client = data->client; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_ID; + err = ft_i2c_read(client, ®_addr, 1, + &data->fw_id[0], FT_FW_ID_LEN); + if (err < 0) + dev_err(&client->dev, "fw id read failed"); + + dev_info(&client->dev, "Firmware id = 0x%02x%02x\n", + data->fw_id[0], data->fw_id[1]); +} + +static void ft_ud_fix_mismatch(struct ft_ts_data *data) +{ + int i; + struct input_dev *ip_dev = data->input_dev; + + for (i = 0; i < display_ud_stats.ud_len; i++) { + if (display_ud[i].mismatch) { + pr_debug("%s MISMATCH[%d]\n", display_ud_stats.name, i); + input_mt_slot(ip_dev, i); + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); + } + } +} + +static irqreturn_t ft_ts_interrupt(int irq, void *dev_id) +{ + struct ft_ts_data *data = dev_id; + struct input_dev *ip_dev; + int rc, i; + u32 id, x, y, status, num_touches; + u8 reg, *buf; + bool update_input = false; + + num_touches = 0; + + if (!data) { + pr_err("%s: Invalid data\n", __func__); + return IRQ_HANDLED; + } + + if (ft_filter_interrupt(data) == IRQ_HANDLED) + return IRQ_HANDLED; + + ip_dev = data->input_dev; + buf = data->tch_data; + + /* + * Read touch data start from register FT_REG_DEV_MODE. + * The touch x/y value start from FT_TOUCH_X_H/L_POS and + * FT_TOUCH_Y_H/L_POS in buf. + */ + reg = FT_REG_DEV_MODE; + rc = ft_i2c_read(data->client, ®, 1, buf, data->tch_data_len); + if (rc < 0) { + dev_err(&data->client->dev, "%s: read data fail\n", __func__); + return IRQ_HANDLED; + } + + for (i = 0; i < data->pdata->num_max_touches; i++) { + /* + * Getting the finger ID of the touch event incase of + * multiple touch events + */ + id = (buf[FT_TOUCH_ID_POS + FT_ONE_TCH_LEN * i]) >> 4; + if (id >= FT_MAX_ID) + break; + + update_input = true; + + x = (buf[FT_TOUCH_X_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_X_L_POS + FT_ONE_TCH_LEN * i]); + y = (buf[FT_TOUCH_Y_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_Y_L_POS + FT_ONE_TCH_LEN * i]); + + if (data->pdata->x_flip) + x = data->pdata->x_max - x; + if (data->pdata->y_flip) + y = data->pdata->y_max - y; + + status = buf[FT_TOUCH_EVENT_POS + FT_ONE_TCH_LEN * i] >> 6; + + num_touches = buf[FT_TD_STATUS] & FT_STATUS_NUM_TP_MASK; + + /* invalid combination */ + if (!num_touches && !status && !id) + break; + + if (data->clipping_on && data->clipa) { + bool inside; + + inside = (x >= data->clipa->xul_clip) && + (x <= data->clipa->xbr_clip) && + (y >= data->clipa->yul_clip) && + (y <= data->clipa->ybr_clip); + + if (inside == data->clipa->inversion) { + /* Touch might still be active, but we're */ + /* sending release anyway to avoid touch */ + /* stuck at the last reported position. */ + /* Driver will suppress reporting to UI */ + /* touch events from within clipping area */ + status = 1; + + dev_dbg(&data->client->dev, + "finger id-%d (%d,%d) within clipping area\n", + id, x, y); + } + } + + input_mt_slot(ip_dev, id); + if (status == FT_TOUCH_DOWN || status == FT_TOUCH_CONTACT) { + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1); + input_report_abs(ip_dev, ABS_MT_POSITION_X, x); + input_report_abs(ip_dev, ABS_MT_POSITION_Y, y); + } else { + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); + } + } + + /* report up event if FW missed to do */ + if (num_touches == 0) + ft_ud_fix_mismatch(data); + + if (update_input) { + input_mt_report_pointer_emulation(ip_dev, false); + input_sync(ip_dev); + } + + return IRQ_HANDLED; +} + +static int ft_gpio_configure(struct ft_ts_data *data, bool on) +{ + int err = 0; + + if (on) { + if (gpio_is_valid(data->pdata->irq_gpio)) { + err = gpio_request(data->pdata->irq_gpio, + "ft_irq_gpio"); + if (err) { + dev_err(&data->client->dev, + "irq gpio request failed"); + goto err_irq_gpio_req; + } + + err = gpio_direction_input(data->pdata->irq_gpio); + if (err) { + dev_err(&data->client->dev, + "set_direction for irq gpio failed\n"); + goto err_irq_gpio_dir; + } + } + + if (gpio_is_valid(data->pdata->reset_gpio)) { + err = gpio_request(data->pdata->reset_gpio, + "ft_reset_gpio"); + if (err) { + dev_err(&data->client->dev, + "reset gpio request failed"); + goto err_irq_gpio_dir; + } + + err = gpio_direction_output(data->pdata->reset_gpio, 0); + if (err) { + dev_err(&data->client->dev, + "set_direction for reset gpio failed\n"); + goto err_reset_gpio_dir; + } + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + + return 0; + } + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); + + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); +err_irq_gpio_dir: + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); +err_irq_gpio_req: + return err; +} + +static int ft_power_on(struct ft_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto power_off; + + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + return rc; + } + + rc = regulator_enable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c enable failed rc=%d\n", rc); + regulator_disable(data->vdd); + } + + return rc; + +power_off: + rc = regulator_disable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd disable failed rc=%d\n", rc); + return rc; + } + + if (!IS_ERR(data->vcc_i2c)) { + rc = regulator_disable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c disable failed rc=%d\n", rc); + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + } + } + } + + return rc; +} + +static int ft_power_init(struct ft_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto pwr_deinit; + + data->vdd = regulator_get(&data->client->dev, "vdd"); + if (IS_ERR(data->vdd)) { + rc = PTR_ERR(data->vdd); + dev_err(&data->client->dev, + "Regulator get failed vdd rc=%d\n", rc); + return rc; + } + + if (regulator_count_voltages(data->vdd) > 0) { + rc = regulator_set_voltage(data->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto reg_vdd_put; + } + } + + data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c"); + if (IS_ERR(data->vcc_i2c)) { + rc = PTR_ERR(data->vcc_i2c); + dev_err(&data->client->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + goto reg_vdd_set_vtg; + } + + if (regulator_count_voltages(data->vcc_i2c) > 0) { + rc = regulator_set_voltage(data->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto reg_vcc_i2c_put; + } + } + + return 0; + +reg_vcc_i2c_put: + regulator_put(data->vcc_i2c); +reg_vdd_set_vtg: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV); +reg_vdd_put: + regulator_put(data->vdd); + return rc; + +pwr_deinit: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV); + + regulator_put(data->vdd); + + if (regulator_count_voltages(data->vcc_i2c) > 0) + regulator_set_voltage(data->vcc_i2c, 0, FT_I2C_VTG_MAX_UV); + + regulator_put(data->vcc_i2c); + return 0; +} + +static int ft_ts_pinctrl_init(struct ft_ts_data *data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + data->ts_pinctrl = devm_pinctrl_get(&(data->client->dev)); + if (IS_ERR_OR_NULL(data->ts_pinctrl)) { + retval = PTR_ERR(data->ts_pinctrl); + dev_dbg(&data->client->dev, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + data->pinctrl_state_active + = pinctrl_lookup_state(data->ts_pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(data->pinctrl_state_active)) { + retval = PTR_ERR(data->pinctrl_state_active); + dev_err(&data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + data->pinctrl_state_suspend + = pinctrl_lookup_state(data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(data->pinctrl_state_suspend)) { + retval = PTR_ERR(data->pinctrl_state_suspend); + dev_err(&data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + data->pinctrl_state_release + = pinctrl_lookup_state(data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + retval = PTR_ERR(data->pinctrl_state_release); + dev_dbg(&data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(data->ts_pinctrl); +err_pinctrl_get: + data->ts_pinctrl = NULL; + return retval; +} + +static ssize_t ft_ud_stat(struct ft_ts_data *data, char *buf, ssize_t size) +{ + int i; + ssize_t total = 0; + + total += scnprintf(buf + total, size - total, "screen: "); + for (i = 0; i < display_ud_stats.ud_len; i++) + if (display_ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + display_ud[i].counter, + display_ud[i].mismatch); + else if (display_ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + display_ud[i].counter); + return total; +} + +#ifdef CONFIG_PM +static int ft_ts_start(struct device *dev) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + int err; + + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Cannot get active pinctrl state\n"); + } + + err = ft_gpio_configure(data, true); + if (err < 0) { + dev_err(&data->client->dev, + "failed to put gpios in resue state\n"); + goto err_gpio_configuration; + } + + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + usleep_range(800, 1000); + if (data->regulator_en) { + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } else { + err = ft_power_on(data, true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } + } + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + + msleep(data->pdata->soft_rst_dly); + + ft_irq_enable(data); + data->suspended = false; + + return 0; + +err_gpio_configuration: + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_suspend); + if (err < 0) + dev_err(dev, "Cannot get suspend pinctrl state\n"); + } + return err; +} + +static int ft_ts_stop(struct device *dev) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + char txbuf[2]; + static char ud_stats[PAGE_SIZE]; + int i, err; + + ft_irq_disable(data); + + /* print UD statistics */ + ft_ud_stat(data, ud_stats, sizeof(ud_stats)); + pr_info("%s\n", ud_stats); + /* release all touches */ + for (i = 0; i < data->pdata->num_max_touches; i++) { + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0); + } + input_mt_report_pointer_emulation(data->input_dev, false); + input_sync(data->input_dev); + + if (gpio_is_valid(data->pdata->reset_gpio)) { + txbuf[0] = FT_REG_PMODE; + txbuf[1] = FT_PMODE_HIBERNATE; + ft_i2c_write(data->client, txbuf, sizeof(txbuf)); + } + + if (data->regulator_en) { + if (data->pdata->power_on) { + err = data->pdata->power_on(false); + if (err) { + dev_err(dev, "power off failed"); + goto pwr_off_fail; + } + } else { + err = ft_power_on(data, false); + if (err) { + dev_err(dev, "power off failed"); + goto pwr_off_fail; + } + } + } + + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_suspend); + if (err < 0) + dev_err(dev, "Cannot get suspend pinctrl state\n"); + } + + err = ft_gpio_configure(data, false); + if (err < 0) { + dev_err(&data->client->dev, + "failed to put gpios in suspend state\n"); + goto gpio_configure_fail; + } + + data->suspended = true; + + return 0; + +gpio_configure_fail: + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Cannot get active pinctrl state\n"); + } + if (data->regulator_en) { + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) + dev_err(dev, "power on failed"); + } else { + err = ft_power_on(data, true); + if (err) + dev_err(dev, "power on failed"); + } + } +pwr_off_fail: + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + ft_irq_enable(data); + return err; +} + +static int ft_ts_suspend(struct device *dev) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + if (data->loading_fw) { + dev_info(dev, "Firmware loading in process...\n"); + return 0; + } + + if (data->suspended) { + dev_info(dev, "Already in suspend state\n"); + return 0; + } + + data->flash_enabled = false; + ft_secure_touch_stop(data, true); + + return ft_ts_stop(dev); +} + +static int ft_ts_resume(struct device *dev) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + int err; + + if (!data->suspended) { + dev_dbg(dev, "Already in awake state\n"); + return 0; + } + + data->flash_enabled = true; + ft_secure_touch_stop(data, true); + + err = ft_ts_start(dev); + if (err < 0) + return err; + + if (data->charger_detection_enabled) + ft_resume_ps_chg_cm_state(data); + + return 0; +} + +static const struct dev_pm_ops ft_ts_pm_ops = { +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) + .suspend = ft_ts_suspend, + .resume = ft_ts_resume, +#endif +}; + +#else +static int ft_ts_suspend(struct device *dev) +{ + return 0; +} + +static int ft_ts_resume(struct device *dev) +{ + return 0; +} + +#endif + +#if defined(CONFIG_FB) +static void fb_notify_resume_work(struct work_struct *work) +{ + struct ft_ts_data *data = + container_of(work, struct ft_ts_data, fb_notify_work); + ft_ts_resume(&data->client->dev); +} + +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *d) +{ + struct fb_event *evdata = d; + int *blank; + struct ft_ts_data *data = + container_of(self, struct ft_ts_data, fb_notif); + + if ((event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK) && + evdata && evdata->data && data && data->client) { + blank = evdata->data; + pr_debug("fb notification: event = %lu blank = %d\n", event, + *blank); + if (data->pdata->resume_in_workqueue) { + if (event == FB_EARLY_EVENT_BLANK) { + if (*blank != FB_BLANK_POWERDOWN) + return 0; + flush_work(&data->fb_notify_work); + ft_ts_suspend(&data->client->dev); + } else if (*blank == FB_BLANK_UNBLANK || + (*blank == FB_BLANK_NORMAL && + data->suspended)) { + schedule_work(&data->fb_notify_work); + } + } else { + if (event == FB_EARLY_EVENT_BLANK) { + if (*blank != FB_BLANK_POWERDOWN) + return 0; + ft_ts_suspend(&data->client->dev); + } else if (*blank == FB_BLANK_UNBLANK || + (*blank == FB_BLANK_NORMAL && + data->suspended)) { + ft_ts_resume(&data->client->dev); + } + } + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void ft_ts_early_suspend(struct early_suspend *handler) +{ + struct ft_ts_data *data = container_of(handler, + struct ft_ts_data, + early_suspend); + + /* + * During early suspend/late resume, the driver doesn't access xPU/SMMU + * protected HW resources. So, there is no compelling need to block, + * but notifying the userspace that a power event has occurred is + * enough. Hence 'blocking' variable can be set to false. + */ + ft_secure_touch_stop(data, false); + ft_ts_suspend(&data->client->dev); +} + +static void ft_ts_late_resume(struct early_suspend *handler) +{ + struct ft_ts_data *data = container_of(handler, + struct ft_ts_data, + early_suspend); + + ft_secure_touch_stop(data, false); + ft_ts_resume(&data->client->dev); +} +#endif + +static int ft_set_charger_state(struct ft_ts_data *data, + bool enable) +{ + int retval; + struct device *dev = &data->client->dev; + + /* disable IRQ to set CHARGER_STATE reg */ + ft_irq_disable(data); + if (enable) { + retval = ft_write_reg(data->client, + FT_REG_CHG_STATE, 1); + if (retval < 0) + dev_err(dev, "enable chg state failed(%d)\n", + retval); + else + dev_info(dev, "set chg state\n"); + } else { + retval = ft_write_reg(data->client, + FT_REG_CHG_STATE, 0); + if (retval < 0) + dev_err(dev, "enable chg state failed(%d)\n", + retval); + else + dev_info(dev, "unset chg state\n"); + } + ft_irq_enable(data); + + return (retval > 0) ? 0 : retval; +} + +static int ps_get_state(struct ft_ts_data *data, + struct power_supply *psy, bool *present) +{ + struct device *dev = &data->client->dev; + union power_supply_propval pval = {0}; + int retval; + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + dev_err(dev, "%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + dev_dbg(dev, "%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static void ft_update_ps_chg_cm_state( + struct ft_ts_data *data, bool present) +{ + struct config_modifier *cm = NULL; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == FT_MOD_CHARGER) { + cm->effective = present; + break; + } + } + up(&data->modifiers.list_sema); +} + +static void ft_resume_ps_chg_cm_state(struct ft_ts_data *data) +{ + struct config_modifier *cm = NULL; + bool is_active = false; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == FT_MOD_CHARGER) { + is_active = cm->effective; + break; + } + } + up(&data->modifiers.list_sema); + + /* FT_REG_CHG_STATE bit is 0 when powerup + * So only need set this bit when need to be set as 1 + */ + if (is_active) + ft_set_charger_state(data, true); +} + +static void ps_notify_callback_work(struct work_struct *work) +{ + struct ft_ts_data *data = + container_of(work, struct ft_ts_data, ps_notify_work); + bool present = data->ps_is_present; + struct device *dev = &data->client->dev; + int retval; + + /* enable IC if it in suspend state */ + if (data->suspended) { + dev_dbg(dev, "charger resumes tp ic\n"); + ft_ts_resume(&data->client->dev); + } + + retval = ft_set_charger_state(data, present); + if (retval) { + dev_err(dev, "set charger state failed rc=%d\n", retval); + return; + } + + ft_update_ps_chg_cm_state(data, present); +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + struct ft_ts_data *data = + container_of(self, struct ft_ts_data, ps_notif); + struct power_supply *psy = p; + struct device *dev = NULL; + bool present; + int retval; + + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb")) && data) { + dev = &data->client->dev; + dev_dbg(dev, "ps notification: event = %lu\n", event); + retval = ps_get_state(data, psy, &present); + if (retval) { + dev_err(dev, "psy get property failed\n"); + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (data->ps_is_present == present) { + dev_info(dev, "ps present state not change\n"); + return 0; + } + } + data->ps_is_present = present; + schedule_work(&data->ps_notify_work); + } + + return 0; +} + +static void ft_detection_work(struct work_struct *work) +{ + struct ft_exp_fn *exp_fhandler, *next_list_entry; + struct ft_ts_data *data; + int error; + bool det_again = false; + + mutex_lock(&exp_fn_ctrl_mutex); + data = exp_fn_ctrl.data_ptr; + mutex_unlock(&exp_fn_ctrl_mutex); + + if (data == NULL) { + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + return; + } + + mutex_lock(&exp_fn_ctrl.list_mutex); + if (list_empty(&exp_fn_ctrl.fn_list)) + goto release_mutex; + + list_for_each_entry_safe(exp_fhandler, + next_list_entry, + &exp_fn_ctrl.fn_list, + link) { + if (exp_fhandler->func_init == NULL) { + if (exp_fhandler->inserted == true) { + if (exp_fhandler->func_init != NULL) + exp_fhandler->func_remove(data); + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + } + continue; + } + + if (exp_fhandler->inserted == true) + continue; + + error = exp_fhandler->func_init(data); + if (error < 0) { + pr_err("Failed to init exp function. err=%d\n", error); + det_again = true; + continue; + } + exp_fhandler->inserted = true; + } + +release_mutex: + mutex_unlock(&exp_fn_ctrl.list_mutex); + if (det_again) { + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + } +} + +void ft_new_function(bool insert, + int (*func_init)(struct ft_ts_data *data), + void (*func_remove)(struct ft_ts_data *data), + void (*func_attn)(struct ft_ts_data *data, + unsigned char intr_mask)) +{ + struct ft_exp_fn *exp_fhandler; + + mutex_lock(&exp_fn_ctrl_mutex); + if (!exp_fn_ctrl.inited) { + mutex_init(&exp_fn_ctrl.list_mutex); + INIT_LIST_HEAD(&exp_fn_ctrl.fn_list); + exp_fn_ctrl.det_workqueue = + create_singlethread_workqueue("rmi_det_workqueue"); + if (IS_ERR_OR_NULL(exp_fn_ctrl.det_workqueue)) + pr_err("unable to create a workqueue\n"); + INIT_DELAYED_WORK(&exp_fn_ctrl.det_work, + ft_detection_work); + exp_fn_ctrl.inited = true; + } + mutex_unlock(&exp_fn_ctrl_mutex); + + mutex_lock(&exp_fn_ctrl.list_mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("failed to alloc mem for expansion function\n"); + goto exit; + } + exp_fhandler->func_init = func_init; + exp_fhandler->func_attn = func_attn; + exp_fhandler->func_remove = func_remove; + exp_fhandler->inserted = false; + list_add_tail(&exp_fhandler->link, &exp_fn_ctrl.fn_list); + } else { + list_for_each_entry(exp_fhandler, &exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->func_init == func_init) { + exp_fhandler->inserted = false; + exp_fhandler->func_init = NULL; + exp_fhandler->func_attn = NULL; + goto exit; + } + } + } + +exit: + mutex_unlock(&exp_fn_ctrl.list_mutex); + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, 0); +} +EXPORT_SYMBOL(ft_new_function); + +static int fps_notifier_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + int fps_state = *(int *)p; + struct ft_ts_data *data = + container_of(self, struct ft_ts_data, fps_notif); + + if (data && event == 0xBEEF && data->client) { + struct config_modifier *cm = + ft_modifier_by_id(data, FT_MOD_FPS); + if (!cm) { + dev_err(&data->client->dev, + "No FPS modifier found\n"); + goto done; + } + + if (fps_state) {/* on */ + data->clipping_on = true; + data->clipa = cm->clipa; + cm->effective = true; + } else {/* off */ + data->clipping_on = false; + data->clipa = NULL; + cm->effective = false; + } + pr_info("FPS: clipping is %s\n", + data->clipping_on ? "ON" : "OFF"); + } +done: + return 0; +} + +static int ft_fps_register_init(struct ft_ts_data *data) +{ + int retval; + + retval = FPS_register_notifier(&data->fps_notif, 0xBEEF, false); + if (retval < 0) { + dev_err(&data->client->dev, + "Failed to register fps_notifier: %d\n", retval); + return retval; + } + data->is_fps_registered = true; + dev_info(&data->client->dev, + "Register fps_notifier OK\n"); + + return 0; +} + +static int ft_auto_cal(struct i2c_client *client) +{ + struct ft_ts_data *data = i2c_get_clientdata(client); + u8 temp = 0, i; + + /* set to factory mode */ + msleep(2 * data->pdata->soft_rst_dly); + ft_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); + msleep(data->pdata->soft_rst_dly); + + /* start calibration */ + ft_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START); + msleep(2 * data->pdata->soft_rst_dly); + for (i = 0; i < FT_CAL_RETRY; i++) { + ft_read_reg(client, FT_REG_CAL, &temp); + /* return to normal mode, calibration finish */ + if (((temp & FT_CAL_MASK) >> FT_4BIT_SHIFT) == FT_CAL_FIN) + break; + } + + /*calibration OK */ + msleep(2 * data->pdata->soft_rst_dly); + ft_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); + msleep(data->pdata->soft_rst_dly); + + /* store calibration data */ + ft_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE); + msleep(2 * data->pdata->soft_rst_dly); + + /* set to normal mode */ + ft_write_reg(client, FT_REG_DEV_MODE, FT_WORKMODE_VALUE); + msleep(2 * data->pdata->soft_rst_dly); + + return 0; +} + +static int ft_fw_upgrade_start(struct i2c_client *client, + const u8 *data, u32 data_len) +{ + struct ft_ts_data *ts_data = i2c_get_clientdata(client); + struct fw_upgrade_info info = ts_data->pdata->info; + u8 reset_reg; + u8 w_buf[FT_MAX_WR_BUF] = {0}, r_buf[FT_MAX_RD_BUF] = {0}; + u8 pkt_buf[FT_FW_PKT_LEN + FT_FW_PKT_META_LEN]; + int i, j, temp; + u32 pkt_num, pkt_len; + u8 is_5336_new_bootloader = false; + u8 is_5336_fwsize_30 = false; + u8 fw_ecc; + + /* determine firmware size */ + if (*(data + data_len - FT_BLOADER_SIZE_OFF) == FT_BLOADER_NEW_SIZE) + is_5336_fwsize_30 = true; + else + is_5336_fwsize_30 = false; + + for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) { + msleep(FT_EARSE_DLY_MS); + /* reset - write 0xaa and 0x55 to reset register */ + if (ts_data->family_id == FT6X06_ID + || ts_data->family_id == FT6X36_ID) + reset_reg = FT_RST_CMD_REG2; + else + reset_reg = FT_RST_CMD_REG1; + + ft_write_reg(client, reset_reg, FT_UPGRADE_AA); + msleep(info.delay_aa); + + ft_write_reg(client, reset_reg, FT_UPGRADE_55); + if (i <= (FT_UPGRADE_LOOP / 2)) + msleep(info.delay_55 + i * 3); + else + msleep(info.delay_55 - (i - (FT_UPGRADE_LOOP / 2)) * 2); + + /* Set i2c to std i2c mode */ + w_buf[0] = FT_REG_I2C_MODE; + w_buf[1] = FT_VAL_I2C_MODE; + w_buf[2] = FT_VAL_I2C_MODE_STD; + ft_i2c_read(client, w_buf, 3, r_buf, 3); + if (r_buf[0] != FT_REG_I2C_MODE || + r_buf[1] != FT_VAL_I2C_MODE || + r_buf[2] != FT_VAL_I2C_MODE_STD_CFM) { + dev_err(&client->dev, + "set std i2c error. r_val = 0x%02x%02x%02x\n", + r_buf[0], r_buf[1], r_buf[2]); + continue; + } + + /* Enter upgrade mode */ + w_buf[0] = FT_UPGRADE_55; + ft_i2c_write(client, w_buf, 1); + usleep_range(FT_55_AA_DLY_NS, FT_55_AA_DLY_NS + 1); + w_buf[0] = FT_UPGRADE_AA; + ft_i2c_write(client, w_buf, 1); + + /* check READ_ID */ + msleep(info.delay_readid); + w_buf[0] = FT_READ_ID_REG; + w_buf[1] = 0x00; + w_buf[2] = 0x00; + w_buf[3] = 0x00; + + ft_i2c_read(client, w_buf, 4, r_buf, 2); + + if (r_buf[0] != info.upgrade_id_1 + || r_buf[1] != info.upgrade_id_2) { + dev_err(&client->dev, "Upgrade ID mismatch(%d), IC=0x%x 0x%x, info=0x%x 0x%x\n", + i, r_buf[0], r_buf[1], + info.upgrade_id_1, info.upgrade_id_2); + } else + break; + } + + if (i >= FT_UPGRADE_LOOP) { + dev_err(&client->dev, "Abort upgrade\n"); + return -EIO; + } + + w_buf[0] = 0xcd; + ft_i2c_read(client, w_buf, 1, r_buf, 1); + + if (r_buf[0] <= 4) + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + else if (r_buf[0] == 7) + is_5336_new_bootloader = FT_BLOADER_VERSION_Z7; + else if (r_buf[0] >= 0x0f && + ((ts_data->family_id == FT_FT5336_FAMILY_ID_0x11) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x12) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x13) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x14))) + is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; + else + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + + dev_dbg(&client->dev, "bootloader type=%d, r_buf=0x%x, family_id=0x%x\n", + is_5336_new_bootloader, r_buf[0], ts_data->family_id); + /* is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; */ + + /* erase app and panel paramenter area */ + w_buf[0] = FT_ERASE_APP_REG; + ft_i2c_write(client, w_buf, 1); + msleep(info.delay_erase_flash); + + if (is_5336_fwsize_30) { + w_buf[0] = FT_ERASE_PANEL_REG; + ft_i2c_write(client, w_buf, 1); + } + msleep(FT_EARSE_DLY_MS); + + w_buf[0] = FT_REG_FW_LEN; + w_buf[1] = (u8)((data_len>>16) & 0xff); + w_buf[2] = (u8)((data_len>>8) & 0xff); + w_buf[3] = (u8)((data_len) & 0xff); + ft_i2c_write(client, w_buf, 4); + + pkt_num = (data_len) / FT_FW_PKT_LEN; + pkt_len = FT_FW_PKT_LEN; + pkt_buf[0] = FT_FW_START_REG; + pkt_buf[1] = 0x00; + fw_ecc = 0; + + for (i = 0; i < pkt_num; i++) { + temp = i * FT_FW_PKT_LEN; + pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[3] = (u8) temp; + pkt_buf[4] = (u8) (pkt_len >> FT_8BIT_SHIFT); + pkt_buf[5] = (u8) pkt_len; + + for (j = 0; j < FT_FW_PKT_LEN; j++) { + pkt_buf[6 + j] = data[i * FT_FW_PKT_LEN + j]; + fw_ecc ^= pkt_buf[6 + j]; + } + + ft_i2c_write(client, pkt_buf, + FT_FW_PKT_LEN + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + + /* send remaining bytes */ + if ((data_len) % FT_FW_PKT_LEN > 0) { + temp = pkt_num * FT_FW_PKT_LEN; + pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[3] = (u8) temp; + temp = (data_len) % FT_FW_PKT_LEN; + pkt_buf[4] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[5] = (u8) temp; + + for (i = 0; i < temp; i++) { + pkt_buf[6 + i] = data[pkt_num * FT_FW_PKT_LEN + i]; + fw_ecc ^= pkt_buf[6 + i]; + } + + ft_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + + /* send the finishing packet */ + if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4 || + is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) { + for (i = 0; i < FT_FINISHING_PKT_LEN_OLD_FW; i++) { + if (is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) + temp = FT_MAGIC_BLOADER_Z7 + i; + else if (is_5336_new_bootloader == + FT_BLOADER_VERSION_LZ4) + temp = FT_MAGIC_BLOADER_LZ4 + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + } else if (is_5336_new_bootloader == FT_BLOADER_VERSION_GZF) { + for (i = 0; i < FT_FINISHING_PKT_LEN_NEW_FW; i++) { + if (is_5336_fwsize_30) + temp = FT_MAGIC_BLOADER_GZF_30 + i; + else + temp = FT_MAGIC_BLOADER_GZF + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + + } + } + + /* verify checksum */ + w_buf[0] = FT_REG_ECC; + ft_i2c_read(client, w_buf, 1, r_buf, 1); + if (r_buf[0] != fw_ecc) { + dev_err(&client->dev, "ECC error! dev_ecc=%02x fw_ecc=%02x\n", + r_buf[0], fw_ecc); + return -EIO; + } + + /* reset */ + w_buf[0] = FT_REG_RESET_FW; + ft_i2c_write(client, w_buf, 1); + msleep(ts_data->pdata->soft_rst_dly); + + dev_info(&client->dev, "Firmware upgrade successful\n"); + + return 0; +} + +static int ft_fw_upgrade(struct device *dev, bool force) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + const struct firmware *fw = NULL; + int rc; + u8 fw_file_maj, fw_file_min, fw_file_sub_min, fw_file_vendor_id; + bool fw_upgrade = false; + + if (data->suspended) { + dev_err(dev, "Device is in suspend state: Exit FW upgrade\n"); + return -EBUSY; + } + + rc = request_firmware(&fw, data->fw_name, dev); + if (rc < 0) { + dev_err(dev, "Request firmware failed - %s (%d)\n", + data->fw_name, rc); + return rc; + } + + if (fw->size < FT_FW_MIN_SIZE || fw->size > FT_FW_MAX_SIZE) { + dev_err(dev, "Invalid firmware size (%zu)\n", fw->size); + rc = -EIO; + goto rel_fw; + } + + if (data->family_id == FT6X36_ID) { + fw_file_maj = FT_FW_FILE_MAJ_VER_FT6X36(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID_FT6X36(fw); + } else { + fw_file_maj = FT_FW_FILE_MAJ_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); + } + fw_file_min = FT_FW_FILE_MIN_VER(fw); + fw_file_sub_min = FT_FW_FILE_SUB_MIN_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); + + dev_info(dev, "Current firmware: %d.%d.%d", data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj, + fw_file_min, fw_file_sub_min); + + if (force) + fw_upgrade = true; + else if ((data->fw_ver[0] < fw_file_maj) && + data->fw_vendor_id == fw_file_vendor_id) + fw_upgrade = true; + + if (!fw_upgrade) { + dev_info(dev, "Exiting fw upgrade...\n"); + rc = -EFAULT; + goto rel_fw; + } + + /* start firmware upgrade */ + if (FT_FW_CHECK(fw, data)) { + rc = ft_fw_upgrade_start(data->client, fw->data, fw->size); + if (rc < 0) + dev_err(dev, "update failed (%d). try later...\n", rc); + else if (data->pdata->info.auto_cal) + ft_auto_cal(data->client); + } else { + dev_err(dev, "FW format error\n"); + rc = -EIO; + } + + ft_update_fw_ver(data); + ft_update_fw_id(data); + + FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_vkey_support ? "yes" : "no", + data->pdata->fw_name, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); +rel_fw: + release_firmware(fw); + return rc; +} + +static ssize_t ft_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + bool val; + + mutex_lock(&data->input_dev->mutex); + val = data->suspended; + mutex_unlock(&data->input_dev->mutex); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + val == false && + data->flash_enabled); +} + +static DEVICE_ATTR(poweron, 0444, ft_poweron_show, NULL); + +static ssize_t ft_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->pdata->name); +} + +static DEVICE_ATTR(productinfo, 0444, ft_productinfo_show, NULL); + +static ssize_t ft_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + data->force_reflash = (input == 0) ? false : true; + + return count; +} + +static DEVICE_ATTR(forcereflash, 0220, NULL, ft_force_reflash_store); + +static ssize_t ft_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + (data->loading_fw) ? 1 : 0); +} + +static DEVICE_ATTR(flashprog, 0444, ft_flashprog_show, NULL); + +static ssize_t ft_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + int retval; + char prefix[FT_FW_NAME_MAX_LEN] = "focaltech"; + char template[FT_FW_NAME_MAX_LEN]; + + if (count > FT_FW_NAME_MAX_LEN) { + dev_err(&data->client->dev, + "%s: FW filename is too long\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (data->suspended) { + dev_err(&data->client->dev, + "%s: In suspend state, try again later\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (data->loading_fw) { + dev_err(&data->client->dev, + "%s: In FW flashing state, try again later\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!data->force_reflash) { + if (strncmp(buf, prefix, + strnlen(prefix, sizeof(prefix)))) { + dev_err(&data->client->dev, + "%s: FW does not belong to Focaltech\n", + __func__); + retval = -EINVAL; + goto exit; + } + + snprintf(template, sizeof(template), "-%s-", + data->pdata->name); + if (!strnstr(buf + strnlen(prefix, sizeof(prefix)), template, + count)) { + dev_err(&data->client->dev, + "%s: FW does not belong to %s\n", + __func__, + data->pdata->name); + retval = -EINVAL; + goto exit; + } + } + + strlcpy(data->fw_name, buf, count); + dev_dbg(&data->client->dev, + "%s: FW filename: %s\n", + __func__, + data->fw_name); + + mutex_lock(&data->input_dev->mutex); + ft_irq_disable(data); + data->loading_fw = true; + if(data->pdata->fw_update_mmi) { + retval = fts_ctpm_auto_upgrade(data->client, + data->fw_name, + data->pdata); + } else { + retval = ft_fw_upgrade(dev, true); + } + + if (retval) + dev_err(&data->client->dev, + "%s: FW %s upgrade failed\n", + __func__, + data->pdata->name); + ft_irq_enable(data); + data->loading_fw = false; + mutex_unlock(&data->input_dev->mutex); + + retval = count; +exit: + data->fw_name[0] = 0; + return retval; +} + +static DEVICE_ATTR(doreflash, 0220, NULL, ft_do_reflash_store); + +static ssize_t ft_build_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%02x%02x-%02x\n", + data->fw_id[0], data->fw_id[1], data->fw_ver[0]); +} + +static DEVICE_ATTR(buildid, 0444, ft_build_id_show, NULL); + +static ssize_t ft_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + struct ft_ts_data *data = dev_get_drvdata(dev); + + if ('1' != buf[0]) { + pr_err("Invalid argument for reset\n"); + return -EINVAL; + } + + mutex_lock(&data->input_dev->mutex); + ft_irq_disable(data); + retval = gpio_direction_output(data->pdata->reset_gpio, 0); + if (retval == 0) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } else { + dev_err(&data->client->dev, + "set direction for reset gpio failed\n"); + } + msleep(100); + ft_irq_enable(data); + mutex_unlock(&data->input_dev->mutex); + return count; +} + +static DEVICE_ATTR(reset, 0220, NULL, ft_reset_store); + +static ssize_t ft_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->irq_enabled ? "ENABLED" : "DISABLED"); +} + +static ssize_t ft_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + if (data->panel_supplier) + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->panel_supplier); + return 0; +} +static DEVICE_ATTR(panel_supplier, 0444, ft_panel_supplier_show, NULL); + +static ssize_t ft_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct ft_ts_data *data = dev_get_drvdata(dev); + + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + switch (value) { + case 0: + /* Disable irq */ + ft_irq_disable(data); + break; + case 1: + /* Enable irq */ + ft_irq_enable(data); + break; + default: + pr_err("Invalid value\n"); + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR(drv_irq, 0664, ft_drv_irq_show, ft_drv_irq_store); + +static ssize_t ft_ud_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + ssize_t total = 0; + + total += scnprintf(buf + total, PAGE_SIZE - total, "display:\n"); + for (i = 0; i < display_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + display_ud[i].counter, + display_ud[i].mismatch); + + return total; +} + +static DEVICE_ATTR(tsi, 0444, ft_ud_show, NULL); + +static const struct attribute *ft_attrs[] = { + &dev_attr_poweron.attr, + &dev_attr_productinfo.attr, + &dev_attr_forcereflash.attr, + &dev_attr_flashprog.attr, + &dev_attr_doreflash.attr, + &dev_attr_buildid.attr, + &dev_attr_reset.attr, + &dev_attr_panel_supplier.attr, + &dev_attr_drv_irq.attr, + &dev_attr_tsi.attr, + NULL +}; + +static ssize_t ts_info_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + strlcpy(buf, ts_info_buff, FT_INFO_MAX_LEN); + return strnlen(buf, FT_INFO_MAX_LEN); +} +static struct kobj_attribute ts_info_attr = __ATTR_RO(ts_info); + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + pr_err("cannot get ft_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&data->client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "focaltech"); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int ft_ts_sysfs_class(void *_data, bool create) +{ + struct ft_ts_data *data = _data; + const struct ft_ts_platform_data *pdata = data->pdata; + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(data->client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + data, pdata->name); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} + +static bool ft_debug_addr_is_valid(int addr) +{ + if (addr < 0 || addr > 0xFF) { + pr_err("FT reg address is invalid: 0x%x\n", addr); + return false; + } + + return true; +} + +static int ft_debug_data_set(void *_data, u64 val) +{ + struct ft_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft_debug_addr_is_valid(data->addr)) + dev_info(&data->client->dev, + "Writing into FT registers not supported\n"); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int ft_debug_data_get(void *_data, u64 *val) +{ + struct ft_ts_data *data = _data; + int rc; + u8 reg = 0; + + mutex_lock(&data->input_dev->mutex); + + if (ft_debug_addr_is_valid(data->addr)) { + rc = ft_read_reg(data->client, data->addr, ®); + if (rc < 0) + dev_err(&data->client->dev, + "FT read register 0x%x failed (%d)\n", + data->addr, rc); + else + *val = reg; + } + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, ft_debug_data_get, + ft_debug_data_set, "0x%02llX\n"); + +static int ft_debug_addr_set(void *_data, u64 val) +{ + struct ft_ts_data *data = _data; + + if (ft_debug_addr_is_valid(val)) { + mutex_lock(&data->input_dev->mutex); + data->addr = val; + mutex_unlock(&data->input_dev->mutex); + } + + return 0; +} + +static int ft_debug_addr_get(void *_data, u64 *val) +{ + struct ft_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft_debug_addr_is_valid(data->addr)) + *val = data->addr; + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, ft_debug_addr_get, + ft_debug_addr_set, "0x%02llX\n"); + +static int ft_debug_suspend_set(void *_data, u64 val) +{ + struct ft_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (val) + ft_ts_suspend(&data->client->dev); + else + ft_ts_resume(&data->client->dev); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int ft_debug_suspend_get(void *_data, u64 *val) +{ + struct ft_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + *val = data->suspended; + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, ft_debug_suspend_get, + ft_debug_suspend_set, "%lld\n"); + +static int ft_debug_dump_info(struct seq_file *m, void *v) +{ + struct ft_ts_data *data = m->private; + + seq_printf(m, "%s\n", data->ts_info); + + return 0; +} + +static int debugfs_dump_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, ft_debug_dump_info, inode->i_private); +} + +static const struct file_operations debug_dump_info_fops = { + .owner = THIS_MODULE, + .open = debugfs_dump_info_open, + .read = seq_read, + .release = single_release, +}; + +static ssize_t ft_proc_read(struct file *filp, char __user *buff, + size_t count, loff_t *ppos) +{ + unsigned char *read_buf = NULL; + size_t read_buf_len = (count > PAGE_SIZE) ? count : PAGE_SIZE; + u8 regval = 0x00; + u8 regaddr = 0x00; + struct ft_ts_data *data = PDE_DATA(filp->f_inode); + int num_read_chars = 0; + int retval; + + if (data == NULL) { + pr_err("%s: cannot get ts data\n", __func__); + return -EFAULT; + } + read_buf = kmalloc(read_buf_len, GFP_KERNEL); + if (read_buf == NULL) + return -ENOMEM; + + mutex_lock(&data->input_dev->mutex); + switch (data->proc_operate_mode) { + case PROC_UPGRADE: + regaddr = FT_REG_FW_VER; + retval = ft_read_reg(data->client, + regaddr, ®val); + if (retval < 0) + num_read_chars = snprintf(read_buf, read_buf_len, + "get fw version failed.\n"); + else + num_read_chars = snprintf(read_buf, read_buf_len, + "current fw version:0x%02x\n", regval); + break; + case PROC_READ_REGISTER: + retval = ft_i2c_read(data->client, NULL, 0, read_buf, 1); + if (retval < 0) + dev_err(&data->client->dev, + "%s: read I2C error\n", __func__); + num_read_chars = 1; + break; + case PROC_READ_DATA: + retval = ft_i2c_read(data->client, + NULL, 0, read_buf, count); + if (retval < 0) + dev_err(&data->client->dev, + "%s: read I2C error\n", __func__); + num_read_chars = count; + break; + default: + break; + } + + mutex_unlock(&data->input_dev->mutex); + + if (copy_to_user(buff, read_buf, num_read_chars)) { + dev_err(&data->client->dev, + "%s: copy to user error\n", __func__); + kfree(read_buf); + return -EFAULT; + } + kfree(read_buf); + + return num_read_chars; +} + +static ssize_t ft_proc_write(struct file *filp, const char __user *buff, + size_t count, loff_t *ppos) +{ + unsigned char *write_buf = NULL; + size_t write_buf_len = count; + struct ft_ts_data *data = PDE_DATA(filp->f_inode); + int writelen = 0; + int retval; + + if (data == NULL) { + pr_err("%s: cannot get ts data\n", __func__); + return -EFAULT; + } + write_buf = kmalloc(write_buf_len, GFP_KERNEL); + if (write_buf == NULL) + return -ENOMEM; + + if (copy_from_user(write_buf, buff, count)) { + dev_err(&data->client->dev, + "%s: copy from user error\n", __func__); + kfree(write_buf); + return -EFAULT; + } + + mutex_lock(&data->input_dev->mutex); + data->proc_operate_mode = write_buf[0]; + switch (data->proc_operate_mode) { + case PROC_READ_REGISTER: + retval = ft_i2c_write(data->client, write_buf + 1, 1); + if (retval < 0) + dev_err(&data->client->dev, + "%s: write I2C error\n", __func__); + break; + case PROC_WRITE_REGISTER: + retval = ft_i2c_write(data->client, write_buf + 1, 2); + if (retval < 0) + dev_err(&data->client->dev, + "%s: write I2C error\n", __func__); + break; + case PROC_READ_DATA: + case PROC_WRITE_DATA: + writelen = count - 1; + if (writelen > 0) { + retval = ft_i2c_write(data->client, + write_buf + 1, writelen); + if (retval < 0) + dev_err(&data->client->dev, + "%s: write I2C error\n", __func__); + } + break; + default: + dev_err(&data->client->dev, + "%s: unsupport opt %d\n", + __func__, data->proc_operate_mode); + break; + } + + mutex_unlock(&data->input_dev->mutex); + kfree(write_buf); + + return count; +} + +static const struct file_operations ft_proc_ops = { + .owner = THIS_MODULE, + .read = ft_proc_read, + .write = ft_proc_write +}; + +static int ft_create_proc_entry(struct ft_ts_data *data) +{ + struct proc_dir_entry *entry = data->proc_entry; + struct i2c_client *client = data->client; + + entry = proc_create_data(FT_PROC_DIR_NAME, + 0664, + NULL, + &ft_proc_ops, + data); + if (entry == NULL) { + dev_err(&client->dev, "Couldn't create proc entry\n"); + return -ENOMEM; + } + dev_info(&client->dev, "Create proc entry success\n"); + + return 0; +} + +static void ft_remove_proc_entry(struct ft_ts_data *data) +{ + remove_proc_entry(FT_PROC_DIR_NAME, NULL); +} + +#ifdef CONFIG_OF +static int ft_get_dt_coords(struct device *dev, char *name, + struct ft_ts_platform_data *pdata) +{ + u32 coords[FT_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size, rc; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != FT_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read %s\n", name); + return rc; + } + + if (!strcmp(name, "focaltech,panel-coords")) { + pdata->panel_minx = coords[0]; + pdata->panel_miny = coords[1]; + pdata->panel_maxx = coords[2]; + pdata->panel_maxy = coords[3]; + } else if (!strcmp(name, "focaltech,display-coords")) { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } else { + dev_err(dev, "unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +/* ASCII names order MUST match enum */ +static const char const *ascii_names[] = {"charger", "fps", "na"}; + +static int ft_modifier_name2id(const char *name) +{ + int i, len2cmp, chosen = -1; + + for (i = 0; i < FT_MOD_MAX; i++) { + len2cmp = min_t(int, strlen(name), strlen(ascii_names[i])); + if (!strncmp(name, ascii_names[i], len2cmp)) { + chosen = i; + break; + } + } + return chosen; +} + +static struct config_modifier *ft_modifier_by_id( + struct ft_ts_data *data, int id) +{ + struct config_modifier *cm, *found = NULL; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + pr_debug("walk-thru: ptr=%p modifier[%s] id=%d\n", + cm, cm->name, cm->id); + if (cm->id == id) { + found = cm; + break; + } + } + up(&data->modifiers.list_sema); + pr_debug("returning modifier id=%d[%d]\n", found ? found->id : -1, id); + return found; +} + +static void ft_dt_parse_modifier(struct ft_ts_data *data, + struct device_node *parent, struct config_modifier *config, + const char *modifier_name, bool active) +{ + struct device *dev = &data->client->dev; + char node_name[64]; + struct ft_clip_area clipa; + struct device_node *np_config; + int err; + + scnprintf(node_name, 63, "%s-%s", modifier_name, + active ? "active" : "suspended"); + np_config = of_find_node_by_name(parent, node_name); + if (!np_config) { + dev_dbg(dev, "%s: node does not exist\n", node_name); + return; + } + + err = of_property_read_u32_array(np_config, "touch-clip-area", + (unsigned int *)&clipa, sizeof(clipa)/sizeof(unsigned int)); + if (!err) { + config->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!config->clipa) { + dev_err(dev, "clip area allocation failure\n"); + return; + } + memcpy(config->clipa, &clipa, sizeof(clipa)); + pr_notice("using touch clip area in %s\n", node_name); + } + + of_node_put(np_config); +} + +static int ft_dt_parse_modifiers(struct ft_ts_data *data) +{ + struct device *dev = &data->client->dev; + struct device_node *np = dev->of_node; + struct device_node *np_mod; + int i, num_names, ret = 0; + char node_name[64]; + static const char **modifiers_names; + + sema_init(&data->modifiers.list_sema, 1); + INIT_LIST_HEAD(&data->modifiers.mod_head); + data->modifiers.mods_num = 0; + + num_names = of_property_count_strings(np, "config_modifier-names"); + if (num_names < 0) { + dev_err(dev, "Cannot parse config_modifier-names: %d\n", + num_names); + return -ENODEV; + } + + modifiers_names = devm_kzalloc(dev, + sizeof(*modifiers_names) * num_names, GFP_KERNEL); + if (!modifiers_names) + return -ENOMEM; + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "config_modifier-names", + i, &modifiers_names[i]); + if (ret < 0) { + dev_err(dev, "Cannot parse modifier-names: %d\n", ret); + return ret; + } + } + + data->modifiers.mods_num = num_names; + + for (i = 0; i < num_names; i++) { + int id; + struct config_modifier *cm, *config; + + scnprintf(node_name, 63, "config_modifier-%s", + modifiers_names[i]); + np_mod = of_find_node_by_name(np, node_name); + if (!np_mod) { + dev_warn(dev, "cannot find modifier node %s\n", + node_name); + continue; + } + + /* check for duplicate nodes in devtree */ + id = ft_modifier_name2id(modifiers_names[i]); + dev_info(dev, "processing modifier %s[%d]\n", node_name, id); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == id) { + dev_err(dev, "duplicate modifier node %s\n", + node_name); + return -EFAULT; + } + } + /* allocate modifier's structure */ + config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + list_add_tail(&config->link, &data->modifiers.mod_head); + config->name = modifiers_names[i]; + config->id = id; + + if (of_property_read_bool(np_mod, "enable-notification")) { + switch (id) { + case FT_MOD_CHARGER: + pr_notice("using charger detection\n"); + data->charger_detection_enabled = true; + break; + case FT_MOD_FPS: + pr_notice("using fingerprint sensor detection\n"); + data->fps_detection_enabled = true; + break; + default: + pr_notice("no notification found\n"); + break; + } + } else { + config->effective = true; + dev_dbg(dev, "modifier %s enabled unconditionally\n", + node_name); + } + + dev_dbg(dev, "processing modifier %s[%d]\n", + node_name, config->id); + + ft_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], true); + ft_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], false); + + of_node_put(np_mod); + } + + return 0; +} + +static int ft_parse_dt(struct device *dev, + struct ft_ts_platform_data *pdata) +{ + int rc; + struct device_node *np = dev->of_node; + struct property *prop; + u32 temp_val, num_buttons; + u32 button_map[MAX_BUTTONS]; + u32 num_vendor_ids, i; + + pdata->name = "focaltech"; + rc = of_property_read_string(np, "focaltech,name", &pdata->name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read name\n"); + return rc; + } + + rc = ft_get_dt_coords(dev, "focaltech,panel-coords", pdata); + if (rc && (rc != -EINVAL)) + return rc; + + rc = ft_get_dt_coords(dev, "focaltech,display-coords", pdata); + if (rc) + return rc; + + pdata->i2c_pull_up = of_property_read_bool(np, + "focaltech,i2c-pull-up"); + + pdata->no_force_update = of_property_read_bool(np, + "focaltech,no-force-update"); + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio", + 0, &pdata->reset_gpio_flags); + if (pdata->reset_gpio < 0) + return pdata->reset_gpio; + + pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", + 0, &pdata->irq_gpio_flags); + if (pdata->irq_gpio < 0) + return pdata->irq_gpio; + + pdata->fw_name = "ft_fw.bin"; + rc = of_property_read_string(np, "focaltech,fw-name", &pdata->fw_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw name\n"); + return rc; + } + + rc = of_property_read_u32(np, "focaltech,group-id", &temp_val); + if (!rc) + pdata->group_id = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,hard-reset-delay-ms", + &temp_val); + if (!rc) + pdata->hard_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,soft-reset-delay-ms", + &temp_val); + if (!rc) + pdata->soft_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,num-max-touches", &temp_val); + if (!rc) + pdata->num_max_touches = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,fw-delay-aa-ms", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay aa\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_aa = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-55-ms", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay 55\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_55 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-upgrade-id1", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw upgrade id1\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.upgrade_id_1 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-upgrade-id2", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw upgrade id2\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.upgrade_id_2 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-readid-ms", + &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay read id\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_readid = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-era-flsh-ms", + &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay erase flash\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_erase_flash = temp_val; + + pdata->info.auto_cal = of_property_read_bool(np, + "focaltech,fw-auto-cal"); + + pdata->fw_vkey_support = of_property_read_bool(np, + "focaltech,fw-vkey-support"); + + pdata->ignore_id_check = of_property_read_bool(np, + "focaltech,ignore-id-check"); + + pdata->gesture_support = of_property_read_bool(np, + "focaltech,gesture-support"); + + pdata->resume_in_workqueue = of_property_read_bool(np, + "focaltech,resume-in-workqueue"); + + if (of_property_read_bool(np, "focaltech,fw-update-mmi")) { + pr_notice("using mmi firmware update\n"); + pdata->fw_update_mmi = true; + } + + if (of_property_read_bool(np, "focaltech,x-flip")) { + pr_notice("using flipped X axis\n"); + pdata->x_flip = true; + } + + if (of_property_read_bool(np, "focaltech,y-flip")) { + pr_notice("using flipped Y axis\n"); + pdata->y_flip = true; + } + + rc = of_property_read_u32(np, "focaltech,family-id", &temp_val); + if (!rc) + pdata->family_id = temp_val; + else + return rc; + + prop = of_find_property(np, "focaltech,button-map", NULL); + if (prop) { + num_buttons = prop->length / sizeof(temp_val); + if (num_buttons > MAX_BUTTONS) + return -EINVAL; + + rc = of_property_read_u32_array(np, + "focaltech,button-map", button_map, + num_buttons); + if (rc) { + dev_err(dev, "Unable to read key codes\n"); + return rc; + } + } + + num_vendor_ids = of_property_count_elems_of_size(np, + "focaltech,vendor_ids", sizeof(u32)); + if ((num_vendor_ids > 0) && (num_vendor_ids < MAX_PANEL_SUPPLIERS)) { + const char *name; + + rc = of_property_read_u32_array(np, + "focaltech,vendor_ids", pdata->vendor_ids, + num_vendor_ids); + if (rc) { + dev_err(dev, "Unable to read vendor ids\n"); + return rc; + } + prop = of_find_property(np, "focaltech,vendor_names", NULL); + if (!prop) { + dev_err(dev, "Unable to read vendor names\n"); + return rc; + } + for (name = of_prop_next_string(prop, NULL), i = 0; + i < num_vendor_ids; + name = of_prop_next_string(prop, name), i++) + pdata->vendor_names[i] = name; + + pdata->num_vendor_ids = num_vendor_ids; + } + + return 0; +} +#else +static int ft_parse_dt(struct device *dev, + struct ft_ts_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static int ft_reboot(struct notifier_block *nb, + unsigned long event, + void *unused) +{ + struct ft_ts_data *data = + container_of(nb, struct ft_ts_data, ft_reboot); + + dev_info(&data->client->dev, "touch shutdown\n"); + + if (data->is_fps_registered) { + FPS_unregister_notifier(&data->fps_notif, 0xBEEF); + data->is_fps_registered = false; + } + + if (data->charger_detection_enabled) { + power_supply_unreg_notifier(&data->ps_notif); + data->charger_detection_enabled = false; + } + +#if defined(CONFIG_FB) + fb_unregister_client(&data->fb_notif); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&data->early_suspend); +#endif + + if (data->irq_enabled) { + ft_irq_disable(data); + free_irq(data->client->irq, data); + } + + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_direction_output(data->pdata->reset_gpio, 0); + msleep(data->pdata->hard_rst_dly); + gpio_free(data->pdata->reset_gpio); + } + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); + + if (data->regulator_en) { + if (data->pdata->power_on) + data->pdata->power_on(false); + else + ft_power_on(data, false); + if (data->pdata->power_init) + data->pdata->power_init(false); + else + ft_power_init(data, false); + data->regulator_en = false; + } + + return NOTIFY_DONE; +} + +static int ft_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ft_ts_platform_data *pdata; + struct ft_ts_data *data; + struct input_dev *input_dev; + struct dentry *temp; + u8 reg_value = 0; + u8 reg_addr; + int err, retval, attr_count; + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct ft_ts_platform_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + err = ft_parse_dt(&client->dev, pdata); + if (err) { + dev_err(&client->dev, "DT parsing failed\n"); + return err; + } + } else + pdata = client->dev.platform_data; + + if (!pdata) { + dev_err(&client->dev, "Invalid pdata\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C not supported\n"); + return -ENODEV; + } + + data = devm_kzalloc(&client->dev, + sizeof(struct ft_ts_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->tch_data_len = FT_TCH_LEN(pdata->num_max_touches); + data->tch_data = devm_kzalloc(&client->dev, + data->tch_data_len, GFP_KERNEL); + if (!data->tch_data) + return -ENOMEM; + + data->client = client; + data->pdata = pdata; + data->force_reflash = (data->pdata->no_force_update) ? false : true; + data->flash_enabled = true; + data->irq_enabled = false; + + data->patching_enabled = true; + err = ft_dt_parse_modifiers(data); + if (err) + data->patching_enabled = false; + + i2c_set_clientdata(client, data); + + if (pdata->power_init) { + err = pdata->power_init(true); + if (err) + dev_warn(&client->dev, "power init failed"); + else + data->regulator_en = true; + } else { + err = ft_power_init(data, true); + if (err) + dev_warn(&client->dev, "power init failed"); + else + data->regulator_en = true; + } + + if (data->regulator_en) { + if (pdata->power_on) { + err = pdata->power_on(true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto regulator_en_err; + } + } else { + err = ft_power_on(data, true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto regulator_en_err; + } + } + } + + err = ft_ts_pinctrl_init(data); + if (!err && data->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) { + dev_err(&client->dev, + "failed to select pin to active state"); + } + } + + err = ft_gpio_configure(data, true); + if (err < 0) { + dev_err(&client->dev, + "Failed to configure the gpios\n"); + goto gpio_config_err; + } + + /* make sure CTP already finish startup process */ + msleep(data->pdata->soft_rst_dly); + + /* check the controller id */ + reg_addr = FT_REG_ID; + err = ft_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) { + dev_err(&client->dev, "version read failed"); + goto free_gpio; + } + + dev_info(&client->dev, "Device ID = 0x%x\n", reg_value); + + if ((pdata->family_id != reg_value) && (!pdata->ignore_id_check)) { + dev_err(&client->dev, "%s:Unsupported controller\n", __func__); + goto free_gpio; + } + + data->family_id = pdata->family_id; + + /*get some register information */ + reg_addr = FT_REG_POINT_RATE; + ft_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) + dev_err(&client->dev, "report rate read failed"); + + dev_info(&client->dev, "report rate = %dHz\n", reg_value * 10); + + reg_addr = FT_REG_THGROUP; + err = ft_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) + dev_err(&client->dev, "threshold read failed"); + + dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "failed to allocate input device\n"); + goto free_gpio; + } + + data->input_dev = input_dev; + input_dev->name = "focaltech_ts"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_set_drvdata(input_dev, data); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + input_mt_init_slots(input_dev, pdata->num_max_touches, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, + pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, + pdata->y_max, 0, 0); + + err = input_register_device(input_dev); + if (err) { + dev_err(&client->dev, "Input device registration failed\n"); + goto input_register_device_err; + } + + if (pdata->family_id == FT8006U_ID) { + fts_extra_init(client, input_dev, pdata); + } + + err = request_threaded_irq(client->irq, NULL, + ft_ts_interrupt, + /* + * the interrupt trigger mode will be set in Device Tree with property + * "interrupts", so here we just need to set the flag IRQF_ONESHOT + */ + IRQF_ONESHOT, + client->dev.driver->name, data); + if (err) { + dev_err(&client->dev, "request irq failed\n"); + goto request_irq_err; + } + + /* request_threaded_irq enable irq,so set the flag irq_enabled */ + data->irq_enabled = true; + + err = ft_ts_sysfs_class(data, true); + if (err) { + dev_err(&client->dev, "sys class files creation failed\n"); + goto sysfs_class_err; + } + + err = sysfs_create_files(&client->dev.kobj, ft_attrs); + if (err) { + dev_err(&client->dev, "sys files creation failed\n"); + goto sysfs_create_files_err; + } + + data->dir = debugfs_create_dir(FT_DEBUG_DIR_NAME, NULL); + if (data->dir == NULL || IS_ERR(data->dir)) { + pr_err("debugfs_create_dir failed(%ld)\n", PTR_ERR(data->dir)); + err = PTR_ERR(data->dir); + goto debugfs_create_dir_err; + } + + temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, data->dir, data, + &debug_addr_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, data->dir, data, + &debug_data_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, data->dir, + data, &debug_suspend_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("dump_info", S_IRUSR | S_IWUSR, data->dir, + data, &debug_dump_info_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + err = ft_create_proc_entry(data); + if (err) { + dev_err(&client->dev, "create proc entry failed"); + goto free_debug_dir; + } + + data->ts_info = devm_kzalloc(&client->dev, + FT_INFO_MAX_LEN, GFP_KERNEL); + if (!data->ts_info) + goto free_proc_entry; + + /*creation touch panel info kobj*/ + data->ts_info_kobj = kobject_create_and_add(FT_TS_INFO_SYSFS_DIR_NAME, + kernel_kobj); + if (!data->ts_info_kobj) { + dev_err(&client->dev, "kobject creation failed.\n"); + } else { + err = sysfs_create_file(data->ts_info_kobj, &ts_info_attr.attr); + if (err) { + kobject_put(data->ts_info_kobj); + dev_err(&client->dev, "sysfs creation failed.\n"); + } else { + ts_info_buff = devm_kzalloc(&client->dev, + FT_INFO_MAX_LEN, GFP_KERNEL); + if (!ts_info_buff) + goto free_ts_info; + } + } + FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_vkey_support ? "yes" : "no", + data->pdata->fw_name, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + + /*Initialize secure touch */ + ft_secure_touch_init(data); + ft_secure_touch_stop(data, true); + mutex_init(&(data->ft_clk_io_ctrl_mutex)); + + /* Creation of secure touch sysfs files */ + for (attr_count = 0; attr_count < ARRAY_SIZE(st_attrs); attr_count++) { + retval = sysfs_create_file(&data->input_dev->dev.kobj, + &st_attrs[attr_count].attr); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto free_secure_touch_sysfs; + } + } + + ft_update_fw_ver(data); + ft_update_fw_vendor_id(data); + ft_update_fw_id(data); + +#if defined(CONFIG_FB) + INIT_WORK(&data->fb_notify_work, fb_notify_resume_work); + data->fb_notif.notifier_call = fb_notifier_callback; + + err = fb_register_client(&data->fb_notif); + if (err) + dev_err(&client->dev, "Unable to register fb_notifier: %d\n", + err); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + + FT_SUSPEND_LEVEL; + data->early_suspend.suspend = ft_ts_early_suspend; + data->early_suspend.resume = ft_ts_late_resume; + register_early_suspend(&data->early_suspend); +#endif + + mutex_lock(&exp_fn_ctrl_mutex); + if (!exp_fn_ctrl.inited) { + mutex_init(&exp_fn_ctrl.list_mutex); + INIT_LIST_HEAD(&exp_fn_ctrl.fn_list); + exp_fn_ctrl.det_workqueue = + create_singlethread_workqueue("rmi_det_workqueue"); + if (IS_ERR_OR_NULL(exp_fn_ctrl.det_workqueue)) + pr_err("unable to create a workqueue\n"); + INIT_DELAYED_WORK(&exp_fn_ctrl.det_work, + ft_detection_work); + exp_fn_ctrl.inited = true; + } + exp_fn_ctrl.data_ptr = data; + mutex_unlock(&exp_fn_ctrl_mutex); + + if (data->charger_detection_enabled) { + struct power_supply *psy = NULL; + + INIT_WORK(&data->ps_notify_work, ps_notify_callback_work); + data->ps_notif.notifier_call = ps_notify_callback; + err = power_supply_reg_notifier(&data->ps_notif); + if (err) { + dev_err(&client->dev, + "Unable to register ps_notifier: %d\n", err); + goto free_fb_notifier; + } + + /* if power supply supplier registered brfore TP + * ps_notify_callback will not receive PSY_EVENT_PROP_ADDED + * event, and will cause miss to set TP into charger state. + * So check PS state in probe. + */ + psy = power_supply_get_by_name("usb"); + if (psy) { + retval = ps_get_state(data, psy, &data->ps_is_present); + if (retval) { + dev_err(&client->dev, + "psy get property failed rc=%d\n", + retval); + goto free_fb_notifier; + } + + retval = ft_set_charger_state(data, + data->ps_is_present); + if (retval) { + dev_err(&client->dev, + "set charger state failed rc=%d\n", + retval); + goto free_fb_notifier; + } + + ft_update_ps_chg_cm_state(data, + data->ps_is_present); + } + } + + if (data->fps_detection_enabled) { + data->fps_notif.notifier_call = fps_notifier_callback; + dev_dbg(&client->dev, "registering FPS notifier\n"); + ft_new_function(true, + ft_fps_register_init, + NULL, + NULL); + } + + data->ft_reboot.notifier_call = ft_reboot; + data->ft_reboot.next = NULL; + data->ft_reboot.priority = 1; + err = register_reboot_notifier(&data->ft_reboot); + if (err) { + dev_err(&client->dev, "register for reboot failed\n"); + goto reboot_register_err; + } + + return 0; + +reboot_register_err: + if (data->is_fps_registered) + FPS_unregister_notifier(&data->fps_notif, 0xBEEF); + if (data->charger_detection_enabled) + power_supply_unreg_notifier(&data->ps_notif); +free_fb_notifier: +#if defined(CONFIG_FB) + if (fb_unregister_client(&data->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&data->early_suspend); +#endif +free_secure_touch_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&data->input_dev->dev.kobj, + &st_attrs[attr_count].attr); + } +free_ts_info: + kobject_put(data->ts_info_kobj); +free_proc_entry: + ft_remove_proc_entry(data); +free_debug_dir: + debugfs_remove_recursive(data->dir); +debugfs_create_dir_err: + sysfs_remove_files(&client->dev.kobj, ft_attrs); +sysfs_create_files_err: + ft_ts_sysfs_class(data, false); +sysfs_class_err: + free_irq(client->irq, data); + data->irq_enabled = false; +request_irq_err: + input_unregister_device(input_dev); +input_register_device_err: + data->input_dev = NULL; + input_free_device(input_dev); +free_gpio: + ft_gpio_configure(data, false); +gpio_config_err: + if (data->ts_pinctrl) { + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + } else { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_release); + if (err) + pr_err("failed to select relase pinctrl state\n"); + } + } + + if (data->regulator_en) { + if (pdata->power_on) + pdata->power_on(false); + else + ft_power_on(data, false); + } +regulator_en_err: + if (pdata->power_init) + pdata->power_init(false); + else + ft_power_init(data, false); + return err; +} + +static int ft_ts_remove(struct i2c_client *client) +{ + struct ft_ts_data *data = i2c_get_clientdata(client); + int retval, attr_count; + + if (exp_fn_ctrl.inited) { + cancel_delayed_work_sync(&exp_fn_ctrl.det_work); + flush_workqueue(exp_fn_ctrl.det_workqueue); + destroy_workqueue(exp_fn_ctrl.det_workqueue); + } + + if (data->is_fps_registered) + FPS_unregister_notifier(&data->fps_notif, 0xBEEF); + + unregister_reboot_notifier(&data->ft_reboot); + + if (data->charger_detection_enabled) + power_supply_unreg_notifier(&data->ps_notif); + +#if defined(CONFIG_FB) + if (fb_unregister_client(&data->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&data->early_suspend); +#endif + + for (attr_count = 0; attr_count < ARRAY_SIZE(st_attrs); attr_count++) { + sysfs_remove_file(&data->input_dev->dev.kobj, + &st_attrs[attr_count].attr); + } + + ft_remove_proc_entry(data); + debugfs_remove_recursive(data->dir); + sysfs_remove_files(&client->dev.kobj, ft_attrs); + ft_ts_sysfs_class(data, false); + + free_irq(client->irq, data); + data->irq_enabled = false; + + ft_gpio_configure(data, false); + + if (data->pdata->family_id == FT8006U_ID) { + fts_extra_exit(); + } + + if (data->ts_pinctrl) { + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + } else { + retval = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_release); + if (retval < 0) + pr_err("failed to select release pinctrl state\n"); + } + } + + if (data->regulator_en) { + if (data->pdata->power_on) + data->pdata->power_on(false); + else + ft_power_on(data, false); + } + + if (data->pdata->power_init) + data->pdata->power_init(false); + else + ft_power_init(data, false); + + input_unregister_device(data->input_dev); + data->input_dev = NULL; + input_free_device(data->input_dev); + kobject_put(data->ts_info_kobj); + return 0; +} + +static const struct i2c_device_id ft_ts_id[] = { + {"focaltech_ts", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ft_ts_id); + +#ifdef CONFIG_OF +static const struct of_device_id ft_match_table[] = { + { .compatible = "focaltech,mmi",}, + { }, +}; +#else +#define ft_match_table NULL +#endif + +static struct i2c_driver ft_ts_driver = { + .probe = ft_ts_probe, + .remove = ft_ts_remove, + .driver = { + .name = "focaltech_ts", + .owner = THIS_MODULE, + .of_match_table = ft_match_table, +#ifdef CONFIG_PM + .pm = &ft_ts_pm_ops, +#endif + }, + .id_table = ft_ts_id, +}; + +static int __init ft_ts_init(void) +{ + return i2c_add_driver(&ft_ts_driver); +} +module_init(ft_ts_init); + +static void __exit ft_ts_exit(void) +{ + i2c_del_driver(&ft_ts_driver); +} +module_exit(ft_ts_exit); + +MODULE_DESCRIPTION("FocalTech mmi TouchScreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_common.h b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_common.h new file mode 100644 index 000000000000..07d8eae51821 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_common.h @@ -0,0 +1,178 @@ +#ifndef __LINUX_FOCALTECH_UPGRADE_COMMON_H__ +#define __LINUX_FOCALTECH_UPGRADE_COMMON_H__ + +#include +#include +#include "focaltech_flash.h" + +#define FTS_MAX_TRIES 5 +#define FTS_RETRY_DLY 20 +#define FTS_MAX_WR_BUF 10 +#define FTS_MAX_RD_BUF 2 +#define FTS_FW_PKT_META_LEN 6 +#define FTS_FW_PKT_DLY_MS 20 +#define FTS_FW_LAST_PKT 0x6ffa +#define FTS_EARSE_DLY_MS 100 +#define FTS_55_AA_DLY_NS 5000 +#define FTS_CAL_START 0x04 +#define FTS_CAL_FIN 0x00 +#define FTS_CAL_STORE 0x05 +#define FTS_CAL_RETRY 100 +#define FTS_REG_CAL 0x00 +#define FTS_CAL_MASK 0x70 +#define FTS_BLOADER_SIZE_OFF 12 +#define FTS_BLOADER_NEW_SIZE 30 +#define FTS_DATA_LEN_OFF_OLD_FW 8 +#define FTS_DATA_LEN_OFF_NEW_FW 14 +#define FTS_FINISHING_PKT_LEN_OLD_FW 6 +#define FTS_FINISHING_PKT_LEN_NEW_FW 12 +#define FTS_MAGIC_BLOADER_Z7 0x7bfa +#define FTS_MAGIC_BLOADER_LZ4 0x6ffa +#define FTS_MAGIC_BLOADER_GZF_30 0x7ff4 +#define FTS_MAGIC_BLOADER_GZF 0x7bf4 +#define FTS_REG_ECC 0xCC +#define FTS_RST_CMD_REG2 0xBC +#define FTS_READ_ID_REG 0x90 +#define FTS_ERASE_APP_REG 0x61 +#define FTS_ERASE_PARAMS_CMD 0x63 +#define FTS_FW_WRITE_CMD 0xBF +#define FTS_REG_RESET_FW 0x07 +#define FTS_RST_CMD_REG1 0xFC +#define LEN_FLASH_ECC_MAX 0xFFFE + +#define BL_VERSION_LZ4 0 +#define BL_VERSION_Z7 1 +#define BL_VERSION_GZF 2 + +#define FTS_PACKET_LENGTH 128 +#define FTS_SETTING_BUF_LEN 128 + +#define FTS_UPGRADE_LOOP 30 +#define FTS_MAX_POINTS_2 2 +#define FTS_MAX_POINTS_5 5 +#define FTS_MAX_POINTS_10 10 +#define AUTO_CLB_NEED 1 +#define AUTO_CLB_NONEED 0 +#define FTS_UPGRADE_AA 0xAA +#define FTS_UPGRADE_55 0x55 + +/* Register Address */ +#define FTS_REG_INT_CNT 0x8F +#define FTS_REG_FLOW_WORK_CNT 0x91 +#define FTS_REG_WORKMODE 0x00 +#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40 +#define FTS_REG_WORKMODE_WORK_VALUE 0x00 +#define FTS_REG_CHIP_ID 0xA3 +#define FTS_REG_CHIP_ID2 0x9F +#define FTS_REG_POWER_MODE 0xA5 +#define FTS_REG_POWER_MODE_SLEEP_VALUE 0x03 +#define FTS_REG_FW_VER 0xA6 +#define FTS_REG_VENDOR_ID 0xA8 +#define FTS_REG_LCD_BUSY_NUM 0xAB +#define FTS_REG_FACE_DEC_MODE_EN 0xB0 +#define FTS_REG_GLOVE_MODE_EN 0xC0 +#define FTS_REG_COVER_MODE_EN 0xC1 +#define FTS_REG_CHARGER_MODE_EN 0x8B +#define FTS_REG_GESTURE_EN 0xD0 +#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3 +#define FTS_REG_ESD_SATURATE 0xED + +#define FLAGBIT(x) (0x00000001 << (x)) +#define FLAGBITS(x, y) ((0xFFFFFFFF >> (32 - (y) - 1)) << (x)) + +#define FLAG_ICSERIALS_LEN 5 +#define FLAG_IDC_BIT 11 + +#define IC_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN - 1)) +#define FTS_CHIP_IDC(x) (((x) & FLAGBIT(FLAG_IDC_BIT)) == \ + FLAGBIT(FLAG_IDC_BIT)) + +#define FTS_DEBUG pr_debug +#define FTS_ERROR pr_err +#define FTS_INFO pr_info +#define FTS_FUNC_ENTER() pr_debug("[FTS]%s: Enter\n", __func__) +#define FTS_FUNC_EXIT() pr_debug("[FTS]%s: Exit(%d)\n", __func__, \ + __LINE__) + +enum ENUM_APP_INFO { + APP_LEN = 0x00, + APP_LEN_NE = 0x02, + APP_P1_ECC = 0x04, + APP_P1_ECC_NE = 0x06, + APP_P2_ECC = 0x08, + APP_P2_ECC_NE = 0x0A, + APP_LEN_H = 0x12, + APP_LEN_H_NE = 0x14, + APP_BLR_ID = 0x1C, + APP_BLR_ID_NE = 0x1D, + PBOOT_ID_H = 0x1E, + PBOOT_ID_L = 0x1F +}; + +enum FW_STATUS { + FTS_RUN_IN_ERROR, + FTS_RUN_IN_APP, + FTS_RUN_IN_ROM, + FTS_RUN_IN_PRAM, + FTS_RUN_IN_BOOTLOADER +}; + +struct ft_chip_t { + unsigned long type; + unsigned char chip_idh; + unsigned char chip_idl; + unsigned char rom_idh; + unsigned char rom_idl; + unsigned char pramboot_idh; + unsigned char pramboot_idl; + unsigned char bootloader_idh; + unsigned char bootloader_idl; + unsigned long chip_type; +}; + +struct fts_upgrade_fun { + int (*get_app_bin_file_ver)(const char *); + int (*upgrade_with_app_bin_file)(struct i2c_client *, const char *); + int (*upgrade_with_lcd_cfg_bin_file)(struct i2c_client *, const char *); +}; + +extern int fts_i2c_read(struct i2c_client *client, + char *writebuf, int writelen, + char *readbuf, int readlen); +extern int fts_i2c_write(struct i2c_client *client, + char *writebuf, int writelen); +extern int fts_i2c_write_reg(struct i2c_client *client, + u8 addr, const u8 val); +extern int fts_i2c_read_reg(struct i2c_client *client, u8 addr, u8 *val); +extern enum FW_STATUS fts_ctpm_get_pram_or_rom_id + (struct i2c_client *client); + +extern bool fts_ecc_check(const u8 *pbt_buf, + u32 star_addr, u32 len, u16 ecc_addr); + +static inline u16 fts_data_word(const u8 *pbt_buf, u32 addr) +{ + return ((u16)pbt_buf[addr] << 8) + pbt_buf[addr + 1]; +} + + +extern int fts_ctpm_upgrade_idc_init(struct i2c_client *client, + u32 dw_length); +extern void fts_ctpm_start_pramboot(struct i2c_client *client); +extern int fts_ctpm_start_fw_upgrade(struct i2c_client *client); +extern bool fts_ctpm_check_run_state(struct i2c_client *client, + int state); +extern int fts_ctpm_upgrade_ecc(struct i2c_client *client, + u32 startaddr, u32 length); +extern int fts_ctpm_pramboot_ecc(struct i2c_client *client); +extern int fts_ctpm_erase_flash(struct i2c_client *client); +extern int fts_ctpm_write_pramboot_for_idc(struct i2c_client *client, + u32 length, const u8 *readbuf); +extern int fts_ctpm_write_app_for_idc(struct i2c_client *client, + u32 length, const u8 *readbuf); +extern void ft8716_set_upgrade_function(struct fts_upgrade_fun **curr); +extern void ft8716_set_chip_id(struct ft_chip_t **curr); +extern void ft5x46_set_upgrade_function(struct fts_upgrade_fun **curr); +extern void ft5x46_set_chip_id(struct ft_chip_t **curr); + +#endif diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft5x46.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft5x46.c new file mode 100644 index 000000000000..351fde6741b7 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft5x46.c @@ -0,0 +1,451 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include "focaltech_flash.h" +#include "focaltech_upgrade_common.h" + +#define APP_FILE_MAX_SIZE (60 * 1024) +#define APP_FILE_MIN_SIZE (8) +#define CONFIG_START_ADDR (0xD780) +#define CONFIG_VENDOR_ID_OFFSET (0x04) +#define CONFIG_PROJECT_ID_OFFSET (0x20) +#define CONFIG_VENDOR_ID_ADDR (CONFIG_START_ADDR+CONFIG_VENDOR_ID_OFFSET) +#define CONFIG_PROJECT_ID_ADDR (CONFIG_START_ADDR+CONFIG_PROJECT_ID_OFFSET) + +static int fts_5x46_get_app_bin_file_ver(const char *firmware_name); +static int fts_5x46_fw_upgrade_with_app_bin_file( + struct i2c_client *client, const char *firmware_name); + + +static struct fts_upgrade_fun fts_updatefun_5x46 = { + .get_app_bin_file_ver = fts_5x46_get_app_bin_file_ver, + .upgrade_with_app_bin_file = fts_5x46_fw_upgrade_with_app_bin_file, + .upgrade_with_lcd_cfg_bin_file = NULL, +}; + +static struct ft_chip_t fts_chip_type_5x46 = { + .type = 0x02, + .chip_idh = 0x54, + .chip_idl = 0x22, + .rom_idh = 0x54, + .rom_idl = 0x22, + .pramboot_idh = 0x00, + .pramboot_idl = 0x00, + .bootloader_idh = 0x54, + .bootloader_idl = 0x2C, + .chip_type = 0x54220002, +}; + +inline void ft5x46_set_upgrade_function(struct fts_upgrade_fun **curr) +{ + *curr = &fts_updatefun_5x46; +} +inline void ft5x46_set_chip_id(struct ft_chip_t **curr) +{ + *curr = &fts_chip_type_5x46; +} + +static int fts_5x46_get_app_bin_file_ver(const char *firmware_name) +{ + int fw_ver = 0; + int rc; + const struct firmware *fw = NULL; + + FTS_FUNC_ENTER(); + + rc = request_firmware(&fw, firmware_name, NULL); + if (rc < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + firmware_name, rc); + return rc; + } + if (fw->size < APP_FILE_MIN_SIZE || fw->size > APP_FILE_MAX_SIZE) { + FTS_ERROR("[UPGRADE] FW length(%zd) error\n", fw->size); + return -EIO; + goto file_ver_rel_fw; + } + + fw_ver = fw->data[fw->size - 2]; + +file_ver_rel_fw: + release_firmware(fw); + FTS_FUNC_EXIT(); + + return fw_ver; +} + + +/************************************************************************ +* Name: fts_ft5436_upgrade_use_buf +* Brief: fw upgrade +* Input: i2c info, file buf, file len +* Output: no +* Return: fail <0 +***********************************************************************/ +static int fts_ft5x46_upgrade_use_buf(struct i2c_client *client, + const u8 *pbt_buf, u32 dw_lenth) +{ + u8 reg_val[4] = {0}; + u32 i = 0; + u32 packet_number; + u32 j = 0; + u32 temp; + u32 lenght; + u8 packet_buf[FTS_PACKET_LENGTH + 6]; + u8 auc_i2c_write_buf[10]; + u8 upgrade_ecc; + int i_ret; + + fts_ctpm_i2c_hid2std(client); + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) + { + /*********Step 1:Reset CTPM *****/ + fts_i2c_write_reg(client, FTS_RST_CMD_REG1, FTS_UPGRADE_AA); + msleep(10); + fts_i2c_write_reg(client, FTS_RST_CMD_REG1, FTS_UPGRADE_55); + msleep(200); + + /*********Step 2:Enter upgrade mode *****/ + fts_ctpm_i2c_hid2std(client); + msleep(5); + + auc_i2c_write_buf[0] = FTS_UPGRADE_55; + auc_i2c_write_buf[1] = FTS_UPGRADE_AA; + i_ret = fts_i2c_write(client, auc_i2c_write_buf, 2); + if (i_ret < 0) + { + FTS_ERROR("[UPGRADE]: failed writing 0x55 and 0xaa!!"); + continue; + } + + /*********Step 3:Check bootloader ID *****/ + msleep(1); + auc_i2c_write_buf[0] = FTS_READ_ID_REG; + auc_i2c_write_buf[1] = auc_i2c_write_buf[2] = auc_i2c_write_buf[3] = 0x00; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 4, reg_val, 2); + if ((reg_val[0] == 0x54) && (reg_val[1] != 0)) + { + FTS_DEBUG("[UPGRADE]: read bootload id ok!! ID1 = 0x%x,ID2 = 0x%x!!",reg_val[0], reg_val[1]); + break; + } + else + { + FTS_ERROR("[UPGRADE]: read bootload id fail!! ID1 = 0x%x,ID2 = 0x%x!!",reg_val[0], reg_val[1]); + continue; + } + } + + if (i >= FTS_UPGRADE_LOOP) + { + FTS_ERROR("[UPGRADE]: failed writing 0x55 and 0xaa : i = %d!!", i); + return -EIO; + } + + /*Step 4:erase app and panel paramenter area*/ + FTS_DEBUG("[UPGRADE]: erase app and panel paramenter area!!"); + auc_i2c_write_buf[0] = FTS_ERASE_APP_REG; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(1350); + for (i = 0; i < 15; i++) + { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + if ((0xF0 == reg_val[0]) && (0xAA == reg_val[1])) + { + break; + } + msleep(50); + } + FTS_DEBUG("[UPGRADE]: erase app area reg_val[0] = %x reg_val[1] = %x!!", reg_val[0], reg_val[1]); + + auc_i2c_write_buf[0] = 0xB0; + auc_i2c_write_buf[1] = (u8) ((dw_lenth >> 16) & 0xFF); + auc_i2c_write_buf[2] = (u8) ((dw_lenth >> 8) & 0xFF); + auc_i2c_write_buf[3] = (u8) (dw_lenth & 0xFF); + fts_i2c_write(client, auc_i2c_write_buf, 4); + + /*********Step 5:write firmware(FW) to ctpm flash*********/ + upgrade_ecc = 0; + FTS_DEBUG("[UPGRADE]: write FW to ctpm flash!!"); + temp = 0; + packet_number = (dw_lenth) / FTS_PACKET_LENGTH; + packet_buf[0] = FTS_FW_WRITE_CMD; + packet_buf[1] = 0x00; + + for (j = 0; j < packet_number; j++) + { + temp = j * FTS_PACKET_LENGTH; + packet_buf[2] = (u8) (temp >> 8); + packet_buf[3] = (u8) temp; + lenght = FTS_PACKET_LENGTH; + packet_buf[4] = (u8) (lenght >> 8); + packet_buf[5] = (u8) lenght; + for (i = 0; i < FTS_PACKET_LENGTH; i++) + { + packet_buf[6 + i] = pbt_buf[j * FTS_PACKET_LENGTH + i]; + upgrade_ecc ^= packet_buf[6 + i]; + } + fts_i2c_write(client, packet_buf, FTS_PACKET_LENGTH + 6); + msleep(10); + + for (i = 0; i < 30; i++) + { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + if ((j + 0x1000) == (((reg_val[0]) << 8) | reg_val[1])) + { + break; + } + FTS_DEBUG("[UPGRADE]: reg_val[0] = %x reg_val[1] = %x!!", reg_val[0], reg_val[1]); + //msleep(1); + msleep(1000); + } + } + + if ((dw_lenth) % FTS_PACKET_LENGTH > 0) + { + temp = packet_number * FTS_PACKET_LENGTH; + packet_buf[2] = (u8) (temp >> 8); + packet_buf[3] = (u8) temp; + temp = (dw_lenth) % FTS_PACKET_LENGTH; + packet_buf[4] = (u8) (temp >> 8); + packet_buf[5] = (u8) temp; + for (i = 0; i < temp; i++) + { + packet_buf[6 + i] = pbt_buf[packet_number * FTS_PACKET_LENGTH + i]; + upgrade_ecc ^= packet_buf[6 + i]; + } + fts_i2c_write(client, packet_buf, temp + 6); + msleep(10); + + for (i = 0; i < 30; i++) + { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + if ((0x1000 + ((packet_number * FTS_PACKET_LENGTH)/((dw_lenth) % FTS_PACKET_LENGTH))) == (((reg_val[0]) << 8) | reg_val[1])) + { + break; + } + FTS_DEBUG("[UPGRADE]: reg_val[0] = %x reg_val[1] = %x reg_val[2] = 0x%x!!", reg_val[0], reg_val[1], (((packet_number * FTS_PACKET_LENGTH)/((dw_lenth) % FTS_PACKET_LENGTH))+0x1000)); + //msleep(1); + msleep(100); + } + } + + msleep(50); + + /*********Step 6: read out checksum***********************/ + /*send the opration head */ + FTS_DEBUG("[UPGRADE]: read out checksum!!"); + auc_i2c_write_buf[0] = 0x64; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(300); + + temp = 0; + auc_i2c_write_buf[0] = 0x65; + auc_i2c_write_buf[1] = (u8)(temp >> 16); + auc_i2c_write_buf[2] = (u8)(temp >> 8); + auc_i2c_write_buf[3] = (u8)(temp); + temp = dw_lenth; + auc_i2c_write_buf[4] = (u8)(temp >> 8); + auc_i2c_write_buf[5] = (u8)(temp); + i_ret = fts_i2c_write(client, auc_i2c_write_buf, 6); + msleep(dw_lenth/256); + + for (i = 0; i < 100; i++) + { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + FTS_DEBUG("[UPGRADE]: reg_val[0]=%02x reg_val[0]=%02x!!", reg_val[0], reg_val[1]); + if ((0xF0 == reg_val[0]) && (0x55 == reg_val[1])) + { + break; + } + msleep(1); + } + auc_i2c_write_buf[0] = 0x66; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 1); + if (reg_val[0] != upgrade_ecc) + { + FTS_ERROR("[UPGRADE]: ecc error! FW=%02x upgrade_ecc=%02x!!", reg_val[0], upgrade_ecc); + return -EIO; + } + FTS_DEBUG("[UPGRADE]: checksum %x %x!!",reg_val[0],upgrade_ecc); + + FTS_DEBUG("[UPGRADE]: reset the new FW!!"); + auc_i2c_write_buf[0] = FTS_REG_RESET_FW; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(200); + + fts_ctpm_i2c_hid2std(client); + + return 0; +} + +#define AL2_FCS_COEF ((1 << 7) + (1 << 6) + (1 << 5)) +/***************************************************************************** +* Name: ecc_calc +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static u8 ecc_calc(const u8 *pbt_buf, u16 start, u16 length) +{ + u8 cFcs = 0; + u16 i, j; + + for ( i = 0; i < length; i++ ) + { + cFcs ^= pbt_buf[start++]; + for (j = 0; j < 8; j ++) + { + if (cFcs & 1) + { + cFcs = (u8)((cFcs >> 1) ^ AL2_FCS_COEF); + } + else + { + cFcs >>= 1; + } + } + } + return cFcs; +} + + +/***************************************************************************** +* Name: fts_check_app_bin_valid +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static bool fts_5x46_check_app_bin_valid(const u8 *pbt_buf, u32 dw_lenth) +{ + u8 ecc1; + u8 ecc2; + u16 len1; + u16 len2; + u8 cal_ecc; + u16 usAddrInfo; + + /* 1. First Byte */ + if (pbt_buf[0] != 0x02) + { + FTS_DEBUG("[UPGRADE]APP.BIN Verify- the first byte(%x) error", pbt_buf[0]); + return false; + } + + usAddrInfo = dw_lenth - 8; + + /* 2.len */ + len1 = pbt_buf[usAddrInfo++] << 8; + len1 += pbt_buf[usAddrInfo++]; + + len2 = pbt_buf[usAddrInfo++] << 8; + len2 += pbt_buf[usAddrInfo++]; + + if ((len1 + len2) != 0xFFFF) + { + FTS_DEBUG("[UPGRADE]APP.BIN Verify- LENGTH(%04x) XOR error", len1); + return false; + } + + /* 3.ecc */ + ecc1 = pbt_buf[usAddrInfo++]; + ecc2 = pbt_buf[usAddrInfo++]; + + if ((ecc1 + ecc2) != 0xFF) + { + FTS_DEBUG("[UPGRADE]APP.BIN Verify- ECC(%x) XOR error", ecc1); + return false; + } + + cal_ecc = ecc_calc(pbt_buf, 0x0, len1); + + if (ecc1 != cal_ecc) + { + FTS_DEBUG("[UPGRADE]APP.BIN Verify- ECC calc error"); + return false; + } + return true; +} + +/************************************************************************ +* Name: fts_5436_fw_upgrade_with_app_bin_file +* Brief: upgrade with *.bin file +* Input: i2c info, file name +* Output: no +* Return: success =0 +***********************************************************************/ +static int fts_5x46_fw_upgrade_with_app_bin_file(struct i2c_client *client, const char *firmware_name) +{ + int i_ret = 0; + bool ecc_ok = false; + const struct firmware *fw = NULL; + + FTS_INFO("[UPGRADE] start upgrade with app.bin\n"); + + i_ret = request_firmware(&fw, firmware_name, &client->dev); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + firmware_name, i_ret); + return i_ret; + } + + if (fw->size < APP_FILE_MIN_SIZE || fw->size > APP_FILE_MAX_SIZE) { + FTS_ERROR("[UPGRADE] FW length(%zd) error\n", fw->size); + i_ret = -EINVAL; + goto file_upgrade_rel_fw; + } + + ecc_ok = fts_5x46_check_app_bin_valid(fw->data, fw->size); + + if (ecc_ok) + { + FTS_INFO("[UPGRADE] app.bin ecc ok"); + i_ret = fts_ft5x46_upgrade_use_buf(client, fw->data, fw->size); + if (i_ret != 0) + { + FTS_ERROR("[UPGRADE]: upgrade app.bin failed"); + } + else + { + FTS_INFO("[UPGRADE]: upgrade app.bin succeed"); + } + } + else + { + FTS_ERROR("[UPGRADE] app.bin ecc failed"); + } + + return i_ret; + +file_upgrade_rel_fw: + release_firmware(fw); + return i_ret; +} + diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8006u.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8006u.c new file mode 100644 index 000000000000..1cf7803e669b --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8006u.c @@ -0,0 +1,944 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2017, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_upgrade_ft8006u.c +* +* Author: Focaltech Driver Team +* +* Created: 2017-07-22 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_flash.h" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +#define FT8006U_PRAMBOOT_FW_NAME "FT8006U_Pramboot.bin" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define MAX_BANK_DATA 0x80 +#define MAX_GAMMA_LEN 0x180 +#define LIC_CHECKSUM_H_OFF 0x01 +#define LIC_CHECKSUM_L_OFF 0x00 +#define LIC_LCD_ECC_H_OFF 0x05 +#define LIC_LCD_ECC_L_OFF 0x04 +#define LIC_BANKECC_H_OFF 0x0F +#define LIC_BANKECC_L_OFF 0x0E +#define LIC_REG_2 0xB2 +#define LIC_BANK_START_ADDR 0x0A + +static int gamma_enable[] = { 0x042c, 0x91, 0x80, 0x00, 0x12, 0x01 }; +bool gamma_has_enable = false; + +union short_bits { + u16 dshort; + struct bits { + u16 bit0: 1; + u16 bit1: 1; + u16 bit2: 1; + u16 bit3: 1; + u16 bit4: 1; + u16 bit5: 1; + u16 bit6: 1; + u16 bit7: 1; + u16 bit8: 1; + u16 bit9: 1; + u16 bit10: 1; + u16 bit11: 1; + u16 bit12: 1; + u16 bit13: 1; + u16 bit14: 1; + u16 bit15: 1; + } bits; +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/* calculate lcd init code ecc */ +static int cal_lcdinitcode_ecc(u8 *buf, u16 *ecc_val) +{ + u32 bank_crc_en = 0; + u8 bank_data[MAX_BANK_DATA] = { 0 }; + u16 bank_len = 0; + u16 bank_addr = 0; + u32 bank_num = 0; + u16 file_len = 0; + u16 pos = 0; + int i = 0; + union short_bits ecc; + union short_bits ecc_last; + union short_bits temp_byte; + u8 bank_mapping[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x18, 0x19, 0x1A, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x23, 0x24 + }; /* Actaul mipi bank */ + u8 banknum = 0; + + ecc.dshort = 0; + ecc_last.dshort = 0; + temp_byte.dshort = 0; + + file_len = (u16)(((u16)buf[3] << 8) + buf[2]); + if ((file_len >= FTS_MAX_LEN_SECTOR) || (file_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", file_len); + return -EINVAL; + } + + bank_crc_en = (u32)(((u32)buf[9] << 24) + ((u32)buf[8] << 16) + \ + ((u32)buf[7] << 8) + (u32)buf[6]); + FTS_INFO("lcd init code len=%x bank en=%x", file_len, bank_crc_en); + + pos = LIC_BANK_START_ADDR; /* addr of first bank */ + while (pos < file_len) { + bank_addr = (u16)(((u16)buf[pos + 0] << 8 ) + buf[pos + 1]); + bank_len = (u16)(((u16)buf[pos + 2] << 8 ) + buf[pos + 3]); + if (bank_len > MAX_BANK_DATA) + return -EINVAL; + memset(bank_data, 0, MAX_BANK_DATA); + memcpy(bank_data, buf + pos + 4, bank_len); + + bank_num = (bank_addr - 0x8000) / MAX_BANK_DATA; + if ((bank_num == 0x15) || (bank_num == 0x16)) + bank_num = 0x14; + if (bank_num == 0x1B) + bank_num = 0x1A; + for (i = 0; i < sizeof(bank_mapping) / sizeof(u8); i++) { + if (bank_num == bank_mapping[i]) { + banknum = i; + break; + } + } + if (i >= sizeof(bank_mapping) / sizeof(u8)) { + FTS_INFO("actual mipi bank(%d) not find in bank mapping, need jump", bank_num); + } else { + if ((bank_crc_en >> banknum) & 0x01) { + for (i = 0; i < bank_len; i++) { + temp_byte.dshort = (u16)bank_data[i]; + + ecc.bits.bit0 = ecc_last.bits.bit8 ^ ecc_last.bits.bit9 ^ ecc_last.bits.bit10 ^ ecc_last.bits.bit11 + ^ ecc_last.bits.bit12 ^ ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 + ^ temp_byte.bits.bit0 ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3 + ^ temp_byte.bits.bit4 ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc.bits.bit1 = ecc_last.bits.bit9 ^ ecc_last.bits.bit10 ^ ecc_last.bits.bit11 ^ ecc_last.bits.bit12 + ^ ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 + ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3 ^ temp_byte.bits.bit4 + ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc.bits.bit2 = ecc_last.bits.bit8 ^ ecc_last.bits.bit9 ^ temp_byte.bits.bit0 ^ temp_byte.bits.bit1; + + ecc.bits.bit3 = ecc_last.bits.bit9 ^ ecc_last.bits.bit10 ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2; + + ecc.bits.bit4 = ecc_last.bits.bit10 ^ ecc_last.bits.bit11 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3; + + ecc.bits.bit5 = ecc_last.bits.bit11 ^ ecc_last.bits.bit12 ^ temp_byte.bits.bit3 ^ temp_byte.bits.bit4; + + ecc.bits.bit6 = ecc_last.bits.bit12 ^ ecc_last.bits.bit13 ^ temp_byte.bits.bit4 ^ temp_byte.bits.bit5; + + ecc.bits.bit7 = ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6; + + ecc.bits.bit8 = ecc_last.bits.bit0 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc.bits.bit9 = ecc_last.bits.bit1 ^ ecc_last.bits.bit15 ^ temp_byte.bits.bit7; + + ecc.bits.bit10 = ecc_last.bits.bit2; + + ecc.bits.bit11 = ecc_last.bits.bit3; + + ecc.bits.bit12 = ecc_last.bits.bit4; + + ecc.bits.bit13 = ecc_last.bits.bit5; + + ecc.bits.bit14 = ecc_last.bits.bit6; + + ecc.bits.bit15 = ecc_last.bits.bit7 ^ ecc_last.bits.bit8 ^ ecc_last.bits.bit9 ^ ecc_last.bits.bit10 + ^ ecc_last.bits.bit11 ^ ecc_last.bits.bit12 ^ ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 + ^ temp_byte.bits.bit0 ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3 + ^ temp_byte.bits.bit4 ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc_last.dshort = ecc.dshort; + + } + } + } + pos += bank_len + 4; + } + + *ecc_val = ecc.dshort; + return 0; +} + + +/* calculate lcd init code checksum */ +static u16 cal_lcdinitcode_checksum(u8 *ptr , int length) +{ + /* CRC16 */ + u16 cfcs = 0; + int i = 0; + int j = 0; + + length = (length % 2 == 0) ? length : (length - 1); + + for (i = 0; i < length; i += 2) { + cfcs ^= ((ptr[i] << 8) + ptr[i + 1]); + for (j = 0; j < 16; j++) { + if (cfcs & 1) + cfcs = (u16)((cfcs >> 1) ^ ((1 << 15) + (1 << 10) + (1 << 3))); + else + cfcs >>= 1; + } + } + return cfcs; +} + +/* + * check_initial_code_valid - check initial code valid or not + */ +static int check_initial_code_valid(struct i2c_client *client, u8 *buf) +{ + int ret = 0; + u16 initcode_ecc = 0; + u16 buf_ecc = 0; + u16 initcode_checksum = 0; + u16 buf_checksum = 0; + u16 hlic_len = 0; + + hlic_len = (u16)(((u16)buf[3]) << 8) + buf[2]; + if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", hlic_len); + return -EINVAL; + } + + initcode_checksum = cal_lcdinitcode_checksum(buf + 2, hlic_len - 2); + buf_checksum = ((u16)((u16)buf[1] << 8) + buf[0]); + FTS_DEBUG("lcd init code calc checksum:0x%04x,0x%04x", initcode_checksum, buf_checksum); + if (initcode_checksum != buf_checksum) { + FTS_ERROR("Initial Code checksum fail"); + return -EINVAL; + } + + ret = cal_lcdinitcode_ecc(buf, &initcode_ecc); + if (ret < 0) { + FTS_ERROR("lcd init code ecc calculate fail"); + return ret; + } + buf_ecc = ((u16)((u16)buf[5] << 8) + buf[4]); + FTS_DEBUG("lcd init code cal ecc:%04x, %04x", initcode_ecc, buf_ecc); + if (initcode_ecc != buf_ecc) { + FTS_ERROR("Initial Code ecc check fail"); + return -EINVAL; + } + + return 0; +} + +static int print_data(u8 *buf, u32 len) +{ + int i = 0; + int n = 0; + u8 *p = NULL; + + p = kmalloc(len * 4, GFP_KERNEL); + memset(p, 0, len * 4); + + for (i = 0; i < len; i++) + n += sprintf(p + n, "%02x ", buf[i]); + + FTS_DEBUG("%s", p); + + kfree(p); + return 0; +} + +/* + * description : find the address of one bank in initcode + * + * parameters : + * initcode : initcode + * bank_start_addr - the address of one bank, search bank from this address + * bank_sign - bank signature, 2 bytes + * bank_pos - return the position of the bank + * return: return 0 if success, otherwise return error code + */ +static int find_bank( + u8 *initcode, + u16 bank_start_addr, + u16 bank_sign, + u16 *bank_pos) +{ + u16 file_len = 0; + u16 pos = 0; + u8 bank[2] = { 0 }; + + file_len = (u16)(((u16)initcode[3] << 8) + initcode[2]); + if ((file_len >= FTS_MAX_LEN_SECTOR) || (file_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", file_len); + return -EINVAL; + } + + bank[0] = bank_sign >> 8; + bank[1] = bank_sign; + pos = bank_start_addr; + while (pos < file_len) { + if ((initcode[pos] == bank[0]) + && (initcode[pos + 1] == bank[1])) { + FTS_INFO("bank(%x %x) find", bank[0], bank[1]); + *bank_pos = pos; + return 0; + } else { + pos += ((u16)initcode[pos + 2] << 8 ) + initcode[pos + 3] + 4; + } + } + + return -ENODATA; +} + +static int read_3gamma(struct i2c_client *client, u8 **gamma, u16 *len) +{ + int ret = 0; + int i = 0; + int packet_num = 0; + int packet_len = 0; + int remainder = 0; + u8 cmd[4] = { 0 }; + u32 addr = 0x01D000; + u8 gamma_header[0x20] = { 0 }; + u16 gamma_len = 0; + u16 gamma_len_n = 0; + u16 pos = 0; + u8 *pgamma = NULL; + int j = 0; + u8 gamma_ecc = 0; + + cmd[0] = 0x03; + cmd[1] = (u8)(addr >> 16); + cmd[2] = (u8)(addr >> 8); + cmd[3] = (u8)addr; + ret = fts_i2c_write(client, cmd, 4); + msleep(10); + ret = fts_i2c_read(client, NULL, 0, gamma_header, 0x20); + if (ret < 0) { + FTS_ERROR("read 3-gamma header fail"); + return ret; + } + + gamma_len = (u16)((u16)gamma_header[0] << 8) + gamma_header[1]; + gamma_len_n = (u16)((u16)gamma_header[2] << 8) + gamma_header[3]; + + if ((gamma_len + gamma_len_n) != 0xFFFF) { + FTS_INFO("gamma length check fail:%x %x", gamma_len, gamma_len); + return -EIO; + } + + if ((gamma_header[4] + gamma_header[5]) != 0xFF) { + FTS_INFO("gamma ecc check fail:%x %x", gamma_header[4], gamma_header[5]); + return -EIO; + } + + if (gamma_len > MAX_GAMMA_LEN) { + FTS_ERROR("gamma data len(%d) is too long", gamma_len); + return -EINVAL; + } + + *gamma = kmalloc(MAX_GAMMA_LEN, GFP_KERNEL); + if (NULL == *gamma) { + FTS_ERROR("malloc gamma memory fail"); + return -ENOMEM; + } + pgamma = *gamma; + + packet_num = gamma_len / 256; + packet_len = 256; + remainder = gamma_len % 256; + if (remainder) + packet_num++; + FTS_INFO("3-gamma len:%d", gamma_len); + cmd[0] = 0x03; + addr += 0x20; + for (i = 0; i < packet_num; i++) { + addr += i * 256; + cmd[1] = (u8)(addr >> 16); + cmd[2] = (u8)(addr >> 8); + cmd[3] = (u8)addr; + if ((i == packet_num - 1) && remainder) + packet_len = remainder; + ret = fts_i2c_write(client, cmd, 4); + msleep(10); + ret = fts_i2c_read(client, NULL, 0, pgamma + i * 256, packet_len); + if (ret < 0) { + FTS_ERROR("read 3-gamma data fail"); + return ret; + } + } + + /* ecc */ + for (j = 0; j < gamma_len; j++) + gamma_ecc ^= pgamma[j]; + FTS_DEBUG("backup_3gamma_ecc: 0x%x, 0x%x", gamma_ecc, gamma_header[0x04]); + if (gamma_ecc != gamma_header[0x04]) { + FTS_ERROR("back gamma ecc check fail:%x %x", gamma_ecc, gamma_header[0x04]); + return -EIO; + } + + /* check last byte is 91 80 00 19 01 */ + pos = gamma_len - 5; + if ((gamma_enable[1] == pgamma[pos]) && (gamma_enable[2] == pgamma[pos + 1]) + && (gamma_enable[3] == pgamma[pos + 2]) && (gamma_enable[4] == pgamma[pos + 3])) { + gamma_has_enable = true; + } + + if (false == gamma_has_enable) { + FTS_INFO("3-gamma has no gamma enable info"); + pgamma[gamma_len++] = gamma_enable[1]; + pgamma[gamma_len++] = gamma_enable[2]; + pgamma[gamma_len++] = gamma_enable[3]; + pgamma[gamma_len++] = gamma_enable[4]; + pgamma[gamma_len++] = gamma_enable[5]; + } + + *len = gamma_len; + + FTS_DEBUG("read 3-gamma data:"); + print_data(*gamma, gamma_len); + + return 0; +} + +static int replace_3gamma(u8 *initcode, u8 *gamma, u16 gamma_len) +{ + int ret = 0; + u16 gamma_pos = 0; + int gamma_analog[] = {0x003B, 0x85, 0x00, 0x00, 0x2D, 0x2D}; + int gamma_digital1[] = {0x0374, 0x8D, 0x00, 0x00, 0x80, 0x80}; + int gamma_digital2[] = {0x03F8, 0x8D, 0x80, 0x00, 0x14, 0x14}; + u16 bank_addr = 0; + u16 bank_saddr = 0; + u16 bank_sign = 0; + u16 bank_len = 0; + + /* Analog Gamma */ + bank_saddr = LIC_BANK_START_ADDR; + bank_sign = ((u16)gamma_analog[1] << 8) + gamma_analog[2]; + ret = find_bank(initcode, LIC_BANK_START_ADDR, bank_sign, &bank_addr); + if (ret < 0) { + FTS_ERROR("find bank analog gamma fail"); + goto find_gamma_bank_err; + } + bank_len = ((u16)initcode[bank_addr + 2] << 8 ) + initcode[bank_addr + 3]; + memcpy(initcode + bank_addr + 4 , gamma + gamma_pos + 4, bank_len); + initcode[bank_addr + 4] = 0xA5; + gamma_pos += bank_len + 4; + + /* Digital1 Gamma */ + bank_saddr = bank_addr; + bank_sign = ((u16)gamma_digital1[1] << 8) + gamma_digital1[2]; + ret = find_bank(initcode, bank_saddr, bank_sign, &bank_addr); + if (ret < 0) { + FTS_ERROR("find bank analog gamma fail"); + goto find_gamma_bank_err; + } + bank_len = ((u16)initcode[bank_addr + 2] << 8 ) + initcode[bank_addr + 3]; + memcpy(initcode + bank_addr + 4 , gamma + gamma_pos + 4, bank_len); + gamma_pos += bank_len + 4; + + /* Digital2 Gamma */ + bank_saddr = bank_addr; + bank_sign = ((u16)gamma_digital2[1] << 8) + gamma_digital2[2]; + ret = find_bank(initcode, bank_saddr, bank_sign, &bank_addr); + if (ret < 0) { + FTS_ERROR("find bank analog gamma fail"); + goto find_gamma_bank_err; + } + bank_len = ((u16)initcode[bank_addr + 2] << 8 ) + initcode[bank_addr + 3]; + memcpy(initcode + bank_addr + 4 , gamma + gamma_pos + 4, bank_len); + gamma_pos += bank_len + 4; + + /* enable Gamma */ + bank_saddr = bank_addr; + bank_sign = ((u16)gamma_enable[1] << 8) + gamma_enable[2]; + ret = find_bank(initcode, bank_saddr, bank_sign, &bank_addr); + if (ret < 0) { + FTS_ERROR("find bank analog gamma fail"); + goto find_gamma_bank_err; + } + if (gamma[gamma_pos + 4]) + initcode[bank_addr + 4 + 15] |= 0x01; + else + initcode[bank_addr + 4 + 15] &= 0xFE; + gamma_pos += 1 + 4; + + FTS_DEBUG("replace 3-gamma data:"); + print_data(initcode, 1100); + + return 0; + +find_gamma_bank_err: + FTS_INFO("3-gamma bank(%02x %02x) not find", + gamma[gamma_pos], gamma[gamma_pos + 1]); + return -ENODATA; +} + +static int cal_replace_ecc(u8 *initcode) +{ + int ret = 0; + u16 initcode_ecc = 0; + u16 bank31 = 0x9200; + u16 bank31_addr = 0; + + ret = cal_lcdinitcode_ecc(initcode, &initcode_ecc); + if (ret < 0) { + FTS_ERROR("lcd init code ecc calculate fail"); + return ret; + } + FTS_INFO("lcd init code cal ecc:%04x", initcode_ecc); + initcode[LIC_LCD_ECC_H_OFF] = (u8)(initcode_ecc >> 8); + initcode[LIC_LCD_ECC_L_OFF] = (u8)(initcode_ecc); + ret = find_bank(initcode, LIC_BANK_START_ADDR, bank31, &bank31_addr); + if (ret < 0) { + FTS_ERROR("find bank 31 fail"); + return ret; + } + FTS_INFO("lcd init code ecc bank addr:0x%04x", bank31_addr); + initcode[bank31_addr + LIC_BANKECC_H_OFF + 4] = (u8)(initcode_ecc >> 8); + initcode[bank31_addr + LIC_BANKECC_L_OFF + 4] = (u8)(initcode_ecc); + + return 0; +} + +/* + * read_replace_3gamma - read and replace 3-gamma data + */ +static int read_replace_3gamma(struct i2c_client *client, u8 *buf, bool flag) +{ + int ret = 0; + u16 initcode_checksum = 0; + u8 *tmpbuf = NULL; + u8 *gamma = NULL; + u16 gamma_len = 0; + u16 hlic_len = 0; + int base_addr = 0; + int i = 0; + + FTS_FUNC_ENTER(); + + ret = read_3gamma(client, &gamma, &gamma_len); + if (ret < 0) { + FTS_INFO("no vaid 3-gamma data, not replace"); + if (gamma) { + kfree(gamma); + gamma = NULL; + } + return 0; + } + + base_addr = 0; + for (i = 0; i < 2; i++) { + if (i == 1) { + if (flag == true) + base_addr = 0x7C0; + else + break; + } + + tmpbuf = buf + base_addr; + ret = replace_3gamma(tmpbuf, gamma, gamma_len); + if (ret < 0) { + FTS_ERROR("replace 3-gamma fail"); + goto REPLACE_GAMMA_ERR; + } + + ret = cal_replace_ecc(tmpbuf); + if (ret < 0) { + FTS_ERROR("lcd init code ecc calculate/replace fail"); + goto REPLACE_GAMMA_ERR; + } + + hlic_len = (u16)(((u16)tmpbuf[3]) << 8) + tmpbuf[2]; + if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", hlic_len); + ret = -EINVAL; + goto REPLACE_GAMMA_ERR; + } + initcode_checksum = cal_lcdinitcode_checksum(tmpbuf + 2, hlic_len - 2); + FTS_INFO("lcd init code calc checksum:0x%04x", initcode_checksum); + tmpbuf[LIC_CHECKSUM_H_OFF] = (u8)(initcode_checksum >> 8); + tmpbuf[LIC_CHECKSUM_L_OFF] = (u8)(initcode_checksum); + } + + if (gamma) { + kfree(gamma); + gamma = NULL; + } + + FTS_FUNC_EXIT(); + return 0; + +REPLACE_GAMMA_ERR: + if (gamma) { + kfree(gamma); + gamma = NULL; + } + return ret; +} + +static int fts_ft8006u_upgrade_mode( + struct i2c_client *client, + enum FW_FLASH_MODE mode, + u8 *buf, + u32 len) +{ + int ret = 0; + bool flag = false; + u32 start_addr = 0; + u8 cmd[4] = { 0 }; + u32 delay = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + + if ((NULL == buf) || (len < FTS_MIN_LEN)) { + FTS_ERROR("buffer/len(%x) is invalid", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(client); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret); + goto fw_reset; + } + + cmd[0] = FTS_CMD_FLASH_MODE; + cmd[1] = FLASH_MODE_UPGRADE_VALUE; + start_addr = upgrade_func_ft8006u.appoff; + if (FLASH_MODE_LIC == mode) { + /* lcd initial code upgrade */ + /* read replace 3-gamma yet */ + ret = read_replace_3gamma(client, buf, flag); + if (ret < 0) { + FTS_ERROR("replace 3-gamma fail, not upgrade lcd init code"); + goto fw_reset; + } + cmd[1] = FLASH_MODE_LIC_VALUE; + start_addr = upgrade_func_ft8006u.licoff; + } else if (FLASH_MODE_PARAM == mode) { + cmd[1] = FLASH_MODE_PARAM_VALUE; + start_addr = upgrade_func_ft8006u.paramcfgoff; + } + FTS_DEBUG("flash mode:0x%02x, start addr=0x%04x", cmd[1], start_addr); + + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto fw_reset; + } + + delay = FTS_ERASE_SECTOR_DELAY * (len / FTS_MAX_LEN_SECTOR); + ret = fts_fwupg_erase(client, delay); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + ecc_in_host = fts_flash_write_buf(client, start_addr, buf, len, 1); + if (ecc_in_host < 0 ) { + FTS_ERROR("lcd initial code write fail"); + goto fw_reset; + } + + /* ecc */ + ecc_in_tp = fts_fwupg_ecc_cal(client, start_addr, len); + if (ecc_in_tp < 0 ) { + FTS_ERROR("ecc read fail"); + goto fw_reset; + } + + FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc check fail"); + goto fw_reset; + } + + FTS_INFO("upgrade success, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) + FTS_ERROR("reset to normal boot fail"); + + msleep(400); + return 0; + +fw_reset: + return -EIO; +} + +/* + * fts_ft8006u_get_hlic_ver - read host lcd init code version + * + * return 0 if host lcd init code is valid, otherwise return error code + */ +static int fts_ft8006u_get_hlic_ver(u8 *initcode) +{ + u8 *hlic_buf = initcode; + u16 hlic_len = 0; + u8 hlic_ver[2] = { 0 }; + + hlic_len = (u16)(((u16)hlic_buf[3]) << 8) + hlic_buf[2]; + FTS_INFO("host lcd init code len:%x", hlic_len); + if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", hlic_len); + return -EINVAL; + } + + hlic_ver[0] = hlic_buf[hlic_len]; + hlic_ver[1] = hlic_buf[hlic_len + 1]; + + FTS_DEBUG("host lcd init code ver:%x %x", hlic_ver[0], hlic_ver[1]); + if (0xFF != (hlic_ver[0] + hlic_ver[1])) { + FTS_ERROR("host lcd init code version check fail"); + return -EINVAL; + } + + return hlic_ver[0]; + } + +/************************************************************************ +* Name: fts_ft8006u_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft8006u_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 *tmpbuf = NULL; + u32 app_len = 0; + + FTS_DEBUG("fw app upgrade..."); + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw buffer len(%x) fail", len); + return -EINVAL; + } + + app_len = len - upgrade_func_ft8006u.appoff; + tmpbuf = buf + upgrade_func_ft8006u.appoff; + ret = fts_ft8006u_upgrade_mode(client, FLASH_MODE_APP, tmpbuf, app_len); + if (ret < 0) { + FTS_INFO("fw upgrade fail,reset to normal boot"); + if (fts_fwupg_reset_in_boot(client) < 0) + FTS_ERROR("reset to normal boot fail"); + return ret; + } + + return 0; +} + +/************************************************************************ +* Name: fts_ft8006u_lic_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft8006u_lic_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 *tmpbuf = NULL; + u32 lic_len = 0; + + FTS_DEBUG("lcd initial code upgrade..."); + if (NULL == buf) { + FTS_ERROR("lcd initial code buffer is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("lcd initial code buffer len(%x) fail", len); + return -EINVAL; + } + + ret = check_initial_code_valid(client, buf); + if (ret < 0) { + FTS_ERROR("initial code invalid, not upgrade lcd init code"); + return -EINVAL; + } + + /* remalloc memory for initcode, need change content of initcode afterwise */ + lic_len = FTS_MAX_LEN_SECTOR; + tmpbuf = kmalloc(lic_len, GFP_KERNEL); + if (NULL == tmpbuf) { + FTS_INFO("initial code buf malloc fail"); + return -EINVAL; + } + memcpy(tmpbuf, buf, lic_len); + + ret = fts_ft8006u_upgrade_mode(client, FLASH_MODE_LIC, tmpbuf, lic_len); + if (ret < 0) { + FTS_INFO("lcd initial code upgrade fail,reset to normal boot"); + if (fts_fwupg_reset_in_boot(client) < 0) + FTS_ERROR("reset to normal boot fail"); + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } + return ret; + } + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } + return 0; +} + +/************************************************************************ + * Name: fts_ft8006u_param_upgrade + * Brief: + * Input: buf - all.bin + * len - len of all.bin + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_ft8006u_param_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 *tmpbuf = NULL; + u32 param_length = 0; + + FTS_DEBUG("parameter configure upgrade..."); + if (NULL == buf) { + FTS_ERROR("fw file buffer is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file buffer len(%x) fail", len); + return -EINVAL; + } + + tmpbuf = buf + upgrade_func_ft8006u.paramcfgoff; + param_length = len - upgrade_func_ft8006u.paramcfgoff; + ret = fts_ft8006u_upgrade_mode(client, FLASH_MODE_PARAM, tmpbuf, param_length); + if (ret < 0) { + FTS_INFO("fw upgrade fail,reset to normal boot"); + if (fts_fwupg_reset_in_boot(client) < 0) + FTS_ERROR("reset to normal boot fail"); + return ret; + } + + return 0; +} + +/************************************************************************ + * fts_ft8006u_pram_write_remap - write pramboot to pram and start pramboot + * + * return 0 if success, otherwise return error code +***********************************************************************/ +int fts_ft8006u_pram_write_remap(struct i2c_client *client) +{ + int ret = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + u8 *pb_buf = NULL; + u32 pb_len = 0; + const struct firmware *fw = NULL; + //struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("[UPGRADE]write pram and remap\n"); + + ret = request_firmware(&fw, FT8006U_PRAMBOOT_FW_NAME, &client->dev); + if (ret < 0) { + FTS_ERROR("[UPGRADE] Request pramboot failed - %s (%d)\n", + FT8006U_PRAMBOOT_FW_NAME, ret); + return ret; + } + + if (fw->size < FTS_MIN_LEN) { + FTS_ERROR("[UPGRADE]pramboot length(%x) fail\n", (unsigned int)fw->size); + return -EINVAL; + } + + pb_buf = (u8*)fw->data; + pb_len= (u32)fw->size; + + /* write pramboot to pram */ + ecc_in_host = fts_pram_write_buf(client, pb_buf, pb_len); + if (ecc_in_host < 0) { + FTS_ERROR( "write pramboot fail"); + return ecc_in_host; + } + + /* read out checksum */ + ecc_in_tp = fts_pram_ecc_cal(client, 0, pb_len); + if (ecc_in_tp < 0) { + FTS_ERROR( "read pramboot ecc fail"); + return ecc_in_tp; + } + + FTS_DEBUG("[UPGRADE]pram ecc in tp:%x, host:%x\n", ecc_in_tp, ecc_in_host); + /* pramboot checksum != fw checksum, upgrade fail */ + if (ecc_in_host != ecc_in_tp) { + FTS_ERROR("[UPGRADE]pramboot ecc check fail\n"); + return -EIO; + } + + /*start pram*/ + ret = fts_pram_start(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram start fail\n"); + return ret; + } + + return 0; +} + +struct upgrade_func upgrade_func_ft8006u = { + .ctype = {0x0B}, + .fwveroff = 0x210E, + .fwcfgoff = 0x1F80, + .appoff = 0x2000, + .licoff = 0x0000, + .paramcfgoff = 0x12000, + .paramcfgveroff = 0x12004, + .pramboot_supported = true, + .hid_supported = false, + .fts_8006u = true, + .upgrade = fts_ft8006u_upgrade, + .get_hlic_ver = fts_ft8006u_get_hlic_ver, + .lic_upgrade = fts_ft8006u_lic_upgrade, + .param_upgrade = fts_ft8006u_param_upgrade, +}; diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8716.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8716.c new file mode 100644 index 000000000000..b69498847a84 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8716.c @@ -0,0 +1,396 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include +#include "focaltech_flash.h" +#include "focaltech_upgrade_common.h" + +#define APP_FILE_MAX_SIZE (64 * 1024) +#define APP_FILE_MIN_SIZE (8) +#define APP_FILE_VER_MAPPING (0x10E) +#define APP_FILE_VENDORID_MAPPING (0x10C) +#define APP_FILE_CHIPID_MAPPING (0x11E) +#define CONFIG_START_ADDR (0x0000) +#define CONFIG_VENDOR_ID_OFFSET (0x04) +#define CONFIG_PROJECT_ID_OFFSET (0x20) +#define CONFIG_VENDOR_ID_ADDR (CONFIG_START_ADDR+ \ + CONFIG_VENDOR_ID_OFFSET) +#define CONFIG_PROJECT_ID_ADDR (CONFIG_START_ADDR+ \ + CONFIG_PROJECT_ID_OFFSET) + +#define FW_CFG_TOTAL_SIZE 0x00 +#define APP1_START 0x00 +#define APP1_LEN 0x100 +#define APP_VERIF_ADDR (APP1_START + APP1_LEN) +#define APP_VERIF_LEN 0x20 +#define APP1_ECC_ADDR (APP_VERIF_ADDR + APP_P1_ECC) +#define APP2_START (APP_VERIF_ADDR + APP_VERIF_LEN + \ + FW_CFG_TOTAL_SIZE) +#define APP2_ECC_ADDR (APP_VERIF_ADDR + APP_P2_ECC) + +#define FT8716_PRAMBOOT_FW_NAME "FT8716_Pramboot.bin" + +static int fts_ctpm_get_app_bin_file_ver(const char *firmware_name); +static int fts_ctpm_fw_upgrade_with_app_bin_file( + struct i2c_client *client, const char *firmware_name); + +static struct fts_upgrade_fun fts_updatefun_8716 = { + .get_app_bin_file_ver = fts_ctpm_get_app_bin_file_ver, + .upgrade_with_app_bin_file = fts_ctpm_fw_upgrade_with_app_bin_file, + .upgrade_with_lcd_cfg_bin_file = NULL, +}; + + +static struct ft_chip_t fts_chip_type_8716 = { + .type = 0x05, + .chip_idh = 0x87, + .chip_idl = 0x16, + .rom_idh = 0x87, + .rom_idl = 0x16, + .pramboot_idh = 0x87, + .pramboot_idl = 0xA6, + .bootloader_idh = 0x00, + .bootloader_idl = 0x00, + .chip_type = 0x87160805, +}; + +inline void ft8716_set_upgrade_function(struct fts_upgrade_fun **curr) +{ + *curr = &fts_updatefun_8716; +} +inline void ft8716_set_chip_id(struct ft_chip_t **curr) +{ + *curr = &fts_chip_type_8716; +} + +static int fts_ctpm_get_app_bin_file_ver(const char *firmware_name) +{ + int fw_ver = 0; + int rc; + const struct firmware *fw = NULL; + + FTS_FUNC_ENTER(); + + rc = request_firmware(&fw, firmware_name, NULL); + if (rc < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + firmware_name, rc); + return rc; + } + if (fw->size < APP_FILE_MIN_SIZE || fw->size > APP_FILE_MAX_SIZE) { + FTS_ERROR("[UPGRADE] FW length(%zd) error\n", fw->size); + return -EIO; + goto file_ver_rel_fw; + } + + fw_ver = fw->data[APP_FILE_VER_MAPPING]; + +file_ver_rel_fw: + release_firmware(fw); + FTS_FUNC_EXIT(); + + return fw_ver; +} + +static int fts_ctpm_get_vendor_id_flash(struct i2c_client *client) +{ + int i_ret = 0; + u8 vendorid[4] = {0}; + u8 auc_i2c_write_buf[10]; + u8 i = 0; + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + auc_i2c_write_buf[0] = 0x03; + auc_i2c_write_buf[1] = 0x00; + + auc_i2c_write_buf[2] = (u8)((CONFIG_VENDOR_ID_ADDR - 1) >> 8); + auc_i2c_write_buf[3] = (u8)(CONFIG_VENDOR_ID_ADDR - 1); + i_ret = fts_i2c_read(client, auc_i2c_write_buf, 4, vendorid, 2); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] read flash i_ret = %d\n", i_ret); + continue; + } + break; + } + + FTS_INFO("[UPGRADE] vendor id from flash rom: 0x%x\n", vendorid[1]); + if (i >= FTS_UPGRADE_LOOP) { + FTS_ERROR("[UPGRADE] read vendor id failed\n"); + return -EIO; + } + + return 0; +} + +static int fts_ctpm_write_pram(struct i2c_client *client, + const char *pramfw_name) +{ + int i_ret; + bool inrom = false; + const struct firmware *pramfw = NULL; + + FTS_FUNC_ENTER(); + + i_ret = request_firmware(&pramfw, pramfw_name, &client->dev); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + pramfw_name, i_ret); + return i_ret; + } + + /*check the length of the pramboot*/ + if ((pramfw->size > APP_FILE_MAX_SIZE) || + (pramfw->size < APP_FILE_MIN_SIZE)) { + FTS_ERROR("[UPGRADE] pramboot length(%zd) fail\n", + pramfw->size); + i_ret = -EINVAL; + goto write_pram_rel_fw; + } + + /*send command to FW, reset and start write pramboot*/ + i_ret = fts_ctpm_start_fw_upgrade(client); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] send upgrade cmd to FW error\n"); + goto write_pram_rel_fw; + } + + /*check run in rom or not! if run in rom, will write pramboot*/ + inrom = fts_ctpm_check_run_state(client, FTS_RUN_IN_ROM); + if (!inrom) { + FTS_ERROR("[UPGRADE] not run in rom, write pramboot fail\n"); + i_ret = -EIO; + goto write_pram_rel_fw; + } + + /*write pramboot to pram*/ + i_ret = fts_ctpm_write_pramboot_for_idc(client, + pramfw->size, pramfw->data); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] write pramboot fail\n"); + goto write_pram_rel_fw; + } + + /*read out checksum*/ + i_ret = fts_ctpm_pramboot_ecc(client); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] write pramboot ecc error\n"); + goto write_pram_rel_fw; + } + + /*start pram*/ + fts_ctpm_start_pramboot(client); + + FTS_FUNC_EXIT(); + +write_pram_rel_fw: + release_firmware(pramfw); + return i_ret; +} + +static int fts_ctpm_write_app(struct i2c_client *client, + const u8 *pbt_buf, u32 dw_length) +{ + u32 temp; + int i_ret; + bool inpram = false; + + FTS_FUNC_ENTER(); + + /*check run in pramboot or not if not rum in pramboot, can not upgrade*/ + inpram = fts_ctpm_check_run_state(client, FTS_RUN_IN_PRAM); + if (!inpram) { + FTS_ERROR("[UPGRADE] not run in pram, upgrade fail\n"); + return -EIO; + } + + /*upgrade init*/ + i_ret = fts_ctpm_upgrade_idc_init(client, dw_length); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] upgrade init error, upgrade fail\n"); + return i_ret; + } + + /*read vendor id from flash, if vendor id error, can not upgrade*/ + i_ret = fts_ctpm_get_vendor_id_flash(client); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] read vendor id in flash fail\n"); + return i_ret; + } + + /*erase the app erea in flash*/ + i_ret = fts_ctpm_erase_flash(client); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] erase flash error\n"); + return i_ret; + } + + /*start to write app*/ + i_ret = fts_ctpm_write_app_for_idc(client, dw_length, pbt_buf); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] write app error\n"); + return i_ret; + } + + /*read check sum*/ + temp = 0x1000; + i_ret = fts_ctpm_upgrade_ecc(client, temp, dw_length); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] ecc error\n"); + return i_ret; + } + + FTS_FUNC_EXIT(); + + return 0; +} + +static int fts_ctpm_fw_upgrade_use_buf(struct i2c_client *client, + const u8 *pbt_buf, u32 fwsize) +{ + int i_ret = 0; + + FTS_FUNC_ENTER(); + FTS_DEBUG("[UPGRADE] pramboot\n"); + i_ret = fts_ctpm_write_pram(client, FT8716_PRAMBOOT_FW_NAME); + if (i_ret != 0) { + FTS_ERROR("[UPGRADE] write pram failed\n"); + return -EIO; + } + + i_ret = fts_ctpm_write_app(client, pbt_buf, fwsize); + + FTS_FUNC_EXIT(); + + return i_ret; +} + +static int fts_ctpm_check_vendor_id(struct i2c_client *client, + const u8 *data) +{ + u8 vendor_id; + + fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &vendor_id); + if ((vendor_id != 0x8D) && + (data[APP_FILE_VENDORID_MAPPING] != vendor_id)) { + FTS_ERROR("[UPGRADE] vendor id(%02x)[%02x] is error\n", + data[APP_FILE_VENDORID_MAPPING], vendor_id); + return -EINVAL; + } + return 0; +} + +static int fts_ctpm_check_chip_id(const u8 *data) +{ + if ((data[APP_FILE_CHIPID_MAPPING] != + fts_chip_type_8716.pramboot_idh) || + (data[APP_FILE_CHIPID_MAPPING + 1] != + fts_chip_type_8716.pramboot_idl)) { + FTS_ERROR("[UPGRADE] chip id(%02X%02X)[%02X%02X] is error\n", + data[APP_FILE_CHIPID_MAPPING], + data[APP_FILE_CHIPID_MAPPING + 1], + fts_chip_type_8716.pramboot_idh, + fts_chip_type_8716.pramboot_idl); + return -EINVAL; + } + return 0; +} + +static bool fts_ctpm_check_app_bin_valid(const u8 *pbt_buf) +{ + u32 len; + + if (pbt_buf[0] != 0x02) { + FTS_ERROR("[UPGRADE] APP.BIN Verify the first byte(%x) error\n", + pbt_buf[0]); + return -EINVAL; + } + + if (!fts_ecc_check(pbt_buf, APP1_START, APP1_LEN, APP1_ECC_ADDR)) { + FTS_ERROR("[UPGRADE] APP.BIN Verify- ecc1 error\n"); + return -EINVAL; + } + + if ((fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN) + + fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN_NE)) != + 0xFFFF) { + FTS_ERROR("[UPGRADE] APP.BIN Verify- Length XOR error\n"); + return -EINVAL; + } + + len = fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN); + FTS_DEBUG("%x %x %x %x", APP2_START, len, + ((u32)fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN_H) << 16), + fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN)); + len -= APP2_START; + if (!fts_ecc_check(pbt_buf, APP2_START, len, APP2_ECC_ADDR)) { + FTS_ERROR("[UPGRADE] APP.BIN Verify- ecc2 error\n"); + return -EINVAL; + } + + return 0; +} + +static int fts_ctpm_fw_upgrade_with_app_bin_file + (struct i2c_client *client, const char *firmware_name) +{ + int i_ret = 0; + const struct firmware *fw = NULL; + + FTS_INFO("[UPGRADE] start upgrade with app.bin\n"); + + i_ret = request_firmware(&fw, firmware_name, &client->dev); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + firmware_name, i_ret); + return i_ret; + } + + if (fw->size < APP_FILE_MIN_SIZE || fw->size > APP_FILE_MAX_SIZE) { + FTS_ERROR("[UPGRADE] FW length(%zd) error\n", fw->size); + i_ret = -EINVAL; + goto file_upgrade_rel_fw; + } + + i_ret = fts_ctpm_check_vendor_id(client, fw->data); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] check vendor id failed\n"); + goto file_upgrade_rel_fw; + } + + i_ret = fts_ctpm_check_chip_id(fw->data); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] check chip id failed\n"); + goto file_upgrade_rel_fw; + } + + i_ret = fts_ctpm_check_app_bin_valid(fw->data); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] app.bin ecc failed\n"); + goto file_upgrade_rel_fw; + } + + i_ret = fts_ctpm_fw_upgrade_use_buf(client, fw->data, fw->size); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] upgrade app.bin failed\n"); + goto file_upgrade_rel_fw; + } + +file_upgrade_rel_fw: + release_firmware(fw); + return i_ret; +} diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_idc.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_idc.c new file mode 100644 index 000000000000..90ed285a4c9f --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_idc.c @@ -0,0 +1,410 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include "focaltech_flash.h" +#include "focaltech_upgrade_common.h" + +static u8 upgrade_ecc; + +int fts_ctpm_upgrade_idc_init(struct i2c_client *client, u32 dw_length) +{ + int i_ret = 0; + u8 reg_val_id[4] = {0}; + u8 auc_i2c_write_buf[10]; + + FTS_INFO("[UPGRADE] Upgrade setting Init\n"); + + /*read flash ID*/ + auc_i2c_write_buf[0] = 0x05; + reg_val_id[0] = 0x00; + i_ret = fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val_id, 1); + if (i_ret < 0) + return -EIO; + + if (dw_length == 0) + return -EIO; + + /*check the fw length by flash type*/ + if (0x81 == (int)reg_val_id[0]) { + if (dw_length > 1024 * 60) + return -EIO; + } else if (0x80 == (int)reg_val_id[0]) { + if (dw_length > 1024 * 64) + return -EIO; + } + + /*set flash clk*/ + auc_i2c_write_buf[0] = 0x05; + auc_i2c_write_buf[1] = reg_val_id[0]; + auc_i2c_write_buf[2] = 0x00; + fts_i2c_write(client, auc_i2c_write_buf, 3); + + /*send upgrade type to reg 0x09: 0x0B: upgrade; 0x0A: download*/ + auc_i2c_write_buf[0] = 0x09; + auc_i2c_write_buf[1] = 0x0B; + fts_i2c_write(client, auc_i2c_write_buf, 2); + + return 0; +} + + +void fts_ctpm_start_pramboot(struct i2c_client *client) +{ + u8 auc_i2c_write_buf[10]; + + FTS_INFO("[UPGRADE] start pramboot\n"); + auc_i2c_write_buf[0] = 0x08; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(20); +} + + +int fts_ctpm_start_fw_upgrade(struct i2c_client *client) +{ + int i_ret = 0; + + /*send the soft upgrade commond to FW, and start upgrade*/ + FTS_INFO("[UPGRADE] send 0xAA and 0x55 to FW, start upgrade\n"); + + i_ret = fts_i2c_write_reg(client, FTS_RST_CMD_REG1, FTS_UPGRADE_AA); + msleep(20); + i_ret = fts_i2c_write_reg(client, FTS_RST_CMD_REG1, FTS_UPGRADE_55); + msleep(200); + + return i_ret; +} + + +bool fts_ctpm_check_run_state(struct i2c_client *client, int rstate) +{ + int i = 0; + enum FW_STATUS cstate = FTS_RUN_IN_ERROR; + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + cstate = fts_ctpm_get_pram_or_rom_id(client); + FTS_DEBUG("[UPGRADE] run state = %d\n", cstate); + + if (cstate == rstate) + return true; + msleep(20); + } + + return false; +} + + +int fts_ctpm_pramboot_ecc(struct i2c_client *client) +{ + u8 auc_i2c_write_buf[10]; + u8 reg_val[4] = {0}; + + FTS_FUNC_ENTER(); + + /*read out checksum if pramboot checksum != host checksum upgrade fail*/ + FTS_INFO("[UPGRADE] read out pramboot checksum\n"); + auc_i2c_write_buf[0] = 0xcc; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 1); + /*pramboot checksum != host checksum, upgrade fail*/ + if (reg_val[0] != upgrade_ecc) { + FTS_ERROR("[UPGRADE] checksum fail pramboot=%x, host=%x\n", + reg_val[0], upgrade_ecc); + return -EIO; + } + FTS_DEBUG("[UPGRADE] checksum success pramboot=%x, host=%x\n", + reg_val[0], upgrade_ecc); + msleep(100); + + FTS_FUNC_EXIT(); + + return 0; +} + + +int fts_ctpm_upgrade_ecc(struct i2c_client *client, + u32 startaddr, u32 length) +{ + u32 i = 0; + u8 auc_i2c_write_buf[10]; + u32 temp; + u8 reg_val[4] = {0}; + int i_ret = 0; + + FTS_INFO("[UPGRADE] read out checksum\n"); + + /*check sum init*/ + auc_i2c_write_buf[0] = 0x64; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(300); + + /*send commond to pramboot to start checksum*/ + auc_i2c_write_buf[0] = 0x65; + auc_i2c_write_buf[1] = (u8)(startaddr >> 16); + auc_i2c_write_buf[2] = (u8)(startaddr >> 8); + auc_i2c_write_buf[3] = (u8)(startaddr); + + if (length > LEN_FLASH_ECC_MAX) + temp = LEN_FLASH_ECC_MAX; + else + temp = length; + + auc_i2c_write_buf[4] = (u8)(temp >> 8); + auc_i2c_write_buf[5] = (u8)(temp); + i_ret = fts_i2c_write(client, auc_i2c_write_buf, 6); + msleep(length / 256); + + /*read status : if check sum is finished*/ + for (i = 0; i < 100; i++) { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + if (0xF0 == reg_val[0] && 0x55 == reg_val[1]) + break; + + msleep(20); + } + + if (length > LEN_FLASH_ECC_MAX) { + temp = LEN_FLASH_ECC_MAX; + auc_i2c_write_buf[0] = 0x65; + auc_i2c_write_buf[1] = (u8)(temp >> 16); + auc_i2c_write_buf[2] = (u8)(temp >> 8); + auc_i2c_write_buf[3] = (u8)(temp); + temp = length - LEN_FLASH_ECC_MAX; + auc_i2c_write_buf[4] = (u8)(temp >> 8); + auc_i2c_write_buf[5] = (u8)(temp); + i_ret = fts_i2c_write(client, auc_i2c_write_buf, 6); + + msleep(length / 256); + + for (i = 0; i < 100; i++) { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + if (0xF0 == reg_val[0] && 0x55 == reg_val[1]) + break; + + msleep(20); + } + } + + /*read out check sum*/ + auc_i2c_write_buf[0] = 0x66; + i_ret = fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 1); + if (reg_val[0] != upgrade_ecc) { /*if check sum fail, upgrade fail*/ + FTS_ERROR("[UPGRADE] ecc error! FW=%02x upgrade_ecc=%02x\n", + reg_val[0], upgrade_ecc); + return -EIO; + } + + FTS_DEBUG("[UPGRADE] ecc success : FW=%02x upgrade_ecc=%02x\n", + reg_val[0], upgrade_ecc); + + upgrade_ecc = 0; + + return i_ret; +} + + +int fts_ctpm_erase_flash(struct i2c_client *client) +{ + u32 i = 0; + u8 auc_i2c_write_buf[10]; + u8 reg_val[4] = {0}; + + FTS_INFO("[UPGRADE] erase app now\n"); + + /*send to erase flash*/ + auc_i2c_write_buf[0] = 0x61; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(1350); + + for (i = 0; i < 15; i++) { + /*get the erase app status, if get 0xF0AA٬erase flash success*/ + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + /*erase flash success*/ + if (0xF0 == reg_val[0] && 0xAA == reg_val[1]) + break; + + msleep(50); + } + + /*erase flash fail*/ + if ((0xF0 != reg_val[0] || 0xAA != reg_val[1]) && (i >= 15)) { + FTS_ERROR("[UPGRADE] erase app error.reset tp and reload FW\n"); + return -EIO; + } + FTS_DEBUG("[UPGRADE] erase app ok\n"); + + return 0; +} + + +int fts_ctpm_write_pramboot_for_idc(struct i2c_client *client, + u32 length, const u8 *readbuf) +{ + u32 i = 0; + u32 j; + u32 temp; + u32 packet_number; + u8 packet_buf[FTS_PACKET_LENGTH + 6]; + + upgrade_ecc = 0; + FTS_INFO("[UPGRADE] write pramboot to pram\n"); + + temp = 0; + packet_number = (length) / FTS_PACKET_LENGTH; + if ((length) % FTS_PACKET_LENGTH > 0) + packet_number++; + + packet_buf[0] = 0xae; + packet_buf[1] = 0x00; + + for (j = 0; j < packet_number; j++) { + temp = j * FTS_PACKET_LENGTH; + packet_buf[2] = (u8) (temp >> 8); + packet_buf[3] = (u8) temp; + if (j < (packet_number - 1)) + temp = FTS_PACKET_LENGTH; + else + temp = (length) % FTS_PACKET_LENGTH; + + packet_buf[4] = (u8) (temp >> 8); + packet_buf[5] = (u8) temp; + + for (i = 0; i < temp; i++) { + packet_buf[6 + i] = readbuf[j * FTS_PACKET_LENGTH + i]; + upgrade_ecc ^= packet_buf[6 + i]; + } + fts_i2c_write(client, packet_buf, temp + 6); + } + + return 0; +} + + +int fts_ctpm_write_app_for_idc(struct i2c_client *client, + u32 length, const u8 *readbuf) +{ + u32 j; + u32 i = 0; + u32 packet_number; + u32 temp; + u32 writelenght; + u8 packet_buf[FTS_PACKET_LENGTH + 6]; + u8 auc_i2c_write_buf[10]; + u8 reg_val[4] = {0}; + + FTS_INFO("[UPGRADE] write app to flash\n"); + + upgrade_ecc = 0; + + packet_number = (length) / FTS_PACKET_LENGTH; + if (((length) % FTS_PACKET_LENGTH) > 0) + packet_number++; + + packet_buf[0] = 0xbf; + + for (j = 0; j < packet_number; j++) { + temp = 0x1000 + j * FTS_PACKET_LENGTH; + + if (j < (packet_number - 1)) + writelenght = FTS_PACKET_LENGTH; + else + writelenght = ((length) % FTS_PACKET_LENGTH); + + packet_buf[1] = (u8) (temp >> 16); + packet_buf[2] = (u8) (temp >> 8); + packet_buf[3] = (u8) temp; + packet_buf[4] = (u8) (writelenght >> 8); + packet_buf[5] = (u8) writelenght; + + for (i = 0; i < writelenght; i++) { + packet_buf[6 + i] = readbuf[(temp - 0x1000 + i)]; + upgrade_ecc ^= packet_buf[6 + i]; + } + + fts_i2c_write(client, packet_buf, (writelenght + 6)); + + for (i = 0; i < 30; i++) { + /*read status and check if the app write is finished*/ + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + if ((j + 0x20 + 0x1000) == + (((reg_val[0]) << 8) | reg_val[1])) + break; + + msleep(20); + } + } + msleep(50); + + return 0; +} + + +#define AL2_FCS_COEF ((1 << 15) + (1 << 10) + (1 << 3)) + +static u16 fts_crc_calc(const u8 *pbt_buf, u32 addr, u16 length) +{ + u16 cFcs = 0; + u16 i, j; + + FTS_DEBUG("[UPGRADE][ECC] %04x data:%04x, len:%04x\n", + (addr), fts_data_word(pbt_buf, (addr)), length); + for (i = 0; i < length; i++) { + cFcs ^= fts_data_word(pbt_buf, (addr + i * 2)); + for (j = 0; j < 16; j++) { + if (cFcs & 1) + cFcs = (u16)((cFcs >> 1) ^ AL2_FCS_COEF); + else + cFcs >>= 1; + } + } + return cFcs; +} + +bool fts_ecc_check(const u8 *pbt_buf, u32 star_addr, u32 len, u16 ecc_addr) +{ + u16 ecc1; + u16 ecc2; + u16 cal_ecc; + + ecc1 = fts_data_word(pbt_buf, ecc_addr); + ecc2 = fts_data_word(pbt_buf, ecc_addr + 2); + + if ((ecc1 + ecc2) != 0xFFFF) + return false; + + cal_ecc = fts_crc_calc(pbt_buf, star_addr, (len / 2)); + + FTS_DEBUG("[UPGRADE][ECC] ecc1 = %x, cal_ecc = %x\n", ecc1, cal_ecc); + if (ecc1 != cal_ecc) { + FTS_DEBUG("[UPGRADE][ECC] ecc error\n"); + return false; + } + return true; +} + diff --git a/include/linux/input/focaltech_mmi_modules.h b/include/linux/input/focaltech_mmi_modules.h new file mode 100644 index 000000000000..18cf99cbe4b2 --- /dev/null +++ b/include/linux/input/focaltech_mmi_modules.h @@ -0,0 +1,80 @@ +/* + * + * FocalTech mmi TouchScreen driver header file. + * + * Copyright (c) 2010 Focal tech Ltd. + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef __LINUX_FOCALTECH_MMI_MODULES_H__ +#define __LINUX_FOCALTECH_MMI_MODULES_H__ + +#define FT5X06_ID 0x55 +#define FT5X16_ID 0x0A +#define FT5X36_ID 0x14 +#define FT6X06_ID 0x06 +#define FT6X36_ID 0x36 +#define FT8716_ID 0x87 +#define FT5X46_ID 0x54 +#define FT8006U_ID 0xF0 + +#define MAX_PANEL_SUPPLIERS 5 + +struct fw_upgrade_info { + bool auto_cal; + u16 delay_aa; + u16 delay_55; + u8 upgrade_id_1; + u8 upgrade_id_2; + u16 delay_readid; + u16 delay_erase_flash; +}; + +struct ft_ts_platform_data { + struct fw_upgrade_info info; + const char *name; + const char *fw_name; + u32 irqflags; + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + u32 family_id; + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + u32 panel_minx; + u32 panel_miny; + u32 panel_maxx; + u32 panel_maxy; + u32 group_id; + u32 hard_rst_dly; + u32 soft_rst_dly; + u32 num_max_touches; + bool fw_vkey_support; + bool fw_update_mmi; + bool no_force_update; + bool i2c_pull_up; + bool ignore_id_check; + bool gesture_support; + bool resume_in_workqueue; + bool x_flip; + bool y_flip; + int (*power_init)(bool); + int (*power_on)(bool); + u32 num_vendor_ids; + u32 vendor_ids[MAX_PANEL_SUPPLIERS]; + const char *vendor_names[MAX_PANEL_SUPPLIERS]; +}; + +#endif