MTK TP驱动代码调用流程

驱动注册加载流程

注册kernel-4.49/drivers/input/touchscreen/mediatek/目录下的具体驱动(如ft5x0x),入口module_init(tpd_driver_init)会调用tpd_get_dts_info()获取到dts 设备树中与 touch 相关的配置项并保存到全局变量tpd_dts_data中,然后在tpd_driver_init函数里调用tpd_driver_add(&tpd_device_driver)

//tpd.h		相关结构体定义
struct tpd_dts_info tpd_dts_data;
struct tpd_dts_info {
	int tpd_resolution[2];
	int touch_max_num;
	int use_tpd_button;
	int tpd_key_num;
	int tpd_key_local[4];
	bool tpd_use_ext_gpio;
	int rst_ext_gpio_num;
	struct tpd_key_dim_local tpd_key_dim_local[4];
	struct tpd_filter_t touch_filter;
};
struct tpd_driver_t {
	char *tpd_device_name;
	int (*tpd_local_init)(void);
	void (*suspend)(struct device *h);
	void (*resume)(struct device *h);
	int tpd_have_button;
	struct tpd_attrs attrs;
};

//ft5x0x_driver.c
static struct tpd_driver_t tpd_device_driver = {
	.tpd_device_name = "FT5x0x",
	.tpd_local_init = tpd_local_init,
	.suspend = tpd_suspend,
	.resume = tpd_resume,
	.attrs = {
		.attr = ft5x0x_attrs,
		.num  = ARRAY_SIZE(ft5x0x_attrs),
	},
};
/* called when loaded into kernel */
static int __init tpd_driver_init(void)
{
	TPD_DMESG("MediaTek FT5x0x touch panel driver init\n");		//打印提示
	tpd_get_dts_info();					//调用tpd_get_dts_info函数,获取该驱动的dts文件中的配置信息,tpd_get_dts_info()函数将dts信息保存在tpd_dts_info类型结构体变量tpd_dts_data里
	if (tpd_driver_add(&tpd_device_driver) < 0)		//调用tpd_drive_add添加一个驱动,
        //实则将这个驱动添加到静态数组tpd_driver_list中,添加成功返回1,失败返回-1
        //tpd_device_driver为驱动的TP驱动结构体,里面保存了驱动的name等信息
		TPD_DMESG("add FT5x0x driver failed\n");		

	return 0;
}

然后,在tpd_driver_add(&tpd_device_driver)中,会将这个TP驱动注册添加到 tpd_driver_list[] 全局数组中

//位置:kernel-4.9\drivers\input\touchscreen\mediatek\mtk_tpd.c
//添加驱动,将TP驱动添加到tpd_driver_list[]静态数组中去
int tpd_driver_add(struct tpd_driver_t *tpd_drv)	//传入ft5x0x驱动的tpd_device_driver结构体
{
	int i;
    //验证传入参数的有效性
	if (g_tpd_drv != NULL) {
		TPD_DMESG("touch driver exist\n");
		return -1;
	}
	/* check parameter */
	if (tpd_drv == NULL)	
		return -1;
	tpd_drv->tpd_have_button = tpd_dts_data.use_tpd_button;	//从k65v1_64_bsp.dst文件内获取tp虚拟按键配置的个数
	/* R-touch */
	if (strcmp(tpd_drv->tpd_device_name, "generic") == 0) {		//判断是否为电阻屏,便将该驱动添加到静态数组tpd_driver_list[0]中,并退出函数
		tpd_driver_list[0].tpd_device_name = tpd_drv->tpd_device_name;
		tpd_driver_list[0].tpd_local_init = tpd_drv->tpd_local_init;
		tpd_driver_list[0].suspend = tpd_drv->suspend;
		tpd_driver_list[0].resume = tpd_drv->resume;
		tpd_driver_list[0].tpd_have_button = tpd_drv->tpd_have_button;
		return 0;
	}
	for (i = 1; i < TP_DRV_MAX_COUNT; i++) {			//电容屏,将tp驱动添加到tpd_driver_list[i]
		/* add tpd driver into list */
		if (tpd_driver_list[i].tpd_device_name == NULL) {	
			tpd_driver_list[i].tpd_device_name =
				tpd_drv->tpd_device_name;
			tpd_driver_list[i].tpd_local_init =
				tpd_drv->tpd_local_init;
			tpd_driver_list[i].suspend = tpd_drv->suspend;
			tpd_driver_list[i].resume = tpd_drv->resume;
			tpd_driver_list[i].tpd_have_button =
				tpd_drv->tpd_have_button;
			tpd_driver_list[i].attrs = tpd_drv->attrs;
			break;
		}
		if (strcmp(tpd_driver_list[i].tpd_device_name,	//用添加后的list再次判断name是否一致
			tpd_drv->tpd_device_name) == 0)
			return 1;	/* driver exist *///返回1,表示添加成功
	}

	return 0;
}

mtk_tpd的平台调用流程

在kernel-4.9\drivers\input\touchscreen\mediatek\mtk_tpd.c文件里,late_initcall(tpd_device_init)入口函数首先加载进入tpd_device_init(),代码如下:

1、tpd_device_init()函数:

  • 调用tpd_init_work_callback函数

  • 然后调用platform_driver_register(&tpd_driver)注册一个平台驱动driver

  • 工作队列(workqueue)是另外一种将工作推后执行的形式.工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠。

//位置:kernel-4.9\drivers\input\touchscreen\mediatek\mtk_tpd.c
static int __init tpd_device_init(void)				
{
	int res = 0;

	tpd_init_workqueue = create_singlethread_workqueue("mtk-tpd");	//create_singlethread_workqueue用于创建workqueue工作队列,@name:workqueue名称
	INIT_WORK(&tpd_init_work, tpd_init_work_callback);	//初始化工作队列,调用tpd_init_work_callback函数用于注册一个对应的平台驱动,INIT_WORK只是定义了work和work对应的操作

	res = queue_work(tpd_init_workqueue, &tpd_init_work);	//queue_work激活时,执行对应work,即执行tpd_init_work_callback函数
	if (!res)
		pr_info("tpd : touch device init failed res:%d\n", res);
	return 0;
}

//
/* called when loaded into kernel */
static void tpd_init_work_callback(struct work_struct *work)
{
	TPD_DEBUG("MediaTek touch panel driver init\n");
	if (platform_driver_register(&tpd_driver) != 0)		//调用platform_driver_register(&tpd_driver)注册一个平台驱动driver 
		TPD_DMESG("unable to register touch panel driver.\n");
}


2、平台驱动(platform driver)注册成功后,会调用 of_match_table = touch_of_match 的compatible = “mediatek,touch” 与 在设备树dts文件内注册设备的platform device touch的 compatible = “mediatek,touch” 进行匹配。compatible相同匹配成功,就执行tpd_probe函数。

//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) {	遍历mtk的tpd_driver_list里面的所有的驱动,每个module touch IC 驱动都会添加到这个静态数组里面
			tpd_driver_list[i].tpd_local_init();	//调用tpd_local_init()函数,主要注册 i2c设备驱动,并初始化TP使用的reglator:上电 2.8v调用。i2c_add_driver(&tpd_i2c_driver),注册i2c驱动成功会跟注册的device匹配,匹配通过(name一致)然后具体驱动的probe成功的话就会将tpd_load_status变量置1
			/* msleep(1); */
			if (tpd_load_status == 1) {	//此处的tpd_load_status 是在对应使用的ft5x0x驱动中的probe函数中设置的,TP驱动初始化成功后,会设置此位为1,通过这个值tpd_load_status判断注册是否成功
				TPD_DMESG("tpd_probe, tpd_driver_name=%s\n",
				tpd_driver_list[i].tpd_device_name);
				g_tpd_drv = &tpd_driver_list[i];	//然后调用g_tpd_drv = &tpd_driver_list[i]去执行具体的TP ic驱动
				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,应该会在相应的屏的驱动里面进行通知调用 
    //在这里进行 TP 的唤醒和休眠操作
	tpd_fb_notifier.notifier_call = tpd_fb_notifier_callback;
	tpd_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) //参数:谁调用谁传
// 注册通知链
    fb_register_client(&tpd_fb_notifier)
// 设置输入设备 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);

//注册输入设备
if (input_register_device(tpd->dev))	//失败返回0
		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);

3、在ft5x0x_driver.c驱动文件内tpd_local_init函数

static int tpd_local_init(void)
{
	int retval;
#if defined(CONFIG_MACH_MT6765)	
    tpd->reg = regulator_get(tpd->tpd_dev, "vldo28");	//并初始化TP使用的reglator:上电 2.8v调用。
#else
	tpd->reg = regulator_get(tpd->tpd_dev, "vgp1");
#endif

#if defined(CONFIG_MACH_MT6761)	
    tpd->reg = regulator_get(tpd->tpd_dev, "vldo28");
#endif 

	retval = regulator_set_voltage(tpd->reg, 2800000, 2800000);		
	if (retval != 0) {
		TPD_DMESG("Failed to set reg-vgp6 voltage: %d\n", retval);
		return -1;
	}
	if (i2c_add_driver(&tpd_i2c_driver) != 0) {	//调用i2c_add_driver(&tpd_i2c_driver),注册一个i2c设备驱动,并与注册的device name匹配,匹配一致后将执行tpd_device_driver里面的tpd_probe函数,即ft5x0x的probe侦测函数,然后具体驱动的probe成功的话就会将tpd_load_status变量置1
		TPD_DMESG("unable to add i2c driver.\n");
		return -1;
	}
     /* tpd_load_status = 1; */
	if (tpd_dts_data.use_tpd_button) {
		tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local,
		tpd_dts_data.tpd_key_dim_local);
	}

4、中断部分

static int tpd_irq_registration(void)
{
	struct device_node *node = NULL;
	int ret = 0;

	node = of_find_compatible_node(NULL, NULL, "mediatek,touch");//在设备树(mt6765.dts)中匹配节点,获取touch_of_match数组中的name字符串与此处的touch进行匹配,
	if (node) {
		/*touch_irq = gpio_to_irq(tpd_int_gpio_number);*/	//不用gpio_to_irq来使用中断
		touch_irq = irq_of_parse_and_map(node, 0);	//使用irq_of_parse_and_map函数,解析dts内容的中断信息给驱动使用,后边的这个 0表示的是偏移索引,返回值:成功返回中断号
		TPD_DMESG("[%s] tpd request_irq irq=%d.", __func__,touch_irq);
		/*****************
		request_irq(touch_irq, tpd_eint_interrupt_handler,IRQF_TRIGGER_FALLING, TPD_DEVICE, NULL)函数,用于申请中断,
		@touch_irq:为中断号;
		@tpd_eint_interrupt_handler:为中断处理函数,一个回调函数,中断发生时系统将会调用这个函数;
		@IRQF_TRIGGER_FALLING:为中断处理的属性;
		@TPD_DEVICE:中断名称,通常是设备驱动程序的名称,在mtk_tpd.c文件内设置;
		@返回值:返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享
		*******************/
		ret = request_irq(touch_irq, tpd_eint_interrupt_handler,
					IRQF_TRIGGER_FALLING, TPD_DEVICE, NULL);
			if (ret > 0)
				TPD_DMESG("tpd request_irq IRQ LINE NOT AVAILABLE!.");
	} else {
		TPD_DMESG("[%s] tpd request_irq can not find touch eint device node!.", __func__);
	}
	return 0;
}
cust.dtsi设备树文件内
&i2c0 {
	#address-cells = <1>;
	#size-cells = <0>;
	clock-frequency = <100000>;
	mediatek,use-open-drain;
	cap_touch_mtk:cap_touch@5D {
		compatible = "mediatek,cap_touch";	//用于和驱动中的compatible相匹配
		reg = <0x5D>;		//reg是TP_IC的通信地址
		status = "okay";
	};
};
&touch {
	interrupt-parent = <&pio>;	//interrupt-parent 对应的是平台的中断控制器,里面应用的 pio对应的是mt6765.dtsi文件里面的中断控制器dts描述
	interrupts = <0 IRQ_TYPE_EDGE_FALLING 0 0>;	//interrupts 的第一个参数对应的是中断号,第二个参数对应的是中断的触发方式,第三个参数是GPIO号,&pio内设置,第四个是GPIO口的电平
	status = "okay";
};
  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值