DRM驱动(七)之atomic_commit
atomic_commit,drm框架中逻辑比较复杂的一块,这里通过简短的语言和思维导图把刷图的过程呈现出来,并说明每个回调接口的作用
上节已经把应用的参数check了一遍,这次就可以把对应的参数配置到硬件里进行刷图操作了
int drm_atomic_commit(struct drm_atomic_state *state)
{
struct drm_mode_config *config = &state->dev->mode_config;
int ret;
ret = drm_atomic_check_only(state);
if (ret)
return ret;
DRM_DEBUG_ATOMIC("committing %p\n", state);
return config->funcs->atomic_commit(state->dev, state, false);
}
config->funcs->atomic_commit是drm驱动初始化时创建的,可以由soc厂商实现也可以使用drm框架中的drm_atomic_helper_commit,如rockchip的驱动
static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
.fb_create = rockchip_user_fb_create,
.output_poll_changed = rockchip_drm_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
drm_atomic_helper_commit
我们重点分析下drm_atomic_helper_commit的主要内容
- 初始化一个工作队列,应用如果使用noblock的方式刷图,就使用此工作队列,这样驱动可以直接返回给应用,让应用做其他事情
- 交换state,将要刷新的state赋给各组件的state,将上次刷新的state保存到state结构体中
- 根据是否采用noblock方式刷图来决定如何调用commit_tail
int drm_atomic_helper_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool nonblock)
{
... ...
INIT_WORK(&state->commit_work, commit_work);
... ...
ret = drm_atomic_helper_swap_state(state, true);
if (ret)
goto err;
... ...
if (nonblock)
queue_work(system_unbound_wq, &state->commit_work);
else
commit_tail(state);
return 0;
}
int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
bool stall)
{
for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) {
WARN_ON(connector->state != old_conn_state);
old_conn_state->state = state;
new_conn_state->state = NULL;
state->connectors[i].state = old_conn_state;
connector->state = new_conn_state;
}
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
WARN_ON(crtc->state != old_crtc_state);
old_crtc_state->state = state;
new_crtc_state->state = NULL;
state->crtcs[i].state = old_crtc_state;
crtc->state = new_crtc_state;
}
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
WARN_ON(plane->state != old_plane_state);
old_plane_state->state = state;
new_plane_state->state = NULL;
state->planes[i].state = old_plane_state;
plane->state = new_plane_state;
}
for_each_oldnew_private_obj_in_state(state, obj, old_obj_state, new_obj_state, i) {
WARN_ON(obj->state != old_obj_state);
old_obj_state->state = state;
new_obj_state->state = NULL;
state->private_objs[i].state = old_obj_state;
obj->state = new_obj_state;
}
return 0;
}
从代码可以看到如果noblock被置位,将启动commit_work,否则调用commit_tail
static void commit_work(struct work_struct *work)
{
struct drm_atomic_state *state = container_of(work,
struct drm_atomic_state,
commit_work);
commit_tail(state);
}
commit_work中调用的也是commit_tail.
commit_tail中主要进行一些fence等事件的判断,和drm_atomic_helper_commit_tail
fence的内容我们现在不作为重点,我们主要看刷图相关内容,刷图主要在drm_atomic_helper_commit_tail里面,如果soc厂商有实现atomic_commit_tail,将调用soc实现的接口
static void commit_tail(struct drm_atomic_state *old_state)
{
struct drm_device *dev = old_state->dev;
const struct drm_mode_config_helper_funcs *funcs;
funcs = dev->mode_config.helper_private;
drm_atomic_helper_wait_for_fences(dev, old_state, false);
drm_atomic_helper_wait_for_dependencies(old_state);
if (funcs && funcs->atomic_commit_tail)
funcs->atomic_commit_tail(old_state);
else
drm_atomic_helper_commit_tail(old_state);
drm_atomic_helper_commit_cleanup_done(old_state);
drm_atomic_state_put(old_state);
}
drm_atomic_helper_commit_tail
drm_atomic_helper_commit_tail调用的几个函数比较复杂,决定用几个思维导图介绍下。
drm_atomic_helper_commit_modeset_disables:
- 如果mode发生改变其实就是state->mode_changed state->active_changed state->connectors_changed,状态被置位(上节atomic_check中有聊到在哪里置位的)就会关掉crtc,和encoder;图中带颜色的均是soc厂商需要根据硬件行为实现的接口,比如关掉显示和tcon等等
- 在crtc_set_mode中,如果active被置位,并且上面mode_changed等状态被置位则调用crtc->helper_private->mode_set_nofb和encoder的modeset函数,来将对应mode设置到配置到硬件,同样这些回调需要soc厂商来实现。
总结一句话:如果只是mode发生了变化,就先关crtc和encoder,再配置mode到crtc到encoder;如果只是状态发送变化,则只关掉crtc和encoder
drm_atomic_helper_commit_planes
此函数非常重要,因为大多数的硬件均是在这里配置的。
plane->helper_private->atomic_update(plane, old_plane_state):根据plane_state中图像大小和位置等和fb的显存地址配置layer的信息到硬件
crtc->helper_private->atomic_flush(crtc, old_crtc_state):更新除了layer的其他相关的内容,比如trigger位
这两个回调主要用到之前保存到state里的信息,后面有机会分析一下别人实现好的驱动就很好理解了。
drm_atomic_helper_commit_modeset_enables:
- 将之前关闭的crtc和encoder打开;配置硬件打开显示和tcon,一并打开encoder
drm_atomic_helper_commit_hw_done:上报hw_done事件,通知硬件配置已经完成。
drm_atomic_helper_wait_for_vblanks(dev, old_state):等待vblank事件,硬件更新完成之后,会等到下一个vsync中断到来后才会生效,vblank事件会在vsync中断处理函数中上报;drm中默认等待50ms,如果显示硬件出现异常超过50ms未上报vblank事件,会打印警告和堆栈
W kernel: [CRTC:35:crtc-0] vblank wait timed out
W kernel: ------------[ cut here ]------------
W kernel: WARNING: CPU: 0 PID: 24879 at gpu/drm/drm_atomic_helper.c:1240 drm_atomic_helper_wait_for_vblanks.part.7+0x2ac/0x2b8
W kernel: CPU: 0 PID: 24879 Comm: kworker/u12:5 Tainted: P W O L 4.14.61+ #1
W kernel: Workqueue: events_unbound commit_work
W kernel: task: ffff8000c4904880 task.stack: ffff00000d6d8000
W kernel: PC is at drm_atomic_helper_wait_for_vblanks.part.7+0x2ac/0x2b8
W kernel: LR is at drm_atomic_helper_wait_for_vblanks.part.7+0x2ac/0x2b8
W kernel: [<ffff0000086fb094>] drm_atomic_helper_wait_for_vblanks.part.7+0x2ac/0x2b8
W kernel: [<ffff0000086fb140>] drm_atomic_helper_commit_tail+0x60/0x78
W kernel: [<ffff0000086fb1e4>] commit_tail+0x8c/0x90
W kernel: [<ffff0000086fb208>] commit_work+0x20/0x30
W kernel: [<ffff0000081017e4>] process_one_work+0x1dc/0x4a8
W kernel: [<ffff000008101b00>] worker_thread+0x50/0x478
W kernel: [<ffff000008108eb8>] kthread+0x138/0x140
W kernel: [<ffff00000808518c>] ret_from_fork+0x10/0x1c
vsync中断处理函数中也会上报filpdone事件,代表新的一帧已经在硬件生效,新的一帧图像已经出现在屏幕上。
至此刷新一帧图的大致流程已分析完毕,分析过程省略了很多细节,感兴趣可以对照源码看一遍。
drm把显示的框架实现的非常完善,一些显示通用的部分有drm框架处理,差异化的内容通过回调的方式交由各个soc厂商实现,避免了重复造轮子。
后面有时间会结合一个具体的驱动分析一下drm驱动初始化的过程,包括drm的一些回调函数的功能
更多推荐
所有评论(0)