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 个 propertiesstruct 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);