KMS(Kernel Mode Setting)是负责显示输出的核心组件,它处理与plane、crtc、encoder和connector相关的各项任务。简单来说,KMS就是结构体drm_mode_config、drm_mode_object和组件(object)的结合。
KMS=drm_mode_config + drm_mode_object + 组件(object)
一、drm_mode_config、drm_mode_object和组件(object)的关系
object是由drm_mode_object描述,通过type来确定对象类型,由 dev->mode_config.object_idr 申请过来的 idr来获取object的id。
struct drm_mode_object {
uint32_t id; // 由 dev->mode_config.object_idr 申请过来的 idr, 本质是查找object的索引
uint32_t type; // obj 类型, 不同的 type 表示不同的对象.
struct drm_object_properties *properties; // 最多支持 24 个 properties
struct kref refcount;
void (*free_cb)(struct kref *kref); // 释放回调接口
};
drm_mode_config、drm_mode_object以及object的关系如下图所示:
二、代码中组件间的联系
2内核驱动中的初始化
/* 初始化 DRM 设备*/
drm_mode_config_init(dev);
/* 注册 CRTC*/
dev->mode_config.funcs = &my_crtc_funcs;
drm_crtc_init(dev, &my_crtc, &my_crtc_helper_funcs);
/* 注册 Encoder*/
drm_encoder_init(dev, &my_encoder, &my_encoder_funcs, DRM_MODE_ENCODER_TMDS);
/* 注册 Connector*/
drm_connector_init(dev, &my_connector, &my_connector_funcs, DRM_MODE_CONNECTOR_HDMIA);
/* 注册 Plane*/
drm_plane_init(dev, &my_plane, DRM_PLANE_TYPE_PRIMARY);
2.1drm_mode_config_init
作用主要是初始化 drm_device->mode_config 结构体和设置资源管理机制,确保资源能够自动释放。
static inline int drm_mode_config_init(struct drm_device *dev)
{
return drmm_mode_config_init(dev);
}
int drmm_mode_config_init(struct drm_device *dev)
{
mutex_init(&dev->mode_config.mutex);
drm_modeset_lock_init(&dev->mode_config.connection_mutex);
mutex_init(&dev->mode_config.idr_mutex);
mutex_init(&dev->mode_config.fb_lock);
mutex_init(&dev->mode_config.blob_lock);
INIT_LIST_HEAD(&dev->mode_config.fb_list);
INIT_LIST_HEAD(&dev->mode_config.crtc_list);
INIT_LIST_HEAD(&dev->mode_config.connector_list);
INIT_LIST_HEAD(&dev->mode_config.encoder_list);
INIT_LIST_HEAD(&dev->mode_config.property_list);
INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
INIT_LIST_HEAD(&dev->mode_config.plane_list);
INIT_LIST_HEAD(&dev->mode_config.privobj_list);
idr_init(&dev->mode_config.object_idr);
idr_init(&dev->mode_config.tile_idr);
ida_init(&dev->mode_config.connector_ida);
spin_lock_init(&dev->mode_config.connector_list_lock);
init_llist_head(&dev->mode_config.connector_free_list);
INIT_WORK(&dev->mode_config.connector_free_work, drm_connector_free_work_fn);
drm_mode_create_standard_properties(dev);
/* Just to be sure */
dev->mode_config.num_fb = 0;
dev->mode_config.num_connector = 0;
dev->mode_config.num_crtc = 0;
dev->mode_config.num_encoder = 0;
dev->mode_config.num_total_plane = 0;
if (IS_ENABLED(CONFIG_LOCKDEP)) {
struct drm_modeset_acquire_ctx modeset_ctx;
struct ww_acquire_ctx resv_ctx;
struct dma_resv resv;
int ret;
dma_resv_init(&resv);
drm_modeset_acquire_init(&modeset_ctx, 0);
ret = drm_modeset_lock(&dev->mode_config.connection_mutex,
&modeset_ctx);
if (ret == -EDEADLK)
ret = drm_modeset_backoff(&modeset_ctx);
ww_acquire_init(&resv_ctx, &reservation_ww_class);
ret = dma_resv_lock(&resv, &resv_ctx);
if (ret == -EDEADLK)
dma_resv_lock_slow(&resv, &resv_ctx);
dma_resv_unlock(&resv);
ww_acquire_fini(&resv_ctx);
drm_modeset_drop_locks(&modeset_ctx);
drm_modeset_acquire_fini(&modeset_ctx);
dma_resv_fini(&resv);
}
return drmm_add_action_or_reset(dev, drm_mode_config_init_release,
NULL);
}
2.1.1drm_mode_create_standard_properties
作用是提供一组通用的属性接口,方便用户空间程序和内核驱动之间的交互,属性通常有blob、range、enum、object等,例如属性enum的“SRC_W”,“SRC_H”等。
static int drm_mode_create_standard_properties(struct drm_device *dev)
{
struct drm_property *prop;
int ret;
ret = drm_connector_create_standard_properties(dev);
if (ret)
return ret;
prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
"type", drm_plane_type_enum_list,
ARRAY_SIZE(drm_plane_type_enum_list));
if (!prop)
return -ENOMEM;
dev->mode_config.plane_type_property = prop;
prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
"SRC_X", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_x = prop;
prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
"SRC_Y", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_y = prop;
prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
"SRC_W", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_w = prop;
prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
"SRC_H", 0, UINT_MAX);
if (!prop)
return -ENOMEM;
dev->mode_config.prop_src_h = prop;
...
}
2.2组件之间的关联
组件之间的关联通过以下方式建立
CRTC 和 Encoder:
drm_mode_connector_attach_encoder(&my_connector, &my_encoder);
drm_encoder_helper_add(&my_encoder, &my_encoder_helper_funcs);
Encoder 和 Connector:
drm_connector_attach_encoder(&my_connector, &my_encoder);
Plane 和 CRTC:
drm_plane_helper_add(&my_plane, &my_plane_helper_funcs);
2.3用户空间配置 KMS 组件
用户空间程序(如 Wayland 或 Xorg)通过 DRM API 配置 KMS 组件。例如:
使用 drmModeSetCrtc 设置 CRTC 的显示模式。
使用 drmModeSetPlane 配置 Plane 的显示内容。
使用 drmModeConnectorSetProperty 设置 Connector 的属性(如分辨率、刷新率)。
2.4 KMS 的数据流
KMS 的数据流如下:
a.用户空间渲染:
用户空间程序将渲染好的图像放入 Framebuffer。
b.配置显示模式:
用户空间程序通过 DRM API 配置 CRTC、Encoder 和 Connector 的显示模式。
c.提交 Framebuffer:
用户空间程序将 Framebuffer 的内容提交给 Plane。
d.显示图像:
CRTC 从 Plane 中读取 Framebuffer 的内容,生成显示时序信号。
Encoder 将信号转换为物理接口支持的格式。
Connector 将信号发送到显示器。
一个简单的KMS配置实例
/* 获取 Connector*/
drmModeConnector *connector = drmModeGetConnector(fd, connector_id);
/* 获取 Encoder*/
drmModeEncoder *encoder = drmModeGetEncoder(fd, connector->encoder_id);
/* 获取 CRTC*/
drmModeCrtc *crtc = drmModeGetCrtc(fd, encoder->crtc_id);
/* 设置显示模式*/
drmModeSetCrtc(fd, crtc->crtc_id, fb_id, 0, 0, &connector->connector_id, 1, &connector->modes[0]);
/* 提交 Framebuffer*/
drmModeSetPlane(fd, plane_id, crtc->crtc_id, fb_id, 0, 0, 0, width, height, 0, 0, width << 16, height << 16);