/* * 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" int goodix_set_cmd(struct goodix_ts_core *core_data, u8 reg, u8 mode) { struct goodix_ts_cmd temp_cmd; int ret = 0; temp_cmd.len = 5; temp_cmd.cmd = reg; temp_cmd.data[0] = mode; ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) ts_err("set(0x%X) mode [%d] failed", reg, mode); return ret; } static void fw_update(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = 0, update_type; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } update_type = sec->cmd_param[0]; switch (update_type) { case TSP_SDCARD: #if IS_ENABLED(CONFIG_SAMSUNG_PRODUCT_SHIP) update_type = TSP_SIGNED_SDCARD; #endif case TSP_BUILT_IN: case TSP_SPU: case TSP_VERIFICATION: ret = goodix_fw_update(core_data, update_type, true); if (ret) { ts_err("failed to fw update %d", ret); ret = -EIO; } break; default: ret = -EINVAL; ts_err("not supported %d", sec->cmd_param[0]); break; } goodix_get_custom_library(core_data); core_data->plat_data->init(core_data); if (ret < 0) { snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "OK"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_OK; } ts_info("%s", buff); } static void get_chip_vendor(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); snprintf(buff, sizeof(buff), "GOODIX"); ts_info("%s", buff); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "IC_VENDOR"); sec->cmd_state = SEC_CMD_STATUS_OK; } static void get_chip_name(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); snprintf(buff, sizeof(buff), "GT%s", core_data->fw_version.patch_pid); ts_info("%s", buff); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "IC_NAME"); sec->cmd_state = SEC_CMD_STATUS_OK; } static void get_fw_ver_ic(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; struct goodix_ic_info_sec *info_sec = &core_data->ic_info.sec; char buff[SEC_CMD_STR_LEN] = { 0 }; char model[SEC_CMD_STR_LEN] = { 0 }; int ret = 0; sec_cmd_set_default_result(sec); ret = hw_ops->read_version(core_data, &core_data->fw_version); if (ret) { ts_err("failed to read version, %d", ret); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } ret = hw_ops->get_ic_info(core_data, &core_data->ic_info); if (ret) { ts_err("failed to get ic info, %d", ret); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } /* IC version, Project version, module version, fw version */ snprintf(buff, sizeof(buff), "GT%02X%02X%02X%02X", info_sec->ic_name_list, info_sec->project_id, info_sec->module_version, info_sec->firmware_version); snprintf(model, sizeof(model), "GT%02X%02X", info_sec->ic_name_list, info_sec->project_id); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) { sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "FW_VER_IC"); sec_cmd_set_cmd_result_all(sec, model, strnlen(model, sizeof(model)), "FW_MODEL"); } sec->cmd_state = SEC_CMD_STATUS_OK; ts_info("%s", buff); } static void get_fw_ver_bin(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); /* IC version, Project version, module version, fw version */ snprintf(buff, sizeof(buff), "GT%02X%02X%02X%02X", core_data->fw_info_bin.ic_name_list, core_data->fw_info_bin.project_id, core_data->fw_info_bin.module_version, core_data->fw_info_bin.firmware_version); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "FW_VER_BIN"); sec->cmd_state = SEC_CMD_STATUS_OK; ts_info("%s", buff); } static void get_config_ver(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); snprintf(buff, sizeof(buff), "GT_%08X_%02X", core_data->ic_info.version.config_id, core_data->ic_info.version.config_version); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_OK; ts_info("%s", buff); } static void get_x_num(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[16] = { 0 }; sec_cmd_set_default_result(sec); snprintf(buff, sizeof(buff), "%d", core_data->ic_info.parm.drv_num); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_OK; ts_info("%s", buff); } static void get_y_num(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[16] = { 0 }; sec_cmd_set_default_result(sec); snprintf(buff, sizeof(buff), "%d", core_data->ic_info.parm.sen_num); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_OK; ts_info("%s", buff); } static void module_off_master(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[3] = { 0 }; ts_info("force power off"); core_data->hw_ops->irq_enable(core_data, false); goodix_ts_power_off(core_data); snprintf(buff, sizeof(buff), "OK"); sec_cmd_set_default_result(sec); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_OK; } static void module_on_master(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[3] = { 0 }; ts_info("force power on"); goodix_ts_power_on(core_data); core_data->hw_ops->irq_enable(core_data, true); snprintf(buff, sizeof(buff), "OK"); sec_cmd_set_default_result(sec); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_OK; } static void set_factory_level(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < OFFSET_FAC_SUB || sec->cmd_param[0] > OFFSET_FAC_MAIN) { ts_err("cmd data is abnormal, %d", sec->cmd_param[0]); goto NG; } core_data->factory_position = sec->cmd_param[0]; ts_info("%d", core_data->factory_position); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; NG: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); } enum trx_short_test_type { TEST_NONE = 0, OPEN_TEST, SHORT_TEST, }; static void run_trx_short_test(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = -EIO; int type = TEST_NONE; char test[32]; char fail_buff[1024] = {0}; char tempv[25] = {0}; sec_cmd_set_default_result(sec); if (core_data->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("IC is off"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } if (sec->cmd_param[0] == 1 && sec->cmd_param[1] == 1) { type = OPEN_TEST; } else if (sec->cmd_param[0] == 1 && sec->cmd_param[1] == 2) { type = SHORT_TEST; } if (type == TEST_NONE) { ts_err("unsupported param %d,%d", sec->cmd_param[0], sec->cmd_param[1]); snprintf(buff, sizeof(buff), "NA"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; return; } memset(core_data->test_data.open_short_test_trx_result, 0x00, OPEN_SHORT_TEST_RESULT_LEN); core_data->hw_ops->irq_enable(core_data, false); if (type == OPEN_TEST) { memset(&core_data->test_data.info[SEC_OPEN_TEST], 0x00, sizeof(struct goodix_test_info)); ret = goodix_open_test(core_data); if (ret < 0) goto out; if (core_data->test_data.info[SEC_OPEN_TEST].data[0] == 0) ret = 0; else ret = -EINVAL; } else if (type == SHORT_TEST) { memset(&core_data->test_data.info[SEC_SHORT_TEST], 0x00, sizeof(struct goodix_test_info)); ret = goodix_short_test(core_data); if (ret < 0) goto out; if (core_data->test_data.info[SEC_SHORT_TEST].data[0] == 0) ret = 0; else ret = -EINVAL; } out: goodix_ts_release_all_finger(core_data); core_data->hw_ops->reset(core_data, 200); core_data->hw_ops->irq_enable(core_data, true); if (sec->cmd_param[1]) snprintf(test, sizeof(test), "TEST=%d,%d", sec->cmd_param[0], sec->cmd_param[1]); else snprintf(test, sizeof(test), "TEST=%d", sec->cmd_param[0]); if (ret < 0) { int i, j; char *result_data = &core_data->test_data.open_short_test_trx_result[0]; sec_cmd_send_event_to_user(sec, test, "RESULT=FAIL"); if (type == OPEN_TEST) { snprintf(tempv, 25, " TX/RX_OPEN:"); } else if (type == SHORT_TEST) { snprintf(tempv, 25, " TX/RX_SHORT:"); } strlcat(fail_buff, tempv, sizeof(fail_buff)); /* make fail result */ /* DRV[0~55] total 7 bytes */ for (i = 0; i < DRV_CHAN_BYTES; i++) { if (result_data[i]) { for (j = 0; j < 8; j++) { if (result_data[i] & (1 << j)) { ts_info("DRV/TX[%d] open circuit, ret=0x%X", i * 8 + j, result_data[i]); memset(tempv, 0x00, 25); snprintf(tempv, 25, "TX%d,", i * 8 + j); strlcat(fail_buff, tempv, sizeof(fail_buff)); } } } } /* SEN[0~79] total 10 bytes */ for (i = 0; i < SEN_CHAN_BYTES; i++) { if (result_data[i + DRV_CHAN_BYTES]) { for (j = 0; j < 8; j++) { if (result_data[i + DRV_CHAN_BYTES] & (1 << j)) { ts_info("SEN/RX[%d] open circuit, ret=0x%X", i * 8 + j, result_data[i + DRV_CHAN_BYTES]); memset(tempv, 0x00, 25); snprintf(tempv, 20, "RX%d,", i * 8 + j); strlcat(fail_buff, tempv, sizeof(fail_buff)); } } } } ts_info("fail buff = %s", fail_buff); snprintf(buff, sizeof(buff), "NG%s", fail_buff); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { sec_cmd_send_event_to_user(sec, test, "RESULT=PASS"); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; } if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING && type == SHORT_TEST) { char tmp_buff[10] = { 0 }; if (!ret) snprintf(tmp_buff, sizeof(tmp_buff), "%d", GOODIX_TEST_RESULT_PASS); else snprintf(tmp_buff, sizeof(tmp_buff), "%d", GOODIX_TEST_RESULT_FAIL); sec_cmd_set_cmd_result_all(sec, tmp_buff, strnlen(tmp_buff, sizeof(tmp_buff)), "SHORT"); } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } static void goodix_ts_print_frame(struct goodix_ts_core *core_data, struct goodix_ts_test_rawdata *rawdata) { int i = 0; int j = 0; unsigned char *pStr = NULL; unsigned char pTmp[16] = { 0 }; int lsize = CMD_RESULT_WORD_LEN * (core_data->ic_info.parm.drv_num + 1); ts_raw_info("[MUTUAL] datasize:%d, min:%d, max:%d", rawdata->size, rawdata->min, rawdata->max); pStr = kzalloc(lsize, GFP_KERNEL); if (pStr == NULL) return; memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), " TX"); strlcat(pStr, pTmp, lsize); for (i = 0; i < core_data->ic_info.parm.drv_num; i++) { snprintf(pTmp, sizeof(pTmp), " %02d ", i); strlcat(pStr, pTmp, lsize); } ts_raw_info("%s", pStr); memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), " +"); strlcat(pStr, pTmp, lsize); for (i = 0; i < core_data->ic_info.parm.drv_num; i++) { snprintf(pTmp, sizeof(pTmp), "----"); strlcat(pStr, pTmp, lsize); } ts_raw_info("%s", pStr); for (i = 0; i < core_data->ic_info.parm.sen_num; i++) { memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), "RX%02d | ", i); strlcat(pStr, pTmp, lsize); for (j = 0; j < core_data->ic_info.parm.drv_num; j++) { snprintf(pTmp, sizeof(pTmp), " %5d", rawdata->data[j + (i * core_data->ic_info.parm.drv_num)]); strlcat(pStr, pTmp, lsize); } ts_raw_info("%s", pStr); } kfree(pStr); } static void goodix_ts_print_channel(struct goodix_ts_core *core_data, struct goodix_ts_test_self_rawdata *rawdata) { unsigned char *pStr = NULL; unsigned char pTmp[16] = { 0 }; int i = 0, j = 0, k = 0; int lsize = CMD_RESULT_WORD_LEN * (core_data->ic_info.parm.drv_num + 1); if (!core_data->ic_info.parm.drv_num) return; ts_raw_info("[SELF] datasize:%d, TX :min:%d, max:%d, RX :min:%d, max:%d", rawdata->size, rawdata->tx_min, rawdata->tx_max, rawdata->rx_min, rawdata->rx_max); pStr = vzalloc(lsize); if (!pStr) return; memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), " TX"); strlcat(pStr, pTmp, lsize); for (k = 0; k < core_data->ic_info.parm.drv_num; k++) { snprintf(pTmp, sizeof(pTmp), " %02d", k); strlcat(pStr, pTmp, lsize); } ts_raw_info("%s", pStr); memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), " +"); strlcat(pStr, pTmp, lsize); for (k = 0; k < core_data->ic_info.parm.drv_num; k++) { snprintf(pTmp, sizeof(pTmp), "------"); strlcat(pStr, pTmp, lsize); } ts_raw_info("%s", pStr); memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), " | "); strlcat(pStr, pTmp, lsize); for (i = 0; i < core_data->ic_info.parm.drv_num + core_data->ic_info.parm.sen_num; i++) { if (i == core_data->ic_info.parm.drv_num) { ts_raw_info("%s", pStr); ts_raw_info(" "); memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), " RX"); strlcat(pStr, pTmp, lsize); for (k = 0; k < core_data->ic_info.parm.sen_num; k++) { snprintf(pTmp, sizeof(pTmp), " %02d", k); strlcat(pStr, pTmp, lsize); } ts_raw_info("%s", pStr); memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), " +"); strlcat(pStr, pTmp, lsize); for (k = 0; k < core_data->ic_info.parm.drv_num; k++) { snprintf(pTmp, sizeof(pTmp), "------"); strlcat(pStr, pTmp, lsize); } ts_raw_info("%s", pStr); memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), " | "); strlcat(pStr, pTmp, lsize); } else if (i && !(i % core_data->ic_info.parm.drv_num)) { ts_raw_info("%s", pStr); memset(pStr, 0x0, lsize); snprintf(pTmp, sizeof(pTmp), " | "); strlcat(pStr, pTmp, lsize); } snprintf(pTmp, sizeof(pTmp), " %5d", rawdata->data[i]); strlcat(pStr, pTmp, lsize); j++; } ts_raw_info("%s", pStr); vfree(pStr); } static void goodix_get_gap_data(void *device_data, int freq) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_test_rawdata *rawdata; char buff[16] = { 0 }; char spec_name[SEC_CMD_STR_LEN] = { 0 }; int ii; int node_gap_tx = 0; int node_gap_rx = 0; int tx_max = 0; int rx_max = 0; if (freq == FREQ_HIGH) { rawdata = &core_data->test_data.high_freq_rawdata; snprintf(spec_name, sizeof(spec_name), "HIGH_FREQ_MUTUAL_RAW_GAP"); } else if (freq == FREQ_LOW) { rawdata = &core_data->test_data.low_freq_rawdata; snprintf(spec_name, sizeof(spec_name), "LOW_FREQ_MUTUAL_RAW_GAP"); } else { rawdata = &core_data->test_data.rawdata; snprintf(spec_name, sizeof(spec_name), "MUTUAL_RAW_GAP"); } for (ii = 0; ii < (core_data->ic_info.parm.sen_num * core_data->ic_info.parm.drv_num); ii++) { if ((ii + 1) % (core_data->ic_info.parm.drv_num) != 0) { if (rawdata->data[ii] > rawdata->data[ii + 1]) node_gap_tx = 100 - (rawdata->data[ii + 1] * 100 / rawdata->data[ii]); else node_gap_tx = 100 - (rawdata->data[ii] * 100 / rawdata->data[ii + 1]); tx_max = max(tx_max, node_gap_tx); } if (ii < (core_data->ic_info.parm.sen_num - 1) * core_data->ic_info.parm.drv_num) { if (rawdata->data[ii] > rawdata->data[ii + core_data->ic_info.parm.drv_num]) node_gap_rx = 100 - (rawdata->data[ii + core_data->ic_info.parm.drv_num] * 100 / rawdata->data[ii]); else node_gap_rx = 100 - (rawdata->data[ii] * 100 / rawdata->data[ii + core_data->ic_info.parm.drv_num]); rx_max = max(rx_max, node_gap_rx); } } if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) { char temp[SEC_CMD_STR_LEN] = { 0 }; snprintf(temp, sizeof(temp), "%s_X", spec_name); snprintf(buff, sizeof(buff), "%d,%d", 0, tx_max); sec_cmd_set_cmd_result_all(sec, buff, SEC_CMD_STR_LEN, temp); snprintf(temp, sizeof(temp), "%s_Y", spec_name); snprintf(buff, sizeof(buff), "%d,%d", 0, rx_max); sec_cmd_set_cmd_result_all(sec, buff, SEC_CMD_STR_LEN, temp); snprintf(buff, sizeof(buff), "%d,%d", 0, max(tx_max, rx_max)); sec_cmd_set_cmd_result_all(sec, buff, SEC_CMD_STR_LEN, spec_name); } sec->cmd_state = SEC_CMD_STATUS_OK; } static void get_high_frequency_gap_data(void *device_data) { goodix_get_gap_data(device_data, FREQ_HIGH); } static void get_low_frequency_gap_data(void *device_data) { goodix_get_gap_data(device_data, FREQ_LOW); } static void get_gap_data(void *device_data) { goodix_get_gap_data(device_data, FREQ_NORMAL); } static void goodix_get_gap_data_all(void *device_data, int freq) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_test_rawdata *rawdata; char *buff = NULL; int ii; int node_gap = 0; int node_gap_tx = 0; int node_gap_rx = 0; char temp[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); buff = kzalloc(core_data->ic_info.parm.drv_num * core_data->ic_info.parm.sen_num * CMD_RESULT_WORD_LEN, GFP_KERNEL); if (!buff) return; if (freq == FREQ_HIGH) rawdata = &core_data->test_data.high_freq_rawdata; else if (freq == FREQ_LOW) rawdata = &core_data->test_data.low_freq_rawdata; else rawdata = &core_data->test_data.rawdata; for (ii = 0; ii < (core_data->ic_info.parm.sen_num * core_data->ic_info.parm.drv_num); ii++) { node_gap = node_gap_tx = node_gap_rx = 0; if ((ii + 1) % (core_data->ic_info.parm.drv_num) != 0) { if (rawdata->data[ii] > rawdata->data[ii + 1]) node_gap_tx = 100 - (rawdata->data[ii + 1] * 100 / rawdata->data[ii]); else node_gap_tx = 100 - (rawdata->data[ii] * 100 / rawdata->data[ii + 1]); } if (ii < (core_data->ic_info.parm.sen_num - 1) * core_data->ic_info.parm.drv_num) { if (rawdata->data[ii] > rawdata->data[ii + core_data->ic_info.parm.drv_num]) node_gap_rx = 100 - (rawdata->data[ii + core_data->ic_info.parm.drv_num] * 100 / rawdata->data[ii]); else node_gap_rx = 100 - (rawdata->data[ii] * 100 / rawdata->data[ii + core_data->ic_info.parm.drv_num]); } node_gap = max(node_gap_tx, node_gap_rx); snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", node_gap); strlcat(buff, temp, core_data->ic_info.parm.drv_num * core_data->ic_info.parm.sen_num * CMD_RESULT_WORD_LEN); memset(temp, 0x00, SEC_CMD_STR_LEN); } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, core_data->ic_info.parm.drv_num * core_data->ic_info.parm.sen_num * CMD_RESULT_WORD_LEN)); sec->cmd_state = SEC_CMD_STATUS_OK; kfree(buff); } static void get_high_frequency_gap_data_all(void *device_data) { goodix_get_gap_data_all(device_data, FREQ_HIGH); } static void get_low_frequency_gap_data_all(void *device_data) { goodix_get_gap_data_all(device_data, FREQ_LOW); } static void get_gap_data_all(void *device_data) { goodix_get_gap_data_all(device_data, FREQ_NORMAL); } static void goodix_run_rawdata_read(void *device_data, int freq) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_test_rawdata *rawdata; char buff[SEC_CMD_STR_LEN] = { 0 }; char spec_name[SEC_CMD_STR_LEN] = { 0 }; int ret = -EIO; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } core_data->hw_ops->irq_enable(core_data, false); ts_raw_info("[FREQ] %s", (freq == FREQ_HIGH) ? "HIGH" : (freq == FREQ_LOW) ? "LOW" : "NORMAL"); if (freq == FREQ_HIGH) { rawdata = &core_data->test_data.high_freq_rawdata; snprintf(spec_name, sizeof(spec_name), "HIGH_FREQ_MUTUAL_RAW"); } else if (freq == FREQ_LOW) { rawdata = &core_data->test_data.low_freq_rawdata; snprintf(spec_name, sizeof(spec_name), "LOW_FREQ_MUTUAL_RAW"); } else { rawdata = &core_data->test_data.rawdata; snprintf(spec_name, sizeof(spec_name), "MUTUAL_RAW"); } memset(rawdata, 0x00, sizeof(struct goodix_ts_test_rawdata)); ret = goodix_cache_rawdata(core_data, RAWDATA_TEST_TYPE_MUTUAL_RAW, freq); if (ret < 0) { ts_err("test failed, %d", ret); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "%d,%d", rawdata->min, rawdata->max); sec->cmd_state = SEC_CMD_STATUS_OK; } goodix_ts_print_frame(core_data, rawdata); goodix_ts_release_all_finger(core_data); core_data->hw_ops->reset(core_data, 200); core_data->hw_ops->irq_enable(core_data, true); if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), spec_name); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } static void run_high_frequency_rawdata_read(void *device_data) { goodix_run_rawdata_read(device_data, FREQ_HIGH); } static void run_low_frequency_rawdata_read(void *device_data) { goodix_run_rawdata_read(device_data, FREQ_LOW); } static void run_rawdata_read(void *device_data) { goodix_run_rawdata_read(device_data, FREQ_NORMAL); } static void run_rawdata_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char *rawdata_buff; u8 tx = core_data->ic_info.parm.drv_num; u8 rx = core_data->ic_info.parm.sen_num; int buff_size = tx * rx * 7; int i; sec_cmd_set_default_result(sec); rawdata_buff = vzalloc(buff_size); if (!rawdata_buff) { ts_err("failed to alloc rawdata_buff"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; } run_rawdata_read(sec); sec_cmd_set_default_result(sec); if (core_data->test_data.rawdata.size != (tx * rx)) { ts_err("test result is NG"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); vfree(rawdata_buff); return; } for (i = 0; i < core_data->test_data.rawdata.size; i++) { snprintf(buff, sizeof(buff), "%d,", core_data->test_data.rawdata.data[i]); strlcat(rawdata_buff, buff, buff_size); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, rawdata_buff, buff_size); ts_info("%s", rawdata_buff); vfree(rawdata_buff); } static void run_cs_raw_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char *rawdata_buff; u8 tx = core_data->ic_info.parm.drv_num; u8 rx = core_data->ic_info.parm.sen_num; int buff_size = tx * rx * 7; int i; sec_cmd_set_default_result(sec); rawdata_buff = vzalloc(buff_size); if (!rawdata_buff) { ts_err("failed to alloc rawdata_buff"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; } core_data->hw_ops->irq_enable(core_data, false); goodix_read_realtime(core_data, RAWDATA_TEST_TYPE_MUTUAL_RAW); goodix_ts_release_all_finger(core_data); core_data->hw_ops->irq_enable(core_data, true); goodix_ts_print_frame(core_data, &core_data->test_data.rawdata); if (core_data->test_data.rawdata.size != (tx * rx)) { ts_err("test result is NG"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); vfree(rawdata_buff); return; } for (i = 0; i < core_data->test_data.rawdata.size; i++) { snprintf(buff, sizeof(buff), "%d,", core_data->test_data.rawdata.data[i]); strlcat(rawdata_buff, buff, buff_size); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, rawdata_buff, buff_size); ts_info("%s", rawdata_buff); vfree(rawdata_buff); } static void run_cs_delta_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char *rawdata_buff; u8 tx = core_data->ic_info.parm.drv_num; u8 rx = core_data->ic_info.parm.sen_num; int buff_size = tx * rx * 7; int i; sec_cmd_set_default_result(sec); rawdata_buff = vzalloc(buff_size); if (!rawdata_buff) { ts_err("failed to alloc rawdata_buff"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; } core_data->hw_ops->irq_enable(core_data, false); goodix_read_realtime(core_data, RAWDATA_TEST_TYPE_MUTUAL_DIFF); goodix_ts_release_all_finger(core_data); core_data->hw_ops->irq_enable(core_data, true); goodix_ts_print_frame(core_data, &core_data->test_data.diffdata); if (core_data->test_data.diffdata.size != (tx * rx)) { ts_err("test result is NG"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); vfree(rawdata_buff); return; } for (i = 0; i < core_data->test_data.diffdata.size; i++) { snprintf(buff, sizeof(buff), "%d,", core_data->test_data.diffdata.data[i]); strlcat(rawdata_buff, buff, buff_size); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, rawdata_buff, buff_size); ts_info("%s", rawdata_buff); vfree(rawdata_buff); } static void run_high_frequency_rawdata_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char *rawdata_buff; u8 tx = core_data->ic_info.parm.drv_num; u8 rx = core_data->ic_info.parm.sen_num; int buff_size = tx * rx * 7; int i; sec_cmd_set_default_result(sec); rawdata_buff = vzalloc(buff_size); if (!rawdata_buff) { ts_err("failed to alloc rawdata_buff"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; } run_high_frequency_rawdata_read(sec); sec_cmd_set_default_result(sec); if (core_data->test_data.high_freq_rawdata.size != (tx * rx)) { ts_err("test result is NG"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); vfree(rawdata_buff); return; } for (i = 0; i < core_data->test_data.high_freq_rawdata.size; i++) { snprintf(buff, sizeof(buff), "%d,", core_data->test_data.high_freq_rawdata.data[i]); strlcat(rawdata_buff, buff, buff_size); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, rawdata_buff, buff_size); ts_info("%s", rawdata_buff); vfree(rawdata_buff); } static void run_low_frequency_rawdata_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char *rawdata_buff; u8 tx = core_data->ic_info.parm.drv_num; u8 rx = core_data->ic_info.parm.sen_num; int buff_size = tx * rx * 7; int i; sec_cmd_set_default_result(sec); rawdata_buff = vzalloc(buff_size); if (!rawdata_buff) { ts_err("failed to alloc rawdata_buff"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; } run_low_frequency_rawdata_read(sec); sec_cmd_set_default_result(sec); if (core_data->test_data.low_freq_rawdata.size != (tx * rx)) { ts_err("test result is NG"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); vfree(rawdata_buff); return; } for (i = 0; i < core_data->test_data.low_freq_rawdata.size; i++) { snprintf(buff, sizeof(buff), "%d,", core_data->test_data.low_freq_rawdata.data[i]); strlcat(rawdata_buff, buff, buff_size); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, rawdata_buff, buff_size); ts_info("%s", rawdata_buff); vfree(rawdata_buff); } static void run_diffdata_read(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = -EIO; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } core_data->hw_ops->irq_enable(core_data, false); memset(&core_data->test_data.diffdata, 0x00, sizeof(core_data->test_data.diffdata)); ret = goodix_cache_rawdata(core_data, RAWDATA_TEST_TYPE_MUTUAL_DIFF, FREQ_NORMAL); if (ret < 0) { ts_err("test failed, %d", ret); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "%d,%d", core_data->test_data.diffdata.min, core_data->test_data.diffdata.max); sec->cmd_state = SEC_CMD_STATUS_OK; } goodix_ts_print_frame(core_data, &core_data->test_data.diffdata); goodix_ts_release_all_finger(core_data); core_data->hw_ops->reset(core_data, 200); core_data->hw_ops->irq_enable(core_data, true); if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "MUTUAL_DIFF"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } static void run_diffdata_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char *rawdata_buff; u8 tx = core_data->ic_info.parm.drv_num; u8 rx = core_data->ic_info.parm.sen_num; int buff_size = tx * rx * 7; int i; sec_cmd_set_default_result(sec); rawdata_buff = vzalloc(buff_size); if (!rawdata_buff) { ts_err("failed to alloc rawdata_buff"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; } run_diffdata_read(sec); sec_cmd_set_default_result(sec); if (core_data->test_data.diffdata.size != (tx * rx)) { ts_err("test result is NG"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); vfree(rawdata_buff); return; } for (i = 0; i < core_data->test_data.diffdata.size; i++) { snprintf(buff, sizeof(buff), "%d,", core_data->test_data.diffdata.data[i]); strlcat(rawdata_buff, buff, buff_size); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, rawdata_buff, buff_size); ts_info("%s", rawdata_buff); vfree(rawdata_buff); } static void run_self_rawdata_tx_read(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = -EIO; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } core_data->hw_ops->irq_enable(core_data, false); memset(&core_data->test_data.selfraw, 0x00, sizeof(core_data->test_data.selfraw)); ret = goodix_cache_rawdata(core_data, RAWDATA_TEST_TYPE_SELF_RAW, FREQ_NORMAL); if (ret < 0) { ts_err("test failed, %d", ret); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "%d,%d", core_data->test_data.selfraw.tx_min, core_data->test_data.selfraw.tx_max); sec->cmd_state = SEC_CMD_STATUS_OK; } goodix_ts_print_channel(core_data, &core_data->test_data.selfraw); goodix_ts_release_all_finger(core_data); core_data->hw_ops->reset(core_data, 200); core_data->hw_ops->irq_enable(core_data, true); if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "SELF_RAW_TX"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } static void run_self_rawdata_tx_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char *rawdata_buff; u8 tx = core_data->ic_info.parm.drv_num; u8 rx = core_data->ic_info.parm.sen_num; int buff_size = (tx + rx) * 7; int i; sec_cmd_set_default_result(sec); rawdata_buff = vzalloc(buff_size); if (!rawdata_buff) { ts_err("failed to alloc rawdata_buff"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; } run_self_rawdata_tx_read(sec); sec_cmd_set_default_result(sec); if (core_data->test_data.selfraw.size != (tx + rx)) { ts_err("test result is NG"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); vfree(rawdata_buff); return; } for (i = 0; i < tx; i++) { snprintf(buff, sizeof(buff), "%d,", core_data->test_data.selfraw.data[i]); strlcat(rawdata_buff, buff, buff_size); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, rawdata_buff, buff_size); ts_info("%s", rawdata_buff); vfree(rawdata_buff); } static void run_self_rawdata_rx_read(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = -EIO; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } core_data->hw_ops->irq_enable(core_data, false); memset(&core_data->test_data.selfraw, 0x00, sizeof(core_data->test_data.selfraw)); ret = goodix_cache_rawdata(core_data, RAWDATA_TEST_TYPE_SELF_RAW, FREQ_NORMAL); if (ret < 0) { ts_err("test failed, %d", ret); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "%d,%d", core_data->test_data.selfraw.rx_min, core_data->test_data.selfraw.rx_max); sec->cmd_state = SEC_CMD_STATUS_OK; } goodix_ts_print_channel(core_data, &core_data->test_data.selfraw); goodix_ts_release_all_finger(core_data); core_data->hw_ops->reset(core_data, 200); core_data->hw_ops->irq_enable(core_data, true); if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "SELF_RAW_RX"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } static void run_self_rawdata_rx_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char *rawdata_buff; u8 tx = core_data->ic_info.parm.drv_num; u8 rx = core_data->ic_info.parm.sen_num; int buff_size = (tx + rx) * 7; int i; sec_cmd_set_default_result(sec); rawdata_buff = vzalloc(buff_size); if (!rawdata_buff) { ts_err("failed to alloc rawdata_buff"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; } run_self_rawdata_rx_read(sec); sec_cmd_set_default_result(sec); if (core_data->test_data.selfraw.size != (tx + rx)) { ts_err("test result is NG"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); vfree(rawdata_buff); return; } for (i = tx; i < (tx + rx); i++) { snprintf(buff, sizeof(buff), "%d,", core_data->test_data.selfraw.data[i]); strlcat(rawdata_buff, buff, buff_size); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, rawdata_buff, buff_size); ts_info("%s", rawdata_buff); vfree(rawdata_buff); } static void run_self_diffdata_read(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = -EIO; int min, max; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } core_data->hw_ops->irq_enable(core_data, false); memset(&core_data->test_data.selfdiff, 0x00, sizeof(core_data->test_data.selfdiff)); ret = goodix_cache_rawdata(core_data, RAWDATA_TEST_TYPE_SELF_DIFF, FREQ_NORMAL); if (ret < 0) { ts_err("test failed, %d", ret); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { min = core_data->test_data.selfdiff.tx_min > core_data->test_data.selfdiff.rx_min ? core_data->test_data.selfdiff.rx_min : core_data->test_data.selfdiff.tx_min; max = core_data->test_data.selfdiff.tx_max > core_data->test_data.selfdiff.rx_max ? core_data->test_data.selfdiff.tx_max : core_data->test_data.selfdiff.rx_max; ts_raw_info("%s : selfdiff tx: %d,%d rx: %d,%d\n", __func__, core_data->test_data.selfdiff.tx_min, core_data->test_data.selfdiff.tx_max, core_data->test_data.selfdiff.rx_min, core_data->test_data.selfdiff.rx_max); snprintf(buff, sizeof(buff), "%d,%d", min, max); sec->cmd_state = SEC_CMD_STATUS_OK; } goodix_ts_print_channel(core_data, &core_data->test_data.selfdiff); goodix_ts_release_all_finger(core_data); core_data->hw_ops->reset(core_data, 200); core_data->hw_ops->irq_enable(core_data, true); if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "SELF_DIFF"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } static void run_self_diffdata_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char *rawdata_buff; u8 tx = core_data->ic_info.parm.drv_num; u8 rx = core_data->ic_info.parm.sen_num; int buff_size = (tx + rx) * 7; int i; sec_cmd_set_default_result(sec); rawdata_buff = vzalloc(buff_size); if (!rawdata_buff) { ts_err("failed to alloc rawdata_buff"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); return; } run_self_diffdata_read(sec); sec_cmd_set_default_result(sec); if (core_data->test_data.selfdiff.size != (tx + rx)) { ts_err("test result is NG"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); vfree(rawdata_buff); return; } for (i = 0; i < core_data->test_data.selfdiff.size; i++) { snprintf(buff, sizeof(buff), "%d,", core_data->test_data.selfdiff.data[i]); strlcat(rawdata_buff, buff, buff_size); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, rawdata_buff, buff_size); ts_info("%s", rawdata_buff); vfree(rawdata_buff); } static void run_jitter_test(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = -EIO; int mutual[2] = { 0 }; int self[2] = { 0 }; int min_idx = 0; int max_idx = 1; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } core_data->hw_ops->irq_enable(core_data, false); memset(&core_data->test_data.info[SEC_JITTER1_TEST], 0x00, sizeof(struct goodix_test_info)); ret = goodix_jitter_test(core_data, JITTER_100_FRAMES); if (ret < 0) { ts_err("test failed, %d", ret); goto out; } if (!core_data->test_data.info[SEC_JITTER1_TEST].isFinished) { ts_err("test is not finished"); ret = -EIO; goto out; } mutual[min_idx] = core_data->test_data.info[SEC_JITTER1_TEST].data[1]; mutual[max_idx] = core_data->test_data.info[SEC_JITTER1_TEST].data[0]; self[min_idx] = core_data->test_data.info[SEC_JITTER1_TEST].data[3]; self[max_idx] = core_data->test_data.info[SEC_JITTER1_TEST].data[2]; ts_raw_info("mutual: %d, %d | self: %d, %d", mutual[min_idx], mutual[max_idx], self[min_idx], self[max_idx]); out: goodix_ts_release_all_finger(core_data); core_data->hw_ops->reset(core_data, 200); core_data->hw_ops->irq_enable(core_data, true); if (ret < 0) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) { sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "MUTUAL_JITTER"); sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "SELF_JITTER"); } } else { snprintf(buff, sizeof(buff), "%d,%d", mutual[min_idx], mutual[max_idx]); sec->cmd_state = SEC_CMD_STATUS_OK; if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) { char buffer[SEC_CMD_STR_LEN] = { 0 }; snprintf(buffer, sizeof(buffer), "%d,%d", mutual[min_idx], mutual[max_idx]); sec_cmd_set_cmd_result_all(sec, buffer, strnlen(buffer, sizeof(buffer)), "MUTUAL_JITTER"); memset(buffer, 0x00, sizeof(buffer)); snprintf(buffer, sizeof(buffer), "%d,%d", self[min_idx], self[max_idx]); sec_cmd_set_cmd_result_all(sec, buffer, strnlen(buffer, sizeof(buffer)), "SELF_JITTER"); } } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } static void run_jitter_delta_test(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = -EIO; int min_matrix[2] = { 0 }; int max_matrix[2] = { 0 }; int avg_matrix[2] = { 0 }; int min_idx = 0; int max_idx = 1; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } core_data->hw_ops->irq_enable(core_data, false); memset(&core_data->test_data.info[SEC_JITTER2_TEST], 0x00, sizeof(struct goodix_test_info)); ret = goodix_jitter_test(core_data, JITTER_1000_FRAMES); if (ret < 0) { ts_err("test failed, %d", ret); goto out; } if (!core_data->test_data.info[SEC_JITTER2_TEST].isFinished) { ts_err("test is not finished"); ret = -EIO; goto out; } min_matrix[min_idx] = core_data->test_data.info[SEC_JITTER2_TEST].data[0]; min_matrix[max_idx] = core_data->test_data.info[SEC_JITTER2_TEST].data[1]; max_matrix[min_idx] = core_data->test_data.info[SEC_JITTER2_TEST].data[2]; max_matrix[max_idx] = core_data->test_data.info[SEC_JITTER2_TEST].data[3]; avg_matrix[min_idx] = core_data->test_data.info[SEC_JITTER2_TEST].data[4]; avg_matrix[max_idx] = core_data->test_data.info[SEC_JITTER2_TEST].data[5]; ts_raw_info("min: %d, %d | max: %d, %d | avg: %d, %d", min_matrix[min_idx], min_matrix[max_idx], max_matrix[min_idx], max_matrix[max_idx], avg_matrix[min_idx], avg_matrix[max_idx]); out: goodix_ts_release_all_finger(core_data); core_data->hw_ops->reset(core_data, 200); core_data->hw_ops->irq_enable(core_data, true); if (ret < 0) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) { sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "JITTER_DELTA_MIN"); sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "JITTER_DELTA_MAX"); sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "JITTER_DELTA_AVG"); } } else { snprintf(buff, sizeof(buff), "%d,%d,%d,%d,%d,%d", min_matrix[min_idx], min_matrix[max_idx], max_matrix[min_idx], max_matrix[max_idx], avg_matrix[min_idx], avg_matrix[max_idx]); sec->cmd_state = SEC_CMD_STATUS_OK; if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) { char buffer[SEC_CMD_STR_LEN] = { 0 }; snprintf(buffer, sizeof(buffer), "%d,%d", min_matrix[min_idx], min_matrix[max_idx]); sec_cmd_set_cmd_result_all(sec, buffer, strnlen(buffer, sizeof(buffer)), "JITTER_DELTA_MIN"); memset(buffer, 0x00, sizeof(buffer)); snprintf(buffer, sizeof(buffer), "%d,%d", max_matrix[min_idx], max_matrix[max_idx]); sec_cmd_set_cmd_result_all(sec, buffer, strnlen(buffer, sizeof(buffer)), "JITTER_DELTA_MAX"); memset(buffer, 0x00, sizeof(buffer)); snprintf(buffer, sizeof(buffer), "%d,%d", avg_matrix[min_idx], avg_matrix[max_idx]); sec_cmd_set_cmd_result_all(sec, buffer, strnlen(buffer, sizeof(buffer)), "JITTER_DELTA_AVG"); } } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } static void run_snr_non_touched(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = 1; int frame_cnt = 0; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } if (sec->cmd_param[0] < 1 || sec->cmd_param[0] > 1000) { ts_err("abnormal parm : %d", sec->cmd_param[0]); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } frame_cnt = sec->cmd_param[0]; ts_raw_info("frame_cnt(%d)", frame_cnt); goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); ret = goodix_snr_test(core_data, SNR_TEST_NON_TOUCH, frame_cnt); goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); if (ret < 0) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } static void run_snr_touched(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; char tbuff[SEC_CMD_STR_LEN] = { 0 }; int ret = 1; int i = 0; int frame_cnt = 0; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } if (sec->cmd_param[0] < 1 || sec->cmd_param[0] > 1000) { ts_err("abnormal parm : %d", sec->cmd_param[0]); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } frame_cnt = sec->cmd_param[0]; ts_raw_info("frame_cnt(%d)", frame_cnt); goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); ret = goodix_snr_test(core_data, SNR_TEST_TOUCH, frame_cnt); goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); if (ret < 0) { snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } for (i = 0 ; i < 9 ; i++) { ts_raw_info("[#%d] average:%d, snr1:%d, snr2:%d\n", i, core_data->test_data.snr_result[i * 3], core_data->test_data.snr_result[i * 3 + 1], core_data->test_data.snr_result[i * 3 + 2]); snprintf(tbuff, sizeof(tbuff), "%d,%d,%d,", core_data->test_data.snr_result[i * 3], core_data->test_data.snr_result[i * 3 + 1], core_data->test_data.snr_result[i * 3 + 2]); strlcat(buff, tbuff, sizeof(buff)); } sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("done : %s", buff); } static void run_sram_test(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = 1; sec_cmd_set_default_result(sec); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; return; } core_data->hw_ops->irq_enable(core_data, false); memset(&core_data->test_data.info[SEC_SRAM_TEST], 0x00, sizeof(struct goodix_test_info)); ret = goodix_sram_test(core_data); if (ret < 0) { ts_err("test failed, %d", ret); goto out; } if (!core_data->test_data.info[SEC_SRAM_TEST].isFinished) { ts_err("test is not finished"); ret = -EIO; goto out; } ret = core_data->test_data.info[SEC_SRAM_TEST].data[0]; out: goodix_ts_release_all_finger(core_data); core_data->hw_ops->reset(core_data, 200); core_data->hw_ops->irq_enable(core_data, true); if (ret < 0) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "%d", ret); sec->cmd_state = SEC_CMD_STATUS_OK; } if (sec->cmd_all_factory_state == SEC_CMD_STATUS_RUNNING) sec_cmd_set_cmd_result_all(sec, buff, strnlen(buff, sizeof(buff)), "SRAM"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); ts_raw_info("%s", buff); } int goodix_write_nvm_data(struct goodix_ts_core *cd, unsigned char *data, int size); int goodix_read_nvm_data(struct goodix_ts_core *cd, unsigned char *data, int size); static void increase_disassemble_count(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); int ret = 0; char buff[SEC_CMD_STR_LEN] = { 0 }; unsigned char data[2] = { 0 }; sec_cmd_set_default_result(sec); ret = goodix_read_nvm_data(core_data, data, 1); if (ret < 0) { ts_err("nvm read error(%d)", ret); goto out; } ts_info("nvm read disassemble_count(%d)", data[0]); if (data[0] == 0xFF) data[0] = 0; if (data[0] < 0xFE) data[0]++; ret = goodix_write_nvm_data(core_data, data, 1); if (ret < 0) { ts_err("nvm write error(%d)", ret); } out: snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); } static void get_disassemble_count(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; unsigned char data[2] = { 0 }; int ret = 0; sec_cmd_set_default_result(sec); ret = goodix_read_nvm_data(core_data, data, 1); if (ret < 0) { ts_err("nvm read error(%d)", data[0]); data[0] = 0; } if (data[0] == 0xff) { ts_err("clear nvm disassemble count(%X)", data[0]); data[0] = 0; ret = goodix_write_nvm_data(core_data, data, 1); if (ret < 0) { ts_err("nvm write error(%d)", ret); } } ts_info("nvm disassemble count(%d)", data[0]); snprintf(buff, sizeof(buff), "%d", data[0]); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); } static void factory_cmd_result_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); sec->item_count = 0; memset(sec->cmd_result_all, 0x00, SEC_CMD_RESULT_STR_LEN); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); sec->cmd_all_factory_state = SEC_CMD_STATUS_FAIL; goto out; } mutex_lock(&core_data->input_dev->mutex); sec->cmd_all_factory_state = SEC_CMD_STATUS_RUNNING; get_chip_vendor(sec); get_chip_name(sec); get_fw_ver_bin(sec); get_fw_ver_ic(sec); run_rawdata_read(sec); get_gap_data(sec); run_high_frequency_rawdata_read(sec); get_high_frequency_gap_data(sec); run_low_frequency_rawdata_read(sec); get_low_frequency_gap_data(sec); run_self_rawdata_rx_read(sec); run_self_rawdata_tx_read(sec); run_jitter_test(sec); run_sram_test(sec); /* for short test */ sec->cmd_param[0] = 1; sec->cmd_param[1] = 2; run_trx_short_test(sec); sec->cmd_all_factory_state = SEC_CMD_STATUS_OK; mutex_unlock(&core_data->input_dev->mutex); out: ts_raw_info("%d%s", sec->item_count, sec->cmd_result_all); } void goodix_ts_run_rawdata_all(struct goodix_ts_core *cd) { struct sec_cmd_data *sec = &cd->sec; run_rawdata_read(sec); } static void factory_cmd_result_all_imagetest(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); sec->item_count = 0; memset(sec->cmd_result_all, 0x00, SEC_CMD_RESULT_STR_LEN); if (atomic_read(&core_data->suspended)) { ts_err("IC is on suspend state"); sec->cmd_all_factory_state = SEC_CMD_STATUS_FAIL; goto out; } mutex_lock(&core_data->input_dev->mutex); sec->cmd_all_factory_state = SEC_CMD_STATUS_RUNNING; run_jitter_delta_test(sec); sec->cmd_all_factory_state = SEC_CMD_STATUS_OK; mutex_unlock(&core_data->input_dev->mutex); out: ts_info("%d%s", sec->item_count, sec->cmd_result_all); } #if 0 static int goodix_ts_set_mode(struct goodix_ts_core *core_data, u8 reg, u8 data, int len) { struct goodix_ts_cmd temp_cmd; int ret; temp_cmd.len = len; temp_cmd.cmd = reg; temp_cmd.data[0] = data; ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_err("send cmd failed"); return ret; } ts_info("reg : 0x0X, data:0x0X, len:%d", reg, data, len); return 0; } #endif #define GOODIX_POKET_MODE_STATE_ADDR 0x71 // 0:ed, 1:pocket static void pocket_mode_enable(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { ts_err("abnormal parm (%d)", sec->cmd_param[0]); goto fail; } if (core_data->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("IC is OFF"); goto fail; } core_data->plat_data->pocket_mode = sec->cmd_param[0]; ts_info("pocket mode : %s", core_data->plat_data->pocket_mode ? "on" : "off"); ret = core_data->hw_ops->pocket_mode_enable(core_data, core_data->plat_data->pocket_mode); if (ret < 0) { ts_err("send pocket mode cmd failed"); goto fail; } ts_info("send pocket mode cmd done"); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; fail: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } static void ear_detect_enable(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret; sec_cmd_set_default_result(sec); if (!(sec->cmd_param[0] == 0 || sec->cmd_param[0] == 1 || sec->cmd_param[0] == 3)) { ts_err("abnormal parm (%d)", sec->cmd_param[0]); goto fail; } if (core_data->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("IC is OFF"); goto fail; } core_data->plat_data->ed_enable = sec->cmd_param[0]; ts_info("ear detect mode(%d)", core_data->plat_data->ed_enable); ret = core_data->hw_ops->ed_enable(core_data, core_data->plat_data->ed_enable); if (ret < 0) { ts_err("send ear detect cmd failed"); goto fail; } ts_info("send ear detect cmd done"); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; fail: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } static void run_prox_intensity_read_all(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; struct goodix_ts_cmd temp_cmd; int retry = 20; u16 sum_x, sum_y; u16 thd_x, thd_y; u8 temp_buf[11]; int ret; sec_cmd_set_default_result(sec); if (core_data->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("IC is OFF"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } /* must enabled call mode 3 firstly */ if (core_data->plat_data->ed_enable != 3) { temp_cmd.cmd = GOODIX_ED_MODE_ADDR; temp_cmd.data[0] = 3; temp_cmd.len = 5; ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_err("enable ear mode failed"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; goto out; } } temp_cmd.cmd = 0x79; temp_cmd.data[0] = 1; temp_cmd.len = 5; ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_err("enable prox test failed"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; goto out; } while (retry--) { sec_delay(5); ret = core_data->hw_ops->read(core_data, 0x15D4C, temp_buf, sizeof(temp_buf)); if (ret == 0 && temp_buf[0] == 0xAA) break; } if (retry < 0) { ts_err("SUM value not ready, status[%X]", temp_buf[0]); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; goto out; } if (checksum_cmp(temp_buf + 1, sizeof(temp_buf) - 1, CHECKSUM_MODE_U8_LE)) { ts_err("prox data checksum error"); ts_err("data:%*ph", (int)sizeof(temp_buf) - 1, temp_buf + 1); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; goto out; } sum_x = le16_to_cpup((__le16 *)(temp_buf + 1)); sum_y = le16_to_cpup((__le16 *)(temp_buf + 3)); thd_x = le16_to_cpup((__le16 *)(temp_buf + 5)); thd_y = le16_to_cpup((__le16 *)(temp_buf + 7)); ts_info("SUM_X:%d SUM_Y:%d THD_X:%d THD_Y:%d", sum_x, sum_y, thd_x, thd_y); snprintf(buff, sizeof(buff), "SUM_X:%d SUM_Y:%d THD_X:%d THD_Y:%d", sum_x, sum_y, thd_x, thd_y); sec->cmd_state = SEC_CMD_STATUS_OK; out: temp_cmd.cmd = 0x79; temp_cmd.data[0] = 0; temp_cmd.len = 5; core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (core_data->plat_data->ed_enable != 3) { temp_cmd.cmd = GOODIX_ED_MODE_ADDR; temp_cmd.data[0] = 0; temp_cmd.len = 5; core_data->hw_ops->send_cmd(core_data, &temp_cmd); } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } #if !IS_ENABLED(CONFIG_SAMSUNG_PRODUCT_SHIP) static void pocket_mode_state(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; struct goodix_ts_cmd temp_cmd; unsigned char status; int ret; sec_cmd_set_default_result(sec); if (core_data->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("IC is OFF"); goto fail; } temp_cmd.len = 5; temp_cmd.cmd = GOODIX_POKET_MODE_STATE_ADDR; temp_cmd.data[0] = 1; ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_info("fail to send_cmd : %d", ret); goto fail; } sec_delay(20); ret = core_data->hw_ops->read(core_data, core_data->ic_info.misc.cmd_addr, &status, 1); if (ret < 0) { ts_info("fail to read_cmd : %d", ret); goto fail; } if (status == 0x91) snprintf(buff, sizeof(buff), "enable"); else if (status == 0x90) snprintf(buff, sizeof(buff), "disable"); ts_info("pocket mode %s", buff); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; fail: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } static void ear_detect_state(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; struct goodix_ts_cmd temp_cmd; unsigned char status; int ret; sec_cmd_set_default_result(sec); if (core_data->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("IC is OFF"); goto fail; } temp_cmd.len = 5; temp_cmd.cmd = GOODIX_POKET_MODE_STATE_ADDR; temp_cmd.data[0] = 0; ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_info("fail to send_cmd : %d", ret); goto fail; } sec_delay(20); ret = core_data->hw_ops->read(core_data, core_data->ic_info.misc.cmd_addr, &status, 1); if (ret < 0) { ts_info("fail to read_cmd : %d", ret); goto fail; } if (status == 0x91) snprintf(buff, sizeof(buff), "enable"); else if (status == 0x90) snprintf(buff, sizeof(buff), "disable"); ts_info("ear detect mode %s", buff); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; fail: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } #endif static void set_game_mode(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_cmd temp_cmd; int ret; char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } ts_info("game mode : %s", sec->cmd_param[0] ? "on" : "off"); if (sec->cmd_param[0]) { //enable game mode temp_cmd.len = 5; temp_cmd.cmd = 0xC2; temp_cmd.data[0] = 1; } else { //disabled game mode temp_cmd.len = 5; temp_cmd.cmd = 0xC2; temp_cmd.data[0] = 0; } ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_err("send game mode cmd failed"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void glove_mode(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); int ret; char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } ts_info("glove mode : %s", sec->cmd_param[0] ? "on" : "off"); if (sec->cmd_param[0]) core_data->glove_enable = 1; else core_data->glove_enable = 0; ret = goodix_set_cmd(core_data, GOODIX_GLOVE_MODE_ADDR, core_data->glove_enable); if (ret < 0) { ts_err("send glove mode cmd failed"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void set_sip_mode(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_cmd temp_cmd; int ret; char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } ts_info("sip mode : %s", sec->cmd_param[0] ? "on" : "off"); if (sec->cmd_param[0]) { //enable sip mode temp_cmd.len = 5; temp_cmd.cmd = 0x73; temp_cmd.data[0] = 1; } else { //disabled sip mode temp_cmd.len = 5; temp_cmd.cmd = 0x73; temp_cmd.data[0] = 0; } ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_err("send sip mode cmd failed"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void set_note_mode(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_cmd temp_cmd; int ret; char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } ts_info("note mode : %s", sec->cmd_param[0] ? "on" : "off"); if (sec->cmd_param[0]) { //enable note mode temp_cmd.len = 5; temp_cmd.cmd = 0x74; temp_cmd.data[0] = 1; } else { //disabled note mode temp_cmd.len = 5; temp_cmd.cmd = 0x74; temp_cmd.data[0] = 0; } ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_err("send note mode cmd failed"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void spay_enable(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } if (sec->cmd_param[0]) ts->plat_data->lowpower_mode |= SEC_TS_MODE_SPONGE_SWIPE; else ts->plat_data->lowpower_mode &= ~SEC_TS_MODE_SPONGE_SWIPE; ts_info(" spay %s, %02X\n", sec->cmd_param[0] ? "on" : "off", ts->plat_data->lowpower_mode); goodix_set_custom_library(ts); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } int goodix_set_aod_rect(struct goodix_ts_core *ts) { u8 data[8] = {0, }; int ret = 0; int i; for (i = 0; i < 4; i++) { data[i * 2] = ts->plat_data->aod_data.rect_data[i] & 0xFF; data[i * 2 + 1] = (ts->plat_data->aod_data.rect_data[i] >> 8) & 0xFF; } ret = ts->hw_ops->write_to_sponge(ts, 0x02, data, 8); if (ret < 0) ts_info("Failed to write sponge\n"); return ret; } static void set_aod_rect(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret, i; sec_cmd_set_default_result(sec); if (ts->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("IC is OFF"); goto NG; } ts_info(" w:%d, h:%d, x:%d, y:%d, lowpower_mode:0x%02X\n", sec->cmd_param[0], sec->cmd_param[1], sec->cmd_param[2], sec->cmd_param[3], ts->plat_data->lowpower_mode); for (i = 0; i < 4; i++) ts->plat_data->aod_data.rect_data[i] = sec->cmd_param[i]; ret = goodix_set_aod_rect(ts); if (ret < 0) goto NG; snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; NG: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void get_aod_rect(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; u8 data[8] = {0, }; u16 rect_data[4] = {0, }; int ret, i; sec_cmd_set_default_result(sec); if (ts->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("IC is OFF"); goto NG; } ret = ts->hw_ops->read_from_sponge(ts, 0x02, data, 8); if (ret < 0) { ts_info("Failed to read rect\n"); goto NG; } for (i = 0; i < 4; i++) rect_data[i] = (data[i * 2 + 1] & 0xFF) << 8 | (data[i * 2] & 0xFF); ts_info("w:%d, h:%d, x:%d, y:%d\n", rect_data[0], rect_data[1], rect_data[2], rect_data[3]); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; NG: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void aod_enable(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } if (sec->cmd_param[0]) ts->plat_data->lowpower_mode |= SEC_TS_MODE_SPONGE_AOD; else ts->plat_data->lowpower_mode &= ~SEC_TS_MODE_SPONGE_AOD; ts_info("aod: %s, %02X\n", sec->cmd_param[0] ? "on" : "off", ts->plat_data->lowpower_mode); goodix_set_custom_library(ts); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void aot_enable(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } if (sec->cmd_param[0]) ts->plat_data->lowpower_mode |= SEC_TS_MODE_SPONGE_DOUBLETAP_TO_WAKEUP; else ts->plat_data->lowpower_mode &= ~SEC_TS_MODE_SPONGE_DOUBLETAP_TO_WAKEUP; ts_info("%s, %02X\n", sec->cmd_param[0] ? "on" : "off", ts->plat_data->lowpower_mode); goodix_set_custom_library(ts); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } void goodix_get_custom_library(struct goodix_ts_core *ts) { u8 data[6] = { 0 }; int ret, i; mutex_lock(&ts->modechange_mutex); ret = ts->hw_ops->read_from_sponge(ts, SEC_TS_CMD_SPONGE_AOD_ACTIVE_INFO, data, 6); if (ret < 0) { ts_err("Failed to read aod active area"); } for (i = 0; i < 3; i++) ts->plat_data->aod_data.active_area[i] = (data[i * 2 + 1] & 0xFF) << 8 | (data[i * 2] & 0xFF); ts_info("aod_active_area - top:%d, edge:%d, bottom:%d", ts->plat_data->aod_data.active_area[0], ts->plat_data->aod_data.active_area[1], ts->plat_data->aod_data.active_area[2]); memset(data, 0x00, 6); ret = ts->hw_ops->read_from_sponge(ts, SEC_TS_CMD_SPONGE_FOD_INFO, data, 4); if (ret < 0) { ts_err("Failed to read fod info"); } mutex_unlock(&ts->modechange_mutex); sec_input_set_fod_info(ts->bus->dev, data[0], data[1], data[2], data[3]); } int goodix_set_custom_library(struct goodix_ts_core *ts) { u8 data[1] = { 0 }; int ret; u8 force_fod_enable = 0; mutex_lock(&ts->modechange_mutex); #if IS_ENABLED(CONFIG_SEC_FACTORY) /* enable FOD when LCD on state */ if (ts->plat_data->support_fod && ts->plat_data->enabled) force_fod_enable = SEC_TS_MODE_SPONGE_PRESS; #endif ts_info("Sponge (0x%02x)%s\n", ts->plat_data->lowpower_mode, force_fod_enable ? ", force fod enable" : ""); if (ts->plat_data->prox_power_off) { data[0] = (ts->plat_data->lowpower_mode | force_fod_enable) & ~SEC_TS_MODE_SPONGE_DOUBLETAP_TO_WAKEUP; ts_info("prox off. disable AOT"); } else { data[0] = ts->plat_data->lowpower_mode | force_fod_enable; } ret = ts->hw_ops->write_to_sponge(ts, 0, data, 1); if (ret < 0) ts_err("Failed to write sponge"); mutex_unlock(&ts->modechange_mutex); return ret; } int goodix_set_press_property(struct goodix_ts_core *ts) { u8 data[1] = { 0 }; int ret; if (!ts->plat_data->support_fod) return 0; data[0] = ts->plat_data->fod_data.press_prop; ret = ts->hw_ops->write_to_sponge(ts, SEC_TS_CMD_SPONGE_PRESS_PROPERTY, data, 1); if (ret < 0) ts_err("Failed to write sponge\n"); ts_info("%d", ts->plat_data->fod_data.press_prop); return ret; } int goodix_set_fod_rect(struct goodix_ts_core *ts) { u8 data[8] = { 0 }; int ret, i; ts_info("l:%d, t:%d, r:%d, b:%d\n", ts->plat_data->fod_data.rect_data[0], ts->plat_data->fod_data.rect_data[1], ts->plat_data->fod_data.rect_data[2], ts->plat_data->fod_data.rect_data[3]); for (i = 0; i < 4; i++) { data[i * 2] = ts->plat_data->fod_data.rect_data[i] & 0xFF; data[i * 2 + 1] = (ts->plat_data->fod_data.rect_data[i] >> 8) & 0xFF; } ret = ts->hw_ops->write_to_sponge(ts, 0x4b, data, 8); if (ret < 0) ts_err("Failed to write sponge"); return ret; } static void fod_enable(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } else if (!ts->plat_data->support_fod) { snprintf(buff, sizeof(buff), "NA"); sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } if (sec->cmd_param[0]) ts->plat_data->lowpower_mode |= SEC_TS_MODE_SPONGE_PRESS; else ts->plat_data->lowpower_mode &= ~SEC_TS_MODE_SPONGE_PRESS; ts->plat_data->fod_data.press_prop = (sec->cmd_param[1] & 0x01) | ((sec->cmd_param[2] & 0x01) << 1); ts_info("%s, fast:%s, strict:%s, %02X\n", sec->cmd_param[0] ? "on" : "off", ts->plat_data->fod_data.press_prop & 1 ? "on" : "off", ts->plat_data->fod_data.press_prop & 2 ? "on" : "off", ts->plat_data->lowpower_mode); // mutex_lock(&ts->modechange); if (!ts->plat_data->enabled && !ts->plat_data->lowpower_mode && !ts->plat_data->pocket_mode && !ts->plat_data->ed_enable && !ts->plat_data->fod_lp_mode) { if (device_may_wakeup(ts->bus->dev) && ts->plat_data->power_state == SEC_INPUT_STATE_LPM) disable_irq_wake(ts->irq); ts->hw_ops->irq_enable(ts, false); goodix_ts_power_off(ts); } else { goodix_set_custom_library(ts); goodix_set_press_property(ts); } // mutex_unlock(&ts->modechange); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } static void fod_lp_mode(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); char buff[64] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } else if (!ts->plat_data->support_fod_lp_mode) { snprintf(buff, sizeof(buff), "NA"); sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } ts->plat_data->fod_lp_mode = sec->cmd_param[0]; ts_info("%d", ts->plat_data->fod_lp_mode); snprintf(buff, sizeof(buff), "%s", "OK"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_exit(sec); } static void set_fod_rect(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = 0; sec_cmd_set_default_result(sec); ts_info("l:%d, t:%d, r:%d, b:%d", sec->cmd_param[0], sec->cmd_param[1], sec->cmd_param[2], sec->cmd_param[3]); if (!ts->plat_data->support_fod) { snprintf(buff, sizeof(buff), "NA"); sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } if (!sec_input_set_fod_rect(ts->bus->dev, sec->cmd_param)) goto NG; if (ts->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("Touch is stopped! Set data at reinit()"); goto OK; } ret = goodix_set_fod_rect(ts); if (ret < 0) goto NG; OK: snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; NG: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void singletap_enable(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } if (sec->cmd_param[0]) ts->plat_data->lowpower_mode |= SEC_TS_MODE_SPONGE_SINGLE_TAP; else ts->plat_data->lowpower_mode &= ~SEC_TS_MODE_SPONGE_SINGLE_TAP; ts_info("singletap: %s, %02X\n", sec->cmd_param[0] ? "on" : "off", ts->plat_data->lowpower_mode); goodix_set_custom_library(ts); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void debug(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[16] = { 0 }; sec_cmd_set_default_result(sec); core_data->debug_flag = sec->cmd_param[0]; ts_info("%s: debug_flag is 0x%X\n", __func__, core_data->debug_flag); snprintf(buff, sizeof(buff), "OK"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_exit(sec); } static void check_connection(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; uint32_t esd_addr = core_data->ic_info.misc.esd_addr; uint8_t esd_val = 0xAA; int ret; sec_cmd_set_default_result(sec); if (core_data->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF || core_data->plat_data->power_state == SEC_INPUT_STATE_LPM) { ts_err("IC is not ready(%d)", core_data->plat_data->power_state); goto err_out; } ret = core_data->hw_ops->write(core_data, esd_addr, &esd_val, 1); if (ret < 0) { ts_err("write esd val failed"); goto err_out; } sec_delay(100); /* If the FW don't set esd_val to 0xFF, the FW has been crashed */ ret = core_data->hw_ops->read(core_data, esd_addr, &esd_val, 1); if (ret < 0) { ts_err("read esd val failed"); goto err_out; } if (esd_val == 0xAA) { ts_err("esd check failed"); goto err_out; } ts_info("check connection OK"); snprintf(buff, sizeof(buff), "OK"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_exit(sec); return; err_out: snprintf(buff, sizeof(buff), "NG"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_exit(sec); } // have change it #if 0 static void set_charger_mode(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_cmd temp_cmd; int ret; char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); switch (sec->cmd_param[0]) { case TYPE_WIRELESS_CHARGER_NONE: temp_cmd.cmd = 0xAF; temp_cmd.data[0] = 0; temp_cmd.len = 5; break; case TYPE_WIRELESS_CHARGER: temp_cmd.cmd = 0xAF; temp_cmd.data[0] = 1; temp_cmd.len = 5; break; default: ts_err("invalid param %d", sec->cmd_param[0]); goto NG; } ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_err("send charger cmd failed"); goto NG; } snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); NG: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } #endif static void set_grip_data(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; u8 mode = G_NONE; sec_cmd_set_default_result(sec); memset(buff, 0, sizeof(buff)); if (sec->cmd_param[0] == 0) { // edge handler if (sec->cmd_param[1] == 0) { // clear core_data->plat_data->grip_data.edgehandler_direction = 0; } else if (sec->cmd_param[1] < 3) { core_data->plat_data->grip_data.edgehandler_direction = sec->cmd_param[1]; core_data->plat_data->grip_data.edgehandler_start_y = sec->cmd_param[2]; core_data->plat_data->grip_data.edgehandler_end_y = sec->cmd_param[3]; } else { ts_err("cmd1 is abnormal, %d", sec->cmd_param[1]); goto err_grip_data; } mode = mode | G_SET_EDGE_HANDLER; core_data->plat_data->set_grip_data(core_data->bus->dev, mode); } else if (sec->cmd_param[0] == 1) { // portrait mode core_data->plat_data->grip_data.edge_range = sec->cmd_param[1]; core_data->plat_data->grip_data.deadzone_up_x = sec->cmd_param[2]; core_data->plat_data->grip_data.deadzone_dn_x = sec->cmd_param[3]; core_data->plat_data->grip_data.deadzone_y = sec->cmd_param[4]; mode = mode | G_SET_NORMAL_MODE; core_data->plat_data->set_grip_data(core_data->bus->dev, mode); } else if (sec->cmd_param[0] == 2) { // landscape mode if (sec->cmd_param[1] == 0) { // use previous portrait setting core_data->plat_data->grip_data.landscape_mode = 0; mode = mode | G_CLR_LANDSCAPE_MODE; } else if (sec->cmd_param[1] == 1) { core_data->plat_data->grip_data.landscape_mode = 1; core_data->plat_data->grip_data.landscape_edge = sec->cmd_param[2]; core_data->plat_data->grip_data.landscape_deadzone = sec->cmd_param[3]; core_data->plat_data->grip_data.landscape_top_deadzone = sec->cmd_param[4]; core_data->plat_data->grip_data.landscape_bottom_deadzone = sec->cmd_param[5]; core_data->plat_data->grip_data.landscape_top_gripzone = sec->cmd_param[6]; core_data->plat_data->grip_data.landscape_bottom_gripzone = sec->cmd_param[7]; mode = mode | G_SET_LANDSCAPE_MODE; } else { ts_err("cmd1 is abnormal, %d", sec->cmd_param[1]); goto err_grip_data; } core_data->plat_data->set_grip_data(core_data->bus->dev, mode); } else { ts_err("cmd0 is abnormal, %d", sec->cmd_param[0]); goto err_grip_data; } snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; err_grip_data: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } int goodix_set_cover_mode(struct goodix_ts_core *core_data) { struct goodix_ts_cmd temp_cmd; int ret = -1; if (core_data->plat_data->sense_off_when_cover_closed) { if (core_data->flip_enable) { ret = core_data->hw_ops->sense_off(core_data, 1); goodix_ts_release_all_finger(core_data); } else { ret = core_data->hw_ops->sense_off(core_data, 0); } } else { if (core_data->plat_data->cover_type >= 0) { temp_cmd.len = 6; temp_cmd.cmd = GOODIX_COVER_MODE_ADDR; temp_cmd.data[0] = core_data->flip_enable; temp_cmd.data[1] = sec_input_check_cover_type(core_data->bus->dev) & 0xFF; ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) ts_err("cover mode [%d/%d] failed", core_data->flip_enable, core_data->plat_data->cover_type); } else { ts_err("Abnormal cover type [%d]", core_data->plat_data->cover_type); } } return ret; } static void clear_cover_mode(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret = 0; sec_cmd_set_default_result(sec); #if IS_ENABLED(CONFIG_SEC_FACTORY) ts_info("skip for factory binary"); snprintf(buff, sizeof(buff), "OK"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_WAITING; sec_cmd_set_cmd_exit(sec); return; #endif if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 3) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; ts_err("not support param[%d/%d]", sec->cmd_param[0], sec->cmd_param[1]); goto exit; } if (sec->cmd_param[0] > 1) { /* cover closed */ core_data->flip_enable = true; } else { /* cover opened */ core_data->flip_enable = false; } core_data->plat_data->cover_type = sec->cmd_param[1]; if (core_data->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_info("tsp ic off, save data & set later"); goto out; } mutex_lock(&core_data->input_dev->mutex); ret = goodix_set_cover_mode(core_data); mutex_unlock(&core_data->input_dev->mutex); out: if (ret < 0) { ts_err("failed to set cover %s [%d]", core_data->flip_enable ? "close" : "open", core_data->plat_data->cover_type); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { ts_info("set cover %s [%d] success", core_data->flip_enable ? "close" : "open", core_data->plat_data->cover_type); snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; } exit: sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } int set_refresh_rate_mode(struct goodix_ts_core *core_data) { struct goodix_ts_cmd temp_cmd; int ret; temp_cmd.len = 5; temp_cmd.cmd = 0x9D; temp_cmd.data[0] = core_data->refresh_rate; ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) ts_err("failed to set scan rate[%d]", core_data->refresh_rate); else ts_info("set scan rate[%d] success", core_data->refresh_rate); return ret; } /* refresh_rate_mode byte[0]: 60 / 90 | 120 * 0 : normal (60hz) * 1 : adaptive * 2 : always (90Hz/120hz) */ static void refresh_rate_mode(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); char buff[SEC_CMD_STR_LEN] = { 0 }; int ret; sec_cmd_set_default_result(sec); if (!core_data->refresh_rate_enable) { ts_err("not support cmd(%d)!", sec->cmd_param[0]); goto NG; } if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 2) { ts_err("not support param[%d]", sec->cmd_param[0]); goto NG; } core_data->refresh_rate = sec->cmd_param[0]; ret = set_refresh_rate_mode(core_data); if (ret < 0) goto NG; snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; NG: snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } /* * 0x00 express not report from 0.5mm to edge. (dead_zone_enable,1) * 0x01 express report from 0.5mm to edge. (dead_zone_enable,0) * */ static void dead_zone_enable(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_cmd temp_cmd; int ret; char buff[SEC_CMD_STR_LEN] = { 0 }; sec_cmd_set_default_result(sec); if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); return; } ts_info("report edge : %s", sec->cmd_param[0] ? "off" : "on"); temp_cmd.len = 5; temp_cmd.cmd = 0x75; if (sec->cmd_param[0] == 0) temp_cmd.data[0] = 1; else temp_cmd.data[0] = 0; ret = core_data->hw_ops->send_cmd(core_data, &temp_cmd); if (ret < 0) { ts_err("send report edge cmd failed"); snprintf(buff, sizeof(buff), "NG"); sec->cmd_state = SEC_CMD_STATUS_FAIL; } else { snprintf(buff, sizeof(buff), "OK"); sec->cmd_state = SEC_CMD_STATUS_OK; } sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec_cmd_set_cmd_exit(sec); } static void not_support_cmd(void *device_data) { struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; char buff[16] = { 0 }; sec_cmd_set_default_result(sec); snprintf(buff, sizeof(buff), "%s", "NA"); sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; sec_cmd_set_cmd_exit(sec); ts_info("%s", buff); } static struct sec_cmd sec_cmds[] = { {SEC_CMD("fw_update", fw_update),}, {SEC_CMD("get_fw_ver_bin", get_fw_ver_bin),}, {SEC_CMD("get_fw_ver_ic", get_fw_ver_ic),}, {SEC_CMD("get_config_ver", get_config_ver),}, {SEC_CMD("module_off_master", module_off_master),}, {SEC_CMD("module_on_master", module_on_master),}, {SEC_CMD("get_chip_vendor", get_chip_vendor),}, {SEC_CMD("get_chip_name", get_chip_name),}, {SEC_CMD("get_x_num", get_x_num),}, {SEC_CMD("get_y_num", get_y_num),}, {SEC_CMD("set_factory_level", set_factory_level),}, // {SEC_CMD("get_checksum_data", get_checksum_data),}, // {SEC_CMD("get_crc_check", check_fw_corruption),}, {SEC_CMD("run_cs_raw_read_all", run_cs_raw_read_all),}, {SEC_CMD("run_cs_delta_read_all", run_cs_delta_read_all),}, {SEC_CMD("run_rawdata_read", run_rawdata_read),}, {SEC_CMD("run_rawdata_read_all", run_rawdata_read_all),}, {SEC_CMD("get_gap_data_all", get_gap_data_all),}, {SEC_CMD("run_high_frequency_rawdata_read", run_high_frequency_rawdata_read),}, {SEC_CMD("run_high_frequency_rawdata_read_all", run_high_frequency_rawdata_read_all),}, {SEC_CMD("get_high_frequency_gap_data_all", get_high_frequency_gap_data_all),}, {SEC_CMD("run_low_frequency_rawdata_read", run_low_frequency_rawdata_read),}, {SEC_CMD("run_low_frequency_rawdata_read_all", run_low_frequency_rawdata_read_all),}, {SEC_CMD("get_low_frequency_gap_data_all", get_low_frequency_gap_data_all),}, {SEC_CMD("run_diffdata_read", run_diffdata_read),}, {SEC_CMD("run_diffdata_read_all", run_diffdata_read_all),}, {SEC_CMD("run_self_rawdata_tx_read", run_self_rawdata_tx_read),}, {SEC_CMD("run_self_rawdata_tx_read_all", run_self_rawdata_tx_read_all),}, {SEC_CMD("run_self_rawdata_rx_read", run_self_rawdata_rx_read),}, {SEC_CMD("run_self_rawdata_rx_read_all", run_self_rawdata_rx_read_all),}, {SEC_CMD("run_self_diffdata_read", run_self_diffdata_read),}, {SEC_CMD("run_self_diffdata_read_all", run_self_diffdata_read_all),}, {SEC_CMD("run_trx_short_test", run_trx_short_test),}, {SEC_CMD("run_jitter_test", run_jitter_test),}, {SEC_CMD("run_jitter_delta_test", run_jitter_delta_test),}, {SEC_CMD_H("glove_mode", glove_mode),}, {SEC_CMD_H("clear_cover_mode", clear_cover_mode),}, // {SEC_CMD("set_wirelesscharger_mode", set_wirelesscharger_mode),}, {SEC_CMD("set_grip_data", set_grip_data),}, {SEC_CMD_H("refresh_rate_mode", refresh_rate_mode),}, {SEC_CMD("dead_zone_enable", dead_zone_enable),}, {SEC_CMD_H("spay_enable", spay_enable),}, {SEC_CMD_H("aod_enable", aod_enable),}, {SEC_CMD("set_aod_rect", set_aod_rect),}, {SEC_CMD("get_aod_rect", get_aod_rect),}, {SEC_CMD_H("aot_enable", aot_enable),}, {SEC_CMD_H("fod_lp_mode", fod_lp_mode),}, {SEC_CMD_H("fod_enable", fod_enable),}, {SEC_CMD("set_fod_rect", set_fod_rect),}, {SEC_CMD_H("singletap_enable", singletap_enable),}, // {SEC_CMD_H("external_noise_mode", external_noise_mode),}, // {SEC_CMD_H("set_touchable_area", set_touchable_area),}, {SEC_CMD("increase_disassemble_count", increase_disassemble_count),}, {SEC_CMD("get_disassemble_count", get_disassemble_count),}, {SEC_CMD("factory_cmd_result_all", factory_cmd_result_all),}, {SEC_CMD("factory_cmd_result_all_imagetest", factory_cmd_result_all_imagetest),}, // {SEC_CMD_H("fix_active_mode", fix_active_mode),}, // {SEC_CMD_H("touch_aging_mode", touch_aging_mode),}, {SEC_CMD("run_snr_non_touched", run_snr_non_touched),}, {SEC_CMD("run_snr_touched", run_snr_touched),}, {SEC_CMD("run_sram_test", run_sram_test),}, {SEC_CMD("set_sip_mode", set_sip_mode),}, {SEC_CMD_H("set_note_mode", set_note_mode),}, {SEC_CMD_H("set_game_mode", set_game_mode),}, {SEC_CMD_H("pocket_mode_enable", pocket_mode_enable),}, {SEC_CMD_H("ear_detect_enable", ear_detect_enable),}, {SEC_CMD("run_prox_intensity_read_all", run_prox_intensity_read_all),}, #if !IS_ENABLED(CONFIG_SAMSUNG_PRODUCT_SHIP) {SEC_CMD_H("pocket_mode_state", pocket_mode_state),}, {SEC_CMD_H("ear_detect_state", ear_detect_state),}, #endif {SEC_CMD("check_connection", check_connection),}, {SEC_CMD("debug", debug),}, {SEC_CMD("not_support_cmd", not_support_cmd),}, }; static ssize_t prox_power_off_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); ts_info("%s: %d\n", __func__, core_data->plat_data->prox_power_off); return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", core_data->plat_data->prox_power_off); } static ssize_t prox_power_off_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); long data; int ret; ret = kstrtol(buf, 10, &data); if (ret < 0) return ret; ts_info("%s: %ld", __func__, data); core_data->plat_data->prox_power_off = data; return count; } /** virtual_prox **/ static ssize_t protos_event_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); ts_info("hover = %d", core_data->ts_event.hover_event); return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", core_data->ts_event.hover_event != 3 ? 0 : 3); } static ssize_t protos_event_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *core_data = container_of(sec, struct goodix_ts_core, sec); u8 data; int ret; ret = kstrtou8(buf, 10, &data); if (ret < 0) return ret; ts_info("read parm = %d", data); if (data != 0 && data != 1) { ts_err("incorrect parm data(%d)", data); return -EINVAL; } if (core_data->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("IC is OFF"); return count; } core_data->plat_data->ed_enable = data; ts_info("ear detect mode(%d)", core_data->plat_data->ed_enable); ret = core_data->hw_ops->ed_enable(core_data, core_data->plat_data->ed_enable); if (ret < 0) ts_err("send ear detect cmd failed"); return count; } #define SENSITIVITY_POINT_CNT 9 static ssize_t sensitivity_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *cd = container_of(sec, struct goodix_ts_core, sec); int value[SENSITIVITY_POINT_CNT]; char result[SENSITIVITY_POINT_CNT * 10] = { 0 }; char tempv[10] = {0}; char buff[40] = {0}; int ret, i; int retry = 20; unsigned int sen_addr = cd->production_test_addr; if (cd->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("power off in IC"); return 0; } while (retry--) { ret = cd->hw_ops->read(cd, sen_addr, buff, 2); if (ret < 0) { ts_err("read sensitivity status failed"); return ret; } if (buff[0] == 0xAA && buff[1] == 0xAA) break; ts_err("retry(%d), buff[0] = 0x%x,buff[1] = 0x%x\n", retry, buff[0], buff[1]); sec_delay(5); } if (retry < 0) { ts_err("sensitivity is not ready, status:0x%x, 0x%x", buff[0], buff[1]); return -EINVAL; } ret = cd->hw_ops->read(cd, sen_addr, buff, sizeof(buff)); if (ret < 0) { ts_err("read sensitivity data failed"); return ret; } if (checksum_cmp(buff + 2, sizeof(buff) - 2, CHECKSUM_MODE_U8_LE)) { ts_err("sensitivity data checksum error"); ts_err("data:%*ph", (int)sizeof(buff) - 2, buff + 2); return -EINVAL; } memset(buff, 0, 2); cd->hw_ops->write(cd, sen_addr, buff, 2); for (i = 0 ; i < SENSITIVITY_POINT_CNT ; i++) { value[i] = (s16)le16_to_cpup((__le16 *)&buff[i * 4 + 2]); if (i != 0) strlcat(result, ",", sizeof(result)); snprintf(tempv, 10, "%d", value[i]); strlcat(result, tempv, sizeof(result)); } ts_info("sensitivity mode : %s", result); return snprintf(buf, PAGE_SIZE, "%s", result); } static ssize_t sensitivity_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *cd = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_cmd temp_cmd; unsigned long value = 0; int ret = 0; unsigned char sen_cmd = cd->sensitive_cmd; if (cd->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("power off in IC"); return 0; } if (count > 2) return -EINVAL; ret = kstrtoul(buf, 10, &value); if (ret != 0) return ret; if (value == 1) { temp_cmd.len = 5; temp_cmd.cmd = sen_cmd; temp_cmd.data[0] = 1; ret = cd->hw_ops->send_cmd(cd, &temp_cmd); if (ret < 0) { ts_err("send sensitivity mode on fail!"); return ret; } goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); ts_info("enable end"); } else { temp_cmd.len = 5; temp_cmd.cmd = sen_cmd; temp_cmd.data[0] = 0; ret = cd->hw_ops->send_cmd(cd, &temp_cmd); if (ret < 0) { ts_err("send sensitivity mode off fail!"); return ret; } goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); ts_info("disable end"); } ts_info("set sensitivity mode Done(%ld)\n", value); return count; } static ssize_t single_driving_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *cd = container_of(sec, struct goodix_ts_core, sec); struct goodix_ts_cmd temp_cmd; unsigned long value = 0; int ret = 0; if (cd->bus->ic_type != IC_TYPE_BERLIN_D) { ts_info("only support GT9895"); return 0; } if (cd->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("power off in IC"); return 0; } ret = kstrtoul(buf, 10, &value); if (ret != 0) return ret; if (value == 1) { temp_cmd.len = 5; temp_cmd.cmd = 0x67; temp_cmd.data[0] = 1; ret = cd->hw_ops->send_cmd(cd, &temp_cmd); if (ret < 0) { ts_err("enable single driving mode fail!"); return ret; } ts_info("enable end"); } else { goodix_ts_release_all_finger(cd); cd->hw_ops->reset(cd, 100); ts_info("disable end"); } ts_info("single driving mode(%ld)", value); return count; } /* * read_support_feature function * returns the bit combination of specific feature that is supported. */ static ssize_t support_feature_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); u32 feature = 0; if (ts->plat_data->enable_settings_aot) feature |= INPUT_FEATURE_ENABLE_SETTINGS_AOT; if (ts->plat_data->sync_reportrate_120) feature |= INPUT_FEATURE_ENABLE_SYNC_RR120; if (ts->plat_data->support_vrr) feature |= INPUT_FEATURE_ENABLE_VRR; if (ts->plat_data->support_open_short_test) feature |= INPUT_FEATURE_SUPPORT_OPEN_SHORT_TEST; if (ts->plat_data->support_mis_calibration_test) feature |= INPUT_FEATURE_SUPPORT_MIS_CALIBRATION_TEST; if (ts->plat_data->support_wireless_tx) feature |= INPUT_FEATURE_SUPPORT_WIRELESS_TX; if (ts->plat_data->support_input_monitor) feature |= INPUT_FEATURE_SUPPORT_INPUT_MONITOR; if (ts->plat_data->enable_sysinput_enabled) feature |= INPUT_FEATURE_ENABLE_SYSINPUT_ENABLED; ts_info("%d%s%s%s%s%s%s%s%s%s", feature, feature & INPUT_FEATURE_ENABLE_SETTINGS_AOT ? " aot" : "", feature & INPUT_FEATURE_ENABLE_PRESSURE ? " pressure" : "", feature & INPUT_FEATURE_ENABLE_SYNC_RR120 ? " RR120hz" : "", feature & INPUT_FEATURE_ENABLE_VRR ? " vrr" : "", feature & INPUT_FEATURE_SUPPORT_OPEN_SHORT_TEST ? " openshort" : "", feature & INPUT_FEATURE_SUPPORT_MIS_CALIBRATION_TEST ? " miscal" : "", feature & INPUT_FEATURE_SUPPORT_WIRELESS_TX ? " wirelesstx" : "", feature & INPUT_FEATURE_SUPPORT_INPUT_MONITOR ? " inputmonitor" : "", feature & INPUT_FEATURE_ENABLE_SYSINPUT_ENABLED ? " SE" : ""); return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", feature); } static ssize_t scrub_pos_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *cd = container_of(sec, struct goodix_ts_core, sec); char buff[256] = { 0 }; #if IS_ENABLED(CONFIG_SAMSUNG_PRODUCT_SHIP) ts_info("id: %d", cd->plat_data->gesture_id); #else ts_info("id: %d, X:%d, Y:%d", cd->plat_data->gesture_id, cd->plat_data->gesture_x, cd->plat_data->gesture_y); #endif snprintf(buff, sizeof(buff), "%d %d %d", cd->plat_data->gesture_id, cd->plat_data->gesture_x, cd->plat_data->gesture_y); cd->plat_data->gesture_x = 0; cd->plat_data->gesture_y = 0; return snprintf(buf, PAGE_SIZE, "%s", buff); } static ssize_t goodix_fod_position_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); u8 data[255] = { 0 }; char buff[3] = { 0 }; int i, ret; if (!ts->plat_data->support_fod) { ts_err("fod is not supported"); return snprintf(buf, SEC_CMD_BUF_SIZE, "NG"); } if (!ts->plat_data->fod_data.vi_size) { ts_err("not read fod_info yet"); return snprintf(buf, SEC_CMD_BUF_SIZE, "NG"); } ret = ts->hw_ops->read_from_sponge(ts, SEC_TS_CMD_SPONGE_FOD_POSITION, data, ts->plat_data->fod_data.vi_size); if (ret < 0) { ts_err("Failed to read"); return snprintf(buf, SEC_CMD_BUF_SIZE, "NG"); } for (i = 0; i < ts->plat_data->fod_data.vi_size; i++) { snprintf(buff, 3, "%02X", data[i]); strlcat(buf, buff, SEC_CMD_BUF_SIZE); } return strlen(buf); } static ssize_t goodix_fod_info_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); return sec_input_get_fod_info(ts->bus->dev, buf); } static ssize_t get_lp_dump(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *cd = container_of(sec, struct goodix_ts_core, sec); u8 string_data[10] = {0, }; u16 current_index; u16 dump_start, dump_end, dump_cnt; int i, ret, dump_area, dump_gain; unsigned char *sec_spg_dat; if (cd->plat_data->power_state == SEC_INPUT_STATE_POWER_OFF) { ts_err("Touch is stopped!"); return snprintf(buf, SEC_CMD_BUF_SIZE, "TSP turned off"); } /* preparing dump buffer */ sec_spg_dat = vzalloc(SEC_TS_MAX_SPONGE_DUMP_BUFFER); if (!sec_spg_dat) return snprintf(buf, SEC_CMD_BUF_SIZE, "vmalloc failed"); cd->hw_ops->irq_enable(cd, false); ret = cd->hw_ops->read_from_sponge(cd, SEC_TS_CMD_SPONGE_LP_DUMP_CUR_IDX, string_data, 2); if (ret < 0) { ts_err("Failed to read lp dump cur idx"); snprintf(buf, SEC_CMD_BUF_SIZE, "NG, Failed to read lp dump cur idx"); goto out; } if (cd->sponge_inf_dump) dump_gain = 2; else dump_gain = 1; current_index = (string_data[1] & 0xFF) << 8 | (string_data[0] & 0xFF); dump_start = SEC_TS_CMD_SPONGE_LP_DUMP_EVENT; dump_end = dump_start + (cd->sponge_dump_format * ((cd->sponge_dump_event * dump_gain) - 1)); if (current_index > dump_end || current_index < dump_start) { ts_err("Failed to Sponge LP log %d", current_index); snprintf(buf, SEC_CMD_BUF_SIZE, "NG, Failed to Sponge LP log, current_index=%d", current_index); goto out; } /* legacy get_lp_dump */ ts_info("DEBUG format=%d, num=%d, start=%d, end=%d, current_index=%d", cd->sponge_dump_format, cd->sponge_dump_event, dump_start, dump_end, current_index); for (i = (cd->sponge_dump_event * dump_gain) - 1 ; i >= 0 ; i--) { u16 data0, data1, data2, data3, data4; char buff[30] = {0, }; u16 string_addr; if (current_index < (cd->sponge_dump_format * i)) string_addr = (cd->sponge_dump_format * cd->sponge_dump_event * dump_gain) + current_index - (cd->sponge_dump_format * i); else string_addr = current_index - (cd->sponge_dump_format * i); if (string_addr < dump_start) string_addr += (cd->sponge_dump_format * cd->sponge_dump_event * dump_gain); ret = cd->hw_ops->read_from_sponge(cd, string_addr, string_data, cd->sponge_dump_format); if (ret < 0) { ts_err("Failed to read sponge"); snprintf(buf, SEC_CMD_BUF_SIZE, "NG, Failed to read sponge, addr=%d", string_addr); goto out; } data0 = (string_data[1] & 0xFF) << 8 | (string_data[0] & 0xFF); data1 = (string_data[3] & 0xFF) << 8 | (string_data[2] & 0xFF); data2 = (string_data[5] & 0xFF) << 8 | (string_data[4] & 0xFF); data3 = (string_data[7] & 0xFF) << 8 | (string_data[6] & 0xFF); data4 = (string_data[9] & 0xFF) << 8 | (string_data[8] & 0xFF); if (data0 || data1 || data2 || data3 || data4) { if (cd->sponge_dump_format == 10) { snprintf(buff, sizeof(buff), "%d: %04x%04x%04x%04x%04x\n", string_addr, data0, data1, data2, data3, data4); } else { snprintf(buff, sizeof(buff), "%d: %04x%04x%04x%04x\n", string_addr, data0, data1, data2, data3); } strlcat(buf, buff, PAGE_SIZE); } } if (cd->sponge_inf_dump) { u16 addr = 0; u8 clear_data = 1; if (current_index >= cd->sponge_dump_border) { dump_cnt = ((current_index - (cd->sponge_dump_border)) / cd->sponge_dump_format) + 1; dump_area = 1; addr = cd->sponge_dump_border; } else { dump_cnt = ((current_index - SEC_TS_CMD_SPONGE_LP_DUMP_EVENT) / cd->sponge_dump_format) + 1; dump_area = 0; addr = SEC_TS_CMD_SPONGE_LP_DUMP_EVENT; } ret = cd->hw_ops->read_from_sponge(cd, addr, sec_spg_dat, dump_cnt * cd->sponge_dump_format); if (ret < 0) { ts_err("Failed to read sponge"); goto out; } for (i = 0 ; i <= dump_cnt ; i++) { int e_offset = i * cd->sponge_dump_format; char ibuff[30] = {0, }; u16 edata[5]; edata[0] = (sec_spg_dat[1 + e_offset] & 0xFF) << 8 | (sec_spg_dat[0 + e_offset] & 0xFF); edata[1] = (sec_spg_dat[3 + e_offset] & 0xFF) << 8 | (sec_spg_dat[2 + e_offset] & 0xFF); edata[2] = (sec_spg_dat[5 + e_offset] & 0xFF) << 8 | (sec_spg_dat[4 + e_offset] & 0xFF); edata[3] = (sec_spg_dat[7 + e_offset] & 0xFF) << 8 | (sec_spg_dat[6 + e_offset] & 0xFF); edata[4] = (sec_spg_dat[9 + e_offset] & 0xFF) << 8 | (sec_spg_dat[8 + e_offset] & 0xFF); if (edata[0] || edata[1] || edata[2] || edata[3] || edata[4]) { snprintf(ibuff, sizeof(ibuff), "%03d: %04x%04x%04x%04x%04x\n", i + (cd->sponge_dump_event * dump_area), edata[0], edata[1], edata[2], edata[3], edata[4]); #if IS_ENABLED(CONFIG_SEC_DEBUG_TSP_LOG) sec_tsp_sponge_log(ibuff); #endif } } cd->sponge_dump_delayed_flag = false; ret = cd->hw_ops->write_to_sponge(cd, SEC_TS_CMD_SPONGE_DUMP_FLUSH, &clear_data, 1); if (ret < 0) ts_err("Failed to clear sponge dump"); } out: vfree(sec_spg_dat); cd->hw_ops->irq_enable(cd, true); return strlen(buf); } static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); if (!ts->plat_data->enable_sysinput_enabled) return -EINVAL; ts_info("%d", ts->plat_data->enabled); return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->plat_data->enabled); } static ssize_t enabled_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sec_cmd_data *sec = dev_get_drvdata(dev); struct goodix_ts_core *ts = container_of(sec, struct goodix_ts_core, sec); #if 0 struct input_dev *input_dev = ts->plat_data->input_dev; #endif int buff[2]; int ret; if (!ts->plat_data->enable_sysinput_enabled) return -EINVAL; ret = sscanf(buf, "%d,%d", &buff[0], &buff[1]); if (ret != 2) { ts_err("failed read params [%d]\n", ret); return -EINVAL; } #if 0 if (buff[0] == DISPLAY_STATE_ON && buff[1] == DISPLAY_EVENT_LATE) { if (ts->plat_data->enabled) { ts_info("device already enabled\n"); goto out; } ret = sec_input_enable_device(input_dev); } else if (buff[0] == DISPLAY_STATE_OFF && buff[1] == DISPLAY_EVENT_EARLY) { if (!ts->plat_data->enabled) { ts_info("device already disabled\n"); goto out; } ret = sec_input_disable_device(input_dev); } else if (buff[0] == DISPLAY_STATE_FORCE_ON) { if (ts->plat_data->enabled) { ts_info("device already enabled\n"); goto out; } ret = sec_input_enable_device(input_dev); ts_info("DISPLAY_STATE_FORCE_ON(%d)\n", ret); } else if (buff[0] == DISPLAY_STATE_FORCE_OFF) { if (!ts->plat_data->enabled) { ts_info("device already disabled\n"); goto out; } ret = sec_input_disable_device(input_dev); ts_info("DISPLAY_STATE_FORCE_OFF(%d)\n", ret); } #endif if (ret) return ret; out: return count; } static DEVICE_ATTR(prox_power_off, 0664, prox_power_off_show, prox_power_off_store); static DEVICE_ATTR(virtual_prox, 0664, protos_event_show, protos_event_store); static DEVICE_ATTR(sensitivity_mode, 0664, sensitivity_mode_show, sensitivity_mode_store); static DEVICE_ATTR(single_driving, 0220, NULL, single_driving_store); static DEVICE_ATTR(support_feature, 0444, support_feature_show, NULL); static DEVICE_ATTR(scrub_pos, 0444, scrub_pos_show, NULL); static DEVICE_ATTR(fod_pos, 0444, goodix_fod_position_show, NULL); static DEVICE_ATTR(fod_info, 0444, goodix_fod_info_show, NULL); static DEVICE_ATTR(get_lp_dump, 0444, get_lp_dump, NULL); static DEVICE_ATTR(enabled, 0664, enabled_show, enabled_store); static struct attribute *cmd_attributes[] = { &dev_attr_scrub_pos.attr, // &dev_attr_hw_param.attr, &dev_attr_sensitivity_mode.attr, &dev_attr_single_driving.attr, &dev_attr_support_feature.attr, &dev_attr_get_lp_dump.attr, &dev_attr_prox_power_off.attr, &dev_attr_virtual_prox.attr, &dev_attr_fod_pos.attr, &dev_attr_fod_info.attr, // &dev_attr_aod_active_area.attr, &dev_attr_enabled.attr, NULL, }; static struct attribute_group cmd_attr_group = { .attrs = cmd_attributes, }; int goodix_ts_cmd_init(struct goodix_ts_core *ts) { int retval = 0; retval = sec_cmd_init(&ts->sec, sec_cmds, ARRAY_SIZE(sec_cmds), SEC_CLASS_DEVT_TSP); if (retval < 0) { ts_err("Failed to sec_cmd_init"); goto exit; } retval = sysfs_create_group(&ts->sec.fac_dev->kobj, &cmd_attr_group); if (retval < 0) { ts_err("Failed to create sysfs attributes"); sec_cmd_exit(&ts->sec, SEC_CLASS_DEVT_TSP); goto exit; } retval = sysfs_create_link(&ts->sec.fac_dev->kobj, &ts->input_dev->dev.kobj, "input"); if (retval < 0) { ts_err("Failed to create input symbolic link"); sysfs_remove_group(&ts->sec.fac_dev->kobj, &cmd_attr_group); sec_cmd_exit(&ts->sec, SEC_CLASS_DEVT_TSP); goto exit; } retval = sec_input_sysfs_create(&ts->plat_data->input_dev->dev.kobj); if (retval < 0) { ts_err("Failed to create sec_input_sysfs attributes"); sysfs_remove_link(&ts->sec.fac_dev->kobj, "input"); sysfs_remove_group(&ts->sec.fac_dev->kobj, &cmd_attr_group); sec_cmd_exit(&ts->sec, SEC_CLASS_DEVT_TSP); goto exit; } return 0; exit: return retval; } void goodix_ts_cmd_remove(struct goodix_ts_core *ts) { ts_info("called"); sec_input_sysfs_remove(&ts->plat_data->input_dev->dev.kobj); sysfs_remove_link(&ts->sec.fac_dev->kobj, "input"); sysfs_remove_group(&ts->sec.fac_dev->kobj, &cmd_attr_group); sec_cmd_exit(&ts->sec, SEC_CLASS_DEVT_TSP); }