blob: 78a5c582e2e4e5ca3c588de59d67f99c2c2d8071 [file] [edit]
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/version.h>
#include "goodix_ts_core.h"
#define CMD_FW_UPDATE "fw_update"
#define CMD_AUTO_TEST "auto_test"
#define CMD_STYLUS_RAW_TEST "stylus_raw_test"
#define CMD_STYLUS_OSC_TEST "stylus_osc_test"
#define CMD_OPEN_TEST "open_test"
#define CMD_SELF_OPEN_TEST "self_open_test"
#define CMD_NOISE_TEST "noise_test"
#define CMD_AUTO_NOISE_TEST "auto_noise_test"
#define CMD_SHORT_TEST "short_test"
#define CMD_GET_PACKAGE_ID "get_package_id"
#define CMD_GET_MCU_ID "get_mcu_id"
#define CMD_GET_VERSION "get_version"
#define CMD_GET_RAWDATA "get_raw"
#define CMD_GET_DIFFDATA "get_diff"
#define CMD_GET_BASEDATA "get_base"
#define CMD_GET_SELF_RAWDATA "get_self_raw"
#define CMD_GET_SELF_DIFFDATA "get_self_diff"
#define CMD_GET_SELF_BASEDATA "get_self_base"
#define CMD_SET_DOUBLE_TAP "set_double_tap"
#define CMD_SET_SINGLE_TAP "set_single_tap"
#define CMD_SET_LONG_PRESS "set_long_press"
#define CMD_SET_ST_PARAM "set_st_param"
#define CMD_SET_LP_PARAM "set_lp_param"
#define CMD_SET_CHARGE_MODE "set_charge_mode"
#define CMD_SET_IRQ_ENABLE "set_irq_enable"
#define CMD_SET_ESD_ENABLE "set_esd_enable"
#define CMD_SET_DEBUG_LOG "set_debug_log"
#define CMD_SET_SCAN_MODE "set_scan_mode"
#define CMD_GET_SCAN_MODE "get_scan_mode"
#define CMD_SET_CONTINUE_MODE "set_continue_mode"
#define CMD_GET_CHANNEL_NUM "get_channel_num"
#define CMD_GET_TX_FREQ "get_tx_freq"
#define CMD_RESET "reset"
#define CMD_SET_SENSE_MODE "set_sense_mode"
#define CMD_GET_CONFIG "get_config"
#define CMD_GET_FW_STATUS "get_fw_status"
#define CMD_SET_HIGHSENSE_MODE "set_highsense_mode"
#define CMD_SET_GRIP_DATA "set_grip_data"
#define CMD_SET_GRIP_MODE "set_grip_mode"
#define CMD_SET_PALM_MODE "set_palm_mode"
#define CMD_SET_NOISE_MODE "set_noise_mode"
#define CMD_SET_WATER_MODE "set_water_mode"
#define CMD_SET_HEATMAP "set_heatmap"
#define CMD_GET_SELF_COMPEN "get_self_compensation"
#define CMD_SET_REPORT_RATE "set_report_rate"
#define CMD_GET_DUMP_LOG "get_dump_log"
#define CMD_GET_STYLUS_DATA "get_stylus_data"
#define CMD_SET_FREQ_INDEX "set_freq_index"
#define CMD_DISABLE_FILTER "disable_filter"
#define CMD_GET_IM_DATA "get_im_data"
#define CMD_SET_HSYNC_SPEED "set_hsync_speed"
#define CMD_SET_WIRELESS_CHARGE "set_wireless_charge"
#define CMD_SET_CONTINUE_HEATMAP "set_continue_heatmap"
struct cmd_handler {
const char *name;
void (*handler)(struct goodix_ts_core *cd, int *buf, int bufsz);
};
/* test limits keyword */
#define CSV_TP_SPECIAL_RAW_MIN "special_raw_min"
#define CSV_TP_SPECIAL_RAW_MAX "special_raw_max"
#define CSV_TP_SPECIAL_FREQ_RAW_MIN "special_freq_raw_min"
#define CSV_IP_SPECIAL_FREQ_RAW_MAX "special_freq_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_SPECIAL_STYLUSRAW_MAX "special_stylusraw_max"
#define CSV_TP_SPECIAL_STYLUSRAW_MIN "special_stylusraw_min"
#define CSV_TP_SPECIAL_FREQ_STYLUSRAW_MAX "special_freq_stylusraw_max"
#define CSV_TP_SPECIAL_FREQ_STYLUSRAW_MIN "special_freq_stylusraw_min"
#define CSV_TP_NOISE_LIMIT "noise_data_limit"
#define CSV_TP_SELFNOISE_LIMIT "noise_selfdata_limit"
#define CSV_TP_ICAL_RAW_MIN "ical_raw_min"
#define CSV_TP_ICAL_RAW_MAX "ical_raw_max"
#define CSV_TP_ICAL_SHORT_THRESHOLD "ical_shortciurt_threshold"
#define CSV_TP_TEST_CONFIG "test_config"
#define PALM_FUNC 0
#define NOISE_FUNC 1
#define WATER_FUNC 2
#define GRIP_FUNC 3
#define SHORT_SIZE 512
#define LARGE_SIZE (15 * 1024)
#define HUGE_SIZE (100 * 1024)
static char wbuf[SHORT_SIZE];
static char *rbuf;
static uint32_t index;
/* factory test */
#define DISCARD_FRAME_NUMS 6
#define ABS(x) (((x) >= 0) ? (x) : -(x))
#define MAX(a, b) ((a > b) ? a : b)
#define GTP_CAP_TEST 1
#define GTP_DELTA_TEST 2
#define GTP_NOISE_TEST 3
#define GTP_SHORT_TEST 5
#define GTP_SELFCAP_TEST 6
#define GTP_SELFNOISE_TEST 7
#define GTP_STYLUS_RAW_TEST 8
#define MAX_TEST_ITEMS 10 /* 0P-1P-2P-3P-5P total test items */
#define TEST_OK 1
#define TEST_NG 0
#define MAX_LINE_LEN (1024 * 10)
#define MAX_DRV_NUM 52
#define MAX_SEN_NUM 75
#define MAX_SHORT_NUM 15
/* 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 GESTURE_STTW 0
#define GESTURE_LPTW 1
typedef union __attribute__((packed)) {
struct {
u8 length;
u8 type;
u16 st_min_x;
u16 st_max_x;
u16 st_min_y;
u16 st_max_y;
u16 st_min_count;
u16 st_max_count;
u16 st_motion_tolerance;
u16 st_max_size;
u16 checksum;
};
u8 buf[20];
} gesture_param_st_t;
static gesture_param_st_t gesture_param_st;
typedef union __attribute__((packed)) {
struct {
u8 length;
u8 type;
u16 lp_min_x;
u16 lp_max_x;
u16 lp_min_y;
u16 lp_max_y;
u16 lp_min_count;
u16 lp_max_size;
u16 lp_marginal_min_x;
u16 lp_marginal_max_x;
u16 lp_marginal_min_y;
u16 lp_marginal_max_y;
u8 lp_monitor_chan_min_tx;
u8 lp_monitor_chan_max_tx;
u8 lp_monitor_chan_min_rx;
u8 lp_monitor_chan_max_rx;
u16 lp_min_node_count;
u16 lp_motion_tolerance_inner;
u16 lp_motion_tolerance_outer;
u16 checksum;
};
u8 buf[34];
} gesture_param_lp_t;
static gesture_param_lp_t gesture_param_lp;
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;
/* 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 };
struct ts_short_test_info {
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;
};
static struct ts_short_test_info 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,
};
static struct ts_short_test_info 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,
};
static struct ts_short_test_info 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_short_res {
u8 short_num;
s16 short_msg[4 * MAX_SHORT_NUM];
};
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_test_stylus_rawdata {
s16 data[MAX_DRV_NUM + MAX_SEN_NUM];
u32 size;
};
static int raw_data_cnt;
static int noise_data_cnt;
struct goodix_ts_test {
bool item[MAX_TEST_ITEMS];
char result[MAX_TEST_ITEMS];
s16 min_limits[MAX_DRV_NUM * MAX_SEN_NUM];
s16 max_limits[MAX_DRV_NUM * MAX_SEN_NUM];
s16 deviation_limits[MAX_DRV_NUM * MAX_SEN_NUM];
s16 self_max_limits[MAX_DRV_NUM + MAX_SEN_NUM];
s16 self_min_limits[MAX_DRV_NUM + MAX_SEN_NUM];
s16 stylus_min_limits[MAX_DRV_NUM + MAX_SEN_NUM];
s16 stylus_max_limits[MAX_DRV_NUM + MAX_SEN_NUM];
s16 noise_threshold;
s16 short_threshold;
s16 r_drv_drv_threshold;
s16 r_drv_sen_threshold;
s16 r_sen_sen_threshold;
s16 r_drv_gnd_threshold;
s16 r_sen_gnd_threshold;
s16 avdd_value;
int freq;
bool is_ical;
int stylus_test_freq;
struct ts_test_rawdata *rawdata;
struct ts_test_rawdata *deltadata;
struct ts_test_rawdata *noisedata;
struct ts_test_self_rawdata selfrawdata;
struct ts_test_stylus_rawdata *stylusraw;
struct ts_short_test_info *params_info;
struct ts_short_res short_res;
};
static struct goodix_ts_test *ts_test;
static int cal_cha_to_cha_res(struct goodix_ts_core *cd, int v1, int v2)
{
if (cd->bus->ic_type == IC_TYPE_BERLIN_A)
return (v1 - v2) * 63 / v2;
else if (cd->bus->ic_type == IC_TYPE_BERLIN_B)
return (v1 - v2) * 74 / v2 + 20;
else if (cd->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_core *cd, int v1, int v2)
{
if (cd->bus->ic_type == IC_TYPE_BERLIN_A)
return 64 * (2 * v2 - 25) * 40 / v1 - 40;
else if (cd->bus->ic_type == IC_TYPE_BERLIN_B)
return 64 * (2 * v2 - 25) * 99 / v1 - 60;
else if (cd->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_core *cd, int v)
{
if (cd->bus->ic_type == IC_TYPE_BERLIN_A)
return 64148 / v - 40;
else if (cd->bus->ic_type == IC_TYPE_BERLIN_B)
return 150500 / v - 60;
else if (cd->bus->ic_type == IC_TYPE_BERLIN_D)
return 145000 / v - 15;
else
return 120000 / v - 16;
}
/*
* [GOOG]
* Use kvzalloc/kzalloc to alloc instead of vmalloc/kmalloc(vendor).
* This will help to use kvfree for all cases w/o additional `malloc_type`.
*/
//static u8 malloc_type;
static inline void *malloc_proc_buffer(size_t size)
{
if (size > HUGE_SIZE)
return kvzalloc(size, GFP_KERNEL);
return kzalloc(size, GFP_KERNEL);
}
static inline void free_proc_buffer(char **ptr)
{
if (*ptr == NULL)
return;
kvfree(*ptr);
*ptr = NULL;
}
/*~[GOOG] */
static int malloc_test_resource(void)
{
ts_test = vzalloc(sizeof(*ts_test));
if (!ts_test)
return -ENOMEM;
if (raw_data_cnt > 0) {
ts_test->rawdata =
vzalloc(raw_data_cnt * sizeof(struct ts_test_rawdata));
if (ts_test->rawdata == NULL)
return -ENOMEM;
ts_test->deltadata =
vzalloc(raw_data_cnt * sizeof(struct ts_test_rawdata));
if (ts_test->deltadata == NULL)
return -ENOMEM;
ts_test->stylusraw =
vzalloc(raw_data_cnt * sizeof(struct ts_test_stylus_rawdata));
if (ts_test->stylusraw == NULL)
return -ENOMEM;
}
if (noise_data_cnt > 0) {
ts_test->noisedata = vzalloc(
noise_data_cnt * sizeof(struct ts_test_rawdata));
if (ts_test->noisedata == NULL)
return -ENOMEM;
}
return 0;
}
static void release_test_resource(void)
{
if (ts_test) {
vfree(ts_test->rawdata);
vfree(ts_test->deltadata);
vfree(ts_test->noisedata);
vfree(ts_test->stylusraw);
vfree(ts_test);
ts_test = NULL;
}
raw_data_cnt = 0;
noise_data_cnt = 0;
}
#define CHN_VDD 0xFF
#define CHN_GND 0x7F
#define DRV_CHANNEL_FLAG 0x80
static u32 map_die2pin(u32 chn_num)
{
int i = 0;
u32 res = 255;
if (chn_num & DRV_CHANNEL_FLAG)
chn_num = (chn_num & ~DRV_CHANNEL_FLAG) +
ts_test->params_info->max_sen_num;
for (i = 0; i < ts_test->params_info->max_sen_num; i++) {
if (ts_test->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 < ts_test->params_info->max_drv_num; i++) {
if (ts_test->params_info->drv_map[i] == chn_num) {
res = i;
break;
}
}
if (i >= ts_test->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(u16 chn1, u16 chn2, int r)
{
int i;
u8 repeat_cnt = 0;
u8 repeat = 0;
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_core *cd, 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 = ts_test->params_info->drv_drv_selfcode_reg;
int max_drv_num = ts_test->params_info->max_drv_num;
int max_sen_num = ts_test->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 */
for (i = 0; i < short_ch_num; i++) {
ret = cd->hw_ops->read(cd, 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 = ts_test->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 < ts_test->short_threshold)
continue;
short_r = (u32)cal_cha_to_cha_res(cd, self_capdata, adc_signal);
if (short_r < r_threshold) {
master_pin_num = map_die2pin(short_die_num + max_sen_num);
slave_pin_num = map_die2pin(j + max_sen_num);
if (master_pin_num == 0xFF ||
slave_pin_num == 0xFF) {
ts_info("WARNNING invalid pin");
continue;
}
goodix_save_short_res(
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_core *cd, 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 = ts_test->params_info->sen_sen_selfcode_reg;
int max_sen_num = ts_test->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 */
for (i = 0; i < short_ch_num; i++) {
ret = cd->hw_ops->read(cd, 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 = ts_test->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 < ts_test->short_threshold)
continue;
short_r = (u32)cal_cha_to_cha_res(cd, self_capdata, adc_signal);
if (short_r < r_threshold) {
master_pin_num = map_die2pin(short_die_num);
slave_pin_num = map_die2pin(j);
if (master_pin_num == 0xFF ||
slave_pin_num == 0xFF) {
ts_info("WARNNING invalid pin");
continue;
}
goodix_save_short_res(
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_core *cd, 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 = ts_test->params_info->drv_sen_selfcode_reg;
int max_drv_num = ts_test->params_info->max_drv_num;
int max_sen_num = ts_test->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 */
for (i = 0; i < short_ch_num; i++) {
ret = cd->hw_ops->read(cd, 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 = ts_test->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 < ts_test->short_threshold)
continue;
short_r = (u32)cal_cha_to_cha_res(cd, self_capdata, adc_signal);
if (short_r < r_threshold) {
master_pin_num = map_die2pin(short_die_num);
slave_pin_num = map_die2pin(j + max_sen_num);
if (master_pin_num == 0xFF ||
slave_pin_num == 0xFF) {
ts_info("WARNNING invalid pin");
continue;
}
goodix_save_short_res(
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 goodix_ts_core *cd, 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;
int max_drv_num = ts_test->params_info->max_drv_num;
int max_sen_num = ts_test->params_info->max_sen_num;
avdd_value = ts_test->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(cd, adc_signal);
} else {
/* short to VDD */
r = cal_cha_to_avdd_res(cd, adc_signal, avdd_value);
}
if (pos < max_drv_num)
r_th = ts_test->r_drv_gnd_threshold;
else
r_th = ts_test->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(chn_id_tmp);
goodix_save_short_res(
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_core *cd)
{
int ret = 0, err = 0;
int size = 0, i = 0;
u16 adc_signal = 0;
u32 data_reg = ts_test->params_info->diffcode_data_reg;
u8 *data_buf = NULL;
int max_drv_num = ts_test->params_info->max_drv_num;
int max_sen_num = ts_test->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 */
ret = cd->hw_ops->read(cd, 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(cd, adc_signal, i);
if (ret != 0) {
ts_err("Resistance to-gnd/vdd short");
err = ret;
}
}
err_out:
kfree(data_buf);
return err;
}
#define NOTT_CHECKSUM_LEN 54
#define BRLD_CHECKSUM_LEN 62
#define BRLB_CHECKSUM_LEN 129
static int goodix_shortcircut_analysis(struct goodix_ts_core *cd)
{
int ret;
int err = 0;
u8 temp_buf[140];
test_result_t test_result;
int checksum_len = 0;
ret = cd->hw_ops->read(cd, ts_test->params_info->short_test_result_reg,
temp_buf, sizeof(temp_buf));
if (ret < 0) {
ts_err("Read TEST_RESULT_REG failed");
return ret;
}
if (cd->bus->ic_type == IC_TYPE_BERLIN_B) {
checksum_len = BRLB_CHECKSUM_LEN;
} else if (cd->bus->ic_type == IC_TYPE_BERLIN_D) {
checksum_len = BRLD_CHECKSUM_LEN;
} else if (cd->bus->ic_type == IC_TYPE_NOTTINGHAM) {
checksum_len = NOTT_CHECKSUM_LEN;
}
if (checksum_cmp(&temp_buf[sizeof(test_result)], checksum_len,
CHECKSUM_MODE_U8_LE)) {
ts_err("short result checksum err");
return -EINVAL;
}
memcpy((u8 *)&test_result, temp_buf, sizeof(test_result));
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(cd, test_result.drv_drv_num);
if (test_result.sen_sen_num)
err |= gdix_check_rx_rx_shortcircut(cd, test_result.sen_sen_num);
if (test_result.drv_sen_num)
err |= gdix_check_tx_rx_shortcircut(cd, test_result.drv_sen_num);
if (test_result.drv_gnd_avdd_num || test_result.sen_gnd_avdd_num)
err |= gdix_check_gndvdd_shortcircut(cd);
ts_info(">>>>> short check return 0x%x", err);
return err;
}
#define INSPECT_FW_SWITCH_CMD 0x85
#define SHORT_TEST_RUN_FLAG 0xAA
#define SHORT_TEST_RUN_REG 0x10400
static int goodix_short_test_prepare(struct goodix_ts_core *cd)
{
struct goodix_ts_cmd tmp_cmd;
int ret;
int retry;
int resend = 3;
u8 status;
ts_info("short test prepare IN");
tmp_cmd.len = 4;
tmp_cmd.cmd = INSPECT_FW_SWITCH_CMD;
resend_cmd:
ret = cd->hw_ops->send_cmd(cd, &tmp_cmd);
if (ret < 0) {
ts_err("send test mode failed");
return ret;
}
retry = 3;
while (retry--) {
msleep(40);
ret = cd->hw_ops->read(cd, 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--) {
cd->hw_ops->reset(cd, goodix_get_normal_reset_delay(cd));
goto resend_cmd;
}
return -EINVAL;
}
#define MAX_TEST_TIME_MS 15000
#define DEFAULT_TEST_TIME_MS 7000
#define SHORT_TEST_FINISH_FLAG 0x88
static int goodix_shortcircut_test(struct goodix_ts_core *cd)
{
int ret = 0;
int res;
int retry;
u16 test_time;
u8 status;
ts_info("---------------------- short_test begin ----------------------");
ret = goodix_short_test_prepare(cd);
if (ret < 0) {
ts_err("Failed enter short test mode");
return ret;
}
if (cd->bus->ic_type == IC_TYPE_BERLIN_B)
msleep(500);
/* get short test time */
ret = cd->hw_ops->read(cd, ts_test->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 (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 */
status = 0;
cd->hw_ops->write(cd, SHORT_TEST_RUN_REG, &status, 1);
/* wait short test finish */
if (test_time > 0)
msleep(test_time);
retry = 50;
while (retry--) {
ret = cd->hw_ops->read(cd,
ts_test->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 -EINVAL;
}
/* start analysis short result */
ts_info("short_test finished, start analysis");
res = goodix_shortcircut_analysis(cd);
if (res == 0) {
ts_test->result[GTP_SHORT_TEST] = TEST_OK;
ret = 0;
}
return ret;
}
static void *seq_start(struct seq_file *s, loff_t *pos)
{
if (*pos >= index)
return NULL;
return rbuf + *pos;
}
static int seq_show(struct seq_file *s, void *v)
{
seq_printf(s, (u8 *)v);
return 0;
}
static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
{
*pos += index;
return NULL;
}
static void seq_stop(struct seq_file *s, void *v)
{
if (s->read_pos >= index) {
// ts_info("read_pos:%d", (int)s->read_pos);
free_proc_buffer(&rbuf);
index = 0;
release_test_resource();
}
}
static const struct seq_operations seq_ops = {
.start = seq_start, .next = seq_next, .stop = seq_stop, .show = seq_show
};
static int driver_test_open(struct inode *inode, struct file *file)
{
struct seq_file *seq_file;
int ret;
ret = seq_open(file, &seq_ops);
if (ret)
return ret;
seq_file = (struct seq_file *)file->private_data;
if (seq_file) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0))
seq_file->private = pde_data(inode);
#else
seq_file->private = PDE_DATA(inode);
#endif
}
mutex_lock(&goodix_devices.mutex);
return 0;
}
static int driver_test_release(struct inode *inode, struct file *file)
{
mutex_unlock(&goodix_devices.mutex);
return seq_release(inode, file);
}
static void goodix_save_header(struct goodix_ts_core *cd)
{
int i;
bool total_result = true;
index = sprintf(
&rbuf[index], "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
index += sprintf(&rbuf[index], "<TESTLOG>\n");
index += sprintf(&rbuf[index], "<Header>\n");
/* sava test result */
for (i = 0; i < MAX_TEST_ITEMS; i++) {
if (ts_test->item[i] && ts_test->result[i] == TEST_NG)
total_result = false;
}
if (total_result == false) {
index += sprintf(&rbuf[index], "<Result>NG</Result>\n");
} else {
index += sprintf(&rbuf[index], "<Result>OK</Result>\n");
}
index += sprintf(&rbuf[index], "<DeviceType>GT%s</DeviceType>\n",
cd->fw_version.patch_pid);
index += sprintf(&rbuf[index], "<SensorId>%d</SensorId>\n",
cd->fw_version.sensor_id);
index += sprintf(&rbuf[index], "</Header>\n");
index += sprintf(&rbuf[index], "<ItemList>\n");
if (ts_test->item[GTP_CAP_TEST]) {
if (ts_test->result[GTP_CAP_TEST] == TEST_NG) {
index += sprintf(&rbuf[index],
"<Item name=\"Rawdata MAX/MIN Test\" result=\"NG\"/>\n");
} else {
index += sprintf(&rbuf[index],
"<Item name=\"Rawdata MAX/MIN Test\" result=\"OK\"/>\n");
}
if (ts_test->result[GTP_DELTA_TEST] == TEST_NG) {
index += sprintf(&rbuf[index],
"<Item name=\"Rawdata Adjcent Deviation Test\" result=\"NG\"/>\n");
} else {
index += sprintf(&rbuf[index],
"<Item name=\"Rawdata Adjcent Deviation Test\" result=\"OK\"/>\n");
}
}
if (ts_test->item[GTP_NOISE_TEST]) {
if (ts_test->result[GTP_NOISE_TEST] == TEST_NG) {
index += sprintf(&rbuf[index],
"<Item name=\"Diffdata Jitter Test\" result=\"NG\"/>\n");
} else {
index += sprintf(&rbuf[index],
"<Item name=\"Diffdata Jitter Test\" result=\"OK\"/>\n");
}
}
if (ts_test->item[GTP_SELFCAP_TEST]) {
if (ts_test->result[GTP_SELFCAP_TEST] == TEST_NG) {
index += sprintf(&rbuf[index],
"<Item name=\"Self Rawdata Upper Limit Test\" result=\"NG\"/>\n");
} else {
index += sprintf(&rbuf[index],
"<Item name=\"Self Rawdata Upper Limit Test\" result=\"OK\"/>\n");
}
}
if (ts_test->item[GTP_SHORT_TEST]) {
if (ts_test->result[GTP_SHORT_TEST] == TEST_NG) {
index += sprintf(&rbuf[index],
"<Item name=\"Short Test\" result=\"NG\"/>\n");
} else {
index += sprintf(&rbuf[index],
"<Item name=\"Short Test\" result=\"OK\"/>\n");
}
}
if (ts_test->item[GTP_STYLUS_RAW_TEST]) {
if (ts_test->result[GTP_STYLUS_RAW_TEST] == TEST_NG) {
index += sprintf(&rbuf[index],
"<Item name=\"Stylus Rawdata MAX/MIN Test\" result=\"NG\"/>\n");
} else {
index += sprintf(&rbuf[index],
"<Item name=\"Stylus Rawdata MAX/MIN Test\" result=\"OK\"/>\n");
}
}
index += sprintf(&rbuf[index], "</ItemList>\n");
}
//static void goodix_save_limits(void)
//{
// int tx = cd->ic_info.parm.drv_num;
// int rx = cd->ic_info.parm.sen_num;
// int i;
// int chn1;
// int chn2;
// int r;
//
// index += sprintf(&rbuf[index], "<TestItems>\n");
//
// /* save short result */
// if (ts_test->item[GTP_SHORT_TEST]) {
// index += sprintf(&rbuf[index], "<Item name=\"Short Test\">\n");
// index += sprintf(&rbuf[index], "<ShortNum>%d</ShortNum>\n",
// ts_test->short_res.short_num);
// for (i = 0; i < ts_test->short_res.short_num; i++) {
// chn1 = ts_test->short_res.short_msg[4 * i];
// chn2 = ts_test->short_res.short_msg[4 * i + 1];
// r = (ts_test->short_res.short_msg[4 * i + 2] << 8) +
// ts_test->short_res.short_msg[4 * i + 3];
// if (chn1 == CHN_VDD)
// index += sprintf(&rbuf[index],
// "<ShortMess Chn1=\"VDD\" ");
// else if (chn1 == CHN_GND)
// index += sprintf(&rbuf[index],
// "<ShortMess Chn1=\"GND\" ");
// else if (chn1 & DRV_CHANNEL_FLAG)
// index += sprintf(&rbuf[index],
// "<ShortMess Chn1=\"Tx%d\" ",
// chn1 & 0x7f);
// else
// index += sprintf(&rbuf[index],
// "<ShortMess Chn1=\"Rx%d\" ",
// chn1 & 0x7f);
// if (chn2 == CHN_VDD)
// index += sprintf(&rbuf[index],
// "Chn2=\"VDD\" ShortResistor= \"%dKom\"/>\n",
// r);
// else if (chn2 == CHN_GND)
// index += sprintf(&rbuf[index],
// "Chn2=\"GND\" ShortResistor= \"%dKom\"/>\n",
// r);
// else if (chn2 & DRV_CHANNEL_FLAG)
// index += sprintf(&rbuf[index],
// "Chn2=\"Tx%d\" ShortResistor= \"%dKom\"/>\n",
// chn2 & 0x7f, r);
// else
// index += sprintf(&rbuf[index],
// "Chn2=\"Rx%d\" ShortResistor= \"%dKom\"/>\n",
// chn2 & 0x7f, r);
// }
// index += sprintf(&rbuf[index], "</Item>\n");
// }
//
// /* save open limits */
// if (ts_test->item[GTP_CAP_TEST]) {
// index += sprintf(
// &rbuf[index], "<Item name=\"Rawdata Test Sets\">\n");
// index += sprintf(&rbuf[index],
// "<TotalFrameCnt>%d</TotalFrameCnt>\n", raw_data_cnt);
// /* rawdata max limit */
// index += sprintf(&rbuf[index], "<MaxRawLimit>\n");
// for (i = 0; i < tx * rx; i++) {
// index += sprintf(
// &rbuf[index], "%d,", ts_test->max_limits[i]);
// if ((i + 1) % tx == 0)
// index += sprintf(&rbuf[index], "\n");
// }
// index += sprintf(&rbuf[index], "</MaxRawLimit>\n");
// /* rawdata min limit */
// index += sprintf(&rbuf[index], "<MinRawLimit>\n");
// for (i = 0; i < tx * rx; i++) {
// index += sprintf(
// &rbuf[index], "%d,", ts_test->min_limits[i]);
// if ((i + 1) % tx == 0)
// index += sprintf(&rbuf[index], "\n");
// }
// index += sprintf(&rbuf[index], "</MinRawLimit>\n");
// /* Max Accord limit */
// index += sprintf(&rbuf[index], "<MaxAccordLimit>\n");
// for (i = 0; i < tx * rx; i++) {
// index += sprintf(&rbuf[index], "%d,",
// ts_test->deviation_limits[i]);
// if ((i + 1) % tx == 0)
// index += sprintf(&rbuf[index], "\n");
// }
// index += sprintf(&rbuf[index], "</MaxAccordLimit>\n");
// index += sprintf(&rbuf[index], "</Item>\n");
// }
//
// /* save noise limit */
// if (ts_test->item[GTP_NOISE_TEST]) {
// index += sprintf(
// &rbuf[index], "<Item name=\"Diffdata Test Sets\">\n");
// index += sprintf(&rbuf[index],
// "<TotalFrameCnt>%d</TotalFrameCnt>\n", noise_data_cnt);
// index += sprintf(&rbuf[index],
// "<MaxJitterLimit>%d</MaxJitterLimit>\n",
// ts_test->noise_threshold);
// index += sprintf(&rbuf[index], "</Item>\n");
// }
//
// /* save self rawdata limit */
// if (ts_test->item[GTP_SELFCAP_TEST]) {
// index += sprintf(&rbuf[index],
// "<Item name=\"Self Rawdata Test Sets\">\n");
// index += sprintf(
// &rbuf[index], "<TotalFrameCnt>1</TotalFrameCnt>\n");
// index += sprintf(&rbuf[index], "<MaxRawLimit>\n");
// for (i = 0; i < tx + rx; i++) {
// index += sprintf(&rbuf[index], "%d,",
// ts_test->self_max_limits[i]);
// if ((i + 1) % tx == 0)
// index += sprintf(&rbuf[index], "\n");
// }
// if ((tx + rx) % tx != 0)
// index += sprintf(&rbuf[index], "\n");
// index += sprintf(&rbuf[index], "</MaxRawLimit>\n");
// index += sprintf(&rbuf[index], "<MinRawLimit>\n");
// for (i = 0; i < tx + rx; i++) {
// index += sprintf(&rbuf[index], "%d,",
// ts_test->self_min_limits[i]);
// if ((i + 1) % tx == 0)
// index += sprintf(&rbuf[index], "\n");
// }
// if ((tx + rx) % tx != 0)
// index += sprintf(&rbuf[index], "\n");
// index += sprintf(&rbuf[index], "</MinRawLimit>\n");
// index += sprintf(&rbuf[index], "</Item>\n");
// }
//
// index += sprintf(&rbuf[index], "</TestItems>\n");
//}
static void goodix_data_cal(s16 *data, size_t data_size, s16 *stat_result)
{
int i = 0;
s16 avg = 0;
s16 min = 0;
s16 max = 0;
long long sum = 0;
min = data[0];
max = data[0];
for (i = 0; i < data_size; i++) {
sum += data[i];
if (max < data[i])
max = data[i];
if (min > data[i])
min = data[i];
}
avg = div_s64(sum, data_size);
stat_result[0] = avg;
stat_result[1] = max;
stat_result[2] = min;
}
static void goodix_save_data(struct goodix_ts_core *cd)
{
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
s16 stat_result[3];
int i, j;
index += sprintf(&rbuf[index], "<DataRecord>\n");
/* save rawdata */
if (ts_test->item[GTP_CAP_TEST]) {
index += sprintf(&rbuf[index], "<RawDataRecord>\n");
for (i = 0; i < raw_data_cnt; i++) {
goodix_data_cal(
ts_test->rawdata[i].data, tx * rx, stat_result);
index += sprintf(&rbuf[index],
"<DataContent No.=\"%d\" DataCount=\"%d\" Maximum=\"%d\" Minimum=\"%d\" Average=\"%d\">\n",
i, tx * rx, stat_result[1], stat_result[2],
stat_result[0]);
for (j = 0; j < tx * rx; j++) {
index += sprintf(&rbuf[index], "%5d,",
ts_test->rawdata[i].data[j]);
if ((j + 1) % tx == 0)
index += sprintf(&rbuf[index], "\n");
}
index += sprintf(&rbuf[index], "</DataContent>\n");
goodix_data_cal(ts_test->deltadata[i].data, tx * rx,
stat_result);
index += sprintf(&rbuf[index],
"<RawAccord No.=\"%d\" DataCount=\"%d\" Maximum=\"%d\" Minimum=\"%d\" Average=\"%d\">\n",
i, tx * rx, stat_result[1], stat_result[2],
stat_result[0]);
for (j = 0; j < tx * rx; j++) {
index += sprintf(&rbuf[index], "%5d,",
ts_test->deltadata[i].data[j]);
if ((j + 1) % tx == 0)
index += sprintf(&rbuf[index], "\n");
}
index += sprintf(&rbuf[index], "</RawAccord>\n");
}
index += sprintf(&rbuf[index], "</RawDataRecord>\n");
}
/* save noisedata */
if (ts_test->item[GTP_NOISE_TEST]) {
index += sprintf(&rbuf[index], "<DiffDataRecord>\n");
for (i = 0; i < noise_data_cnt; i++) {
goodix_data_cal(ts_test->noisedata[i].data, tx * rx,
stat_result);
index += sprintf(&rbuf[index],
"<DataContent No.=\"%d\" DataCount=\"%d\" Maximum=\"%d\" Minimum=\"%d\" Average=\"%d\">\n",
i, tx * rx, stat_result[1], stat_result[2],
stat_result[0]);
for (j = 0; j < tx * rx; j++) {
index += sprintf(&rbuf[index], "%5d,",
ts_test->noisedata[i].data[j]);
if ((j + 1) % tx == 0)
index += sprintf(&rbuf[index], "\n");
}
index += sprintf(&rbuf[index], "</DataContent>\n");
}
index += sprintf(&rbuf[index], "</DiffDataRecord>\n");
}
/* save self rawdata */
if (ts_test->item[GTP_SELFCAP_TEST]) {
index += sprintf(&rbuf[index], "<selfDataRecord>\n");
goodix_data_cal(
ts_test->selfrawdata.data, tx + rx, stat_result);
index += sprintf(&rbuf[index],
"<DataContent No.=\"0\" DataCount=\"%d\" Maximum=\"%d\" Minimum=\"%d\" Average=\"%d\">\n",
tx + rx, stat_result[1], stat_result[2],
stat_result[0]);
for (i = 0; i < tx + rx; i++) {
index += sprintf(&rbuf[index], "%5d,",
ts_test->selfrawdata.data[i]);
if ((i + 1) % tx == 0)
index += sprintf(&rbuf[index], "\n");
}
if ((tx + rx) % tx != 0)
index += sprintf(&rbuf[index], "\n");
index += sprintf(&rbuf[index], "</DataContent>\n");
index += sprintf(&rbuf[index], "</selfDataRecord>\n");
}
/* save stylus rawdata */
if (ts_test->item[GTP_STYLUS_RAW_TEST]) {
index += sprintf(&rbuf[index], "<StylusRawDataRecord>\n");
for (i = 0; i < raw_data_cnt; i++) {
goodix_data_cal(ts_test->stylusraw[i].data, tx + rx, stat_result);
index += sprintf(&rbuf[index],
"<DataContent No.=\"%d\" DataCount=\"%d\" Maximum=\"%d\" Minimum=\"%d\" Average=\"%d\">\n",
i, tx + rx, stat_result[1], stat_result[2], stat_result[0]);
for (j = 0; j < tx + rx; j++) {
index += sprintf(&rbuf[index], "%5d,",
ts_test->stylusraw[i].data[j]);
if ((j + 1) % tx == 0)
index += sprintf(&rbuf[index], "\n");
}
if ((tx + rx) % tx != 0)
index += sprintf(&rbuf[index], "\n");
index += sprintf(&rbuf[index], "</DataContent>\n");
}
index += sprintf(&rbuf[index], "</StylusRawDataRecord>\n");
}
index += sprintf(&rbuf[index], "</DataRecord>\n");
}
static void goodix_save_tail(struct goodix_ts_core *cd)
{
index += sprintf(&rbuf[index], "</TESTLOG>\n");
}
static void goodix_save_test_result(struct goodix_ts_core *cd, bool is_brief)
{
if (is_brief) {
if (ts_test->item[GTP_CAP_TEST]) {
index += sprintf(&rbuf[index], "Open test:\n");
index += sprintf(&rbuf[index], "%s\n",
ts_test->result[GTP_CAP_TEST] ? "PASS"
: "FAIL");
}
if (ts_test->item[GTP_DELTA_TEST]) {
index += sprintf(&rbuf[index], "Delta test:\n");
index += sprintf(&rbuf[index], "%s\n",
ts_test->result[GTP_DELTA_TEST] ? "PASS"
: "FAIL");
}
if (ts_test->item[GTP_SHORT_TEST]) {
index += sprintf(&rbuf[index], "Short test:\n");
index += sprintf(&rbuf[index], "%s\n",
ts_test->result[GTP_SHORT_TEST] ? "PASS"
: "FAIL");
}
if (ts_test->item[GTP_NOISE_TEST]) {
index += sprintf(&rbuf[index], "Noise test:\n");
index += sprintf(&rbuf[index], "%s\n",
ts_test->result[GTP_NOISE_TEST] ? "PASS"
: "FAIL");
}
if (ts_test->item[GTP_SELFCAP_TEST]) {
index += sprintf(&rbuf[index], "Self test:\n");
index += sprintf(&rbuf[index], "%s\n",
ts_test->result[GTP_SELFCAP_TEST] ? "PASS"
: "FAIL");
}
} else {
goodix_save_header(cd);
goodix_save_data(cd);
goodix_save_tail(cd);
}
}
static void goto_next_line(char **ptr)
{
do {
*ptr = *ptr + 1;
} while (**ptr != '\n' && **ptr != '\0');
if (**ptr == '\0') {
return;
}
*ptr = *ptr + 1;
}
static void copy_this_line(char *dest, char *src)
{
char *copy_from;
char *copy_to;
copy_from = src;
copy_to = dest;
do {
*copy_to = *copy_from;
copy_from++;
copy_to++;
} while ((*copy_from != '\n') && (*copy_from != '\r') &&
(*copy_from != '\0'));
*copy_to = '\0';
}
static int getrid_space(s8 *data, s32 len)
{
u8 *buf = NULL;
s32 i;
u32 count = 0;
buf = (char *)kzalloc(len + 5, GFP_KERNEL);
if (buf == NULL) {
ts_err("get space kzalloc error");
return -ESRCH;
}
for (i = 0; i < len; i++) {
if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n') {
continue;
}
buf[count++] = data[i];
}
buf[count++] = '\0';
memcpy(data, buf, count);
kfree(buf);
return count;
}
static int parse_valid_data(
char *buf_start, loff_t buf_size, char *ptr, s16 *data, s32 rows)
{
int i = 0;
int j = 0;
char *token = NULL;
char *tok_ptr = NULL;
char *row_data = NULL;
long temp_val;
if (!ptr || !data)
return -EINVAL;
row_data = (char *)kzalloc(MAX_LINE_LEN, GFP_KERNEL);
if (!row_data) {
ts_err("alloc index %d failed.", MAX_LINE_LEN);
return -ENOMEM;
}
for (i = 0; i < rows; i++) {
memset(row_data, 0, MAX_LINE_LEN);
copy_this_line(row_data, ptr);
getrid_space(row_data, strlen(row_data));
tok_ptr = row_data;
while ((token = strsep(&tok_ptr, ","))) {
if (strlen(token) == 0)
continue;
if (kstrtol(token, 0, &temp_val)) {
kfree(row_data);
return -EINVAL;
}
data[j++] = (s16)temp_val;
}
if (i == rows - 1)
break;
goto_next_line(&ptr); // next row
if (!ptr || (0 == strlen(ptr)) ||
(ptr >= (buf_start + buf_size))) {
ts_info("invalid ptr, return");
kfree(row_data);
row_data = NULL;
return -EPERM;
}
}
kfree(row_data);
return j;
}
static int parse_csvfile(
char *buf, size_t size, char *target_name, s16 *data, s32 rows, s32 col)
{
char *ptr = NULL;
if (!data || !buf)
return -EIO;
ptr = buf;
ptr = strstr(ptr, target_name);
if (!ptr) {
ts_info("load %s failed 1, maybe not this item", target_name);
return -EINTR;
}
goto_next_line(&ptr);
if (!ptr || (0 == strlen(ptr))) {
ts_err("load %s failed 2!", target_name);
return -EIO;
}
return parse_valid_data(buf, size, ptr, data, rows);
}
static int goodix_obtain_testlimits(struct goodix_ts_core *cd)
{
const struct firmware *firmware = NULL;
struct device *dev = &cd->pdev->dev;
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
char *limit_file = cd->board_data.test_limits_name;
char *temp_buf = NULL;
char *raw_limit_min = CSV_TP_SPECIAL_RAW_MIN;
char *raw_limit_max = CSV_TP_SPECIAL_RAW_MAX;
char *short_threshold = CSV_TP_SHORT_THRESHOLD;
s16 data_buf[7];
int ret;
ts_info("limit_file_name:%s", limit_file);
ret = request_firmware(&firmware, limit_file, dev);
if (ret < 0) {
ts_err("limits file [%s] not available", limit_file);
return -EINVAL;
}
if (firmware->size <= 0) {
ts_err("request_firmware, limits param length error,len:%zu",
firmware->size);
ret = -EINVAL;
goto exit_free;
}
temp_buf = kzalloc(firmware->size + 1, GFP_KERNEL);
if (!temp_buf) {
ret = -ENOMEM;
goto exit_free;
}
memcpy(temp_buf, firmware->data, firmware->size);
if (ts_test->item[GTP_CAP_TEST]) {
if (ts_test->freq > 0) {
raw_limit_min = CSV_TP_SPECIAL_FREQ_RAW_MIN;
raw_limit_max = CSV_IP_SPECIAL_FREQ_RAW_MAX;
} else if (ts_test->is_ical) {
raw_limit_min = CSV_TP_ICAL_RAW_MIN;
raw_limit_max = CSV_TP_ICAL_RAW_MAX;
}
/* obtain mutual_raw min */
ret = parse_csvfile(temp_buf, firmware->size, raw_limit_min,
ts_test->min_limits, rx, tx);
if (ret < 0) {
ts_err("Failed get min_limits");
goto exit_free;
}
/* obtain mutual_raw max */
ret = parse_csvfile(temp_buf, firmware->size, raw_limit_max,
ts_test->max_limits, rx, tx);
if (ret < 0) {
ts_err("Failed get max_limits");
goto exit_free;
}
/* obtain delta limit */
ret = parse_csvfile(temp_buf, firmware->size,
CSV_TP_SPECIAL_RAW_DELTA, ts_test->deviation_limits, rx,
tx);
if (ret < 0) {
ts_err("Failed get delta limit");
goto exit_free;
}
}
if (ts_test->item[GTP_SELFCAP_TEST]) {
/* obtain self_raw min */
ret = parse_csvfile(temp_buf, firmware->size,
CSV_TP_SPECIAL_SELFRAW_MIN, ts_test->self_min_limits, 1,
tx + rx);
if (ret < 0) {
ts_err("Failed get self_min_limits");
goto exit_free;
}
/* obtain self_raw max */
ret = parse_csvfile(temp_buf, firmware->size,
CSV_TP_SPECIAL_SELFRAW_MAX, ts_test->self_max_limits, 1,
tx + rx);
if (ret < 0) {
ts_err("Failed get self_min_limits");
goto exit_free;
}
}
if (ts_test->item[GTP_NOISE_TEST]) {
/* obtain noise_threshold */
ret = parse_csvfile(temp_buf, firmware->size,
CSV_TP_NOISE_LIMIT, &ts_test->noise_threshold, 1, 1);
if (ret < 0) {
ts_err("Failed get noise limits");
goto exit_free;
}
}
if (ts_test->item[GTP_SHORT_TEST]) {
if (ts_test->is_ical)
short_threshold = CSV_TP_ICAL_SHORT_THRESHOLD;
/* obtain short_params */
ret = parse_csvfile(temp_buf, firmware->size,
short_threshold, data_buf, 1, 7);
if (ret < 0) {
ts_err("Failed get short circuit limits");
goto exit_free;
}
ts_test->short_threshold = data_buf[0];
ts_test->r_drv_drv_threshold = data_buf[1];
ts_test->r_drv_sen_threshold = data_buf[2];
ts_test->r_sen_sen_threshold = data_buf[3];
ts_test->r_drv_gnd_threshold = data_buf[4];
ts_test->r_sen_gnd_threshold = data_buf[5];
ts_test->avdd_value = data_buf[6];
if (cd->bus->ic_type == IC_TYPE_BERLIN_B)
ts_test->params_info = &params_brb;
else if (cd->bus->ic_type == IC_TYPE_BERLIN_D)
ts_test->params_info = &params_brd;
else if (cd->bus->ic_type == IC_TYPE_NOTTINGHAM)
ts_test->params_info = &params_not;
}
if (ts_test->item[GTP_STYLUS_RAW_TEST]) {
if (ts_test->stylus_test_freq > 0) {
raw_limit_min = CSV_TP_SPECIAL_FREQ_STYLUSRAW_MIN;
raw_limit_max = CSV_TP_SPECIAL_FREQ_STYLUSRAW_MAX;
} else {
raw_limit_min = CSV_TP_SPECIAL_STYLUSRAW_MIN;
raw_limit_max = CSV_TP_SPECIAL_STYLUSRAW_MAX;
}
ret = parse_csvfile(temp_buf, firmware->size, raw_limit_min,
ts_test->stylus_min_limits, 1, tx + rx);
if (ret < 0) {
ts_err("Failed get %s", raw_limit_min);
goto exit_free;
}
ret = parse_csvfile(temp_buf, firmware->size, raw_limit_max,
ts_test->stylus_max_limits, 1, tx + rx);
if (ret < 0) {
ts_err("Failed get %s", raw_limit_max);
goto exit_free;
}
}
exit_free:
kfree(temp_buf);
if (firmware)
release_firmware(firmware);
return ret;
}
static int goodix_delta_test(struct goodix_ts_core *cd)
{
int i, j;
int max_val;
int raw;
int temp;
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
u32 data_size = tx * rx;
int ret = 0;
for (i = 0; i < raw_data_cnt; i++) {
for (j = 0; j < data_size; j++) {
raw = ts_test->rawdata[i].data[j];
max_val = 0;
/* calcu delta with above node */
if (j - tx >= 0) {
temp = ts_test->rawdata[i].data[j - tx];
temp = ABS(temp - raw);
max_val = MAX(max_val, temp);
}
/* calcu delta with bellow node */
if (j + tx < data_size) {
temp = ts_test->rawdata[i].data[j + tx];
temp = ABS(temp - raw);
max_val = MAX(max_val, temp);
}
/* calcu delta with left node */
if (j % tx) {
temp = ts_test->rawdata[i].data[j - 1];
temp = ABS(temp - raw);
max_val = MAX(max_val, temp);
}
/* calcu delta with right node */
if ((j + 1) % tx) {
temp = ts_test->rawdata[i].data[j + 1];
temp = ABS(temp - raw);
max_val = MAX(max_val, temp);
}
temp = max_val * 1000 / raw;
ts_test->deltadata[i].data[j] = temp;
if (temp > ts_test->deviation_limits[j]) {
// ts_err("delta_data[%d] > limits[%d]",
// temp, ts_test->deviation_limits[j]);
ret = -EINVAL;
}
}
}
return ret;
}
static void print_open_test_ng_data(
struct goodix_ts_core *cd, int index, s16 *data)
{
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
char *output_buf;
int buf_size = (MAX(tx, rx) + 1) * 6;
u32 self_raw_addr;
s16 self_raw_data[MAX_DRV_NUM + MAX_SEN_NUM];
int i, j;
int cnt = 0;
self_raw_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len +
cd->ic_info.misc.fw_log_len +
cd->ic_info.misc.mutual_struct_len + 10;
output_buf = malloc_proc_buffer(buf_size);
if (output_buf == NULL) {
ts_err("failed to alloc output buffer");
return;
}
ts_info("NG Rawdata[%d]:", index);
for (i = 0; i < rx; i++) {
cnt = 0;
memset(output_buf, 0, buf_size);
for (j = 0; j < tx; j++)
cnt += scnprintf(
&output_buf[cnt], buf_size - cnt, "%5d,", data[i * tx + j]);
ts_info("%s", output_buf);
}
cd->hw_ops->read(cd, self_raw_addr, (unsigned char *)self_raw_data,
(tx + rx) * 2);
ts_info("self Tx Raw:");
cnt = 0;
memset(output_buf, 0, buf_size);
for (i = 0; i < tx; i++)
cnt += scnprintf(&output_buf[cnt], buf_size - cnt, "%d,",
self_raw_data[i]);
ts_info("%s", output_buf);
ts_info("self Rx Raw:");
cnt = 0;
memset(output_buf, 0, buf_size);
for (i = tx; i < tx + rx; i++)
cnt += scnprintf(&output_buf[cnt], buf_size - cnt, "%d,",
self_raw_data[i]);
ts_info("%s", output_buf);
free_proc_buffer(&output_buf);
}
static void print_self_compensation(struct goodix_ts_core *cd)
{
u8 *cfg;
u8 *cfg_buf;
int len;
int cfg_num;
int sub_cfg_index;
int sub_cfg_len;
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
char *output_buf;
int buf_size = (MAX(tx, rx) + 1) * 6;
int cnt;
s16 val;
int i, j;
cfg_buf = kzalloc(GOODIX_CFG_MAX_SIZE, GFP_KERNEL);
if (cfg_buf == NULL) {
ts_err("failed to alloc cfg buffer");
return;
}
output_buf = malloc_proc_buffer(buf_size);
if (output_buf == NULL) {
ts_err("failed to alloc output buffer");
goto exit_free_cfg_buf;
}
if (cd->bus->ic_type == IC_TYPE_BERLIN_B) {
cd->hw_ops->read(cd, cd->ic_info.misc.auto_scan_cmd_addr,
cfg_buf, (tx + rx) * 2);
ts_info("self Tx compensation:");
cnt = 0;
memset(output_buf, 0, buf_size);
for (i = 0; i < tx; i++) {
val = le16_to_cpup((__le16 *)&cfg_buf[i * 2]);
cnt += scnprintf(&output_buf[cnt], buf_size - cnt, "%d,", val);
}
ts_info("%s", output_buf);
cnt = 0;
memset(output_buf, 0, buf_size);
ts_info("self Tx compensation:");
for (i = 0; i < rx; i++) {
val = le16_to_cpup((__le16 *)&cfg_buf[tx * 2 + i * 2]);
cnt += scnprintf(&output_buf[cnt], buf_size - cnt, "%d,", val);
}
ts_info("%s", output_buf);
goto exit_free;
}
len = cd->hw_ops->read_config(cd, cfg_buf, GOODIX_CFG_MAX_SIZE);
if (len < 0) {
ts_err("read config failed");
goto exit_free;
}
cfg = cfg_buf;
cfg_num = cfg[61];
cfg += 64;
for (i = 0; i < cfg_num; i++) {
sub_cfg_len = cfg[0] - 2;
sub_cfg_index = cfg[1];
if (sub_cfg_index == 13) { // TX self cancel
ts_info("self Tx compensation:");
cnt = 0;
memset(output_buf, 0, buf_size);
for (j = 0; j < tx; j++) {
val = le16_to_cpup((__le16 *)&cfg[2 + j * 4]) +
le16_to_cpup((__le16 *)&cfg[4 + j * 4]);
cnt += scnprintf(&output_buf[cnt], buf_size - cnt, "%d,", val);
}
ts_info("%s", output_buf);
} else if (sub_cfg_index == 14) { // RX self cancel
cnt = 0;
memset(output_buf, 0, buf_size);
ts_info("self Tx compensation:");
for (j = 0; j < rx; j++) {
val = le16_to_cpup((__le16 *)&cfg[2 + j * 2]);
cnt += scnprintf(&output_buf[cnt], buf_size - cnt, "%d,", val);
}
ts_info("%s", output_buf);
}
cfg += (sub_cfg_len + 2);
}
exit_free:
free_proc_buffer(&output_buf);
exit_free_cfg_buf:
kfree(cfg_buf);
}
static int goodix_open_test(struct goodix_ts_core *cd)
{
u8 *tmp_buf;
struct goodix_ts_cmd temp_cmd = {0};
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
u32 sync_addr = cd->ic_info.misc.frame_data_addr;
u32 raw_addr;
int ret;
int err_cnt = 0;
int i, j;
s16 tmp_val;
u16 tmp_freq;
u8 val;
int retry;
bool is_ng;
raw_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len + cd->ic_info.misc.fw_log_len +
8;
tmp_buf = kzalloc(GOODIX_MAX_FRAMEDATA_LEN, GFP_KERNEL);
if (tmp_buf == NULL)
return -ENOMEM;
/* open test prepare */
temp_cmd.cmd = 0x90;
temp_cmd.data[0] = 0x84;
temp_cmd.len = 5;
if (cd->bus->sub_ic_type == IC_TYPE_SUB_GT7986) {
raw_addr = cd->ic_info.misc.mutual_rawdata_addr;
sync_addr = cd->ic_info.misc.touch_data_addr;
temp_cmd.cmd = 0x01;
temp_cmd.len = 4;
}
ret = cd->hw_ops->send_cmd(cd, &temp_cmd);
if (ret < 0) {
ts_err("send rawdata cmd failed");
goto exit;
}
/* switch freq */
if (ts_test->freq > 0) {
ts_info("set freq %d", ts_test->freq);
tmp_freq = ts_test->freq / 61;
temp_cmd.len = 6;
temp_cmd.cmd = 0xB1;
temp_cmd.data[0] = tmp_freq & 0xFF;
temp_cmd.data[1] = (tmp_freq >> 8) & 0xFF;
ret = cd->hw_ops->send_cmd(cd, &temp_cmd);
if (ret < 0) {
ts_err("set freq %d failed", ts_test->freq);
goto exit;
}
}
/* discard the first few frames */
for (i = 0; i < DISCARD_FRAME_NUMS; i++) {
val = 0;
cd->hw_ops->write(cd, sync_addr, &val, 1);
usleep_range(20000, 21000);
}
/* read rawdata */
for (i = 0; i < raw_data_cnt; i++) {
val = 0;
cd->hw_ops->write(cd, sync_addr, &val, 1);
retry = 20;
while (retry--) {
usleep_range(5000, 5100);
cd->hw_ops->read(cd, sync_addr, &val, 1);
if (val & 0x80)
break;
}
if (retry < 0) {
ts_err("rawdata is not ready val:0x%02x i:%d, exit",
val, i);
ret = -EAGAIN;
goto exit;
}
cd->hw_ops->read(cd, raw_addr, tmp_buf, tx * rx * 2);
goodix_rotate_abcd2cbad(tx, rx, (s16 *)tmp_buf, NULL);
memcpy((u8 *)ts_test->rawdata[i].data, tmp_buf, tx * rx * 2);
is_ng = false;
for (j = 0; j < tx * rx; j++) {
tmp_val = ts_test->rawdata[i].data[j];
if (tmp_val > ts_test->max_limits[j] ||
tmp_val < ts_test->min_limits[j]) {
err_cnt++;
is_ng = true;
}
}
if (is_ng)
print_open_test_ng_data(
cd, i, ts_test->rawdata[i].data);
}
if (err_cnt == 0) {
ts_test->result[GTP_CAP_TEST] = TEST_OK;
} else {
ret = -EINVAL;
ts_test->result[GTP_CAP_TEST] = TEST_NG;
print_self_compensation(cd);
}
if (goodix_delta_test(cd) == 0)
ts_test->result[GTP_DELTA_TEST] = TEST_OK;
exit:
kfree(tmp_buf);
return ret;
}
#define SELF_RAWDATA_NG_RETRY 3
static int goodix_self_open_test(struct goodix_ts_core *cd)
{
u8 *tmp_buf;
struct goodix_ts_cmd temp_cmd = {0};
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
u32 sync_addr = cd->ic_info.misc.frame_data_addr;
u32 raw_addr;
int ret;
int j;
s16 tmp_val;
u8 val;
int retry;
int ng_rty = SELF_RAWDATA_NG_RETRY;
raw_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len + cd->ic_info.misc.fw_log_len +
cd->ic_info.misc.mutual_struct_len + 10;
tmp_buf = kzalloc(GOODIX_MAX_FRAMEDATA_LEN, GFP_KERNEL);
if (tmp_buf == NULL)
return -ENOMEM;
restart:
/* test prepare */
temp_cmd.cmd = 0x90;
temp_cmd.data[0] = 0x84;
temp_cmd.len = 5;
if (cd->bus->sub_ic_type == IC_TYPE_SUB_GT7986) {
raw_addr = cd->ic_info.misc.self_rawdata_addr;
sync_addr = cd->ic_info.misc.touch_data_addr;
temp_cmd.cmd = 0x01;
temp_cmd.len = 4;
}
ret = cd->hw_ops->send_cmd(cd, &temp_cmd);
if (ret < 0) {
ts_err("send rawdata cmd failed");
goto exit;
}
/* discard the first few frames */
for (j = 0; j < DISCARD_FRAME_NUMS; j++) {
val = 0;
cd->hw_ops->write(cd, sync_addr, &val, 1);
usleep_range(20000, 21000);
}
/* read self rawdata */
val = 0;
cd->hw_ops->write(cd, sync_addr, &val, 1);
retry = 20;
while (retry--) {
usleep_range(5000, 5100);
cd->hw_ops->read(cd, sync_addr, &val, 1);
if (val & 0x80)
break;
}
if (retry < 0) {
ts_err("self rawdata is not ready val:0x%02x, exit", val);
ret = -EAGAIN;
goto exit;
}
cd->hw_ops->read(cd, raw_addr, tmp_buf, (tx + rx) * 2);
memcpy((u8 *)ts_test->selfrawdata.data, tmp_buf, (tx + rx) * 2);
/* analysis results */
ts_test->result[GTP_SELFCAP_TEST] = TEST_OK;
for (j = 0; j < tx + rx; j++) {
tmp_val = ts_test->selfrawdata.data[j];
if (tmp_val > ts_test->self_max_limits[j] ||
tmp_val < ts_test->self_min_limits[j]) {
// ts_err("self_rawdata[%d] out of range[%d %d]",
// tmp_val, ts_test->self_min_limits[j],
// ts_test->self_max_limits[j]);
ts_test->result[GTP_SELFCAP_TEST] = TEST_NG;
ret = -EINVAL;
}
}
if (ts_test->result[GTP_SELFCAP_TEST] == TEST_NG && ng_rty > 0) {
ng_rty--;
cd->hw_ops->reset(cd, goodix_get_normal_reset_delay(cd));
ts_info("self rawdata out of limit, retry:%d",
SELF_RAWDATA_NG_RETRY - ng_rty);
goto restart;
}
exit:
kfree(tmp_buf);
return ret;
}
static int goodix_noise_test(struct goodix_ts_core *cd)
{
u8 *tmp_buf = NULL;
struct goodix_ts_cmd temp_cmd = {0};
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
u32 sync_addr = cd->ic_info.misc.frame_data_addr;
u32 raw_addr;
int ret = 0;
int i, j;
s16 tmp_val;
u8 val;
int retry;
raw_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len + cd->ic_info.misc.fw_log_len +
8;
tmp_buf = kzalloc(GOODIX_MAX_FRAMEDATA_LEN, GFP_KERNEL);
if (tmp_buf == NULL)
return -ENOMEM;
/* open test prepare */
temp_cmd.cmd = 0x90;
temp_cmd.data[0] = 0x82;
temp_cmd.len = 5;
if (cd->bus->sub_ic_type == IC_TYPE_SUB_GT7986) {
raw_addr = cd->ic_info.misc.mutual_diffdata_addr;
sync_addr = cd->ic_info.misc.touch_data_addr;
temp_cmd.cmd = 0x01;
temp_cmd.len = 4;
}
ret = cd->hw_ops->send_cmd(cd, &temp_cmd);
if (ret < 0) {
ts_err("send rawdata cmd failed");
goto exit;
}
/* discard the first few frames */
for (i = 0; i < DISCARD_FRAME_NUMS; i++) {
val = 0;
cd->hw_ops->write(cd, sync_addr, &val, 1);
usleep_range(20000, 21000);
}
/* read noisedata */
for (i = 0; i < noise_data_cnt; i++) {
val = 0;
cd->hw_ops->write(cd, sync_addr, &val, 1);
retry = 20;
while (retry--) {
usleep_range(5000, 5100);
cd->hw_ops->read(cd, sync_addr, &val, 1);
if (val & 0x80)
break;
}
if (retry < 0) {
ts_err("noisedata is not ready val:0x%02x i:%d, exit",
val, i);
ret = -EAGAIN;
goto exit;
}
cd->hw_ops->read(cd, raw_addr, tmp_buf, tx * rx * 2);
goodix_rotate_abcd2cbad(tx, rx, (s16 *)tmp_buf, NULL);
memcpy((u8 *)ts_test->noisedata[i].data, tmp_buf, tx * rx * 2);
}
/* analysis results */
ts_test->result[GTP_NOISE_TEST] = TEST_OK;
for (i = 0; i < noise_data_cnt; i++) {
for (j = 0; j < tx * rx; j++) {
tmp_val = ts_test->noisedata[i].data[j];
tmp_val = ABS(tmp_val);
if (tmp_val > ts_test->noise_threshold) {
// ts_err("noise data[%d] > noise
// threshold[%d]",
// tmp_val, ts_test->noise_threshold);
ts_test->result[GTP_NOISE_TEST] = TEST_NG;
ret = -EINVAL;
}
}
}
exit:
kfree(tmp_buf);
return ret;
}
static int goodix_stylus_rawdata_test(struct goodix_ts_core *cd)
{
struct goodix_ts_cmd temp_cmd = {0};
int tmp_freq = ts_test->stylus_test_freq / 61;
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
u32 sync_addr = cd->ic_info.misc.frame_data_addr;
struct goodix_frame_head frame_head;
u32 raw_addr;
int retry;
int ret;
int i, j;
int tmp_val;
int err_cnt = 0;
u8 val;
raw_data_cnt = 16;
rbuf = malloc_proc_buffer(HUGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
ret = -ENOMEM;
goto exit;
}
raw_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len +
cd->ic_info.misc.fw_log_len + 28;
temp_cmd.len = 7;
temp_cmd.cmd = 0x65;
temp_cmd.data[0] = 1;
temp_cmd.data[1] = tmp_freq & 0xFF;
temp_cmd.data[2] = (tmp_freq >> 8) & 0xFF;
ret = cd->hw_ops->send_cmd(cd, &temp_cmd);
if (ret < 0) {
ts_err("send stylus test cmd failed");
goto exit;
}
temp_cmd.len = 5;
temp_cmd.cmd = 0x90;
temp_cmd.data[0] = 0x81;
ret = cd->hw_ops->send_cmd(cd, &temp_cmd);
if (ret < 0) {
ts_err("send rawdata cmd failed");
goto exit;
}
/* discard the first few frames */
for (i = 0; i < DISCARD_FRAME_NUMS; i++) {
val = 0;
cd->hw_ops->write(cd, sync_addr, &val, 1);
usleep_range(20000, 21000);
}
for (i = 0; i < raw_data_cnt; i++) {
val = 0;
cd->hw_ops->write(cd, sync_addr, &val, 1);
retry = 20;
while (retry--) {
usleep_range(5000, 5100);
cd->hw_ops->read(cd, sync_addr, (u8 *)&frame_head, sizeof(frame_head));
if (frame_head.sync & 0x80)
break;
}
if (retry < 0) {
ts_err("rawdata is not ready val:0x%02x i:%d, exit", frame_head.sync, i);
ret = -EINVAL;
goto exit;
}
if (!(frame_head.data_en & STYLUS_PACK_EN)) {
ts_err("frame has no stylus pack data");
ret = -EINVAL;
goto exit;
}
cd->hw_ops->read(cd, raw_addr, (u8 *)ts_test->stylusraw[i].data, (tx + rx) * 2);
}
/* analysis results */
for (i = 0; i < raw_data_cnt; i++) {
for (j = 0; j < tx + rx; j++) {
tmp_val = ts_test->stylusraw[i].data[j];
if (tmp_val > ts_test->stylus_max_limits[j] ||
tmp_val < ts_test->stylus_min_limits[j]) {
ts_err("stylusraw[%d] out of range[%d %d]",
tmp_val, ts_test->stylus_min_limits[j],
ts_test->stylus_max_limits[j]);
err_cnt++;
}
}
}
if (err_cnt > 0) {
ret = -EINVAL;
} else {
ret = 0;
ts_test->result[GTP_STYLUS_RAW_TEST] = TEST_OK;
}
exit:
return ret;
}
#pragma pack(1)
struct clk_test_parm {
union {
struct {
u8 gio;
u8 div:2; /* 0:no div 1:8 div */
u8 is_64m:1; /* 0: not 64M osc 1:64M osc */
u8 en:1;
u8 pll_prediv:2;
u8 pll_fbdiv:2;
u8 trigger_mode; /* 0:rising 1:high 2:falling 3:low */
u16 clk_in_num; /* collect clk num (1-1022) */
u16 checksum;
};
u8 buf[7];
};
};
#pragma pack()
static int goodix_auto_test(struct goodix_ts_core *cd, bool is_brief)
{
struct goodix_ts_cmd temp_cmd = {0};
int ret;
int i;
ret = goodix_obtain_testlimits(cd);
if (ret < 0) {
ts_err("obtain open test limits failed");
return ret;
}
cd->hw_ops->irq_enable(cd, false);
goodix_ts_esd_off(cd);
temp_cmd.len = 5;
temp_cmd.cmd = 0x64;
temp_cmd.data[0] = 1;
if (ts_test->item[GTP_CAP_TEST]) {
for (i = 0; i < 3; i++) {
cd->hw_ops->send_cmd(cd, &temp_cmd);
ret = goodix_open_test(cd);
cd->hw_ops->reset(cd, goodix_get_normal_reset_delay(cd));
if (ret != -EAGAIN)
break;
}
}
if (ts_test->item[GTP_NOISE_TEST]) {
for (i = 0; i < 3; i++) {
if (!cd->board_data.noise_test_disable_cmd)
cd->hw_ops->send_cmd(cd, &temp_cmd);
ret = goodix_noise_test(cd);
cd->hw_ops->reset(cd, goodix_get_normal_reset_delay(cd));
if (ret != -EAGAIN)
break;
}
}
if (ts_test->item[GTP_SELFCAP_TEST]) {
for (i = 0; i < 3; i++) {
ret = goodix_self_open_test(cd);
cd->hw_ops->reset(cd, goodix_get_normal_reset_delay(cd));
if (ret != -EAGAIN)
break;
}
}
if (ts_test->item[GTP_SHORT_TEST]) {
goodix_shortcircut_test(cd);
cd->hw_ops->reset(cd, goodix_get_normal_reset_delay(cd));
}
if (ts_test->item[GTP_STYLUS_RAW_TEST]) {
ret = cd->hw_ops->send_cmd(cd, &temp_cmd);
if (ret < 0)
ts_err("enter test mode failed");
goodix_stylus_rawdata_test(cd);
cd->hw_ops->reset(cd, goodix_get_normal_reset_delay(cd));
}
cd->hw_ops->irq_enable(cd, true);
goodix_ts_esd_on(cd);
goodix_save_test_result(cd, is_brief);
return 0;
}
static void goodix_auto_noise_test(struct goodix_ts_core *cd, u16 cnt, int threshold)
{
struct goodix_ts_cmd temp_cmd = {0};
u32 sync_addr = cd->ic_info.misc.frame_data_addr;
u32 raw_addr;
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
s16 *tmp_buf;
int tmp_val;
u8 status;
int test_try = 2;
int ret = 0;
int retry = 20;
int err_cnt = 0;
int i;
tmp_buf = kcalloc(MAX_DRV_NUM * MAX_SEN_NUM, 2, GFP_KERNEL);
if (tmp_buf == NULL)
return;
raw_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len + cd->ic_info.misc.fw_log_len +
8;
cd->hw_ops->irq_enable(cd, false);
goodix_ts_esd_off(cd);
restart:
temp_cmd.len = 0x07;
temp_cmd.cmd = 0x90;
if (cd->bus->ic_type == IC_TYPE_BERLIN_B)
temp_cmd.data[0] = 0x87;
else
temp_cmd.data[0] = 0x86;
temp_cmd.data[1] = cnt & 0xFF;
temp_cmd.data[2] = (cnt >> 8) & 0xFF;
cd->hw_ops->send_cmd(cd, &temp_cmd);
msleep(cnt * 8);
while (retry--) {
cd->hw_ops->read(cd, sync_addr, &status, 1);
if (status & 0x80)
break;
usleep_range(5000, 5100);
}
if (retry < 0) {
ts_err("noise data not ready, status[%x]", status);
ret = -EINVAL;
goto exit;
}
cd->hw_ops->read(cd, raw_addr, (u8 *)tmp_buf, tx * rx * 2);
goodix_rotate_abcd2cbad(tx, rx, tmp_buf, NULL);
index += sprintf(&rbuf[index], "max:\n");
for (i = 0; i < tx * rx; i++) {
tmp_val = tmp_buf[i];
index += sprintf(&rbuf[index], "%3d,", tmp_val);
if ((i + 1) % tx == 0)
index += sprintf(&rbuf[index], "\n");
if (ABS(tmp_val) > threshold)
err_cnt++;
}
status = 0;
cd->hw_ops->write(cd, sync_addr, &status, 1);
retry = 10;
while (retry--) {
cd->hw_ops->read(cd, sync_addr, &status, 1);
if (status & 0x80)
break;
usleep_range(5000, 5100);
}
if (retry < 0) {
ts_err("noise data not ready, status[%x]", status);
ret = -EINVAL;
goto exit;
}
cd->hw_ops->read(cd, raw_addr, (u8 *)tmp_buf, tx * rx * 2);
goodix_rotate_abcd2cbad(tx, rx, tmp_buf, NULL);
index += sprintf(&rbuf[index], "min:\n");
for (i = 0; i < tx * rx; i++) {
tmp_val = tmp_buf[i];
index += sprintf(&rbuf[index], "%3d,", tmp_val);
if ((i + 1) % tx == 0)
index += sprintf(&rbuf[index], "\n");
if (ABS(tmp_val) > threshold)
err_cnt++;
}
if (err_cnt > 0)
index += sprintf(&rbuf[index], "Result: FAIL\n");
else
index += sprintf(&rbuf[index], "Result: PASS\n");
exit:
cd->hw_ops->reset(cd, goodix_get_normal_reset_delay(cd));
if (ret < 0 && --test_try > 0) {
ts_err("auto noise running failed, retry:%d", test_try);
ret = 0;
index = 0;
goto restart;
}
kfree(tmp_buf);
cd->hw_ops->irq_enable(cd, true);
goodix_ts_esd_on(cd);
}
static int get_cap_data(struct goodix_ts_core *cd, uint8_t *type)
{
struct goodix_ts_cmd temp_cmd = {0};
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
u8 val;
int retry = 40;
u8 *frame_buf;
u32 flag_addr = cd->ic_info.misc.frame_data_addr;
u32 mutual_addr;
u32 self_addr;
u32 tx_freq_addr;
int i;
int ret;
mutual_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len +
cd->ic_info.misc.fw_log_len + 8;
self_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len + cd->ic_info.misc.fw_log_len +
cd->ic_info.misc.mutual_struct_len + 10;
tx_freq_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len +
cd->ic_info.misc.fw_log_len + 2;
frame_buf = kzalloc(GOODIX_MAX_FRAMEDATA_LEN, GFP_KERNEL);
if (frame_buf == NULL)
return -ENOMEM;
/* disable irq & close esd */
cd->hw_ops->irq_enable(cd, false);
goodix_ts_esd_off(cd);
if (strstr(type, CMD_GET_BASEDATA) ||
strstr(type, CMD_GET_SELF_BASEDATA)) {
temp_cmd.data[0] = 0x83;
} else if (strstr(type, CMD_GET_RAWDATA) ||
strstr(type, CMD_GET_SELF_RAWDATA) ||
strstr(type, CMD_GET_TX_FREQ)) {
temp_cmd.data[0] = 0x81;
} else {
temp_cmd.data[0] = 0x82;
}
temp_cmd.cmd = 0x90;
temp_cmd.len = 5;
if (cd->bus->sub_ic_type == IC_TYPE_SUB_GT7986) {
if (strstr(type, CMD_GET_BASEDATA))
mutual_addr = cd->ic_info.misc.mutual_refdata_addr;
else if (strstr(type, CMD_GET_RAWDATA))
mutual_addr = cd->ic_info.misc.mutual_rawdata_addr;
else if (strstr(type, CMD_GET_DIFFDATA))
mutual_addr = cd->ic_info.misc.mutual_diffdata_addr;
else if (strstr(type, CMD_GET_SELF_BASEDATA))
self_addr = cd->ic_info.misc.self_refdata_addr;
else if (strstr(type, CMD_GET_SELF_RAWDATA))
self_addr = cd->ic_info.misc.self_rawdata_addr;
else if (strstr(type, CMD_GET_SELF_DIFFDATA))
self_addr = cd->ic_info.misc.self_diffdata_addr;
flag_addr = cd->ic_info.misc.touch_data_addr;
temp_cmd.cmd = 0x01;
temp_cmd.len = 4;
}
ret = cd->hw_ops->send_cmd(cd, &temp_cmd);
if (ret < 0) {
ts_err("report rawdata failed, exit!");
goto exit;
}
/* clean touch event flag */
val = 0;
ret = cd->hw_ops->write(cd, flag_addr, &val, 1);
if (ret < 0) {
ts_err("clean touch event failed, exit!");
goto exit;
}
while (retry--) {
usleep_range(2000, 2100);
ret = cd->hw_ops->read(cd, flag_addr, &val, 1);
if (!ret && (val & 0x80))
break;
}
if (retry < 0) {
ts_err("framedata is not ready val:0x%02x, exit!", val);
ret = -EINVAL;
goto exit;
}
if (strstr(type, CMD_GET_TX_FREQ)) {
ret = cd->hw_ops->read(cd, tx_freq_addr, frame_buf, 2);
if (ret < 0) {
ts_err("read frame data failed");
goto exit;
}
index = sprintf(rbuf, "%s: %dHz\n", CMD_GET_TX_FREQ,
le16_to_cpup((__le16 *)frame_buf) * 61);
goto exit;
}
if (strstr(type, CMD_GET_RAWDATA) || strstr(type, CMD_GET_DIFFDATA) ||
strstr(type, CMD_GET_BASEDATA)) {
ret = cd->hw_ops->read(cd, mutual_addr, frame_buf, tx * rx * 2);
if (ret < 0) {
ts_err("read frame data failed");
goto exit;
}
goodix_rotate_abcd2cbad(tx, rx, (s16 *)frame_buf, NULL);
for (i = 0; i < tx * rx; i++) {
index += sprintf(
&rbuf[index], "%5d,", *((s16 *)frame_buf + i));
if ((i + 1) % tx == 0)
index += sprintf(&rbuf[index], "\n");
}
} else {
ret = cd->hw_ops->read(cd, self_addr, frame_buf, (tx + rx) * 2);
if (ret < 0) {
ts_err("read frame data failed");
goto exit;
}
index += sprintf(&rbuf[index], "TX:");
for (i = 0; i < tx + rx; i++) {
index += sprintf(
&rbuf[index], "%5d,", *((s16 *)frame_buf + i));
if ((i + 1) == tx)
index += sprintf(&rbuf[index], "\nRX:");
}
index += sprintf(&rbuf[index], "\n");
}
exit:
kfree(frame_buf);
temp_cmd.cmd = 0x90;
temp_cmd.data[0] = 0;
temp_cmd.len = 5;
if (cd->bus->sub_ic_type == IC_TYPE_SUB_GT7986) {
temp_cmd.cmd = 0;
temp_cmd.len = 4;
}
cd->hw_ops->send_cmd(cd, &temp_cmd);
/* clean touch event flag */
val = 0;
cd->hw_ops->write(cd, flag_addr, &val, 1);
/* enable irq & esd */
cd->hw_ops->irq_enable(cd, true);
goodix_ts_esd_on(cd);
return ret;
}
static void goodix_read_config(struct goodix_ts_core *cd)
{
int ret;
u8 *cfg_buf;
u32 cfg_id;
u8 cfg_ver;
cfg_buf = kzalloc(GOODIX_CFG_MAX_SIZE, GFP_KERNEL);
if (cfg_buf == NULL) {
ts_err("failed to alloc cfg buffer");
return;
}
ret = cd->hw_ops->read_config(cd, cfg_buf, GOODIX_CFG_MAX_SIZE);
if (ret < 0) {
ts_err("read config failed");
goto exit;
}
cfg_id = le32_to_cpup((__le32 *)&cfg_buf[30]);
cfg_ver = cfg_buf[34];
index = sprintf(
rbuf, "config_id:0x%X config_ver:0x%02X\n", cfg_id, cfg_ver);
exit:
kfree(cfg_buf);
}
static void goodix_set_custom_mode(struct goodix_ts_core *cd, u8 type, u8 val)
{
struct goodix_ts_cmd temp_cmd = {0};
if (type == PALM_FUNC) {
index = sprintf(&rbuf[index], "set palm %s\n",
val ? "enabled" : "disabled");
} else if (type == NOISE_FUNC) {
if (val == 0) {
index = sprintf(&rbuf[index], "set noise disabled\n");
} else if (val == 1) {
index = sprintf(&rbuf[index], "set noise enabled\n");
} else if (val == 2) {
index = sprintf(&rbuf[index], "set noise lv0\n");
} else {
index = sprintf(&rbuf[index], "set noise lv1\n");
}
} else if (type == WATER_FUNC) {
index = sprintf(&rbuf[index], "set water %s\n",
val ? "enabled" : "disabled");
} else if (type == GRIP_FUNC) {
index = sprintf(&rbuf[index], "set grip %s\n",
val ? "enabled" : "disabled");
} else {
ts_err("invalid type, %d", type);
return;
}
temp_cmd.len = 6;
temp_cmd.cmd = 0xC7;
temp_cmd.data[0] = type;
temp_cmd.data[1] = val;
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_set_gesture_param(struct goodix_ts_core *cd, u8 type)
{
struct goodix_ts_cmd temp_cmd = {0};
u32 cmd_reg = cd->ic_info.misc.cmd_addr;
u32 fw_buffer_addr = cd->ic_info.misc.fw_buffer_addr;
int retry;
u8 status;
temp_cmd.len = 4;
temp_cmd.cmd = 0xC8;
cd->hw_ops->send_cmd(cd, &temp_cmd);
retry = 20;
while (retry--) {
usleep_range(5000, 5100);
cd->hw_ops->read(cd, cmd_reg, &status, 1);
if (status == 0x80)
break;
}
if (retry < 0) {
ts_err("failed to start write gesture param, status[%x]",
status);
goto exit;
}
if (type == GESTURE_STTW) {
ts_info("STTW param:%*ph", gesture_param_st.length,
gesture_param_st.buf);
cd->hw_ops->write(cd, fw_buffer_addr, gesture_param_st.buf,
sizeof(gesture_param_st));
} else {
ts_info("LPTW param:%*ph", gesture_param_lp.length,
gesture_param_lp.buf);
cd->hw_ops->write(cd, fw_buffer_addr, gesture_param_lp.buf,
sizeof(gesture_param_lp));
}
temp_cmd.len = 4;
temp_cmd.cmd = 0x05;
cd->hw_ops->send_cmd(cd, &temp_cmd);
retry = 20;
while (retry--) {
usleep_range(5000, 5100);
cd->hw_ops->read(cd, cmd_reg, &status, 1);
if (status == 0x80 || status == 0x03)
break;
}
if (retry < 0) {
ts_err("failed to update gesture param, status[%x]", status);
goto exit;
}
if (status == 0x80) {
ts_info("update gesture param OK");
index = sprintf(rbuf, "update gesture param OK\n");
} else {
ts_info("update gesture param FAIL");
index = sprintf(rbuf, "update gesture param FAIL\n");
}
exit:
temp_cmd.len = 4;
temp_cmd.cmd = 0x06;
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_force_update(struct goodix_ts_core *cd, int *buf, int bufsz)
{
int ret;
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
ret = goodix_get_config_proc(cd);
if (ret < 0)
ts_err("not found valid config");
ret = goodix_do_fw_update(cd, UPDATE_MODE_BLOCK | UPDATE_MODE_FORCE |
UPDATE_MODE_SRC_REQUEST);
if (ret < 0)
index = sprintf(rbuf, "%s: NG\n", CMD_FW_UPDATE);
else
index = sprintf(rbuf, "%s: OK\n", CMD_FW_UPDATE);
}
static void goodix_run_auto_test(struct goodix_ts_core *cd, int *buf, int bufsz)
{
int ret;
raw_data_cnt = 16;
noise_data_cnt = 1;
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
ret = malloc_test_resource();
if (ret < 0) {
ts_err("malloc test resource failed");
return;
}
ts_test->item[GTP_CAP_TEST] = true;
ts_test->item[GTP_NOISE_TEST] = true;
ts_test->item[GTP_DELTA_TEST] = true;
ts_test->item[GTP_SELFCAP_TEST] = true;
ts_test->item[GTP_SHORT_TEST] = true;
goodix_auto_test(cd, true);
}
static void goodix_run_open_test(struct goodix_ts_core *cd, int *buf, int bufsz)
{
int ret;
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
if (bufsz < 1 || bufsz > 2) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
if (buf[0] <= 0) {
ts_err("invalid input param:%d", buf[0]);
return;
}
raw_data_cnt = buf[0];
/*
* [GOOG]
* Change the size based on the channel number(tx * rx) for
* output (raw + data) and markup text with format "%5d,".
*/
//rbuf = malloc_proc_buffer(raw_data_cnt * 30000);
rbuf = malloc_proc_buffer(raw_data_cnt * tx * rx * 6 * 3);
/*~[GOOG]*/
if (rbuf == NULL) {
ts_err("failed to malloc rbuf");
return;
}
ret = malloc_test_resource();
if (ret < 0) {
ts_err("malloc test resource failed");
return;
}
ts_test->item[GTP_CAP_TEST] = true;
ts_test->freq = buf[1];
goodix_auto_test(cd, false);
}
static void goodix_run_self_open_test(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
int ret;
rbuf = malloc_proc_buffer(HUGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
ret = malloc_test_resource();
if (ret < 0) {
ts_err("malloc test resource failed");
return;
}
ts_test->item[GTP_SELFCAP_TEST] = true;
goodix_auto_test(cd, false);
}
static void goodix_run_noise_test(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
int ret;
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
if (buf[0] <= 0) {
ts_err("invalid input param:%d", buf[0]);
return;
}
noise_data_cnt = buf[0];
/*
* [GOOG]
* Change the size based on the channel number(tx * rx) for
* output (raw + data) and markup text with format "%5d,".
*/
//rbuf = malloc_proc_buffer(noise_data_cnt * 30000);
rbuf = malloc_proc_buffer(noise_data_cnt * tx * rx * 6 * 3);
/*~[GOOG]*/
if (rbuf == NULL) {
ts_err("failed to malloc rbuf");
return;
}
ret = malloc_test_resource();
if (ret < 0) {
ts_err("malloc test resource failed");
return;
}
ts_test->item[GTP_NOISE_TEST] = true;
goodix_auto_test(cd, false);
}
static void goodix_run_auto_noise_test(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 2) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
if (buf[0] <= 0 || buf[1] <= 0) {
ts_err("invalid input param:%d,%d", buf[0], buf[1]);
return;
}
rbuf = malloc_proc_buffer(HUGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
goodix_auto_noise_test(cd, buf[0], buf[1]);
}
static void goodix_run_short_test(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
int ret;
rbuf = malloc_proc_buffer(HUGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
ret = malloc_test_resource();
if (ret < 0) {
ts_err("malloc test resource failed");
return;
}
ts_test->item[GTP_SHORT_TEST] = true;
goodix_auto_test(cd, false);
}
static void goodix_get_package_id(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
int ret;
u8 id;
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
mutex_lock(&cd->cmd_lock);
usleep_range(6000, 6100);
if (cd->bus->ic_type == IC_TYPE_BERLIN_B)
ret = cd->hw_ops->read_flash(cd, 0x3F301, &id, 1);
else
ret = cd->hw_ops->read_flash(cd, 0x1F301, &id, 1);
mutex_unlock(&cd->cmd_lock);
if (ret < 0)
index = sprintf(rbuf, "%s: NG\n", CMD_GET_PACKAGE_ID);
else
index = sprintf(rbuf, "%s: 0x%x\n", CMD_GET_PACKAGE_ID, id);
}
static void goodix_get_mcu_id(struct goodix_ts_core *cd, int *buf, int bufsz)
{
int ret;
u8 id;
if (cd->bus->ic_type == IC_TYPE_BERLIN_B) {
ts_info("berlinB is always TSMC");
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
mutex_lock(&cd->cmd_lock);
ret = cd->hw_ops->read_flash(cd, 0x1F314, &id, 1);
mutex_unlock(&cd->cmd_lock);
if (ret < 0)
index = sprintf(rbuf, "%s: NG\n", CMD_GET_MCU_ID);
else
index = sprintf(rbuf, "%s: 0x%x\n", CMD_GET_MCU_ID, id);
}
static void goodix_get_version(struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_fw_version fw_ver;
struct goodix_ic_info ic_info;
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
cd->hw_ops->read_version(cd, &fw_ver);
cd->hw_ops->get_ic_info(cd, &ic_info);
index = sprintf(rbuf, "%s: 0x%02x%02x%02x%02x 0x%x\n", CMD_GET_VERSION,
fw_ver.patch_vid[0], fw_ver.patch_vid[1], fw_ver.patch_vid[2],
fw_ver.patch_vid[3], ic_info.version.config_id);
}
static void goodix_get_rawdata(struct goodix_ts_core *cd, int *buf, int bufsz)
{
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
get_cap_data(cd, CMD_GET_RAWDATA);
}
static void goodix_get_diffdata(struct goodix_ts_core *cd, int *buf, int bufsz)
{
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
get_cap_data(cd, CMD_GET_DIFFDATA);
}
static void goodix_get_basedata(struct goodix_ts_core *cd, int *buf, int bufsz)
{
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
get_cap_data(cd, CMD_GET_BASEDATA);
}
static void goodix_get_self_rawdata(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
get_cap_data(cd, CMD_GET_SELF_RAWDATA);
}
static void goodix_get_self_diffdata(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
get_cap_data(cd, CMD_GET_SELF_DIFFDATA);
}
static void goodix_get_self_basedata(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
get_cap_data(cd, CMD_GET_SELF_BASEDATA);
}
static void goodix_set_double_tap_gesture(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
if (buf[0] == 0) {
cd->gesture_type &= ~GESTURE_DOUBLE_TAP;
index = sprintf(rbuf, "%s: disable OK\n", CMD_SET_DOUBLE_TAP);
ts_info("disable double tap");
} else {
cd->gesture_type |= GESTURE_DOUBLE_TAP;
index = sprintf(rbuf, "%s: enable OK\n", CMD_SET_DOUBLE_TAP);
ts_info("enable single tap");
}
}
static void goodix_set_single_tap_gesture(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
if (buf[0] == 0) {
cd->gesture_type &= ~GESTURE_SINGLE_TAP;
index = sprintf(rbuf, "%s: disable OK\n", CMD_SET_SINGLE_TAP);
ts_info("disable single tap");
} else {
cd->gesture_type |= GESTURE_SINGLE_TAP;
index = sprintf(rbuf, "%s: enable OK\n", CMD_SET_SINGLE_TAP);
ts_info("enable single tap");
}
}
static void goodix_set_long_press_gesture(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
if (buf[0] == 0) {
cd->gesture_type &= ~GESTURE_FOD_PRESS;
index = sprintf(rbuf, "%s: disable OK\n", CMD_SET_LONG_PRESS);
ts_info("disable long press");
} else {
cd->gesture_type |= GESTURE_FOD_PRESS;
index = sprintf(rbuf, "%s: enable OK\n", CMD_SET_LONG_PRESS);
ts_info("enable long press");
}
}
static void goodix_set_st_param(struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 8) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
gesture_param_st.length = sizeof(gesture_param_st);
gesture_param_st.type = GESTURE_STTW;
gesture_param_st.st_min_x = buf[0];
gesture_param_st.st_max_x = buf[1];
gesture_param_st.st_min_y = buf[2];
gesture_param_st.st_max_y = buf[3];
gesture_param_st.st_min_count = buf[4];
gesture_param_st.st_max_count = buf[5];
gesture_param_st.st_motion_tolerance = buf[6];
gesture_param_st.st_max_size = buf[7];
goodix_append_checksum(gesture_param_st.buf,
sizeof(gesture_param_st) - 2, CHECKSUM_MODE_U8_LE);
ts_info("st_min_x: %d", gesture_param_st.st_min_x);
ts_info("st_max_x: %d", gesture_param_st.st_max_x);
ts_info("st_min_y: %d", gesture_param_st.st_min_y);
ts_info("st_max_y: %d", gesture_param_st.st_max_y);
ts_info("st_min_count: %d", gesture_param_st.st_min_count);
ts_info("st_max_count: %d", gesture_param_st.st_max_count);
ts_info("st_motion_tolerance: %d",
gesture_param_st.st_motion_tolerance);
ts_info("st_max_size: %d", gesture_param_st.st_max_size);
goodix_set_gesture_param(cd, GESTURE_STTW);
}
static void goodix_set_lp_param(struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 17) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
gesture_param_lp.length = sizeof(gesture_param_lp);
gesture_param_lp.type = GESTURE_LPTW;
gesture_param_lp.lp_min_x = buf[0];
gesture_param_lp.lp_max_x = buf[1];
gesture_param_lp.lp_min_y = buf[2];
gesture_param_lp.lp_max_y = buf[3];
gesture_param_lp.lp_min_count = buf[4];
gesture_param_lp.lp_max_size = buf[5];
gesture_param_lp.lp_marginal_min_x = buf[6];
gesture_param_lp.lp_marginal_max_x = buf[7];
gesture_param_lp.lp_marginal_min_y = buf[8];
gesture_param_lp.lp_marginal_max_y = buf[9];
gesture_param_lp.lp_monitor_chan_min_tx = buf[10];
gesture_param_lp.lp_monitor_chan_max_tx = buf[11];
gesture_param_lp.lp_monitor_chan_min_rx = buf[12];
gesture_param_lp.lp_monitor_chan_max_rx = buf[13];
gesture_param_lp.lp_min_node_count = buf[14];
gesture_param_lp.lp_motion_tolerance_inner = buf[15];
gesture_param_lp.lp_motion_tolerance_outer = buf[16];
goodix_append_checksum(gesture_param_lp.buf,
sizeof(gesture_param_lp) - 2, CHECKSUM_MODE_U8_LE);
ts_info("lp_min_x: %d", gesture_param_lp.lp_min_x);
ts_info("lp_max_x: %d", gesture_param_lp.lp_max_x);
ts_info("lp_min_y: %d", gesture_param_lp.lp_min_y);
ts_info("lp_max_y: %d", gesture_param_lp.lp_max_y);
ts_info("lp_min_count: %d", gesture_param_lp.lp_min_count);
ts_info("lp_max_size: %d", gesture_param_lp.lp_max_size);
ts_info("lp_marginal_min_x: %d",
gesture_param_lp.lp_marginal_min_x);
ts_info("lp_marginal_max_x: %d",
gesture_param_lp.lp_marginal_max_x);
ts_info("lp_marginal_min_y: %d",
gesture_param_lp.lp_marginal_min_y);
ts_info("lp_marginal_max_y: %d",
gesture_param_lp.lp_marginal_max_y);
ts_info("lp_monitor_chan_min_tx: %d",
gesture_param_lp.lp_monitor_chan_min_tx);
ts_info("lp_monitor_chan_max_tx: %d",
gesture_param_lp.lp_monitor_chan_max_tx);
ts_info("lp_monitor_chan_min_rx: %d",
gesture_param_lp.lp_monitor_chan_min_rx);
ts_info("lp_monitor_chan_max_rx: %d",
gesture_param_lp.lp_monitor_chan_max_rx);
ts_info("lp_min_node_count: %d",
gesture_param_lp.lp_min_node_count);
ts_info("lp_motion_tolerance_inner: %d",
gesture_param_lp.lp_motion_tolerance_inner);
ts_info("lp_motion_tolerance_outer: %d",
gesture_param_lp.lp_motion_tolerance_outer);
goodix_set_gesture_param(cd, GESTURE_LPTW);
}
static void goodix_set_irq_enable(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
if (buf[0] == 0) {
cd->hw_ops->irq_enable(cd, false);
index = sprintf(rbuf, "%s: disable OK\n", CMD_SET_IRQ_ENABLE);
} else {
cd->hw_ops->irq_enable(cd, true);
index = sprintf(rbuf, "%s: enable OK\n", CMD_SET_IRQ_ENABLE);
}
}
static void goodix_set_esd_enable(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
if (buf[0] == 0) {
goodix_ts_esd_off(cd);
index = sprintf(rbuf, "%s: disable OK\n", CMD_SET_ESD_ENABLE);
} else {
goodix_ts_esd_on(cd);
index = sprintf(rbuf, "%s: enable OK\n", CMD_SET_ESD_ENABLE);
}
}
static void goodix_set_debug_log(struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
if (buf[0] == 0) {
debug_log_flag = false;
index = sprintf(rbuf, "%s: disable OK\n", CMD_SET_DEBUG_LOG);
} else {
debug_log_flag = true;
index = sprintf(rbuf, "%s: enable OK\n", CMD_SET_DEBUG_LOG);
}
}
static void goodix_set_scan_mode(struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
temp_cmd.len = 5;
temp_cmd.cmd = 0x9F;
if (buf[0] == 0) {
temp_cmd.data[0] = 0;
ts_info("set scan mode to default");
index = sprintf(rbuf, "set scan mode to default\n");
} else if (buf[0] == 1) {
temp_cmd.data[0] = 3;
ts_info("set scan mode to idle");
index = sprintf(rbuf, "set scan mode to idle\n");
} else {
temp_cmd.data[0] = 2;
ts_info("set scan mode to active");
index = sprintf(rbuf, "set scan mode to active\n");
}
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_get_scan_mode(struct goodix_ts_core *cd, int *buf, int bufsz)
{
u8 status;
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
cd->hw_ops->read(cd, 0x10219, &status, 1);
ts_info("ic status:%d", status);
if (status == 1) {
index = sprintf(rbuf, "normal active\n");
} else if (status == 2) {
index = sprintf(rbuf, "normal idle\n");
} else if (status == 3) {
index = sprintf(rbuf, "lowpower active\n");
} else if (status == 4) {
index = sprintf(rbuf, "lowpower idle\n");
} else if (status == 5) {
index = sprintf(rbuf, "sleep\n");
} else {
index = sprintf(rbuf, "invalid mode %d\n", status);
}
}
static void goodix_set_continue_mode(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
if (buf[0] == 0) {
ts_info("enable continue report");
index = sprintf(rbuf, "enable continue report\n");
} else {
ts_info("disable continue report");
index = sprintf(rbuf, "disable continue report\n");
}
temp_cmd.len = 5;
temp_cmd.cmd = 0xC6;
temp_cmd.data[0] = buf[0];
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_get_channel_num(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
index = sprintf(rbuf, "TX:%d RX:%d\n", cd->ic_info.parm.drv_num,
cd->ic_info.parm.sen_num);
}
static void goodix_get_tx_freq(struct goodix_ts_core *cd, int *buf, int bufsz)
{
int ret;
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
ret = get_cap_data(cd, CMD_GET_TX_FREQ);
if (ret < 0)
index = sprintf(rbuf, "%s: NG\n", CMD_GET_TX_FREQ);
}
static void goodix_hw_reset(struct goodix_ts_core *cd, int *buf, int bufsz)
{
cd->hw_ops->irq_enable(cd, false);
cd->hw_ops->reset(cd, goodix_get_normal_reset_delay(cd));
cd->hw_ops->irq_enable(cd, true);
}
static void goodix_set_sense_mode(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
temp_cmd.len = 4;
temp_cmd.cmd = 0xA7;
if (buf[0] == 1) {
/* normal mode */
index = sprintf(rbuf, "switch to coordinate mode\n");
cd->hw_ops->send_cmd(cd, &temp_cmd);
goodix_ts_esd_on(cd);
cd->hw_ops->irq_enable(cd, true);
atomic_set(&cd->suspended, 0);
} else if (buf[0] == 2) {
/* gesture mode */
index = sprintf(rbuf, "switch to gesture mode\n");
goodix_ts_esd_off(cd);
cd->hw_ops->gesture(cd, 0);
cd->hw_ops->irq_enable(cd, true);
atomic_set(&cd->suspended, 1);
} else {
/* sleep mode */
index = sprintf(rbuf, "switch to sleep mode\n");
goodix_ts_esd_off(cd);
cd->hw_ops->irq_enable(cd, false);
cd->hw_ops->suspend(cd);
}
}
static void goodix_get_config(struct goodix_ts_core *cd, int *buf, int bufsz)
{
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
goodix_read_config(cd);
}
static void goodix_get_fw_status(struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_status_data status_data;
u8 val[2];
index = 0;
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
cd->hw_ops->read(cd, 0x1021A, val, sizeof(val));
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"coordfilter_status[%d] ", (val[0] >> 7) & 0x01);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"set_highsense_mode[%d] ", (val[0] >> 6) & 0x01);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"set_noise_mode[%d] ", (val[0] >> 4) & 0x03);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"set_water_mode[%d] ", (val[0] >> 3) & 0x01);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"set_grip_mode[%d] ", (val[0] >> 2) & 0x01);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"set_palm_mode[%d] ", (val[0] >> 1) & 0x01);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"set_heatmap_mode[%d]\n", (val[0] >> 0) & 0x01);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"fw_ns/hs[%d]", (val[1] >> 0) & 0x01);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"wireless mode[%d]", (val[1] >> 1) & 0x01);
cd->hw_ops->read(cd, 0x1021C, (u8 *)&status_data, sizeof(status_data));
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"water[%d] ", status_data.water_sta);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"palm[%d] ", status_data.palm_sta);
index += scnprintf(&rbuf[index], SHORT_SIZE - index,
"noise_lv[%d]\n", status_data.noise_lv);
}
static void goodix_set_highsense_mode(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
if (buf[0] == 0) {
ts_info("exit highsense mode");
index = sprintf(rbuf, "exit highsense mode\n");
} else {
ts_info("enter highsense mode");
index = sprintf(rbuf, "enter highsense mode\n");
}
temp_cmd.len = 5;
temp_cmd.cmd = 0x72;
temp_cmd.data[0] = buf[0];
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_set_grip_data(struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
if (buf[0] == 0) {
ts_info("portrait mode");
index = sprintf(rbuf, "portrait mode\n");
temp_cmd.len = 4;
temp_cmd.cmd = 0x18;
} else if (buf[0] == 1) {
ts_info("landscape left");
index = sprintf(rbuf, "landscape left\n");
temp_cmd.len = 5;
temp_cmd.cmd = 0x17;
temp_cmd.data[0] = 0;
} else if (buf[0] == 2) {
ts_info("landscape right");
index = sprintf(rbuf, "landscape right\n");
temp_cmd.len = 5;
temp_cmd.cmd = 0x17;
temp_cmd.data[0] = 1;
} else {
ts_err("invalid grip data, %d", buf[0]);
return;
}
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_set_grip_mode(struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
goodix_set_custom_mode(cd, GRIP_FUNC, buf[0]);
}
static void goodix_set_palm_mode(struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
goodix_set_custom_mode(cd, PALM_FUNC, buf[0]);
}
static void goodix_set_noise_mode(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
goodix_set_custom_mode(cd, NOISE_FUNC, buf[0]);
}
static void goodix_set_water_mode(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
goodix_set_custom_mode(cd, WATER_FUNC, buf[0]);
}
static void goodix_set_heatmap(struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
cd->hw_ops->irq_enable(cd, false);
if (buf[0] == 0) {
index = sprintf(rbuf, "disable heatmap\n");
/*
* [GOOG]
* Use goodix_update_heatmap() to do the heatmap process.
kfree(cd->heatmap_buffer);
cd->heatmap_buffer = NULL;
*/
temp_cmd.len = 5;
temp_cmd.cmd = 0xC9;
temp_cmd.data[0] = 0;
} else {
index = sprintf(rbuf, "enable heatmap\n");
/*
* [GOOG]
* Use goodix_update_heatmap() to do the heatmap process.
cd->heatmap_buffer = kzalloc(GOODIX_MAX_FRAMEDATA_LEN, GFP_KERNEL);
*/
temp_cmd.len = 5;
temp_cmd.cmd = 0xC9;
temp_cmd.data[0] = 1;
}
// no need to do anything for GT7986
if (cd->bus->sub_ic_type != IC_TYPE_SUB_GT7986)
cd->hw_ops->send_cmd(cd, &temp_cmd);
cd->hw_ops->irq_enable(cd, true);
}
static void goodix_get_self_compensation(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
u8 *cfg;
u8 *cfg_buf;
int len;
int cfg_num;
int sub_cfg_index;
int sub_cfg_len;
int tx = cd->ic_info.parm.drv_num;
int rx = cd->ic_info.parm.sen_num;
struct goodix_ts_cmd temp_cmd = {0};
s16 val;
int i, j;
cfg_buf = kzalloc(GOODIX_CFG_MAX_SIZE, GFP_KERNEL);
if (cfg_buf == NULL) {
ts_err("failed to alloc cfg buffer");
return;
}
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
temp_cmd.len = 5;
temp_cmd.cmd = 0x9F;
if (cd->bus->ic_type == IC_TYPE_BERLIN_B) {
/* exit idle firstly */
temp_cmd.data[0] = 2;
cd->hw_ops->send_cmd(cd, &temp_cmd);
msleep(20);
cd->hw_ops->read(cd, cd->ic_info.misc.auto_scan_cmd_addr, cfg_buf,
(tx + rx) * 2);
/* restore default */
temp_cmd.data[0] = 0;
cd->hw_ops->send_cmd(cd, &temp_cmd);
index += sprintf(&rbuf[index], "Tx:");
for (i = 0; i < tx; i++) {
val = le16_to_cpup((__le16 *)&cfg_buf[i * 2]);
index += sprintf(&rbuf[index], "%d,", val);
}
index += sprintf(&rbuf[index], "\nRx:");
for (i = 0; i < rx; i++) {
val = le16_to_cpup((__le16 *)&cfg_buf[tx * 2 + i * 2]);
index += sprintf(&rbuf[index], "%d,", val);
}
index += sprintf(&rbuf[index], "\n");
goto exit_free;
}
len = cd->hw_ops->read_config(cd, cfg_buf, GOODIX_CFG_MAX_SIZE);
if (len < 0) {
ts_err("read config failed");
goto exit_free;
}
cfg = cfg_buf;
cfg_num = cfg[61];
cfg += 64;
for (i = 0; i < cfg_num; i++) {
sub_cfg_len = cfg[0] - 2;
sub_cfg_index = cfg[1];
if (sub_cfg_index == 13) { // TX self cancel
ts_info("sub_cfg_len:%d", sub_cfg_len);
index += sprintf(&rbuf[index], "Tx:");
for (j = 0; j < tx; j++) {
val = le16_to_cpup((__le16 *)&cfg[2 + j * 4]) +
le16_to_cpup((__le16 *)&cfg[4 + j * 4]);
index += sprintf(&rbuf[index], "%d,", val);
}
index += sprintf(&rbuf[index], "\n");
} else if (sub_cfg_index == 14) { // RX self cancel
ts_info("sub_cfg_len:%d", sub_cfg_len);
index += sprintf(&rbuf[index], "Rx:");
for (j = 0; j < rx; j++) {
val = le16_to_cpup((__le16 *)&cfg[2 + j * 2]);
index += sprintf(&rbuf[index], "%d,", val);
}
index += sprintf(&rbuf[index], "\n");
}
cfg += (sub_cfg_len + 2);
}
exit_free:
kfree(cfg_buf);
}
static void goodix_set_report_rate(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
index = sprintf(rbuf, "set report rate %d\n", buf[0]);
ts_info("set report rate %d", buf[0]);
temp_cmd.len = 5;
temp_cmd.cmd = 0x9D;
temp_cmd.data[0] = buf[0];
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
#define DUMP_AREA1_ADDR 0x10194
#define DUMP_AREA1_LEN 132
#define DUMP_AREA2_ADDR 0x10400
#define DUMP_AREA2_LEN 596
#define DUMP_AREA3_ADDR 0x10308
#define DUMP_AREA3_LEN 64
static void goodix_get_dump_log(struct goodix_ts_core *cd, int *buf, int bufsz)
{
u8 debug_buf[DUMP_AREA2_LEN];
int i;
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
cd->hw_ops->read(cd, DUMP_AREA1_ADDR, debug_buf, DUMP_AREA1_LEN);
index += scnprintf(&rbuf[index], DUMP_AREA2_LEN - index, "0x%04x:\n", DUMP_AREA1_ADDR);
for (i = 0; i < DUMP_AREA1_LEN; i++) {
index += scnprintf(&rbuf[index], sizeof(rbuf) - index,
"%02x,", debug_buf[i]);
if ((i + 1) % 32 == 0)
index += sprintf(&rbuf[index], "\n");
}
cd->hw_ops->read(cd, DUMP_AREA2_ADDR, debug_buf, DUMP_AREA2_LEN);
index += scnprintf(&rbuf[index], DUMP_AREA2_LEN - index,
"\n0x%04x:\n", DUMP_AREA2_ADDR);
for (i = 0; i < DUMP_AREA2_LEN; i++) {
index += scnprintf(&rbuf[index], sizeof(rbuf) - index, "%02x,",
debug_buf[i]);
if ((i + 1) % 32 == 0)
index += sprintf(&rbuf[index], "\n");
}
cd->hw_ops->read(cd, DUMP_AREA3_ADDR, debug_buf, DUMP_AREA3_LEN);
index += scnprintf(&rbuf[index], DUMP_AREA2_LEN - index,
"\n0x%04x:\n", DUMP_AREA3_ADDR);
for (i = 0; i < DUMP_AREA3_LEN; i++) {
index += scnprintf(&rbuf[index], sizeof(rbuf) - index, "%02x,",
debug_buf[i]);
if ((i + 1) % 32 == 0)
index += sprintf(&rbuf[index], "\n");
}
}
static void goodix_set_freq_index(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
index = sprintf(rbuf, "set frequency index %d\n", buf[0]);
ts_info("set frequency index %d", buf[0]);
temp_cmd.len = 5;
temp_cmd.cmd = 0x9C;
temp_cmd.data[0] = buf[0];
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_disable_coor_filter(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
index = sprintf(rbuf, "disable coordinate filter %d\n", buf[0]);
temp_cmd.len = 5;
temp_cmd.cmd = 0xCA;
temp_cmd.data[0] = buf[0] ? 1 : 0;
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_get_im_rawdata(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
u32 im_addr = cd->ic_info.misc.frame_data_addr +
cd->ic_info.misc.frame_data_head_len +
cd->ic_info.misc.fw_attr_len +
cd->ic_info.misc.fw_log_len +
cd->ic_info.misc.mutual_struct_len +
cd->ic_info.misc.self_struct_len + 32;
int freq_num = cd->ic_info.parm.mutual_freq_num;
int rx = cd->ic_info.parm.sen_num;
s16 data[MAX_SCAN_FREQ_NUM * MAX_SEN_NUM_BRB];
int i, j;
int cnt = 0;
struct goodix_ts_cmd temp_cmd = {0};
u32 flag_addr = cd->ic_info.misc.frame_data_addr;
u8 val;
int retry = 20;
/* disable irq & close esd */
cd->hw_ops->irq_enable(cd, false);
goodix_ts_esd_off(cd);
temp_cmd.cmd = 0x90;
temp_cmd.data[0] = 0x81;
temp_cmd.len = 5;
cd->hw_ops->send_cmd(cd, &temp_cmd);
/* clean touch event flag */
val = 0;
cd->hw_ops->write(cd, flag_addr, &val, 1);
while (retry--) {
usleep_range(2000, 2100);
cd->hw_ops->read(cd, flag_addr, &val, 1);
if (val & 0x80)
break;
}
if (retry < 0) {
ts_err("framedata is not ready val:0x%02x, exit!", val);
goto exit;
}
cd->hw_ops->read(cd, im_addr, (u8 *)data, freq_num * rx * 2);
rbuf = malloc_proc_buffer(LARGE_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
for (i = 0; i < freq_num; i++) {
index += sprintf(&rbuf[index], "freq%d: ", i);
for (j = 0; j < rx; j++)
index += sprintf(&rbuf[index], "%d,", data[cnt++]);
index += sprintf(&rbuf[index], "\n");
}
exit:
temp_cmd.cmd = 0x90;
temp_cmd.data[0] = 0;
temp_cmd.len = 5;
cd->hw_ops->send_cmd(cd, &temp_cmd);
val = 0;
cd->hw_ops->write(cd, flag_addr, &val, 1);
/* enable irq & esd */
cd->hw_ops->irq_enable(cd, true);
goodix_ts_esd_on(cd);
}
static void goodix_set_hsync_speed(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd = {0};
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
index = sprintf(rbuf, "hsync mode: %s\n",
(buf[0] == 0) ? "high speed" : "normal speed");
temp_cmd.len = 5;
temp_cmd.cmd = 0xF2;
temp_cmd.data[0] = (buf[0] == 0) ? 0 : 1;
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_set_wireless_charge(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd;
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return;
}
index = sprintf(
rbuf, "%s wireless mode\n", (buf[0] == 0) ? "exit" : "enter");
temp_cmd.len = 5;
temp_cmd.cmd = 0xAF;
temp_cmd.data[0] = (buf[0] == 0) ? 0 : 1;
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static void goodix_set_continue_heatmap(
struct goodix_ts_core *cd, int *buf, int bufsz)
{
struct goodix_ts_cmd temp_cmd;
if (bufsz != 1) {
ts_err("invalid cmd size:%d", bufsz);
return;
}
rbuf = malloc_proc_buffer(SHORT_SIZE);
index = sprintf(rbuf, "%s continue heatmap\n",
(buf[0] == 0) ? "disable" : "enable");
temp_cmd.len = 5;
temp_cmd.cmd = 0xCC;
temp_cmd.data[0] = (buf[0] == 0) ? 0 : 1;
cd->hw_ops->send_cmd(cd, &temp_cmd);
}
static struct cmd_handler cmd_handler_list[] = {
{ CMD_FW_UPDATE, goodix_force_update },
{ CMD_AUTO_TEST, goodix_run_auto_test }, { CMD_STYLUS_RAW_TEST, NULL },
{ CMD_STYLUS_OSC_TEST, NULL }, { CMD_GET_STYLUS_DATA, NULL },
{ CMD_OPEN_TEST, goodix_run_open_test },
{ CMD_SELF_OPEN_TEST, goodix_run_self_open_test },
{ CMD_NOISE_TEST, goodix_run_noise_test },
{ CMD_AUTO_NOISE_TEST, goodix_run_auto_noise_test },
{ CMD_SHORT_TEST, goodix_run_short_test },
{ CMD_GET_PACKAGE_ID, goodix_get_package_id },
{ CMD_GET_MCU_ID, goodix_get_mcu_id },
{ CMD_GET_VERSION, goodix_get_version },
{ CMD_GET_RAWDATA, goodix_get_rawdata },
{ CMD_GET_DIFFDATA, goodix_get_diffdata },
{ CMD_GET_BASEDATA, goodix_get_basedata },
{ CMD_GET_SELF_RAWDATA, goodix_get_self_rawdata },
{ CMD_GET_SELF_DIFFDATA, goodix_get_self_diffdata },
{ CMD_GET_SELF_BASEDATA, goodix_get_self_basedata },
{ CMD_SET_DOUBLE_TAP, goodix_set_double_tap_gesture },
{ CMD_SET_SINGLE_TAP, goodix_set_single_tap_gesture },
{ CMD_SET_LONG_PRESS, goodix_set_long_press_gesture },
{ CMD_SET_ST_PARAM, goodix_set_st_param },
{ CMD_SET_LP_PARAM, goodix_set_lp_param },
{ CMD_SET_CHARGE_MODE, NULL },
{ CMD_SET_IRQ_ENABLE, goodix_set_irq_enable },
{ CMD_SET_ESD_ENABLE, goodix_set_esd_enable },
{ CMD_SET_DEBUG_LOG, goodix_set_debug_log },
{ CMD_SET_SCAN_MODE, goodix_set_scan_mode },
{ CMD_GET_SCAN_MODE, goodix_get_scan_mode },
{ CMD_SET_CONTINUE_MODE, goodix_set_continue_mode },
{ CMD_GET_CHANNEL_NUM, goodix_get_channel_num },
{ CMD_GET_TX_FREQ, goodix_get_tx_freq }, { CMD_RESET, goodix_hw_reset },
{ CMD_SET_SENSE_MODE, goodix_set_sense_mode },
{ CMD_GET_CONFIG, goodix_get_config },
{ CMD_GET_FW_STATUS, goodix_get_fw_status },
{ CMD_SET_HIGHSENSE_MODE, goodix_set_highsense_mode },
{ CMD_SET_GRIP_DATA, goodix_set_grip_data },
{ CMD_SET_GRIP_MODE, goodix_set_grip_mode },
{ CMD_SET_PALM_MODE, goodix_set_palm_mode },
{ CMD_SET_NOISE_MODE, goodix_set_noise_mode },
{ CMD_SET_WATER_MODE, goodix_set_water_mode },
{ CMD_SET_HEATMAP, goodix_set_heatmap },
{ CMD_GET_SELF_COMPEN, goodix_get_self_compensation },
{ CMD_SET_REPORT_RATE, goodix_set_report_rate },
{ CMD_GET_DUMP_LOG, goodix_get_dump_log },
{ CMD_SET_FREQ_INDEX, goodix_set_freq_index },
{ CMD_DISABLE_FILTER, goodix_disable_coor_filter },
{ CMD_GET_IM_DATA, goodix_get_im_rawdata },
{ CMD_SET_HSYNC_SPEED, goodix_set_hsync_speed },
{ CMD_SET_WIRELESS_CHARGE, goodix_set_wireless_charge },
{ CMD_SET_CONTINUE_HEATMAP, goodix_set_continue_heatmap },
{ NULL, NULL } };
static int split_string(char *str, char *cmd, int *param, int *param_len)
{
char buf[150] = { 0 };
char *token = NULL;
char *rest = buf;
int i = 0;
s32 val;
strcpy(buf, str);
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = 0;
token = strsep(&rest, ",");
if (token == NULL)
return -1;
strcpy(cmd, token);
while ((token = strsep(&rest, ",")) != NULL) {
if (kstrtos32(token, 10, &val))
return -1;
if (i == *param_len)
break;
param[i++] = val;
}
*param_len = i;
return 0;
}
static ssize_t driver_test_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
struct seq_file *seq_file = (struct seq_file *)file->private_data;
struct goodix_ts_core *cd = (struct goodix_ts_core *)seq_file->private;
struct cmd_handler *cmder = &cmd_handler_list[0];
int ret;
char *p = wbuf;
char cmd[64] = { 0 };
int tx_buf[32] = { 0 };
int tx_len = 0;
if (count >= SHORT_SIZE) { /* [GOOG] */
ts_err("invalid cmd size[%ld]", count);
return count;
}
memset(wbuf, 0, sizeof(wbuf));
if (copy_from_user(p, buf, count) != 0) {
ts_err("copy from user failed");
return count;
}
free_proc_buffer(&rbuf);
index = 0;
release_test_resource();
tx_len = 32;
ret = split_string(p, cmd, tx_buf, &tx_len);
if (ret < 0) {
ts_err("invalid cmd: %s", p);
return count;
}
ts_info("input cmd[%s]", p);
while (cmder->name) {
if (!strcmp(cmd, cmder->name))
break;
cmder++;
}
if (cmder->name == NULL) {
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
return count;
}
index = sprintf(rbuf, "not support cmd %s\n", p);
ts_err("not support cmd[%s]", p);
return count;
}
if (cmder->handler)
cmder->handler(cd, tx_buf, tx_len);
return count;
}
static int cmd_list_show(struct seq_file *m, void *v)
{
struct cmd_handler *cmder = &cmd_handler_list[0];
if (!m || !v)
return -EIO;
while (cmder->name != NULL) {
seq_printf(m, "%s\n", cmder->name);
cmder++;
}
return 0;
}
static int cmd_list_open(struct inode *inode, struct file *file)
{
return single_open(file, cmd_list_show, NULL);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
static const struct proc_ops driver_test_ops = {
.proc_open = driver_test_open,
.proc_read = seq_read,
.proc_write = driver_test_write,
.proc_lseek = seq_lseek,
.proc_release = driver_test_release,
};
static const struct proc_ops cmd_list_ops = {
.proc_open = cmd_list_open,
.proc_read = seq_read,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
#else
static const struct file_operations driver_test_ops = {
.open = driver_test_open,
.read = seq_read,
.write = driver_test_write,
.llseek = seq_lseek,
.release = driver_test_release,
};
static const struct file_operations cmd_list_ops = {
.open = cmd_list_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
#endif
/* [GOOG] */
int driver_test_selftest(struct goodix_ts_core *cd, char *buf, bool *result,
bool is_ical)
{
int ret = 0;
release_test_resource();
index = 0;
free_proc_buffer(&rbuf);
raw_data_cnt = 16;
noise_data_cnt = 1;
rbuf = malloc_proc_buffer(SHORT_SIZE);
if (rbuf == NULL) {
ts_err("failed to alloc rbuf");
ret = -ENOMEM;
goto exit;
}
ret = malloc_test_resource();
if (ret < 0) {
ts_err("malloc test resource failed");
goto exit;
}
ts_test->is_ical = is_ical;
if (is_ical) {
ts_test->item[GTP_CAP_TEST] = true;
ts_test->item[GTP_NOISE_TEST] = false;
ts_test->item[GTP_DELTA_TEST] = false;
ts_test->item[GTP_SELFCAP_TEST] = false;
ts_test->item[GTP_SHORT_TEST] = true;
goodix_auto_test(cd, true);
*result = ts_test->result[GTP_CAP_TEST] &&
ts_test->result[GTP_SHORT_TEST];
} else {
ts_test->item[GTP_CAP_TEST] = true;
ts_test->item[GTP_NOISE_TEST] = true;
ts_test->item[GTP_DELTA_TEST] = true;
ts_test->item[GTP_SELFCAP_TEST] = true;
ts_test->item[GTP_SHORT_TEST] = true;
goodix_auto_test(cd, true);
*result = ts_test->result[GTP_CAP_TEST] &&
ts_test->result[GTP_DELTA_TEST] &&
ts_test->result[GTP_SHORT_TEST] &&
ts_test->result[GTP_NOISE_TEST] &&
ts_test->result[GTP_SELFCAP_TEST];
}
strlcpy(buf, rbuf, PAGE_SIZE);
exit:
return ret;
}
int driver_test_proc_init(struct goodix_ts_core *core_data)
{
struct proc_dir_entry *proc_entry;
proc_entry = proc_create_data(
"driver_test", 0660, core_data->proc_dir_entry, &driver_test_ops, core_data);
if (!proc_entry) {
ts_err("failed to create proc driver_test");
return -ENODEV;
}
proc_entry =
proc_create("cmd_list", 0440, core_data->proc_dir_entry, &cmd_list_ops);
if (!proc_entry) {
ts_err("failed to create proc cmd_list");
return -ENODEV;
}
return 0;
}
void driver_test_proc_remove(struct goodix_ts_core *core_data)
{
remove_proc_entry("cmd_list", core_data->proc_dir_entry);
remove_proc_entry("driver_test", core_data->proc_dir_entry);
}