本系列文章基于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);
}