drm_atomic_helper_commit
前面讲完drm_atomic_commit函数先调用drm_atomic_check_only检查完参数的合法性;最后调用config->funcs->atomic_commit,此函数是drm驱动初始化drm_mode_config时创建的。可以由SOC厂商实现,也可以使用drm-core中的helper函数:drm_atomic_helper_commit。
为了方便我们更好的理解atomic_commit函数,先得搞清楚drm_atomic_helper_commit这个函数的流程。
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_prepare_planes(dev, state);
......
if (!nonblock) {
ret = drm_atomic_helper_wait_for_fences(dev, state, true);
}
ret = drm_atomic_helper_swap_state(state, true);
......
if (nonblock)
queue_work(system_unbound_wq, &state->commit_work);
else
commit_tail(state);
return 0;
}
1)初始化state->commit_work这个工作队列;
2)交换state,把刷新的state赋值给各个组件的state,并将上一次刷新的state保存;
3)根据nonblock标志决定如何调用commit_tail;如果nonblock被置位,则启动commit_work(commit_work中也是调用commit_tail函数);否则直接调用commit_tail函数;
commit_tail函数的流程如下:
static void commit_tail(struct drm_atomic_state *old_state)
{
......
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);
......
}
commit_tail函数主要是进行一些fence等事件的判断(fence的内容我们现在先不关注,后面会专门去拆解这一部分),然后调用:drm_atomic_helper_commit_tail,这个函数才是刷图需要关注的重点。
函数重要的调用流程已经用红色序号标出,drm_atomic_helper_commit_modeset_disables函数流程比较复杂,下图有一个思维导图。
1)如果mode发生改变(其实就是state->mode_changed,state->active_changed,state->connectors_changed状态被置位),就会关掉crtc和encoder;SOC厂商需要根据硬件行为实现对应的hook函数,比如关掉显示和TCON等;
2)crtc_set_mode函数,如果active被置位,则调用crtc->helper_private->mode_set_nofb和encoder->helper_private->atomic_mode_set函数,来将对应的mode配置到硬件,这些回调函数都需要SOC厂商来实现。
一句话总结:如果只是mode发生了变化,就先关crtc和encoder,再配置mode到crtc和encoder;如果只是状态发生变化,则只关掉crtc和encoder。
接下来还有一个重磅函数:drm_atomic_helper_commit_planes,大部分的硬件行为都是在这个函数配置的。其中,plane->helper_private->atomic_update函数根据plane_state中图像位置,大小和fb的显存地址信息等,作为layer的参数更新到硬件;crtc->helper_private->atomic_flush(crtc, old_crtc_state)更新除了layer的其他相关内容。这2个回调函数都会用到之前保存在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:等待vblank事件,硬件更新完成之后,会等到下一个vsync中断到来后才会生效,vblank事件会在vsync中断处理函数中上报;drm中默认等待50ms。由于接触的都是dsi_vdo的屏幕,vsync中断其实就是DSI模块中FRAME_DONE_INT_FLAG这个中断。
到这里,drm_atomic_helper_commit函数流程就结束了,下面来看mtk的实现。
mtk_atomic_commit
static int mtk_atomic_commit(struct drm_device *drm,
struct drm_atomic_state *state, bool async)
{
ret = drm_atomic_helper_prepare_planes(drm, state);
ret = drm_atomic_helper_swap_state(state, 0);
if (async)
mtk_atomic_schedule(private, state);
else
mtk_atomic_complete(private, state);
}
mtk的实现流程跟drm_atomic_helper_commit基本一致。async跟nonblock是同一个参数,最后都会调用到:mtk_atomic_complete。
static void mtk_atomic_complete(struct mtk_drm_private *private,
struct drm_atomic_state *state)
{
mtk_atomic_wait_for_fences(state);
drm_atomic_helper_commit_modeset_disables(drm, state);
drm_atomic_helper_commit_modeset_enables(drm, state);
mtk_set_first_config(drm, state);
mtk_drm_enable_trig(drm, state);
mtk_atomic_disp_rsz_roi(drm, state);
mtk_atomic_calculate_plane_enabled_number(drm, state);
mtk_atomic_check_plane_sec_state(drm, state);
mtk_atomic_mml(drm, state);
if (!mtk_atomic_skip_plane_update(private, state))
drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY);
if (!mtk_drm_helper_get_opt(private->helper_opt, MTK_DRM_OPT_COMMIT_NO_WAIT_VBLANK))
drm_atomic_helper_wait_for_vblanks(drm, state);
}
mtk对比drm_atomic_helper_commit函数,新增了不少客制化的东西,由于缺乏相应的文档和理解,目前对这一部分存疑。估计后续要搞清楚mtk实现的那一套disp_ddp的东西,这里就先过吧。
mtk_drm_enable_trig:更新trigger位???
mtk_atomic_disp_rsz_roi:???
mtk_atomic_mml:???
前面已经提到了,drm_atomic_helper_commit_planes会调用:plane->helper_private->atomic_update去更新layer信息到硬件。
最后会调用:crtc->helper_private->atomic_flush做善后的处理,mtk对此的实现也就是:mtk_drm_crtc_atomic_flush(这个函数完全看不懂???)。
写到这里,atomic_commit流程基本是写完了。给人的感觉是,从分析到mtk实现的drm_mode_config->func->atomic_config开始,代码中全是充斥着ddp和cmdq相关的内容,这一块如果不了解disp中相关的IP,这一块是完全看不懂。我们是终端设备的工程师,芯片厂mtk对这一块的资料是少之又少,这部分等后面有机会再补坑了。
后续的计划是:
1)vblank的流程;
2)fence的处理;
3)研究一下mtk_ddp的东西;
4)研究一下mtk DSI的东西;
5)还可以写一点mtk lcm的移植流程;