目录
1.1 drm_atomic_helper_swap_state
2. drm_atomic_helper_commit_tail
2.1 drm_atomic_helper_commit_modeset_disables
2.2 drm_atomic_helper_commit_planes
2.3 drm_atomic_helper_commit_modeset_enables
2.4 drm_atomic_helper_commit_hw_done
2.5 drm_atomic_helper_wait_for_vblanks
前文分析了drm_atomic_check_only,本文分析一下config->funcs->atomic_commit(state->dev, state, false);
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);
}
static const struct drm_mode_config_funcs malidp_mode_config_funcs = {
.fb_create = drm_fb_cma_create,
.output_poll_changed = malidp_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
1. drm_atomic_helper_commit
int drm_atomic_helper_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool nonblock)
{
int ret;
if (state->async_update) {
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret)
return ret;
drm_atomic_helper_async_commit(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
return 0;
}
ret = drm_atomic_helper_setup_commit(state, nonblock);
if (ret)
return ret;
INIT_WORK(&state->commit_work, commit_work);
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret)
return ret;
if (!nonblock) {
ret = drm_atomic_helper_wait_for_fences(dev, state, true);
if (ret)
goto err;
}
/*
* This is the point of no return - everything below never fails except
* when the hw goes bonghits. Which means we can commit the new state on
* the software side now.
*/
ret = drm_atomic_helper_swap_state(state, true);
if (ret)
goto err;
/*
* Everything below can be run asynchronously without the need to grab
* any modeset locks at all under one condition: It must be guaranteed
* that the asynchronous work has either been cancelled (if the driver
* supports it, which at least requires that the framebuffers get
* cleaned up with drm_atomic_helper_cleanup_planes()) or completed
* before the new state gets committed on the software side with
* drm_atomic_helper_swap_state().
*
* This scheme allows new atomic state updates to be prepared and
* checked in parallel to the asynchronous completion of the previous
* update. Which is important since compositors need to figure out the
* composition of the next frame right after having submitted the
* current layout.
*
* NOTE: Commit work has multiple phases, first hardware commit, then
* cleanup. We want them to overlap, hence need system_unbound_wq to
* make sure work items don't artifically stall on each another.
*/
drm_atomic_state_get(state);
if (nonblock)
queue_work(system_unbound_wq, &state->commit_work);
else
commit_tail(state);
return 0;
err:
drm_atomic_helper_cleanup_planes(dev, state);
return ret;
}
我们重点分析下drm_atomic_helper_commit的主要内容:
(1)初始化一个工作队列,应用如果使用noblock的方式刷图,就使用此工作队列,这样驱动可以直接返回给应用,让应用做其他事情。
(2)交换state,将要刷新的state赋给各组件的state,将上次刷新的state保存到state结构体中。
(3)根据是否采用noblock方式刷图来决定如何调用commit_tail。
1.1 drm_atomic_helper_swap_state
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。
1.2 commit_tail
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);
}
commit_tail最终调用了drm_atomic_helper_commit_tail函数。
2. drm_atomic_helper_commit_tail
void drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state)
{
struct drm_device *dev = old_state->dev;
drm_atomic_helper_commit_modeset_disables(dev, old_state);
drm_atomic_helper_commit_planes(dev, old_state, 0);
drm_atomic_helper_commit_modeset_enables(dev, old_state);
drm_atomic_helper_commit_hw_done(old_state);
drm_atomic_helper_wait_for_vblanks(dev, old_state);
drm_atomic_helper_cleanup_planes(dev, old_state);
}
2.1 drm_atomic_helper_commit_modeset_disables
(1)如果mode发生改变其实就是state->mode_changed state->active_changed state->connectors_changed,状态被置位(上节atomic_check中有聊到在哪里置位的)就会关掉crtc,和encoder
(2)在crtc_set_mode中,如果active被置位,并且上面mode_changed等状态被置位则调用crtc->helper_private->mode_set_nofb和encoder的modeset函数,来将对应mode设置到配置到硬件,同样这些回调需要soc厂商来实现。
2.2 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位。
2.3 drm_atomic_helper_commit_modeset_enables
(1)将之前关闭的crtc和encoder打开;配置硬件打开显示和tcon,一并打开encoder。
2.4 drm_atomic_helper_commit_hw_done
报hw_done事件,通知硬件配置已经完成。
2.5 drm_atomic_helper_wait_for_vblanks
等待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事件,代表新的一帧已经在硬件生效,新的一帧图像已经出现在屏幕上。