4. DRM架构与核心对象

在 Linux DRM/KMS 子系统中,显示管线并不是由某一个“大而全”的结构体独立完成,而是由一组职责清晰、彼此关联的核心对象协同工作。理解这些对象的定位与连接关系,是阅读驱动源码、调试显示问题、编写自定义显示驱动的基础。本节围绕 drm_device、drm_driver、drm_crtc、drm_encoder、drm_connector、drm_plane 六个核心对象展开,先建立整体架构认知,再逐层拆解其职责与数据流。

一、核心原理:从设备到管线的对象分层

DRM 的核心设计思想是“设备抽象 + 管线抽象”。drm_device 代表一个 DRM 显示设备实例,是内核中所有 DRM 资源的容器;drm_driver 则描述“这个设备如何被驱动”,包含初始化、卸载、IOCTL 扩展等回调。二者关系类似“对象实例”与“操作函数集”:drm_device 持有 drm_driver 指针,驱动通过 drm_driver 注册进 DRM 框架。

真正描述“画面如何从内存走到屏幕”的,是 KMS 的四个管线对象:drm_plane 负责提供图像数据源,drm_crtc 负责扫描输出与时序生成,drm_encoder 负责将 CRTC 的像素流转换为特定接口信号,drm_connector 负责描述物理连接器与显示能力。它们通过 possible_crtcs、possible_clones、encoder 指针等字段建立拓扑关系,最终形成一条完整的显示链路。

一句话记忆:plane 是“画什么”,crtc 是“怎么扫”,encoder 是“怎么转”,connector 是“接到哪”,device 是“整个设备”,driver 是“驱动怎么管”。

二、对象职责与关键字段

下面用表格梳理六个核心对象的职责、关键字段与典型操作,便于在源码中快速定位。

对象核心职责关键字段/接口典型操作
drm_deviceDRM 设备实例,资源容器dev_private、driver、mode_configdrm_dev_alloc、drm_dev_register
drm_driver驱动回调与能力描述load、unload、dumb_create、gem_create_object注册、初始化、内存管理
drm_plane图像数据源与合成层possible_crtcs、format_types、funcsdrm_universal_plane_init
drm_crtc扫描输出与时序控制mode、enabled、funcs、statedrm_crtc_init_with_planes
drm_encoder像素流到接口信号的转换possible_crtcs、possible_clones、funcsdrm_encoder_init
drm_connector物理连接器与显示能力connector_type、modes、status、funcsdrm_connector_init

需要特别注意的是,drm_crtc 与 drm_plane 之间通过 possible_crtcs 位掩码建立“可用关系”,而不是硬绑定;drm_encoder 与 drm_connector 之间则通过 drm_connector_attach_encoder 建立连接。这种“能力声明 + 运行时选择”的机制,使同一套驱动可以适配多种硬件拓扑。

三、方法流程:一条显示链路的建立过程

以典型的显示驱动初始化为例,核心对象的创建顺序通常如下:

  1. 分配并初始化 drm_device,绑定 drm_driver。
  2. 创建 drm_plane,声明其支持的像素格式与可挂载的 CRTC。
  3. 创建 drm_crtc,并关联主 plane 与 cursor plane。
  4. 创建 drm_encoder,声明其可连接的 CRTC 与可克隆的 encoder。
  5. 创建 drm_connector,读取 EDID、填充 modes,并 attach 到 encoder。
  6. 注册设备,向用户空间暴露 /dev/dri/cardX 与 /dev/dri/renderDX。

下面是一段简化的内核驱动初始化代码片段,展示对象创建与连接的关键调用:

static int my_display_probe(struct platform_device *pdev)
{
    struct drm_device *drm;
    struct drm_crtc *crtc;
    struct drm_encoder *encoder;
    struct drm_connector *connector;
    struct drm_plane *primary, *cursor;

    drm = drm_dev_alloc(&my_drm_driver, &pdev->dev);
    if (IS_ERR(drm))
        return PTR_ERR(drm);

    /* 创建 plane */
    primary = my_plane_create(drm, DRM_PLANE_TYPE_PRIMARY);
    cursor  = my_plane_create(drm, DRM_PLANE_TYPE_CURSOR);

    /* 创建 CRTC,并绑定主 plane 与 cursor plane */
    crtc = my_crtc_create(drm, primary, cursor);

    /* 创建 encoder,声明可用的 CRTC */
    encoder = my_encoder_create(drm, BIT(drm_crtc_index(crtc)));

    /* 创建 connector,并 attach 到 encoder */
    connector = my_connector_create(drm);
    drm_connector_attach_encoder(connector, encoder);

    drm_dev_register(drm, 0);
    return 0;
}

这段代码体现了 DRM 的“先建对象、再连关系、最后注册”的典型流程。实际驱动中,plane、crtc、encoder、connector 的创建往往封装在各自模块中,但连接关系必须显式建立,否则用户空间无法枚举出完整显示链路。

四、案例与场景:HDMI 输出链路的对象映射

假设一块 SoC 内置显示控制器,支持一个 HDMI 输出。其对象映射关系可以描述为:

  • drm_plane:主图层,支持 ARGB8888、XRGB8888 等格式。
  • drm_crtc:显示控制器,负责生成 1080p60 时序。
  • drm_encoder:HDMI TX 编码器,将并行像素流转换为 TMDS 信号。
  • drm_connector:HDMI-A 连接器,读取 EDID,报告热插拔状态。

当用户空间执行 modetest 或 weston 时,会通过 GET_CONNECTOR、GET_ENCODER、GET_CRTC 等 IOCTL 枚举这些对象,并根据 possible_crtcs 与 attach 关系选择一条可用链路。若 connector 的 status 为 disconnected,则不会出现在可用模式列表中。

调试提示:使用 modetest -M <driver> -c 可以列出所有 connector、encoder、crtc 及其连接关系;cat /sys/kernel/debug/dri/0/state 可查看当前原子状态,是排查链路问题的第一手资料。

五、误区与复盘

初学者常犯的错误是把 drm_encoder 与 drm_connector 混为一谈。encoder 是“信号转换逻辑”,connector 是“物理接口与能力描述”。一个 encoder 可以连接多个 connector(通过 clone),一个 connector 也可以在不同场景下切换到不同 encoder。另一个常见误区是忽略 possible_crtcs 的位掩码含义,导致 plane 或 encoder 无法被正确挂载到目标 CRTC。

复盘时建议按以下清单检查:

  • plane 的 possible_crtcs 是否包含目标 CRTC 的索引位?
  • encoder 的 possible_crtcs 是否与 CRTC 匹配?
  • connector 是否已通过 drm_connector_attach_encoder 绑定?
  • connector 的 status 与 modes 是否已正确填充?
  • drm_device 是否已注册并暴露 card 节点?

只有这些对象各司其职、关系正确,KMS 才能顺利完成模式设置与页面翻转。理解它们,就等于掌握了 DRM 显示驱动的“骨架”。

DRM/KMS 核心对象与显示链路 drm_device(设备实例 / 资源容器) drm_driver load / unload / dumb_create drm_plane 图像数据源 / 图层 possible_crtcs / format_types drm_crtc 扫描输出 / 时序生成 mode / enabled / funcs drm_encoder 像素流 → 接口信号 possible_crtcs / possible_clones drm_connector 物理连接器 / 显示能力 connector_type / modes / status 数据流 像素流 attach_encoder 注册 plane crtc encoder connector driver 数据/信号流 连接关系