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\n");
11     if (platform_driver_register(&tpd_driver) != 0) {
12         TPD_DMESG("unable to register touch panel driver.\n");
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\n");
 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\n",
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\n",
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\n");
27         } else {
28             TPD_DMESG("[mtk-tpd]cap touch and Generic touch both are not loaded!!\n");
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!\n");
 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\n");
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)\n");

在此就不展開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\n");
 5     tpd_get_dts_info();
 6     if (tpd_driver_add(&tpd_device_driver) < 0)
 7         GTP_INFO("add generic driver failed\n");
 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,接着運行着的線程被叫醒,執行坐標上報關鍵代碼。

至此一個驅動大概框架就是這樣,后面再為大家帶來一些細致的講解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM