MTK TP驱动移植调试一般分为五步:
1、硬件IO口配置;
2、TP驱动移植;
3、I2C通信;
4、中断触发;
5、数据上报;
MTK Touch 驱动的组成:
Mtk Touch driver 驱动包括:Mtkplatform 虚拟平台设备驱动、Module touch IC 驱动、Inputsubsystem。
Mtk platform 设备驱动是mtk为了兼容多个touch IC驱动而设计出来的虚拟驱动,它会去遍历每一个touch IC驱动,直到其中一个初始化成功。
Linux input_subsystem是linux的输入子系统,我们的输入设备都要通过这个子系统进行上报事件以及设置事件的类型。
一、在LINUX启动过程中,先注册kernel-4.4/drivers/input/touchscreen/mediatek/目录下的具体驱动(如msg2238_ilitek),在驱动tpd_driver_init函数里调用tpd_driver_add(&tpd_device_driver)
把该tp驱动加入到一个静态数组tpd_driver_list[i]中。 在kernel-4.4/drivers/input/touchscreen/mediatek/mtk_tpd.c中moduel_init首先加载的函数tpd_device_init 函数中
调用tpd_init_work_callback --> 然后platform_driver_register(&tpd_driver)注册一个mtk_touch_driver平台驱动driver --> mtk_touch_driver函数
的.of_match_table = touch_of_match的compatible = "mediatek,mt6739-touch"与在mt6739.dts注册的设备device touch: touch compatible = “mediatek,mt6739-touch”;相同,就执行tpd_probe函数。
根据platform bus match的规则:driver_name 和device_name相同就会调用platform_driver 的probe函数。(这里牵扯到Linux的设备模型知识,需要了解的人可以看下Linux platform bus。)
在tpd_probe函数中,调用tpd_get_gpio_info(pdev)函数解析dts参数,调用input_allocate_device()分配一个输入设备,设置input 设备事件类型,
执行if(tpd_driver_list[i].tpd_device_name != NULL)//这里是在遍历mtk的tpd_driver_list里面的所有的驱动,判断名字是否为NULL,每一个module touch IC 驱动都会添加到这个静态数组里面。
tpd_driver_list[i].tpd_local_init(); if(tpd_load_status ==1) {//这里我们会判断我们所遍历的每一个module IC 驱动的初始化函数tpd_local_init(),该函数会注册i2c驱动i2c_add_
driver(&tpd_i2c_driver),注册成功会跟注册的device匹配,匹配通过(name一致)然后probe成功的话就会将tpd_load_status变量置1,
所以我们就是通过这个值tpd_load_status判断使用哪一个驱动的。然后调用g_tpd_drv = &tpd_driver_list[i]去执行具体的TP ic驱动。
具体TP ic驱动里面,主要进行IC的上电、申请中断号注册中断处理函数、Update FW等动作。
例如分析msg2238驱动:从mtk_tpd.c中匹配到具体的TP ic驱动之后,首先执行ilitek_drv_mtk.c中tpd_driver_init --> 执行i2c_register_board_info(I2C_BUS_ID, &i2c_tpd, 1);注册i2c设备,
–> 执行tpd_driver_add(&tpd_device_driver)注册驱动–> 执行tpd_local_init --> 执行i2c_add_driver(&tpd_i2c_driver)函数,其实就是将这个驱动添加到上面说的静态数组tpd_driver_lis里面,
–> 设备&i2c_tpd device的name:TP_IC_NAME和驱动tpd_i2c_driver的name:TP_IC_NAME一致,根据i2c驱动与设备匹配原则,就执行tpd_device_driver里面的tpd_probe函数。
–> MsDrvInterfaceTouchDeviceSetIicDataRate(g_I2cClient, 100000); 设置i2c速率 --> ret = regulator_set_voltage(g_ReguVdd, 2800000, 2800000)上电
–> 然后执行MsDrvInterfaceTouchDeviceProbe函数(ilitek_drv_main.c) --> 然后执行DrvTouchDeviceRegisterFingerTouchInterruptHandler();(ilitek_drv_main.c)
–> 申请中断request_threaded_irq (ilitek_drv_main.c)–> 注册中断处理函数_DrvFingerTouchInterruptHandler,(ilitek_drv_main.c)注册中断后,每次触摸到TP就会产生硬件中断,
调用到中断处理函数_DrvFingerTouchInterruptHandler。–> 注册完中断后执行INIT_WORK(&_gFingerTouchWork, _DrvFingerTouchDoWork);初始化触摸工作队列_DrvFingerTouchDoWork;
---------检测到有中断到来时-----------
–> 执行_DrvHandleFingerTouch(); --> 执行_DrvSelfHandleFingerTouch();然后调用rc = IicReadData(SLAVE_I2C_ID_DWI2C, &pPacket[0], nReportPacketLength);读取tp寄存器的数据:,
–> 并且通过input输入子系统上报数据到安卓层,_DrvSelfHandleFingerTouch有执行iic读取数据的操作:rc = IicReadData(SLAVE_I2C_ID_DWI2C, &pPacket[0], nReportPacketLength
二:中断处理流程()
注册中断后,每次触摸到TP就会产生硬件中断,调用到中断处理函数_DrvFingerTouchInterruptHandler,从中断处理函数_DrvFingerTouchInterruptHandler,分析下TP的触摸事件处理流程:
如果有触摸动作,产生中断( if (_gInterruptFlag == 1),然后disable_irq_nosync(_gIrq);//禁止中断,无需进行同步,防止死锁 --> 启动工作队列schedule_work(&_gFingerTouchWork);
–> _DrvFingerTouchDoWork --> 执行_DrvHandleFingerTouch(); --> 执行_DrvSelfHandleFingerTouch();然后调用rc = IicReadData(SLAVE_I2C_ID_DWI2C, &pPacket[0], nReportPacketLength);
读取TP对应寄存器的数据。–> 调用input_report_key 上报虚拟按键信息,input_report_abs 上报相对坐标信息 ,每次上报完都要调用input_sync(g_InputDevice);每个点上报后都需要同步
表示上报数据完成 -->调用DrvFingerTouchPressed(tInfo.tPoint[i].nX, tInfo.tPoint[i].nY, 1, i);收集手指按压TP的数据信息,
调用DrvFingerTouchReleased收集释放的动作信息(其中涉及多点触摸协议(可百度了解),分A类和B类);
下面是Ft6xxx(敦泰的Touch IC)的一个中断处理流程,供参考:
二:中断处理流程
注册中断后,每次触摸到TP就会产生硬件中断,调用到中断处理函数tpd_eint_interrupt_handler
从中断处理函数tpd_eint_interrupt_handler()分析下TP的触摸事件处理流程
static void tpd_eint_interrupt_handler(void)
{
TPD_DEBUG_PRINT_INT;
tpd_flag = 1;
//唤醒内核进程touch_event_handler中的等待队列
wake_up_interruptible(&waiter);
}
static int touch_event_handler(void *unused)
{
//设置Task 的状态为可中断的等待状态
set_current_state(TASK_INTERRUPTIBLE);
//满足tpd_flag!=0 就唤醒队列
wait_event_interruptible(waiter,tpd_flag!=0);
tpd_flag = 0; //改变条件
//设置Task 的状态为执行态
set_current_state(TASK_RUNNING);
//touchinfo 函数获取手指触摸TP的信息,譬如按下的手指数目,每个点的坐标
if (tpd_touchinfo(&cinfo, &pinfo))
//tpd_down 上报按下的坐标信息
tpd_down(cinfo.Point[i].X, cinfo.Point[i].Y, i);
input_report_key(tpd->dev, BTN_TOUCH, 1);
input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, 20);
input_report_abs(tpd->dev, ABS_MT_POSITION_X, x);
input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y);
//tpd_up 上报离开的信息
tpd_up(map_x, map_y, 0);
input_mt_sync //单点同步,每个点上报后都需要同步
input_sync(tpd->dev); //每一次操作结束后的同步,不论是多点还是单点值,
这里面,我们看到几个新函数
tpd_calibrate,tpd_down,tpd_up
主要是通过传入坐标值,对TOUCH的down和up值做处理。
void tpd_down(int raw_x, int raw_y, int x, int y, int p) {
if(tpd && tpd->dev && tpd_register_flag1) {
input_report_abs(tpd->dev, ABS_PRESSURE, p/PRESSURE_FACTOR);
input_report_key(tpd->dev, BTN_TOUCH, 1);
input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, p/PRESSURE_FACTOR);
input_report_abs(tpd->dev, ABS_MT_WIDTH_MAJOR, p/PRESSURE_FACTOR);
input_report_abs(tpd->dev, ABS_MT_POSITION_X, x);
input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y);
input_mt_sync(tpd->dev);
TPD_DEBUG(“D[M M M]\n”, x, y, p);
TPD_EM_PRINT(raw_x, raw_y, x, y, p, 1);
}
}
void tpd_up(int raw_x, int raw_y, int x, int y, int p) {
if(tpd && tpd->dev && tpd_register_flag1) {
input_report_abs(tpd->dev, ABS_PRESSURE, 0);
input_report_key(tpd->dev, BTN_TOUCH, 0);
input_report_abs(tpd->dev, ABS_MT_TOUCH_MAJOR, 0);
input_report_abs(tpd->dev, ABS_MT_WIDTH_MAJOR, 0);
input_report_abs(tpd->dev, ABS_MT_POSITION_X, x);
input_report_abs(tpd->dev, ABS_MT_POSITION_Y, y);
input_mt_sync(tpd->dev);
TPD_DEBUG(“U[M M M]\n”, x, y, 0);
TPD_EM_PRINT(raw_x, raw_y, x, y, p, 0);
}
}
这里面主要通过驱动linux的输入设备驱动函数input_report_abs, input_report_key把linux输入设备的坐标和输入设备事件提交到linux内核里面去。
其他:虚拟按键在dts配置,在ilitek_drv_common.h中有对应定义:
#ifdef CONFIG_TP_HAVE_KEY
#define TOUCH_KEY_MENU (139) //229
#define TOUCH_KEY_HOME (172) //102
#define TOUCH_KEY_BACK (158)
#define TOUCH_KEY_SEARCH (217)
#define MAX_KEY_NUM (4)
#endif //CONFIG_TP_HAVE_KEY
相关的一些宏控:
打开固件自动升级:CONFIG_UPDATE_FIRMWARE_BY_SW_ID=y
打开TP手势唤醒:CONFIG_ENABLE_GESTURE_WAKEUP=y