| /* |
| * |
| * FocalTech fts TouchScreen driver. |
| * |
| * Copyright (c) 2012-2018, 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_ft8006.c |
| * |
| * Author: Focaltech Driver Team |
| * |
| * Created: 2016-12-29 |
| * |
| * Abstract: |
| * |
| * Reference: |
| * |
| *****************************************************************************/ |
| |
| /***************************************************************************** |
| * 1.Included header files |
| *****************************************************************************/ |
| #include "../focaltech_core.h" |
| #include "../focaltech_flash.h" |
| |
| /***************************************************************************** |
| * Global variable or extern global variabls/functions |
| *****************************************************************************/ |
| u8 pb_file_ft8006[] = { |
| #include "../include/pramboot/FT8006_Pramboot_v1.4_20180319.i" |
| }; |
| |
| /***************************************************************************** |
| * Private constant and macro definitions using #define |
| *****************************************************************************/ |
| #define FTS_MAX_LEN_APP_FT8006 (94 * 1024) |
| |
| #define MAX_BANK_DATA 0x80 |
| #define MAX_GAMMA_LEN 0x180 |
| #define LIC_CHECKSUM_H_OFF 0x00 |
| #define LIC_CHECKSUM_L_OFF 0x01 |
| #define LIC_LCD_ECC_H_OFF 0x04 |
| #define LIC_LCD_ECC_L_OFF 0x05 |
| #define LIC_ECC_REG_H_OFF 0x43D |
| #define LIC_ECC_REG_L_OFF 0x43C |
| #define LIC_REG_2 0xB2 |
| |
| static int gamma_enable[] = { 0x040d, 0x91, 0x80, 0x00, 0x19, 0x01 }; |
| 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 |
| *****************************************************************************/ |
| /* |
| * fts_ft8006_init - remap pramboot file |
| */ |
| static int fts_ft8006_init(void) |
| { |
| int i = 0; |
| int j = 0; |
| u8 temp = 0; |
| struct upgrade_func *upgf = &upgrade_func_ft8006; |
| |
| if ((NULL == upgf->pramboot) || (upgf->pb_length < FTS_MIN_LEN)) { |
| FTS_ERROR("pramboot/length(%x) fail", upgf->pb_length); |
| } |
| |
| for (i = 0; i < upgf->pb_length; i = i + 4) { |
| for (j = 0; j < 2; j++) { |
| temp = upgf->pramboot[i + j]; |
| upgf->pramboot[i + j] = upgf->pramboot[i + 3 - j]; |
| upgf->pramboot[i + 3 - j] = temp; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* 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[2] << 8) + buf[3]); |
| 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 = 0x0A; /* 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]); |
| /* FTS_INFO("bank pos=%x bank_addr=%x bank_len=%x", pos, bank_addr, bank_len); */ |
| 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; |
| /* FTS_INFO("actual mipi bank number = %x", bank_num); */ |
| 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 { |
| /* FTS_INFO("bank number = %d", banknum); */ |
| if ((bank_crc_en >> banknum) & 0x01) { |
| for (i = 0; i < MAX_BANK_DATA; i++) { |
| temp_byte.dshort = (u16)bank_data[i]; |
| /* if(i == 0) */ |
| /* FTS_INFO("data0=%x, %d %d %d %d %d %d %d %d", temp_byte.dshort, 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.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, j; |
| |
| if (length % 2) { |
| return 0xFFFF; |
| } |
| |
| 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; |
| } |
| |
| 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 += snprintf(p + n, PAGE_SIZE, "%02x ", buf[i]); |
| } |
| |
| FTS_DEBUG("%s", p); |
| |
| kfree(p); |
| return 0; |
| } |
| |
| 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; |
| bool gamma_has_enable = false; |
| 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_INFO("back_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) |
| { |
| u16 gamma_pos = 0; |
| int gamma_analog[] = { 0x003A, 0x85, 0x00, 0x00, 0x2C, 0x2B }; |
| int gamma_digital1[] = { 0x0355, 0x8D, 0x00, 0x00, 0x80, 0x80 }; |
| int gamma_digital2[] = { 0x03d9, 0x8D, 0x80, 0x00, 0x14, 0x13 }; |
| |
| /* Analog Gamma */ |
| if ((initcode[gamma_analog[0]] == gamma[gamma_pos]) |
| && (initcode[gamma_analog[0] + 1] == gamma[gamma_pos + 1])) { |
| memcpy(initcode + gamma_analog[0] + 4 , gamma + gamma_pos + 4, gamma_analog[5]); |
| gamma_pos += gamma_analog[5] + 4; |
| } else |
| goto find_gamma_bank_err; |
| |
| /* Digital1 Gamma */ |
| if ((initcode[gamma_digital1[0]] == gamma[gamma_pos]) |
| && (initcode[gamma_digital1[0] + 1] == gamma[gamma_pos + 1])) { |
| memcpy(initcode + gamma_digital1[0] + 4 , gamma + gamma_pos + 4, gamma_digital1[5]); |
| gamma_pos += gamma_digital1[5] + 4; |
| } else |
| goto find_gamma_bank_err; |
| |
| /* Digital2 Gamma */ |
| if ((initcode[gamma_digital2[0]] == gamma[gamma_pos]) |
| && (initcode[gamma_digital2[0] + 1] == gamma[gamma_pos + 1])) { |
| memcpy(initcode + gamma_digital2[0] + 4 , gamma + gamma_pos + 4, gamma_digital2[5]); |
| gamma_pos += gamma_digital2[5] + 4; |
| } else |
| goto find_gamma_bank_err; |
| |
| /* enable Gamma */ |
| if ((initcode[gamma_enable[0]] == gamma[gamma_pos]) |
| && (initcode[gamma_enable[0] + 1] == gamma[gamma_pos + 1])) { |
| if (gamma[gamma_pos + 4]) |
| initcode[gamma_enable[0] + 4 + 15] |= 0x01; |
| else |
| initcode[gamma_enable[0] + 4 + 15] &= 0xFE; |
| gamma_pos += 1 + 4; |
| } else |
| goto find_gamma_bank_err; |
| |
| 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; |
| } |
| |
| /* |
| * 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_ecc = 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 (1 == i) { |
| if (true == flag) |
| 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_lcdinitcode_ecc(tmpbuf, &initcode_ecc); |
| if (ret < 0) { |
| FTS_ERROR("lcd init code ecc calculate fail"); |
| goto REPLACE_GAMMA_ERR; |
| } |
| FTS_INFO("lcd init code cal ecc:%04x", initcode_ecc); |
| tmpbuf[LIC_LCD_ECC_H_OFF] = (u8)(initcode_ecc >> 8); |
| tmpbuf[LIC_LCD_ECC_L_OFF] = (u8)(initcode_ecc); |
| tmpbuf[LIC_ECC_REG_H_OFF] = (u8)(initcode_ecc >> 8); |
| tmpbuf[LIC_ECC_REG_L_OFF] = (u8)(initcode_ecc); |
| |
| hlic_len = (u16)(((u16)tmpbuf[2]) << 8) + tmpbuf[3]; |
| 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; |
| } |
| |
| /* |
| * 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[2]) << 8) + buf[3]; |
| 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[0] << 8) + buf[1]); |
| FTS_INFO("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[4] << 8) + buf[5]); |
| FTS_INFO("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 bool fts_ft8006_check_ide(u8 *buf, u32 len) |
| { |
| u32 off = 0; |
| |
| FTS_INFO("Host FW file IDE version check"); |
| if (NULL == buf) { |
| FTS_ERROR("buf is null fail"); |
| return false; |
| } |
| |
| if (len < FTS_MAX_LEN_FILE) { |
| FTS_INFO("buf len(%x) abnormal, no IDE", len); |
| return false; |
| } |
| |
| off = upgrade_func_ft8006.paramcfgoff; |
| if ((buf[off] == 'I') && (buf[off + 1] == 'D') && (buf[off + 2] == 'E')) |
| return true; |
| |
| return false; |
| } |
| |
| /* fts_ft8006_write_ecc - write and check ecc |
| * return 0 if success |
| */ |
| static int fts_ft8006_write_ecc( |
| struct i2c_client *client, |
| u32 saddr, |
| u8 *buf, |
| u32 len) |
| { |
| int ecc_in_host = 0; |
| int ecc_in_tp = 0; |
| |
| ecc_in_host = fts_flash_write_buf(client, saddr, buf, len, 1); |
| if (ecc_in_host < 0 ) { |
| FTS_ERROR("write buffer to flash fail"); |
| return ecc_in_host; |
| } |
| |
| /* ecc */ |
| ecc_in_tp = fts_fwupg_ecc_cal(client, saddr, len); |
| if (ecc_in_tp < 0 ) { |
| FTS_ERROR("ecc read fail"); |
| return ecc_in_tp; |
| } |
| |
| 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"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| /************************************************************************ |
| * Name: fts_ft8006_param_flash |
| * Brief: param upgrade(erase/write/ecc check) |
| * Input: buf - all.bin |
| * len - len of all.bin |
| * Output: |
| * Return: return 0 if success, otherwise return error code |
| ***********************************************************************/ |
| static int fts_ft8006_param_flash(struct i2c_client *client, u8 *buf, u32 len) |
| { |
| int ret = 0; |
| u8 cmd[2] = { 0 }; |
| u32 delay = 0; |
| u32 start_addr = 0; |
| u32 paramcfg_len = 0; |
| u8 *tmpbuf = NULL; |
| |
| /* erase gesture & parameter sector */ |
| cmd[0] = FTS_CMD_FLASH_MODE; |
| cmd[1] = FLASH_MODE_PARAM_VALUE; |
| ret = fts_i2c_write(client, cmd, 2); |
| if (ret < 0) { |
| FTS_ERROR("upgrade mode(09) cmd write fail"); |
| goto PARAM_FLASH_ERR; |
| } |
| |
| delay = FTS_ERASE_SECTOR_DELAY * 2; |
| ret = fts_fwupg_erase(client, delay); |
| if (ret < 0) { |
| FTS_ERROR("erase cmd write fail"); |
| goto PARAM_FLASH_ERR; |
| } |
| |
| /* write flash */ |
| start_addr = upgrade_func_ft8006.paramcfgoff; |
| paramcfg_len = FTS_MAX_LEN_SECTOR; |
| tmpbuf = buf + start_addr; |
| ret = fts_ft8006_write_ecc(client, start_addr, tmpbuf, paramcfg_len); |
| if (ret < 0 ) { |
| FTS_ERROR("parameter configure area write fail"); |
| goto PARAM_FLASH_ERR; |
| } |
| |
| start_addr = upgrade_func_ft8006.paramcfg2off; |
| paramcfg_len = FTS_MAX_LEN_SECTOR; |
| tmpbuf = buf + start_addr; |
| ret = fts_ft8006_write_ecc(client, start_addr, tmpbuf, paramcfg_len); |
| if (ret < 0 ) { |
| FTS_ERROR("parameter2 configure area write fail"); |
| goto PARAM_FLASH_ERR; |
| } |
| |
| return 0; |
| |
| PARAM_FLASH_ERR: |
| return ret; |
| } |
| |
| /* |
| * fts_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_ft8006_get_hlic_ver(u8 *initcode) |
| { |
| u8 *hlic_buf = initcode; |
| u16 hlic_len = 0; |
| u8 hlic_ver[2] = { 0 }; |
| |
| hlic_len = (u16)(((u16)hlic_buf[2]) << 8) + hlic_buf[3]; |
| 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_INFO("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_ft8006_upgrade |
| * Brief: |
| * Input: buf - all.bin |
| * len - len of all.bin |
| * Output: |
| * Return: return 0 if success, otherwise return error code |
| ***********************************************************************/ |
| static int fts_ft8006_upgrade(struct i2c_client *client, u8 *buf, u32 len) |
| { |
| int ret = 0; |
| u8 *tmpbuf = NULL; |
| u8 cmd[2] = { 0 }; |
| u32 delay = 0; |
| u32 start_addr = 0; |
| u32 app_1_len = 0; |
| u32 app_2_len = 0; |
| u32 app_len = 0; |
| u32 off = 0; |
| |
| FTS_INFO("app 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; |
| } |
| |
| off = upgrade_func_ft8006.appoff + FTS_APPINFO_OFF + FTS_APPINFO_APPLEN_OFF; |
| app_1_len = (((u32)buf[off] << 8) + buf[off + 1]); |
| off = upgrade_func_ft8006.appoff + FTS_APPINFO_OFF + FTS_APPINFO_APPLEN2_OFF; |
| app_2_len = (((u32)buf[off] << 8) + buf[off + 1]); |
| app_len = (app_2_len << 16) + app_1_len; |
| if ((app_len < FTS_MIN_LEN) || (app_len > FTS_MAX_LEN_APP_FT8006)) { |
| FTS_ERROR("app len(%x) fail", app_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 APP_UPG_ERR; |
| } |
| |
| /* erase gesture & parameter sector */ |
| cmd[0] = FTS_CMD_FLASH_MODE; |
| cmd[1] = FLASH_MODE_UPGRADE_VALUE; |
| ret = fts_i2c_write(client, cmd, 2); |
| if (ret < 0) { |
| FTS_ERROR("upgrade mode(09) cmd write fail"); |
| goto APP_UPG_ERR; |
| } |
| |
| delay = FTS_ERASE_SECTOR_DELAY * (app_len / FTS_MAX_LEN_SECTOR); |
| ret = fts_fwupg_erase(client, delay); |
| if (ret < 0) { |
| FTS_ERROR("erase cmd write fail"); |
| goto APP_UPG_ERR; |
| } |
| |
| /* write flash */ |
| start_addr = upgrade_func_ft8006.appoff; |
| tmpbuf = buf + start_addr; |
| ret = fts_ft8006_write_ecc(client, start_addr, tmpbuf, app_len); |
| if (ret < 0 ) { |
| FTS_ERROR("app buffer write fail"); |
| goto APP_UPG_ERR; |
| } |
| |
| if (fts_ft8006_check_ide(buf, len)) { |
| FTS_INFO("erase and write param configure area"); |
| ret = fts_ft8006_param_flash(client, buf, len); |
| if (ret < 0 ) { |
| FTS_ERROR("param upgrade(erase/write/ecc) fail"); |
| goto APP_UPG_ERR; |
| } |
| } |
| |
| 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; |
| |
| APP_UPG_ERR: |
| return ret; |
| } |
| |
| /************************************************************************ |
| * Name: fts_ft8006_lic_upgrade |
| * Brief: |
| * Input: buf - all.bin |
| * len - len of all.bin |
| * Output: |
| * Return: return 0 if success, otherwise return error code |
| ***********************************************************************/ |
| static int fts_ft8006_lic_upgrade(struct i2c_client *client, u8 *buf, u32 len) |
| { |
| int ret = 0; |
| u8 *tmpbuf = NULL; |
| u8 cmd[2] = { 0 }; |
| u32 delay = 0; |
| u32 start_addr = 0; |
| u32 lic_len = 0; |
| u8 val = 0; |
| bool flag = false; |
| |
| FTS_INFO("LCD initial code 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; |
| } |
| |
| ret = check_initial_code_valid(client, buf); |
| if (ret < 0) { |
| FTS_ERROR("initial code invalid, not upgrade lcd init code"); |
| return -EINVAL; |
| } |
| |
| ret = fts_i2c_read_reg(client, LIC_REG_2, &val); |
| FTS_DEBUG("lic flag:%x", val); |
| if ((ret > 0) && (1 == val)) |
| flag = true; |
| |
| lic_len = FTS_MAX_LEN_SECTOR; |
| /* remalloc memory for initcode, need change content of initcode afterwise */ |
| tmpbuf = kmalloc(lic_len, GFP_KERNEL); |
| if (NULL == tmpbuf) { |
| FTS_INFO("initial code buf malloc fail"); |
| return -EINVAL; |
| } |
| start_addr = upgrade_func_ft8006.licoff; |
| memcpy(tmpbuf, buf + start_addr, lic_len); |
| |
| /* 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 LIC_UPG_ERR; |
| } |
| |
| /* 3-gamma remap */ |
| ret = read_replace_3gamma(client, tmpbuf, flag); |
| if (ret < 0) { |
| FTS_ERROR("replace 3-gamma fail, not upgrade lcd init code"); |
| goto LIC_UPG_ERR; |
| } |
| |
| /* erase gesture & parameter sector */ |
| cmd[0] = FTS_CMD_FLASH_MODE; |
| cmd[1] = FLASH_MODE_LIC_VALUE; |
| ret = fts_i2c_write(client, cmd, 2); |
| if (ret < 0) { |
| FTS_ERROR("upgrade mode(09) cmd write fail"); |
| goto LIC_UPG_ERR; |
| } |
| |
| delay = FTS_ERASE_SECTOR_DELAY * 1; |
| ret = fts_fwupg_erase(client, delay); |
| if (ret < 0) { |
| FTS_ERROR("erase cmd write fail"); |
| goto LIC_UPG_ERR; |
| } |
| |
| ret = fts_ft8006_write_ecc(client, start_addr, tmpbuf, lic_len); |
| if (ret < 0 ) { |
| FTS_ERROR("LCD initial code write fail"); |
| goto LIC_UPG_ERR; |
| } |
| |
| 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"); |
| } |
| |
| if (tmpbuf) { |
| kfree(tmpbuf); |
| tmpbuf = NULL; |
| } |
| |
| msleep(400); |
| return 0; |
| |
| LIC_UPG_ERR: |
| if (tmpbuf) { |
| kfree(tmpbuf); |
| tmpbuf = NULL; |
| } |
| return ret; |
| } |
| |
| /************************************************************************ |
| * Name: fts_ft8006_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_ft8006_param_upgrade(struct i2c_client *client, u8 *buf, u32 len) |
| { |
| int ret = 0; |
| |
| FTS_INFO("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; |
| } |
| |
| /* 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 PARAM_UPG_ERR; |
| } |
| |
| ret = fts_ft8006_param_flash(client, buf, len); |
| if (ret < 0 ) { |
| FTS_ERROR("param upgrade(erase/write/ecc) fail"); |
| goto PARAM_UPG_ERR; |
| } |
| |
| 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; |
| |
| PARAM_UPG_ERR: |
| return ret; |
| } |
| |
| /************************************************************************ |
| * Name: fts_ft8006_force_upgrade |
| * Brief: |
| * Input: buf - all.bin |
| * len - constant:128 * 1024 |
| * Output: |
| * Return: return 0 if success, otherwise return error code |
| ***********************************************************************/ |
| static int fts_ft8006_force_upgrade(struct i2c_client *client, u8 *buf, u32 len) |
| { |
| int ret = 0; |
| u8 *tmpbuf = NULL; |
| u8 cmd[2] = { 0 }; |
| u32 delay = 0; |
| u32 start_addr = 0; |
| u32 tmplen = 0; |
| |
| FTS_INFO("fw force 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; |
| } |
| |
| /* 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 FORCE_UPG_ERR; |
| } |
| |
| /* erase 0k~116k flash */ |
| cmd[0] = FTS_CMD_FLASH_MODE; |
| cmd[1] = FLASH_MODE_WRITE_FLASH_VALUE; |
| ret = fts_i2c_write(client, cmd, 2); |
| if (ret < 0) { |
| FTS_ERROR("upgrade mode(09) cmd write fail"); |
| goto FORCE_UPG_ERR; |
| } |
| |
| if (len > (116 * 1024)) { |
| tmplen = 116 * 1024; |
| } else { |
| tmplen = len; |
| } |
| delay = FTS_ERASE_SECTOR_DELAY * (tmplen / FTS_MAX_LEN_SECTOR); |
| ret = fts_fwupg_erase(client, delay); |
| if (ret < 0) { |
| FTS_ERROR("erase cmd write fail"); |
| goto FORCE_UPG_ERR; |
| } |
| |
| /* write flash */ |
| start_addr = 0; |
| tmpbuf = buf + start_addr; |
| ret = fts_ft8006_write_ecc(client, start_addr, tmpbuf, tmplen); |
| if (ret < 0 ) { |
| FTS_ERROR("app buffer write fail"); |
| goto FORCE_UPG_ERR; |
| } |
| |
| if (fts_ft8006_check_ide(buf, len)) { |
| FTS_INFO("erase and write param configure area"); |
| ret = fts_ft8006_param_flash(client, buf, len); |
| if (ret < 0 ) { |
| FTS_ERROR("param upgrade(erase/write/ecc) fail"); |
| goto FORCE_UPG_ERR; |
| } |
| } |
| |
| FTS_INFO("upgrade success, reset to normal boot"); |
| FORCE_UPG_ERR: |
| ret = fts_fwupg_reset_in_boot(client); |
| if (ret < 0) { |
| FTS_ERROR("reset to normal boot fail"); |
| } |
| |
| msleep(400); |
| return ret; |
| } |
| |
| struct upgrade_func upgrade_func_ft8006 = { |
| .ctype = {0x07, 0x10}, |
| .fwveroff = 0x510E, |
| .fwcfgoff = 0x0F80, |
| .appoff = 0x5000, |
| .licoff = 0x0000, |
| .paramcfgoff = 0x1F000, |
| .paramcfgveroff = 0x1F004, |
| .paramcfg2off = 0x4000, |
| .pramboot_supported = true, |
| .pramboot = pb_file_ft8006, |
| .pb_length = sizeof(pb_file_ft8006), |
| .hid_supported = false, |
| .init = fts_ft8006_init, |
| .upgrade = fts_ft8006_upgrade, |
| .get_hlic_ver = fts_ft8006_get_hlic_ver, |
| .lic_upgrade = fts_ft8006_lic_upgrade, |
| .param_upgrade = fts_ft8006_param_upgrade, |
| .force_upgrade = fts_ft8006_force_upgrade, |
| }; |