环境:
MT8788 Android11 focaltech_touch HD720 5寸tp
log信息 分析步骤:
一 获取dts信息 和 添加驱动到内核
1.获取dts设备树中配置的信息 2.将tp厂商的驱动文件注册添加到 tpd_driver_list[] 全局数组中
- 驱动注册加载流程:
- 执行tpd_driver_init()
- 1.tpd_get_dts_info();
- 2.tpd_driver_add(&tpd_device_driver);
- static struct tpd_driver_t tpd_device_driver = {
- .tpd_device_name = FTS_DRIVER_NAME,
- .tpd_local_init = tpd_local_init,
- .suspend = tpd_suspend,
- .resume = tpd_resume, };
二 注册一个平台驱动driver 匹配后 tpd_probe执行
1.模块入口函数,实现调用tpd_init_work_callback函数
a.create_singlethread_workqueue用于创建workqueue工作队列,@name:workqueue名称
b.初始化工作队列,调用tpd_init_work_callback函数用于注册一个对应的平台驱动,INIT_WORK只是定义了work和work对应的操作
c.queue_work激活时,执行对应work,即执行tpd_init_work_callback函数
2.执行tpd_init_work_callback函数
a.调用platform_driver_register(&tpd_driver)注册一个平台驱动driver 平台驱动(platform driver)注册成功后,compatible相同匹配成功,就执行tpd_probe函数
- mtk_tpd的平台调用流程: kernel-4.14\drivers\input\touchscreen\mediatek\mtk_tpd.c
- 1. static int __init tpd_device_init(void)
- a. tpd_init_workqueue = create_singlethread_workqueue("mtk-tpd");
- b. INIT_WORK(&tpd_init_work, tpd_init_work_callback);
- c. res = queue_work(tpd_init_workqueue, &tpd_init_work);
- 2. static void tpd_init_work_callback(struct work_struct *work)
- a. platform_driver_register(&tpd_driver) != 0)
三 tpd_Probe函数执行(重点调用第三方驱动注册的 tpd_local_init 函数)
- kernel-4.14\drivers\input\touchscreen\mediatek\mtk_tpd.c //内分析tpd_probe函数
- tpd_probe(struct platform_device *pdev)
- 1.注册杂项设备
- static struct miscdevice tpd_misc_device = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "touch",
- .fops = &tpd_fops,}
- 2.获得 dts 相关引脚配置
- pinctrltpd_get_gpio_info(pdev);
- 3.分配 mtk 封装的 tp 数据结构
- tpd = kmalloc(sizeof(struct tpd_device), GFP_KERNEL);
- 4.分配输入子系统
- tpd->dev = input_allocate_device();
- 5.处理 touch panel 报点旋转
- #ifdef CONFIG_MTK_LCM_PHYSICAL_ROTATION
- if (strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "90", 2) == 0 || strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "270", 3) == 0) {
- #ifdef CONFIG_MTK_FB
- /*Fix build errors,as some projects cannot support these apis while bring up*/
- TPD_RES_Y = DISP_GetScreenWidth();
- TPD_RES_X = DISP_GetScreenHeight();
- #endif
- 6.相关报点参数设置
- 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;
- 7.设置输入设备 TP 上报事件类型
- set_bit(EV_ABS, tpd->dev->evbit);
- 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);
- 8.调用第三方驱动注册的的 init 函数
- for (i = 1; i < TP_DRV_MAX_COUNT; i++){
- /* add tpd driver into list */
- if (tpd_driver_list[i].tpd_device_name != NULL) {
- 8.1.调用tpd_local_init()函数
- tpd_driver_list[i].tpd_local_init();
- /* msleep(1); */
- if (tpd_load_status == 1) {
- TPD_DMESG("tpd_probe, tpd_driver_name=%s\n",
- tpd_driver_list[i].tpd_device_name);
- 8.2.去执行具体的TP ic驱动
- g_tpd_drv = &tpd_driver_list[i];
- break;
- }
- }
- }
- 9.如果没有注册 tp 驱动,则调用默认的 R 电阻屏驱动
- if (g_tpd_drv == NULL) {
- 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();
- }
- }
- 10.调用当前使用的 tp 驱动的 resume() 函数
- touch_resume_workqueue = create_singlethread_workqueue("touch_resume");
- INIT_WORK(&touch_resume_work, touch_resume_workqueue_callback);
- 11.注册内核通知链,监听fb_notifier,应该会在相应的屏的驱动里面进行通知调用
- 11.1.在这里进行 TP 的唤醒和休眠操作
- tpd_fb_notifier.notifier_call = tpd_fb_notifier_callback;
- tpd_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
- 11.2.注册通知链
- fb_register_client(&tpd_fb_notifier)
- 11.3.设置输入设备 TP 上报事件
- 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);
- input_abs_set_res(tpd->dev, ABS_Y, TPD_RES_Y);
- input_set_abs_params(tpd->dev, ABS_PRESSURE, 0, 255, 0, 0);
- input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);
- 11.4.注册输入设备
- if (input_register_device(tpd->dev))
- TPD_DMESG("input_register_device failed.(tpd)\n");
- else
- tpd_register_flag = 1;
- 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);
四 tpd_local_init函数执行过程
1.调用i2c_add_driver(&tpd_i2c_driver),(有的驱动在这里上电)
a.注册一个i2c设备驱动,并与注册的device name匹配,匹配一致后将执行tpd_device_driver里面的tpd_probe函数,成功的话就会将tpd_load_status变量置1
2.设置按键
3.i2c驱动probe函数执行
a.创建工作队列(中断下半部)
b.中断申请函数
c.初始化 注册输入设备
- 目录:\kernel-4.14\drivers\input\touchscreen\mediatek\focaltech_touch\focaltech_core.c
- 1.tpd_local_init
- 1.1.-> fts_ts_driver_init()
- 1.2.-> i2c_add_driver(&fts_ts_driver);
- 2.tpd_button_setting
- 3.static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
- -> ret = fts_ts_probe_entry(ts_data);
五.申请中断
1.获取触摸设备节点
2.创建内核线程 touch_event_handler(重要函数)
3.申请中断 fts_irq_handler为中断处理函数
- 目录:\kernel-4.14\drivers\input\touchscreen\mediatek\focaltech_touch\focaltech_core.c
- static int fts_irq_registration(struct fts_ts_data *ts_data)
- {
- 1.node = of_find_matching_node(node, touch_of_match);
- 2.ts_data->thread_tpd = kthread_run(touch_event_handler, 0, TPD_DEVICE);
- 3.ret = request_irq(ts_data->irq, fts_irq_handler,
- ts_data->pdata->irq_gpio_flags, FTS_DRIVER_NAME,
- ts_data);
- return ret;
- }
4.中断处理函数
a.(主动唤醒)当设备可以使用的时候就要唤醒进入休眠态的进程,wakr_up等待队列头中的所有进程都唤醒。
b.1.除了主动唤醒以外,也可以设置等待队列等待某个事件,当这个事件满足以后就自动唤醒等待队列中的进程
b.2.等待以waiter为等待队列头的等待队列被唤醒,前提是 condition 条件必须满足(为真),否则一直阻塞,允许被信号打断
注释: 第二个函数看的不是很懂猜测大概就是给当前正在运行的进程创建并初始化了一个等待队列项。
c.重要函数 fts_irq_read_report() 调用 fts_input_report_b(ts_data)
- 目录:\kernel-4.14\drivers\input\touchscreen\mediatek\focaltech_touch\focaltech_core.c
- 1.static irqreturn_t fts_irq_handler(int irq, void *data)
- wake_up_interruptible(&waiter);
- 2. static int touch_event_handler(void *unused)
- {
- struct sched_param param = {.sched_priority = RTPM_PRIO_TPD};
- sched_setscheduler(current, SCHED_RR, ¶m);
- do {
- set_current_state(TASK_INTERRUPTIBLE);
- wait_event_interruptible(waiter, tpd_flag != 0);
- tpd_flag = 0;
- set_current_state(TASK_RUNNING);
- #if FTS_PSENSOR_EN
- if (fts_proximity_readdata(fts_data) == 0)
- continue;
- #endif
- 3. fts_irq_read_report();
- // -> fts_input_report_b(ts_data);
- } while (!kthread_should_stop());
- return 0;
- }
5.Linux 内核通过读取设备树中的中断属性信息来配置中断
5.1.iic设备信息
5.2. interrupt-parent 对应的是平台的中断控制器,里面应用的 pio对应的是mt6771.dtsi文件里面的中断控制器dts描述
5.3.interrupts 的第一个参数对应的是中断号,第二个参数对应的是中断的触发方式,第三个参数是GPIO号,&pio内设置,第四个是GPIO口的电平
- //cust.dtsi设备树文件内
- 1.&i2c0 {
- #address-cells = <1>;
- #size-cells = <0>;
- clock-frequency = <100000>;
- mediatek,use-open-drain;
- cap_touch_mtk:cap_touch@5D {
- compatible = "mediatek,cap_touch";
- reg = <0x5D>;
- status = "okay";
- };
- };
- &touch {
- 2. interrupt-parent = <&pio>;
- 3. interrupts = <0 IRQ_TYPE_EDGE_FALLING 0 0>;
- status = "okay";
- };
六.最后就是fts_input_report_b(ts_data) 上报和同步读取到的坐标信息