/* * 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" #include #include #include #include #include /* test config */ #define TOTAL_FRAME_NUM 1 /* rawdata test frames */ #define NOISEDATA_TEST_TIMES 1 /* noise test frames */ #define GOODIX_RESULT_SAVE_PATH "/vendor/etc/Test_Data.csv" #define GOODIX_TEST_FILE_NAME "goodix" #define MAX_DATA_BUFFER 28000 #define MAX_SHORT_NUM 15 #define MAX_LINE_LEN (1024 * 3 * 7) #define MAX_DRV_NUM 52 #define MAX_SEN_NUM 75 #define STATISTICS_DATA_LEN 32 #define MAX_STR_LEN 32 #define MAX_TEST_ITEMS 10 /* 0P-1P-2P-3P-5P total test items */ #define GTP_CAP_TEST 1 #define GTP_DELTA_TEST 2 #define GTP_NOISE_TEST 3 #define GTP_OPEN_TEST 4 #define GTP_SHORT_TEST 5 #define GTP_SELFCAP_TEST 6 #define GTP_SELFNOISE_TEST 7 #define GTP_TEST_PASS 1 #define GTP_PANEL_REASON 2 #define SYS_SOFTWARE_REASON 3 #define CHN_VDD 0xFF #define CHN_GND 0x7F #define DRV_CHANNEL_FLAG 0x80 #define CSV_TP_SPECIAL_RAW_MIN "special_raw_min" #define CSV_TP_SPECIAL_RAW_MAX "special_raw_max" #define CSV_TP_SPECIAL_RAW_DELTA "special_raw_delta" #define CSV_TP_SHORT_THRESHOLD "shortciurt_threshold" #define CSV_TP_SPECIAL_SELFRAW_MAX "special_selfraw_max" #define CSV_TP_SPECIAL_SELFRAW_MIN "special_selfraw_min" #define CSV_TP_NOISE_LIMIT "noise_data_limit" #define CSV_TP_SELFNOISE_LIMIT "noise_selfdata_limit" #define CSV_TP_TEST_CONFIG "test_config" #define MAX_TEST_TIME_MS 15000 #define DEFAULT_TEST_TIME_MS 7000 /* berlin A */ #define MAX_DRV_NUM_BRA 21 #define MAX_SEN_NUM_BRA 42 #define SHORT_TEST_TIME_REG_BRA 0x11FF2 #define DFT_ADC_DUMP_NUM_BRA 1396 #define DFT_SHORT_THRESHOLD_BRA 16 #define DFT_DIFFCODE_SHORT_THRESHOLD_BRA 16 #define SHORT_TEST_STATUS_REG_BRA 0x10400 #define SHORT_TEST_RESULT_REG_BRA 0x10410 #define DRV_DRV_SELFCODE_REG_BRA 0x1045E #define SEN_SEN_SELFCODE_REG_BRA 0x1084E #define DRV_SEN_SELFCODE_REG_BRA 0x11712 #define DIFF_CODE_DATA_REG_BRA 0x11F72 /* berlin B */ #define MAX_DRV_NUM_BRB 52 #define MAX_SEN_NUM_BRB 75 #define SHORT_TEST_TIME_REG_BRB 0x26AE0 #define DFT_ADC_DUMP_NUM_BRB 762 #define DFT_SHORT_THRESHOLD_BRB 100 #define DFT_DIFFCODE_SHORT_THRESHOLD_BRB 32 #define SHORT_TEST_STATUS_REG_BRB 0x20400 #define SHORT_TEST_RESULT_REG_BRB 0x20410 #define DRV_DRV_SELFCODE_REG_BRB 0x2049A #define SEN_SEN_SELFCODE_REG_BRB 0x21AF2 #define DRV_SEN_SELFCODE_REG_BRB 0x248A6 #define DIFF_CODE_DATA_REG_BRB 0x269E0 /* berlinD */ #define MAX_DRV_NUM_BRD 20 #define MAX_SEN_NUM_BRD 40 #define SHORT_TEST_TIME_REG_BRD 0x14D7A #define DFT_ADC_DUMP_NUM_BRD 762 #define DFT_SHORT_THRESHOLD_BRD 100 #define DFT_DIFFCODE_SHORT_THRESHOLD_BRD 32 #define SHORT_TEST_STATUS_REG_BRD 0x13400 #define SHORT_TEST_RESULT_REG_BRD 0x13408 #define DRV_DRV_SELFCODE_REG_BRD 0x1344E #define SEN_SEN_SELFCODE_REG_BRD 0x137E6 #define DRV_SEN_SELFCODE_REG_BRD 0x14556 #define DIFF_CODE_DATA_REG_BRD 0x14D00 /* nottingham */ #define MAX_DRV_NUM_NOT 17 #define MAX_SEN_NUM_NOT 35 #define SHORT_TEST_TIME_REG_NOT 0x1479E #define SHORT_TEST_STATUS_REG_NOT 0x13400 #define SHORT_TEST_RESULT_REG_NOT 0x13408 #define DRV_DRV_SELFCODE_REG_NOT 0x13446 #define SEN_SEN_SELFCODE_REG_NOT 0x136EE #define DRV_SEN_SELFCODE_REG_NOT 0x14152 #define DIFF_CODE_DATA_REG_NOT 0x14734 #define ABS(val) ((val < 0) ? -(val) : val) #define MAX(a, b) ((a > b) ? a : b) static bool module_initialized; /* short threshold, drv-drv, drv-sen, sen-sen, drv-gnd, sen-gnd, avdd */ static u8 short_circuit_threshold[] = { 10, 200, 200, 200, 200, 200, 30 }; /* berlin A drv-sen map */ static u8 brl_a_drv_map[] = { 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62 }; static u8 brl_a_sen_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41 }; /* berlin B drv-sen map */ static u8 brl_b_drv_map[] = { 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126 }; static u8 brl_b_sen_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74 }; /* berlin D drv-sen map */ static u8 brl_d_drv_map[] = { 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, }; static u8 brl_d_sen_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, }; /* nottingham drv-sen map */ static u8 not_drv_map[] = { 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }; static u8 not_sen_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 }; typedef struct __attribute__((packed)) { u8 result; u8 drv_drv_num; u8 sen_sen_num; u8 drv_sen_num; u8 drv_gnd_avdd_num; u8 sen_gnd_avdd_num; u16 checksum; } test_result_t; struct params_info_t { u32 max_drv_num; u32 max_sen_num; u8 *drv_map; u8 *sen_map; u32 short_test_time_reg; u32 short_test_status_reg; u32 short_test_result_reg; u32 drv_drv_selfcode_reg; u32 sen_sen_selfcode_reg; u32 drv_sen_selfcode_reg; u32 diffcode_data_reg; u16 short_test_dump_num; u16 dft_short_threshold; u16 short_diffcode_threshold; }; struct params_info_t params_bra = { MAX_DRV_NUM_BRA, MAX_SEN_NUM_BRA, brl_a_drv_map, brl_a_sen_map, SHORT_TEST_TIME_REG_BRA, SHORT_TEST_STATUS_REG_BRA, SHORT_TEST_RESULT_REG_BRA, DRV_DRV_SELFCODE_REG_BRA, SEN_SEN_SELFCODE_REG_BRA, DRV_SEN_SELFCODE_REG_BRA, DIFF_CODE_DATA_REG_BRA, DFT_ADC_DUMP_NUM_BRA, DFT_SHORT_THRESHOLD_BRA, DFT_DIFFCODE_SHORT_THRESHOLD_BRA, }; struct params_info_t params_brb = { MAX_DRV_NUM_BRB, MAX_SEN_NUM_BRB, brl_b_drv_map, brl_b_sen_map, SHORT_TEST_TIME_REG_BRB, SHORT_TEST_STATUS_REG_BRB, SHORT_TEST_RESULT_REG_BRB, DRV_DRV_SELFCODE_REG_BRB, SEN_SEN_SELFCODE_REG_BRB, DRV_SEN_SELFCODE_REG_BRB, DIFF_CODE_DATA_REG_BRB, DFT_ADC_DUMP_NUM_BRB, DFT_SHORT_THRESHOLD_BRB, DFT_DIFFCODE_SHORT_THRESHOLD_BRB, }; struct params_info_t params_brd = { MAX_DRV_NUM_BRD, MAX_SEN_NUM_BRD, brl_d_drv_map, brl_d_sen_map, SHORT_TEST_TIME_REG_BRD, SHORT_TEST_STATUS_REG_BRD, SHORT_TEST_RESULT_REG_BRD, DRV_DRV_SELFCODE_REG_BRD, SEN_SEN_SELFCODE_REG_BRD, DRV_SEN_SELFCODE_REG_BRD, DIFF_CODE_DATA_REG_BRD, DFT_ADC_DUMP_NUM_BRD, DFT_SHORT_THRESHOLD_BRD, DFT_DIFFCODE_SHORT_THRESHOLD_BRD, }; struct params_info_t params_not = { MAX_DRV_NUM_NOT, MAX_SEN_NUM_NOT, not_drv_map, not_sen_map, SHORT_TEST_TIME_REG_NOT, SHORT_TEST_STATUS_REG_NOT, SHORT_TEST_RESULT_REG_NOT, DRV_DRV_SELFCODE_REG_NOT, SEN_SEN_SELFCODE_REG_NOT, DRV_SEN_SELFCODE_REG_NOT, DIFF_CODE_DATA_REG_NOT, 0, 0, 0, }; struct ts_test_params { bool test_items[MAX_TEST_ITEMS]; u32 rawdata_addr; u32 noisedata_addr; u32 self_rawdata_addr; u32 self_noisedata_addr; u32 drv_num; u32 sen_num; struct params_info_t *params_info; s32 cfg_buf[GOODIX_CFG_MAX_SIZE]; s32 max_limits[MAX_DRV_NUM * MAX_SEN_NUM]; s32 min_limits[MAX_DRV_NUM * MAX_SEN_NUM]; s32 deviation_limits[MAX_DRV_NUM * MAX_SEN_NUM]; s32 self_max_limits[MAX_DRV_NUM + MAX_SEN_NUM]; s32 self_min_limits[MAX_DRV_NUM + MAX_SEN_NUM]; s32 noise_threshold; s32 self_noise_threshold; u32 short_threshold; u32 r_drv_drv_threshold; u32 r_drv_sen_threshold; u32 r_sen_sen_threshold; u32 r_drv_gnd_threshold; u32 r_sen_gnd_threshold; u32 avdd_value; }; struct ts_test_rawdata { s16 data[MAX_DRV_NUM * MAX_SEN_NUM]; u32 size; }; struct ts_test_self_rawdata { s16 data[MAX_DRV_NUM + MAX_SEN_NUM]; u32 size; }; struct ts_short_res { u8 short_num; s16 short_msg[4 * MAX_SHORT_NUM]; }; struct ts_open_res { u8 beyond_max_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; u8 beyond_min_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; u8 beyond_accord_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; }; struct goodix_ts_test { struct goodix_ts_core *ts; struct ts_test_params test_params; struct ts_test_rawdata rawdata[TOTAL_FRAME_NUM]; struct ts_test_rawdata accord_arr[TOTAL_FRAME_NUM]; struct ts_test_rawdata noisedata[NOISEDATA_TEST_TIMES]; struct goodix_ic_config test_config; struct ts_test_self_rawdata self_rawdata; struct ts_test_self_rawdata self_noisedata; struct ts_short_res short_res; struct ts_open_res open_res; /*[0][0][0][0][0].. 0 without test; 1 pass, 2 panel failed; 3 software * failed */ char test_result[MAX_TEST_ITEMS]; char test_info[TS_RAWDATA_RESULT_MAX]; }; static int cal_cha_to_cha_res(struct goodix_ts_test *ts_test, int v1, int v2) { if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) return (v1 - v2) * 63 / v2; else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) return (v1 - v2) * 74 / v2 + 20; else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) return (v1 / v2 - 1) * 70 + 59; else return (v1 / v2 - 1) * 55 + 45; } static int cal_cha_to_avdd_res(struct goodix_ts_test *ts_test, int v1, int v2) { if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) return 64 * (2 * v2 - 25) * 40 / v1 - 40; else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) return 64 * (2 * v2 - 25) * 99 / v1 - 60; else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) return 64 * (2 * v2 - 25) * 93 / v1 - 20; else return 64 * (2 * v2 - 25) * 76 / v1 - 15; } static int cal_cha_to_gnd_res(struct goodix_ts_test *ts_test, int v) { if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) return 64148 / v - 40; else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) return 150500 / v - 60; else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) return 145000 / v - 15; else return 120000 / v - 16; } static int ts_test_reset(struct goodix_ts_test *ts_test, u32 delay_ms) { return ts_test->ts->hw_ops->reset(ts_test->ts, delay_ms); } static int ts_test_read( struct goodix_ts_test *ts_test, u32 addr, u8 *data, u32 len) { return ts_test->ts->hw_ops->read(ts_test->ts, addr, data, len); } static int ts_test_write( struct goodix_ts_test *ts_test, u32 addr, u8 *data, u32 len) { return ts_test->ts->hw_ops->write(ts_test->ts, addr, data, len); } static int ts_test_send_cmd( struct goodix_ts_test *ts_test, struct goodix_ts_cmd *cmd) { return ts_test->ts->hw_ops->send_cmd(ts_test->ts, cmd); } static int ts_test_irq_enable(struct goodix_ts_test *ts_test, bool flag) { return ts_test->ts->hw_ops->irq_enable(ts_test->ts, flag); } /* static int ts_test_send_config(struct goodix_ts_test *ts_test, int type) { struct goodix_ic_config *cfg; if (type >= GOODIX_MAX_CONFIG_GROUP) { ts_err("unsupported config type %d", type); return -EINVAL; } cfg = ts_test->ts->ic_configs[type]; if (!cfg || cfg->len <= 0) { ts_err("no valid normal config found"); return -EINVAL; } return ts_test->ts->hw_ops->send_config( ts_test->ts, cfg->data, cfg->len); } */ static int ts_test_read_version( struct goodix_ts_test *ts_test, struct goodix_fw_version *version) { return ts_test->ts->hw_ops->read_version(ts_test->ts, version); } static void goodix_init_params(struct goodix_ts_test *ts_test) { struct goodix_ts_core *ts = ts_test->ts; struct ts_test_params *test_params = &ts_test->test_params; test_params->rawdata_addr = ts->ic_info.misc.mutual_rawdata_addr; test_params->noisedata_addr = ts->ic_info.misc.mutual_diffdata_addr; test_params->self_rawdata_addr = ts->ic_info.misc.self_rawdata_addr; test_params->self_noisedata_addr = ts->ic_info.misc.self_diffdata_addr; test_params->short_threshold = short_circuit_threshold[0]; test_params->r_drv_drv_threshold = short_circuit_threshold[1]; test_params->r_drv_sen_threshold = short_circuit_threshold[2]; test_params->r_sen_sen_threshold = short_circuit_threshold[3]; test_params->r_drv_gnd_threshold = short_circuit_threshold[4]; test_params->r_sen_gnd_threshold = short_circuit_threshold[5]; test_params->avdd_value = short_circuit_threshold[6]; test_params->drv_num = ts->ic_info.parm.drv_num; test_params->sen_num = ts->ic_info.parm.sen_num; if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) test_params->params_info = ¶ms_bra; else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) test_params->params_info = ¶ms_brb; else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) test_params->params_info = ¶ms_brd; else if (ts_test->ts->bus->ic_type == IC_TYPE_NOTTINGHAM) test_params->params_info = ¶ms_not; } static int goodix_tptest_prepare(struct goodix_ts_test *ts_test) { ts_info("TP test prepare IN"); goodix_init_params(ts_test); /* disable irq */ ts_test_irq_enable(ts_test, false); /* close esd */ goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); return 0; } static void goodix_tptest_finish(struct goodix_ts_test *ts_test) { ts_info("TP test finish IN"); /* reset chip */ ts_test_reset(ts_test, 100); /* open esd */ goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); /* enable irq */ ts_test_irq_enable(ts_test, true); } #define PRODUCT_TEST_ADDR 0x15D4C #define DRV_CHAN_BYTES 7 #define SEN_CHAN_BYTES 10 static void goodix_opencircuit_test(struct goodix_ts_test *ts_test) { struct goodix_ts_cmd temp_cmd; u8 buf[24]; int retry = 20; int i; int ret; ts_info("---------------------- open_test begin ----------------------"); ts_test->test_result[GTP_OPEN_TEST] = SYS_SOFTWARE_REASON; temp_cmd.cmd = 0x63; temp_cmd.len = 4; ret = ts_test_send_cmd(ts_test, &temp_cmd); if (ret < 0) { ts_err("send open test cmd failed"); return; } msleep(200); while (retry--) { ret = ts_test_read( ts_test, PRODUCT_TEST_ADDR, buf, sizeof(buf)); if (ret < 0) { ts_err("read open test result failed"); return; } if (buf[0] == 0xCC && buf[1] == 0xCC) break; msleep(50); } if (retry < 0) { ts_err("open test not ready, status = %x%x", buf[0], buf[1]); return; } ret = checksum_cmp(buf, sizeof(buf), CHECKSUM_MODE_U8_LE); if (ret) { ts_err("open test result checksum error"); return; } if (buf[2] == 0) { ts_info("open test pass"); ts_test->test_result[GTP_OPEN_TEST] = GTP_TEST_PASS; } else { ts_err("open test failed"); ts_test->test_result[GTP_OPEN_TEST] = GTP_PANEL_REASON; /* DRV[0~55] total 7 bytes */ for (i = 0; i < DRV_CHAN_BYTES; i++) { if (buf[i + 3]) ts_info("DRV[%d~%d] open circuit, ret=0x%X", i * 8, i * 8 + 7, buf[i + 3]); } /* SEN[0~79] total 10 bytes */ for (i = 0; i < SEN_CHAN_BYTES; i++) { if (buf[i + 10]) ts_info("SEN[%d~%d] open circuit, ret=0x%X", i * 8, i * 8 + 7, buf[i + 10]); } } } #define SHORT_TEST_RUN_REG 0x10400 #define SHORT_TEST_RUN_FLAG 0xAA #define INSPECT_FW_SWITCH_CMD 0x85 #define TEST_FW_PID "OST" static int goodix_short_test_prepare(struct goodix_ts_test *ts_test) { struct goodix_ts_cmd tmp_cmd; struct goodix_fw_version fw_ver; int ret; int retry; int resend = 3; u8 status; ts_info("short test prepare IN"); ts_test->test_result[GTP_SHORT_TEST] = SYS_SOFTWARE_REASON; tmp_cmd.len = 4; tmp_cmd.cmd = INSPECT_FW_SWITCH_CMD; resend_cmd: ret = ts_test_send_cmd(ts_test, &tmp_cmd); if (ret < 0) { ts_err("send test mode failed"); return ret; } retry = 3; while (retry--) { msleep(40); if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) { ret = ts_test_read_version(ts_test, &fw_ver); if (ret < 0) { ts_err("read test version failed"); return ret; } ret = memcmp(&(fw_ver.patch_pid[3]), TEST_FW_PID, strlen(TEST_FW_PID)); if (ret == 0) return 0; else ts_info("patch ID dismatch %s != %s", fw_ver.patch_pid, TEST_FW_PID); } else { ret = ts_test_read( ts_test, SHORT_TEST_RUN_REG, &status, 1); if (!ret && status == SHORT_TEST_RUN_FLAG) return 0; ts_info("short_mode_status=0x%02x ret=%d", status, ret); } } if (resend--) { ts_test_reset(ts_test, 100); goto resend_cmd; } return -EINVAL; } static u32 map_die2pin(struct ts_test_params *test_params, u32 chn_num) { int i = 0; u32 res = 255; if (chn_num & DRV_CHANNEL_FLAG) chn_num = (chn_num & ~DRV_CHANNEL_FLAG) + test_params->params_info->max_sen_num; for (i = 0; i < test_params->params_info->max_sen_num; i++) { if (test_params->params_info->sen_map[i] == chn_num) { res = i; break; } } /* res != 255 mean found the corresponding channel num */ if (res != 255) return res; /* if cannot find in SenMap try find in DrvMap */ for (i = 0; i < test_params->params_info->max_drv_num; i++) { if (test_params->params_info->drv_map[i] == chn_num) { res = i; break; } } if (i >= test_params->params_info->max_drv_num) ts_err("Faild found corrresponding channel num:%d", chn_num); else res |= DRV_CHANNEL_FLAG; return res; } static void goodix_save_short_res( struct ts_test_params *params, u16 chn1, u16 chn2, int r) { int i; u8 repeat_cnt = 0; u8 repeat = 0; struct goodix_ts_test *ts_test = container_of(params, struct goodix_ts_test, test_params); struct ts_short_res *short_res = &ts_test->short_res; if (chn1 == chn2 || short_res->short_num >= MAX_SHORT_NUM) return; for (i = 0; i < short_res->short_num; i++) { repeat_cnt = 0; if (short_res->short_msg[4 * i] == chn1) repeat_cnt++; if (short_res->short_msg[4 * i] == chn2) repeat_cnt++; if (short_res->short_msg[4 * i + 1] == chn1) repeat_cnt++; if (short_res->short_msg[4 * i + 1] == chn2) repeat_cnt++; if (repeat_cnt >= 2) { repeat = 1; break; } } if (repeat == 0) { short_res->short_msg[4 * short_res->short_num + 0] = chn1; short_res->short_msg[4 * short_res->short_num + 1] = chn2; short_res->short_msg[4 * short_res->short_num + 2] = (r >> 8) & 0xFF; short_res->short_msg[4 * short_res->short_num + 3] = r & 0xFF; if (short_res->short_num < MAX_SHORT_NUM) short_res->short_num++; } } static int gdix_check_tx_tx_shortcircut( struct goodix_ts_test *ts_test, u8 short_ch_num) { int ret = 0, err = 0; u32 r_threshold = 0, short_r = 0; int size = 0, i = 0, j = 0; u16 adc_signal = 0; u8 master_pin_num, slave_pin_num; u8 *data_buf; u32 data_reg; struct ts_test_params *test_params = &ts_test->test_params; int max_drv_num = test_params->params_info->max_drv_num; int max_sen_num = test_params->params_info->max_sen_num; u16 self_capdata, short_die_num = 0; size = 4 + max_drv_num * 2 + 2; data_buf = kzalloc(size, GFP_KERNEL); if (!data_buf) { ts_err("Failed to alloc memory"); return -ENOMEM; } /* drv&drv shortcircut check */ data_reg = test_params->params_info->drv_drv_selfcode_reg; for (i = 0; i < short_ch_num; i++) { ret = ts_test_read(ts_test, data_reg, data_buf, size); if (ret < 0) { ts_err("Failed read Drv-to-Drv short rawdata"); err = -EINVAL; break; } if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { ts_err("Drv-to-Drv adc data checksum error"); err = -EINVAL; break; } r_threshold = test_params->r_drv_drv_threshold; short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); short_die_num -= max_sen_num; if (short_die_num >= max_drv_num) { ts_info("invalid short pad num:%d", short_die_num + max_sen_num); continue; } /* TODO: j start position need recheck */ self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); if (self_capdata == 0xffff || self_capdata == 0) { ts_info("invalid self_capdata:0x%x", self_capdata); continue; } for (j = short_die_num + 1; j < max_drv_num; j++) { adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); if (adc_signal < test_params->short_threshold) continue; short_r = (u32)cal_cha_to_cha_res( ts_test, self_capdata, adc_signal); if (short_r < r_threshold) { master_pin_num = map_die2pin(test_params, short_die_num + max_sen_num); slave_pin_num = map_die2pin( test_params, j + max_sen_num); if (master_pin_num == 0xFF || slave_pin_num == 0xFF) { ts_info("WARNNING invalid pin"); continue; } goodix_save_short_res(test_params, master_pin_num, slave_pin_num, short_r); ts_err("short circut:R=%dK,R_Threshold=%dK", short_r, r_threshold); ts_err("%s%d--%s%d shortcircut", (master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", (master_pin_num & ~DRV_CHANNEL_FLAG), (slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", (slave_pin_num & ~DRV_CHANNEL_FLAG)); err = -EINVAL; } } data_reg += size; } kfree(data_buf); return err; } static int gdix_check_rx_rx_shortcircut( struct goodix_ts_test *ts_test, u8 short_ch_num) { int ret = 0, err = 0; u32 r_threshold = 0, short_r = 0; int size = 0, i = 0, j = 0; u16 adc_signal = 0; u8 master_pin_num, slave_pin_num; u8 *data_buf; u32 data_reg; struct ts_test_params *test_params = &ts_test->test_params; int max_sen_num = test_params->params_info->max_sen_num; u16 self_capdata, short_die_num = 0; size = 4 + max_sen_num * 2 + 2; data_buf = kzalloc(size, GFP_KERNEL); if (!data_buf) { ts_err("Failed to alloc memory"); return -ENOMEM; } /* drv&drv shortcircut check */ data_reg = test_params->params_info->sen_sen_selfcode_reg; for (i = 0; i < short_ch_num; i++) { ret = ts_test_read(ts_test, data_reg, data_buf, size); if (ret) { ts_err("Failed read Sen-to-Sen short rawdata"); err = -EINVAL; break; } if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { ts_err("Sen-to-Sen adc data checksum error"); err = -EINVAL; break; } r_threshold = test_params->r_sen_sen_threshold; short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); if (short_die_num >= max_sen_num) { ts_info("invalid short pad num:%d", short_die_num); continue; } /* TODO: j start position need recheck */ self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); if (self_capdata == 0xffff || self_capdata == 0) { ts_info("invalid self_capdata:0x%x", self_capdata); continue; } for (j = short_die_num + 1; j < max_sen_num; j++) { adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); if (adc_signal < test_params->short_threshold) continue; short_r = (u32)cal_cha_to_cha_res( ts_test, self_capdata, adc_signal); if (short_r < r_threshold) { master_pin_num = map_die2pin(test_params, short_die_num); slave_pin_num = map_die2pin(test_params, j); if (master_pin_num == 0xFF || slave_pin_num == 0xFF) { ts_info("WARNNING invalid pin"); continue; } goodix_save_short_res(test_params, master_pin_num, slave_pin_num, short_r); ts_err("short circut:R=%dK,R_Threshold=%dK", short_r, r_threshold); ts_err("%s%d--%s%d shortcircut", (master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", (master_pin_num & ~DRV_CHANNEL_FLAG), (slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", (slave_pin_num & ~DRV_CHANNEL_FLAG)); err = -EINVAL; } } data_reg += size; } kfree(data_buf); return err; } static int gdix_check_tx_rx_shortcircut( struct goodix_ts_test *ts_test, u8 short_ch_num) { int ret = 0, err = 0; u32 r_threshold = 0, short_r = 0; int size = 0, i = 0, j = 0; u16 adc_signal = 0; u8 master_pin_num, slave_pin_num; u8 *data_buf = NULL; u32 data_reg; struct ts_test_params *test_params = &ts_test->test_params; int max_drv_num = test_params->params_info->max_drv_num; int max_sen_num = test_params->params_info->max_sen_num; u16 self_capdata, short_die_num = 0; size = 4 + max_drv_num * 2 + 2; data_buf = kzalloc(size, GFP_KERNEL); if (!data_buf) { ts_err("Failed to alloc memory"); return -ENOMEM; } /* drv&sen shortcircut check */ data_reg = test_params->params_info->drv_sen_selfcode_reg; for (i = 0; i < short_ch_num; i++) { ret = ts_test_read(ts_test, data_reg, data_buf, size); if (ret) { ts_err("Failed read Drv-to-Sen short rawdata"); err = -EINVAL; break; } if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { ts_err("Drv-to-Sen adc data checksum error"); err = -EINVAL; break; } r_threshold = test_params->r_drv_sen_threshold; short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); if (short_die_num >= max_sen_num) { ts_info("invalid short pad num:%d", short_die_num); continue; } /* TODO: j start position need recheck */ self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); if (self_capdata == 0xffff || self_capdata == 0) { ts_info("invalid self_capdata:0x%x", self_capdata); continue; } for (j = 0; j < max_drv_num; j++) { adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); if (adc_signal < test_params->short_threshold) continue; short_r = (u32)cal_cha_to_cha_res( ts_test, self_capdata, adc_signal); if (short_r < r_threshold) { master_pin_num = map_die2pin(test_params, short_die_num); slave_pin_num = map_die2pin( test_params, j + max_sen_num); if (master_pin_num == 0xFF || slave_pin_num == 0xFF) { ts_info("WARNNING invalid pin"); continue; } goodix_save_short_res(test_params, master_pin_num, slave_pin_num, short_r); ts_err("short circut:R=%dK,R_Threshold=%dK", short_r, r_threshold); ts_err("%s%d--%s%d shortcircut", (master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", (master_pin_num & ~DRV_CHANNEL_FLAG), (slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", (slave_pin_num & ~DRV_CHANNEL_FLAG)); err = -EINVAL; } } data_reg += size; } kfree(data_buf); return err; } static int gdix_check_resistance_to_gnd( struct ts_test_params *test_params, u16 adc_signal, u32 pos) { long r = 0; u16 r_th = 0, avdd_value = 0; u16 chn_id_tmp = 0; u8 pin_num = 0; unsigned short short_type; struct goodix_ts_test *ts_test = container_of(test_params, struct goodix_ts_test, test_params); int max_drv_num = test_params->params_info->max_drv_num; int max_sen_num = test_params->params_info->max_sen_num; avdd_value = test_params->avdd_value; short_type = adc_signal & 0x8000; adc_signal &= ~0x8000; if (adc_signal == 0) adc_signal = 1; if (short_type == 0) { /* short to GND */ r = cal_cha_to_gnd_res(ts_test, adc_signal); } else { /* short to VDD */ r = cal_cha_to_avdd_res(ts_test, adc_signal, avdd_value); } if (pos < max_drv_num) r_th = test_params->r_drv_gnd_threshold; else r_th = test_params->r_sen_gnd_threshold; chn_id_tmp = pos; if (chn_id_tmp < max_drv_num) chn_id_tmp += max_sen_num; else chn_id_tmp -= max_drv_num; if (r < r_th) { pin_num = map_die2pin(test_params, chn_id_tmp); goodix_save_short_res(test_params, pin_num, short_type ? CHN_VDD : CHN_GND, r); ts_err("%s%d shortcircut to %s,R=%ldK,R_Threshold=%dK", (pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", (pin_num & ~DRV_CHANNEL_FLAG), short_type ? "VDD" : "GND", r, r_th); return -EINVAL; } return 0; } static int gdix_check_gndvdd_shortcircut(struct goodix_ts_test *ts_test) { int ret = 0, err = 0; int size = 0, i = 0; u16 adc_signal = 0; u32 data_reg; u8 *data_buf = NULL; int max_drv_num = ts_test->test_params.params_info->max_drv_num; int max_sen_num = ts_test->test_params.params_info->max_sen_num; size = (max_drv_num + max_sen_num) * 2 + 2; data_buf = kzalloc(size, GFP_KERNEL); if (!data_buf) { ts_err("Failed to alloc memory"); return -ENOMEM; } /* read diff code, diff code will be used to calculate * resistance between channel and GND */ data_reg = ts_test->test_params.params_info->diffcode_data_reg; ret = ts_test_read(ts_test, data_reg, data_buf, size); if (ret < 0) { ts_err("Failed read to-gnd rawdata"); err = -EINVAL; goto err_out; } if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { ts_err("diff code checksum error"); err = -EINVAL; goto err_out; } for (i = 0; i < max_drv_num + max_sen_num; i++) { adc_signal = le16_to_cpup((__le16 *)&data_buf[i * 2]); ret = gdix_check_resistance_to_gnd( &ts_test->test_params, adc_signal, i); if (ret != 0) { ts_err("Resistance to-gnd/vdd short"); err = ret; } } err_out: kfree(data_buf); return err; } static int goodix_shortcircut_analysis(struct goodix_ts_test *ts_test) { int ret; int err = 0; test_result_t test_result; ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_result_reg, (u8 *)&test_result, sizeof(test_result)); if (ret < 0) { ts_err("Read TEST_RESULT_REG failed"); return ret; } if (checksum_cmp((u8 *)&test_result, sizeof(test_result), CHECKSUM_MODE_U8_LE)) { ts_err("shrot result checksum err"); return -EINVAL; } if (!(test_result.result & 0x0F)) { ts_info(">>>>> No shortcircut"); return 0; } ts_info("short flag 0x%02x, drv&drv:%d, sen&sen:%d, drv&sen:%d, drv/GNDVDD:%d, sen/GNDVDD:%d", test_result.result, test_result.drv_drv_num, test_result.sen_sen_num, test_result.drv_sen_num, test_result.drv_gnd_avdd_num, test_result.sen_gnd_avdd_num); if (test_result.drv_drv_num) err |= gdix_check_tx_tx_shortcircut( ts_test, test_result.drv_drv_num); if (test_result.sen_sen_num) err |= gdix_check_rx_rx_shortcircut( ts_test, test_result.sen_sen_num); if (test_result.drv_sen_num) err |= gdix_check_tx_rx_shortcircut( ts_test, test_result.drv_sen_num); if (test_result.drv_gnd_avdd_num || test_result.sen_gnd_avdd_num) err |= gdix_check_gndvdd_shortcircut(ts_test); ts_info(">>>>> short check return 0x%x", err); return err; } #define SHORT_FW_CMD_REG 0x10400 static int send_test_cmd( struct goodix_ts_test *ts_test, struct goodix_ts_cmd *cmd) { int ret; u32 reg = SHORT_FW_CMD_REG; cmd->state = 0; cmd->ack = 0; goodix_append_checksum( &(cmd->buf[2]), cmd->len - 2, CHECKSUM_MODE_U8_LE); ret = ts_test_write(ts_test, reg, cmd->buf, cmd->len + 2); if (ret < 0) return ret; usleep_range(10000, 11000); return ret; } #define INSPECT_PARAM_CMD 0xAA #define SHORT_TEST_FINISH_FLAG 0x88 #define SHORT_TEST_THRESHOLD_REG 0x20402 static void goodix_shortcircuit_test(struct goodix_ts_test *ts_test) { int ret = 0; int retry; u16 test_time; u8 status; int ic_type = ts_test->ts->bus->ic_type; struct goodix_ts_cmd test_parm_cmd; // u8 test_param[6]; ts_info("---------------------- short_test begin ----------------------"); ret = goodix_short_test_prepare(ts_test); if (ret < 0) { ts_err("Failed enter short test mode"); return; } /* get short test time */ ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_time_reg, (u8 *)&test_time, 2); if (ret < 0) { ts_err("Failed to get test_time, default %dms", DEFAULT_TEST_TIME_MS); test_time = DEFAULT_TEST_TIME_MS; } else { if (ic_type == IC_TYPE_BERLIN_A) test_time /= 10; if (test_time > MAX_TEST_TIME_MS) { ts_info("test time too long %d > %d", test_time, MAX_TEST_TIME_MS); test_time = MAX_TEST_TIME_MS; } ts_info("get test time %dms", test_time); } /* start short test */ if (ic_type == IC_TYPE_BERLIN_A) { test_parm_cmd.len = 0x0A; test_parm_cmd.cmd = INSPECT_PARAM_CMD; test_parm_cmd.data[0] = ts_test->test_params.params_info->dft_short_threshold & 0xFF; test_parm_cmd.data[1] = (ts_test->test_params.params_info ->dft_short_threshold >> 8) & 0xFF; test_parm_cmd.data[2] = ts_test->test_params.params_info ->short_diffcode_threshold & 0xFF; test_parm_cmd.data[3] = (ts_test->test_params.params_info ->short_diffcode_threshold >> 8) & 0xFF; test_parm_cmd.data[4] = ts_test->test_params.params_info->short_test_dump_num & 0xFF; test_parm_cmd.data[5] = (ts_test->test_params.params_info ->short_test_dump_num >> 8) & 0xFF; ret = send_test_cmd(ts_test, &test_parm_cmd); if (ret < 0) { ts_err("send INSPECT_PARAM_CMD failed"); return; } } else { // test_param[0] = // ts_test->test_params.params_info->dft_short_threshold & 0xFF; // test_param[1] = // (ts_test->test_params.params_info->dft_short_threshold >> 8) // & 0xFF; test_param[2] = // ts_test->test_params.params_info->short_diffcode_threshold & // 0xFF; test_param[3] = // (ts_test->test_params.params_info->short_diffcode_threshold // >> 8) & 0xFF; test_param[4] = // ts_test->test_params.params_info->short_test_dump_num & 0xFF; // test_param[5] = // (ts_test->test_params.params_info->short_test_dump_num >> 8) // & 0xFF; ts_test_write(ts_test, SHORT_TEST_THRESHOLD_REG, // test_param, sizeof(test_param)); status = 0; ts_test_write(ts_test, SHORT_TEST_RUN_REG, &status, 1); } /* wait short test finish */ msleep(test_time); retry = 50; while (retry--) { ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_status_reg, &status, 1); if (!ret && status == SHORT_TEST_FINISH_FLAG) break; msleep(50); } if (retry < 0) { ts_err("short test failed, status:0x%02x", status); return; } /* start analysis short result */ ts_info("short_test finished, start analysis"); ret = goodix_shortcircut_analysis(ts_test); if (ret < 0) ts_test->test_result[GTP_SHORT_TEST] = GTP_PANEL_REASON; else ts_test->test_result[GTP_SHORT_TEST] = GTP_TEST_PASS; } int goodix_do_inspect(struct goodix_ts_core *cd, struct ts_rawdata_info *info) { int ret; int cnt; struct goodix_ts_test *ts_test = NULL; if (!cd || !info) { ts_err("core_data or info is NULL"); return -ENODEV; } ts_test = kzalloc(sizeof(*ts_test), GFP_KERNEL); if (!ts_test) return -ENOMEM; ts_test->ts = cd; ret = goodix_tptest_prepare(ts_test); if (ret < 0) { ts_err("Failed to prepare TP test, exit"); strncpy(info->result, "[FAIL]\n", TS_RAWDATA_RESULT_MAX - 1); goto exit_finish; } ts_info("TP test prepare OK"); goodix_opencircuit_test(ts_test); goodix_shortcircuit_test(ts_test); cnt = snprintf(info->result, TS_RAWDATA_RESULT_MAX - 1, "open_test-[%s] ", (ts_test->test_result[GTP_OPEN_TEST] == GTP_TEST_PASS) ? "PASS" : "FAIL"); snprintf(&info->result[cnt], TS_RAWDATA_RESULT_MAX - 1, "short_test-[%s]\n", (ts_test->test_result[GTP_SHORT_TEST] == GTP_TEST_PASS) ? "PASS" : "FAIL"); goodix_tptest_finish(ts_test); exit_finish: kfree(ts_test); return ret; } /* show rawdata */ static ssize_t get_rawdata_show( struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; struct ts_rawdata_info *info = NULL; struct goodix_ts_core *cd = dev_get_drvdata(dev); info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; goodix_do_inspect(cd, info); ret = snprintf(buf, TS_RAWDATA_RESULT_MAX - 1, "%s", info->result); kfree(info); return ret; } static DEVICE_ATTR(get_rawdata, 0444, get_rawdata_show, NULL); int inspect_module_init(void) { int ret; struct kobject *def_kobj = goodix_get_default_kobj(); /* create sysfs */ ret = sysfs_create_file(def_kobj, &dev_attr_get_rawdata.attr); if (ret < 0) { ts_err("create sysfs of get_rawdata failed"); goto err_out; } module_initialized = true; ts_info("inspect module init success"); return 0; err_out: ts_err("inspect module init failed!"); return ret; } void inspect_module_exit(void) { struct kobject *def_kobj = goodix_get_default_kobj(); ts_info("inspect module exit"); if (!module_initialized) return; sysfs_remove_file(def_kobj, &dev_attr_get_rawdata.attr); module_initialized = false; }