/* * drivers/input/touchscreen/hyn_cst2xx.c * * hynitron TouchScreen driver. * * Copyright (c) 2015 hynitron * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * VERSION DATE AUTHOR * 1.0 2015-10-12 Tim * * note: only support mulititouch */ #include #include //#include #include #include #include #include #include #include #include #include //#include #include //#include #include #include #include #include #include #include #include #include #include #include "../tp_suspend.h" #define HYN_DEBUG #if defined (CONFIG_KP_AXP) extern int axp_gpio_set_value(int gpio, int io_state); extern int axp_gpio_set_io(int , int ); #if defined(CONFIG_BOARD_TYPE_ZM726CE_V12) #define PMU_GPIO_NUM 3 #endif #endif #if defined(CONFIG_TP_1680E_726_SD) //#define Y_POL //#define X_POL #define SWAP_X_Y #define SCREEN_MAX_X 1024 #define SCREEN_MAX_Y 600 //#include "CST21680_F_WGJ10276.h" #else #define Y_POL //#define X_POL #define SWAP_X_Y #define SCREEN_MAX_X 1280 #define SCREEN_MAX_Y 800 #include "CST21680SE_S126_D863_7.h" #endif #define ICS_SLOT_REPORT //#define HAVE_TOUCH_KEY #define SLEEP_CLEAR_POINT #define CST2XX_I2C_NAME "cst2xxse" #define CST2XX_I2C_ADDR 0x5A //#define IRQ_PORT RK2928_PIN1_PB0//RK30_PIN1_PB7 //#define WAKE_PORT RK30_PIN0_PA1//RK30_PIN0_PB6 //#define TPD_PROC_DEBUG #ifdef TPD_PROC_DEBUG #include #include //static struct proc_dir_entry *hyn_config_proc = NULL; #define HYN_CONFIG_PROC_FILE "hyn_config" #define CONFIG_LEN 31 //static char hyn_read[CONFIG_LEN]; static u8 hyn_data_proc[8] = {0}; static u8 hyn_proc_flag = 0; //static struct i2c_client *ts->client = NULL; #endif #define TRANSACTION_LENGTH_LIMITED //#define HYN_MONITOR #define PRESS_MAX 255 #define MAX_FINGERS 5 #define MAX_CONTACTS 10 #define DMA_TRANS_LEN 0x20 #ifdef HYN_MONITOR static struct workqueue_struct *hyn_monitor_workqueue = NULL; static u8 int_1st[4] = {0}; static u8 int_2nd[4] = {0}; //static char dac_counter = 0; static char b0_counter = 0; static char bc_counter = 0; static char i2c_lock_flag = 0; #endif //#define HYN_GESTURE // if define enable this function #ifdef HYN_GESTURE extern void rk_send_wakeup_key(void); static int gsl_lcd_flag = -1; static int gsl_gesture_flag = -1; #endif #ifdef HAVE_TOUCH_KEY static u16 key = 0; static int key_state_flag = 0; struct key_data { u16 key; u16 x_min; u16 x_max; u16 y_min; u16 y_max; }; static const u16 key_array[] = { KEY_BACK, KEY_HOME, KEY_MENU, KEY_SEARCH, }; #define MAX_KEY_NUM (sizeof(key_array)/sizeof(key_array[0])) struct key_data hyn_key_data[MAX_KEY_NUM] = { {KEY_BACK, 2048, 2048, 2048, 2048}, {KEY_HOME, 2048, 2048, 2048, 2048}, {KEY_MENU, 2048, 2048, 2048, 2048}, {KEY_SEARCH, 2048, 2048, 2048, 2048}, }; #endif struct hyn_ts { struct i2c_client *client; struct input_dev *input; struct work_struct work; struct workqueue_struct *wq; struct hyn_ts_data *dd; u8 *touch_data; u8 device_id; int irq; int rst_pin; int irq_pin; struct delayed_work hyn_monitor_work; #if defined(CONFIG_HAS_EARLYSUSPEND) struct early_suspend early_suspend; #endif struct tp_device tp; }; int h_wake_pin = 0; #ifdef HYN_DEBUG #define print_info(fmt, args...) \ do{ \ printk(fmt, ##args); \ }while(0) #else #define print_info(fmt, args...) #endif #define ANDROID_TOOL_SURPORT #ifdef ANDROID_TOOL_SURPORT static int cst2xx_update_firmware(struct i2c_client * client, struct hyn_ts *ts, unsigned char *pdata, int data_len); static unsigned short g_unnormal_mode = 0; static unsigned short g_cst2xx_tx = 15; static unsigned short g_cst2xx_rx = 10; static struct hyn_ts *hyn_global_ts=NULL; #endif static int cst2xx_i2c_read(struct i2c_client *client, unsigned char *buf, int len) { int ret = -1; int retries = 0; //client->timing = 370; //client->addr |= I2C_ENEXT_FLAG; while(retries < 2) { ret = i2c_master_recv(client, buf, len); if(ret<=0) retries++; else break; } return ret; } static int cst2xx_i2c_write(struct i2c_client *client, unsigned char *buf, int len) { int ret = -1; int retries = 0; while(retries < 2) { ret = i2c_master_send(client, buf, len); if(ret<=0) retries++; else break; } return ret; } static int cst2xx_i2c_read_register(struct i2c_client *client, unsigned char *buf, int len) { int ret = -1; ret = cst2xx_i2c_write(client, buf, 2); ret = cst2xx_i2c_read(client, buf, len); return ret; } static int cst2xx_test_i2c(struct i2c_client *client) { u8 retry = 0; u8 ret; u8 buf[4]; buf[0] = 0xD0; buf[1] = 0x00; while(retry++ < 5) { ret = cst2xx_i2c_write(client, buf, 2); if (ret > 0) return ret; msleep(2); } if(retry==5) printk("hyn iic test error.ret:%d.\n", ret); return ret; } static void hard_reset_chip(struct hyn_ts *ts, u16 ms) { int ret=0; int retry=0; unsigned char buf[4]; buf[0] = 0xD1; buf[1] = 0x0e; while(retry++ < 3) { ret = cst2xx_i2c_write(ts->client, buf, 2); if (ret > 0) break; msleep(2); } msleep(ms); } #ifdef TPD_PROC_DEBUG static int char_to_int(char ch) { if(ch>='0' && ch<='9') return (ch-'0'); else return (ch-'a'+10); } #endif #define CST2XX_BASE_ADDR (0x00000000) static int cst2xx_enter_download_mode(struct hyn_ts *ts) { int ret; int i; unsigned char buf[3]; hard_reset_chip(ts, 5); for(i=0; i<30; i++) { buf[0] = 0xA0; buf[1] = 0x01; buf[2] = 0xAA; ret = cst2xx_i2c_write(ts->client, buf, 3); if (ret < 0) { msleep(1); continue; } msleep(6); //wait enter download mode buf[0] = 0xA0; buf[1] = 0x03; //check whether into program mode ret = cst2xx_i2c_read_register(ts->client, buf, 1); if(ret < 0) { msleep(1); continue; } if (buf[0] == 0x55) { break; } } if(buf[0] != 0x55) { printk("hyn reciev 0x55 failed.\n"); return -1; } else { buf[0] = 0xA0; buf[1] = 0x06; buf[2] = 0x00; ret = cst2xx_i2c_write(ts->client, buf, 3); } return 0; } static int cst2xx_download_program(unsigned char *pdata, int len, struct hyn_ts *ts) { int i, ret, j,retry; unsigned char *i2c_buf; unsigned char temp_buf[8]; unsigned short eep_addr, iic_addr; int total_kbyte; i2c_buf = kmalloc(sizeof(unsigned char)*(512 + 2), GFP_KERNEL); if (i2c_buf == NULL) { return -1; } //make sure fwbin len is N*1K total_kbyte = len / 512; for (i=0; i>8; ret = cst2xx_i2c_write(ts->client, i2c_buf, 4); if (ret < 0) goto error_out; #if 0 i2c_buf[0] = 0xA0; i2c_buf[1] = 0x18; memcpy(i2c_buf + 2, pdata + eep_addr, 512); ret = cst2xx_i2c_write(ts->client, i2c_buf, 514); if (ret < 0) goto error_out; #else memcpy(i2c_buf, pdata + eep_addr, 512); for(j=0; j<128; j++) { iic_addr = (j<<2); temp_buf[0] = (iic_addr+0xA018)>>8; temp_buf[1] = (iic_addr+0xA018)&0xFF; temp_buf[2] = i2c_buf[iic_addr+0]; temp_buf[3] = i2c_buf[iic_addr+1]; temp_buf[4] = i2c_buf[iic_addr+2]; temp_buf[5] = i2c_buf[iic_addr+3]; ret = cst2xx_i2c_write(ts->client, temp_buf, 6); if (ret < 0) goto error_out; } #endif i2c_buf[0] = 0xA0; i2c_buf[1] = 0x04; i2c_buf[2] = 0xEE; ret = cst2xx_i2c_write(ts->client, i2c_buf, 3); if (ret < 0) goto error_out; msleep(600); for(retry=0;retry<10;retry++) { i2c_buf[0] = 0xA0; i2c_buf[1] = 0x05; ret = cst2xx_i2c_read_register(ts->client, i2c_buf, 1); if (ret < 0){ msleep(100); continue; } else { if (i2c_buf[0] != 0x55){ msleep(100); continue; }else{ break; } } } if(retry==10) { goto error_out; } } i2c_buf[0] = 0xA0; i2c_buf[1] = 0x01; i2c_buf[2] = 0x00; ret = cst2xx_i2c_write(ts->client, i2c_buf, 3); if (ret < 0) goto error_out; i2c_buf[0] = 0xA0; i2c_buf[1] = 0x03; i2c_buf[2] = 0x00; ret = cst2xx_i2c_write(ts->client, i2c_buf, 3); if (i2c_buf != NULL) { kfree(i2c_buf); i2c_buf = NULL; } return 0; error_out: if (i2c_buf != NULL) { kfree(i2c_buf); i2c_buf = NULL; } return -1; } static int cst2xx_read_checksum(struct hyn_ts *ts) { int ret; int i; unsigned int checksum; unsigned int bin_checksum; unsigned char buf[4]; const unsigned char *pData; for(i=0; i<10; i++) { buf[0] = 0xA0; buf[1] = 0x00; ret = cst2xx_i2c_read_register(ts->client, buf, 1); if(ret < 0) { msleep(2); continue; } if(buf[0]!=0) break; else msleep(2); } msleep(4); if(buf[0]==0x01) { buf[0] = 0xA0; buf[1] = 0x08; ret = cst2xx_i2c_read_register(ts->client, buf, 4); if(ret < 0) return -1; //handle read data --> checksum checksum = buf[0] + (buf[1]<<8) + (buf[2]<<16) + (buf[3]<<24); pData=(unsigned char *)fwbin +7680-4; //7*1024 +512 bin_checksum = pData[0] + (pData[1]<<8) + (pData[2]<<16) + (pData[3]<<24); printk("hyn checksum ic:0x%x. bin:0x%x------\n", checksum, bin_checksum); if(checksum!=bin_checksum) { printk("hyn check sum error.\n"); return -1; } } else { printk("hyn No checksum. buf[0]:%d.\n", buf[0]); return -1; } return 0; } static int cst2xx_update_firmware(struct i2c_client * client, struct hyn_ts *ts, unsigned char *pdata, int data_len) { int ret; int retry; unsigned char buf[4]; retry = 0; start_flow: printk("hyn enter the update firmware.\n"); disable_irq(ts->irq); msleep(20); ret = cst2xx_enter_download_mode(ts); if (ret < 0) { printk("hyn enter download mode failed.\n"); goto fail_retry; } ret = cst2xx_download_program(pdata, data_len,ts); if (ret < 0) { printk("hyn download program failed.\n"); goto fail_retry; } msleep(10); ret = cst2xx_read_checksum(ts); if(ret < 0){ printk("hyn check the updating checksum error.\n"); return ret; } else { buf[0] = 0xA0; //exit program buf[1] = 0x06; buf[2] = 0xEE; ret = cst2xx_i2c_write(client, buf, 3); if(ret < 0) goto fail_retry; } printk("hyn download firmware succesfully.\n"); msleep(100); hard_reset_chip(ts, 30); enable_irq(ts->irq); return 0; fail_retry: if (retry < 4) { retry++; goto start_flow; } return -1; } static int cst2xx_boot_update_fw(struct i2c_client * client, struct hyn_ts *ts) { return cst2xx_update_firmware(client, ts, fwbin, FW_BIN_SIZE); } static int cst2xx_check_code(struct hyn_ts *ts) { int retry = 0; int ret; unsigned char buf[4]; unsigned int fw_checksum,fw_version,fw_customer_id; unsigned int bin_checksum,bin_version; const unsigned char *pData; buf[0] = 0xD0; buf[1] = 0x4C; while(retry++ < 3) { ret = cst2xx_i2c_read_register(ts->client, buf, 1); if (ret > 0) break; msleep(2); } if((buf[0]==226)||(buf[0]==237)||(buf[0]==240)) { //checksum return 0; } else if(buf[0]==168) { buf[0] = 0xD0; buf[1] = 0x49; while(retry++ < 3) { ret = cst2xx_i2c_read_register(ts->client, buf, 2); if (ret > 0) break; msleep(2); } fw_customer_id=(buf[0]<<8)+buf[1]; printk("hyn fw_customer_id:%d. \r\n",fw_customer_id); //checksum buf[0] = 0xD2; buf[1] = 0x0C; while(retry++ < 5) { ret = cst2xx_i2c_read_register(ts->client, buf, 4); if (ret > 0) break; msleep(2); } fw_checksum = buf[3]; fw_checksum <<= 8; fw_checksum |= buf[2]; fw_checksum <<= 8; fw_checksum |= buf[1]; fw_checksum <<= 8; fw_checksum |= buf[0]; pData=(unsigned char *)fwbin +7680-4; //7*1024 +512 bin_checksum = pData[0] + (pData[1]<<8) + (pData[2]<<16) + (pData[3]<<24); if(fw_checksum!=bin_checksum) { printk("hyn checksum is different******bin_checksum:0x%x, fw_checksum:0x%x. \r\n",bin_checksum,fw_checksum); //chip version buf[0] = 0xD2; buf[1] = 0x08; while(retry++ < 5) { ret = cst2xx_i2c_read_register(ts->client, buf, 4); if (ret > 0) break; msleep(2); } fw_version = buf[3]; fw_version <<= 8; fw_version |= buf[2]; fw_version <<= 8; fw_version |= buf[1]; fw_version <<= 8; fw_version |= buf[0]; pData=(unsigned char *)fwbin +7680-8; //7*1024 +512 bin_version = pData[0] + (pData[1]<<8) + (pData[2]<<16) + (pData[3]<<24); printk("hyn bin_version is different******bin_version:0x%x, fw_version:0x%x. \r\n",bin_version,fw_version); if(bin_version>=fw_version) { ret = cst2xx_boot_update_fw(ts->client, ts); if(ret<0) { printk("hyn update firmware fail . \r\n"); hard_reset_chip(ts, 20); return -2; } else return 0; } else { printk("hyn bin_version is lower ,no need to update firmware.\n"); return 0; } } else { printk("hyn checksum :0x%x is same,no need to update firmware.\n",fw_checksum); return 0; } } else { printk("hyn check code error. buf[0]:%d.\n", buf[0]); ret = cst2xx_boot_update_fw(ts->client, ts); if(ret<0) return -2; else return 0; } } #ifdef HAVE_TOUCH_KEY static void report_key(struct hyn_ts *ts, u16 x, u16 y) { u16 i = 0; for(i = 0; i < MAX_KEY_NUM; i++) { if((hyn_key_data[i].x_min < x) && (x < hyn_key_data[i].x_max)&&(hyn_key_data[i].y_min < y) && (y < hyn_key_data[i].y_max)){ key = hyn_key_data[i].key; input_report_key(ts->input, key, 1); input_sync(ts->input); key_state_flag = 1; break; } } } #endif static void cst2xx_touch_down(struct input_dev *input_dev, s32 id,s32 x,s32 y,s32 w) { s32 temp_w = (w>>1); #ifdef ICS_SLOT_REPORT input_mt_slot(input_dev, id); input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1); input_report_abs(input_dev, ABS_MT_TRACKING_ID, id); input_report_abs(input_dev, ABS_MT_POSITION_X, x); input_report_abs(input_dev, ABS_MT_POSITION_Y, y); input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, temp_w); input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, temp_w); input_report_abs(input_dev, ABS_MT_PRESSURE, temp_w); #else input_report_key(input_dev, BTN_TOUCH, 1); input_report_abs(input_dev, ABS_MT_POSITION_X, x); input_report_abs(input_dev, ABS_MT_POSITION_Y, y); input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, temp_w); input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, temp_w); input_report_abs(input_dev, ABS_MT_TRACKING_ID, id); input_mt_sync(input_dev); #endif } static void cst2xx_touch_up(struct input_dev *input_dev, int id) { #ifdef ICS_SLOT_REPORT input_mt_slot(input_dev, id); //input_report_abs(input_dev, ABS_MT_TRACKING_ID, -1); input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); #else input_report_key(input_dev, BTN_TOUCH, 0); input_mt_sync(input_dev); #endif } #ifdef ANDROID_TOOL_SURPORT //debug tool support #define CST2XX_PROC_DIR_NAME "cst1xx_ts" #define CST2XX_PROC_FILE_NAME "cst1xx-update" static struct proc_dir_entry *g_proc_dir, *g_update_file; static int CMDIndex = 0; #if 1 static struct file *cst2xx_open_fw_file(char *path) { struct file * filp = NULL; int ret; //*old_fs_p = get_fs(); //set_fs(KERNEL_DS); filp = filp_open(path, O_RDONLY, 0); if (IS_ERR(filp)) { ret = PTR_ERR(filp); return NULL; } filp->f_op->llseek(filp, 0, 0); return filp; } static void cst2xx_close_fw_file(struct file * filp) { // set_fs(old_fs); if(filp) filp_close(filp,NULL); } static int cst2xx_read_fw_file(unsigned char *filename, unsigned char *pdata, int *plen) { struct file *fp; // mm_segment_t old_fs; int size; int length; int ret = -1; if((pdata == NULL) || (strlen(filename) == 0)) { printk("file name is null.\n"); return ret; } fp = cst2xx_open_fw_file(filename); if(fp == NULL) { printk("Open bin file faild.path:%s.\n", filename); goto clean; } length = fp->f_op->llseek(fp, 0, SEEK_END); fp->f_op->llseek(fp, 0, 0); size = fp->f_op->read(fp, pdata, length, &fp->f_pos); if(size == length) { ret = 0; *plen = length; } else { printk("read bin file length fail****size:%d*******length:%d .\n", size,length); } clean: cst2xx_close_fw_file(fp); return ret; } #else static struct file *cst2xx_open_fw_file(char *path, mm_segment_t * old_fs_p) { struct file * filp; int ret; *old_fs_p = get_fs(); set_fs(KERNEL_DS); filp = filp_open(path, O_RDONLY, 0); if (IS_ERR(filp)) { ret = PTR_ERR(filp); return NULL; } filp->f_op->llseek(filp, 0, 0); return filp; } static void cst2xx_close_fw_file(struct file * filp,mm_segment_t old_fs) { set_fs(old_fs); if(filp) filp_close(filp,NULL); } static int cst2xx_read_fw_file(unsigned char *filename, unsigned char *pdata, int *plen) { struct file *fp; mm_segment_t old_fs; int size; int length; int ret = -1; if((pdata == NULL) || (strlen(filename) == 0)) return ret; fp = cst2xx_open_fw_file(filename, &old_fs); if(fp == NULL) { printk("Open bin file faild.path:%s.\n", filename); goto clean; } length = fp->f_op->llseek(fp, 0, SEEK_END); fp->f_op->llseek(fp, 0, 0); size = fp->f_op->read(fp, pdata, length, &fp->f_pos); if(size == length) { ret = 0; *plen = length; } else { printk("read bin file length fail****size:%d*******length:%d .\n", size,length); } clean: cst2xx_close_fw_file(fp, old_fs); return ret; } #endif static int cst2xx_apk_fw_dowmload(struct i2c_client *client, unsigned char *pdata, int length) { int ret; ret = cst2xx_update_firmware(client, hyn_global_ts, pdata, FW_BIN_SIZE); if (ret < 0) { printk("online update fw failed.\n"); return -1; } return 0; } static ssize_t cst2xx_proc_read_foobar(struct file *page,char __user *user_buf, size_t count, loff_t *data) { unsigned char buf[512]; int len = 0; int ret; printk("cst2xx_proc_read_foobar********CMDIndex:%d. \n",CMDIndex); disable_irq(hyn_global_ts->irq); if (CMDIndex == 0) { sprintf(buf,"Hynitron touchscreen driver 1.0.\n"); //strcpy(page,buf); len = strlen(buf); ret = copy_to_user(user_buf,buf,len); } else if (CMDIndex == 1) { buf[0] = g_cst2xx_rx; buf[1] = g_cst2xx_tx; ret = copy_to_user(user_buf,buf,2); len = 2; } if(CMDIndex == 2 || CMDIndex == 3) { unsigned short rx,tx; int data_len; rx = g_cst2xx_rx; tx = g_cst2xx_tx; data_len = rx*tx*2 + 4 + (tx+rx)*2 + rx + rx; //374 if(CMDIndex == 2) //read diff { buf[0] = 0xD1; buf[1] = 0x0D; } else //rawdata { buf[0] = 0xD1; buf[1] = 0x0A; } ret = cst2xx_i2c_write(hyn_global_ts->client, buf, 2); if(ret < 0) { printk("Write command raw/diff mode failed.error:%d.\n", ret); goto END; } g_unnormal_mode = 1; msleep(14); while(!gpio_get_value(hyn_global_ts->irq_pin )); buf[0] = 0x80; buf[1] = 0x01; ret = cst2xx_i2c_write(hyn_global_ts->client, buf, 2); if(ret < 0) { printk("Write command(0x8001) failed.error:%d.\n", ret); goto END; } ret = cst2xx_i2c_read(hyn_global_ts->client, &buf[2], data_len); if(ret < 0) { printk("Read raw/diff data failed.error:%d.\n", ret); goto END; } msleep(2); buf[0] = 0xD1; buf[1] = 0x08; ret = cst2xx_i2c_write(hyn_global_ts->client, buf, 2); if(ret < 0) { printk("Write command normal mode failed.error:%d.\n", ret); goto END; } buf[0] = rx; buf[1] = tx; ret = copy_to_user(user_buf,buf,data_len + 2); len = data_len + 2; msleep(2); } END: g_unnormal_mode = 0; CMDIndex = 0; enable_irq(hyn_global_ts->irq); return len; } static ssize_t cst2xx_proc_write_foobar(struct file *file, const char __user *buffer,size_t count, loff_t *data) { unsigned char cmd[128]; unsigned char *pdata = NULL; int len; int ret; int length = 6*1024; if (count > 128) len = 128; else len = count; if (copy_from_user(cmd, buffer, len)) { printk("copy data from user space failed.\n"); return -EFAULT; } printk("cst2xx_proc_write_foobar*********cmd:%d*****%d******len:%d .\r\n", cmd[0], cmd[1], len); if (cmd[0] == 0) { pdata = kzalloc(sizeof(char)*length, GFP_KERNEL); if(pdata == NULL) { printk("zalloc GFP_KERNEL memory fail.\n"); return -ENOMEM; } ret = cst2xx_read_fw_file(&cmd[1], pdata, &length); if(ret < 0) { printk("cst2xx_read_fw_file fail.\n"); if(pdata != NULL) { kfree(pdata); pdata = NULL; } return -EPERM; } ret = cst2xx_apk_fw_dowmload(hyn_global_ts->client, pdata, length); if(ret < 0) { printk("update firmware failed.\n"); if(pdata != NULL) { kfree(pdata); pdata = NULL; } return -EPERM; } } else if (cmd[0] == 2) { //cst2xx_touch_release(); CMDIndex = cmd[1]; } else if (cmd[0] == 3) { CMDIndex = 0; } return count; } static const struct file_operations proc_tool_debug_fops = { .owner = THIS_MODULE, .read = cst2xx_proc_read_foobar, .write = cst2xx_proc_write_foobar, }; static int cst2xx_proc_fs_init(void) { int ret; g_proc_dir = proc_mkdir(CST2XX_PROC_DIR_NAME, NULL); if (g_proc_dir == NULL) { ret = -ENOMEM; goto out; } g_update_file = proc_create(CST2XX_PROC_FILE_NAME, 0777, g_proc_dir,&proc_tool_debug_fops); if (g_update_file == NULL) { ret = -ENOMEM; printk("proc_create CST2XX_PROC_FILE_NAME failed.\n"); goto no_foo; } /************************************** g_update_file = create_proc_entry(CST2XX_PROC_FILE_NAME, 0666, g_proc_dir); if (g_update_file == NULL) { ret = -ENOMEM; goto no_foo; } g_update_file->read_proc = cst2xx_proc_read_foobar; g_update_file->write_proc = cst2xx_proc_write_foobar; ************************************************/ return 0; no_foo: remove_proc_entry(CST2XX_PROC_FILE_NAME, g_proc_dir); out: return ret; } #endif static int report_flag = 0; static void cst2xx_ts_worker(struct work_struct *work) { //int rc, i; //u8 id, touches; //u16 x, y; u8 buf[60];//30 u8 i2c_buf[8]; u8 key_status; u8 key_id, finger_id, sw; int input_x = 0; int input_y = 0; int input_w = 0; int temp; u8 i, cnt_up, cnt_down; int ret, idx; int cnt, i2c_len, len_1, len_2; #ifdef HYN_NOID_VERSION u32 tmp1; u8 buf[4] = {0}; struct hyn_touch_info cinfo; #endif struct hyn_ts *ts = container_of(work, struct hyn_ts,work); //print_info("=====cst2xx_ts_worker=====\n"); #ifdef TPD_PROC_DEBUG if(hyn_proc_flag == 1) goto schedule; #endif //buf[0] = 0xD1; //buf[1] = 0x08; //ret = cst2xx_i2c_write(g_i2c_client, buf, 2); //if (ret < 0) //{ // printk("send get finger point cmd failed.\r\n"); // goto END; //} buf[0] = 0xD0; buf[1] = 0x00; ret = cst2xx_i2c_read_register(ts->client, buf, 7); if(ret < 0) { printk("hyn iic read touch point data failed.\n"); goto OUT_PROCESS; } key_status = buf[0]; if(buf[6] != 0xAB) { //printk("data is not valid..\r\n"); goto OUT_PROCESS; } cnt = buf[5] & 0x7F; if(cnt>MAX_FINGERS) goto OUT_PROCESS; else if(cnt==0) goto CLR_POINT; if(buf[5] == 0x80) { key_status = buf[0]; key_id = buf[1]; goto KEY_PROCESS; } else if(cnt == 0x01) { goto FINGER_PROCESS; } else { #ifdef TRANSACTION_LENGTH_LIMITED if((buf[5]&0x80) == 0x80) { //key i2c_len = (cnt - 1)*5 + 3; len_1 = i2c_len; for(idx=0; idx=6) { len_2 = 6; len_1 -= 6; } else { len_2 = len_1; len_1 = 0; } ret = cst2xx_i2c_read_register(ts->client, i2c_buf, len_2); if(ret < 0) goto OUT_PROCESS; for(i=0; i=6) { len_2 = 6; len_1 -= 6; } else { len_2 = len_1; len_1 = 0; } ret = cst2xx_i2c_read_register(ts->client, i2c_buf, len_2); if (ret < 0) goto OUT_PROCESS; for(i=0; iclient, &buf[5], i2c_len); if (ret < 0) goto OUT_PROCESS; i2c_len += 5; key_status = buf[i2c_len - 3]; key_id = buf[i2c_len - 2]; } else { buf[5] = 0xD0; buf[6] = 0x07; i2c_len = (cnt - 1)*5 + 1; ret = cst2xx_i2c_read_register(ts->client, &buf[5], i2c_len); if (ret < 0) goto OUT_PROCESS; i2c_len += 5; } #endif if (buf[i2c_len - 1] != 0xAB) { goto OUT_PROCESS; } } if((cnt > 0) && (key_status & 0x80)) //both key and point { if(report_flag==0xA5) goto KEY_PROCESS; } FINGER_PROCESS: i2c_buf[0] = 0xD0; i2c_buf[1] = 0x00; i2c_buf[2] = 0xAB; ret = cst2xx_i2c_write(ts->client, i2c_buf, 3); if (ret < 0) { printk("hyn send read touch info ending failed.\r\n"); hard_reset_chip(ts, 20); } idx = 0; cnt_up = 0; cnt_down = 0; for (i = 0; i < cnt; i++) { input_x = (unsigned int)((buf[idx + 1] << 4) | ((buf[idx + 3] >> 4) & 0x0F)); input_y = (unsigned int)((buf[idx + 2] << 4) | (buf[idx + 3] & 0x0F)); input_w = (unsigned int)(buf[idx + 4]); sw = (buf[idx] & 0x0F) >> 1; finger_id = (buf[idx] >> 4) & 0x0F; #ifdef SWAP_X_Y temp = input_x; input_x = input_y; input_y = temp; #endif #ifdef X_POL input_x = SCREEN_MAX_X - input_x; #endif #ifdef Y_POL input_y = SCREEN_MAX_Y - input_y; #endif //printk("Point x:%d, y:%d, id:%d, sw:%d.\r\n", input_x, input_y, finger_id, sw); if (sw == 0x03) { cst2xx_touch_down(ts->input, finger_id, input_x, input_y, input_w); cnt_down++; #ifdef HYN_GESTURE print_info("\n gsl_lcd_flag = %d ---- gsl_gesture_flag = %d .\n\n", gsl_lcd_flag, gsl_gesture_flag); if(1 == gsl_lcd_flag && 1 == gsl_gesture_flag){ print_info("auto wake up lcd\n"); rk_send_wakeup_key(); }else{ gsl_gesture_flag = 0; } #endif } else { cnt_up++; #ifdef ICS_SLOT_REPORT cst2xx_touch_up(ts->input, finger_id); #endif } idx += 5; } #ifndef ICS_SLOT_REPORT if((cnt_up>0) && (cnt_down==0)) cst2xx_touch_up(ts->input, 0); #endif if(cnt_down==0) report_flag = 0; else report_flag = 0xCA; input_sync(ts->input); goto END; KEY_PROCESS: i2c_buf[0] = 0xD0; i2c_buf[1] = 0x00; i2c_buf[2] = 0xAB; ret = cst2xx_i2c_write(ts->client, i2c_buf, 3); if (ret < 0) { printk("hyn send read touch info ending failed.\r\n"); } #ifdef HAVE_TOUCH_KEY if(key_status&0x80) { if((key_status&0x7F)==0x03) { i = (key_id>>4)-1; key = hyn_key_data[i].key; input_report_key(ts->input, key, 1); report_flag = 0xA5; } else { input_report_key(ts->input, key, 0); report_flag = 0; } } #endif input_sync(ts->input); goto END; CLR_POINT: #ifdef SLEEP_CLEAR_POINT #ifdef ICS_SLOT_REPORT for(i=0; i<=MAX_CONTACTS; i++) { input_mt_slot(ts->input, i); input_report_abs(ts->input, ABS_MT_TRACKING_ID, -1); input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, false); } #else input_mt_sync(ts->input); #endif input_sync(ts->input); #endif OUT_PROCESS: i2c_buf[0] = 0xD0; i2c_buf[1] = 0x00; i2c_buf[2] = 0xAB; ret = cst2xx_i2c_write(ts->client, i2c_buf, 3); if (ret < 0) { printk("send read touch info ending failed.\n"); hard_reset_chip(ts, 20); } END: #ifdef HYN_MONITOR if(i2c_lock_flag != 0) goto i2c_lock_schedule; else i2c_lock_flag = 1; #endif #ifdef TPD_PROC_DEBUG schedule: #endif #ifdef HYN_MONITOR i2c_lock_flag = 0; i2c_lock_schedule: #endif //enable_irq(ts->irq); cnt=0; //printk("========cst2xx_ts_worker end=========\n"); } #ifdef HYN_MONITOR static void hyn_monitor_worker(struct work_struct *work) { //u8 write_buf[4] = {0}; u8 read_buf[4] = {0}; char init_chip_flag = 0; // print_info("----------------hyn_monitor_worker-----------------\n"); struct hyn_ts *ts = container_of(work, struct hyn_ts,hyn_monitor_work.work); if(i2c_lock_flag != 0) { //i2c_lock_flag=1; goto queue_monitor_work; } else i2c_lock_flag = 1; //hyn_ts_read(ts->client, 0x80, read_buf, 4); //printk("======read 0x80: %x %x %x %x ======tony0geshu\n",read_buf[3], read_buf[2], read_buf[1], read_buf[0]); hyn_ts_read(ts->client, 0xb0, read_buf, 4); if(read_buf[3] != 0x5a || read_buf[2] != 0x5a || read_buf[1] != 0x5a || read_buf[0] != 0x5a) b0_counter ++; else b0_counter = 0; if(b0_counter > 1) { printk("======read 0xb0: %x %x %x %x ======\n",read_buf[3], read_buf[2], read_buf[1], read_buf[0]); init_chip_flag = 1; b0_counter = 0; goto queue_monitor_init_chip; } hyn_ts_read(ts->client, 0xb4, read_buf, 4); int_2nd[3] = int_1st[3]; int_2nd[2] = int_1st[2]; int_2nd[1] = int_1st[1]; int_2nd[0] = int_1st[0]; int_1st[3] = read_buf[3]; int_1st[2] = read_buf[2]; int_1st[1] = read_buf[1]; int_1st[0] = read_buf[0]; //printk("======int_1st: %x %x %x %x , int_2nd: %x %x %x %x ======\n",int_1st[3], int_1st[2], int_1st[1], int_1st[0], int_2nd[3], int_2nd[2],int_2nd[1],int_2nd[0]); if(int_1st[3] == int_2nd[3] && int_1st[2] == int_2nd[2] &&int_1st[1] == int_2nd[1] && int_1st[0] == int_2nd[0]) { printk("======int_1st: %x %x %x %x , int_2nd: %x %x %x %x ======\n",int_1st[3], int_1st[2], int_1st[1], int_1st[0], int_2nd[3], int_2nd[2],int_2nd[1],int_2nd[0]); init_chip_flag = 1; goto queue_monitor_init_chip; } hyn_ts_read(ts->client, 0xbc, read_buf, 4); if(read_buf[3] != 0 || read_buf[2] != 0 || read_buf[1] != 0 || read_buf[0] != 0) bc_counter++; else bc_counter = 0; if(bc_counter > 1) { printk("======read 0xbc: %x %x %x %x======\n",read_buf[3], read_buf[2], read_buf[1], read_buf[0]); init_chip_flag = 1; bc_counter = 0; } /* write_buf[3] = 0x01; write_buf[2] = 0xfe; write_buf[1] = 0x10; write_buf[0] = 0x00; hyn_ts_write(ts->client, 0xf0, write_buf, 4); hyn_ts_read(ts->client, 0x10, read_buf, 4); hyn_ts_read(ts->client, 0x10, read_buf, 4); if(read_buf[3] < 10 && read_buf[2] < 10 && read_buf[1] < 10 && read_buf[0] < 10) dac_counter ++; else dac_counter = 0; if(dac_counter > 1) { printk("======read DAC1_0: %x %x %x %x ======\n",read_buf[3], read_buf[2], read_buf[1], read_buf[0]); init_chip_flag = 1; dac_counter = 0; } */ queue_monitor_init_chip: if(init_chip_flag) init_chip(ts->client,ts); i2c_lock_flag = 0; queue_monitor_work: queue_delayed_work(hyn_monitor_workqueue, &ts->hyn_monitor_work, 100); } #endif static irqreturn_t hyn_ts_irq(int irq, void *dev_id) { ///struct hyn_ts *ts = dev_id; struct hyn_ts *ts = (struct hyn_ts*)dev_id; //printk("========cst2xx Interrupt=========\n"); #ifdef HYN_GESTURE if(1 == gsl_lcd_flag) gsl_gesture_flag = 1; #endif //disable_irq_nosync(ts->irq); if (!work_pending(&ts->work)) { queue_work(ts->wq, &ts->work); } return IRQ_HANDLED; } static int cst2xx_ts_init(struct i2c_client *client, struct hyn_ts *ts) { struct input_dev *input_device; int rc = 0; printk("hyn cst2xx Enter %s\n", __func__); //input_device = devm_input_allocate_device(&ts->client->dev); input_device = input_allocate_device(); if (!input_device) { rc = -ENOMEM; goto error_alloc_dev; } ts->input = input_device; input_device->name = CST2XX_I2C_NAME; input_device->id.bustype = BUS_I2C; input_device->dev.parent = &client->dev; input_set_drvdata(input_device, ts); #ifdef ICS_SLOT_REPORT __set_bit(EV_ABS, input_device->evbit); __set_bit(EV_KEY, input_device->evbit); __set_bit(EV_REP, input_device->evbit); __set_bit(INPUT_PROP_DIRECT, input_device->propbit); input_mt_init_slots(input_device, (MAX_CONTACTS+1), 0); #else input_set_abs_params(input_device,ABS_MT_TRACKING_ID, 0, (MAX_CONTACTS+1), 0, 0); set_bit(EV_ABS, input_device->evbit); set_bit(EV_KEY, input_device->evbit); __set_bit(INPUT_PROP_DIRECT, input_device->propbit); input_device->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); #endif #ifdef HAVE_TOUCH_KEY input_device->evbit[0] = BIT_MASK(EV_KEY); //input_device->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); for (i = 0; i < MAX_KEY_NUM; i++) set_bit(key_array[i], input_device->keybit); #endif set_bit(ABS_MT_POSITION_X, input_device->absbit); set_bit(ABS_MT_POSITION_Y, input_device->absbit); set_bit(ABS_MT_TOUCH_MAJOR, input_device->absbit); set_bit(ABS_MT_WIDTH_MAJOR, input_device->absbit); input_set_abs_params(input_device,ABS_MT_POSITION_X, 0, SCREEN_MAX_X, 0, 0); input_set_abs_params(input_device,ABS_MT_POSITION_Y, 0, SCREEN_MAX_Y, 0, 0); input_set_abs_params(input_device,ABS_MT_TOUCH_MAJOR, 0, PRESS_MAX, 0, 0); input_set_abs_params(input_device,ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0); //client->irq = IRQ_PORT; //ts->irq = client->irq; //create_workqueue ts->wq = create_singlethread_workqueue("kworkqueue_ts"); if (!ts->wq) { dev_err(&client->dev, "hyn Could not create workqueue\n"); goto error_wq_create; } flush_workqueue(ts->wq); INIT_WORK(&ts->work, cst2xx_ts_worker); rc = input_register_device(input_device); if (rc) goto error_unreg_device; return 0; error_unreg_device: destroy_workqueue(ts->wq); error_wq_create: input_free_device(input_device); error_alloc_dev: //kfree(ts->touch_data); return rc; } static int hyn_ts_suspend(struct device *dev) { struct hyn_ts *ts = dev_get_drvdata(dev); int i; printk("I'am in hyn_ts_suspend() start\n"); #ifdef HYN_MONITOR printk( "hyn_ts_suspend () : cancel hyn_monitor_work\n"); cancel_delayed_work_sync(&ts->hyn_monitor_work); #endif #ifdef HYN_GESTURE // disable_irq_nosync(ts->irq); #else disable_irq_nosync(ts->irq); if(h_wake_pin != 0) { gpio_direction_output(ts->rst_pin, 0); } #endif #ifdef SLEEP_CLEAR_POINT msleep(10); #ifdef ICS_SLOT_REPORT for(i=1; i<=MAX_CONTACTS; i++) { input_mt_slot(ts->input, i); input_report_abs(ts->input, ABS_MT_TRACKING_ID, -1); input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, false); } #else input_mt_sync(ts->input); #endif input_sync(ts->input); #endif return 0; } static int hyn_ts_resume(struct device *dev) { struct hyn_ts *ts = dev_get_drvdata(dev); int i, rc; printk("I'am in hyn_ts_resume() start\n"); hard_reset_chip(ts, 30); #ifdef SLEEP_CLEAR_POINT #ifdef ICS_SLOT_REPORT for(i=1; i<=MAX_CONTACTS; i++) { input_mt_slot(ts->input, i); input_report_abs(ts->input, ABS_MT_TRACKING_ID, -1); input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, false); } #else input_mt_sync(ts->input); #endif input_sync(ts->input); #endif #ifdef HYN_MONITOR printk( "hyn_ts_resume () : queue hyn_monitor_work\n"); queue_delayed_work(hyn_monitor_workqueue, &ts->hyn_monitor_work, 300); #endif #ifdef HYN_GESTURE // enable_irq(ts->irq); #else enable_irq(ts->irq); #endif msleep(200); rc = cst2xx_check_code(ts); if(rc < 0){ printk("hyn check code error.\n"); return rc; } return 0; } static int hyn_ts_early_suspend(struct tp_device *tp_d) { struct hyn_ts *ts = container_of(tp_d, struct hyn_ts, tp); #ifdef HYN_GESTURE gsl_lcd_flag = 1; #endif printk("[CST2XX] Enter %s\n", __func__); hyn_ts_suspend(&ts->client->dev); return 0; } static int hyn_ts_late_resume(struct tp_device *tp_d) { struct hyn_ts *ts = container_of(tp_d, struct hyn_ts, tp); #ifdef HYN_GESTURE gsl_lcd_flag = 0; gsl_gesture_flag = 0; #endif printk("[CST2XX] Enter %s\n", __func__); hyn_ts_resume(&ts->client->dev); return 0; } static int hyn_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc; struct hyn_ts *ts=NULL; struct device_node *np = client->dev.of_node; enum of_gpio_flags wake_flags; unsigned long irq_flags; printk("cst2xx enter %s\n", __func__); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "hyn I2C functionality not supported\n"); return -ENODEV; } ts = devm_kzalloc(&client->dev,sizeof(*ts), GFP_KERNEL); //ts = kzalloc(sizeof(*ts), GFP_KERNEL); if (!ts) return -ENOMEM; printk("==kzalloc success=\n"); ts->client = client; i2c_set_clientdata(client, ts); ts->device_id = id->driver_data; //×¢ÒâÖжϵÄÃû×Ö irq ts->irq_pin = of_get_named_gpio_flags(np, "irq-gpio", 0, (enum of_gpio_flags *)&irq_flags); ts->rst_pin = of_get_named_gpio_flags(np, "wake-gpio", 0, &wake_flags); if (gpio_is_valid(ts->rst_pin)) { rc = devm_gpio_request_one(&client->dev, ts->rst_pin, (wake_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, "cst21680 wake pin"); if (rc != 0) { dev_err(&client->dev, "cst21680 wake pin error\n"); //return -EIO; } else { h_wake_pin = ts->rst_pin; } //msleep(100); } else { dev_info(&client->dev, "wake pin invalid\n"); } if (gpio_is_valid(ts->irq_pin)) { rc = 0x00; //rc = devm_gpio_request_one(&client->dev, ts->irq_pin, IRQF_TRIGGER_RISING, "gslX680 irq pin"); //printk("huang-GPIOF_OUT_INIT_LOW=%d\n\n",GPIOF_OUT_INIT_LOW); // printk("huang-GPIOF_OUT_INIT_LOW=%d\n\n",IRQF_TRIGGER_RISING); //rc = request_irq(client->irq,gsl_ts_irq,IRQF_TRIGGER_RISING,client->name,ts); //printk("huang-GPIOF_OUT_INIT_LOW=%d\n\n",IRQF_TRIGGER_RISING); if (rc != 0) { dev_err(&client->dev, "cst21680 irq pin error\n"); return -EIO; } } else { dev_info(&client->dev, "irq pin invalid\n"); } msleep(40); //runing rc = cst2xx_test_i2c(client); if (rc < 0) { dev_err(&client->dev, "hyn cst2xx test iic error.\n"); return rc; } msleep(20); rc = cst2xx_check_code(ts); if(rc < 0){ printk("hyn check code error.\n"); return rc; } rc = cst2xx_ts_init(client, ts); if (rc < 0) { printk("hyn CST2XX init failed\n"); goto error_mutex_destroy; } ts->irq = gpio_to_irq(ts->irq_pin); printk("cst2xx request ts->irq is :%d\n", ts->irq); #if 0 rc = request_irq(client->irq, hyn_ts_irq, IRQF_TRIGGER_RISING, client->name, ts); if (rc < 0) { printk( "hyn_probe: request irq failed\n"); goto error_mutex_destroy; } #endif if(ts->irq) { rc = devm_request_threaded_irq(&client->dev, ts->irq, NULL, hyn_ts_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, client->name, ts); if (rc != 0) { printk(KERN_ALERT "Cannot allocate ts INT!ERRNO:%d\n", rc); goto error_mutex_destroy; } disable_irq(ts->irq); } else { printk("cst21680 irq req fail\n"); goto error_mutex_destroy; } /* create debug attribute */ //rc = device_create_file(&ts->input->dev, &dev_attr_debug_enable); ts->tp.tp_suspend = hyn_ts_early_suspend; ts->tp.tp_resume = hyn_ts_late_resume; tp_register_fb(&ts->tp); #ifdef CONFIG_HAS_EARLYSUSPEND ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; //ts->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1; ts->early_suspend.suspend = hyn_ts_early_suspend; ts->early_suspend.resume = hyn_ts_late_resume; register_early_suspend(&ts->early_suspend); #endif #ifdef HYN_MONITOR printk( "hyn_ts_probe () : queue hyn_monitor_workqueue\n"); INIT_DELAYED_WORK(&ts->hyn_monitor_work, hyn_monitor_worker); hyn_monitor_workqueue = create_singlethread_workqueue("hyn_monitor_workqueue"); queue_delayed_work(hyn_monitor_workqueue, &ts->hyn_monitor_work, 1000); #endif #ifdef ANDROID_TOOL_SURPORT cst2xx_proc_fs_init(); hyn_global_ts=ts; #endif #ifdef TPD_PROC_DEBUG hyn_config_proc = create_proc_entry(HYN_CONFIG_PROC_FILE, 0666, NULL); printk("[tp-hyn] [%s] hyn_config_proc = %x \n",__func__,hyn_config_proc); if (hyn_config_proc == NULL) { print_info("create_proc_entry %s failed\n", HYN_CONFIG_PROC_FILE); } else { hyn_config_proc->read_proc = hyn_config_read_proc; hyn_config_proc->write_proc = hyn_config_write_proc; } #endif enable_irq(ts->irq); printk("[CST2XX] End %s\n", __func__); return 0; error_mutex_destroy: input_free_device(ts->input); return rc; } static int hyn_ts_remove(struct i2c_client *client) { struct hyn_ts *ts = i2c_get_clientdata(client); printk("==hyn_ts_remove=\n"); #ifdef CONFIG_HAS_EARLYSUSPEND unregister_early_suspend(&ts->early_suspend); #endif #ifdef HYN_MONITOR cancel_delayed_work_sync(&ts->hyn_monitor_work); destroy_workqueue(hyn_monitor_workqueue); #endif device_init_wakeup(&client->dev, 0); cancel_work_sync(&ts->work); destroy_workqueue(ts->wq); input_unregister_device(ts->input); //device_remove_file(&ts->input->dev, &dev_attr_debug_enable); //kfree(ts->touch_data); return 0; } #if 1 static struct of_device_id hyn_ts_ids[] = { { .compatible = "cst2xx" }, { } }; #endif static const struct i2c_device_id hyn_ts_id[] = { {CST2XX_I2C_NAME, 0}, {} }; MODULE_DEVICE_TABLE(i2c, hyn_ts_id); static struct i2c_driver hyn_ts_driver = { .driver = { .name = CST2XX_I2C_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(hyn_ts_ids), }, #ifndef CONFIG_HAS_EARLYSUSPEND // .suspend = hyn_ts_suspend, // .resume = hyn_ts_resume, #endif .probe = hyn_ts_probe, .remove = hyn_ts_remove, .id_table = hyn_ts_id, }; static int __init hyn_ts_init(void) { int ret; //printk("==hyn_ts_init==\n"); ret = i2c_add_driver(&hyn_ts_driver); //printk("ret=%d\n",ret); return ret; } static void __exit hyn_ts_exit(void) { //printk("==hyn_ts_exit==\n"); i2c_del_driver(&hyn_ts_driver); return; } module_init(hyn_ts_init); module_exit(hyn_ts_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("HYNCST2XX touchscreen controller driver"); MODULE_AUTHOR("Tim.Tan"); MODULE_ALIAS("platform:hyn_ts");