i2c设备-tp 驱动分析(一)

转载请注明出处 @SayidZhong
https://sayid95.github.io/

下面以思立威的tp为例,不足之处还望指正

主要是梳理下逻辑,主要看其中代码的注释,涉及的知识点还是比较多的中断、队列以后再写

先找到驱动入口,module_init(tpd_driver_init);

接着看到 init 函数里面

/* called when loaded into kernel */
static int __init tpd_driver_init(void) {

    printk("Sileadinc gslX680 touch panel driver init\n");
//注册一个i2c设备,主要配置 IC 挂载在哪条 i2c 总线
    i2c_register_board_info(TPD_I2C_NUMBER, &gslX680_i2c_tpd, 1);

    tpd_get_dts_info();

//添加一个驱动到静态数组 tpd_driver_list 中,加载 TP 设备驱动
    if(tpd_driver_add(&tpd_device_driver) < 0)   
        printk("add gslX680 -driver failed\n");
    return 0;
}

tpd_device_driver 结构体看下面

//tpd_device_name 被保存到 tpd_driver_list[0] 数组中

static struct tpd_driver_t tpd_device_driver = {
    .tpd_device_name = "GSL_I2C_NAME",
    .tpd_local_init = tpd_local_init,
    .suspend = tpd_suspend,
    .resume = tpd_resume,
#ifdef TPD_HAVE_BUTTON
    .tpd_have_button = 1,
#else
    .tpd_have_button = 0,
#endif
};

在看上面 tpd_device_driver 结构体 中的 tpd_local_init 函数

int tpd_local_init(void)
{
    int retval;
    printk("==tpd_local_init==\n");
    TPD_DMESG("Focaltech gslx68x I2C Touchscreen Driver...\n");
//设置vtouch=2.8v
    tpd->reg = regulator_get(tpd->tpd_dev, "vtouch");
    retval = regulator_set_voltage(tpd->reg, 2800000, 2800000);
    if (retval != 0) {
        TPD_DMESG("Failed to set reg-vgp6 voltage: %d\n", retval);
        return -1;
    }

    if(i2c_add_driver(&tpd_i2c_driver)!=0) {
        TPD_DMESG("unable to add i2c driver.\n");
        return -1;
    }

//设置input设备 tpd->dev 支持的最大手指触摸个数
     input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, (MAX_CONTACTS+1), 0, 0);

//MTK虚拟按键的实现在 tpd_button.c 中实现
#ifdef TPD_HAVE_BUTTON
    tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);
  // initialize tpd button data
#endif

#if (defined(TPD_WARP_START) && defined(TPD_WARP_END))
    TPD_DO_WARP = 1;
    memcpy(tpd_wb_start, tpd_wb_start_local, TPD_WARP_CNT*4);
    memcpy(tpd_wb_end, tpd_wb_start_local, TPD_WARP_CNT*4);
#endif

#if (defined(TPD_HAVE_CALIBRATION) && !defined(TPD_CUSTOM_CALIBRATION))
    memcpy(tpd_calmat, tpd_calmat_local, 8*4);
    memcpy(tpd_def_calmat, tpd_def_calmat_local, 8*4);
#endif
//设置TP类型为电容屏
    tpd_type_cap = 1;

    printk("==tpd_local_init end==\n");
    return 0;
}

i2c_add_driver(&tpd_i2c_driver) 其中的结构体

//包含设备的名称、以及 TP 初始化的函数接口
struct i2c_driver tpd_i2c_driver = {
    .driver = {
        .name = GSL_I2C_NAME,
        .of_match_table = of_match_ptr(gslx68x_dt_match),
    },
    .probe = tpd_i2c_probe,
    .remove = tpd_i2c_remove,
    .id_table = tpd_i2c_id,
    .detect = tpd_i2c_detect,
};

分析下 比较重要的 probe 探测函数

static int tpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) {
    int err = 0;
    //char buffer[5];
    //int status=0;
    int ret;
    printk("==tpd_i2c_probe==i2c %d\n", TPD_I2C_NUMBER);

//malloc  memory
    ddata = kzalloc(sizeof(struct gsl_ts_data), GFP_KERNEL);
    if (!ddata) {
        print_info("alloc ddata memory error\n");
        return -ENOMEM;
    }
    ddata->client = client;
        if(i2c_client->addr !=0x40)
        {
            //printk("==gsl---erro---addr=%d\n",ddata->addr);
            //i2c_client->addr=0x40;
        }

    of_get_ft5x0x_platform_data(&client->dev);

    tpd_gpio_output(GTP_RST_PORT, 0);
    //gpio_direction_output(tpd_rst_gpio_number, 0);
    msleep(100);
    ret = regulator_enable(tpd->reg);
    if (ret != 0)
        TPD_DMESG("Failed to enable reg-vgp2: %d\n", ret);

    //gpio_direction_output(tpd_rst_gpio_number, 1);
    msleep(100);
    tpd_gpio_output(GTP_RST_PORT, 1);
    msleep(100);

    /* set tp INT mode */
    tpd_gpio_as_int(GTP_INT_PORT);
    //gpio_direction_input(tpd_int_gpio_number);
    msleep(100);

    i2c_client = client;
    if(i2c_client->addr !=0x40)
        {
            printk("gsl-addr=%d\n",i2c_client->addr);
            i2c_client->addr=0x40;

        }
    printk("gsl-addr=%d\n",i2c_client->addr);
    disable_irq(touch_irq);
    msleep(100);
    init_chip(i2c_client);
   
    msleep(100);
    init_chip(ddata->client);
    check_mem_data(i2c_client);
    
    check_mem_data(ddata->client);
    tpd_irq_registration();
    //msleep(100);
    tpd_load_status = 1;

//创建名为 TPD_DEVICE 线程thread是为了处理中断来临之后读取坐标、上报坐标、手势识别、按键等等信息
    thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);
    if (IS_ERR(thread)) {
        err = PTR_ERR(thread);
        TPD_DMESG(TPD_DEVICE " failed to create kernel thread: %d\n", err);
    }

//静电检测
#ifdef GSL_MONITOR
    printk( "tpd_i2c_probe () : queue gsl_monitor_workqueue\n");
    INIT_DELAYED_WORK(&gsl_monitor_work, gsl_monitor_worker);
    gsl_monitor_workqueue = create_singlethread_workqueue("gsl_monitor_workqueue");
    queue_delayed_work(gsl_monitor_workqueue, &gsl_monitor_work, 1000);
#endif

#ifdef TPD_PROC_DEBUG
#if 0
        gsl_config_proc = create_proc_entry(GSL_CONFIG_PROC_FILE, 0666, NULL);
        printk("[tp-gsl] [%s] gsl_config_proc = %x \n",__func__,gsl_config_proc);
        if (gsl_config_proc == NULL)
        {
            print_info("create_proc_entry %s failed\n", GSL_CONFIG_PROC_FILE);
        }
        else
        {
            gsl_config_proc->read_proc = gsl_config_read_proc;
            gsl_config_proc->write_proc = gsl_config_write_proc;
        }
#else
  
//通过创建 proc 文件节点来进行adb读写(现在大都用/sys/下面的设备节点)
//安卓7.0 可能需要 0777 权限才能创建文件节点
        proc_create(GSL_CONFIG_PROC_FILE,0666,NULL,&gsl_seq_fops);
#endif
        gsl_proc_flag = 0;
#endif
    enable_irq(touch_irq);
    printk("==tpd_i2c_probe end==\n");

    return 0;
}

看下 touch_event_handler 线程

static int touch_event_handler(void *unused)
{
    struct sched_param param = { .sched_priority = 4 };

// 调度策略和调度参数分别设置为 param 指向的 sched_param
    sched_setscheduler(current, SCHED_RR, &param);
        printk("gsl-touch_event_handler, task running===\n");

    do{
        set_current_state(TASK_INTERRUPTIBLE);// 设置当前线程可被打断
        wait_event_interruptible(waiter, tpd_flag != 0);//收到唤醒信号,结束等侍,执行下一条代码
        tpd_flag = 0;// 判断是否有中断标记
        TPD_DEBUG_SET_TIME;
     
        set_current_state(TASK_RUNNING);// 标记当前线程在执行

        eint_flag = 0;//清标志位
        report_data_handle();//上报数据

    } while (!kthread_should_stop());

    return 0;
}

接着看下 report_data_handle(); 怎么上报数据的

static void report_data_handle(void)
{
    u8 touch_data[MAX_FINGERS * 4 + 4] = {0};
    u8 buf[4] = {0};
    unsigned char id, point_num = 0;
    unsigned int x, y, temp_a, temp_b, i;
#ifdef GSL_NOID_VERSION
    struct gsl_touch_info cinfo={{0},{0},{0},0};
    int tmp1 = 0;
#endif
#ifdef GSL_MONITOR
    if(i2c_lock_flag != 0)
        return;
    else
        i2c_lock_flag = 1;
#endif

#ifdef TPD_PROC_DEBUG
    if(gsl_proc_flag == 1)
        return;
#endif
//触摸点数寄存器0x80
 
//中断函数的作用就是报点,就是将从地址 0x80 开始的 44byte 的数据(存放触摸点的信息)通过系统的 API 上报给上层

//对读出的 44 个字节进行分析,得到初步触摸点坐标

    i2c_smbus_read_i2c_block_data(i2c_client, 0x80, 4, &touch_data[0]);
    point_num = touch_data[0];
    if(point_num > 0)
        i2c_smbus_read_i2c_block_data(i2c_client, 0x84, 8, &touch_data[4]);
    if(point_num > 2)
        i2c_smbus_read_i2c_block_data(i2c_client, 0x8c, 8, &touch_data[12]);
    if(point_num > 4)
        i2c_smbus_read_i2c_block_data(i2c_client, 0x94, 8, &touch_data[20]);
    if(point_num > 6)
        i2c_smbus_read_i2c_block_data(i2c_client, 0x9c, 8, &touch_data[28]);
    if(point_num > 8)
        i2c_smbus_read_i2c_block_data(i2c_client, 0xa4, 8, &touch_data[36]);

#ifdef GSL_NOID_VERSION
    cinfo.finger_num = point_num;
    //printk("tp-gsl-------------  finger_num = %d\n",cinfo.finger_num);
    for(i = 0; i < (point_num < MAX_CONTACTS ? point_num : MAX_CONTACTS); i ++)
    {
        temp_a = touch_data[(i + 1) * 4 + 3] & 0x0f;
        temp_b = touch_data[(i + 1) * 4 + 2];
        cinfo.x[i] = temp_a << 8 |temp_b;
        temp_a = touch_data[(i + 1) * 4 + 1];
        temp_b = touch_data[(i + 1) * 4 + 0];
        cinfo.y[i] = temp_a << 8 |temp_b;
        cinfo.id[i] = ((touch_data[(i + 1) * 4 + 3] & 0xf0)>>4);
        //printk("tp-gsl-------------  before: x[%d] = %d, y[%d] = %d, id[%d] = %d \n",i,cinfo.x[i],i,cinfo.y[i],i,cinfo.id[i]);
    }
    cinfo.finger_num = (touch_data[3]<<24)|(touch_data[2]<<16)|
        (touch_data[1]<<8)|touch_data[0];
    gsl_alg_id_main(&cinfo);
    tmp1=gsl_mask_tiaoping();
    print_info("[tp-gsl] tmp1=%x\n",tmp1);
    if(tmp1>0&&tmp1<0xffffffff)
    {
        buf[0]=0xa;buf[1]=0;buf[2]=0;buf[3]=0;
        i2c_smbus_write_i2c_block_data(i2c_client,0xf0,4,buf);
        buf[0]=(u8)(tmp1 & 0xff);
        buf[1]=(u8)((tmp1>>8) & 0xff);
        buf[2]=(u8)((tmp1>>16) & 0xff);
        buf[3]=(u8)((tmp1>>24) & 0xff);
        print_info("tmp1=%08x,buf[0]=%02x,buf[1]=%02x,buf[2]=%02x,buf[3]=%02x\n",
            tmp1,buf[0],buf[1],buf[2],buf[3]);
        i2c_smbus_write_i2c_block_data(i2c_client,0x8,4,buf);
    }
    point_num = cinfo.finger_num;
#endif

    for(i = 1 ;i <= MAX_CONTACTS; i ++)
    {
        if(point_num == 0)
            id_sign[i] = 0;
        id_state_flag[i] = 0;
    }
    for(i = 0; i < (point_num < MAX_FINGERS ? point_num : MAX_FINGERS); i ++)
    {
    #ifdef GSL_NOID_VERSION
        id = cinfo.id[i];
        x =  cinfo.x[i];
        y =  cinfo.y[i];
    #else
        id = touch_data[(i + 1) * 4 + 3] >> 4;
        temp_a = touch_data[(i + 1) * 4 + 3] & 0x0f;
        temp_b = touch_data[(i + 1) * 4 + 2];
        x = temp_a << 8 |temp_b;
        temp_a = touch_data[(i + 1) * 4 + 1];
        temp_b = touch_data[(i + 1) * 4 + 0];
        y = temp_a << 8 |temp_b;
    #endif

        if(1 <= id && id <= MAX_CONTACTS)
        {
        #ifdef FILTER_POINT
            filter_point(x, y ,id);
        #else
            record_point(x, y , id);
        #endif
            tpd_down(id, x_new, y_new, 10);
            id_state_flag[id] = 1;
        }
    }
    for(i = 1; i <= MAX_CONTACTS; i ++)
    {
        if( (0 == point_num) || ((0 != id_state_old_flag[i]) && (0 == id_state_flag[i])) )
        {
            id_sign[i]=0;
        }
        id_state_old_flag[i] = id_state_flag[i];
    }
    if(0 == point_num)
    {
        tpd_up();
    }
    input_sync(tpd->dev);
#ifdef GSL_MONITOR
    i2c_lock_flag = 0;
#endif
}

还有写按下手指和抬起函数

void tpd_down( int id, int x, int y, int p)
{
    //printk("gsl----------------tpd_down id: %d, x:%d, y:%d------ \n", id, x, y);
//通过系统的 API 上报给上层,上报给上 层主要是通过下面几个 API 来实现的:
    input_report_key(tpd->dev, BTN_TOUCH, 1);
    input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, 1);
    input_report_abs(tpd->dev, ABS_MT_POSITION_X, x);
    input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y);
    input_report_abs(tpd->dev, ABS_MT_TRACKING_ID, id);
    input_mt_sync(tpd->dev);
}
void tpd_up(void)
{
    input_report_key(tpd->dev, BTN_TOUCH, 0);
    input_mt_sync(tpd->dev);
}

最后还有休眠和唤醒以及 check 函数

static void tpd_suspend(struct device *h)
{
    printk("==tpd_suspend==\n");

    tpd_halt = 1;
    disable_irq(touch_irq);//屏蔽中断
    tpd_gpio_output(GTP_RST_PORT, 0);//RST拉低
}

/* Function to manage power-on resume */
//void tpd_resume(struct early_suspend *h)
static void tpd_resume(struct device *h)
{
    printk("==tpd_resume==\n");
    tpd_gpio_output(GTP_RST_PORT,1);//RST拉高
    msleep(20);
    reset_chip(i2c_client);//ic复位
    startup_chip(i2c_client);//启动IC
    check_mem_data(i2c_client);//检测吧b0
    enable_irq(touch_irq);//开启中断
    tpd_halt = 0;
}

static void check_mem_data(struct i2c_client *client){
//如果 b0 的值不是 0x5a5a5a5a,说明 TP 的配置(即.h)没 有下载正确,需要重新下载
    u8 read_buf[4]  = {0};

    msleep(30);
    i2c_smbus_read_i2c_block_data(client,0xb0, sizeof(read_buf), read_buf);

    if (read_buf[3] != 0x5a || read_buf[2] != 0x5a || read_buf[1] != 0x5a || read_buf[0] != 0x5a)
    {
        printk("gsl----check mem read 0xb0 = %x %x %x %x \n", read_buf[3], read_buf[2], read_buf[1], read_buf[0]); 
    }
}

下面有些寄存器简单介绍下:

  1. 0xe0 :芯片复位寄存器
  2. 0xe4 :芯片时钟寄存器
  3. 0xbc :掉电检测寄存器
  4. 0xf0 :页码寄存器
  5. 0x80 :触摸点数寄存器
  6. 0x84~0xa8 :触摸点坐标寄存器
  7. 0xb4 :芯片内部中断计数寄存器
  8. 0xb0 :芯片检验码寄存器
  9. 0xac :靠近关屏寄存器
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342