1. 课程导入

在嵌入式 Linux、车载座舱、工业 HMI 与移动终端中,显示子系统始终是连接 GPU、显示控制器与上层图形栈的关键枢纽。早期 Linux 内核用 FrameBuffer(fbdev) 提供了一套极简的显示抽象:一块线性显存、一组固定参数、一个 /dev/fb0 设备节点,应用通过 mmap 写入像素即可上屏。它简单、直接、稳定,在单图层、低分辨率、无合成需求的年代几乎无可替代。然而,随着多图层叠加、硬件光标、页面翻转、垂直同步、多屏异显、GPU 合成与安全显示等需求爆发,fbdev 的“单缓冲 + 全局状态”模型逐渐力不从心。于是,DRM(Direct Rendering Manager) 从内核图形子系统中成长起来,成为现代 Linux 显示的事实标准。

本课程的目标不是简单罗列 API,而是带你完成一次真实的工程迁移:理解 fbdev 的边界,掌握 DRM 的 KMS 模型,最终把旧驱动、旧应用、旧调试习惯平滑迁移到 DRM 体系。下面这张图概括了本节要建立的整体认知:从应用层到内核层,fbdev 与 DRM 在抽象层次、资源管理和显示控制上的根本差异。

Linux 显示子系统:FrameBuffer 与 DRM 分层对比 应用层 / 用户空间 Qt / GTK / 直接写屏 Wayland / X11 合成器 libdrm / EGL / Vulkan fbdev 兼容接口 FrameBuffer(fbdev) /dev/fb0 · 单线性缓冲 · 固定分辨率 mmap 写像素 · ioctl 设参数 无图层 · 无 VSync · 无原子提交 DRM / KMS /dev/dri/card0 · GEM/DMA-BUF CRTC · Plane · Encoder · Connector Atomic Commit · Page Flip · VSync fbdev 驱动 fb_ops · fb_info · 寄存器直写 与显示控制器强耦合 DRM 驱动 drm_driver · modeset · atomic 统一资源模型 · 可复用 显示控制器 / GPU / 面板 / HDMI / MIPI-DSI 时序 · 像素时钟 · 图层混合 · 背光 · EDID 迁移

一、核心原理:fbdev 与 DRM 的本质差异

fbdev 的设计哲学是“一块显存解决所有问题”。它把显示控制器抽象成一个字符设备,应用通过 ioctl(FBIOGET_VSCREENINFO) 获取分辨率、像素格式和时序,再 mmap 到用户空间直接写像素。这种模型在单图层场景下效率极高,但一旦需要多图层叠加,应用只能自己用 CPU 或 GPU 把多个图层“压平”到同一块缓冲,既浪费带宽又难以保证同步。

DRM 则把显示管线拆解为可组合的对象:CRTC 负责扫描输出与时序,Plane 代表可叠加的图层,Encoder 把像素流转换为 HDMI、MIPI-DSI 等信号,Connector 表示物理接口与 EDID 能力。帧缓冲不再是一块固定显存,而是通过 GEM 或 DMA-BUF 管理的可共享对象。提交方式也从“写寄存器”升级为 Atomic Commit:一次 ioctl 原子地更新多个 Plane、CRTC 和属性,内核在 VSync 边界统一生效,避免撕裂。

一句话总结:fbdev 是“单缓冲 + 全局状态 + 直接写屏”,DRM 是“多对象 + 原子提交 + 同步翻转”。迁移的本质,是把显示控制从“应用自己管”变成“内核统一管”。

二、方法流程:迁移的四个阶段

真实项目中的迁移不是重写一切,而是分层推进。建议按以下四个阶段执行:

  1. 现状盘点:确认当前使用的是 fbdev 驱动还是 DRM 驱动,列出应用依赖的 /dev/fb*、ioctl 和自定义扩展。
  2. 驱动层切换:优先启用内核已有的 DRM 驱动(如 msm、i915、rockchip、sun4i),保留 fbdev 兼容层 fbdev emulation 作为过渡。
  3. 应用层适配:把直接操作 /dev/fb0 的代码替换为 libdrm 的 drmModeSetCrtc、drmModePageFlip 或 Atomic API;图形栈迁移到 EGL/GBM 或 Wayland。
  4. 调优与验证:用 modetest、kmscube、drm_info 验证图层、时序、VSync 和功耗,回归多屏与热插拔场景。
# 查看当前显示设备与驱动
ls -l /dev/fb* /dev/dri/*
cat /sys/class/drm/card0/device/driver/module/drivers

# 使用 modetest 枚举 DRM 资源
modetest -M rockchip -c
modetest -M rockchip -p

# 查看 fbdev 兼容层是否启用
dmesg | grep -i "fbdev\|drm"

三、案例场景:车载 HMI 的多图层需求

某车载座舱项目早期使用 fbdev 驱动,仪表盘和中控屏各自占用一个 /dev/fb。当产品要求“仪表盘背景 + 报警弹窗 + 视频倒车影像”三层叠加时,fbdev 方案只能由 CPU 逐帧合成,导致 CPU 占用率飙升到 70% 以上,倒车影像延迟超过 200ms。迁移到 DRM 后,三个图层分别绑定到不同 Plane,由显示控制器硬件完成叠加,CPU 占用降到 12%,延迟稳定在 33ms 以内。这个案例说明:迁移的收益不只是“新”,而是把不可能变成可能。

四、误区与复盘

常见误区一:认为 DRM 只是“fbdev 的替代品”,把 drmModeSetCrtc 当成 FBIOPUT_VSCREENINFO 用。实际上 DRM 的资源生命周期、引用计数和提交语义完全不同,必须理解对象模型。

常见误区二:忽略 fbdev 兼容层的存在,直接删除旧接口,导致依赖 /dev/fb0 的遗留应用无法启动。正确做法是保留 CONFIG_DRM_FBDEV_EMULATION 作为过渡。

常见误区三:只改应用不改驱动,或只改驱动不改应用。迁移是双向的,必须同时梳理内核态与用户态的依赖链。

对比维度 FrameBuffer(fbdev) DRM / KMS
设备节点 /dev/fb0 /dev/dri/card0
缓冲模型 单线性显存,mmap 直写 GEM / DMA-BUF,可共享、可导入
图层能力 无硬件图层,需软件合成 多 Plane 硬件叠加
同步机制 无 VSync,易撕裂 Page Flip + Atomic Commit + VSync
多屏支持 多个 fb 设备,独立管理 统一 CRTC/Connector 拓扑
典型调试工具 fbset、cat /dev/urandom > /dev/fb0 modetest、kmscube、drm_info

五、学习路线图

本课程后续将沿着“认知—驱动—应用—调优”的主线展开:先建立 DRM 对象模型与 KMS 管线认知,再深入驱动层的 drm_driver、atomic_helper 与 drm_panel,接着完成用户态 libdrm 与 EGL/GBM 的适配,最后进入多屏、热插拔、功耗与性能调优的实战。你不需要一次记住所有 API,但需要先建立一张清晰的“显示管线地图”——这正是本节 SVG 想帮你完成的事。

学习建议:在动手改代码之前,先用 modetest 在你的开发板上跑一遍,观察 CRTC、Plane、Connector 的实际数量和属性。真实硬件的拓扑,比任何文档都更有说服力。