美文网首页嵌入式
i2c设备-tp 驱动分析(一)

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

作者: SayidZhong | 来源:发表于2017-11-25 16:30 被阅读23次

    转载请注明出处 @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 :靠近关屏寄存器

    相关文章

      网友评论

        本文标题:i2c设备-tp 驱动分析(一)

        本文链接:https://www.haomeiwen.com/subject/rnucbxtx.html