1. 显示子系统全景概览:从应用层到硬件的完整数据流

做嵌入式Linux显示驱动,第一件事不是翻芯片手册,而是先搞清楚整个显示链路长什么样。我见过太多人一上来就扎进DRM的代码里,结果连数据从哪来到哪去都没弄明白。今天我们就从应用层一路往下,把这条完整的数据流走一遍。

1.1 一条显示命令的奇幻漂流

想象一下,你在手机上点开一张照片。这个简单的动作,背后发生了什么?

  1. 应用层:相册App调用OpenGL ES或Wayland/Wlroots,把图片解码成像素数据。
  2. 合成层:合成器(比如Weston或SurfaceFlinger)把多个图层叠在一起。
  3. DRM/KMS:内核的显示驱动接管,配置显示模式、缓冲区、图层。
  4. 硬件层:GPU或Display Controller把像素数据推送到屏幕。

说白了,这就是一条从「用户空间」到「硬件寄存器」的管道。而DRM/KMS,就是这条管道的总阀门。

核心观点:DRM/KMS是Linux显示子系统的中枢神经。不理解它,你连显示驱动的大门都没摸到。

1.2 为什么是DRM/KMS?

早些年,Linux显示驱动是个大杂烩。FBDEV(Frame Buffer Device)是主流,但它的缺点很明显:不支持多图层、没有硬件合成、无法处理复杂的显示场景。我刚开始做驱动时,用的就是FBDEV,每次切换分辨率都要重新映射内存,别提多痛苦了。

后来DRM(Direct Rendering Manager)出现了。它把显示驱动分成了两层:

  • DRM Core:提供统一的API,管理缓冲区、同步、上下文。
  • KMS(Kernel Mode Setting):负责显示模式、图层、连接器、编码器的配置。

你想想看,有了KMS,用户空间可以直接告诉内核:「我要1920x1080@60Hz,主图层在buffer A,光标在buffer B」。内核再也不用猜你要什么了。

特性 FBDEV DRM/KMS
多图层支持 ❌ 不支持 ✅ 硬件图层叠加
模式设置 ❌ 固定模式 ✅ 动态切换
缓冲区管理 ❌ 单缓冲 ✅ 双缓冲/三缓冲
硬件加速 ❌ 无 ✅ 支持GPU/DPU
社区支持 ❌ 已弃用 ✅ 主流方案

个人经验:如果你还在用FBDEV做新产品,我建议你趁早切换到DRM/KMS。我在一个项目中吃过亏——FBDEV在4K分辨率下性能直接崩了,换成DRM后帧率翻了三倍。

1.3 数据流全景图

下面这张图,是我每次讲显示驱动都会画的。它把整个数据流串起来了。

显示子系统数据流全景图 应用层 App / Wayland / X11 / Android SurfaceFlinger 合成层(Compositor) Weston / Mutter / SurfaceFlinger DRM/KMS 内核层 DRM Core | KMS (CRTC / Encoder / Connector / Plane) libdrm / drm ioctl / dma-buf 硬件抽象层 GPU驱动 / DPU驱动 / HDMI驱动 硬件层 Display Controller / HDMI TX / LCD Panel 数据流方向 ⬇ 从上到下

这张图里,DRM/KMS层是承上启下的关键。它向上对接合成器,向下控制硬件。没有它,应用层的像素数据根本到不了屏幕。

1.4 DRM/KMS的核心对象

KMS里有几个核心对象,你必须记住:

  • CRTC:显示控制器。负责从缓冲区读取数据,生成时序信号。每个CRTC对应一个显示管道。
  • Encoder:编码器。把CRTC输出的信号转换成连接器需要的格式(比如HDMI、LVDS、MIPI DSI)。
  • Connector:连接器。物理接口,比如HDMI口、VGA口、eDP口。它负责检测显示器是否插入、读取EDID。
  • Plane:图层。每个Plane对应一个硬件图层,可以独立设置位置、大小、透明度。
  • FB(Frame Buffer):帧缓冲区。存放像素数据的内存区域。

避坑指南:我曾经在一个项目中,把CRTC和Connector搞混了。结果配置了半天,屏幕死活不亮。后来才发现,CRTC只管时序,Connector才管物理连接。这两个对象一定要分清楚。

1.5 数据流的关键路径

一条完整的显示数据流,走的是这个路径:

  1. 应用层生成像素:App通过OpenGL ES或Skia渲染出帧。
  2. 合成器合成图层:Weston或SurfaceFlinger把多个图层合成一个最终帧。
  3. 提交FB到DRM:合成器通过libdrm调用drmModePageFlip(),把FB提交给KMS。
  4. KMS配置硬件:KMS根据FB的格式、分辨率、刷新率,配置CRTC、Encoder、Connector。
  5. 硬件扫描输出:Display Controller从FB读取像素,通过Encoder和Connector推送到屏幕。

嗯,这里要注意:第3步的PageFlip是异步的。也就是说,合成器提交FB后不会阻塞,而是等下一次VBlank(垂直消隐期)再切换。这样做的好处是避免撕裂(tearing)。

个人习惯:我调试显示驱动时,第一步永远是检查PageFlip的回调是否正常。如果回调没触发,说明VBlank中断有问题,或者CRTC配置错了。

1.6 为什么理解全景图很重要?

你想想看,如果不知道数据流怎么走,遇到问题你会很茫然。比如:

  • 屏幕黑屏:是硬件没初始化?还是CRTC没配置?还是Connector没检测到显示器?
  • 画面撕裂:是PageFlip没同步VBlank?还是双缓冲没配好?
  • 帧率低:是合成器性能不够?还是DRM的缓冲区分配有问题?

有了全景图,你就能快速定位问题出在哪一层。我在项目中用过这个方法,排查效率至少提升了一倍。

1.7 本章小结

显示子系统的数据流,说白了就是一条从App到屏幕的管道。DRM/KMS是这条管道的核心,它管理着CRTC、Encoder、Connector、Plane这些对象。理解它们的关系,是后续所有调试和移植工作的基础。