// SPDX-License-Identifier: GPL-2.0-or-later #define LOG_TAG "Test" #include "cts_config.h" #include "cts_platform.h" #include "cts_core.h" #include "cts_strerror.h" #include "cts_test.h" #include "cts_tcs.h" #ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST /* for ksys_mkdir/sys_mkdir */ #include #include #endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ const char *cts_test_item_str(int test_item) { #define case_test_item(item) \ case CTS_TEST_ ## item: return #item "-TEST" switch (test_item) { case_test_item(RESET_PIN); case_test_item(INT_PIN); case_test_item(RAWDATA); case_test_item(NOISE); case_test_item(OPEN); case_test_item(SHORT); case_test_item(COMPENSATE_CAP); default: return "INVALID"; } #undef case_test_item } #define CTS_TEST_SHORT (0x01) #define CTS_TEST_OPEN (0x02) #define CTS_SHORT_TEST_UNDEFINED (0x00) #define CTS_SHORT_TEST_BETWEEN_COLS (0x01) #define CTS_SHORT_TEST_BETWEEN_ROWS (0x02) #define CTS_SHORT_TEST_BETWEEN_GND (0x03) #define SHORT_COLS_TEST_LOOP (1) #define SHORT_ROWS_TEST_LOOP (3) #define RAWDATA_BUFFER_SIZE(cts_dev) \ (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) struct cts_fw_short_test_param { u8 type; u32 col_pattern[2]; u32 row_pattern[2]; }; int cts_write_file(struct file *filp, const void *data, size_t size) { #ifdef CFG_CTS_FOR_GKI cts_info("%s(): kernel_write is forbiddon with GKI Version!", __func__); return -EPERM; #else loff_t pos; ssize_t ret; pos = filp->f_pos; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) ret = kernel_write(filp, data, size, &pos); #else ret = kernel_write(filp, data, size, pos); #endif if (ret >= 0) { filp->f_pos += ret; } return ret; #endif } #ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST /* copied from fs/namei.c */ long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) { struct dentry *dentry; struct path path; int error; unsigned int lookup_flags = LOOKUP_DIRECTORY; retry: dentry = user_path_create(dfd, pathname, &path, lookup_flags); if (IS_ERR(dentry)) return PTR_ERR(dentry); if (!IS_POSIXACL(path.dentry->d_inode)) mode &= ~current_umask(); error = security_path_mkdir(&path, dentry, mode); if (!error) error = vfs_mkdir(path.dentry->d_inode, dentry, mode); done_path_create(&path, dentry); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } return error; } /* Make directory for filepath * If filepath = "/A/B/C/D.file", it will make dir /A/B/C recursive * like userspace mkdir -p */ int cts_mkdir_for_file(const char *filepath, umode_t mode) { #ifdef CFG_CTS_FOR_GKI cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); return -EPERM; #else char *dirname = NULL; int dirname_len; char *s; int ret; mm_segment_t fs; if (filepath == NULL) { cts_err("Create dir for file with filepath = NULL"); return -EINVAL; } if (filepath[0] == '\0' || filepath[0] != '/') { cts_err("Create dir for file with invalid filepath[0]: %c", filepath[0]); return -EINVAL; } dirname_len = strrchr(filepath, '/') - filepath; if (dirname_len == 0) { cts_warn("Create dir for file '%s' in root dir", filepath); return 0; } dirname = kstrndup(filepath, dirname_len, GFP_KERNEL); if (dirname == NULL) { cts_err("Create dir alloc mem for dirname failed"); return -ENOMEM; } cts_info("Create dir '%s' for file '%s'", dirname, filepath); fs = get_fs(); set_fs(KERNEL_DS); s = dirname + 1; /* Skip leading '/' */ while (1) { char c = '\0'; /* Bypass leading non-'/'s and then subsequent '/'s */ while (*s) { if (*s == '/') { do { ++s; } while (*s == '/'); c = *s; /* Save current char */ *s = '\0'; /* and replace it with nul */ break; } ++s; } cts_dbg(" - Create dir '%s'", dirname); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) ret = ksys_mkdir(dirname, 0777); #else ret = sys_mkdir(dirname, 0777); #endif if (ret < 0 && ret != -EEXIST) { cts_info("Create dir '%s' failed %d(%s)", dirname, ret, cts_strerror(ret)); /* Remove any inserted nul from the path */ *s = c; break; } /* Reset ret to 0 if return -EEXIST */ ret = 0; if (c) { /* Remove any inserted nul from the path */ *s = c; } else { break; } } set_fs(fs); if (dirname) { kfree(dirname); } return ret; #endif } #endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ static struct file *cts_test_data_filp; int cts_start_dump_test_data_to_file(const char *filepath, bool append_to_file) { int ret; #define START_BANNER \ ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" cts_info("Start dump test data to file '%s'", filepath); #ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST ret = cts_mkdir_for_file(filepath, 0777); if (ret) { cts_err("Create dir for test data file failed %d", ret); return ret; } #endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ #ifdef CFG_CTS_FOR_GKI cts_info("%s(): filp_open is forbiddon with GKI Version!", __func__); ret = -EPERM; return ret; #else cts_test_data_filp = filp_open(filepath, O_WRONLY | O_CREAT | (append_to_file ? O_APPEND : O_TRUNC), S_IRUGO | S_IWUGO); if (IS_ERR(cts_test_data_filp)) { ret = PTR_ERR(cts_test_data_filp); cts_test_data_filp = NULL; cts_err("Open file '%p' for test data failed %d", cts_test_data_filp, ret); return ret; } cts_write_file(cts_test_data_filp, START_BANNER, strlen(START_BANNER)); return 0; #endif #undef START_BANNER } void cts_stop_dump_test_data_to_file(void) { #define END_BANNER \ "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" #ifndef CFG_CTS_FOR_GKI int r; #endif cts_info("Stop dump test data to file"); if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, END_BANNER, strlen(END_BANNER)); #ifndef CFG_CTS_FOR_GKI r = filp_close(cts_test_data_filp, NULL); if (r) { cts_err("Close test data file failed %d", r); } #endif cts_test_data_filp = NULL; } else { cts_warn("Stop dump tsdata to file with filp = NULL"); } #undef END_BANNER } static void cts_dump_tsdata(struct cts_device *cts_dev, const char *desc, const u16 *data, bool to_console) { #define SPLIT_LINE_STR \ "--------------------------------------------------------"\ "--------------------------------------------------------" #define ROW_NUM_FORMAT_STR "%2d | " #define COL_NUM_FORMAT_STR "%-5u " #define DATA_FORMAT_STR "%-5u " int r, c; u32 max, min, sum, average; int max_r, max_c, min_r, min_c; char line_buf[512]; int count = 0; max = min = data[0]; sum = 0; max_r = max_c = min_r = min_c = 0; for (r = 0; r < cts_dev->fwdata.cols; r++) { for (c = 0; c < cts_dev->fwdata.rows; c++) { u16 val = data[r * cts_dev->fwdata.rows + c]; sum += val; if (val > max) { max = val; max_r = c; max_c = r; } else if (val < min) { min = val; min_r = c; min_c = r; } } } average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); count = 0; count += scnprintf(line_buf + count, sizeof(line_buf) - count, " %s test data MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", desc, min_r, min_c, min, max_r, max_c, max, average); if (to_console) { cts_info(SPLIT_LINE_STR); cts_info("%s", line_buf); cts_info(SPLIT_LINE_STR); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); cts_write_file(cts_test_data_filp, line_buf, count); cts_write_file(cts_test_data_filp, "\n", 1); cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); } count = 0; count += scnprintf(line_buf + count, sizeof(line_buf) - count, " | "); for (c = 0; c < cts_dev->fwdata.rows; c++) { count += scnprintf(line_buf + count, sizeof(line_buf) - count, COL_NUM_FORMAT_STR, c); } if (to_console) { cts_info("%s", line_buf); cts_info(SPLIT_LINE_STR); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, line_buf, count); cts_write_file(cts_test_data_filp, "\n", 1); cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); } for (r = 0; r < cts_dev->fwdata.cols; r++) { count = 0; count += scnprintf(line_buf + count, sizeof(line_buf) - count, ROW_NUM_FORMAT_STR, r); for (c = 0; c < cts_dev->fwdata.rows; c++) { count += scnprintf(line_buf + count, sizeof(line_buf) - count, DATA_FORMAT_STR, data[r * cts_dev->fwdata.rows + c]); } if (to_console) { cts_info("%s", line_buf); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, line_buf, count); cts_write_file(cts_test_data_filp, "\n", 1); } } if (to_console) { cts_info(SPLIT_LINE_STR); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); } #undef SPLIT_LINE_STR #undef ROW_NUM_FORMAT_STR #undef COL_NUM_FORMAT_STR #undef DATA_FORMAT_STR } static void cts_dump_stylus_data(struct cts_device *cts_dev, const char *desc, const u16 *data, u8 px_rows, u8 px_cols, bool to_console) { #define SPLIT_LINE_STR \ "--------------------------------------------------------"\ "--------------------------------------------------------" #define ROW_NUM_FORMAT_STR "%2d | " #define COL_NUM_FORMAT_STR "%-5u " #define DATA_FORMAT_STR "%-5u " int r, c; u32 max, min, sum, average; int max_r, max_c, min_r, min_c; char line_buf[512]; int count = 0; max = min = data[0]; sum = 0; max_r = max_c = min_r = min_c = 0; for (r = 0; r < px_rows; r++) { for (c = 0; c < px_cols; c++) { u16 val = data[r * px_cols + c]; sum += val; if (val > max) { max = val; max_r = c; max_c = r; } else if (val < min) { min = val; min_r = c; min_c = r; } } } average = sum / (px_rows * px_cols); count = 0; count += scnprintf(line_buf + count, sizeof(line_buf) - count, " %s test data MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", desc, min_r, min_c, min, max_r, max_c, max, average); if (to_console) { cts_info(SPLIT_LINE_STR); cts_info("%s", line_buf); cts_info(SPLIT_LINE_STR); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); cts_write_file(cts_test_data_filp, line_buf, count); cts_write_file(cts_test_data_filp, "\n", 1); cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); } count = 0; count += scnprintf(line_buf + count, sizeof(line_buf) - count, " | "); for (c = 0; c < px_cols; c++) { count += scnprintf(line_buf + count, sizeof(line_buf) - count, COL_NUM_FORMAT_STR, c); } if (to_console) { cts_info("%s", line_buf); cts_info(SPLIT_LINE_STR); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, line_buf, count); cts_write_file(cts_test_data_filp, "\n", 1); cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); } for (r = 0; r < px_rows; r++) { count = 0; count += scnprintf(line_buf + count, sizeof(line_buf) - count, ROW_NUM_FORMAT_STR, r); for (c = 0; c < px_cols; c++) { count += scnprintf(line_buf + count, sizeof(line_buf) - count, DATA_FORMAT_STR, data[r * px_cols + c]); } if (to_console) { cts_info("%s", line_buf); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, line_buf, count); cts_write_file(cts_test_data_filp, "\n", 1); } } if (to_console) { cts_info(SPLIT_LINE_STR); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); } #undef SPLIT_LINE_STR #undef ROW_NUM_FORMAT_STR #undef COL_NUM_FORMAT_STR #undef DATA_FORMAT_STR } static bool is_invalid_node(u32 *invalid_nodes, u32 num_invalid_nodes, u16 row, u16 col) { int i; for (i = 0; i < num_invalid_nodes; i++) { if (MAKE_INVALID_NODE(row, col) == invalid_nodes[i]) { return true; } } return false; } /* Return number of failed nodes */ static int validate_tsdata(struct cts_device *cts_dev, const char *desc, u16 *data, u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) { #define SPLIT_LINE_STR \ "------------------------------" int r, c; int failed_cnt = 0; cts_info("%s validate data: %s, num invalid node: %u, thresh[0]=[%d, %d]", desc, per_node ? "Per-Node" : "Uniform-Threshold", num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); for (r = 0; r < cts_dev->fwdata.rows; r++) { for (c = 0; c < cts_dev->fwdata.cols; c++) { int offset = r * cts_dev->fwdata.cols + c; if (num_invalid_nodes && is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { continue; } if ((min != NULL && data[offset] < min[per_node ? offset : 0]) || (max != NULL && data[offset] > max[per_node ? offset : 0])) { if (failed_cnt == 0) { cts_info(SPLIT_LINE_STR); cts_info("%s failed nodes:", desc); } failed_cnt++; cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, r, c, data[offset]); } } } if (failed_cnt) { cts_info(SPLIT_LINE_STR); cts_info("%s test %d node total failed", desc, failed_cnt); } return failed_cnt; #undef SPLIT_LINE_STR } /* Return number of failed nodes */ static int validate_stylus_pc_data(struct cts_device *cts_dev, const char *desc, u16 *data, u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) { #define SPLIT_LINE_STR \ "------------------------------" u8 pc_rows_used = cts_dev->fwdata.pc_rows_used; u8 pc_cols_used = cts_dev->fwdata.pc_cols_used; u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; int r, c; int failed_cnt = 0; cts_info("%s validate data: %s, num invalid node: %u, thresh[0]=[%d, %d]", desc, per_node ? "Per-Node" : "Uniform-Threshold", num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); for (r = 0; r < pc_rows_used; r++) { for (c = 0; c < pc_cols_used * pen_freq_num; c++) { int offset = r * pc_cols_used * pen_freq_num + c; if (num_invalid_nodes && is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { continue; } if ((min != NULL && data[offset] < min[per_node ? offset : 0]) || (max != NULL && data[offset] > max[per_node ? offset : 0])) { if (failed_cnt == 0) { cts_info(SPLIT_LINE_STR); cts_info("%s failed nodes:", desc); } failed_cnt++; cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, r, c, data[offset]); } } } if (failed_cnt) { cts_info(SPLIT_LINE_STR); cts_info("%s test %d node total failed", desc, failed_cnt); } return failed_cnt; #undef SPLIT_LINE_STR } /* Return number of failed nodes */ static int validate_stylus_pr_data(struct cts_device *cts_dev, const char *desc, u16 *data, u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) { #define SPLIT_LINE_STR \ "------------------------------" u8 pr_rows_used = cts_dev->fwdata.pr_rows_used; u8 pr_cols_used = cts_dev->fwdata.pr_cols_used; u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; int r, c; int failed_cnt = 0; cts_info("%s validate data: %s, num invalid node: %u, thresh[0]=[%d, %d]", desc, per_node ? "Per-Node" : "Uniform-Threshold", num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); for (r = 0; r < pr_rows_used * pen_freq_num; r++) { for (c = 0; c < pr_cols_used; c++) { int offset = r * pr_cols_used + c; if (num_invalid_nodes && is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { continue; } if ((min != NULL && data[offset] < min[per_node ? offset : 0]) || (max != NULL && data[offset] > max[per_node ? offset : 0])) { if (failed_cnt == 0) { cts_info(SPLIT_LINE_STR); cts_info("%s failed nodes:", desc); } failed_cnt++; cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, r, c, data[offset]); } } } if (failed_cnt) { cts_info(SPLIT_LINE_STR); cts_info("%s test %d node total failed", desc, failed_cnt); } return failed_cnt; #undef SPLIT_LINE_STR } #ifdef CFG_CTS_HAS_RESET_PIN int cts_test_reset_pin(struct cts_device *cts_dev, struct cts_test_param *param) { ktime_t start_time, end_time, delta_time; int ret; if (cts_dev == NULL || param == NULL) { cts_err("Reset-pin test with invalid param: cts_dev: %p test param: %p", cts_dev, param); return -EINVAL; } cts_info("Reset-Pin test, flags: 0x%08x, drive log file: '%s' buf size: %d", param->flags, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto show_test_result; } cts_lock_device(cts_dev); cts_plat_set_reset(cts_dev->pdata, 0); mdelay(50); if (cts_plat_is_normal_mode(cts_dev->pdata)) { ret = -EIO; cts_err("Device is alive while reset is low"); } cts_plat_set_reset(cts_dev->pdata, 1); mdelay(120); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); } if (!cts_plat_is_normal_mode(cts_dev->pdata)) { ret = -EIO; cts_err("Device is offline while reset is high"); } cts_unlock_device(cts_dev); cts_start_device(cts_dev); if (!cts_dev->rtdata.program_mode) { cts_set_normal_addr(cts_dev); } show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret) { cts_info("Reset-Pin test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { cts_info("Reset-Pin test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return ret; } #endif int cts_test_int_pin(struct cts_device *cts_dev, struct cts_test_param *param) { ktime_t start_time, end_time, delta_time; int ret; if (cts_dev == NULL || param == NULL) { cts_err("Int-pin test with invalid param: cts_dev: %p test param: %p", cts_dev, param); return -EINVAL; } cts_info("Int-Pin test, flags: 0x%08x, drive log file: '%s' buf size: %d", param->flags, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto show_test_result; } cts_lock_device(cts_dev); ret = cts_tcs_set_int_test(cts_dev, 1); if (ret) { cts_err("Enable Int Test failed"); goto unlock_device; } ret = cts_tcs_set_int_pin(cts_dev, 1); if (ret) { cts_err("Enable Int Test High failed"); goto exit_int_test; } mdelay(10); if (cts_plat_get_int_pin(cts_dev->pdata) == 0) { cts_err("INT pin state != HIGH"); ret = -EFAULT; goto exit_int_test; } ret = cts_tcs_set_int_pin(cts_dev, 0); if (ret) { cts_err("Enable Int Test LOW failed"); goto exit_int_test; } mdelay(10); if (cts_plat_get_int_pin(cts_dev->pdata) != 0) { cts_err("INT pin state != LOW"); ret = -EFAULT; goto exit_int_test; } exit_int_test: { int r = cts_tcs_set_int_test(cts_dev, 0); if (r) { cts_err("Disable Int Test failed %d", r); } } mdelay(10); unlock_device: cts_unlock_device(cts_dev); cts_start_device(cts_dev); show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret) { cts_info("Int-Pin test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { cts_info("Int-Pin test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return ret; } int cts_test_rawdata(struct cts_device *cts_dev, struct cts_test_param *param) { struct cts_rawdata_test_priv_param *priv_param; bool driver_validate_data = false; bool validate_data_per_node = false; bool stop_test_if_validate_fail = false; bool dump_test_data_to_user = false; bool dump_test_data_to_console = false; bool dump_test_data_to_file = false; int num_nodes; int tsdata_frame_size; int frame; int fail_frame = 0; u16 *rawdata = NULL; ktime_t start_time, end_time, delta_time; int i; int ret; if (cts_dev == NULL || param == NULL) { cts_err("Rawdata test with dev or null param"); return -EINVAL; } if (param->priv_param_size != sizeof(*priv_param) || param->priv_param == NULL) { cts_err("Rawdata test with invalid param: priv param: %p size: %d", param->priv_param, param->priv_param_size); return -EINVAL; } priv_param = param->priv_param; if (priv_param->frames <= 0) { cts_info("Rawdata test with too little frame %u", priv_param->frames); return -EINVAL; } num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; tsdata_frame_size = 2 * cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); validate_data_per_node = !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); dump_test_data_to_user = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); dump_test_data_to_console = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); dump_test_data_to_file = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); stop_test_if_validate_fail = !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); cts_info("Rawdata test, flags: 0x%08x, frames: %d, num invalid node: %u, " "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", param->flags, priv_param->frames, param->num_invalid_node, param->test_data_filepath, param->test_data_buf_size, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); if (dump_test_data_to_user) { rawdata = (u16 *) param->test_data_buf; } else { rawdata = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); if (rawdata == NULL) { cts_err("Allocate memory for rawdata failed"); ret = -ENOMEM; goto show_test_result; } } /* Stop device to avoid un-wanted interrrupt */ ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto free_mem; } if (dump_test_data_to_file) { int r = cts_start_dump_test_data_to_file(param->test_data_filepath, !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); if (r) { cts_err("Start dump test data to file failed %d", r); } } cts_lock_device(cts_dev); cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); goto unlock; } ret = cts_tcs_set_product_en(cts_dev, 1); if (ret) { cts_err("Set product en failed %d", ret); goto unlock; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); goto unlock; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Wait CTS_KRANG_CFG_MODE failed %d", ret); goto unlock; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_FIN_DBG_MODE); if (ret) { cts_err("Set CTS_KRANG_FIN_DBG_MODE failed %d", ret); goto unlock; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_FIN_DBG_MODE); if (ret) { cts_err("Wait CTS_KRANG_FIN_DBG_MODE failed %d", ret); goto unlock; } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); if (ret) { cts_err("Set INT_DATA_TYPE_RAWDATA failed %d", ret); goto unlock; } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); if (ret) { cts_err("Set INT_DATA_METHOD_POLLING failed %d", ret); goto unlock; } for (frame = 0; frame < priv_param->frames; frame++) { bool data_valid = false; for (i = 0; i < 3; i++) { ret = cts_polling_test_data(cts_dev, (u8 *)rawdata, RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); if (ret < 0) { cts_err("Get raw data failed: %d", ret); mdelay(30); } else { data_valid = true; break; } } if (!data_valid) { ret = -EIO; break; } if (dump_test_data_to_user) { *param->test_data_wr_size += 2 * num_nodes; } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_tsdata(cts_dev, "Rawdata", rawdata, dump_test_data_to_console); } if (driver_validate_data) { ret = validate_tsdata(cts_dev, "Rawdata", rawdata, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); if (ret) { cts_err("Rawdata test failed %d", ret); fail_frame++; cts_err("Rawdata test has %d nodes failed", ret); if (stop_test_if_validate_fail) { break; } } } if (dump_test_data_to_user) { rawdata += num_nodes; } } if (dump_test_data_to_file) { cts_stop_dump_test_data_to_file(); } unlock: cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); if (ret) { cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); if (ret) { cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); } cts_unlock_device(cts_dev); cts_start_device(cts_dev); free_mem: if (!dump_test_data_to_user && rawdata != NULL) { kfree(rawdata); } show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret) { cts_info("Rawdata test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else if (fail_frame > 0) { cts_info("Rawdata test has %d frame(s) FAIL, ELAPSED TIME: %lldms", fail_frame, ktime_to_ms(delta_time)); } else if (fail_frame == 0) { cts_info("Rawdata test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return (ret ? ret : (fail_frame ? fail_frame : 0)); } int cts_test_noise(struct cts_device *cts_dev, struct cts_test_param *param) { struct cts_noise_test_priv_param *priv_param; bool driver_validate_data = false; bool validate_data_per_node = false; bool dump_test_data_to_user = false; bool dump_test_data_to_console = false; bool dump_test_data_to_file = false; int num_nodes; int tsdata_frame_size; int frame; u16 *buffer = NULL; int buf_size = 0; u16 *curr_rawdata = NULL; u16 *max_rawdata = NULL; u16 *min_rawdata = NULL; u16 *noise = NULL; bool first_frame = true; ktime_t start_time, end_time, delta_time; int i; int ret; if (cts_dev == NULL || param == NULL) { cts_err("Noise test with dev or null param"); return -EINVAL; } if (param->priv_param_size != sizeof(*priv_param) || param->priv_param == NULL) { cts_err("Noise test with invalid param: priv param: %p size: %d", param->priv_param, param->priv_param_size); return -EINVAL; } priv_param = param->priv_param; if (priv_param->frames < 2) { cts_err("Noise test with too little frame %u", priv_param->frames); return -EINVAL; } num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; tsdata_frame_size = 2 * cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); validate_data_per_node = !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); dump_test_data_to_user = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); dump_test_data_to_console = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); dump_test_data_to_file = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); cts_info("Noise test, flags: 0x%08x, frames: %d, num invalid node: %u, " "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", param->flags, priv_param->frames, param->num_invalid_node, param->test_data_filepath, param->test_data_buf_size, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); buf_size = (driver_validate_data ? 4 : 1) * tsdata_frame_size; buffer = (u16 *) kmalloc(buf_size, GFP_KERNEL); if (buffer == NULL) { cts_err("Alloc mem for touch data failed"); ret = -ENOMEM; goto show_test_result; } curr_rawdata = buffer; if (driver_validate_data) { noise = curr_rawdata + 1 * num_nodes; min_rawdata = curr_rawdata + 2 * num_nodes; max_rawdata = curr_rawdata + 3 * num_nodes; } /* Stop device to avoid un-wanted interrrupt */ ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto free_mem; } if (dump_test_data_to_file) { int r = cts_start_dump_test_data_to_file(param->test_data_filepath, !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); if (r) { cts_err("Start dump test data to file failed %d", r); } } cts_lock_device(cts_dev); cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); goto unlock; } ret = cts_tcs_set_product_en(cts_dev, 1); if (ret) { cts_err("Set product en failed %d", ret); goto unlock; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); goto unlock; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Wait CTS_KRANG_CFG_MODE failed %d", ret); goto unlock; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_FIN_DBG_MODE); if (ret) { cts_err("Set CTS_KRANG_FIN_DBG_MODE failed %d", ret); goto unlock; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_FIN_DBG_MODE); if (ret) { cts_err("Wait CTS_KRANG_FIN_DBG_MODE failed %d", ret); goto unlock; } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); if (ret) { cts_err("Set INT_DATA_TYPE_RAWDATA failed %d", ret); goto unlock; } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); if (ret) { cts_err("Set INT_DATA_METHOD_POLLING failed %d", ret); goto unlock; } for (frame = 0; frame < priv_param->frames; frame++) { for (i = 0; i < 3; i++) { ret = cts_polling_test_data(cts_dev, (u8 *)curr_rawdata, RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); if (ret < 0) { cts_err("Get raw data failed: %d", ret); mdelay(30); } else { break; } } if (i >= 3) { cts_err("Read rawdata failed"); ret = -EIO; goto disable_get_tsdata; } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_tsdata(cts_dev, "Noise-rawdata", curr_rawdata, dump_test_data_to_console); } if (dump_test_data_to_user) { memcpy(param->test_data_buf + frame * 2 * num_nodes, curr_rawdata, 2 * num_nodes); *param->test_data_wr_size += 2 * num_nodes; } if (driver_validate_data) { if (unlikely(first_frame)) { memcpy(min_rawdata, curr_rawdata, 2 * num_nodes); memcpy(max_rawdata, curr_rawdata, 2 * num_nodes); first_frame = false; } else { for (i = 0; i < num_nodes; i++) { if (curr_rawdata[i] > max_rawdata[i]) { max_rawdata[i] = curr_rawdata[i]; } else if (curr_rawdata[i] < min_rawdata[i]) { min_rawdata[i] = curr_rawdata[i]; } } } } } if (driver_validate_data) { for (i = 0; i < num_nodes; i++) { noise[i] = max_rawdata[i] - min_rawdata[i]; } if (dump_test_data_to_user) { memcpy(param->test_data_buf + (priv_param->frames + 0) * 2 * num_nodes, noise, 2 * num_nodes); *param->test_data_wr_size += 2 * num_nodes; memcpy(param->test_data_buf + (priv_param->frames + 1) * 2 * num_nodes, min_rawdata, 2 * num_nodes); *param->test_data_wr_size += 2 * num_nodes; memcpy(param->test_data_buf + (priv_param->frames + 2) * 2 * num_nodes, max_rawdata, 2 * num_nodes); *param->test_data_wr_size += 2 * num_nodes; } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_tsdata(cts_dev, "Noise", noise, dump_test_data_to_console); cts_dump_tsdata(cts_dev, "Rawdata MIN", min_rawdata, dump_test_data_to_console); cts_dump_tsdata(cts_dev, "Rawdata MAX", max_rawdata, dump_test_data_to_console); } ret = validate_tsdata(cts_dev, "Noise test", noise, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); } disable_get_tsdata: if (dump_test_data_to_file) { cts_stop_dump_test_data_to_file(); } unlock: cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); if (ret) { cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); if (ret) { cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); } cts_unlock_device(cts_dev); cts_start_device(cts_dev); free_mem: if (buffer != NULL) { kfree(buffer); } show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { cts_info("Noise test has %d nodes FAIL, ELAPSED TIME: %lldms", ret, ktime_to_ms(delta_time)); } else if (ret < 0) { cts_info("Noise test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { cts_info("Noise test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return ret; } /* Return 0 success negative value while error occurs positive value means how many nodes fail */ int cts_test_open(struct cts_device *cts_dev, struct cts_test_param *param) { bool driver_validate_data = false; bool validate_data_per_node = false; bool dump_test_data_to_user = false; bool dump_test_data_to_console = false; bool dump_test_data_to_file = false; int num_nodes; int tsdata_frame_size; int ret; u16 *test_result = NULL; ktime_t start_time, end_time, delta_time; u8 old_int_data_method; u16 old_int_data_types; if (cts_dev == NULL || param == NULL) { cts_err("Open test with invalid param: cts_dev: %p test param: %p", cts_dev, param); return -EINVAL; } old_int_data_method = cts_dev->fwdata.int_data_method; old_int_data_types = cts_dev->fwdata.int_data_types; num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; tsdata_frame_size = 2 * cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); validate_data_per_node = !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); dump_test_data_to_user = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); dump_test_data_to_console = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); dump_test_data_to_file = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); cts_info("Open test, flags: 0x%08x, num invalid node: %u, " "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", param->flags, param->num_invalid_node, param->test_data_filepath, param->test_data_buf_size, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); if (dump_test_data_to_user) { test_result = (u16 *) param->test_data_buf; } else { test_result = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); if (test_result == NULL) { cts_err("Allocate memory for test result faild"); ret = -ENOMEM; goto show_test_result; } } ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto free_mem; } cts_lock_device(cts_dev); cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); goto unlock_device; } ret = cts_tcs_set_product_en(cts_dev, 1); if (ret) { cts_err("Set product en failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Wait CTS_CFG_MODE failed %d", ret); goto unlock_device; } ret = cts_tcs_set_openshort_mode(cts_dev, CTS_TEST_OPEN); if (ret) { cts_err("Set test type to OPEN_TEST failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_OPEN_SHORT_DET_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", ret); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_OPEN_SHORT_DET_MODE); if (ret) { cts_err("wait_to_curr_mode failed %d", ret); goto unlock_device; } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); if (ret) { cts_err("Set INT_DATA_TYPE_RAWDATA failed %d", ret); goto unlock_device; } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); if (ret) { cts_err("Set INT_DATA_METHOD_POLLING failed %d", ret); goto unlock_device; } ret = cts_polling_test_data(cts_dev, (u8 *)test_result, RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); if (ret) { cts_err("Read test result failed %d", ret); goto unlock_device; } if (dump_test_data_to_user) { *param->test_data_wr_size += 2 * num_nodes; } if (dump_test_data_to_file) { int r = cts_start_dump_test_data_to_file(param->test_data_filepath, !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); if (r) { cts_err("Start dump test data to file failed %d", r); } } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_tsdata(cts_dev, "Open-circuit", test_result, dump_test_data_to_console); } if (dump_test_data_to_file) { cts_stop_dump_test_data_to_file(); } if (driver_validate_data) { ret = validate_tsdata(cts_dev, "Open-circuit", test_result, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); } unlock_device: cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); if (ret) { cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); if (ret) { cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); } cts_unlock_device(cts_dev); cts_start_device(cts_dev); free_mem: if (!dump_test_data_to_user && test_result) { kfree(test_result); } show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { cts_info("Open test has %d nodes FAIL, ELAPSED TIME: %lldms", ret, ktime_to_ms(delta_time)); } else if (ret < 0) { cts_info("Open test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { cts_info("Open test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return ret; } /* Return 0 success negative value while error occurs positive value means how many nodes fail */ int cts_test_short(struct cts_device *cts_dev, struct cts_test_param *param) { bool driver_validate_data = false; bool validate_data_per_node = false; bool stop_if_failed = false; bool dump_test_data_to_user = false; bool dump_test_data_to_console = false; bool dump_test_data_to_file = false; int num_nodes; int tsdata_frame_size; int loopcnt; int ret; u16 *test_result = NULL; ktime_t start_time, end_time, delta_time; u8 old_int_data_method; u16 old_int_data_types; if (cts_dev == NULL || param == NULL) { cts_err("Short test with invalid param: cts_dev: %p test param: %p", cts_dev, param); return -EINVAL; } old_int_data_method = cts_dev->fwdata.int_data_method; old_int_data_types = cts_dev->fwdata.int_data_types; num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; tsdata_frame_size = 2 * cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); validate_data_per_node = !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); dump_test_data_to_user = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); dump_test_data_to_console = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); dump_test_data_to_file = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); stop_if_failed = !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); cts_info("Short test, flags: 0x%08x, num invalid node: %u, " "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", param->flags, param->num_invalid_node, param->test_data_filepath, param->test_data_buf_size, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); if (dump_test_data_to_user) { test_result = (u16 *) param->test_data_buf; } else { test_result = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); if (test_result == NULL) { cts_err("Allocate test result buffer failed"); ret = -ENOMEM; goto show_test_result; } } ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto err_free_test_result; } cts_lock_device(cts_dev); cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); goto unlock_device; } ret = cts_tcs_set_product_en(cts_dev, 1); if (ret) { cts_err("Set product en failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Wait CTS_CFG_MODE failed %d", ret); goto unlock_device; } cts_info("Test short to GND"); ret = cts_tcs_set_short_test_type(cts_dev, CTS_SHORT_TEST_UNDEFINED); if (ret) { cts_err("Set short test type to SHORT_TO_GND failed %d", ret); goto unlock_device; } ret = cts_tcs_set_openshort_mode(cts_dev, CTS_TEST_SHORT); if (ret) { cts_err("Set test type to SHORT failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_OPEN_SHORT_DET_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", ret); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_OPEN_SHORT_DET_MODE); if (ret) { cts_err("wait_to_curr_mode failed %d", ret); goto unlock_device; } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); if (ret) { cts_err("Set INT_DATA_TYPE_RAWDATA failed %d", ret); goto unlock_device; } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); if (ret) { cts_err("Set INT_DATA_METHOD_POLLING failed %d", ret); goto unlock_device; } ret = cts_tcs_set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_GND); if (ret) { cts_err("Set short test type to SHORT_TO_GND failed %d", ret); goto unlock_device; } ret = cts_polling_test_data(cts_dev, (u8 *)test_result, RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); if (ret) { cts_err("Read test result failed %d", ret); goto unlock_device; } if (dump_test_data_to_user) { *param->test_data_wr_size += 2 * num_nodes; } if (dump_test_data_to_file) { int r = cts_start_dump_test_data_to_file(param->test_data_filepath, !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); if (r) { cts_err("Start dump test data to file failed %d", r); } } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_tsdata(cts_dev, "GND-short", test_result, dump_test_data_to_console); } if (driver_validate_data) { ret = validate_tsdata(cts_dev, "GND-short", test_result, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); if (ret) { cts_err("Short to GND test failed %d", ret); if (stop_if_failed) { goto stop_dump_test_data_to_file; } } } if (dump_test_data_to_user) { test_result += num_nodes; } /* Short between colums */ cts_info("Test short between columns"); ret = cts_tcs_set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_COLS); if (ret) { cts_err("Set short test type to BETWEEN_COLS failed %d", ret); goto stop_dump_test_data_to_file; } for (loopcnt = 0; loopcnt < SHORT_COLS_TEST_LOOP; loopcnt++) { ret = cts_polling_test_data(cts_dev, (u8 *)test_result, RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); if (ret) { cts_err("Read test result failed %d", ret); goto stop_dump_test_data_to_file; } if (dump_test_data_to_user) { *param->test_data_wr_size += 2 * num_nodes; } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_tsdata(cts_dev, "Col-short", test_result, dump_test_data_to_console); } if (driver_validate_data) { ret = validate_tsdata(cts_dev, "Col-short", test_result, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); if (ret) { cts_err("Short between columns test failed %d", ret); if (stop_if_failed) { goto stop_dump_test_data_to_file; } } } if (dump_test_data_to_user) { test_result += num_nodes; } } /* Short between rows */ cts_info("Test short between rows"); ret = cts_tcs_set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_ROWS); if (ret) { cts_err("Set short test type to BETWEEN_ROWS failed %d", ret); goto stop_dump_test_data_to_file; } for (loopcnt = 0; loopcnt < SHORT_ROWS_TEST_LOOP; loopcnt++) { ret = cts_polling_test_data(cts_dev, (u8 *)test_result, RAWDATA_BUFFER_SIZE(cts_dev), INT_DATA_TYPE_RAWDATA); if (ret) { cts_err("Read test result failed %d", ret); goto stop_dump_test_data_to_file; } if (dump_test_data_to_user) { *param->test_data_wr_size += 2 * num_nodes; } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_tsdata(cts_dev, "Row-short", test_result, dump_test_data_to_console); } if (driver_validate_data) { ret = validate_tsdata(cts_dev, "Row-short", test_result, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); if (ret) { cts_err("Short between rows test failed %d", ret); if (stop_if_failed) { goto stop_dump_test_data_to_file; } } } if (dump_test_data_to_user) { test_result += num_nodes; } } stop_dump_test_data_to_file: if (dump_test_data_to_file) { cts_stop_dump_test_data_to_file(); } unlock_device: cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); if (ret) { cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); if (ret) { cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); } cts_unlock_device(cts_dev); cts_start_device(cts_dev); err_free_test_result: if (!dump_test_data_to_user && test_result) { kfree(test_result); } show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { cts_info("Short test has %d nodes FAIL, ELAPSED TIME: %lldms", ret, ktime_to_ms(delta_time)); } else if (ret < 0) { cts_info("Short test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { cts_info("Short test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return ret; } static int validate_comp_cap(struct cts_device *cts_dev, const char *desc, u8 *cap, u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) { #define SPLIT_LINE_STR \ "------------------------------" int r, c; int failed_cnt = 0; cts_info("Validate %s data: %s, num invalid node: %u, thresh[0]=[%d, %d]", desc, per_node ? "Per-Node" : "Uniform-Threshold", num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); for (r = 0; r < cts_dev->fwdata.rows; r++) { for (c = 0; c < cts_dev->fwdata.cols; c++) { int offset = r * cts_dev->fwdata.cols + c; if (num_invalid_nodes && is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { continue; } if ((min != NULL && cap[offset] < min[per_node ? offset : 0]) || (max != NULL && cap[offset] > max[per_node ? offset : 0])) { if (failed_cnt == 0) { cts_info(SPLIT_LINE_STR); cts_info("%s failed nodes:", desc); } failed_cnt++; cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, r, c, cap[offset]); } } } if (failed_cnt) { cts_info(SPLIT_LINE_STR); cts_info("%s test %d node total failed", desc, failed_cnt); } return failed_cnt; #undef SPLIT_LINE_STR } static void cts_dump_comp_cap(struct cts_device *cts_dev, u8 *cap, bool to_console) { #define SPLIT_LINE_STR \ "-----------------------------------------------------------------------------" #define ROW_NUM_FORMAT_STR "%2d | " #define COL_NUM_FORMAT_STR "%3u " #define DATA_FORMAT_STR "%4d" int r, c; u32 max, min, sum, average; int max_r, max_c, min_r, min_c; char line_buf[512]; int count; max = min = cap[0]; sum = 0; max_r = max_c = min_r = min_c = 0; for (r = 0; r < cts_dev->fwdata.cols; r++) { for (c = 0; c < cts_dev->fwdata.rows; c++) { u16 val = cap[r * cts_dev->fwdata.rows + c]; sum += val; if (val > max) { max = val; max_r = r; max_c = c; } else if (val < min) { min = val; min_r = r; min_c = c; } } } average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); count = 0; count += scnprintf(line_buf + count, sizeof(line_buf) - count, " Compensate Cap MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", min_r, min_c, min, max_r, max_c, max, average); if (to_console) { cts_info(SPLIT_LINE_STR); cts_info("%s", line_buf); cts_info(SPLIT_LINE_STR); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); cts_write_file(cts_test_data_filp, line_buf, count); cts_write_file(cts_test_data_filp, "\n", 1); cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); } count = 0; count += scnprintf(line_buf + count, sizeof(line_buf) - count, " "); for (c = 0; c < cts_dev->fwdata.rows; c++) { count += scnprintf(line_buf + count, sizeof(line_buf) - count, COL_NUM_FORMAT_STR, c); } if (to_console) { cts_info("%s", line_buf); cts_info(SPLIT_LINE_STR); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, line_buf, count); cts_write_file(cts_test_data_filp, "\n", 1); cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); } for (r = 0; r < cts_dev->fwdata.cols; r++) { count = 0; count += scnprintf(line_buf + count, sizeof(line_buf) - count, ROW_NUM_FORMAT_STR, r); for (c = 0; c < cts_dev->fwdata.rows; c++) { count += scnprintf(line_buf + count, sizeof(line_buf) - count, DATA_FORMAT_STR, cap[r * cts_dev->fwdata.rows + c]); } if (to_console) { cts_info("%s", line_buf); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, line_buf, count); cts_write_file(cts_test_data_filp, "\n", 1); } } if (to_console) { cts_info(SPLIT_LINE_STR); } if (cts_test_data_filp) { cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); cts_write_file(cts_test_data_filp, "\n", 1); } #undef SPLIT_LINE_STR #undef ROW_NUM_FORMAT_STR #undef COL_NUM_FORMAT_STR #undef DATA_FORMAT_STR } int cts_test_compensate_cap(struct cts_device *cts_dev, struct cts_test_param *param) { bool driver_validate_data = false; bool validate_data_per_node = false; bool dump_test_data_to_user = false; bool dump_test_data_to_console = false; bool dump_test_data_to_file = false; int num_nodes; int tsdata_frame_size; u8 *cap = NULL; int ret = 0; ktime_t start_time, end_time, delta_time; if (cts_dev == NULL || param == NULL) { cts_err("Compensate cap test with invalid param: cts_dev: %p test param: %p", cts_dev, param); return -EINVAL; } num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; tsdata_frame_size = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); if (driver_validate_data) { validate_data_per_node = !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); } dump_test_data_to_user = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); dump_test_data_to_console = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); dump_test_data_to_file = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); cts_info("Compensate cap test, flags: 0x%08x num invalid node: %u, " "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", param->flags, param->num_invalid_node, param->test_data_filepath, param->test_data_buf_size, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); if (dump_test_data_to_user) { cap = (u8 *) param->test_data_buf; } else { cap = (u8 *) kzalloc(tsdata_frame_size, GFP_KERNEL); if (cap == NULL) { cts_err("Alloc mem for compensate cap failed"); ret = -ENOMEM; goto show_test_result; } } /* Stop device to avoid un-wanted interrrupt */ ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto free_mem; } cts_lock_device(cts_dev); cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); goto unlock_device; } ret = cts_tcs_set_product_en(cts_dev, 1); if (ret) { cts_err("Set product en failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Wait CTS_KRANG_CFG_MODE failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_FIN_DBG_MODE); if (ret) { cts_err("Set CTS_KRANG_FIN_DBG_MODE failed %d", ret); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_FIN_DBG_MODE); if (ret) { cts_err("Wait CTS_KRANG_FIN_DBG_MODE failed %d", ret); goto unlock_device; } ret = cts_tcs_top_get_cnegdata(cts_dev, cap, num_nodes, 0); if (ret) { cts_err("Get compensate cap failed %d", ret); } if (dump_test_data_to_user) { *param->test_data_wr_size = num_nodes; } if (dump_test_data_to_file) { int r = cts_start_dump_test_data_to_file(param->test_data_filepath, !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); if (r) { cts_err("Start dump test data to file failed %d", r); } } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_comp_cap(cts_dev, cap, dump_test_data_to_console); } if (dump_test_data_to_file) { cts_stop_dump_test_data_to_file(); } if (driver_validate_data) { ret = validate_comp_cap(cts_dev, "Compensate-Cap", cap, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); } unlock_device: cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); if (ret) { cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); if (ret) { cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); } cts_unlock_device(cts_dev); cts_start_device(cts_dev); free_mem: if (!dump_test_data_to_user && cap) { kfree(cap); } show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { cts_info("Compensate-Cap test has %d nodes FAIL, ELAPSED TIME: %lldms", ret, ktime_to_ms(delta_time)); } else if (ret < 0) { cts_info("Compensate-Cap test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { cts_info("Compensate-Cap test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return ret; } int cts_test_stylus_rawdata(struct cts_device *cts_dev, struct cts_test_param *param) { struct cts_rawdata_test_priv_param *priv_param; bool stop_test_if_validate_fail = false; bool driver_validate_data = false; bool validate_data_per_node = false; bool dump_test_data_to_user = false; bool dump_test_data_to_console = false; bool dump_test_data_to_file = false; int num_nodes; int tsdata_frame_size; int frame; u16 *stylus_rawdata = NULL; u16 data_type; u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; u8 cascade_num = cts_dev->fwdata.cascade_num; u8 pr_rows_used = cts_dev->fwdata.pr_rows_used; u8 pr_cols_used = cts_dev->fwdata.pr_cols_used; u8 pc_cols_used = cts_dev->fwdata.pc_cols_used; u8 pc_rows_used = cts_dev->fwdata.pc_rows_used; u8 pr_rows = cts_dev->fwdata.pr_rows; u8 pr_cols = cts_dev->fwdata.pr_cols; u8 pc_rows = cts_dev->fwdata.pc_rows; u8 pc_cols = cts_dev->fwdata.pc_cols; int pr_data_index = pc_cols_used * pc_rows_used * pen_freq_num; int fail_frame = 0; int i, ret = 0; ktime_t start_time, end_time, delta_time; if (cts_dev == NULL || param == NULL) { cts_err("stylus rawdata test with invalid param: cts_dev: %p test param: %p", cts_dev, param); return -EINVAL; } if (param->priv_param_size != sizeof(*priv_param) || param->priv_param == NULL) { cts_err("Stylus rawdata test with invalid param: priv param: %p size: %d", param->priv_param, param->priv_param_size); return -EINVAL; } priv_param = param->priv_param; if (priv_param->frames <= 0) { cts_info("Stylus rawdata test with too little frame %u", priv_param->frames); return -EINVAL; } num_nodes = (pr_rows_used * pr_cols_used + pc_cols_used * pc_rows_used) * pen_freq_num; tsdata_frame_size = (pr_rows * pr_cols + pc_rows * pc_cols) * pen_freq_num * sizeof(u16) * cascade_num; driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); if (driver_validate_data) { validate_data_per_node = !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); } dump_test_data_to_user = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); dump_test_data_to_console = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); dump_test_data_to_file = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); stop_test_if_validate_fail = !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); cts_info("Stylus rawdata test, flags: 0x%08x num invalid node: %u, " "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", param->flags, param->num_invalid_node, param->test_data_filepath, param->test_data_buf_size, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); if (dump_test_data_to_user) { stylus_rawdata = (u16 *) param->test_data_buf; } else { stylus_rawdata = (u16 *) kzalloc(tsdata_frame_size, GFP_KERNEL); if (stylus_rawdata == NULL) { cts_err("Alloc mem for stylus_rawdata failed"); ret = -ENOMEM; goto show_test_result; } } ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto free_mem; } cts_lock_device(cts_dev); cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); goto unlock_device; } ret = cts_tcs_set_product_en(cts_dev, 1); if (ret) { cts_err("Set product en failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Wait CTS_CFG_MODE failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_STY_DBG_MODE); if (ret) { cts_err("Set krang CTS_KRANG_STY_DBG_MODE failed"); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_STY_DBG_MODE); if (ret) { cts_err("Wait krang CTS_KRANG_STY_DBG_MODE failed"); goto unlock_device; } ret = cts_tcs_set_pwr_mode(cts_dev, CTS_PWR_MODE_ACTIVE); if (ret) { cts_err("Set PWR_MODE_ACTIVE failed"); goto unlock_device; } data_type = INT_DATA_TYPE_RAWDATA | BIT(14); ret = cts_set_int_data_types(cts_dev, data_type); if (ret) { cts_err("Set data_type '0x%04x' failed", data_type); goto unlock_device; } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); if (ret) { cts_err("Set data_method polling failed"); goto unlock_device; } if (dump_test_data_to_file) { int r = cts_start_dump_test_data_to_file(param->test_data_filepath, !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); if (r) { cts_err("Start dump test data to file failed %d", r); } } for (frame = 0; frame < priv_param->frames; frame++) { for (i = 0; i < 3; i++) { ret = cts_polling_test_data(cts_dev, (u8 *)stylus_rawdata, tsdata_frame_size, data_type); if (ret < 0) { cts_err("Get stylus rawdata failed: %d", ret); mdelay(30); } else { break; } } if (i >= 3) { cts_err("Read stylus rawdata failed"); ret = -EIO; goto disable_get_tsdata; } if (dump_test_data_to_user) { *param->test_data_wr_size += 2 * num_nodes; } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_stylus_data(cts_dev, "Stylus-Rawdata-pc", stylus_rawdata, pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); cts_dump_stylus_data(cts_dev, "Stylus-Rawdata-pr", stylus_rawdata + pr_data_index, pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); } if (driver_validate_data) { ret = validate_stylus_pc_data(cts_dev, "Stylus-Rawdata-pc", stylus_rawdata, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); ret += validate_stylus_pr_data(cts_dev, "Stylus-Rawdata-pr", stylus_rawdata + pr_data_index, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); if (ret) { cts_err("Stylus rawdata test failed %d", ret); fail_frame++; cts_err("Stylus rawdata test has %d nodes failed", ret); if (stop_test_if_validate_fail) { break; } } } } disable_get_tsdata: if (dump_test_data_to_file) { cts_stop_dump_test_data_to_file(); } unlock_device: cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); if (ret) { cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); if (ret) { cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); } cts_unlock_device(cts_dev); cts_start_device(cts_dev); free_mem: if (!dump_test_data_to_user && stylus_rawdata) { kfree(stylus_rawdata); } show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { cts_info("Stylus-rawdata test has %d nodes FAIL, ELAPSED TIME: %lldms", ret, ktime_to_ms(delta_time)); } else if (ret < 0) { cts_info("Stylus-rawdata test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { cts_info("Stylus-Noise test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return ret; } int cts_test_stylus_noise(struct cts_device *cts_dev, struct cts_test_param *param) { struct cts_noise_test_priv_param *priv_param; bool driver_validate_data = false; bool validate_data_per_node = false; bool dump_test_data_to_user = false; bool dump_test_data_to_console = false; bool dump_test_data_to_file = false; int num_nodes; int tsdata_frame_size; int frame; bool first_frame = true; u16 *stylus_rawdata = NULL; u16 *max_stylus = NULL; u16 *min_stylus = NULL; u16 *noise_stylus = NULL; u16 data_type; u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; u8 cascade_num = cts_dev->fwdata.cascade_num; u8 pr_rows_used = cts_dev->fwdata.pr_rows_used; u8 pr_cols_used = cts_dev->fwdata.pr_cols_used; u8 pc_cols_used = cts_dev->fwdata.pc_cols_used; u8 pc_rows_used = cts_dev->fwdata.pc_rows_used; u8 pr_rows = cts_dev->fwdata.pr_rows; u8 pr_cols = cts_dev->fwdata.pr_cols; u8 pc_rows = cts_dev->fwdata.pc_rows; u8 pc_cols = cts_dev->fwdata.pc_cols; int pr_data_index = pc_cols_used * pc_rows_used * pen_freq_num; int i, ret = 0; ktime_t start_time, end_time, delta_time; if (cts_dev == NULL || param == NULL) { cts_err("stylus noise test with invalid param: cts_dev: %p test param: %p", cts_dev, param); return -EINVAL; } if (param->priv_param_size != sizeof(*priv_param) || param->priv_param == NULL) { cts_err("Stylus noise test with invalid param: priv param: %p size: %d", param->priv_param, param->priv_param_size); return -EINVAL; } priv_param = param->priv_param; if (priv_param->frames <= 0) { cts_info("Stylus noise test with too little frame %u", priv_param->frames); return -EINVAL; } cts_info("pc_rows=%d", pc_rows); cts_info("pc_cols=%d", pc_cols); cts_info("pr_rows=%d", pr_rows); cts_info("pr_cols=%d", pr_cols); cts_info("pc_rows_used=%d", pc_rows_used); cts_info("pc_cols_used=%d", pc_cols_used); cts_info("pr_rows_used=%d", pr_rows_used); cts_info("pr_cols_used=%d", pr_cols_used); cts_info("pen_freq_num=%d", pen_freq_num); cts_info("cascade_num=%d", cascade_num); num_nodes = (pr_rows_used * pr_cols_used + pc_cols_used * pc_rows_used) * pen_freq_num; tsdata_frame_size = (pr_rows * pr_cols + pc_rows * pc_cols) * pen_freq_num * sizeof(u16) * cascade_num; driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); if (driver_validate_data) { validate_data_per_node = !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); } dump_test_data_to_user = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); dump_test_data_to_console = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); dump_test_data_to_file = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); cts_info("Stylus rawdata test, flags: 0x%08x num invalid node: %u, " "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", param->flags, param->num_invalid_node, param->test_data_filepath, param->test_data_buf_size, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); if (dump_test_data_to_user) { stylus_rawdata = (u16 *) param->test_data_buf; } else { stylus_rawdata = (u16 *) kzalloc(tsdata_frame_size * 4, GFP_KERNEL); if (stylus_rawdata == NULL) { cts_err("Alloc mem for stylus_rawdata failed"); ret = -ENOMEM; goto show_test_result; } } max_stylus = stylus_rawdata + 1 * tsdata_frame_size/sizeof(u16); min_stylus = stylus_rawdata + 2 * tsdata_frame_size/sizeof(u16); noise_stylus = stylus_rawdata + 3 * tsdata_frame_size/sizeof(u16); ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto free_mem; } cts_lock_device(cts_dev); cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); goto unlock_device; } ret = cts_tcs_set_product_en(cts_dev, 1); if (ret) { cts_err("Set product en failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Wait CTS_CFG_MODE failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_STY_DBG_MODE); if (ret) { cts_err("Set krang CTS_KRANG_STY_DBG_MODE failed"); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_STY_DBG_MODE); if (ret) { cts_err("Wait krang CTS_KRANG_STY_DBG_MODE failed"); goto unlock_device; } ret = cts_tcs_set_pwr_mode(cts_dev, CTS_PWR_MODE_ACTIVE); if (ret) { cts_err("Set PWR_MODE_ACTIVE failed"); goto unlock_device; } data_type = INT_DATA_TYPE_RAWDATA | BIT(14); ret = cts_set_int_data_types(cts_dev, data_type); if (ret) { cts_err("Set data_type '0x%04x' failed", data_type); goto unlock_device; } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); if (ret) { cts_err("Set data_method polling failed"); goto unlock_device; } if (dump_test_data_to_file) { int r = cts_start_dump_test_data_to_file(param->test_data_filepath, !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); if (r) { cts_err("Start dump test data to file failed %d", r); } } for (frame = 0; frame < priv_param->frames; frame++) { for (i = 0; i < 3; i++) { ret = cts_polling_test_data(cts_dev, (u8 *)stylus_rawdata, tsdata_frame_size, data_type); if (ret < 0) { cts_err("Get stylus rawdata failed: %d", ret); mdelay(30); } else { break; } } if (i >= 3) { cts_err("Read stylus rawdata failed"); ret = -EIO; goto disable_get_tsdata; } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_stylus_data(cts_dev, "Stylus-Rawdata-pc", stylus_rawdata, pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); cts_dump_stylus_data(cts_dev, "Stylus-Rawdata-pr", stylus_rawdata + pr_data_index, pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); } if (dump_test_data_to_user) { memcpy(param->test_data_buf + frame * 2 * num_nodes, stylus_rawdata, 2 * num_nodes); *param->test_data_wr_size += 2 * num_nodes; } if (driver_validate_data) { if (unlikely(first_frame)) { memcpy(min_stylus, stylus_rawdata, 2 * num_nodes); memcpy(max_stylus, stylus_rawdata, 2 * num_nodes); first_frame = false; } else { for (i = 0; i < num_nodes; i++) { if (stylus_rawdata[i] > max_stylus[i]) { max_stylus[i] = stylus_rawdata[i]; } if (stylus_rawdata[i] < min_stylus[i]) { min_stylus[i] = stylus_rawdata[i]; } } } } } if (driver_validate_data) { for (i = 0; i < num_nodes; i++) { noise_stylus[i] = max_stylus[i] - min_stylus[i]; } if (dump_test_data_to_user) { memcpy(param->test_data_buf + (priv_param->frames + 0) * 2 * num_nodes, noise_stylus, 2 * num_nodes); *param->test_data_wr_size += 2 * num_nodes; memcpy(param->test_data_buf + (priv_param->frames + 1) * 2 * num_nodes, min_stylus, 2 * num_nodes); *param->test_data_wr_size += 2 * num_nodes; memcpy(param->test_data_buf + (priv_param->frames + 2) * 2 * num_nodes, max_stylus, 2 * num_nodes); *param->test_data_wr_size += 2 * num_nodes; } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_stylus_data(cts_dev, "Stylus-Noise-pc", noise_stylus, pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); cts_dump_stylus_data(cts_dev, "Stylus-Noise-pr", noise_stylus + pr_data_index, pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); cts_dump_stylus_data(cts_dev, "Stylus-MIN-pc", min_stylus, pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); cts_dump_stylus_data(cts_dev, "Stylus-MIN-pr", min_stylus + pr_data_index, pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); cts_dump_stylus_data(cts_dev, "Stylus-MAX-pc", max_stylus, pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); cts_dump_stylus_data(cts_dev, "Stylus-MAX-pr", max_stylus + pr_data_index, pr_rows_used * pen_freq_num, pr_cols_used, dump_test_data_to_console); } ret = validate_stylus_pc_data(cts_dev, "Stylus-Noise-pc", noise_stylus, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); ret += validate_stylus_pr_data(cts_dev, "Stylus-Noise-pr", noise_stylus + pr_data_index, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); } disable_get_tsdata: if (dump_test_data_to_file) { cts_stop_dump_test_data_to_file(); } unlock_device: cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); if (ret) { cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); if (ret) { cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); } cts_unlock_device(cts_dev); cts_start_device(cts_dev); free_mem: if (!dump_test_data_to_user && stylus_rawdata) { kfree(stylus_rawdata); } show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { cts_info("Stylus-Noise test has %d nodes FAIL, ELAPSED TIME: %lldms", ret, ktime_to_ms(delta_time)); } else if (ret < 0) { cts_info("Stylus-Noise test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { cts_info("Stylus-Noise test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return ret; } int cts_test_stylus_mnt_rawdata(struct cts_device *cts_dev, struct cts_test_param *param) { struct cts_rawdata_test_priv_param *priv_param; bool stop_test_if_validate_fail = false; bool driver_validate_data = false; bool validate_data_per_node = false; bool dump_test_data_to_user = false; bool dump_test_data_to_console = false; bool dump_test_data_to_file = false; int num_nodes; int tsdata_frame_size; int frame; u16 *stylus_rawdata = NULL; u16 data_type; u8 pen_freq_num = cts_dev->fwdata.pen_freq_num; u8 cascade_num = cts_dev->fwdata.cascade_num; u8 pr_rows_used = cts_dev->fwdata.pr_rows_used; u8 pr_cols_used = cts_dev->fwdata.pr_cols_used; u8 pc_cols_used = cts_dev->fwdata.pc_cols_used; u8 pc_rows_used = cts_dev->fwdata.pc_rows_used; u8 pr_rows = cts_dev->fwdata.pr_rows; u8 pr_cols = cts_dev->fwdata.pr_cols; u8 pc_rows = cts_dev->fwdata.pc_rows; u8 pc_cols = cts_dev->fwdata.pc_cols; int fail_frame = 0; int i, ret = 0; ktime_t start_time, end_time, delta_time; if (cts_dev == NULL || param == NULL) { cts_err("stylus mnt rawdata test with invalid param: cts_dev: %p test param: %p", cts_dev, param); return -EINVAL; } if (param->priv_param_size != sizeof(*priv_param) || param->priv_param == NULL) { cts_err("Stylus mnt rawdata test with invalid param: priv param: %p size: %d", param->priv_param, param->priv_param_size); return -EINVAL; } priv_param = param->priv_param; if (priv_param->frames <= 0) { cts_info("Stylus mnt rawdata test with too little frame %u", priv_param->frames); return -EINVAL; } num_nodes = (pr_rows_used * pr_cols_used + pc_cols_used * pc_rows_used) * cascade_num; tsdata_frame_size = (pr_rows * pr_cols + pc_rows * pc_cols) * pen_freq_num * sizeof(u16) * cascade_num; driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); if (driver_validate_data) { validate_data_per_node = !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); } dump_test_data_to_user = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); dump_test_data_to_console = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); dump_test_data_to_file = !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); stop_test_if_validate_fail = !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); cts_info("Stylus mnt rawdata test, flags: 0x%08x num invalid node: %u, " "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", param->flags, param->num_invalid_node, param->test_data_filepath, param->test_data_buf_size, param->driver_log_filepath, param->driver_log_buf_size); start_time = ktime_get(); if (dump_test_data_to_user) { stylus_rawdata = (u16 *) param->test_data_buf; } else { stylus_rawdata = (u16 *) kzalloc(tsdata_frame_size, GFP_KERNEL); if (stylus_rawdata == NULL) { cts_err("Alloc mem for stylus_rawdata failed"); ret = -ENOMEM; goto show_test_result; } } ret = cts_stop_device(cts_dev); if (ret) { cts_err("Stop device failed %d", ret); goto free_mem; } cts_lock_device(cts_dev); cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); goto unlock_device; } ret = cts_tcs_set_product_en(cts_dev, 1); if (ret) { cts_err("Set product en failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_CFG_MODE); if (ret) { cts_err("Wait CTS_CFG_MODE failed %d", ret); goto unlock_device; } ret = cts_tcs_set_krang_workmode(cts_dev, CTS_KRANG_MNT_DBG_MODE); if (ret) { cts_err("Set krang CTS_KRANG_MNT_DBG_MODE failed"); goto unlock_device; } ret = cts_wait_current_mode(cts_dev, CTS_KRANG_MNT_DBG_MODE); if (ret) { cts_err("Wait krang CTS_KRANG_MNT_DBG_MODE failed"); goto unlock_device; } ret = cts_tcs_set_pwr_mode(cts_dev, CTS_PWR_MODE_MNT_DBG); if (ret) { cts_err("Set CTS_PWR_MODE_MNT_DBG failed"); goto unlock_device; } data_type = INT_DATA_TYPE_RAWDATA | BIT(14); ret = cts_set_int_data_types(cts_dev, data_type); if (ret) { cts_err("Set data_type '0x%04x' failed", data_type); goto unlock_device; } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); if (ret) { cts_err("Set data_method polling failed"); goto unlock_device; } if (dump_test_data_to_file) { int r = cts_start_dump_test_data_to_file(param->test_data_filepath, !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); if (r) { cts_err("Start dump test data to file failed %d", r); } } for (frame = 0; frame < priv_param->frames; frame++) { for (i = 0; i < 3; i++) { ret = cts_polling_test_data(cts_dev, (u8 *)stylus_rawdata, tsdata_frame_size, data_type); if (ret < 0) { cts_err("Get stylus mnt rawdata failed: %d", ret); mdelay(30); } else { break; } } if (i >= 3) { cts_err("Read stylus mnt rawdata failed"); ret = -EIO; goto disable_get_tsdata; } if (dump_test_data_to_user) { *param->test_data_wr_size += 2 * num_nodes; } if (dump_test_data_to_console || dump_test_data_to_file) { cts_dump_stylus_data(cts_dev, "Stylus-mnt-Rawdata-pc", stylus_rawdata, pc_rows_used, pc_cols_used * pen_freq_num, dump_test_data_to_console); } if (driver_validate_data) { /* Only validate pc data */ ret = validate_stylus_pc_data(cts_dev, "Stylus-mnt-Rawdata-pc", stylus_rawdata, param->invalid_nodes, param->num_invalid_node, validate_data_per_node, param->min, param->max); if (ret) { cts_err("Stylus mnt rawdata test failed %d", ret); fail_frame++; cts_err("Stylus mnt rawdata test has %d nodes failed", ret); if (stop_test_if_validate_fail) { break; } } } } disable_get_tsdata: if (dump_test_data_to_file) { cts_stop_dump_test_data_to_file(); } unlock_device: cts_reset_device(cts_dev); ret = cts_wait_current_mode(cts_dev, CTS_KRANG_NORMAL_MODE); if (ret) { cts_err("Wait fw to normal work failed %d", ret); } ret = cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); if (ret) { cts_err("Set INT_DATA_TYPE_NONE failed %d", ret); } ret = cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); if (ret) { cts_err("Set INT_DATA_METHOD_NONE failed %d", ret); } cts_unlock_device(cts_dev); cts_start_device(cts_dev); free_mem: if (!dump_test_data_to_user && stylus_rawdata) { kfree(stylus_rawdata); } show_test_result: end_time = ktime_get(); delta_time = ktime_sub(end_time, start_time); if (ret > 0) { cts_info("Stylus-mnt-rawdata test has %d nodes FAIL, ELAPSED TIME: %lldms", ret, ktime_to_ms(delta_time)); } else if (ret < 0) { cts_info("Stylus-mnt-rawdata test FAIL %d(%s), ELAPSED TIME: %lldms", ret, cts_strerror(ret), ktime_to_ms(delta_time)); } else { cts_info("Stylus-mnt-rawdata test PASS, ELAPSED TIME: %lldms", ktime_to_ms(delta_time)); } return ret; }