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匹配操作
- v9/code/mediatek/custom/common/kernel/touchpanel/src/mtk_tpd.c
2)具體TP IC的 設置我們可以進行IC的上電、設置中斷、UpdateFW等動作
- v9/code/mediatek/custom/common/kernel/touchpanel/ft5206(TPdevicename)/focaltech_ex_fun.c
3)TP的配置文件: 設計屏幕分辨率,虛擬按鍵設置
- v9/code/mediatek/custom/v9_e324_3gb15_qhd_cq4153(projectname)/kernel/touchpanel/ft5206(TPdevicename)/tpd_custom_ft5206.h
配置示例:
- #define TPD_HAVE_BUTTON // 定義該宏表示我們的TP支持虛擬按鍵
- #define TPD_BUTTON_HEIGH (100)
- #define TPD_KEY_COUNT 3 //支持的虛擬按鍵的個數
- #define TPD_KEYS {KEY_MENU, KEY_HOMEPAGE ,KEY_BACK} //對應的功能鍵
- #define TPD_KEYS_DIM //每個功能鍵的座標
- {{90,1010,120,TPD_BUTTON_HEIGH},
- {270,1010,120,TPD_BUTTON_HEIGH},
- {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 初始化
- //v9/code/mediatek/custom/common/kernel/touchpanel/ft5206/focaltech_driver.c
- /*called when loaded into kernel */
- static int __init tpd_driver_init(void) {
- printk("MediaTekFTS touch panel driver init\n");
- i2c_register_board_info(TPD_I2C_NUMBER,&ft5206_i2c_tpd, 1); //這個ft5206_i2c_tpd添加到全局的i2c設備鏈表__i2c_board_list中
- if(tpd_driver_add(&tpd_device_driver)< 0) //這個比較重要,在下面的第4步中進行tpd_i2c_driver驅動的i2c總線註冊會匹配這個i2c總線設備。
- TPD_DMESG("addFTS driver failed\n");
- return 0;
- }
- static struct i2c_driver tpd_i2c_driver = {
- .driver = {
- .name = "mtk-focal",
- },
- .probe =tpd_probe,
- .remove =tpd_remove,
- .id_table =ft5206_tpd_id,
- .detect =tpd_detect,
- };
- struct tpd_driver_t tpd_device_driver = {
- .tpd_device_name= "mtk-focal",
- .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
- };
2) TP 設備加載、卸載和平臺總線註冊
- //W:\.dongxunfeng\v9\code\mediatek\custom\common\kernel\touchpanel\src\mtk_tpd.c
- /* should never be called */
- static void __exit tpd_device_exit(void) {
- TPD_DMESG("MediaTek touch panel driver exit\n");
- //input_unregister_device(tpd->dev);
- platform_driver_unregister(&tpd_driver);
- #ifdef CONFIG_HAS_EARLYSUSPEND
- unregister_early_suspend(&MTK_TS_early_suspend_handler);
- #endif
- }
- /* called when loaded into kernel */
- static int __init tpd_device_init(void) {
- printk("MediaTek touch panel driver init\n");
- rgk_creat_proc_tp_info();
- if(platform_driver_register(&tpd_driver)!=0) {
- TPD_DMESG("unable to register touch panel driver.\n");
- return -1;
- }
- return 0;
- }
- /* called when loaded into kernel */
- static struct platform_driver tpd_driver ={
- .remove = tpd_remove,
- .shutdown = NULL,
- .probe = tpd_probe,
- #ifndef CONFIG_HAS_EARLYSUSPEND
- .suspend = NULL,
- .resume = NULL,
- #endif
- .driver = {
- .name = TPD_DEVICE,
- },
- };
//tpd_driver對應的虛擬總線設備在文件
//code/mediatek/platform/mt6582/kernel/core/mt_devs.c
- /*=======================================================================*/
- /* MT6575 Touch Panel */
- /*=======================================================================*/
- static struct platform_device mtk_tpd_dev ={
- .name = "mtk-tpd", //二者name 字段匹配成功後執行tpd_driver中的probe函數
- .id = -1,
- };
- #if defined(CONFIG_MTK_TOUCHPANEL)
- retval = platform_device_register(&mtk_tpd_dev);
- if (retval != 0) {
- return retval;
- }
- #endif
3) tpd_driver中的probe函數分析(只提取probe函數部分內容)
- struct tpd_device *tpd = 0;
- /* touch panel probe */
- static int tpd_probe(struct platform_device*pdev) {
- ...
- ...
- if((tpd=(struct tpd_device*)kmalloc(sizeof(struct tpd_device),GFP_KERNEL))==NULL) return -ENOMEM;
- memset(tpd, 0, sizeof(struct tpd_device));
- /* allocate input device */
- if((tpd->dev=input_allocate_device())==NULL) { kfree(tpd); return-ENOMEM; } //TP設備抽象爲tpd結構體
- //TPD_RES_X = simple_strtoul(LCM_WIDTH, NULL, 0);
- //TPD_RES_Y = simple_strtoul(LCM_HEIGHT, NULL, 0);
- TPD_RES_X = DISP_GetScreenWidth();
- TPD_RES_Y = DISP_GetScreenHeight();
- printk("mtk_tpd: TPD_RES_X = %d, TPD_RES_Y = %d\n", TPD_RES_X,TPD_RES_Y);
- tpd_mode = TPD_MODE_NORMAL;
- tpd_mode_axis = 0;
- tpd_mode_min = TPD_RES_Y/2;
- tpd_mode_max = TPD_RES_Y;
- tpd_mode_keypad_tolerance = TPD_RES_X*TPD_RES_X/1600;
- /* struct input_dev dev initialization and registration */
- tpd->dev->name = TPD_DEVICE; // 爲TP設備分配一個輸入設備
- set_bit(EV_ABS, tpd->dev->evbit); // 輸入設備的初始化,重點是對輸入設備進行 KEY 和 ABS 兩種輸入類型的初始化
- set_bit(EV_KEY, tpd->dev->evbit);
- set_bit(ABS_X, tpd->dev->absbit);
- set_bit(ABS_Y, tpd->dev->absbit);
- set_bit(ABS_PRESSURE, tpd->dev->absbit);
- set_bit(BTN_TOUCH, tpd->dev->keybit);
- ....
/**************************************************************************
* 全局數組tpd_driver_lsit[i]中的tp驅動依次進行如下操作 ,遍歷整個數組
* 執行tp驅動中的tpd_local_init()接口,此接口主要是對tpd_i2c_driver進行i2c
* 總線的註冊,匹配成功後執行tpd_i2c_driver中的probe ,這個probe比較重要,
* 其中系統會跟TP進行一次i2c通信,如果成功則說明找到了這個驅動,根據
* 此,可以兼容多款不同IC的TP設備
**************************************************************************/
- #if 1
- for(i = 1; i < TP_DRV_MAX_COUNT; i++)
- {
- /* add tpd driver into list */
- if(tpd_driver_list[i].tpd_device_name != NULL)
- {
- #if 1//lisong 2014-3-10 [BUGID:NULL][fixbug:can not go into meta mode if TP disconnected ]start
- if(boot_mode == META_BOOT)
- {/*reserved*/}
- else
- #endif//lisong 2014-3-10 [BUGID:NULL][fixbug:can not go into meta mode if TP disconnected ]end
- tpd_driver_list[i].tpd_local_init(); //執行tp驅動中的tpd_local_init()接口
- //msleep(1);
- if(tpd_load_status ==1) { // 如果上面的i2c總線註冊成功,則將tpd_load_status置位,並將g_tpd_drv指向這個驅動
- TPD_DMESG("[mtk-tpd]tpd_probe, tpd_driver_name=%s\n",tpd_driver_list[i].tpd_device_name);
- g_tpd_drv =&tpd_driver_list[i];
- break;
- }
- }
- }
- if(g_tpd_drv == NULL) { //如果 g_tpd_drv 爲空,說明沒有找到匹配的驅動
- if(tpd_driver_list[0].tpd_device_name != NULL) {
- g_tpd_drv = &tpd_driver_list[0]; //沒有找到匹配的驅動,指定 鏈表首元素作爲默認的驅動程序
- /* touch_type:0: r-touch, 1: C-touch */
- touch_type = 0;
- g_tpd_drv->tpd_local_init();
- TPD_DMESG("[mtk-tpd]Generic touch panel driver\n");
- } else {
- TPD_DMESG("[mtk-tpd]cap touch and Generic touch both are notloaded!!\n");
- return 0;
- }
- }
- strncpy(mtk_tpd_ic_name,(char*)g_tpd_drv->tpd_device_name,sizeof(mtk_tpd_ic_name)-1);// zhoulidong add
- #ifdef CONFIG_HAS_EARLYSUSPEND
- MTK_TS_early_suspend_handler.suspend = g_tpd_drv->suspend;
- MTK_TS_early_suspend_handler.resume = g_tpd_drv->resume;
- register_early_suspend(&MTK_TS_early_suspend_handler);
- #endif
- #ifdef TPD_TYPE_CAPACITIVE
- /*TPD_TYPE_CAPACITIVE handle */
- if(touch_type== 1){
- set_bit(ABS_MT_TRACKING_ID,tpd->dev->absbit); //對輸入設備初始化
- set_bit(ABS_MT_TOUCH_MAJOR,tpd->dev->absbit);
- ....
- #if 0 // linux kernel update from 2.6.35--> 3.0
- tpd->dev->absmax[ABS_MT_POSITION_X]= TPD_RES_X;
- ....
- #else
- input_set_abs_params(tpd->dev,ABS_MT_POSITION_X, 0, TPD_RES_X, 0, 0); //再次對輸入設備初始化
- ....
- #endif
- TPD_DMESG("Cap touch paneldriver\n");
- }
- #endif
- #if 0 //linux kernel update from 2.6.35 --> 3.0
- tpd->dev->absmax[ABS_X] = TPD_RES_X;
- tpd->dev->absmin[ABS_X] = 0;
- tpd->dev->absmax[ABS_Y] = TPD_RES_Y;
- tpd->dev->absmin[ABS_Y] = 0;
- tpd->dev->absmax[ABS_PRESSURE] = 255;
- tpd->dev->absmin[ABS_PRESSURE] = 0;
- #else
- input_set_abs_params(tpd->dev,ABS_X, 0, TPD_RES_X, 0, 0); //再次對輸入設備初始化
- input_set_abs_params(tpd->dev,ABS_Y, 0, TPD_RES_Y, 0, 0);
- input_abs_set_res(tpd->dev,ABS_X, TPD_RES_X);
- ....
- #endif
- if(input_register_device(tpd->dev)) //向輸入子系統註冊輸入設備
- TPD_DMESG("input_register_device failed.(tpd)\n");
- else
- tpd_register_flag= 1;
- /* init R-Touch */
- #if 0
- if(touch_type == 0)
- {
- g_tpd_drv->tpd_local_init();
- }
- #endif
- if(g_tpd_drv->tpd_have_button)
- {
- tpd_button_init();
- }
- if(g_tpd_drv->attrs.num)
- tpd_create_attributes(&pdev->dev,&g_tpd_drv->attrs);
- return 0;
- }
4) 觸摸事件觸發實現
//依次執行其中的tpd_local_init()接口,此接口是對tpd_i2c_driver(下面會提到這個結構體)進行i2c總
線的註冊,先匹配id,若無則匹配name,匹配成功後執行tpd_i2c_driver驅動中的tpd_probe接口
- int tpd_local_init(void)
- {
- // 對 tpd_i2c_driver 進行註冊
- if(i2c_add_driver(&tpd_i2c_driver)!=0) {
- TPD_DMESG("unable to add i2c driver.\n");
- return -1;
- }
- if (0) //if(tpd_load_status == 0)
- {
- TPD_DMESG("add error touch paneldriver.\n");
- i2c_del_driver(&tpd_i2c_driver);
- return -1;
- }
- #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_def_calmat_local, 8*4);
- memcpy(tpd_def_calmat, tpd_def_calmat_local, 8*4);
- #endif
- TPD_DMESG("end %s, %d\n", __FUNCTION__, __LINE__);
- tpd_type_cap = 1;
- return 0;
- }
- // tpd_i2c_driver 初始化
- static struct i2c_driver tpd_i2c_driver = {
- .driver = {
- .name = "mtk-focal",
- },
- .probe =tpd_probe,
- .remove =tpd_remove,
- .id_table =ft5206_tpd_id,
- .detect =tpd_detect,
- };
- // tpd_probe 的定義
- static int tpd_probe(struct i2c_client *client, const struct i2c_device_id *id)
- {
- intretval = TPD_OK;
- chardata;
- intreset_count = 0;
- client->timing= 300;
- i2c_client= client;
- reset_proc:
- //power on, need confirm with SA
- mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
- mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
- mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ZERO);
- msleep(5);
- TPD_DMESG(" fts ic reset\n");
- mt_set_gpio_mode(GPIO_CTP_RST_PIN, GPIO_CTP_RST_PIN_M_GPIO);
- mt_set_gpio_dir(GPIO_CTP_RST_PIN, GPIO_DIR_OUT);
- mt_set_gpio_out(GPIO_CTP_RST_PIN, GPIO_OUT_ONE);
- msleep(200);
- if((i2c_smbus_read_i2c_block_data(i2c_client, 0x00, 1, &data))<0) //系統和TP通過 IIC 通信,若通信失敗則認爲TP 設備未連接到主板
- {
- TPD_DMESG("I2Ctransfer error, line: %d\n", __LINE__);
- #ifdef TPD_RESET_ISSUE_WORKAROUND
- if ( reset_count < TPD_MAX_RESET_COUNT )
- {
- reset_count++;
- goto reset_proc;
- }
- #endif
- return -1;
- }
- mt_set_gpio_mode(GPIO_CTP_EINT_PIN,GPIO_CTP_EINT_PIN_M_EINT); //設置引腳中斷
- mt_set_gpio_dir(GPIO_CTP_EINT_PIN,GPIO_DIR_IN);
- mt_set_gpio_pull_enable(GPIO_CTP_EINT_PIN,GPIO_PULL_ENABLE); //打開中斷使能
- mt_set_gpio_pull_select(GPIO_CTP_EINT_PIN,GPIO_PULL_UP); //設定中斷觸發方式
- // 註冊中斷處理函數,作用是 喚醒(wake up interrupt)等待隊列,等待隊列頭上懸掛着觸摸事件的等待隊列
- mt_eint_registration(CUST_EINT_TOUCH_PANEL_NUM,CUST_EINT_TOUCH_PANEL_TYPE, tpd_eint_interrupt_handler, 0);
- mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
- ....
- #ifdefTPD_AUTO_UPGRADE
- thread= kthread_run(tpd_ft_update_fw, 0, TPD_DEVICE);
- if(IS_ERR(thread))
- {
- retval= PTR_ERR(thread);
- TPD_DMESG(TPD_DEVICE" failed to create kernel thread for update fw: %d\n", retval);
- }
- #endif
- thread= kthread_run(touch_event_handler, 0, TPD_DEVICE); //創建一個內核中運行的線程,這個線程主要是實現觸摸事件發生時,採集觸摸點數
- if(IS_ERR(thread))
- {
- retval= PTR_ERR(thread);
- TPD_DMESG(TPD_DEVICE" failed to create kernel thread: %d\n", retval);
- }
- TPD_DMESG("FTSTouch Panel Device Probe %s\n", (retval < TPD_OK) ? "FAIL" :"PASS");
- ....
- return 0;
- }
- //觸摸中斷事件處理函數:tpd_eint_interrupt_handler
- static voidtpd_eint_interrupt_handler(void)
- {
- //TPD_DEBUG("TPDinterrupt has been triggered\n");
- TPD_DEBUG_PRINT_INT;
- tpd_flag= 1; //將標誌位置位
- wake_up_interruptible(&waiter); // 喚醒等待隊列
- }
- //觸摸事件 採集線程
- static int touch_event_handler(void*unused)
- {
- ....
- sched_setscheduler(current,SCHED_RR, ¶m);
- ....
- do
- {
- set_current_state(TASK_INTERRUPTIBLE); //設置當前線程睡眠
- wait_event_interruptible(waiter,tpd_flag!=0); // 設置等待隊列等待事件,tpd_flag 會在中斷處理函數中置位
- tpd_flag= 0;
- set_current_state(TASK_RUNNING);
- if(tpd_touchinfo(&cinfo, &pinfo, &point_num)) //獲得多點觸控信息
- {
- //TPD_DEBUG("point_num= %d\n",point_num);
- TPD_DEBUG_SET_TIME;
- if(point_num>0)
- {
- for(i =0; i<point_num; i++)
- {
- tpd_down(cinfo.x[i], cinfo.y[i],cinfo.id[i]); //上傳多點觸控信息
- }
- input_sync(tpd->dev);
- }
- else
- {
- tpd_up(cinfo.x[0], cinfo.y[0]); //上傳一個觸控信息
- input_sync(tpd->dev);
- }
- }
- mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
- }while(!kthread_should_stop());
- return0;
- }