586 lines
19 KiB
C
586 lines
19 KiB
C
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
/*
|
||
|
* Copyright (C) 2016 MediaTek Inc.
|
||
|
*/
|
||
|
|
||
|
#include "goodix_cfg_bin.h"
|
||
|
|
||
|
int gt9896s_parse_cfg_bin(struct gt9896s_cfg_bin *cfg_bin)
|
||
|
{
|
||
|
u8 checksum;
|
||
|
int i, r;
|
||
|
u16 offset1, offset2;
|
||
|
|
||
|
if (!cfg_bin->bin_data || cfg_bin->bin_data_len == 0) {
|
||
|
ts_err("NO cfg_bin data, cfg_bin data length:%d",
|
||
|
cfg_bin->bin_data_len);
|
||
|
r = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
/* copy cfg_bin head info */
|
||
|
if (cfg_bin->bin_data_len < sizeof(struct gt9896s_cfg_bin_head)) {
|
||
|
ts_err("Invalid cfg_bin size:%d", cfg_bin->bin_data_len);
|
||
|
r = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
memcpy(&cfg_bin->head, cfg_bin->bin_data,
|
||
|
sizeof(struct gt9896s_cfg_bin_head));
|
||
|
cfg_bin->head.bin_len = le32_to_cpu(cfg_bin->head.bin_len);
|
||
|
|
||
|
/*check length*/
|
||
|
if (cfg_bin->bin_data_len != cfg_bin->head.bin_len) {
|
||
|
ts_err("cfg_bin len check failed,%d != %d",
|
||
|
cfg_bin->head.bin_len, cfg_bin->bin_data_len);
|
||
|
r = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
/*check cfg_bin valid*/
|
||
|
checksum = 0;
|
||
|
for (i = TS_BIN_VERSION_START_INDEX; i < cfg_bin->bin_data_len; i++) {
|
||
|
checksum += cfg_bin->bin_data[i];
|
||
|
}
|
||
|
if (checksum != cfg_bin->head.checksum) {
|
||
|
ts_err("cfg_bin checksum check filed 0x%02x != 0x%02x",
|
||
|
cfg_bin->head.checksum, checksum);
|
||
|
r = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
/*allocate memory for cfg packages*/
|
||
|
cfg_bin->cfg_pkgs = kzalloc(sizeof(struct gt9896s_cfg_package) *
|
||
|
cfg_bin->head.pkg_num, GFP_KERNEL);
|
||
|
if (!cfg_bin->cfg_pkgs) {
|
||
|
ts_err("cfg_pkgs, allocate memory ERROR");
|
||
|
r = -ENOMEM;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
/*get cfg_pkg's info*/
|
||
|
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
|
||
|
/*get cfg pkg length*/
|
||
|
if (i == cfg_bin->head.pkg_num - 1) {
|
||
|
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN] +
|
||
|
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 1] << 8);
|
||
|
|
||
|
cfg_bin->cfg_pkgs[i].pkg_len = cfg_bin->bin_data_len - offset1;
|
||
|
} else {
|
||
|
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN] +
|
||
|
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 1] << 8);
|
||
|
|
||
|
offset2 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 2] +
|
||
|
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 3] << 8);
|
||
|
|
||
|
if (offset2 <= offset1) {
|
||
|
ts_err("offset error,pkg:%d, offset1:%d, offset2:%d", i, offset1, offset2);
|
||
|
r = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
cfg_bin->cfg_pkgs[i].pkg_len = offset2 - offset1;
|
||
|
}
|
||
|
/*get cfg pkg head*/
|
||
|
memcpy(&cfg_bin->cfg_pkgs[i].cnst_info,
|
||
|
&cfg_bin->bin_data[offset1], TS_PKG_CONST_INFO_LEN);
|
||
|
memcpy(&cfg_bin->cfg_pkgs[i].reg_info,
|
||
|
&cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN],
|
||
|
TS_PKG_REG_INFO_LEN);
|
||
|
/*compatible little edition and big edition*/
|
||
|
gt9896s_cfg_pkg_leToCpu(&cfg_bin->cfg_pkgs[i]);
|
||
|
|
||
|
/*get configuration data*/
|
||
|
cfg_bin->cfg_pkgs[i].cfg = &cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN];
|
||
|
}
|
||
|
|
||
|
/*debug, print pkg information*/
|
||
|
ts_info("Driver bin info: ver %s, len %d, pkgs %d", cfg_bin->head.bin_version,
|
||
|
cfg_bin->head.bin_len, cfg_bin->head.pkg_num);
|
||
|
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
|
||
|
ts_debug("---------------------------------------------");
|
||
|
ts_debug("------package:%d------", i + 1);
|
||
|
ts_debug("package len:%04x", cfg_bin->cfg_pkgs[i].cnst_info.pkg_len);
|
||
|
ts_debug("package ic_type:%s", cfg_bin->cfg_pkgs[i].cnst_info.ic_type);
|
||
|
ts_debug("package cfg_type:%01x", cfg_bin->cfg_pkgs[i].cnst_info.cfg_type);
|
||
|
ts_debug("package sensor_id:%01x", cfg_bin->cfg_pkgs[i].cnst_info.sensor_id);
|
||
|
ts_debug("package hw_pid:%s", cfg_bin->cfg_pkgs[i].cnst_info.hw_pid);
|
||
|
ts_debug("package hw_vid:%s", cfg_bin->cfg_pkgs[i].cnst_info.hw_vid);
|
||
|
ts_debug("package fw_mask_version:%s", cfg_bin->cfg_pkgs[i].cnst_info.fw_mask);
|
||
|
ts_debug("package fw_patch_version:%s", cfg_bin->cfg_pkgs[i].cnst_info.fw_patch);
|
||
|
ts_debug("package x_res_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.x_res_offset);
|
||
|
ts_debug("package y_res_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.y_res_offset);
|
||
|
ts_debug("package trigger_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.trigger_offset);
|
||
|
|
||
|
ts_debug("reg info");
|
||
|
ts_debug("send_cfg_flag reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.cfg_send_flag.addr);
|
||
|
ts_debug("version base reg:%02x, len:%d",
|
||
|
cfg_bin->cfg_pkgs[i].reg_info.version_base.addr,
|
||
|
cfg_bin->cfg_pkgs[i].reg_info.version_base.reserved1);
|
||
|
ts_debug("pid reg:%02x:%d", cfg_bin->cfg_pkgs[i].reg_info.pid.addr,
|
||
|
cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1);
|
||
|
ts_debug("vid reg:%02x:%d", cfg_bin->cfg_pkgs[i].reg_info.vid.addr,
|
||
|
cfg_bin->cfg_pkgs[i].reg_info.vid.reserved1);
|
||
|
ts_debug("sensor_id reg:%02x,mask:%x", cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr,
|
||
|
cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr);
|
||
|
ts_debug("fw_status reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.fw_status.addr);
|
||
|
ts_debug("cfg_addr reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.cfg_addr.addr);
|
||
|
ts_debug("esd reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.esd.addr);
|
||
|
ts_debug("command reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.command.addr);
|
||
|
ts_debug("coor:%02x", cfg_bin->cfg_pkgs[i].reg_info.coor.addr);
|
||
|
ts_debug("gesture:%02x", cfg_bin->cfg_pkgs[i].reg_info.gesture.addr);
|
||
|
ts_debug("fw_request:%02x", cfg_bin->cfg_pkgs[i].reg_info.fw_request.addr);
|
||
|
ts_debug("proximity:%02x", cfg_bin->cfg_pkgs[i].reg_info.proximity.addr);
|
||
|
|
||
|
ts_debug("--------------------------------------------");
|
||
|
}
|
||
|
r = 0;
|
||
|
exit:
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
static int gt9896s_cfg_bin_proc(struct gt9896s_ts_core *core_data)
|
||
|
{
|
||
|
int r;
|
||
|
struct gt9896s_ts_device *ts_dev = core_data->ts_dev;
|
||
|
struct gt9896s_cfg_bin *cfg_bin = kzalloc(sizeof(struct gt9896s_cfg_bin),
|
||
|
GFP_KERNEL);
|
||
|
if (!cfg_bin) {
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
if (!ts_dev) {
|
||
|
ts_err("ts device can't be null");
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
/* when start init config bin with error state */
|
||
|
ts_dev->cfg_bin_state = CFG_BIN_STATE_ERROR;
|
||
|
|
||
|
/*get cfg_bin from file system*/
|
||
|
r = gt9896s_read_cfg_bin(ts_dev, cfg_bin);
|
||
|
if (r < 0) {
|
||
|
ts_err("Failed get valid config bin data");
|
||
|
goto exit;
|
||
|
}
|
||
|
/*parse cfg bin*/
|
||
|
r = gt9896s_parse_cfg_bin(cfg_bin);
|
||
|
if (!r) {
|
||
|
ts_info("parse cfg bin SUCCESS");
|
||
|
} else {
|
||
|
ts_err("parse cfg bin FAILED");
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
/*get register address and configuration from cfg bin*/
|
||
|
r = gt9896s_get_reg_and_cfg(ts_dev, cfg_bin);
|
||
|
if (!r) {
|
||
|
ts_info("success get reg and cfg info from cfg bin");
|
||
|
} else {
|
||
|
ts_err("failed get cfg and reg info, update fw then retry");
|
||
|
}
|
||
|
|
||
|
/*debug*/
|
||
|
ts_info("cfg_send_flag:0x%04x", ts_dev->reg.cfg_send_flag);
|
||
|
ts_info("pid:0x%04x", ts_dev->reg.pid);
|
||
|
ts_info("vid:0x%04x", ts_dev->reg.vid);
|
||
|
ts_info("sensor_id:0x%04x", ts_dev->reg.sensor_id);
|
||
|
ts_info("fw_mask:0x%04x", ts_dev->reg.fw_mask);
|
||
|
ts_info("fw_status:0x%04x", ts_dev->reg.fw_status);
|
||
|
ts_info("cfg_addr:0x%04x", ts_dev->reg.cfg_addr);
|
||
|
ts_info("esd:0x%04x", ts_dev->reg.esd);
|
||
|
ts_info("command:0x%04x", ts_dev->reg.command);
|
||
|
ts_info("coor:0x%04x", ts_dev->reg.coor);
|
||
|
ts_info("gesture:0x%04x", ts_dev->reg.gesture);
|
||
|
ts_info("fw_request:0x%04x", ts_dev->reg.fw_request);
|
||
|
ts_info("proximity:0x%04x", ts_dev->reg.proximity);
|
||
|
|
||
|
exit:
|
||
|
kfree(cfg_bin->cfg_pkgs);
|
||
|
kfree(cfg_bin->bin_data);
|
||
|
kfree(cfg_bin);
|
||
|
if (r)
|
||
|
gt9896s_ts_blocking_notify(NOTIFY_CFG_BIN_FAILED, &r);
|
||
|
else
|
||
|
gt9896s_ts_blocking_notify(NOTIFY_CFG_BIN_SUCCESS, &r);
|
||
|
|
||
|
ts_info("cfg bin state %d, ret %d", ts_dev->cfg_bin_state, r);
|
||
|
return r;
|
||
|
out:
|
||
|
kfree(cfg_bin);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
static int gt9896s_extract_cfg_pkg(struct gt9896s_ts_device *ts_dev,
|
||
|
struct gt9896s_cfg_package *cfg_pkg)
|
||
|
{
|
||
|
struct gt9896s_ts_config *ts_cfg;
|
||
|
|
||
|
if (cfg_pkg->cnst_info.cfg_type == TS_NORMAL_CFG) {
|
||
|
ts_cfg = &(ts_dev->normal_cfg);
|
||
|
} else if (cfg_pkg->cnst_info.cfg_type == TS_HIGH_SENSE_CFG) {
|
||
|
ts_cfg = &(ts_dev->highsense_cfg);
|
||
|
} else {
|
||
|
ts_err("unknown cfg type %d", cfg_pkg->cnst_info.cfg_type);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ts_cfg->length = cfg_pkg->pkg_len -
|
||
|
TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN;
|
||
|
if (ts_cfg->length > sizeof(ts_cfg->data)) {
|
||
|
ts_err("illegal cfg length %d", ts_cfg->length);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
if (ts_cfg->length) {
|
||
|
ts_info("get config type %d, len %d",
|
||
|
cfg_pkg->cnst_info.cfg_type, ts_cfg->length);
|
||
|
memcpy(ts_cfg->data, cfg_pkg->cfg, ts_cfg->length);
|
||
|
ts_cfg->initialized = TS_CFG_STABLE;
|
||
|
mutex_init(&ts_cfg->lock);
|
||
|
} else {
|
||
|
ts_info("no config data");
|
||
|
}
|
||
|
|
||
|
/*get register info*/
|
||
|
ts_dev->reg.cfg_send_flag = cfg_pkg->reg_info.cfg_send_flag.addr;
|
||
|
ts_dev->reg.version_base = cfg_pkg->reg_info.version_base.addr;
|
||
|
ts_dev->reg.version_len = cfg_pkg->reg_info.version_base.reserved1;
|
||
|
ts_dev->reg.pid = cfg_pkg->reg_info.pid.addr;
|
||
|
ts_dev->reg.pid_len = cfg_pkg->reg_info.pid.reserved1;
|
||
|
ts_dev->reg.vid = cfg_pkg->reg_info.vid.addr;
|
||
|
ts_dev->reg.vid_len = cfg_pkg->reg_info.vid.reserved1;
|
||
|
ts_dev->reg.sensor_id = cfg_pkg->reg_info.sensor_id.addr;
|
||
|
ts_dev->reg.sensor_id_mask = cfg_pkg->reg_info.sensor_id.reserved1;
|
||
|
ts_dev->reg.fw_mask = cfg_pkg->reg_info.fw_mask.addr;
|
||
|
ts_dev->reg.fw_status = cfg_pkg->reg_info.fw_status.addr;
|
||
|
ts_dev->reg.cfg_addr = cfg_pkg->reg_info.cfg_addr.addr;
|
||
|
ts_dev->reg.esd = cfg_pkg->reg_info.esd.addr;
|
||
|
ts_dev->reg.command = cfg_pkg->reg_info.command.addr;
|
||
|
ts_dev->reg.coor = cfg_pkg->reg_info.coor.addr;
|
||
|
ts_dev->reg.gesture = cfg_pkg->reg_info.gesture.addr;
|
||
|
ts_dev->reg.fw_request = cfg_pkg->reg_info.fw_request.addr;
|
||
|
ts_dev->reg.proximity = cfg_pkg->reg_info.proximity.addr;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int gt9896s_get_reg_and_cfg(struct gt9896s_ts_device *ts_dev,
|
||
|
struct gt9896s_cfg_bin *cfg_bin)
|
||
|
{
|
||
|
int i;
|
||
|
u16 addr;
|
||
|
u8 read_len;
|
||
|
char temp_sensor_id = -1;
|
||
|
u8 temp_fw_mask[TS_CFG_BLOCK_FW_MASK_LEN] = {0x00};
|
||
|
u8 temp_pid[TS_CFG_BLOCK_PID_LEN] = {0x00};
|
||
|
int r = -EINVAL;
|
||
|
|
||
|
if (!cfg_bin->head.pkg_num || !cfg_bin->cfg_pkgs) {
|
||
|
ts_err("there is none cfg package, pkg_num:%d",
|
||
|
cfg_bin->head.pkg_num);
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
memset(&ts_dev->normal_cfg, 0, sizeof(ts_dev->normal_cfg));
|
||
|
memset(&ts_dev->highsense_cfg, 0, sizeof(ts_dev->highsense_cfg));
|
||
|
|
||
|
/* find suitable cfg packages */
|
||
|
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
|
||
|
/*get ic type*/
|
||
|
if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
|
||
|
"normandy", strlen("normandy"))) {
|
||
|
ts_dev->ic_type = IC_TYPE_NORMANDY;
|
||
|
} else if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
|
||
|
"yellowstone_s", strlen("yellowstone_s"))) {
|
||
|
ts_dev->ic_type = IC_TYPE_YELLOWSTONE_SPI;
|
||
|
} else if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
|
||
|
"yellowstone", strlen("yellowstone"))) {
|
||
|
ts_dev->ic_type = IC_TYPE_YELLOWSTONE;
|
||
|
} else {
|
||
|
ts_err("unknow ic type of cfg_bin:%s",
|
||
|
cfg_bin->cfg_pkgs[i].cnst_info.ic_type);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ts_info("ic_type:%d", ts_dev->ic_type);
|
||
|
|
||
|
/* contrast sensor id */
|
||
|
addr = cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr;
|
||
|
if (!addr) {
|
||
|
ts_info("pkg:%d, sensor_id reg is NULL", i);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
r = ts_dev->hw_ops->read(ts_dev, addr, &temp_sensor_id, 1);
|
||
|
if (r < 0) {
|
||
|
ts_err("failed get sensor of pkg:%d, reg:0x%02x", i, addr);
|
||
|
goto get_default_pkg;
|
||
|
}
|
||
|
ts_info("sensor id is %d", temp_sensor_id);
|
||
|
|
||
|
/*sensor.reserved1 is a mask, if it's not ZERO, use it*/
|
||
|
if (cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1 != 0)
|
||
|
temp_sensor_id &= cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1;
|
||
|
|
||
|
if (temp_sensor_id != cfg_bin->cfg_pkgs[i].cnst_info.sensor_id) {
|
||
|
ts_err("pkg:%d, sensor id contrast FAILED, reg:0x%02x",
|
||
|
i, addr);
|
||
|
ts_err("sensor_id from i2c:%d, sensor_id of cfg bin:%d",
|
||
|
temp_sensor_id,
|
||
|
cfg_bin->cfg_pkgs[i].cnst_info.sensor_id);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/*contrast fw_mask, if this reg is null, skip this step*/
|
||
|
addr = cfg_bin->cfg_pkgs[i].reg_info.fw_mask.addr;
|
||
|
if (addr && cfg_bin->cfg_pkgs[i].cnst_info.fw_mask[0]) {
|
||
|
r = ts_dev->hw_ops->read(ts_dev, addr, temp_fw_mask,
|
||
|
sizeof(temp_fw_mask));
|
||
|
if (r < 0) {
|
||
|
ts_err("failed read fw_mask pkg:%d, reg:0x%02x",
|
||
|
i, addr);
|
||
|
goto get_default_pkg;
|
||
|
}
|
||
|
if (strncmp(temp_fw_mask, cfg_bin->cfg_pkgs[i].cnst_info.fw_mask,
|
||
|
sizeof(temp_fw_mask))) {
|
||
|
ts_err("pkg:%d, fw_mask contrast FAILED, reg:0x%02x,", i, addr);
|
||
|
ts_err("mask from i2c:%s, mask of cfg bin:%s",
|
||
|
temp_fw_mask,
|
||
|
cfg_bin->cfg_pkgs[i].cnst_info.fw_mask);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*contrast pid*/
|
||
|
addr = cfg_bin->cfg_pkgs[i].reg_info.pid.addr;
|
||
|
read_len = cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1;
|
||
|
if (!addr) {
|
||
|
ts_err("pkg:%d, pid reg is NULL", i);
|
||
|
continue;
|
||
|
}
|
||
|
if (read_len <= 0 || read_len > TS_CFG_BLOCK_PID_LEN) {
|
||
|
ts_err("pkg:%d, hw_pid length ERROR, len:%d",
|
||
|
i, read_len);
|
||
|
continue;
|
||
|
}
|
||
|
r = ts_dev->hw_ops->read(ts_dev, addr, temp_pid, read_len);
|
||
|
if (r < 0) {
|
||
|
ts_err("failed read pid pkg:%d, pid reg:0x%02x", i, addr);
|
||
|
goto get_default_pkg;
|
||
|
}
|
||
|
if (strncmp(temp_pid, cfg_bin->cfg_pkgs[i].cnst_info.hw_pid, read_len)) {
|
||
|
ts_err("pkg:%d, pid contrast FAILED, reg:0x%02x", i, addr);
|
||
|
ts_err("pid from spi:%s, pid of cfg bin:%s",
|
||
|
temp_pid, cfg_bin->cfg_pkgs[i].cnst_info.hw_pid);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ts_info("try get package info: ic type %s, cfg type %d",
|
||
|
cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
|
||
|
cfg_bin->cfg_pkgs[i].cnst_info.cfg_type);
|
||
|
/* currently only support normal and high_sense config */
|
||
|
if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_NORMAL_CFG ||
|
||
|
cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_HIGH_SENSE_CFG) {
|
||
|
r = gt9896s_extract_cfg_pkg(ts_dev, &cfg_bin->cfg_pkgs[i]);
|
||
|
if (!r) {
|
||
|
ts_dev->cfg_bin_state = CFG_BIN_STATE_INITIALIZED;
|
||
|
ts_info("success parse cfg bin");
|
||
|
} else {
|
||
|
ts_err("failed parse cfg bin");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
get_default_pkg:
|
||
|
if (ts_dev->cfg_bin_state != CFG_BIN_STATE_INITIALIZED) {
|
||
|
ts_err("no valid normal cfg, use cfg_pkg 0 as default");
|
||
|
/* Foo code for recover dead IC.
|
||
|
* force set package 0 config type to normal config, this will
|
||
|
* config will use to recover IC.
|
||
|
*/
|
||
|
cfg_bin->cfg_pkgs[0].cnst_info.cfg_type = TS_NORMAL_CFG;
|
||
|
if (gt9896s_extract_cfg_pkg(ts_dev, &cfg_bin->cfg_pkgs[0])) {
|
||
|
ts_err("failed get valid config for IC recover");
|
||
|
ts_dev->cfg_bin_state = CFG_BIN_STATE_ERROR;
|
||
|
} else {
|
||
|
ts_dev->cfg_bin_state = CFG_BIN_STATE_TEMP;
|
||
|
ts_info("get temp config data");
|
||
|
}
|
||
|
|
||
|
r = -EINVAL;
|
||
|
}
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
int gt9896s_read_cfg_bin(struct gt9896s_ts_device *ts_dev, struct gt9896s_cfg_bin *cfg_bin)
|
||
|
{
|
||
|
const struct firmware *firmware = NULL;
|
||
|
struct gt9896s_ts_board_data *ts_bdata = &ts_dev->board_data;
|
||
|
char cfg_bin_name[64] = {0x00};
|
||
|
int i = 0, r;
|
||
|
|
||
|
/*get cfg_bin_name*/
|
||
|
if (ts_bdata->lcm_max_x == 1080 && ts_bdata->lcm_max_y == 2280) {
|
||
|
r = snprintf(cfg_bin_name, sizeof(cfg_bin_name), "%s%s_1080x2280.bin",
|
||
|
TS_DEFAULT_CFG_BIN, gt9896s_config_buf);
|
||
|
} else if (ts_bdata->lcm_max_x == 1080 && ts_bdata->lcm_max_y == 2300) {
|
||
|
r = snprintf(cfg_bin_name, sizeof(cfg_bin_name), "%s%s_1080x2300.bin",
|
||
|
TS_DEFAULT_CFG_BIN, gt9896s_config_buf);
|
||
|
} else {
|
||
|
r = snprintf(cfg_bin_name, sizeof(cfg_bin_name), "%s%s.bin",
|
||
|
TS_DEFAULT_CFG_BIN, gt9896s_config_buf);
|
||
|
}
|
||
|
if (r >= sizeof(cfg_bin_name)) {
|
||
|
ts_err("get cfg_bin name FAILED!!!");
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
ts_info("cfg_bin_name:%s", cfg_bin_name);
|
||
|
|
||
|
for (i = 0; i < TS_RQST_FW_RETRY_TIMES; i++) {
|
||
|
r = request_firmware(&firmware, cfg_bin_name, ts_dev->dev);
|
||
|
if (r < 0) {
|
||
|
ts_err("failed get cfg bin[%s] error:%d, try_times:%d",
|
||
|
cfg_bin_name, r, i + 1);
|
||
|
msleep(3000);
|
||
|
} else {
|
||
|
ts_info("Cfg_bin image [%s] is ready, try_times:%d",
|
||
|
cfg_bin_name, i + 1);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (i >= TS_RQST_FW_RETRY_TIMES) {
|
||
|
ts_err("get cfg_bin FAILED");
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
if (firmware->size <= 0) {
|
||
|
ts_err("request_firmware, cfg_bin length ERROR,len:%zu",
|
||
|
firmware->size);
|
||
|
r = -EINVAL;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
cfg_bin->bin_data_len = firmware->size;
|
||
|
/*allocate memory for cfg_bin->bin_data*/
|
||
|
cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL);
|
||
|
if (!cfg_bin->bin_data) {
|
||
|
r = -ENOMEM;
|
||
|
goto exit;
|
||
|
}
|
||
|
memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len);
|
||
|
|
||
|
r = 0;
|
||
|
exit:
|
||
|
if (firmware) {
|
||
|
release_firmware(firmware);
|
||
|
firmware = NULL;
|
||
|
}
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
void gt9896s_cfg_pkg_leToCpu(struct gt9896s_cfg_package *pkg)
|
||
|
{
|
||
|
if (!pkg) {
|
||
|
ts_err("cfg package is NULL");
|
||
|
return;
|
||
|
}
|
||
|
/*package const_info*/
|
||
|
pkg->cnst_info.pkg_len = le32_to_cpu(pkg->cnst_info.pkg_len);
|
||
|
pkg->cnst_info.x_res_offset = le16_to_cpu(pkg->cnst_info.x_res_offset);
|
||
|
pkg->cnst_info.y_res_offset = le16_to_cpu(pkg->cnst_info.y_res_offset);
|
||
|
pkg->cnst_info.trigger_offset = le16_to_cpu(pkg->cnst_info.trigger_offset);
|
||
|
|
||
|
/*package reg_info*/
|
||
|
pkg->reg_info.cfg_send_flag.addr = le16_to_cpu(pkg->reg_info.cfg_send_flag.addr);
|
||
|
pkg->reg_info.pid.addr = le16_to_cpu(pkg->reg_info.pid.addr);
|
||
|
pkg->reg_info.vid.addr = le16_to_cpu(pkg->reg_info.vid.addr);
|
||
|
pkg->reg_info.sensor_id.addr = le16_to_cpu(pkg->reg_info.sensor_id.addr);
|
||
|
pkg->reg_info.fw_status.addr = le16_to_cpu(pkg->reg_info.fw_status.addr);
|
||
|
pkg->reg_info.cfg_addr.addr = le16_to_cpu(pkg->reg_info.cfg_addr.addr);
|
||
|
pkg->reg_info.esd.addr = le16_to_cpu(pkg->reg_info.esd.addr);
|
||
|
pkg->reg_info.command.addr = le16_to_cpu(pkg->reg_info.command.addr);
|
||
|
pkg->reg_info.coor.addr = le16_to_cpu(pkg->reg_info.coor.addr);
|
||
|
pkg->reg_info.gesture.addr = le16_to_cpu(pkg->reg_info.gesture.addr);
|
||
|
pkg->reg_info.fw_request.addr = le16_to_cpu(pkg->reg_info.fw_request.addr);
|
||
|
pkg->reg_info.proximity.addr = le16_to_cpu(pkg->reg_info.proximity.addr);
|
||
|
}
|
||
|
|
||
|
static int gt9896s_later_init_thread(void *data)
|
||
|
{
|
||
|
int ret;
|
||
|
struct gt9896s_ts_core *ts_core = data;
|
||
|
struct gt9896s_ts_device *ts_dev;
|
||
|
|
||
|
if (!data) {
|
||
|
ts_err("ts core data can't be null");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
ts_dev = ts_core->ts_dev;
|
||
|
if (!ts_dev) {
|
||
|
ts_err("ts dev data can't be null");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
ret = gt9896s_cfg_bin_proc(ts_core);
|
||
|
if (ret)
|
||
|
ts_err("parse cfg bin encounter error, %d", ret);
|
||
|
else
|
||
|
ts_info("success get cfg bin");
|
||
|
|
||
|
if (ts_dev->cfg_bin_state == CFG_BIN_STATE_ERROR) {
|
||
|
ts_err("parse cfg bin encounter fatal err");
|
||
|
goto release_core;
|
||
|
}
|
||
|
|
||
|
if (ts_dev->cfg_bin_state == CFG_BIN_STATE_TEMP) {
|
||
|
ts_err("failed get valid config data, retry after fwupdate");
|
||
|
ret = gt9896s_do_fw_update(UPDATE_MODE_BLOCK|UPDATE_MODE_FORCE|
|
||
|
UPDATE_MODE_FLASH_CFG|
|
||
|
UPDATE_MODE_SRC_REQUEST);
|
||
|
if (ret) {
|
||
|
ts_err("fw update failed, %d", ret);
|
||
|
goto release_core;
|
||
|
}
|
||
|
ts_info("fw update success retry parse cfg bin");
|
||
|
ret = gt9896s_cfg_bin_proc(ts_core);
|
||
|
if (ret) {
|
||
|
ts_err("failed parse cfg bin after fw update");
|
||
|
goto release_core;
|
||
|
}
|
||
|
} else {
|
||
|
ts_info("success parse config bin");
|
||
|
ret = gt9896s_do_fw_update(UPDATE_MODE_BLOCK|
|
||
|
UPDATE_MODE_FLASH_CFG|
|
||
|
UPDATE_MODE_SRC_REQUEST);
|
||
|
if (ret) {
|
||
|
ts_err("fw update failed, %d[ignore]", ret);
|
||
|
ret = 0;
|
||
|
}
|
||
|
}
|
||
|
ret = gt9896s_ts_stage2_init(ts_core);
|
||
|
if (!ret) {
|
||
|
ts_info("stage2 init success");
|
||
|
return ret;
|
||
|
}
|
||
|
ts_err("stage2 init failed, %d", ret);
|
||
|
|
||
|
release_core:
|
||
|
gt9896s_ts_core_release(ts_core);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int gt9896s_start_later_init(struct gt9896s_ts_core *ts_core)
|
||
|
{
|
||
|
struct task_struct *init_thrd;
|
||
|
/* create and run update thread */
|
||
|
init_thrd = kthread_run(gt9896s_later_init_thread,
|
||
|
ts_core, "gt9896s_init_thread");
|
||
|
if (IS_ERR_OR_NULL(init_thrd)) {
|
||
|
ts_err("Failed to create update thread:%ld",
|
||
|
PTR_ERR(init_thrd));
|
||
|
return -EFAULT;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|