/* * Goodix Touchscreen Driver * Copyright (C) 2020 - 2021 Goodix, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be a reference * to you, when you are integrating the GOODiX's CTP IC into your system, * 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 "goodix_ts_core.h" #define TS_BIN_VERSION_START_INDEX 5 #define TS_BIN_VERSION_LEN 4 #define TS_CFG_BIN_HEAD_RESERVED_LEN 6 #define TS_CFG_OFFSET_LEN 2 #define TS_IC_TYPE_NAME_MAX_LEN 15 #define TS_CFG_BIN_HEAD_LEN \ (sizeof(struct goodix_cfg_bin_head) + TS_CFG_BIN_HEAD_RESERVED_LEN) #define TS_PKG_CONST_INFO_LEN (sizeof(struct goodix_cfg_pkg_const_info)) #define TS_PKG_REG_INFO_LEN (sizeof(struct goodix_cfg_pkg_reg_info)) #define TS_PKG_HEAD_LEN (TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN) /*cfg block definitin*/ #define TS_CFG_BLOCK_PID_LEN 8 #define TS_CFG_BLOCK_VID_LEN 8 #define TS_CFG_BLOCK_FW_MASK_LEN 9 #define TS_CFG_BLOCK_FW_PATCH_LEN 4 #define TS_CFG_BLOCK_RESERVED_LEN 9 #define TS_NORMAL_CFG 0x01 #define TS_HIGH_SENSE_CFG 0x03 #define TS_RQST_FW_RETRY_TIMES 2 #pragma pack(1) struct goodix_cfg_pkg_reg { u16 addr; u8 reserved1; u8 reserved2; }; struct goodix_cfg_pkg_const_info { u32 pkg_len; u8 ic_type[TS_IC_TYPE_NAME_MAX_LEN]; u8 cfg_type; u8 sensor_id; u8 hw_pid[TS_CFG_BLOCK_PID_LEN]; u8 hw_vid[TS_CFG_BLOCK_VID_LEN]; u8 fw_mask[TS_CFG_BLOCK_FW_MASK_LEN]; u8 fw_patch[TS_CFG_BLOCK_FW_PATCH_LEN]; u16 x_res_offset; u16 y_res_offset; u16 trigger_offset; }; struct goodix_cfg_pkg_reg_info { struct goodix_cfg_pkg_reg cfg_send_flag; struct goodix_cfg_pkg_reg version_base; struct goodix_cfg_pkg_reg pid; struct goodix_cfg_pkg_reg vid; struct goodix_cfg_pkg_reg sensor_id; struct goodix_cfg_pkg_reg fw_mask; struct goodix_cfg_pkg_reg fw_status; struct goodix_cfg_pkg_reg cfg_addr; struct goodix_cfg_pkg_reg esd; struct goodix_cfg_pkg_reg command; struct goodix_cfg_pkg_reg coor; struct goodix_cfg_pkg_reg gesture; struct goodix_cfg_pkg_reg fw_request; struct goodix_cfg_pkg_reg proximity; u8 reserved[TS_CFG_BLOCK_RESERVED_LEN]; }; struct goodix_cfg_bin_head { u32 bin_len; u8 checksum; u8 bin_version[TS_BIN_VERSION_LEN]; u8 pkg_num; }; #pragma pack() struct goodix_cfg_package { struct goodix_cfg_pkg_const_info cnst_info; struct goodix_cfg_pkg_reg_info reg_info; const u8 *cfg; u32 pkg_len; }; struct goodix_cfg_bin { unsigned char *bin_data; unsigned int bin_data_len; struct goodix_cfg_bin_head head; struct goodix_cfg_package *cfg_pkgs; }; static int goodix_read_cfg_bin(struct device *dev, const char *cfg_name, struct goodix_cfg_bin *cfg_bin) { const struct firmware *firmware = NULL; int ret; int retry = GOODIX_RETRY_3; ts_info("cfg_bin_name:%s", cfg_name); while (retry--) { ret = request_firmware(&firmware, cfg_name, dev); if (!ret) break; ts_info("get cfg bin retry:[%d]", GOODIX_RETRY_3 - retry); msleep(300); } if (retry < 0) { ts_err("failed get cfg bin[%s] error:%d", cfg_name, ret); return ret; } if (firmware->size <= 0) { ts_err("request_firmware, cfg_bin length ERROR,len:%zu", firmware->size); ret = -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) { ret = -ENOMEM; goto exit; } memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len); exit: release_firmware(firmware); return ret; } static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) { u16 offset1, offset2; u8 checksum; int i; /* copy cfg_bin head info */ if (cfg_bin->bin_data_len < sizeof(struct goodix_cfg_bin_head)) { ts_err("Invalid cfg_bin size:%d", cfg_bin->bin_data_len); return -EINVAL; } memcpy(&cfg_bin->head, cfg_bin->bin_data, sizeof(struct goodix_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); return -EINVAL; } /*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); return -EINVAL; } /*allocate memory for cfg packages*/ cfg_bin->cfg_pkgs = kzalloc( sizeof(struct goodix_cfg_package) * cfg_bin->head.pkg_num, GFP_KERNEL); if (!cfg_bin->cfg_pkgs) return -ENOMEM; /*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); 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); /*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); return 0; exit: kfree(cfg_bin->cfg_pkgs); return -EINVAL; } static int goodix_get_reg_and_cfg( struct goodix_ts_core *cd, u8 sensor_id, struct goodix_cfg_bin *cfg_bin) { int i; u8 cfg_type; u32 cfg_len; struct goodix_cfg_package *cfg_pkg; 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; } /* find cfg packages with same sensor_id */ for (i = 0; i < cfg_bin->head.pkg_num; i++) { cfg_pkg = &cfg_bin->cfg_pkgs[i]; if (sensor_id != cfg_pkg->cnst_info.sensor_id) { ts_info("pkg:%d, sensor id contrast FAILED, bin %d != %d", i, cfg_pkg->cnst_info.sensor_id, sensor_id); continue; } cfg_type = cfg_pkg->cnst_info.cfg_type; if (cfg_type >= GOODIX_MAX_CONFIG_GROUP) { ts_err("usupported config type %d", cfg_pkg->cnst_info.cfg_type); goto err_out; } cfg_len = cfg_pkg->pkg_len - TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN; if (cfg_len > GOODIX_CFG_MAX_SIZE) { ts_err("config len exceed limit %d > %d", cfg_len, GOODIX_CFG_MAX_SIZE); goto err_out; } if (cd->ic_configs[cfg_type]) { ts_err("found same type config twice for sensor id %d, skiped", sensor_id); continue; } cd->ic_configs[cfg_type] = kzalloc(sizeof(struct goodix_ic_config), GFP_KERNEL); if (!cd->ic_configs[cfg_type]) goto err_out; cd->ic_configs[cfg_type]->len = cfg_len; memcpy(cd->ic_configs[cfg_type]->data, cfg_pkg->cfg, cfg_len); ts_info("get config type %d, len %d, for sensor id %d", cfg_type, cfg_len, sensor_id); } return 0; err_out: /* parse config enter error, release memory alloced */ for (i = 0; i < GOODIX_MAX_CONFIG_GROUP; i++) { kfree(cd->ic_configs[i]); cd->ic_configs[i] = NULL; } return -EINVAL; } static int goodix_get_config_data(struct goodix_ts_core *cd, u8 sensor_id) { struct goodix_cfg_bin cfg_bin = { 0 }; char *cfg_name = cd->board_data.cfg_bin_name; int ret; /*get cfg_bin from file system*/ ret = goodix_read_cfg_bin(&cd->pdev->dev, cfg_name, &cfg_bin); if (ret) { ts_err("failed get valid config bin data"); return ret; } /*parse cfg bin*/ ret = goodix_parse_cfg_bin(&cfg_bin); if (ret) { ts_err("failed parse cfg bin"); goto err_out; } /*get register address and configuration from cfg bin*/ ret = goodix_get_reg_and_cfg(cd, sensor_id, &cfg_bin); if (!ret) ts_info("success get reg and cfg info from cfg bin"); else ts_err("failed get cfg and reg info, update fw then retry"); kfree(cfg_bin.cfg_pkgs); err_out: kfree(cfg_bin.bin_data); return ret; } int goodix_get_config_proc(struct goodix_ts_core *cd) { if (cd->board_data.use_one_binary) return 1; return goodix_get_config_data(cd, cd->fw_version.sensor_id); }