基於MTK 的 TP 驅動分析

1. 克隆服務器工程源碼並切換分支 

git clone [email protected]: mt658292_kk v9 

git checkout -b submit_v9_dongxf_tp_modify_v1.0_2014_0910 origin/v6_dev


2. TP 硬件分析

1)硬件圖:

 


2)硬件管腳:

    SCL0: IIC 時鐘引腳

    SDA0:IIC數據引腳

    RST: 復位引腳

    EINT: IIC 中斷引腳,觸摸事件通過IIC 貫穿到系統

   

3. 需要分析的文件:

1)MTK 關於TP的框架文件:實現對input的初始化,具體的 TP IC匹配操作

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. v9/code/mediatek/custom/common/kernel/touchpanel/src/mtk_tpd.c   

2)具體TP IC的 設置我們可以進行IC的上電、設置中斷、UpdateFW等動作

  1. v9/code/mediatek/custom/common/kernel/touchpanel/ft5206(TPdevicename)/focaltech_ex_fun.c  


3)TP的配置文件: 設計屏幕分辨率,虛擬按鍵設置

  1. v9/code/mediatek/custom/v9_e324_3gb15_qhd_cq4153(projectname)/kernel/touchpanel/ft5206(TPdevicename)/tpd_custom_ft5206.h  


配置示例:

  1. #define TPD_HAVE_BUTTON                // 定義該宏表示我們的TP支持虛擬按鍵  
  2. #define TPD_BUTTON_HEIGH       (100)  
  3. #define TPD_KEY_COUNT          3       //支持的虛擬按鍵的個數  
  4. #define TPD_KEYS                {KEY_MENU, KEY_HOMEPAGE ,KEY_BACK} //對應的功能鍵  
  5. #define  TPD_KEYS_DIM                  //每個功能鍵的座標  
  6.            {{90,1010,120,TPD_BUTTON_HEIGH},  
  7.            {270,1010,120,TPD_BUTTON_HEIGH},  
  8.            {450,1010,120,TPD_BUTTON_HEIGH}}//四個參數分別爲x座標,y座標,width,height  

3. TP 工作原理:

       Kernel 通過input 子系統與android交互,每當我們點擊屏幕或擡起都會產生中斷,每當kernel收到中斷會通過i2c總線讀取TP控制器產生的座標數據,kernel就會通過input系統上報給android層, android層會完成相應的動作。

      Android手機TP一般會涉及虛擬按鍵的操作,初始化時會通過tpd_button_settings(),根據定義在tpd _custom_ft5206.h 文件中的配置信息將虛擬按鍵的座標信息寫在/sys/board_properities/virtualkeys. mtk-tpd中,android層再工作時判斷上報的座標是否屬於在某個按鍵的範圍內,如果在這個範圍內,會轉換成相應的button事件

 

4. 驅動框架分析 //按照執行順序的先後,對於某一款TP的驅動,系統加載後會調用其中的初始化部分,以ft5206爲例

1) TP  初始化 

  1. //v9/code/mediatek/custom/common/kernel/touchpanel/ft5206/focaltech_driver.c  

  1. /*called when loaded into kernel */  
  2. static int __init tpd_driver_init(void) {  
  3.          printk("MediaTekFTS touch panel driver init\n");  
  4.          i2c_register_board_info(TPD_I2C_NUMBER,&ft5206_i2c_tpd, 1);  //這個ft5206_i2c_tpd添加到全局的i2c設備鏈表__i2c_board_list中  
  5.          if(tpd_driver_add(&tpd_device_driver)< 0)  //這個比較重要,在下面的第4步中進行tpd_i2c_driver驅動的i2c總線註冊會匹配這個i2c總線設備。  
  6.                    TPD_DMESG("addFTS driver failed\n");  
  7.           return 0;  
  8.  }  

  1. static struct i2c_driver tpd_i2c_driver = {  
  2.        .driver = {  
  3.                  .name    = "mtk-focal",  
  4.        },  
  5.        .probe     =tpd_probe,  
  6.        .remove =tpd_remove,  
  7.        .id_table          =ft5206_tpd_id,  
  8.        .detect    =tpd_detect,  
  9. };  

  1. struct tpd_driver_t tpd_device_driver = {  
  2.          .tpd_device_name= "mtk-focal",  
  3.          .tpd_local_init= tpd_local_init,  
  4.          .suspend= tpd_suspend,  
  5.          .resume= tpd_resume,  
  6. #ifdef TPD_HAVE_BUTTON  
  7.          .tpd_have_button= 1,  
  8. #else  
  9.          .tpd_have_button= 0,  
  10. #endif                 
  11.  };  

 2) TP 設備加載、卸載和平臺總線註冊

  1. //W:\.dongxunfeng\v9\code\mediatek\custom\common\kernel\touchpanel\src\mtk_tpd.c   

  1. /* should never be called */  
  2. static void __exit tpd_device_exit(void) {  
  3.    TPD_DMESG("MediaTek touch panel driver exit\n");  
  4.    //input_unregister_device(tpd->dev);  
  5.    platform_driver_unregister(&tpd_driver);  
  6.    #ifdef CONFIG_HAS_EARLYSUSPEND  
  7.    unregister_early_suspend(&MTK_TS_early_suspend_handler);  
  8.    #endif  
  9. }  


  1. /* called when loaded into kernel */  
  2. static int __init tpd_device_init(void) {  
  3.    printk("MediaTek touch panel driver init\n");  
  4.    rgk_creat_proc_tp_info();        
  5.    if(platform_driver_register(&tpd_driver)!=0) {  
  6.        TPD_DMESG("unable to register touch panel driver.\n");  
  7.        return -1;  
  8.    }    
  9.    return 0;  
  10. }  


  1. /* called when loaded into kernel */  
  2. static struct platform_driver tpd_driver ={  
  3.    .remove     = tpd_remove,  
  4.    .shutdown   = NULL,  
  5.    .probe      = tpd_probe,  
  6.    #ifndef CONFIG_HAS_EARLYSUSPEND  
  7.    .suspend    = NULL,  
  8.    .resume     = NULL,  
  9.    #endif  
  10.    .driver     = {  
  11.        .name = TPD_DEVICE,  
  12.    },  
  13. };  

//tpd_driver對應的虛擬總線設備在文件  

//code/mediatek/platform/mt6582/kernel/core/mt_devs.c

  1. /*=======================================================================*/  
  2. /* MT6575 Touch Panel                                                    */  
  3. /*=======================================================================*/  
  4.    
  5. static struct platform_device mtk_tpd_dev ={  
  6.    .name = "mtk-tpd",  //二者name 字段匹配成功後執行tpd_driver中的probe函數   
  7.    .id   = -1,  
  8. };  
  9.    
  10. #if defined(CONFIG_MTK_TOUCHPANEL)  
  11.    retval = platform_device_register(&mtk_tpd_dev);  
  12.    if (retval != 0) {  
  13.        return retval;  
  14.     }  
  15. #endif  

3) tpd_driver中的probe函數分析(只提取probe函數部分內容)

 

  1. struct tpd_device  *tpd = 0;   
  2.   
  3. /* touch panel probe */  
  4. static int tpd_probe(struct platform_device*pdev) {  
  5.          ...  
  6.    ...  
  7.    if((tpd=(struct tpd_device*)kmalloc(sizeof(struct tpd_device),GFP_KERNEL))==NULL) return -ENOMEM;  
  8.    memset(tpd, 0, sizeof(struct tpd_device));  
  9.    
  10.    /* allocate input device */  
  11.    if((tpd->dev=input_allocate_device())==NULL) { kfree(tpd); return-ENOMEM; } //TP設備抽象爲tpd結構體  
  12.    
  13.  //TPD_RES_X = simple_strtoul(LCM_WIDTH, NULL, 0);  
  14.  //TPD_RES_Y = simple_strtoul(LCM_HEIGHT, NULL, 0);     
  15.  TPD_RES_X = DISP_GetScreenWidth();  
  16.    TPD_RES_Y = DISP_GetScreenHeight();  
  17.    
  18.    
  19.    printk("mtk_tpd: TPD_RES_X = %d, TPD_RES_Y = %d\n", TPD_RES_X,TPD_RES_Y);  
  20.    
  21.    tpd_mode = TPD_MODE_NORMAL;  
  22.    tpd_mode_axis = 0;  
  23.    tpd_mode_min = TPD_RES_Y/2;  
  24.    tpd_mode_max = TPD_RES_Y;  
  25.    tpd_mode_keypad_tolerance = TPD_RES_X*TPD_RES_X/1600;  
  26.    /* struct input_dev dev initialization and registration */  
  27.    tpd->dev->name = TPD_DEVICE;      // 爲TP設備分配一個輸入設備  
  28.    set_bit(EV_ABS, tpd->dev->evbit); // 輸入設備的初始化,重點是對輸入設備進行 KEY 和 ABS 兩種輸入類型的初始化  
  29.    set_bit(EV_KEY, tpd->dev->evbit);   
  30.    set_bit(ABS_X, tpd->dev->absbit);  
  31.    set_bit(ABS_Y, tpd->dev->absbit);  
  32.    set_bit(ABS_PRESSURE, tpd->dev->absbit);  
  33.    set_bit(BTN_TOUCH, tpd->dev->keybit);     
  34.    ....  

 /**************************************************************************

  *    全局數組tpd_driver_lsit[i]中的tp驅動依次進行如下操作 ,遍歷整個數組

  * 執行tp驅動中的tpd_local_init()接口,此接口主要是對tpd_i2c_driver進行i2c

  * 總線的註冊,匹配成功後執行tpd_i2c_driver中的probe ,這個probe比較重要,

  * 其中系統會跟TP進行一次i2c通信,如果成功則說明找到了這個驅動,根據

  * 此,可以兼容多款不同IC的TP設備

  **************************************************************************/    

  1. #if 1     
  2.  for(i = 1; i < TP_DRV_MAX_COUNT; i++)  
  3.          {  
  4.        /* add tpd driver into list */  
  5.        if(tpd_driver_list[i].tpd_device_name != NULL)  
  6.        {  
  7. #if 1//lisong 2014-3-10 [BUGID:NULL][fixbug:can not go into meta mode if TP disconnected ]start              
  8.            if(boot_mode == META_BOOT)  
  9.            {/*reserved*/}  
  10.            else  
  11. #endif//lisong 2014-3-10 [BUGID:NULL][fixbug:can not go into meta mode if TP disconnected ]end                 
  12.                tpd_driver_list[i].tpd_local_init();        //執行tp驅動中的tpd_local_init()接口  
  13.            //msleep(1);  
  14.            if(tpd_load_status ==1) { // 如果上面的i2c總線註冊成功,則將tpd_load_status置位,並將g_tpd_drv指向這個驅動  
  15.                TPD_DMESG("[mtk-tpd]tpd_probe, tpd_driver_name=%s\n",tpd_driver_list[i].tpd_device_name);  
  16.                 g_tpd_drv =&tpd_driver_list[i];  
  17.                 break;  
  18.            }  
  19.        }     
  20.     }  
  21.    if(g_tpd_drv == NULL) { //如果 g_tpd_drv 爲空,說明沒有找到匹配的驅動  
  22.        if(tpd_driver_list[0].tpd_device_name != NULL) {  
  23.            g_tpd_drv = &tpd_driver_list[0]; //沒有找到匹配的驅動,指定 鏈表首元素作爲默認的驅動程序  
  24.            /* touch_type:0: r-touch, 1: C-touch */   
  25.            touch_type = 0;  
  26.            g_tpd_drv->tpd_local_init();   
  27.            TPD_DMESG("[mtk-tpd]Generic touch panel driver\n");  
  28.        } else {  
  29.            TPD_DMESG("[mtk-tpd]cap touch and Generic touch both are notloaded!!\n");  
  30.            return 0;  
  31.        }  
  32.     }         
  33.    
  34.     strncpy(mtk_tpd_ic_name,(char*)g_tpd_drv->tpd_device_name,sizeof(mtk_tpd_ic_name)-1);// zhoulidong add  
  35.    
  36.    #ifdef CONFIG_HAS_EARLYSUSPEND  
  37.    MTK_TS_early_suspend_handler.suspend = g_tpd_drv->suspend;  
  38.    MTK_TS_early_suspend_handler.resume = g_tpd_drv->resume;  
  39.    register_early_suspend(&MTK_TS_early_suspend_handler);  
  40.    #endif                   
  41.    
  42. #ifdef TPD_TYPE_CAPACITIVE  
  43.          /*TPD_TYPE_CAPACITIVE handle */  
  44.          if(touch_type== 1){  
  45.                              
  46.          set_bit(ABS_MT_TRACKING_ID,tpd->dev->absbit);  //對輸入設備初始化  
  47.              set_bit(ABS_MT_TOUCH_MAJOR,tpd->dev->absbit);  
  48.          ....  
  49. #if 0 // linux kernel update from 2.6.35--> 3.0  
  50.              tpd->dev->absmax[ABS_MT_POSITION_X]= TPD_RES_X;  
  51.          ....  
  52. #else  
  53.          input_set_abs_params(tpd->dev,ABS_MT_POSITION_X, 0, TPD_RES_X, 0, 0); //再次對輸入設備初始化  
  54.                    ....  
  55. #endif  
  56.              TPD_DMESG("Cap touch paneldriver\n");   
  57.        }  
  58. #endif  
  59.    #if 0 //linux kernel update from 2.6.35 --> 3.0  
  60.    tpd->dev->absmax[ABS_X] = TPD_RES_X;  
  61.    tpd->dev->absmin[ABS_X] = 0;  
  62.    tpd->dev->absmax[ABS_Y] = TPD_RES_Y;  
  63.    tpd->dev->absmin[ABS_Y] = 0;  
  64.           
  65.    tpd->dev->absmax[ABS_PRESSURE] = 255;  
  66.    tpd->dev->absmin[ABS_PRESSURE] = 0;  
  67.    #else  
  68.          input_set_abs_params(tpd->dev,ABS_X, 0, TPD_RES_X, 0, 0);  //再次對輸入設備初始化  
  69.          input_set_abs_params(tpd->dev,ABS_Y, 0, TPD_RES_Y, 0, 0);  
  70.          input_abs_set_res(tpd->dev,ABS_X, TPD_RES_X);  
  71.             ....  
  72.    
  73.    #endif  
  74.    if(input_register_device(tpd->dev))   //向輸入子系統註冊輸入設備  
  75.        TPD_DMESG("input_register_device failed.(tpd)\n");  
  76.    else  
  77.                             tpd_register_flag= 1;  
  78.    /* init R-Touch */  
  79.    #if 0  
  80.            if(touch_type == 0)  
  81.            {  
  82.                 g_tpd_drv->tpd_local_init();  
  83.            }     
  84.                    #endif  
  85.    if(g_tpd_drv->tpd_have_button)  
  86.     {  
  87.              tpd_button_init();  
  88.     }  
  89.    
  90.          if(g_tpd_drv->attrs.num)  
  91.                    tpd_create_attributes(&pdev->dev,&g_tpd_drv->attrs);  
  92.    
  93.    return 0;  
  94. }  


4) 觸摸事件觸發實現

//依次執行其中的tpd_local_init()接口,此接口是對tpd_i2c_driver(下面會提到這個結構體)進行i2c總

線的註冊,先匹配id,若無則匹配name,匹配成功後執行tpd_i2c_driver驅動中的tpd_probe接口

  1. int tpd_local_init(void)  
  2. {  
  3.     // 對 tpd_i2c_driver 進行註冊  
  4.    if(i2c_add_driver(&tpd_i2c_driver)!=0) {  
  5.      TPD_DMESG("unable to add i2c driver.\n");  
  6.      return -1;  
  7.     }  
  8.    
  9.    if (0) //if(tpd_load_status == 0)  
  10.     {  
  11.              TPD_DMESG("add error touch paneldriver.\n");  
  12.              i2c_del_driver(&tpd_i2c_driver);  
  13.              return -1;  
  14.     }  
  15.      
  16. #ifdef TPD_HAVE_BUTTON      
  17.    tpd_button_setting(TPD_KEY_COUNT, tpd_keys_local, tpd_keys_dim_local);//initialize tpd button data  
  18. #endif    
  19.    
  20. #if (defined(TPD_WARP_START) &&defined(TPD_WARP_END))     
  21.    TPD_DO_WARP = 1;  
  22.    memcpy(tpd_wb_start, tpd_wb_start_local, TPD_WARP_CNT*4);  
  23.    memcpy(tpd_wb_end, tpd_wb_start_local, TPD_WARP_CNT*4);  
  24. #endif  
  25.    
  26. #if (defined(TPD_HAVE_CALIBRATION)&& !defined(TPD_CUSTOM_CALIBRATION))  
  27.    memcpy(tpd_calmat, tpd_def_calmat_local, 8*4);  
  28.    memcpy(tpd_def_calmat, tpd_def_calmat_local, 8*4);          
  29. #endif   
  30.    TPD_DMESG("end %s, %d\n", __FUNCTION__, __LINE__);   
  31.    tpd_type_cap = 1;  
  32.    return 0;  
  33. }  
  34.    
  35. // tpd_i2c_driver 初始化  
  36. static struct i2c_driver tpd_i2c_driver = {  
  37.        .driver = {  
  38.                  .name    = "mtk-focal",  
  39.        },  
  40.        .probe     =tpd_probe,  
  41.        .remove =tpd_remove,  
  42.        .id_table          =ft5206_tpd_id,  
  43.        .detect    =tpd_detect,  
  44. };  
  45.    
  46.    
  47. // tpd_probe 的定義  
  48. static int tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)  
  49. {         
  50.          intretval = TPD_OK;  
  51.          chardata;  
  52.          intreset_count = 0;  
  53.           
  54.          client->timing= 300;  
  55.          i2c_client= client;  
  56. reset_proc:    
  57.     
  58.       //power on, need confirm with SA  
  59.       mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);  
  60.       mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);  
  61.       mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);   
  62.       msleep(5);  
  63.       TPD_DMESG(" fts ic reset\n");  
  64.           
  65.    
  66.       mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);  
  67.       mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);  
  68.       mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);     
  69.    
  70.       msleep(200);  
  71.    
  72.       if((i2c_smbus_read_i2c_block_data(i2c_client, 0x00, 1, &data))<0)  //系統和TP通過 IIC 通信,若通信失敗則認爲TP 設備未連接到主板   
  73.      {  
  74.                    TPD_DMESG("I2Ctransfer error, line: %d\n", __LINE__);  
  75. #ifdef TPD_RESET_ISSUE_WORKAROUND  
  76.        if ( reset_count < TPD_MAX_RESET_COUNT )  
  77.        {  
  78.            reset_count++;  
  79.            goto reset_proc;  
  80.        }  
  81. #endif  
  82.                       return -1;  
  83.          }  
  84.    
  85.          mt_set_gpio_mode(GPIO_CTP_EINT_PIN,GPIO_CTP_EINT_PIN_M_EINT);  //設置引腳中斷  
  86.          mt_set_gpio_dir(GPIO_CTP_EINT_PIN,GPIO_DIR_IN);  
  87.          mt_set_gpio_pull_enable(GPIO_CTP_EINT_PIN,GPIO_PULL_ENABLE); //打開中斷使能  
  88.          mt_set_gpio_pull_select(GPIO_CTP_EINT_PIN,GPIO_PULL_UP);  //設定中斷觸發方式  
  89.    
  90.            // 註冊中斷處理函數,作用是 喚醒(wake up interrupt)等待隊列,等待隊列頭上懸掛着觸摸事件的等待隊列   
  91.         mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM,CUST_EINT_TOUCH_PANEL_TYPE, tpd_eint_interrupt_handler, 0);  
  92.         mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);  
  93.           
  94.          ....  
  95.    
  96.          #ifdefTPD_AUTO_UPGRADE            
  97.          thread= kthread_run(tpd_ft_update_fw, 0, TPD_DEVICE);  
  98.          if(IS_ERR(thread))  
  99.          {  
  100.                    retval= PTR_ERR(thread);  
  101.                    TPD_DMESG(TPD_DEVICE" failed to create kernel thread for update fw: %d\n", retval);  
  102.          }  
  103.          #endif  
  104.           
  105.          thread= kthread_run(touch_event_handler, 0, TPD_DEVICE); //創建一個內核中運行的線程,這個線程主要是實現觸摸事件發生時,採集觸摸點數  
  106.          if(IS_ERR(thread))  
  107.          {  
  108.                    retval= PTR_ERR(thread);  
  109.                    TPD_DMESG(TPD_DEVICE" failed to create kernel thread: %d\n", retval);  
  110.          }  
  111.    
  112.          TPD_DMESG("FTSTouch Panel Device Probe %s\n", (retval < TPD_OK) ? "FAIL" :"PASS");  
  113.    
  114.        ....  
  115.    
  116.   return 0;  
  117.     
  118.  }  
  119.    
  120. //觸摸中斷事件處理函數:tpd_eint_interrupt_handler   
  121. static voidtpd_eint_interrupt_handler(void)  
  122. {  
  123.          //TPD_DEBUG("TPDinterrupt has been triggered\n");  
  124.          TPD_DEBUG_PRINT_INT;  
  125.          tpd_flag= 1;     //將標誌位置位  
  126.          wake_up_interruptible(&waiter);  // 喚醒等待隊列  
  127. }  
  128.    
  129. //觸摸事件 採集線程  
  130. static int touch_event_handler(void*unused)  
  131.  {  
  132.          ....  
  133.          sched_setscheduler(current,SCHED_RR, ¶m);  
  134.         ....  
  135.    
  136.          do  
  137.          {  
  138.                    set_current_state(TASK_INTERRUPTIBLE);   //設置當前線程睡眠  
  139.                    wait_event_interruptible(waiter,tpd_flag!=0);       // 設置等待隊列等待事件,tpd_flag 會在中斷處理函數中置位                                                    
  140.                    tpd_flag= 0;                        
  141.                    set_current_state(TASK_RUNNING);   
  142.    
  143.                       
  144.                    if(tpd_touchinfo(&cinfo, &pinfo, &point_num))              //獲得多點觸控信息  
  145.                    {  
  146.                                 //TPD_DEBUG("point_num= %d\n",point_num);  
  147.                             TPD_DEBUG_SET_TIME;  
  148.                             if(point_num>0)  
  149.                             {  
  150.                                 for(i =0; i<point_num; i++)  
  151.                                 {  
  152.                                      tpd_down(cinfo.x[i], cinfo.y[i],cinfo.id[i]); //上傳多點觸控信息  
  153.                                 }  
  154.                                input_sync(tpd->dev);  
  155.                             }  
  156.                             else   
  157.                                 {  
  158.                                 tpd_up(cinfo.x[0], cinfo.y[0]);  //上傳一個觸控信息  
  159.                                  input_sync(tpd->dev);  
  160.                           }  
  161.                 }  
  162.         mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);           
  163.         }while(!kthread_should_stop());  
  164.          return0;  
  165. }  
  166.    
  167.    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章