KMS(Kernel Mode Setting) 的核心思想就是将图形子系统的各个组件进行拆分和抽象,相对于 fbdev 使得内核能够更灵活、可扩展地管理这些组件。他们被拆分为 PLANE, CRTC, CONNECTOR, ENCODER, FB. 等组件
kms = drm_mode_config + 组件(obj)
元素 | 说明 |
---|---|
CRTC | 对显示 buffer 进行扫描,并产生时序信号的硬件模块,通常指 Display Controller |
ENCODER | 负责将 CRTC 输出的 timing 时序转换成外部设备所需要的信号的模块,如 HDMI 转换器或 DSI Controller |
CONNECTOR | 连接物理显示设备的连接器,如 HDMI、DisplayPort、DSI 总线,通常和 Encoder 驱动绑定在一起 |
PLANE | 硬件图层,有的 Display 硬件支持多层合成显示,但所有的 Display Controller 至少要有 1 个 plane |
FB | Framebuffer,单个图层的显示内容,唯一一个和硬件无关的基本元素 |
VBLANK | 软件和硬件的同步机制,RGB 时序中的垂直消影区,软件通常使用硬件 VSYNC 来实现 |
property | 任何你想设置的参数,都可以做成 property,是 DRM 驱动中最灵活、最方便的 Mode setting 机制 |
DUMB | 只支持连续物理内存,基于 kernel 中通用 CMA API 实现,多用于小分辨率简单场景 |
PRIME | 连续、非连续物理内存都支持,基于 DMA-BUF 机制,可以实现 buffer 共享,多用于大内存复杂场景 |
fence | buffer 同步机制,基于内核 dma_fence 机制实现,用于防止显示内容出现异步问题 |
此处参考最简单的DRM应用程序 (double-buffer)
上述的组件都由 drm_mode_object 描述. 通过 type 来确定 obj 描述的对象类型.
struct drm_mode_object {
uint32_t id; // 由 dev->mode_config.crtc_idr 申请过来的 idr, 本质是查找 obj 的索引
/*
#define DRM_MODE_OBJECT_CRTC 0xcccccccc
#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0
#define DRM_MODE_OBJECT_MODE 0xdededede
#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0
#define DRM_MODE_OBJECT_FB 0xfbfbfbfb
#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
#define DRM_MODE_OBJECT_ANY 0
*/
uint32_t type; // obj 类型, 不同的 type 表示不同的对象.
struct drm_object_properties *properties; // 最多支持 24 个 properties
struct kref refcount;
void (*free_cb)(struct kref *kref); // 释放回调接口
};
从结构体描述可知这些对象主要包括三个部分的内容.
- 对象 id 管理, 通过对象的 id, 可以通过 dev->mode_config.crtc_idr 查找到对应的对象.
- 属性(paropertise)管理, 对象的属性统一被存放到 properties.
- 对象生命周期管理, 释放对象时提供了 free_cb 接口.
内核提供了接口 drm_mode_object_get
用初始化 obj. 该函数从 dev->mode_config.crtc_idr
上申请一个 dir , 并用这个 idr 作为索引初始化 obj 的 id
// dev : 所属的 drm_device
// obj : 要初始化的 obj
// obj_type : obj 是什么对象. 对象包括 CRTC, CONNECTOR, ENCODER, FB, PLANE, PROPERTY 等这些类型
int drm_mode_object_get(struct drm_device *dev,
struct drm_mode_object *obj, uint32_t obj_type)
{
return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL);
}
int drm_mode_object_get_reg(struct drm_device *dev,
struct drm_mode_object *obj,
uint32_t obj_type,
bool register_obj,
void (*obj_free_cb)(struct kref *kref))
{
int ret;
mutex_lock(&dev->mode_config.idr_mutex);
ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL);
if (ret >= 0) {
obj->id = ret; // 设置 id
obj->type = obj_type; // 设置类型
if (obj_free_cb) { // 设置 free_cb
obj->free_cb = obj_free_cb;
kref_init(&obj->refcount);
}
}
mutex_unlock(&dev->mode_config.idr_mutex);
return ret < 0 ? ret : 0;
}
上述 kms 相关的组件 (obj) 通过 drm_mode_config 进行维护和管理. 所以这个数据结构非常重要, 它也是 kms 的核心. 他们的关系如下图所示.
这些组件 (obj) + drm_mode_config 就组成了 kms. 通过 drm_dev 导出的 ioctl 我们就可以使用任意 plane + crtc + encoder + connector + panel 来组合我们需要的硬件显示链路了.例如我们有如下硬件结构
plane0 --> crtc0 --> encoder0(mipi) --> connector0 --> panel0(lcd1)
--> encoder1(hdmi) --> connector1 --> panel1(lcd2)
--> panel2(lcd3)
我们有个 mipi lcd3 想用它显示, 用户可以通过 ioctl 设置我们需要的硬件链路, 十分方便.
plane0 --> crtc0 --> encoder0(mipi) --> connector0 --> panel3