1. 课程导入

在嵌入式 Linux、车载座舱、工业 HMI、平板与机顶盒等产品中,“把画面点亮”只是显示链路的第一步。真正决定产品体验的,是显示子系统能否稳定完成帧缓冲管理、图层合成、模式设置、页面翻转、热插拔检测、多屏协同与电源管理。过去二十年,Linux 显示栈经历了从 fbdev 到 DRM KMS 的主线迁移;今天新 SoC 的 BSP 与上游内核几乎都以 DRM KMS 为默认显示框架,fbdev 更多以兼容层或历史驱动形式存在。理解这条演进路线,是开展显示驱动迁移工程的前提。

一、演进背景:从“一块显存”到“一套显示管线”

fbdev(framebuffer device)诞生于图形需求相对简单的年代。它的核心抽象非常直接:内核暴露 /dev/fb0,用户态通过 mmap 把一块连续显存映射到进程空间,向其中写入像素,硬件便扫描输出。它解决了“无图形系统也能显示控制台”的问题,也支撑了早期 Qt/Embedded、DirectFB 等方案。

但随着显示硬件复杂化,fbdev 的模型逐渐吃力:它默认只有一张主平面、一个固定分辨率、一套全局显示参数;多图层叠加、YUV 视频层、硬件光标、色彩管理、原子提交、多 CRTC 多连接器、HDMI 热插拔与 EDID 解析,都很难在 fbdev 框架内优雅表达。DRM(Direct Rendering Manager)最初服务于 GPU 显存与命令提交,后来通过 KMS(Kernel Mode Setting)接管显示模式设置与输出管线,形成现代 Linux 显示主框架。

可以把两者理解为不同时代的“显示操作系统”:fbdev 像一块可写的画布,DRM KMS 则像一条可编排的流水线。迁移工程不是简单替换 API,而是把“写显存”的思维升级为“管理显示对象与提交状态”的思维。

Linux 显示子系统演进与迁移定位 fbdev 时代:单画布模型 用户态应用 /dev/fb0 单块连续显存 · 固定分辨率 · 单主平面 mmap 写像素 → 硬件扫描输出 DRM KMS 时代:管线模型 用户态 / 合成器 /dev/dri/card0 Plane · CRTC · Encoder · Connector Atomic 提交 · 多图层 · 热插拔 · 电源管理 演进 迁移工程核心目标 保留业务显示能力 → 接入 DRM KMS 对象模型 → 实现 Atomic 提交与页面翻转 → 支持多屏/热插拔/低功耗 → 可回归、可测试、可上游化 关键不是“换接口”,而是“换显示资源管理与状态提交模型” 学习路径(本课程主线) 概念对齐 对象模型 驱动改造 调试验证 工程化

二、fbdev 与 DRM KMS 的定位差异

fbdev 的定位是“通用帧缓冲设备”,强调简单、稳定、低依赖;DRM KMS 的定位是“显示控制器与输出管线管理框架”,强调对象化、可组合、可原子提交。二者不是同一层级的替代品,而是显示栈不同阶段的抽象。迁移时最容易踩的误区,是把 fbdev 的 ioctl(FBIOGET_VSCREENINFO)、mmap、pan_display 直接“翻译”成 DRM 调用,却忽略了 DRM 中资源分配、状态校验、提交时序与生命周期的差异。

对比维度 fbdev DRM KMS
核心抽象 一块帧缓冲 + 固定显示参数 Plane / CRTC / Encoder / Connector / FB
设备节点 /dev/fb0 /dev/dri/card0、renderD128
显示更新 写显存 + pan_display Atomic 提交、Page Flip、Fence
多图层 通常仅主平面 多 Plane 叠加、Z 序、混合
热插拔/EDID 支持弱,常需额外机制 Connector 状态轮询与事件通知
电源管理 DPMS 较粗粒度 CRTC/Connector 级电源与自刷新
典型用户态 DirectFB、早期 Qt/Embedded Weston、Xorg modesetting、KMS API

三、迁移工程的整体目标与方法流程

迁移工程的目标可以拆成四层:功能对齐(分辨率、刷新、色彩格式、旋转)、能力增强(多图层、多屏、热插拔、原子提交)、质量保障(无闪烁、无撕裂、无内存泄漏、可恢复)、工程可持续(代码可维护、可测试、可上游)。建议按以下流程推进:

  1. 现状盘点:梳理原 fbdev 驱动注册的 fb_ops、显存分配方式、时钟与电源序列、中断使用、用户态依赖的 ioctl。
  2. 对象映射:把“一块显存 + 一个输出”映射为 DRM 的 drm_plane、drm_crtc、drm_encoder、drm_connector 与 drm_framebuffer。
  3. 驱动骨架:实现 drm_driver 的 load/unload、dumb_create、atomic_commit、enable_vblank 等回调。
  4. 显示管线:配置 CRTC 时序、Plane 格式与缩放、Encoder 输出接口、Connector 检测与 EDID。
  5. 用户态适配:将原 fb 应用迁移到 DRM/KMS API,或通过 fbdev emulation 过渡。
  6. 验证回归:用 modetest、kmstest、kmscube 与自研用例覆盖模式设置、翻转、热插拔、挂起恢复。

迁移不是重写一切:很多平台可先启用 DRM_FBDEV_EMULATION,让旧 fb 应用继续工作,同时逐步把新功能切到原生 KMS 路径,降低一次性切换风险。

四、典型场景与常见误区

以车载中控为例:原方案用 fbdev 显示仪表与中控两个固定画面,迁移到 DRM KMS 后,需要把仪表、视频、UI 分别放到不同 Plane,通过 Atomic 提交保证同帧生效;同时 Connector 要支持 LVDS 与 HDMI 双屏,热插拔时不能阻塞主线程。此时若仍按 fbdev 思路“先写显存再切页”,就会出现图层错位、撕裂或提交失败。

常见误区:① 把 DRM 当成“另一个 /dev/fb”;② 忽略 Atomic 状态校验,直接改寄存器;③ 在中断上下文做耗时模式设置;④ 忘记释放 drm_framebuffer 与 GEM 对象导致泄漏;⑤ 只测静态画面,不测热插拔与挂起恢复。

# 迁移前后常用排查命令示例
ls /dev/fb*                 # 迁移前:确认 fbdev 节点
ls /dev/dri/                # 迁移后:确认 card0 / renderD128
modetest -M your_drm -c      # 查看 Connector 与模式
modetest -M your_drm -p      # 查看 Plane 与格式
cat /sys/class/drm/card0-HDMI-A-1/status   # 热插拔状态
dmesg | grep -i "drm\|kms\|fb"             # 驱动加载与报错

本课程后续将围绕“概念对齐 → 对象模型 → 驱动改造 → 调试验证 → 工程化”的主线展开:先建立 DRM KMS 的对象与提交模型,再逐项替换 fbdev 的显示能力,最后用可复现的测试与日志体系保证迁移质量。掌握这条路径,你就能把一次“能亮屏”的移植,做成一套“可维护、可扩展、可上游”的显示驱动工程。