您的位置:首页 > 编程语言 > PHP开发

基于MTK 的 TP 驱动分析

2014-09-11 17:52 399 查看
1. 克隆服务器工程源码并切换分支 

git clone git@192.168.20.2: 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;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: