3. DRM框架基础

在全志(Allwinner)平台上移植DRM显示驱动,第一步不是写寄存器,而是理解DRM子系统如何把一块屏幕抽象成一组可编程对象。DRM(Direct Rendering Manager)负责显存与命令提交,KMS(Kernel Mode Setting)负责显示模式设置与画面合成。二者共同构成Linux显示驱动的骨架,而全志的Display Engine(DE)驱动本质上就是把这套骨架“翻译”成DE的图层、时序与DMA通道配置。

一、KMS核心对象:从抽象到硬件

KMS把显示通路拆成五类对象,它们之间是“流水线式”的引用关系:

  • Framebuffer(FB):描述一块可扫描输出的像素缓冲,记录宽高、像素格式、pitch与GEM对象句柄。它不关心显存实际在哪,只描述“怎么读”。
  • Plane:图层,是FB的承载者。一个CRTC可叠加多个Plane(全志DE通常支持多个UI/Video层),Plane负责位置、缩放、混合与alpha。
  • CRTC:显示控制器,对应一路扫描输出引擎,负责时序生成(h/v sync、porch)与像素时钟。它把若干Plane合成后送往Encoder。
  • Encoder:把CRTC的并行像素流转换为具体接口信号,如RGB、LVDS、MIPI DSI、HDMI TMDS。
  • Connector:物理连接器,管理热插拔检测(HPD)、EDID读取与显示模式列表,是用户空间看到的“显示器”。

这五者的关系可以用下面的结构图直观理解:

DRM/KMS 对象流水线与全志DE映射 Framebuffer 格式/pitch/GEM Plane UI/Video 图层 CRTC 时序/像素时钟 Encoder RGB/LVDS/DSI/HDMI Connector HPD/EDID/模式 全志 DE 硬件映射 Plane → DE Layer(UI/Video通道) CRTC → TCON/Display Engine 时序单元 Encoder → TCON 输出接口(RGB/LVDS/DSI) Connector → 面板/桥接芯片

二、GEM与TTM:显存从哪里来

Framebuffer只是“视图”,真正分配显存的是GEM(Graphics Execution Manager)。GEM提供drm_gem_object抽象,负责对象创建、句柄管理、mmap与PRIME导出。它本身不规定显存位置,只定义“一块可被GPU/显示控制器访问的缓冲”。

TTM(Translation Table Manager)则进一步管理显存域(VRAM、GTT、SYSTEM),负责缓冲在不同域之间的迁移与同步。全志平台通常没有独立VRAM,显示缓冲多分配在CMA(Contiguous Memory Allocator)区域,以保证DE能拿到物理连续的地址。因此实际路径是:GEM对象 → CMA分配 → DMA-BUF导出 → 显示控制器扫描。

关键点:GEM管“对象生命周期”,TTM管“显存位置与迁移”,CMA管“物理连续”。三者职责不同,调试时要先判断问题出在哪一层。

三、方法流程:一次模式设置的完整链路

用户空间通过libdrm发起一次显示配置,内核侧大致经历以下步骤:

  1. 打开/dev/dri/card0,调用drmModeGetResources()枚举CRTC、Encoder、Connector。
  2. 用drmModeGetConnector()读取连接状态与EDID,得到可用模式列表。
  3. 创建Dumb Buffer或GEM Buffer,并drmModeAddFB()生成Framebuffer。
  4. 调用drmModeSetCrtc(),把FB绑定到CRTC,并指定输出到哪个Connector。
  5. 若需叠加图层,使用drmModeSetPlane()配置Plane的位置、缩放与混合。
  6. 提交后触发原子(Atomic)或传统(Legacy)路径,驱动写DE寄存器并启动扫描。
# 典型调试命令:查看当前KMS对象
modetest -M sun4i-drm -c        # 列出CRTC
modetest -M sun4i-drm -e        # 列出Encoder
modetest -M sun4i-drm -p        # 列出Connector与模式
modetest -M sun4i-drm -s 40:1920x1080 -P 30@40:1920x1080+0+0

四、案例场景:全志H616 HDMI点屏

以H616 + HDMI为例,设备树中tcon-tv、hdmi、dw-hdmi节点分别对应CRTC、Encoder与Connector。启动后内核日志会打印:

[drm] Initialized sun4i-drm 1.0.0 for display-engine on minor 0
sun4i-drm display-engine: bound 1c0c000.tcon-tv (ops 0xffff...)
sun4i-drm display-engine: bound 1c30000.hdmi (ops 0xffff...)
[drm] Connector HDMI-A-1: connected, EDID 1920x1080@60

此时若modetest -p能看到模式列表,说明Connector与EDID通路正常;若drmModeSetCrtc返回-EINVAL,多半是Framebuffer的pitch或格式与DE不匹配,而不是HDMI本身的问题。

五、常见误区与复盘

误区现象正确理解
把Framebuffer当成显存改FB参数后画面仍异常FB只是视图,真正数据在GEM/CMA缓冲
认为Plane越多越好图层叠加后花屏或撕裂DE图层数有限,且带宽与格式有约束
忽略Connector状态SetCrtc成功但无输出需先确认HPD与EDID,再配置模式
GEM与DMA-BUF混用不当跨设备共享时地址错误用PRIME导出/导入,确保dma-buf附件正确
调试顺序建议:先确认Connector连接与EDID,再验证CRTC时序,最后检查Plane与FB格式。反过来排查,往往会在错误的层次浪费时间。

理解这五类对象与GEM/TTM的分工,是后续分析显示缓存、DMA-BUF共享与全志DE寄存器配置的基础。只有把“谁描述数据、谁搬运数据、谁扫描输出”这三件事分清,移植过程中的花屏、无输出、撕裂等问题才能被快速定位。