DRM系列十一:Drm之config->funcs->atomic_commit
本系列文章基于linux 5.15
上一篇介绍了atomic commit之前的check工作,接着就会调用drm_mode_config的config->funcs->atomic_commit回调,这一回调是通过厂商自己实现,以rockchip为例,会直接调用drm_atomic_helper_commit函数。
static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
.fb_create = rockchip_fb_create,
.output_poll_changed = drm_fb_helper_output_poll_changed,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
一、drm_atomic_helper_commit
用于提交原子模式设置(Atomic Mode Setting)状态的核心函数。它负责将经过验证的原子状态对象应用到硬件中,完成显示配置的更新
int drm_atomic_helper_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool nonblock)
{
int ret;
/*如果 state->async_update 为 true,表示这是一个异步更新,异步更新后续补充*/
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;
}
/*为原子更新操作创建和初始化提交结构体(drm_crtc_commit),
并确保在非阻塞模式下不会因为之前的提交操作未完成而导致新的提交操作被阻塞。*/
ret = drm_atomic_helper_setup_commit(state, nonblock);
if (ret)
return ret;
/*初始化工作队列*/
INIT_WORK(&state->commit_work, commit_work);
/*用于在原子更新过程中准备平面(plane)的状态*/
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret)
return ret;
/*如果 nonblock 为 false(同步模式),调用 drm_atomic_helper_wait_for_fences 等待所有fence完成。fence机制之后再介绍*/
if (!nonblock) {
ret = drm_atomic_helper_wait_for_fences(dev, state, true);
if (ret)
goto err;
}
/*将新的状态(new_state)替换到当前状态(old_state),确保后续操作可以基于新的状态进行。*/
ret = drm_atomic_helper_swap_state(state, true);
if (ret)
goto err;
/*增加原子状态的引用计数,确保状态在提交过程中不会被释放*/
drm_atomic_state_get(state);
/*a.如果 nonblock 为 true(异步模式),将 state->commit_work 加入 system_unbound_wq 工作队列,异步执行提交。异步模式以后讲解。
b.如果 nonblock 为 false(同步模式),直接调用 commit_tail 执行提交。*/
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;
}
1.drm_atomic_helper_setup_commit
主要作用是为原子更新操作创建和初始化提交结构体(drm_crtc_commit),并确保在非阻塞模式下不会因为之前的提交操作未完成而导致新的提交操作被阻塞。
int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
bool nonblock)
{
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
struct drm_connector *conn;
struct drm_connector_state *old_conn_state, *new_conn_state;
struct drm_plane *plane;
struct drm_plane_state *old_plane_state, *new_plane_state;
struct drm_crtc_commit *commit;
const struct drm_mode_config_helper_funcs *funcs;
int i, ret;
funcs = state->dev->mode_config.helper_private;
/*遍历crtc,一方面为每个crtc初始化 drm_crtc_commit 结构体,另一方面new_crtc_state 没有事件,为其分配个事件,这两种情况都增加提交结构体的引用计数*/
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
commit = kzalloc(sizeof(*commit), GFP_KERNEL);
if (!commit)
return -ENOMEM;
/*初始化一个 drm_crtc_commit 结构体*/
init_commit(commit, crtc);
new_crtc_state->commit = commit;
/*目的是确保提交操作不会因为队列中的未完成操作而过度阻塞,从而提高系统的响应性和稳定性*/
ret = stall_checks(crtc, nonblock);
if (ret)
return ret;
/*
如果旧状态和新状态的 CRTC 都是不活动的(active 为 false),则直接完成 flip_done 信号,并跳过当前 CRTC。
这是因为在这种情况下,不需要进行任何实际的显示更新。
*/
if (!old_crtc_state->active && !new_crtc_state->active) {
complete_all(&commit->flip_done);
continue;
}
/* 如果当前原子更新是遗留的光标更新(legacy_cursor_update 为 true),则直接完成 flip_done 信号,并跳过当前 CRTC。
光标更新通常不需要同步,因此可以直接完成。 */
if (state->legacy_cursor_update) {
complete_all(&commit->flip_done);
continue;
}
/*如果 new_crtc_state 没有事件,则分配一个事件结构体并初始化它,设置事件的完成信号为 commit->flip_done,
设置事件的释放函数为 release_crtc_commit,增加提交结构体的引用计数。*/
if (!new_crtc_state->event) {
commit->event = kzalloc(sizeof(*commit->event),
GFP_KERNEL);
if (!commit->event)
return -ENOMEM;
new_crtc_state->event = commit->event;
}
new_crtc_state->event->base.completion = &commit->flip_done;
new_crtc_state->event->base.completion_release = release_crtc_commit;
drm_crtc_commit_get(commit);
/*设置提交结构体的 abort_completion 标志为 true,表示在必要时可以中止提交*/
commit->abort_completion = true;
/*将提交结构体赋值给 state->crtcs[i].commit。
增加提交结构体的引用计数。*/
state->crtcs[i].commit = commit;
drm_crtc_commit_get(commit);
}
for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) {
/*如果是非阻塞模式且旧状态的提交操作未完成,则返回 -EBUSY*/
if (nonblock && old_conn_state->commit &&
!try_wait_for_completion(&old_conn_state->commit->flip_done)) {
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] busy with a previous commit\n",
conn->base.id, conn->name);
return -EBUSY;
}
/* 确保在原子更新操作中,即使没有实际的 CRTC,也能有一个有效的提交结构体用于后续的处理。*/
commit = crtc_or_fake_commit(state, new_conn_state->crtc ?: old_conn_state->crtc);
if (!commit)
return -ENOMEM;
new_conn_state->commit = drm_crtc_commit_get(commit);
}
/*和connector相似,不做解释*/
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
if (nonblock && old_plane_state->commit &&
!try_wait_for_completion(&old_plane_state->commit->flip_done)) {
DRM_DEBUG_ATOMIC("[PLANE:%d:%s] busy with a previous commit\n",
plane->base.id, plane->name);
return -EBUSY;
}
/* Always track planes explicitly for async pageflip support. */
commit = crtc_or_fake_commit(state, new_plane_state->crtc ?: old_plane_state->crtc);
if (!commit)
return -ENOMEM;
new_plane_state->commit = drm_crtc_commit_get(commit);
}
/*调用drm_mode_config_helper_funcs的funcs->atomic_commit_setup回调*/
if (funcs && funcs->atomic_commit_setup)
return funcs->atomic_commit_setup(state);
return 0;
}
1.1init_commit
用于初始化一个 drm_crtc_commit 结构体。
static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc *crtc)
{
/*
这三行代码调用了 init_completion 函数,
分别初始化了 commit 结构体中的三个 completion 对象:
flip_done、hw_done 和 cleanup_done。
a.completion 是 Linux 内核中用于同步操作的机制,表示某个操作完成的信号。
b.init_completion 用于初始化 completion 对象,将其状态设置为未完成(未触发)。
c.这些 completion 对象的作用:
flip_done:用于表示页面翻转操作完成。
hw_done:用于表示硬件操作完成。
cleanup_done:用于表示清理操作完成。
*/
init_completion(&commit->flip_done);
init_completion(&commit->hw_done);
init_completion(&commit->cleanup_done);
INIT_LIST_HEAD(&commit->commit_entry);
/*kref_init 是 Linux 内核中用于初始化引用计数的函数。
commit->ref 是一个引用计数器,用于跟踪 commit 结构体的引用次数。
初始化引用计数为 1,表示该结构体已经被引用了一次。*/
kref_init(&commit->ref);
commit->crtc = crtc;
}
1.2stall_checks
目的是确保提交操作不会因为队列中的未完成操作而过度阻塞,从而提高系统的响应性和稳定性。
static int stall_checks(struct drm_crtc *crtc, bool nonblock)
{
struct drm_crtc_commit *commit, *stall_commit = NULL;
bool completed = true;
int i;
long ret = 0;
spin_lock(&crtc->commit_lock);
i = 0;
list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
/*
try_wait_for_completion(&commit->flip_done):尝试等待第一个提交操作的 flip_done 信号完成。
a.如果 flip_done 已经完成,返回 true;否则返回 false。
b.如果 nonblock 为 true 且 flip_done 未完成,
说明当前操作不能在未完成的提交操作之前执行,因此释放锁并返回 -EBUSY。*/
if (i == 0) {
completed = try_wait_for_completion(&commit->flip_done);
/*
* Userspace is not allowed to get ahead of the previous
* commit with nonblocking ones.
*/
if (!completed && nonblock) {
spin_unlock(&crtc->commit_lock);
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] busy with a previous commit\n",
crtc->base.id, crtc->name);
return -EBUSY;
}
} else if (i == 1) {
/*如果当前遍历到的是第二个提交操作(i == 1),则调用 drm_crtc_commit_get 增加该提交操作的引用计数,
并将其存储到 stall_commit 中。然后退出循环。*/
stall_commit = drm_crtc_commit_get(commit);
break;
}
i++;
}
spin_unlock(&crtc->commit_lock);
/*如果没有找到第二个提交操作(stall_commit 为空),说明队列中没有需要等待的提交操作,直接返回 0*/
if (!stall_commit)
return 0;
/* 等待第二个提交操作的清理完成:
调用 wait_for_completion_interruptible_timeout 等待第二个提交操作的 cleanup_done 信号完成,超时时间为 10*HZ(HZ 是内核的时钟频率,通常为1000)。
如果在超时时间内完成,返回值为剩余时间;如果超时,返回值为 0。
*/
ret = wait_for_completion_interruptible_timeout(&stall_commit->cleanup_done,
10*HZ);
if (ret == 0)
DRM_ERROR("[CRTC:%d:%s] cleanup_done timed out\n",
crtc->base.id, crtc->name);
drm_crtc_commit_put(stall_commit);
return ret < 0 ? ret : 0;
}
1.3crtc_or_fake_commit
作用是确保在原子更新操作中,即使没有实际的 CRTC,也能有一个有效的提交结构体用于后续的处理。这对于一些特殊情况(例如光标更新或平面更新)非常有用,这些操作可能不需要实际的 CRTC,但仍然需要一个提交结构体来管理状态。
static struct drm_crtc_commit *crtc_or_fake_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
{
/*如果指定了有效的 CRTC,返回与该 CRTC 相关联的提交结构体。*/
if (crtc) {
struct drm_crtc_state *new_crtc_state;
new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
return new_crtc_state->commit;
}
/*如果没有指定 CRTC(即 CRTC 为 NULL),则创建一个虚拟的提交结构体,并初始化它,最终返回虚拟提交结构体*/
if (!state->fake_commit) {
state->fake_commit = kzalloc(sizeof(*state->fake_commit), GFP_KERNEL);
if (!state->fake_commit)
return NULL;
init_commit(state->fake_commit, NULL);
}
return state->fake_commit;
}
2.drm_atomic_helper_prepare_planes
用于在原子更新过程中准备平面(plane)的状态。它的主要作用是确保所有平面的帧缓冲区(framebuffer)都准备好,以便后续的显示操作能够顺利进行。
int drm_atomic_helper_prepare_planes(struct drm_device *dev,
struct drm_atomic_state *state)
{
struct drm_connector *connector;
struct drm_connector_state *new_conn_state;
struct drm_plane *plane;
struct drm_plane_state *new_plane_state;
int ret, i, j;
for_each_new_connector_in_state(state, connector, new_conn_state, i) {
if (!new_conn_state->writeback_job)
continue;
ret = drm_writeback_prepare_job(new_conn_state->writeback_job);
if (ret < 0)
return ret;
}
/*遍历所有新的plane状态,如果有funcs->prepare_fb回调,调用drm_plane_helper_funcs的 funcs->prepare_fb回调 函数准备帧缓冲区;如果没有funcs->prepare_fb回调且支持GEM,则调用drm_gem_plane_helper_prepare_fb准备帧缓冲区*/
for_each_new_plane_in_state(state, plane, new_plane_state, i) {
const struct drm_plane_helper_funcs *funcs;
funcs = plane->helper_private;
if (funcs->prepare_fb) {
ret = funcs->prepare_fb(plane, new_plane_state);
if (ret)
goto fail;
} else {
WARN_ON_ONCE(funcs->cleanup_fb);
if (!drm_core_check_feature(dev, DRIVER_GEM))
continue;
ret = drm_gem_plane_helper_prepare_fb(plane, new_plane_state);
if (ret)
goto fail;
}
}
return 0;
fail:
for_each_new_plane_in_state(state, plane, new_plane_state, j) {
const struct drm_plane_helper_funcs *funcs;
if (j >= i)
continue;
funcs = plane->helper_private;
if (funcs->cleanup_fb)
funcs->cleanup_fb(plane, new_plane_state);
}
return ret;
}
2.1drm_writeback_prepare_job
调用drm_connector_helper_funcs的funcs->prepare_writeback_job回调,进行准备工作。
int drm_writeback_prepare_job(struct drm_writeback_job *job)
{
struct drm_writeback_connector *connector = job->connector;
const struct drm_connector_helper_funcs *funcs =
connector->base.helper_private;
int ret;
if (funcs->prepare_writeback_job) {
ret = funcs->prepare_writeback_job(connector, job);
if (ret < 0)
return ret;
}
job->prepared = true;
return 0;
}
3.drm_atomic_helper_swap_state
a.阻塞等待硬件操作完成(可选):如果 stall 参数为 true,则等待所有相关的硬件操作完成。
b.交换状态:将新的状态(new_state)替换到当前状态(old_state),确保后续操作可以基于新的状态进行。
c.更新提交队列:将新的提交结构体(drm_crtc_commit)添加到 CRTC 的提交队列中。
int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
bool stall)
{
int i, ret;
struct drm_connector *connector;
struct drm_connector_state *old_conn_state, *new_conn_state;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
struct drm_plane *plane;
struct drm_plane_state *old_plane_state, *new_plane_state;
struct drm_crtc_commit *commit;
struct drm_private_obj *obj;
struct drm_private_state *old_obj_state, *new_obj_state;
/*在 stall 参数为 true 时,函数会遍历所有旧的 CRTC、连接器和状态,等待它们的硬件操作(hw_done)完成*/
if (stall) {
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
commit = old_crtc_state->commit;
if (!commit)
continue;
ret = wait_for_completion_interruptible(&commit->hw_done);
if (ret)
return ret;
}
for_each_old_connector_in_state(state, connector, old_conn_state, i) {
commit = old_conn_state->commit;
if (!commit)
continue;
ret = wait_for_completion_interruptible(&commit->hw_done);
if (ret)
return ret;
}
for_each_old_plane_in_state(state, plane, old_plane_state, i) {
commit = old_plane_state->commit;
if (!commit)
continue;
ret = wait_for_completion_interruptible(&commit->hw_done);
if (ret)
return ret;
}
}
/*遍历所有connector、crtc、plane和私有对象的状态,将新的状态替换到当前状态中。*/
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;
/*如果新状态有提交结构体(commit),则将提交结构体添加到 CRTC 的提交队列中,并清除事件指针。*/
if (new_crtc_state->commit) {
spin_lock(&crtc->commit_lock);
list_add(&new_crtc_state->commit->commit_entry,
&crtc->commit_list);
spin_unlock(&crtc->commit_lock);
new_crtc_state->commit->event = NULL;
}
}
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;
}
4.commit_tail
commit_tail实现比较复杂,可以参考下一篇文章。