drm vblank使能(enable)与去使能(disable)

一、vblank使能与去使能流程

本文的分析的代码基于linux内核5.4.191。

当应用程序调用命令为DRM_IOCTL_MODE_ATOMIC的ioctl时,陷入内核后会调用函数drm_mode_atomic_ioctl,有如下调用流程,应用程序调用ioctl时会传入类型为struct drm_mode_atomic的参数,该结构体有一个成员flags,以下流程以flags设置了DRM_MODE_ATOMIC_NONBLOCK为例:

应用程序进程						   kworker线程                                      driver irq handler
drm_mode_atomic_ioctl
  ->drm_atomic_nonblocking_commit
    ->drm_atomic_helper_commit
	  ->queue_work	  
									commit_work
									  ->commit_tail
										->drm_atomic_helper_commit_tail_rpm
										  ->drm_atomic_helper_commit_planes
											->xxx_crtc_atomic_flush driver回调函数
										  ->drm_atomic_helper_wait_for_vblanks
											->drm_crtc_vblank_get
											  ->drm_vblank_get
											->drm_crtc_vblank_count(crtc);
											->wait_event_timeout  超时时间100ms
											  睡眠,并挂到队列vblank->queue上
																					 xxx_crtc_irq_handler
																					   ->drm_handle_vblank
																						 ->drm_update_vblank_count
																						 ->wake_up(&vblank->queue);
											->drm_crtc_vblank_put						 ->drm_handle_vblank_events
												->drm_vblank_put							->drm_vblank_put
												  ->vblank_disable_fn/mod_timer				->send_vblank_event
																					   ->drm_crtc_send_vblank_event

以下分析中,黄色字体为应用程序流程,蓝色字体为kworker线程流程, 绿色字体为DC irq handler流程。

1、应用程序调用queue_work后将工作加入工作队列,工作的函数为commit_work,随后应用程序继续运行不需要等待。

2、kworker线程执行应用程序加入work队列的work的函数commit_work。

3、kworker线程会调用driver的回调函数xxx_crtc_atomic_flush,该回调函数主要是设置寄存器显示图像。

4、kworker线程调用函数drm_atomic_helper_wait_for_vblanks:

  • 函数drm_crtc_vblank_get将vblank的refcount即引用计数自增加1。如果引用计数自增后等于1,最终会调用DC driver注册的回调函数xxx_crtc_enable_vblank(类型为struct drm_crtc_funcs的成员enable_vblank指向该函数),该回调函数会使能DC crtc的中断;如果引用计数自增后不为1,则不做处理。
  • 函数drm_crtc_vblank_count获取vblank的count值,并保存该值。
  • 函数wait_event_timeout将kworker自身设置为TASK_UNINTERRUPTIBLE,并挂到vblank的queue队列上,直到超过100ms或上面保存的vblank的count值不等于新获取的vblank的count值。

5、在DC设置为每秒60帧的情况下,每16.7ms会触发一次中断, 所以100ms是远大于DC中断间隔时间的。DC中断触发后,中断处理程序调用函数drm_handle_vblank。

6、drm_handle_vblank会进行如下处理:

  • 函数drm_update_vblank_count会自增vblank的count值,自增的值一般是1。
  • 函数wake_up唤醒vblank的queue上的任务。此时kworker线程被唤醒,发现保存的vblank的count和新获取的vblank的count不等,则继续执行。此时kworker线程可以与DC IRQ handler并行执行。
  • 函数drm_handle_vblank_events将dev的链表vblank_event_list上的所有event发送出去。在该驱动handler中,该链表为空,所以不会执行drm_vblank_put和send_vblank_event。
  • 如果条件dev->vblank_disable_immediate && drm_vblank_offdelay > 0 && !atomic_read(&vblank->refcount)为真,则会调用函数vblank_disable_fn去使能(diable)vblank,关闭DC中断。在上面代码流程图中省略了。因为DC driver并未设置dev->vblank_disable_immediate为true。
  • 函数drm_crtc_send_vblank_event将vblank event发送出去。

7、kworker线程调用函数drm_crtc_vblank_put,最终调用函数drm_crtc_vblank_put,该函数对vblank的引用计数refcount自减1,如果自减后不为0,则不处理;否则进行如下处理:

  • 如果drm_vblank_offdelay为0,则不处理。设置该全局变量默认为5000ms。
  • 如果drm_vblank_offdelay小于,调用函数vblank_disable_fn去使能(diable)vblank。
  • 如果drm_vblank_offdelay大于且dev->vblank_disable_immediate为false,则调用函数mod_timer修改vblank的disable_timer的超时时间为当前时间加上drm_vblank_offdelay(默认为5000ms),其中disbale_timer在vblank初始化中创建,timer超时回调函数为vblank_disable_fn。

所以从上面流程分析可知,只要应用程序调用命令为DRM_IOCTL_MODE_ATOMIC的ioctl的时间不超过5000ms,则不会disable vblank,因为应用程序下一次发起DRM_IOCTL_MODE_ATOMIC ioctl时,kworker线程又会调用drm_vblank_get和drm_vblank_put,而drm_vblank_put会调用mod_timer,mod_timer等价于del_timer(timer); timer->expires = expires; add_timer(timer);,即先将timer从超时队列中删除,然后修改超时时间,最后再加入超时队列等待超时。
如果结束应用程序,从内核日志可以发现5000ms后,vblank被disable了,DC不再触发中断。

注:假设下一次发起DRM_IOCTL_MODE_ATOMIC的ioctl时,调用drm_vblank_get后(vblank 的refcount自增变为1,但是因为vblank的enabled还未设置为false,所以不会enable vblank),但是还未调用drm_vblank_put时,disable_timer超时并调用函数vblank_disable_fn,因为函数vblank_disable_fn会判断vblank的refcount,只有该值为0才会diable vblank,而此时为1所以不会disable vblank。同时drm_vblank_get和vblank_disable_fn都会有自旋锁dev->vbl_lock保护(spin_lock_irqsave),所以不会存在并发问题。

二、driver发送event方式对vlbank refcount的影响

driver有如下两种方式发送vblank event:

  1. driver回调函数xxx_crtc_atomic_flush将vblank event(函数参数类型为struct drm_crtc的成员state->event)保存下来。DC driver irq handler调用函数drm_crtc_send_vblank_event将保存的vblank event发送出去。上面的流程分析就是这种情况。
  2. driver回调函数xxx_crtc_atomic_flush先调用函数drm_crtc_vblank_get,然后调用函数drm_crtc_arm_vblank_event将vblank event插入链表dev->vblank_event_list,DC driver irq handler调用函数drm_crtc_handle_vblank,函数drm_crtc_handle_vblank最终对链表dev->vblank_event_list中的每个vblank event调用一次drm_vblank_put和send_vblank_event将vblank event发送出去。

因为第二种方式调用drm_crtc_handle_vblank会对每个vblank event调用一次drm_vblank_put,所以其他厂商的驱动在调用函数drm_crtc_arm_vblank_event前也会调用一次drm_crtc_vblank_get。这样也能保证在vblank event发送出去之前不会disable vblank。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Linux内核中启用DRM(Direct Rendering Manager),您需要进行以下步骤: 1. 确认您的硬件支持DRM:首先您需要确认您的硬件是否支持DRM。大多数显卡都支持DRM,但是一些较老的显卡可能不支持。您可以查看您的显卡型号和Linux内核版本是否支持DRM。通常,支持DRM的显卡需要至少Linux 2.6.29版本及以上的内核。 2. 确认内核配置:您需要确认内核配置中已经启用了DRM相关的选项。在内核源代码树中,可以使用以下命令来打开内核配置界面: ``` make menuconfig ``` 在内核配置界面中,您需要确认以下选项已经启用: ``` Device Drivers -> Graphics support -> Direct Rendering Manager (DRM) ``` 这个选项启用后,会自动使能许多DRM子系统和驱动程序。 3. 编译内核:完成内核配置后,您需要重新编译内核并安装新内核。具体操作和命令可能因发行版而有所不同,请参考您的Linux发行版的文档。 4. 加载DRM模块:当您启动新内核后,可以使用以下命令来加载DRM模块: ``` modprobe drm ``` 如果您的显卡使用的是DRM子系统中的某个具体驱动程序,您还需要加载该驱动程序的模块。例如,如果您的显卡使用的是Intel集成显卡,您需要加载i915模块: ``` modprobe i915 ``` 加载模块后,您可以使用DRM相关的工具和应用程序了。 以上是启用DRM的基本步骤,具体操作和命令可能因发行版而有所不同。建议您仔细阅读您的Linux发行版的文档,并参考相关的文档和教程来进行操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值