MTK 平台TP 驱动
下面以汇顶gt9xx_driver.c 驱动为例讲解TP 驱动的整个关键点,本篇只讲TP 驱动本身的代码,
在驱动代码涉及的方法技术,因为每一个都牵涉linux内核的设计和知识,后面会逐个展开深入讲解。
首先,我们来总体看下TP 驱动代码初始化流程:

MTK kernel-4.14 TP 驱动初始化和部分工作流程main.cmain.cgt9xx_driver.cgt9xx_driver.cmtk_tpd.cmtk_tpd.ci2c.hi2c.hkthread.hkthread.hwait.hwait.hgt9xx_update.cgt9xx_update.cdo_initcallsmodule_inittpd_driver_inittpd_get_dts_infotpd_driver_addtpd_local_initi2c_add_drivertpd_i2c_probetpd_power_ongtp_init_panelkthread_runtouch_event_handlerwait_event_interruptibletpd_irq_registrationtpd_interrupt_handlerwake_up_interruptiblegup_init_update_proc
首先看TP 驱动模块初始化:
| 1 2 3 4 5 6 7 8 9 |
|
请注意这个tpd_driver_init 是一个__init 修饰的函数,说明这个函数在编译时会被放到跟其他__init 修饰的函数放到一起,
在系统初始化,一旦内核启动后,就释放这些东西。一般用__init修饰的变量或者函数会编译到专门的一个段里面去,
这个段的数据和函数只有在kernel初始化的时候会被调用,以后一定不会被使用,kernel可能会在以后的某个时候释放掉这个段所占用的内存,
给别的地方使用,是不是设计很巧妙? 感兴趣的同学可以继续深入分析看看。
那么,我们看到这个函数主要就做了两件事情:
(1)是获取这个TP 所有的DTS 信息,我们看看这个代码的实现:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
|
这个函数看似很长,其实就是在读取我们定义在dts文件里面跟这个TP相关的数据,包括最大手指数:tpd-max-touch-num,对应的数据上报分辨率:tpd_resolution
复位GPIO 配置:tpd-rst-ext-gpio-num等信息。
我们来看一个相关dts配置信息:
arch/arm64/boot/dts/mediatek/tb8788p1_64_bsp.dts
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
(2)第二件事情,将这个tpd 的驱动加入到内核里面来:
tpd_driver_add(&tpd_device_driver)
| 1 2 3 4 5 6 |
|
这个driver 结构体里面定义了这个触摸屏的真正初始化函数,以及他的睡眠和唤醒函数,
另外tpd_device_name 名字为gt9xx,这个为以后识别设备,然后加载驱动提供的名字匹配。
同时,如果看了tpd_driver_add 代码实现,就能发下这个driver会被加入到tpd_driver_list 中。
接下来我们分析TP的真正初始化:tpd_local_init
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
|
我们看到这个代码里面写了很多个宏,
GTP_ESD_PROTECT 是用来打开 TP 静电保护功能的,这个功能的主要作用是在TP 遇到高电压的静电时能够恢复工作,而不会影响使用。
如果对这个静电机制感兴趣,可以再深入研究,后面我再开一篇文章讲解。
第二个宏CONFIG_GTP_CHARGER_DETECT,这个宏看起来是用来充电情况下保证TP工作的,这应该是跟具体TP有关,因为有的TP在
工作时会受充电器影响,所以这个驱动中加了这个充电检测机制。
第三个宏GTP_SUPPORT_I2C_DMA ,这个主要是申请I2C DMA的空间,用户I2C 传输数据,这个指向的是直接虚拟地址,有利于提高传输数据效率。
接下来我们看到 regulator_set_voltage(tpd->reg, 2800000, 2800000);
这个是设置TP 的工作电压,这个代码看起来是后面加的,正常是把这个配置放DTS文件就好了,不会直接放到驱动初始化。
再来看驱动真正的注册I2C 驱动,因为大部分TP 设备都是挂着I2C 总线上面,所以驱动里面都是注册I2C驱动
i2c_add_driver(&tpd_i2c_driver)
i2c_add_driver 是一个宏定义在i2c.h中,其真正的实现在根据平台需要自己的实现中,有兴趣的可以再继续深入研究。
我们来关注tpd_i2c_driver 本身这个结构体的情况:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
注意,这个是i2c driver,跟上面讲解的tpd devce driver不是同一个!
这个结构体里面name同样是用来匹配设备的,另外还有一个of_match_table是用来匹配dts数据信息的
tpd_i2c_probe 是这个tpd i2c 真正的初始化函数,tpd_i2c_detect主要写入tpd驱动的类型。
static const struct i2c_device_id tpd_i2c_id[] = {{"gt9xx", 0}, {} };
id_table 是用来列出支持这个驱动的设备列表,与name的意义相似。
接下来我们讲讲这个i2c 驱动的初始化:tpd_i2c_probe
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
|
首先我们看到 宏CONFIG_GTP_PROXIMITY ,这个宏意思就是这个TP是否支持距离传感功能,如果支持这个功能,就会在后面通过hwmsen_attach(ID_PROXIMITY, &obj_ps)来注册一个
ID 为ID_PROXIMITY 的sensor到MTK 设备驱动中,但大部分情况下,我们是用的额外的psensor,这里暂时不做这个psensor研究。
接下来我们看到tpd_power_on 给tp IC上电的流程:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
这个函数就是对一些GPIO进行操作,比如复位GPIO,使能IRQ的GPIO,同时使能一些regulator来打开电流。
gtp_init_panel 这个 真正的初始化TP IC 的,这个函数的主要就是对TP的一些寄存器进行设置操作,这部分主要由TP FAE来提供,在没有规格书的情况下是无法进行修改的。
创建一个内核线程:
thread = kthread_run(touch_event_handler, 0, TPD_DEVICE);
这名为TPD_DEVICE线程thread是为了处理中断传来的数据,
需要注意的是,判断thread是否有效需要用IS_ERR()来判断,而不是简单的使用(thread == NULL)判断。
我们看看这个线程的真正实现:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
|
这个函数非常长,它的主要功能就是当中断发生时,读取坐标、上报坐标、手势识别、按键等等信息。
再关注probe函数里面的 tpd_irq_registration 中断注册函数
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
这中断的就是TP 工作时,中断的处理,它的处理函数为:
tpd_interrupt_handler
| 1 2 3 4 5 6 7 |
|
这个中断处理函数是通过一个 waiter 来处理的,它是一个wait 工作队列
static DECLARE_WAIT_QUEUE_HEAD(waiter);
TP 固件自动升级函数:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
也是通过一个thread来进行固件升级,真正的升级流程放在了gup_update_proc函数中。
到这里,所有的TP 主要功能函数都讲解了,具体每个细节后续再通过其他文档补充说明。
最后,我们再来关注一些其他细节情况:
1、tpd_down()和tpd_up() 这两个函数通过input子系统上报坐标以及上报手指抬起的动;
2、tpd_suspend()和tpd_resume() 关于休眠和唤醒的内容根据ic的特性设置。如休眠的时候需要关闭中断、配置进入休眠模式。另外唤醒的复位设置也需要关注;
3、汇顶还实现了gtp_i2c_test 来测试TP设备与I2C 通信是否正常;
4、GTP_MAX_TOUCH 可以限定TP 最大支持touch数量;
5、gtp_reset_guitar 可用来对TP的复位,一般在待机唤醒和ESD 静电等其他容易出现异常的情况下使用。