Linux图形子系统之setCRTC流程

引言

drm通过DRM_IOCTL_MODE_SETCRTC实现最原始的模式设置和送显操作。

1 数据结构

setCrtc的整个流程是围绕的各种state结构,如下图:
请添加图片描述
注:本图及后述内容没特别说明都是以linux5.4.162的源码版本为准。

2 关键流程

2.1 drm_mode_setcrtc

drm_mode_setcrtc是DRM_IOCTL_MODE_SETCRTC的处理函数。其源码如下:

int drm_mode_setcrtc(struct drm_device *dev, void *data,
		     struct drm_file *file_priv)
{
    # 略略略... ...
    mutex_lock(&crtc->dev->mode_config.mutex);
    //构建struct drm_mode_set set
    # 略略略... ...
	set.crtc = crtc;
	set.x = crtc_req->x;
	set.y = crtc_req->y;
	set.mode = mode;
	set.connectors = connector_set;
	set.num_connectors = crtc_req->count_connectors;
	set.fb = fb;
	//执行set_config操作
	if (drm_drv_uses_atomic_modeset(dev))
		ret = crtc->funcs->set_config(&set, &ctx);
	else
		ret = __drm_mode_set_config_internal(&set, &ctx);
out:
    # 略略略... ...
    mutex_unlock(&crtc->dev->mode_config.mutex);
    # 略略略... ...
}

整个函数流程概况:

  • 首先,通过参数构建一个drm_mode_set对象;
  • 执行set_config操作:具体来说,当dev支持原子设置就直接调用drm_crtc_funcs的set_config;否则进入__drm_mode_set_config_internal函数,该函数在调用drm_crtc_funcs的set_config回调前,会让每个crtc的primary plane的old_fb字段备份fb,在调用结束后,再恢复其fb,同时将当前crtc的primary plane的fb指向DRM_IOCTL_MODE_SETCRTC指定的fb。
  • 最后就是执行资源清理工作。

2.2 drm_atomic_helper_set_config

drm_atomic_helper_set_config函数是drm_crtc_funcs的set_config回调默认实现函数。关键源码如下:

int drm_atomic_helper_set_config(struct drm_mode_set *set,
				 struct drm_modeset_acquire_ctx *ctx)
{
	struct drm_atomic_state *state;
	struct drm_crtc *crtc = set->crtc;
	int ret = 0;

	state = drm_atomic_state_alloc(crtc->dev);
	if (!state)
		return -ENOMEM;

	state->acquire_ctx = ctx;
	ret = __drm_atomic_helper_set_config(set, state);
	if (ret != 0)
		goto fail;

	ret = handle_conflicting_encoders(state, true);
	if (ret)
		goto fail;

	ret = drm_atomic_commit(state);

fail:
	drm_atomic_state_put(state);
	return ret;
}
EXPORT_SYMBOL(drm_atomic_helper_set_config);

整个函数流程概况:

  • 首先,通过drm_atomic_state_alloc分配drm_atomic_state。其内部实现为:若驱动的drm_mode_config_funcs函数集合实现了atomic_state_alloc回调,则通过其分配;否则分配内存后,按默认函数drm_atomic_state_init对它初始化。
  • 然后, 通过__drm_atomic_helper_set_config函数往drm_atomic_state里面添加drm_crtc_state、drm_plane_state和drm_connector_state状态。其中通过通过各个组件的drm_{crtc/plane/connector}_funcs的atomic_duplicate_state创建状态机。
  • 然后,通过handle_conflicting_encoders为connector选择一个encoder。其中会调用drm_connector_helper_funcs的atomic_best_encoder/best_encoder(按顺序选第一个非空的函数)回调,如果这些回调都不存在,则调用pick_single_encoder_for_connector选择connector的第一个encoder。
  • 最后,通过drm_atomic_commit提交drm_atomic_state。在正式提交前,会检查是否支持原子特性,最终会调用drm_mode_config_funcs的atomic_check(前提是实现了该回调);在正式提交中,执行drm_mode_config_funcs的atomic_commit回调,其第三个参数nonblock为false(也就是采用阻塞方式调用)。

2.3 drm_atomic_helper_commit

drm_atomic_helper_commit是drm_mode_config_funcs的atomic_commit回调的默认实现函数。关键源码如下:

int drm_atomic_helper_commit(struct drm_device *dev,
			     struct drm_atomic_state *state,
			     bool nonblock) //noblock = false
{
    # 略去代码块if (state->async_update)... ...

	ret = drm_atomic_helper_setup_commit(state, nonblock);
	if (ret)
		return ret;
    
    # 略去INIT_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;
	}

	ret = drm_atomic_helper_swap_state(state, true);
	if (ret)
		goto err;

	drm_atomic_state_get(state);
	if (nonblock)
		# 略去queue_work代码... ...
	else
		commit_tail(state);

	return 0;

err:
	drm_atomic_helper_cleanup_planes(dev, state);
	return ret;
}

整个函数流程概况:

  • 首先,调用drm_atomic_helper_setup_commit为state构建drm_crtc_commit对象;
  • 然后,调用drm_atomic_helper_prepare_planes准备plane资源,在该函数中,根据条件先调用drm_connector_helper_funcs的prepare_writeback_job回调,再调用drm_plane_helper_funcs的prepare_fb回调;
  • 然后, 调用drm_atomic_helper_wait_for_fences去等待new_plane_state->fence,该fence是在drm_plane_helper_funcs的prepare_fb回调中取出的fence(通常代表渲染动作是否完成);
  • 然后,调用drm_atomic_helper_swap_state函数将当前的state设置到各自的组件对象的state字段;
  • 最后,调用commit_tail做最后正式的提交动作,这也是真正与硬件提交相关的代码;

2.4 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);

    # 略略略... ...

	drm_atomic_helper_commit_cleanup_done(old_state);

	drm_atomic_state_put(old_state);
}

整个函数流程概况:

  • 首先,调用drm_atomic_helper_wait_for_fences函数,该操作在上个函数drm_atomic_helper_commit已经调用过,所以内部直接返回。之所以要重复调用是考虑到非阻塞的调用的情况;
  • 然后,调用drm_atomic_helper_wait_for_dependencies等待各个组件(也就是crtc、plane、connector)的drm_crtc_commit的hw_done和flip_done完成动作;
  • 然后,调用drm_mode_config_helper_funcs的atomic_commit_tail回调,当然若驱动没有实现,则进入默认函数drm_atomic_helper_commit_tail;
  • 然后,调用drm_atomic_helper_commit_cleanup_done函数做清理动作。会触发drm_crtc_commit的cleanup_done动作;
  • 最后,释放state的引用计数。因为在上个函数调用前 先施加了一次引用计数。

2.4 drm_atomic_helper_commit_tail

drm_atomic_helper_commit_tail是drm_mode_config_helper_funcs的atomic_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_fake_vblank(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);
}

整个函数流程概况:

  • 首先,调用drm_atomic_helper_commit_modeset_disables对之前老的state对应的组件做清理和复位;
  • 然后,调用drm_atomic_helper_commit_planes对显示做正式提交;
  • 然后,调用drm_atomic_helper_commit_modeset_enables使能硬件,使之显示提交的fb;
  • 然后,调用drm_atomic_helper_fake_vblank。在该函数中,若不支持vblank事件,就会在此刻通过drm_crtc_send_vblank_event发送vblank,这里面会触发drm_crtc_commit的flip_done动作;
  • 然后,调用drm_atomic_helper_commit_hw_done函数,触发drm_crtc_commit的hw_done动作;
  • 然后,调用drm_atomic_helper_wait_for_vblanks去等待硬件的vblank事件;
  • 最后,调用drm_atomic_helper_cleanup_planes做清理工作,调用驱动实现drm_plane_helper_funcs的cleanup_fb回调。

2.4.1 drm_atomic_helper_commit_modeset_disables

drm_atomic_helper_commit_modeset_disables函数用于对上一次的状态设置做disable和复位,同时对当前的state使能。源码如下:

void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev,
					       struct drm_atomic_state *old_state)
{
	disable_outputs(dev, old_state);

	drm_atomic_helper_update_legacy_modeset_state(dev, old_state);

	crtc_set_mode(dev, old_state);
}

整个函数流程概况:

  • 首先,调用disable_outputs去关闭上一次的state设置。先调用drm_encoder_helper_funcs的atomic_disable/prepare/disable/dpms(按顺序选第一个非空的函数)回调;再调用drm_crtc_helper_funcs的prepare/atomic_disable/disable/dpms(按顺序选第一个非空的函数)回调;
  • 然后,调用drm_atomic_helper_update_legacy_modeset_state函数,设置connector的encoder等操作;
  • 最后,调用crtc_set_mode做模式设置。先调用drm_crtc_helper_funcs的mode_set_nofb回调(前提是驱动实现了该回调);再调用drm_encoder_helper_funcs的atomic_mode_set/mode_set(按顺序选第一个非空的函数)回调;

2.4.2 drm_atomic_helper_commit_planes

该函数是硬件提交的核心函数,其源码如下:

void drm_atomic_helper_commit_planes(struct drm_device *dev,
				     struct drm_atomic_state *old_state,
				     uint32_t flags)
{
    # 略略略... ...

	for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
		const struct drm_crtc_helper_funcs *funcs;

		funcs = crtc->helper_private;

		if (!funcs || !funcs->atomic_begin)
			continue;

		if (active_only && !new_crtc_state->active)
			continue;

		funcs->atomic_begin(crtc, old_crtc_state);
	}

	for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) {
		const struct drm_plane_helper_funcs *funcs;
		bool disabling;

		funcs = plane->helper_private;

		if (!funcs)
			continue;

		disabling = drm_atomic_plane_disabling(old_plane_state,
						       new_plane_state);

		# 略去代码块if (active_only) ... ...

		if (disabling && funcs->atomic_disable) {
            # 略略略... ...
			funcs->atomic_disable(plane, old_plane_state);
		} else if (new_plane_state->crtc || disabling) {
			funcs->atomic_update(plane, old_plane_state);
		}
	}

	for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
		const struct drm_crtc_helper_funcs *funcs;

		funcs = crtc->helper_private;

		if (!funcs || !funcs->atomic_flush)
			continue;

		if (active_only && !new_crtc_state->active)
			continue;

		funcs->atomic_flush(crtc, old_crtc_state);
	}
}

整个函数流程概况:

  • 首先,对每个相关的drm_crtc_state,调用drm_crtc_helper_funcs的atomic_begin回调(前提是驱动实现了该回调);
  • 然后,对每个相关的drm_plane_state,调用drm_plane_helper_funcs的atomic_disable,或者atomic_update;
  • 最后,对每个相关的drm_crtc_state,调用drm_crtc_helper_funcs的atomic_flush回调。

2.4.3 drm_atomic_helper_commit_modeset_enables

该函数做硬件的真正使能动作。其核心源码如下:

void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
					      struct drm_atomic_state *old_state)
{
    # 略略略... ...

	for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
		const struct drm_crtc_helper_funcs *funcs;

		/* Need to filter out CRTCs where only planes change. */
		if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
			continue;

		if (!new_crtc_state->active)
			continue;

		funcs = crtc->helper_private;

		if (new_crtc_state->enable) {
			DRM_DEBUG_ATOMIC("enabling [CRTC:%d:%s]\n",
					 crtc->base.id, crtc->name);
			if (funcs->atomic_enable)
				funcs->atomic_enable(crtc, old_crtc_state);
			else if (funcs->commit)
				funcs->commit(crtc);
		}
	}

	for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
		const struct drm_encoder_helper_funcs *funcs;
		struct drm_encoder *encoder;

		if (!new_conn_state->best_encoder)
			continue;

		if (!new_conn_state->crtc->state->active ||
		    !drm_atomic_crtc_needs_modeset(new_conn_state->crtc->state))
			continue;

		# 略略略... ...

		if (funcs) {
			if (funcs->atomic_enable)
				funcs->atomic_enable(encoder, old_state);
			else if (funcs->enable)
				funcs->enable(encoder);
			else if (funcs->commit)
				funcs->commit(encoder);
		}

		# 略略略... ...
	}

	# 略略略... ...
}

整个函数流程概况:

  • 首先,对每个相关的drm_crtc_state,调用drm_crtc_helper_funcs的atomic_enable/commit(按顺序选第一个非空的函数)回调;
  • 然后,对每个相关connector的drm_encoder,调用drm_encoder_helper_funcs的atomic_enable/enable/commit(按顺序选第一个非空的函数)回调;

3 状态变化

以drm_crtc_state为例(其他的类似),整个过程的状态变化如下:
请添加图片描述

  • 首先,在drm_atomic_helper_swap_state调用后,crtc obj指向了新的crtc_state 1;__crtc_state 1的state字段指向了之前的crtc_state 0;同时crtc_state 0的state指向了state 1、而crtc_state 1的state字段不再指向state 1;
  • 最后,在drm_atomic_helper_commit_hw_done调用后,crtc_state 0不再指向commit 0,而是指向了新的commit 1
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值