上节已经把应用的参数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的主要内容

  1. 初始化一个工作队列,应用如果使用noblock的方式刷图,就使用此工作队列,这样驱动可以直接返回给应用,让应用做其他事情
  2. 交换state,将要刷新的state赋给各组件的state,将上次刷新的state保存到state结构体中
  3. 根据是否采用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

  1. 如果mode发生改变其实就是state->mode_changed state->active_changed state->connectors_changed,状态被置位(上节atomic_check中有聊到在哪里置位的)就会关掉crtc,和encoder;图中带颜色的均是soc厂商需要根据硬件行为实现的接口,比如关掉显示和tcon等等
  2. 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:

  1. 将之前关闭的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的一些回调函数的功能

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐