zoukankan      html  css  js  c++  java
  • MTK-TP(触屏)解读一

      MTK中的TP代码结构并不复杂,相比于其他的系统更为的简单些。它使用的是input子系统,通过该系统来上报触摸按键。

      首先我们来看看TP的文件夹下的各代码文件的功能。

    文件名

    具体功能

    关系文件

    tpd.h

    一些宏和extern 函数,外部使用

    Mtk_tpd.c

    tpd_button.c

    关于实体按键的定义

     

    Tpd_calibrate.c

    矫准,比如偏移,可能是MTK以前做电阻屏留下的架构

     

    Tpd_calibrate.h

    矫准,一些宏和数据结构

     

    Tpd_debug.c

    一些函数用于debug信息

     

    Tpd_debug.h

    一些dubug信息和函数申明

     

    Tpd_default.c

    函数似乎为完成,函数只有一个

     

    Tpd_default.h

    似乎未完成

     

    Tpd_init.c

    空文件夹

     

    Tpd_misc.c

    导出一些符号,基本都是切换模式函数

     

    Tpd_setting.c

    传些参数至上层应用

     

    MTK_tpd.c

    关键代码区

     

    met_ftrace_touch.h

    Ftrace工具,用来代码内追踪运行情况

     

    以上代码文件中较为重要的就是mtk_tpd.c,故重点分析该文件。

             在此会有以下几个关键的点:

             1.mtk_tpd.c中的函数运行流程。

             2.mtk_tpd.c中的input系统。

             3.注册i2C

             4.tpd_driver_add函数

             5.LDO芯片设置

             6.通过设备数或者其他方式获取RST,EINT引脚号

             7.通过设备节点获得中断,并请求一个中断号,和注册中断handle

             8.运行线程开启

             9.中断处理函数,唤醒线程,并上报键值

    在此,我将这几个分为一些几部分:

    一、注册MTK下的TP框架,与建立I2C框架。

    二、在probe函数中完成相应电源,设备数,硬件中断,GPIO等设置。

    三、建立一个上报框架分为如下:1)开启一个线程2)申请一个中断irq来绑定硬件中断触发3)irq_handle中唤醒work 4)work在唤醒wait事件5)开启的线程等到wait事件执行上报关键代码。,否则睡眠。

    四、填充关键的TP上报代码(input系统上报)。

    1.mtk_tpd.c中的函数运行流程。
    mtk_tpd.c中,我们最先查看init函数,也就是module添加。

     1 static int tpd_remove(struct platform_device *pdev)
     2 {
     3     input_unregister_device(tpd->dev);
     4     return 0;
     5 }
     6 
     7 /* called when loaded into kernel */
     8 static int __init tpd_device_init(void)
     9 {
    10     TPD_DEBUG("MediaTek touch panel driver init
    ");
    11     if (platform_driver_register(&tpd_driver) != 0) {
    12         TPD_DMESG("unable to register touch panel driver.
    ");
    13         return -1;
    14     }
    15     return 0;
    16 }

    可能有些版本的代码是使用了workqune去注册的,使用这个是让tpd的probe在所有子tp系统add之后再运行mtk_tpd中的probe。

    看到上面代码中,在init中建立一个workqueue,这样使得tpd_init_work_callback中的platform_driver_register延迟工作,在被内核调度室才会进行注册,注册时的probe函数也随之延后运行。(具体作用暂时不知,我们仅仅知道注册了一个platform设备)。
    接下来再看tpd_driver_add:

     1 /* Add driver: if find TPD_TYPE_CAPACITIVE driver successfully, loading it */
     2 int tpd_driver_add(struct tpd_driver_t *tpd_drv)
     3 {
     4     int i;
     5 
     6     if (g_tpd_drv != NULL) {
     7         TPD_DMESG("touch driver exist
    ");
     8         return -1;
     9     }
    10     /* check parameter */
    11     if (tpd_drv == NULL)
    12         return -1;
    13         
    14 //runyee zhou add device info,20161012        
    15 #if defined(CONFIG_RUNYEE_DEVICE_INFO_SUPPORT)
    16       hct_touchpanel_device_add(tpd_drv,DEVICE_SUPPORTED);
    17 #endif    
    18         
    19         
    20     tpd_drv->tpd_have_button = tpd_dts_data.use_tpd_button;
    21     /* R-touch 电阻屏,geneic也就是list[0]为电阻屏*/
    22     if (strcmp(tpd_drv->tpd_device_name, "generic") == 0) {
    23         tpd_driver_list[0].tpd_device_name = tpd_drv->tpd_device_name;
    24         tpd_driver_list[0].tpd_local_init = tpd_drv->tpd_local_init;
    25         tpd_driver_list[0].suspend = tpd_drv->suspend;
    26         tpd_driver_list[0].resume = tpd_drv->resume;
    27         tpd_driver_list[0].tpd_have_button = tpd_drv->tpd_have_button;
    28         return 0;
    29     }
    30     for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
    31         /* add tpd driver into list 使用该函数就会被填入到list中*/
    32         if (tpd_driver_list[i].tpd_device_name == NULL) {
    33             tpd_driver_list[i].tpd_device_name = tpd_drv->tpd_device_name;
    34             tpd_driver_list[i].tpd_local_init = tpd_drv->tpd_local_init;
    35             tpd_driver_list[i].suspend = tpd_drv->suspend;
    36             tpd_driver_list[i].resume = tpd_drv->resume;
    37             tpd_driver_list[i].tpd_have_button = tpd_drv->tpd_have_button;
    38             tpd_driver_list[i].attrs = tpd_drv->attrs;
    39 #if 0
    40             if (tpd_drv->tpd_local_init() == 0) {
    41                 TPD_DMESG("load %s successfully
    ",
    42                       tpd_driver_list[i].tpd_device_name);
    43                 g_tpd_drv = &tpd_driver_list[i];
    44             }
    45 #endif
    46             break;
    47         }
    48         if (strcmp(tpd_driver_list[i].tpd_device_name, tpd_drv->tpd_device_name) == 0)
    49             return 1;    /* driver exist */
    50     }
    51 
    52     return 0;
    53 }

    在此函数中的tpd_driver_list是一个全局的tpd_driver_t结构体,里面的TP_DRV_MAX_COUNT是支持的最大tp设备数。
    驱动中调用add来向tpd_driver_list添加一个新的设备,并在添加前检查该名字是否为NULL,保证add在最末尾。
      接着看probe函数,我们跳过probe前面部分,直接到:

     1     /* save dev for regulator_get() before tpd_local_init() */
     2     tpd->tpd_dev = &pdev->dev;
     3     for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
     4         /* add tpd driver into list */
     5         if (tpd_driver_list[i].tpd_device_name != NULL) {
     6             tpd_driver_list[i].tpd_local_init();//执行具体TP驱动的local_init
     7             /* msleep(1); */
     8             if (tpd_load_status == 1) {
     9                 TPD_DMESG("[mtk-tpd]tpd_probe, tpd_driver_name=%s
    ",
    10                       tpd_driver_list[i].tpd_device_name);
    11                 g_tpd_drv = &tpd_driver_list[i];
    12 //        
    13 #if defined(CONFIG_RUNYEE_DEVICE_INFO_SUPPORT)
    14       hct_set_touch_device_used(g_tpd_drv->tpd_device_name, 0);
    15 #endif                    
    16                 break;
    17             }
    18         }
    19     }
    20     if (g_tpd_drv == NULL) {
    21         if (tpd_driver_list[0].tpd_device_name != NULL) {
    22             g_tpd_drv = &tpd_driver_list[0];
    23             /* touch_type:0: r-touch, 1: C-touch */
    24             touch_type = 0;
    25             g_tpd_drv->tpd_local_init();
    26             TPD_DMESG("[mtk-tpd]Generic touch panel driver
    ");
    27         } else {
    28             TPD_DMESG("[mtk-tpd]cap touch and Generic touch both are not loaded!!
    ");
    29             return 0;
    30         }
    31     }

    在这里面我们可以看到,这里会遍历tpd_driver_list这个表,并且执行表中所有的tpd_local_init函数,而tpd_local_init通常会注册i2C驱动,在register时会执行i2c的probe函数,这样在一个probe函数中将所有的TP注册全部完成。
    在最末尾,执行了input系统注册:

     1     /* use fb_notifier */
     2     tpd_fb_notifier.notifier_call = tpd_fb_notifier_callback;//和上层接收TP是否存在相关
     3     if (fb_register_client(&tpd_fb_notifier))
     4         TPD_DMESG("register fb_notifier fail!
    ");
     5     /* TPD_TYPE_CAPACITIVE handle */
     6     if (touch_type == 1) {
     7 //正常的input设备注册
     8         set_bit(ABS_MT_TRACKING_ID, tpd->dev->absbit);    
     9         set_bit(ABS_MT_TOUCH_MAJOR, tpd->dev->absbit);
    10         set_bit(ABS_MT_TOUCH_MINOR, tpd->dev->absbit);
    11         set_bit(ABS_MT_POSITION_X, tpd->dev->absbit);
    12         set_bit(ABS_MT_POSITION_Y, tpd->dev->absbit);
    13         input_set_abs_params(tpd->dev, ABS_MT_POSITION_X, 0, TPD_RES_X, 0, 0);
    14         input_set_abs_params(tpd->dev, ABS_MT_POSITION_Y, 0, TPD_RES_Y, 0, 0);
    15 #if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_47) 
    16     || defined(CONFIG_MTK_S3320_50) || defined(CONFIG_MTK_MIT200) 
    17     || defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) || defined(CONFIG_MTK_S7020)
    18         input_set_abs_params(tpd->dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
    19         input_set_abs_params(tpd->dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
    20         input_set_abs_params(tpd->dev, ABS_MT_WIDTH_MINOR, 0, 15, 0, 0);
    21         input_mt_init_slots(tpd->dev, 10, 0);
    22 #else
    23         input_set_abs_params(tpd->dev, ABS_MT_TOUCH_MAJOR, 0, 100, 0, 0);
    24         input_set_abs_params(tpd->dev, ABS_MT_TOUCH_MINOR, 0, 100, 0, 0);
    25 #endif /* CONFIG_MTK_S3320 */
    26         TPD_DMESG("Cap touch panel driver
    ");
    27     }
    28     input_set_abs_params(tpd->dev, ABS_X, 0, TPD_RES_X, 0, 0);
    29     input_set_abs_params(tpd->dev, ABS_Y, 0, TPD_RES_Y, 0, 0);
    30     input_abs_set_res(tpd->dev, ABS_X, TPD_RES_X);
    31     input_abs_set_res(tpd->dev, ABS_Y, TPD_RES_Y);
    32     input_set_abs_params(tpd->dev, ABS_PRESSURE, 0, 255, 0, 0);
    33     input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);
    34 
    35     if (input_register_device(tpd->dev))
    36         TPD_DMESG("input_register_device failed.(tpd)
    ");

    在此就不展开input_set_abs_params参数的意义,后面会继续深入。

    2.mtk_tpd.c中的input系统

    要向用户空间发送信息,且是中断,少不了input系统,这里使用MTK中自带的GT9XXTB_hotknot来分析:

     1 static int tpd_history_x = 0, tpd_history_y;
     2 static void tpd_down(s32 x, s32 y, s32 size, s32 id)
     3 {
     4     if ((!size) && (!id)) {
     5         input_report_abs(tpd->dev, ABS_MT_PRESSURE, 100);
     6         input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, 100);
     7     } else {
     8         input_report_abs(tpd->dev, ABS_MT_PRESSURE, size);
     9         input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, size);
    10         /* track id Start 0 */
    11         input_report_abs(tpd->dev, ABS_MT_TRACKING_ID, id);
    12     }
    13 
    14     input_report_key(tpd->dev, BTN_TOUCH, 1);
    15     input_report_abs(tpd->dev, ABS_MT_POSITION_X, x);
    16     input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y);
    17     input_mt_sync(tpd->dev);
    18     TPD_DEBUG_SET_TIME;
    19     TPD_EM_PRINT(x, y, x, y, id, 1);
    20     tpd_history_x = x;
    21     tpd_history_y = y;
    22     /* MMProfileLogEx(MMP_TouchPanelEvent, MMProfileFlagPulse, 1, x+y); */
    23     if (tpd_dts_data.use_tpd_button) {
    24         if (FACTORY_BOOT == get_boot_mode() ||
    25             RECOVERY_BOOT == get_boot_mode())
    26             tpd_button(x, y, 1);
    27     }
    28 }

    这一段tdp_down是关键的上报代码,也就是说最后从TP芯片来的坐标数据需要通过以上input_report_adbs等函数来上报,那先暂时记住该部分。其中的input其实是不需要再次去注册的,因为在mtk_tpd.c中已经注册,只需要:extern struct tpd_device *tpd; 例子中在include/tpd_gt9xx_common.h有定义。

    那么我们大概清楚了,MTK下的TP其实就是我们自己写驱动上报(我们需要做的事),其他的input系统注册我们就不操心了。那么大多数TP都是i2c,我们则需要进行i2c注册。

    3.具体驱动中的i2c注册

     注册I2C这里就简略提一下,i2C可以使用各种方式注册,在此实例使用的是i2c_board_info来注册,同样的先注册device,然后在drivers注册时会匹配进入probe,其中需要做以下事情:

    static s32 tpd_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
        //1.i2c_clien获得,给read,write函数使用
        //2.上电,因为有些TP可能是使用LDO去控制的,所以要开启电源regulator_set_voltage或者mtk的hwPowerDown(MT65XX_POWER_LDO_VGP2, "TP");
        //3.使用设备树,或者直接设置RST,INT脚的状态。      
        //4.简易的test测试tp是否存在,比如读取version,添加异常处理
        //5.INT脚配置硬件中断申请,并链接处理函数
        //6.使用 kthread_run开启一个可休眠线程,等待中断唤醒处理坐标上报
        //7.添加自己的input参数,比如压力,按压面的size   
        //8.与芯片相关的其他操作
    }    

     以上操作都可在代码中发现,也是一些基本的必须的设置,调试时应该先以大体框架通过为主,大体框架就是,local_init->i2c_probe->相关函数设置(thread_run有没有跑,中断函数是否申请了,设备树是否成功匹配),然后再去进行硬件调试:i2c通不通,中断是否能正常触发,能否读取版本等。

    4.tpd_driver_add函数

     1 /* called when loaded into kernel */
     2 static int __init tpd_driver_init(void)
     3 {
     4     GTP_INFO("MediaTek gt91xx touch panel driver init
    ");
     5     tpd_get_dts_info();
     6     if (tpd_driver_add(&tpd_device_driver) < 0)
     7         GTP_INFO("add generic driver failed
    ");
     8 
     9     return 0;
    10 }

    在init函数中加入tpd_driver_add 函数。这样只要以下流程完成即可调试硬件。

    至此,添加一个MTK TP的框架搭建完了,回顾下,主要是:
    1. I2C框架,device与driver注册,还有probe函数(接下来的工作)
    2. 使用tpd_driver_add向MTK TP框架添加设备,完成local_tpd_init函数
    总结下运行流程如下:

     5.各操作实例

    5.1.LDO芯片设置
    有些硬件使用PMU来为TP供电,所以有可能需要使用代码来设备如:

    如果没有的话就直接设置供电打开即可。

    5.2通过设备树或者其他方式获取RST,EINT引脚号

    有些平台使用的是设备树来获取的这两个引脚,这里我的是MTK6755平台,如下:

      

    也可以使用GPIO_request来获取,只要能做到控制这两个引脚即可。

    5.3通过设备节点获得中断,并请求一个中断号,和注册中断handle

    因为上报事件应该定义为一个中断事件,故应该为ENIT引脚分配一个中断号,并申请中断handle。在此使用的是通过设备节点来申请的中断节点,也是使用的是MTK的设备树来获取的,所以可能需要修改dtsi。

    紫色框中是MTK的注册硬件IRQ handle 的方法,而这里使用的是设备树获取中断硬件中断号,并分配中断向量号,之候使用request来注册中断handle。

    5.4运行线程开启

             报点需要高响应,延迟低,所以为TP开启一个线程来保证高效。具体代码如下:

     

             我们进入ilitek_irq_handle_thread

     

             红色框中是关键的TP坐标上报代码,而在此之前这个线程是睡眠的,只是偶尔运行一次,该线程等待中断事件打断,并运行,而唤醒的代码则在之前申请的硬件中断的handle中的work会唤醒。

    5.5中断处理函数,唤醒线程,并上报键值

    该函数则是唤醒wait,则在上节的线程会被唤醒,进行关键的上报代码。
    也就是说当你的手触摸屏幕,屏幕的中断引脚会产生中断(拉低或者高),使得申请的硬件中断handle运行,handle中唤醒wait,接着运行着的线程被叫醒,执行坐标上报关键代码。

    至此一个驱动大概框架就是这样,后面再为大家带来一些细致的讲解

  • 相关阅读:
    JTS相关资料和示例
    微信、支付宝支付那点事
    系统化全方位监控告警,这一篇足矣
    耶鲁大学研究创造了模拟人类认知的计算机芯片
    Docker孵化的5个开源项目
    图解数据中心水系统标准和架构(大全)
    React Native 项目整合 CodePush 全然指南
    1分钟了解MyISAM与InnoDB的索引差异
    Google I/O 官方应用中的动效设计
    为了完毕月入三万的目标,我都做了哪些准备?
  • 原文地址:https://www.cnblogs.com/inkhearts/p/10098460.html
Copyright © 2011-2022 走看看