1. 课程导入
在嵌入式 Linux、桌面 Linux 以及车载、工业 HMI 等场景中,“把画面显示出来”看似简单,背后却是一条从应用层到显示控制器硬件的完整软件栈。很多开发者第一次接触显示驱动时,会从 /dev/fb0 入手,用 mmap 直接写像素,觉得显示不过如此。但当需求升级到多图层叠加、硬件光标、页面翻转、分辨率动态切换、多屏异显时,FBDEV 的局限就会迅速暴露,这时就必须理解并迁移到 DRM/KMS 框架。本课程的目标,就是带你从帧缓冲的直觉认知出发,逐步建立 KMS 的完整心智模型,最终能够独立完成驱动适配与迁移。
一、Linux 图形显示栈的整体分层
Linux 图形显示栈可以自上而下划分为若干层,每一层解决不同的问题。理解分层,是理解 DRM 与 FBDEV 关系的前提。
- 应用与图形库层:Qt、GTK、SDL、Wayland 合成器、X Server 等,它们通过 EGL/OpenGL ES、Vulkan 或直接 ioctl 与内核交互。
- 用户态图形 API 层:Mesa、libdrm、GBM(Generic Buffer Management)等,负责把图形 API 调用翻译成对 DRM 设备的操作。
- 内核显示子系统层:DRM/KMS 是当前主流框架,FBDEV 是早期框架,二者在
/dev下分别暴露为card0与fb0。 - 显示控制器驱动层:CRTC、Encoder、Connector、Plane 等抽象对象,对应 SoC 内部的 Display Controller、MIPI DSI、HDMI、LVDS 等硬件模块。
- 硬件层:GPU、Display Controller、PHY、面板与背光。
其中,DRM 子系统内部又细分为 KMS(Kernel Mode Setting,内核模式设置)与 GEM(Graphics Execution Manager,显存管理)两大块。KMS 负责显示管线的配置与输出,GEM 负责显存缓冲区的分配与共享。FBDEV 则是一个更薄、更早的抽象,它只提供一块线性帧缓冲和有限的 ioctl,几乎不涉及图层、页面翻转和原子提交。
二、DRM 与 FBDEV 的关系:不是替代,而是包含与演进
很多资料把 DRM 和 FBDEV 描述成“新老对立”,这并不准确。更准确的说法是:DRM 在内部提供了 FBDEV 兼容层(drm_fbdev_generic 或 drm_fb_helper),使得一个 DRM 驱动可以同时向用户态暴露 /dev/fb0。这样,尚未适配 DRM 的老应用仍能通过 FBDEV 接口工作,而新应用则可以使用 KMS 的完整能力。
二者的核心差异可以用下表概括:
| 对比维度 | FBDEV | DRM / KMS |
|---|---|---|
| 设备节点 | /dev/fb0 | /dev/dri/card0 |
| 缓冲区模型 | 单一线性帧缓冲 | GEM 对象 + 多 Plane |
| 图层能力 | 无,需软件合成 | 硬件多图层叠加 |
| 页面翻转 | 不支持 | 支持,配合 vblank 同步 |
| 模式设置 | ioctl 有限,能力弱 | 原子提交,全管线配置 |
| 多屏支持 | 弱 | Connector 级多屏异显 |
| 显存共享 | 困难 | dma-buf 跨设备共享 |
register_framebuffer 说明是纯 FBDEV 驱动;看到 drm_dev_register 与 drm_fbdev_generic_setup 同时出现,说明是 DRM 驱动并开启了 FBDEV 兼容层。
三、从帧缓冲到 KMS 的迁移方法流程
迁移不是简单地把 fbdev 的 API 换成 drm 的 API,而是要把“一块内存 + 一个显示窗口”的思维,升级为“一组对象 + 一条管线 + 一次原子提交”的思维。建议按以下流程推进:
- 梳理现有 FBDEV 驱动:确认
fb_ops中的fb_check_var、fb_set_par、fb_pan_display分别对应哪些硬件寄存器操作。 - 抽象出 KMS 对象:把帧缓冲映射为
drm_framebuffer,把显示窗口映射为drm_plane,把时序控制映射为drm_crtc,把输出接口映射为drm_encoder与drm_connector。 - 实现原子提交:用
atomic_begin/atomic_flush包裹寄存器写入,保证一次提交要么全部生效,要么全部回滚。 - 接入 GEM 与 dma-buf:让缓冲区可以被 GPU、视频解码器、显示控制器共享,避免多余的拷贝。
- 保留 FBDEV 兼容:通过
drm_fbdev_generic_setup暴露/dev/fb0,保证老应用平滑过渡。
# 查看当前系统显示设备
ls -l /dev/fb* /dev/dri/
# 查看 DRM 设备能力
modetest -M rockchip -c
modetest -M rockchip -p
# 查看 FBDEV 信息
fbset -i
cat /sys/class/graphics/fb0/virtual_size
四、典型场景与常见误区复盘
在真实项目中,迁移往往由具体需求驱动。例如车载仪表需要“仪表盘 + 视频 + 报警图标”三层叠加,FBDEV 只能靠 CPU 合成,帧率低且功耗高;而 KMS 可以直接把三层分别送到不同的 Plane,由显示控制器硬件合成。再如多屏异显场景,FBDEV 很难为两个屏幕分别维护独立的时序与缓冲区,而 KMS 的 Connector 模型天然支持。
常见误区二:把 KMS 当成“更复杂的 FBDEV”。KMS 的核心是对象模型与原子提交,不是简单的 API 替换。
常见误区三:忽略 vblank 与页面翻转的同步,导致画面撕裂。KMS 的 atomic commit 需要配合
drm_crtc_wait_one_vblank 或事件回调使用。
本课程后续将沿着“显示栈分层 → DRM 对象模型 → KMS 原子提交 → GEM 显存管理 → 驱动适配与调试”的主线展开。你会在每一节中看到真实的内核结构体、ioctl 命令、设备树节点和调试日志,最终能够独立完成一个从 FBDEV 到 KMS 的驱动迁移,并具备排查显示异常的能力。