4. DRM核心对象模型

在 Linux DRM(Direct Rendering Manager)子系统中,显示管线并不是一个“大而全”的单一对象,而是被拆分为若干职责明确、彼此协作的核心对象。理解这些对象的职责边界与连接关系,是读懂 STM32MP 平台显示驱动、使用 modetest 枚举资源、以及后续排查显示问题的前提。本节先建立对象模型,再结合 modetest 在 STM32MP 上的典型输出逐项解读。

一、六个核心对象的职责

drm_device 是整个 DRM 实例的根对象,通常对应一个显示控制器(如 STM32MP 的 LTDC 或 DSI 控制器)。它持有 mode_config,管理本设备上的 CRTC、encoder、connector、plane、framebuffer 等资源列表,并负责文件句柄、GEM 内存管理、中断与 IOCTL 分发。可以把它理解为“显示子系统的总入口”。

drm_crtc 代表一个显示扫描输出通道,负责从 framebuffer 取像素、按 mode 时序产生扫描信号,并驱动 encoder。它持有当前 mode、framebuffer 引用、扫描位置等状态。STM32MP 的 LTDC 通常对应一个 CRTC,若同时使用 DSI 输出,则可能对应另一个 CRTC。

drm_encoder 负责把 CRTC 产生的像素时序转换为某种物理接口信号,例如 RGB 并行、DSI、HDMI 等。它本身不产生时序,而是“翻译”时序。一个 encoder 只能被一个 CRTC 驱动,但可以被多个 connector 共享(实际中通常一对一)。

drm_connector 代表最终对外连接的物理接口或显示终端,例如 DSI 面板、HDMI 接口、VGA 接口。它负责探测连接状态(connected/disconnected)、读取 EDID、暴露可用 mode 列表,并作为用户空间设置显示模式的入口。connector 通过 encoder 与 CRTC 建立链路。

drm_plane 是图层抽象,负责把 framebuffer 的某块区域合成到 CRTC 的扫描输出上。它分为 primary、overlay、cursor 三类。primary plane 通常与 CRTC 一一对应,是显示主画面;overlay plane 用于叠加视频或 UI 层;cursor plane 用于硬件光标。plane 支持位置、缩放、格式、alpha 等属性。

drm_framebuffer 是显存中的一块像素缓冲区,包含宽、高、像素格式、pitch、offset 以及底层 GEM 对象引用。它不关心显示时序,只描述“图像数据长什么样”。CRTC 或 plane 通过引用 framebuffer 来获取要显示的像素。

二、对象之间的连接关系

从数据流角度看,framebuffer 提供像素,plane 负责合成,CRTC 负责扫描输出,encoder 负责接口转换,connector 负责对外连接。用户空间通过 connector 选择 mode,通过 CRTC 设置时序,通过 plane 绑定 framebuffer,最终形成一条完整的显示链路。下图给出这六个对象在 STM32MP 平台上的典型关系。

STM32MP DRM 核心对象关系图 drm_device(LTDC / DSI 显示控制器实例) drm_framebuffer 宽×高 / 格式 / pitch GEM 对象引用 drm_plane primary / overlay / cursor 位置 / 缩放 / alpha drm_crtc mode 时序 / 扫描输出 framebuffer 引用 drm_encoder RGB / DSI / HDMI 转换 drm_connector 连接状态 / EDID / mode 列表 用户空间 modetest / libdrm 绑定 合成 时序 接口 设置 mode 设置 CRTC / plane 属性 数据流方向: framebuffer(像素数据) → plane(图层合成) → crtc(扫描时序) → encoder(接口转换) → connector(物理输出) 控制流方向: 用户空间通过 connector 选 mode,通过 crtc 设时序,通过 plane 绑 framebuffer,最终由 drm_device 统一提交

三、用 modetest 枚举 STM32MP 显示资源

modetest 是 libdrm 提供的调试工具,它通过 DRM IOCTL 读取当前设备的资源列表,并以文本形式打印。在 STM32MP 开发板上,通常先确认 DRM 设备节点:

ls /dev/dri/
# 典型输出:card0  renderD128

modetest -M stm -c

其中 -M stm 指定使用 STM 驱动模块,-c 表示只枚举 connector。实际使用时也可以直接 modetest -M stm 输出全部资源。下面给出一段典型的枚举输出片段(示例数据,用于说明格式,并非某块板卡的实测值):

Connectors:
id      encoder status          name            size (mm)      modes   encoders
38      37      connected       DSI-1           154x86         1       37
        mode: "800x480": 60 33200 800 840 888 928 480 493 496 502 0x0 0x0
        props:
                1 EDID:                     flags=immutable blob_id=0
                2 DPMS:                     flags=enum enum_values=["On","Standby","Suspend","Off"]
                5 link-status:              flags=enum enum_values=["Good","Bad"]
                6 non-desktop:              flags=immutable range=[0,1]

Encoders:
id      crtc    type            possible crtcs   possible clones
37      35      DSI             0x00000001       0x00000001

CRTCs:
id      fb      pos     size
35      40      (0,0)   (800,480)
        mode: "800x480": 60 33200 800 840 888 928 480 493 496 502 0x0 0x0
        props:
                7 ACTIVE:                   flags=range value=1
                8 MODE_ID:                  flags=blob blob_id=0

Planes:
id      crtc    fb      CRTC x,y        x,y     gamma size      possible crtcs
31      35      40      0,0             0,0     0               0x00000001
        formats: AR24 XR24 RG16
        props:
                9 type:                     flags=enum enum_values=["Overlay","Primary","Cursor"]
                10 IN_FORMATS:              flags=immutable blob_id=0

Framebuffers:
id      size    pitch
40      800x480 3200

四、逐项解读输出

Connectors 段:id=38 是 connector 编号,encoder=37 表示它当前绑定到编号 37 的 encoder,status=connected 表示面板已连接。name=DSI-1 说明这是 DSI 接口。size 154x86 是面板物理尺寸(毫米)。modes 数量为 1,下面列出具体 mode:800x480@60,像素时钟 33200 kHz,后面依次是水平有效、水平前肩、水平同步、水平后肩,以及垂直有效、垂直前肩、垂直同步、垂直后肩。这些参数正是 CRTC 扫描时序的来源。

Encoders 段:id=37,crtc=35 表示它当前被 CRTC 35 驱动,type=DSI 表示接口类型,possible crtcs=0x00000001 表示它只能被 CRTC 0 驱动(位掩码)。

CRTCs 段:id=35,fb=40 表示当前绑定的 framebuffer 编号,pos=(0,0) 是扫描起始位置,size=(800,480) 是当前输出分辨率。mode 行与 connector 中一致,说明该 CRTC 正在使用这个时序。ACTIVE=1 表示 CRTC 已启用。

Planes 段:id=31,crtc=35 表示它当前挂在 CRTC 35 上,fb=40 表示绑定了 framebuffer 40,x,y=(0,0) 是图层在屏幕上的位置。formats 列出支持的像素格式:AR24(ARGB8888)、XR24(XRGB8888)、RG16(RGB565)。type 属性说明它是 Primary 还是 Overlay。

Framebuffers 段:id=40,size=800x480,pitch=3200 字节。pitch 是每行字节数,800 像素 × 4 字节/像素 = 3200,说明当前使用 32 位像素格式,与 AR24/XR24 一致。

五、从枚举到实际设置

枚举只是“看”,真正设置显示需要把上述对象串起来。一个典型流程是:先根据 connector 的 mode 列表选定 800x480@60,然后创建 framebuffer(或使用已有 fb),再通过 atomic 或 legacy IOCTL 把 CRTC、plane、connector 的状态一次性提交。modetest 也支持直接设置:

modetest -M stm -s 38:800x480 -P 31@35:800x480+0+0

其中 -s 38:800x480 表示把 connector 38 设置为 800x480 模式,-P 31@35:800x480+0+0 表示把 plane 31 绑定到 CRTC 35,位置 (0,0),尺寸 800x480。执行后如果屏幕出现测试图案,说明整条链路 framebuffer→plane→crtc→encoder→connector 已经打通。

判断链路是否正常,可以依次检查:connector 的 status 是否为 connected;CRTC 的 ACTIVE 是否为 1;plane 的 fb 是否非 0;framebuffer 的 pitch 是否与格式匹配。任何一环为 0 或 disconnected,都说明该环节尚未就绪。

六、对象模型带来的工程意义

把显示拆成六个对象,最大的好处是“可组合、可替换、可调试”。例如更换面板只需改 connector 的 mode 和 EDID;调整图层叠加只需改 plane 属性;切换接口只需换 encoder 类型;而 framebuffer 的格式与 pitch 则决定了内存带宽和显示效果。在 STM32MP 上,LTDC 通常提供 primary plane 和 overlay plane,DSI 作为 encoder,面板作为 connector,理解这些对应关系后,阅读驱动代码和排查显示异常都会更有方向。

最后需要强调:modetest 输出中的 id 是运行时动态分配的,不同板卡、不同内核版本可能不同,但对象类型和字段含义是稳定的。掌握“看类型、看状态、看绑定关系”这三步,就能从任意一段 modetest 输出中还原出当前的显示拓扑。