DRM系列九:Drm之DRM_IOCTL_MODE_ATOMIC

本系列文章基于linux 5.15
当原子模式设置都结束后,需要提交原子模式设置请求,将其配置到硬件上, 此时需要调用ioctl(fd, DRM_IOCTL_MODE_ATOMIC, &atomic)进行提交。

一、整体流程

用户层提供相应的原子模式设置,包含着各种属性和对象,然后通过ioctl(fd, DRM_IOCTL_MODE_ATOMIC, &atomic)进入kernel层,调用drm_mode_atomic_ioctl 提交的原子模式设置请求。主要步骤如下:

  • 参数验证:内核首先会验证用户空间传递的参数是否有效,包括检查对象句柄、属性 ID 等。
  • 属性更新:内核会遍历请求中的所有属性,并逐个应用到相应的 DRM 对象(如 CRTC、平面、连接器等)。
  • 提交检查:在应用属性之前,内核会进行一系列的检查,以确保新的模式设置是有效的,并且不会导致显示问题。
  • 原子提交:如果所有检查和更新都成功,内核会将这些更改原子地提交到硬件,确保显示的一致性。
  • 错误处理:如果在任何步骤中出现错误,内核会回滚所有更改,并返回相应的错误码给用户空间。

1.drm_mode_atomic_ioctl

用于处理与显示相关的原子模式设置请求。

DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER)

int drm_mode_atomic_ioctl(struct drm_device *dev,
			  void *data, struct drm_file *file_priv)
{
	struct drm_mode_atomic *arg = data;
	uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr);
	uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr);
	uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr);
	uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr);
	unsigned int copied_objs, copied_props;
	struct drm_atomic_state *state;
	struct drm_modeset_acquire_ctx ctx;
	struct drm_out_fence_state *fence_state;
	int ret = 0;
	unsigned int i, j, num_fences;
	struct drm_printer p = drm_info_printer(dev->dev);

	/* 检查 driver_feature是否支持DRIVER_ATOMIC*/
	if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
		return -EOPNOTSUPP;

	/* disallow for userspace that has not enabled atomic cap (even
	 * though this may be a bit overkill, since legacy userspace
	 * wouldn't know how to call this ioctl)
	 */
	if (!file_priv->atomic) {
		drm_dbg_atomic(dev,
			       "commit failed: atomic cap not enabled\n");
		return -EINVAL;
	}

	if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS) {
		drm_dbg_atomic(dev, "commit failed: invalid flag\n");
		return -EINVAL;
	}

	if (arg->reserved) {
		drm_dbg_atomic(dev, "commit failed: reserved field set\n");
		return -EINVAL;
	}

	if (arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) {
		drm_dbg_atomic(dev,
			       "commit failed: invalid flag DRM_MODE_PAGE_FLIP_ASYNC\n");
		return -EINVAL;
	}

	/* can't test and expect an event at the same time. */
	if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) &&
			(arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) {
		drm_dbg_atomic(dev,
			       "commit failed: page-flip event requested with test-only commit\n");
		return -EINVAL;
	}
/*用于初始化一个原子状态对象, 这里只更新了原子状态对象中crtc、plane对象的状态变化,connector对象变化不在这里*/
	state = drm_atomic_state_alloc(dev);
	if (!state)
		return -ENOMEM;

	drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
	state->acquire_ctx = &ctx;
	state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET);

retry:
	copied_objs = 0;
	copied_props = 0;
	fence_state = NULL;
	num_fences = 0;

	for (i = 0; i < arg->count_objs; i++) {
		uint32_t obj_id, count_props;
		struct drm_mode_object *obj;
	/*从用户空间安全地读取对象 ID*/
		if (get_user(obj_id, objs_ptr + copied_objs)) {
			ret = -EFAULT;
			goto out;
		}
	/*查找指定 ID 的对象*/
		obj = drm_mode_object_find(dev, file_priv, obj_id, DRM_MODE_OBJECT_ANY);
		if (!obj) {
			ret = -ENOENT;
			goto out;
		}
		if (!obj->properties) {
			drm_mode_object_put(obj);
			ret = -ENOENT;
			goto out;
		}
	/*从用户空间安全地读取当前对象的属性数量/
		if (get_user(count_props, count_props_ptr + copied_objs)) {
			drm_mode_object_put(obj);
			ret = -EFAULT;
			goto out;
		}

		copied_objs++;

		for (j = 0; j < count_props; j++) {
			uint32_t prop_id;
			uint64_t prop_value;
			struct drm_property *prop;
	/*从用户空间安全地读取当前对象属性ID*/
			if (get_user(prop_id, props_ptr + copied_props)) {
				drm_mode_object_put(obj);
				ret = -EFAULT;
				goto out;
			}
	/*查找指定 ID 的对象属性*/
			prop = drm_mode_obj_find_prop_id(obj, prop_id);
			if (!prop) {
				drm_mode_object_put(obj);
				ret = -ENOENT;
				goto out;
			}
	/*从用户空间安全地读取对象属性值*/
			if (copy_from_user(&prop_value,
					   prop_values_ptr + copied_props,
					   sizeof(prop_value))) {
				drm_mode_object_put(obj);
				ret = -EFAULT;
				goto out;
			}
	/*将属性值设置到原子状态对象中*/
			ret = drm_atomic_set_property(state, file_priv,
						      obj, prop, prop_value);
			if (ret) {
				drm_mode_object_put(obj);
				goto out;
			}

			copied_props++;
		}

		drm_mode_object_put(obj);
	}

	ret = prepare_signaling(dev, state, arg, file_priv, &fence_state,
				&num_fences);
	if (ret)
		goto out;
	/*仅测试原子操作是否可行(DRM_MODE_ATOMIC_TEST_ONLY)。
	以非阻塞方式提交原子操作(DRM_MODE_ATOMIC_NONBLOCK)。
	以阻塞方式提交原子操作(默认情况)。*/
	if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) {
		/*检查原子状态是否有效,但不会实际应用这些更改*/
		ret = drm_atomic_check_only(state);
	} else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) {
		/*调用该函数提交原子操作,但不会阻塞当前线程*/
		ret = drm_atomic_nonblocking_commit(state);
	} else {
		/*如果内核调试功能(drm_debug_enabled(DRM_UT_STATE))启用,打印原子状态的调试信息。*/
		if (drm_debug_enabled(DRM_UT_STATE))
			drm_atomic_print_new_state(state, &p);

		ret = drm_atomic_commit(state);
	}

out:
	complete_signaling(dev, state, fence_state, num_fences, !ret);

	if (ret == -EDEADLK) {
		drm_atomic_state_clear(state);
		ret = drm_modeset_backoff(&ctx);
		if (!ret)
			goto retry;
	}

	drm_atomic_state_put(state);

	drm_modeset_drop_locks(&ctx);
	drm_modeset_acquire_fini(&ctx);

	return ret;
}

1.1drm_atomic_set_property

作用是一是根据属性的类型判断是否符合属性的定义,二是根据对象的类型,获取相应的原子状态,设置属性值。

int drm_atomic_set_property(struct drm_atomic_state *state,
			    struct drm_file *file_priv,
			    struct drm_mode_object *obj,
			    struct drm_property *prop,
			    uint64_t prop_value)
{
	struct drm_mode_object *ref;
	int ret;
	/*根据属性的类型(如range、bitmask、enum等)来检查给定的值是否符合属性的定义*/
	if (!drm_property_change_valid_get(prop, prop_value, &ref))
		return -EINVAL;
	/*根据 DRM 对象的类型(CONNECTOR、CRTC、PLANE等),获取相应的原子状态,并调用特定的函数来设置属性值。*/
	switch (obj->type) {
	case DRM_MODE_OBJECT_CONNECTOR: {
		struct drm_connector *connector = obj_to_connector(obj);
		struct drm_connector_state *connector_state;
	/*根据connector->funcs->atomic_duplicate_state(厂商自定义),获取相应的原子状态,*/
		connector_state = drm_atomic_get_connector_state(state, connector);
		if (IS_ERR(connector_state)) {
			ret = PTR_ERR(connector_state);
			break;
		}
	/*设置属性值,如果是自定义property,调用connector->funcs->atomic_set_property(厂商自定义)*/
		ret = drm_atomic_connector_set_property(connector,
				connector_state, file_priv,
				prop, prop_value);
		break;
	}
	case DRM_MODE_OBJECT_CRTC: {
		struct drm_crtc *crtc = obj_to_crtc(obj);
		struct drm_crtc_state *crtc_state;
/*根据crtc->funcs->atomic_duplicate_state(厂商自定义),获取相应的原子状态,*/
		crtc_state = drm_atomic_get_crtc_state(state, crtc);
		if (IS_ERR(crtc_state)) {
			ret = PTR_ERR(crtc_state);
			break;
		}
/*设置属性值,如果是自定义property,调用crtc->funcs->atomic_set_property(厂商自定义)*/
		ret = drm_atomic_crtc_set_property(crtc,
				crtc_state, prop, prop_value);
		break;
	}
	case DRM_MODE_OBJECT_PLANE: {
		struct drm_plane *plane = obj_to_plane(obj);
		struct drm_plane_state *plane_state;
/*根据plane->funcs->atomic_duplicate_state(厂商自定义),获取相应的原子状态,*/
		plane_state = drm_atomic_get_plane_state(state, plane);
		if (IS_ERR(plane_state)) {
			ret = PTR_ERR(plane_state);
			break;
		}
/*设置属性值,如果是自定义property,调用plane->funcs->atomic_set_property(厂商自定义)*/
		ret = drm_atomic_plane_set_property(plane,
				plane_state, file_priv,
				prop, prop_value);
		break;
	}
	default:
		ret = -EINVAL;
		break;
	}
	/*作用是根据属性的类型,正确地释放对应的引用对象,以避免内存泄漏或资源未正确释放*/
	drm_property_change_valid_put(prop, ref);
	return ret;
}
1.1.1.1drm_property_change_valid_get

根据属性的类型(如range、bitmask、enum等)来检查给定的值是否符合属性的定义,并且可以选择性地返回一个引用对象(如 Blob 或其他对象)。

bool drm_property_change_valid_get(struct drm_property *property,
				   uint64_t value, struct drm_mode_object **ref)
{
	int i;

	if (property->flags & DRM_MODE_PROP_IMMUTABLE)
		return false;

	*ref = NULL;

	if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
		if (value < property->values[0] || value > property->values[1])
			return false;
		return true;
	} else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
		int64_t svalue = U642I64(value);

		if (svalue < U642I64(property->values[0]) ||
				svalue > U642I64(property->values[1]))
			return false;
		return true;
	} else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
		uint64_t valid_mask = 0;

		for (i = 0; i < property->num_values; i++)
			valid_mask |= (1ULL << property->values[i]);
		return !(value & ~valid_mask);
	} else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
		struct drm_property_blob *blob;

		if (value == 0)
			return true;

		blob = drm_property_lookup_blob(property->dev, value);
		if (blob) {
			*ref = &blob->base;
			return true;
		} else {
			return false;
		}
	} else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
		/* a zero value for an object property translates to null: */
		if (value == 0)
			return true;

		*ref = __drm_mode_object_find(property->dev, NULL, value,
					      property->values[0]);
		return *ref != NULL;
	}

	for (i = 0; i < property->num_values; i++)
		if (property->values[i] == value)
			return true;
	return false;
}

1.2prepare_signaling

处理与原子操作相关的事件(如页面翻转事件)和同步原语(如 DMA 围栏)。

static int prepare_signaling(struct drm_device *dev,
				  struct drm_atomic_state *state,
				  struct drm_mode_atomic *arg,
				  struct drm_file *file_priv,
				  struct drm_out_fence_state **fence_state,
				  unsigned int *num_fences)
{
	struct drm_crtc *crtc;
	struct drm_crtc_state *crtc_state;
	struct drm_connector *conn;
	struct drm_connector_state *conn_state;
	int i, c = 0, ret;
	/*设置了 DRM_MODE_ATOMIC_TEST_ONLY 标志,表示仅测试原子操作是否可行,不实际提交*/
	if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY)
		return 0;
	/*处理 CRTC 事件和同步原语(fence)*/
	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
		s32 __user *fence_ptr;

		fence_ptr = get_out_fence_for_crtc(crtc_state->state, crtc);
	/*如果设置了 DRM_MODE_PAGE_FLIP_EVENT 标志或存在同步原语(fence)指针,则需要创建事件*/
		if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT || fence_ptr) {
			struct drm_pending_vblank_event *e;
	/*创建一个与垂直消隐(vblank)相关的事件结构体,并将其初始化为页面翻转完成事件。
	该事件可以用于通知用户空间应用程序某个 CRTC 上的页面翻转操作已经完成。*/
			e = create_vblank_event(crtc, arg->user_data);
			if (!e)
				return -ENOMEM;

			crtc_state->event = e;
		}

		if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
			struct drm_pending_vblank_event *e = crtc_state->event;

			if (!file_priv)
				continue;

			ret = drm_event_reserve_init(dev, file_priv, &e->base,
						     &e->event.base);
			if (ret) {
				kfree(e);
				crtc_state->event = NULL;
				return ret;
			}
		}

		if (fence_ptr) {
			struct dma_fence *fence;
			struct drm_out_fence_state *f;

			f = krealloc(*fence_state, sizeof(**fence_state) *
				     (*num_fences + 1), GFP_KERNEL);
			if (!f)
				return -ENOMEM;

			memset(&f[*num_fences], 0, sizeof(*f));

			f[*num_fences].out_fence_ptr = fence_ptr;
			*fence_state = f;
	/*创建一个同步原语*/
			fence = drm_crtc_create_fence(crtc);
			if (!fence)
				return -ENOMEM;
	/*设置同步原语*/
			ret = setup_out_fence(&f[(*num_fences)++], fence);
			if (ret) {
				dma_fence_put(fence);
				return ret;
			}
	/*将同步原语与 CRTC 的事件关联起来*/
			crtc_state->event->base.fence = fence;
		}

		c++;
	}
	/*处理 CRTC 事件和同步原语(fence)*/
	for_each_new_connector_in_state(state, conn, conn_state, i) {
		struct drm_writeback_connector *wb_conn;
		struct drm_out_fence_state *f;
		struct dma_fence *fence;
		s32 __user *fence_ptr;

		if (!conn_state->writeback_job)
			continue;

		fence_ptr = get_out_fence_for_connector(state, conn);
		if (!fence_ptr)
			continue;

		f = krealloc(*fence_state, sizeof(**fence_state) *
			     (*num_fences + 1), GFP_KERNEL);
		if (!f)
			return -ENOMEM;

		memset(&f[*num_fences], 0, sizeof(*f));

		f[*num_fences].out_fence_ptr = fence_ptr;
		*fence_state = f;

		wb_conn = drm_connector_to_writeback(conn);
		fence = drm_writeback_get_out_fence(wb_conn);
		if (!fence)
			return -ENOMEM;

		ret = setup_out_fence(&f[(*num_fences)++], fence);
		if (ret) {
			dma_fence_put(fence);
			return ret;
		}

		conn_state->writeback_job->out_fence = fence;
	}

	/*如果没有 CRTC 需要处理事件,但设置了 DRM_MODE_PAGE_FLIP_EVENT 标志*/
	if (c == 0 && (arg->flags & DRM_MODE_PAGE_FLIP_EVENT))
		return -EINVAL;

	return 0;
}

1.3drm_atomic_commit

用于提交原子状态对象(drm_atomic_state)并实际应用这些状态更改。在提交之前会有check,只有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);
/*调用驱动程序提供的 atomic_commit 回调函数来提交原子状态。*/
	return config->funcs->atomic_commit(state->dev, state, false);
}
Property IN_FENCE_FD 在 DRM 中用于表示输入的同步信号,在使用双缓冲技术时特别有用。它可以用来确保在渲染帧之前,输入数据已经准备就绪。 下面是一个简单的示例代码,展示了如何使用 IN_FENCE_FD 属性: ```c #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <drm/drm.h> #include <drm/drm_mode.h> int main() { int fd; struct drm_mode_create_dumb create_dumb; struct drm_mode_map_dumb map_dumb; struct drm_mode_fb_cmd cmd; struct drm_gem_close gem_close; struct drm_mode_atomic atomic; struct drm_event_fence event_fence; // 打开 DRM 设备 fd = open("/dev/dri/card0", O_RDWR); // 创建 dumb buffer create_dumb.width = 800; create_dumb.height = 600; create_dumb.bpp = 32; ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); // 映射 dumb buffer map_dumb.handle = create_dumb.handle; ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb); // 配置 framebuffer cmd.fb_id = 0; cmd.width = create_dumb.width; cmd.height = create_dumb.height; cmd.pitch = create_dumb.pitch; cmd.bpp = create_dumb.bpp; cmd.depth = 24; cmd.handle = create_dumb.handle; ioctl(fd, DRM_IOCTL_MODE_ADDFB, &cmd); // 准备同步信号 int fence_fd = fence_create(fd, 0); // 提交 buffer atomic.fb_ids_ptr = (uint64_t) &cmd.fb_id; atomic.crtc_id = 0; atomic.connector_id = 0; atomic.count_props = 1; atomic.user_data = 0; atomic.flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_ALLOW_MODESET; atomic.objs_ptr = (uint64_t) &cmd.fb_id; atomic.objs = 1; atomic.fence_fd = fence_fd; // 设置 IN_FENCE_FD ioctl(fd, DRM_IOCTL_MODE_ATOMIC, &atomic); // 等待同步信号 read(fence_fd, &event_fence, sizeof(event_fence)); // 关闭 fence 文件描述符 close(fence_fd); // 关闭 framebuffer ioctl(fd, DRM_IOCTL_MODE_RMFB, &cmd.fb_id); // 关闭 dumb buffer gem_close.handle = create_dumb.handle; ioctl(fd, DRM_IOCTL_GEM_CLOSE, &gem_close); // 关闭 DRM 设备 close(fd); return 0; } ``` 此示例代码演示了如何使用 IN_FENCE_FD 属性来确保在提交 framebuffer 之前,同步信号已经准备就绪。请注意,这只是一个简单的示例,实际使用时可能需要根据具体需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值