之前的文章里说过,drm机制会将一次要更新的内容全部放到state中,然后在更新不同component时把xxx_state里面的值拿出来更新到硬件里。前段时间遇到了个问题,梳理了一下每个组件的state创建,使用,销毁。记录一下防止忘记。
再复习一下drm_atomic_state这个数据结构
struct drm_atomic_state {
struct drm_device *dev;
struct __drm_planes_state *planes;
struct __drm_crtcs_state *crtcs;
int num_connector;
struct __drm_connnectors_state *connectors;
int num_private_objs;
struct __drm_private_objs_state *private_objs;
... ...
};
可以看到里面主要包含4个比较重要的结构__drm_planes_state,__drm_crtcs_state,__drm_connnectors_state,__drm_private_objs_state;每个结构大体相同,创建方式也基本一致,就单拎出来crtc_state来分析。
拿之前的一个图,展示一下各个数据结构的关系
再来复习一下刷图过程,具体可以参看之前的文章《DRM驱动(五)之drm_atomic_state》:
drm_mode_setcrtc ->__drm_mode_set_config_internal->drm_atomic_helper_set_config(这里会创建drm_atomic_state)->__drm_atomic_helper_set_config
在__drm_atomic_helper_set_config调用drm_atomic_get_crtc_state创建crtc_state
int __drm_atomic_helper_set_config(struct drm_mode_set *set,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state;
struct drm_plane_state *primary_state;
struct drm_crtc *crtc = set->crtc;
int hdisplay, vdisplay;
int ret;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
primary_state = drm_atomic_get_plane_state(state, crtc->primary);
if (IS_ERR(primary_state))
return PTR_ERR(primary_state);
... ...
commit:
ret = update_output_state(state, set);
if (ret)
return ret;
return 0;
}
接着来看下drm_atomic_get_crtc_state
struct drm_crtc_state *
drm_atomic_get_crtc_state(struct drm_atomic_state *state,
struct drm_crtc *crtc)
{
int ret, index = drm_crtc_index(crtc);
struct drm_crtc_state *crtc_state;
crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
if (crtc_state)
return crtc_state;
crtc_state = crtc->funcs->atomic_duplicate_state(crtc);
if (!crtc_state)
return ERR_PTR(-ENOMEM);
state->crtcs[index].state = crtc_state;
state->crtcs[index].old_state = crtc->state;
state->crtcs[index].new_state = crtc_state;
state->crtcs[index].ptr = crtc;
crtc_state->state = state;
return crtc_state;
}
drm_atomic_get_crtc_state中主要做三件事:
- 判断state->crtcs[index].state是否已经存在,存在就直接返回。这里一般为空
- 调用crtc中实现的atomic_duplicate_state来创建crtc_state,后面举个具体例子分析
- 将创建的crtc_state存放到state->crtcs[index].state和state->crtcs[index].new_state,并将crtc->state(保留的上次刷图的crtc_state)传给old_state
接下来分析由vendor实现的atomic_duplicate_state
比如rockchip的实现,也非常简单
static const struct drm_crtc_funcs vop_crtc_funcs = {
... ...
.atomic_duplicate_state = vop_crtc_duplicate_state,
.atomic_destroy_state = vop_crtc_destroy_state,
... ...
};
static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct rockchip_crtc_state *rockchip_state;
rockchip_state = kzalloc(sizeof(*rockchip_state), GFP_KERNEL);
if (!rockchip_state)
return NULL;
__drm_atomic_helper_crtc_duplicate_state(crtc, &rockchip_state->base);
return &rockchip_state->base;
}
static void vop_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(state);
__drm_atomic_helper_crtc_destroy_state(&s->base);
kfree(s);
}
rockchip对drm_crtc_state进行了扩展,为了添加一些自己特有的属性,在另外的地方可以根据其中的base通过container_of找到扩展的结构。
可以看到vop_crtc_duplicate_state主要做了两个工作:
- 分配内存
- 将crtc->state copy到新创建的crtc_state然后将地址返回。所以叫duplicate
上面已经说过drm_atomic_get_crtc_state会将duplicate到的crtc_state赋值给state->crtcs[index].state和state->crtcs[index].new_state
plane_state和connector_state的创建方式相同,大家可以看下源码自行分析下。
然后进行刷图,drm_atomic_helper_commit中会调用drm_atomic_helper_swap_state也比较有意思(这里还是拿crtc_state举例)
int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
bool stall)
{
int i, ret;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
struct drm_crtc_commit *commit;
... ...
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;
}
... ...
return 0;
}
drm_atomic_helper_swap_state的主要目的遍历所有的crtc,将新创建的crtc_state赋值crtc->state;state->crtcs[i].state这里和duplicate state赋的是一样的值。
后面就会根据应用传下来的参数向crtc_state填充,驱动会根据crtc_state的参数配置硬件寄存器。
当所有的配置都完成之后,会调用drm_atomic_state_put
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);
}
static inline void drm_atomic_state_put(struct drm_atomic_state *state)
{
kref_put(&state->ref, __drm_atomic_state_free);
}
这里是个引用计数,但是因为state只被get一次,所以调用到这里的时候就会执行__drm_atomic_state_free
然后__drm_atomic_state_free->drm_atomic_state_clear->drm_atomic_state_default_clear
void drm_atomic_state_default_clear(struct drm_atomic_state *state)
{
struct drm_device *dev = state->dev;
struct drm_mode_config *config = &dev->mode_config;
int i;
DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state);
... ...
for (i = 0; i < config->num_crtc; i++) {
struct drm_crtc *crtc = state->crtcs[i].ptr;
if (!crtc)
continue;
crtc->funcs->atomic_destroy_state(crtc,
state->crtcs[i].state);
if (state->crtcs[i].commit) {
kfree(state->crtcs[i].commit->event);
state->crtcs[i].commit->event = NULL;
drm_crtc_commit_put(state->crtcs[i].commit);
}
state->crtcs[i].commit = NULL;
state->crtcs[i].ptr = NULL;
state->crtcs[i].state = NULL;
state->crtcs[i].old_state = NULL;
state->crtcs[i].new_state = NULL;
}
... ...
}
drm_atomic_state_default_clear的主要工作是:
1. 调用vendor实现的atomic_destroy_state,包括crtc,plane,connector,private_obj。
2. 将指针置空,防止出现野指针。
但是这里有个细节,crtc->funcs->atomic_destroy_state(crtc, state->crtcs[i].state); 这里释放的是 state->crtcs[i].state而不是state->crtcs[i].new_state;还记得state->crtcs[i].state是保存的什么时候的crtc_state吗?
对,没错,是上一次的crtc_state,而不是这一次的;也就是说,销毁是落后一帧的。
之前内容只是聊了大概的框架,这篇文章算是对之前内容的补充,把一些细节放大来阐述。最后用几张图总结xxx_state的生命周期
(PS:我一直以为写过了分析具体drm驱动driver实现的内容,翻了好久发现好像没写;等有空了补上,手动狗头)