2. Linux 显示子系统架构:应用层、fbdev框架、DRM/KMS框架、硬件驱动层的关系与数据流

Linux 显示子系统是嵌入式图形栈的基石。理解其分层架构与数据流,是开发高质量 Framebuffer 驱动的必要前提。本章从顶层应用到底层硬件,逐层剖析各模块的职责与协作方式。

2.1 显示子系统四层模型

Linux 显示子系统在逻辑上可划分为四个层次,每一层都封装了特定的抽象与接口:

层次 名称 核心职责 典型组件/接口
Layer 4 应用层 图形渲染、合成、窗口管理 Qt、GTK、Wayland/Weston、X11、DirectFB
Layer 3 显示框架层 提供用户态 API,管理显示资源 fbdev (linux/fb.h)、DRM/KMS (drm.h, libdrm)
Layer 2 内核驱动框架层 实现框架回调,管理硬件状态 fb_ops、drm_driver、drm_mode_config_funcs
Layer 1 硬件驱动层 直接操作显示控制器、DMA、时序 LCD 控制器寄存器、MIPI DSI/DPI、HDMI TX
关键认知: 嵌入式开发中,fbdev 常用于简单场景(如开机 logo、单层 UI),而 DRM/KMS 是当前主流方案,支持多层合成、VSync 控制、GPU 协作等高级特性。

2.2 应用层:图形栈的入口

应用层是用户空间图形栈的起点。它不直接操作显示硬件,而是通过标准系统调用与显示框架交互。

  • 直接操作 fbdev: 应用通过 open("/dev/fb0") 获取文件描述符,使用 mmap() 映射帧缓冲区到用户空间,直接读写像素数据。这种方式简单但缺乏同步与合成能力。
  • 基于 DRM 的合成器: 如 Weston (Wayland compositor) 或 Xorg 的 modesetting 驱动,通过 DRM API 管理多个平面 (plane)、CRTC 和连接器 (connector),实现硬件合成。
  • GPU 加速: 应用通过 EGL/OpenGL ES 将渲染命令提交给 GPU,GPU 将渲染结果写入 DRM 分配的帧缓冲对象 (FBO),再由 KMS 输出到显示器。
嵌入式场景提示: 在资源受限的嵌入式设备中,应用层通常直接使用 fbdev 或轻量级 DRM 封装(如 libdrm 的 modeset 示例),避免引入重量级窗口系统。

2.3 fbdev 框架:传统但仍在使用的显示接口

fbdev (Framebuffer Device) 是 Linux 内核中历史最悠久的显示子系统框架。它提供了一个简单的、基于文件操作的显示抽象。

2.3.1 fbdev 核心数据结构

struct fb_info {
    struct fb_var_screeninfo var;   // 可变参数:分辨率、位深、时序
    struct fb_fix_screeninfo fix;   // 固定参数:物理地址、行长、内存大小
    struct fb_ops *fbops;           // 驱动操作函数集
    char __iomem *screen_base;      // 帧缓冲虚拟地址
    struct device *device;          // 关联的硬件设备
    // ... 其他字段
};

2.3.2 fbdev 数据流

  1. 应用调用 write() 或通过 mmap() 直接写入 screen_base 指向的内存。
  2. 内核 fbdev 核心层将数据更新标记为“脏”,并在下一次刷新周期(通常由硬件 VSync 触发)调用 fb_ops->fb_pan_display() 或 fb_ops->fb_imageblit()。
  3. 驱动层将帧缓冲内存内容通过 DMA 或直接内存拷贝传输到 LCD 控制器的显存 FIFO。
  4. LCD 控制器按设定的时序(HBP、HFP、VBP、VFP)逐行扫描输出到显示面板。
fbdev 的局限性: 不支持多平面合成、缺少原子更新 (atomic update)、无法直接控制 VSync 信号、对 GPU 加速支持薄弱。这些缺陷促使了 DRM/KMS 的诞生。

2.4 DRM/KMS 框架:现代显示子系统

DRM (Direct Rendering Manager) 最初为 GPU 管理设计,KMS (Kernel Mode Setting) 是其显示控制部分。DRM/KMS 提供了比 fbdev 更强大、更灵活的显示架构。

2.4.1 DRM/KMS 核心对象模型

对象 含义 类比 fbdev
CRTC 显示控制器,负责生成时序并扫描输出 fbdev 的显示模式控制
Plane 硬件图层,每个 plane 可绑定一个 framebuffer 无对应,fbdev 只有单层
Connector 物理连接器,如 HDMI、LVDS、eDP fbdev 的 fb_fix_screeninfo 部分信息
Encoder 编码器,将 CRTC 输出转换为连接器所需信号 无直接对应
Framebuffer 内存中的像素数据缓冲区 fbdev 的 screen_base

2.4.2 DRM/KMS 数据流

  1. 应用通过 libdrm 调用 drmModeAddFB() 创建 Framebuffer 对象,绑定 DMA-BUF 或 GEM 对象。
  2. 应用调用 drmModeSetPlane() 或 drmModeAtomicCommit() 提交显示配置。
  3. 内核 DRM 核心层校验配置(分辨率、格式、扫描方式),调用驱动提供的 atomic_check() 和 atomic_commit() 回调。
  4. 驱动层更新硬件寄存器:设置 CRTC 时序、plane 的源/目标矩形、连接器使能状态。
  5. 硬件在下一个 VSync 信号到来时,从 plane 绑定的 framebuffer 中读取像素数据,经合成后输出。
原子提交 (Atomic Commit): DRM/KMS 支持在一次系统调用中同时更新多个对象(CRTC、Plane、Connector),并在 VSync 边界统一生效,彻底消除画面撕裂 (tearing)。

2.5 硬件驱动层:与显示控制器对话

硬件驱动层是内核中直接操作显示控制器的代码。无论是 fbdev 还是 DRM/KMS 框架,最终都需要调用硬件驱动层的函数来配置寄存器、启动 DMA、处理中断。

2.5.1 硬件驱动层核心职责

  • 时序配置: 设置像素时钟、前后肩、同步脉冲宽度等 LCD 控制器时序参数。
  • DMA 引擎管理: 配置 DMA 通道,将帧缓冲数据从系统内存搬运到显示控制器的内部 FIFO。
  • 中断处理: 处理 VSync 中断、FIFO 上溢/下溢中断、DMA 完成中断。
  • 电源管理: 控制显示控制器的时钟门控、面板背光、复位序列。
  • 面板初始化: 通过 MIPI DSI/SPI/I2C 发送初始化命令序列,配置显示面板。

2.5.2 硬件驱动层与框架层的接口

在 fbdev 框架下,硬件驱动层通过填充 struct fb_ops 来注册回调:

static struct fb_ops lcd_fb_ops = {
    .owner          = THIS_MODULE,
    .fb_open        = lcd_fb_open,
    .fb_release     = lcd_fb_release,
    .fb_setcolreg   = lcd_fb_setcolreg,
    .fb_fillrect    = lcd_fb_fillrect,
    .fb_copyarea    = lcd_fb_copyarea,
    .fb_imageblit   = lcd_fb_imageblit,
    .fb_pan_display = lcd_fb_pan_display,
};

在 DRM/KMS 框架下,硬件驱动层需要实现 struct drm_crtc_helper_funcs、struct drm_plane_helper_funcs 等回调集:

static const struct drm_crtc_helper_funcs lcd_crtc_helper_funcs = {
    .mode_valid      = lcd_crtc_mode_valid,
    .mode_set_nofb   = lcd_crtc_mode_set_nofb,
    .atomic_enable   = lcd_crtc_atomic_enable,
    .atomic_disable  = lcd_crtc_atomic_disable,
    .atomic_flush    = lcd_crtc_atomic_flush,
};

2.6 整体数据流与架构关系图

下面的 SVG 图展示了从应用层到硬件驱动层的完整数据流与架构关系:

Linux 显示子系统架构与数据流 应用层 (User Space) Qt / GTK / Wayland Compositor / DirectFB / 裸机应用 系统调用:open() / ioctl() / mmap() / drmModeAtomicCommit() fbdev 框架 /dev/fbX struct fb_info / fb_ops ioctl: FBIOGET_VSCREENINFO DRM/KMS 框架 /dev/dri/card0 CRTC / Plane / Connector / FB Atomic Commit / Page Flip fbdev 驱动回调 fb_pan_display / fb_imageblit fb_setcolreg / fb_fillrect DRM/KMS 驱动回调 atomic_check / atomic_commit mode_set_nofb / enable/disable 硬件驱动层 (Hardware Driver) LCD 控制器寄存器操作 | DMA 引擎配置 | VSync 中断处理 时序生成 (HBP/HFP/VBP/VFP) | 面板初始化 (MIPI DSI/SPI) 显示硬件:LCD 面板 / HDMI 显示器 / MIPI DSI 屏

2.7 框架选择对比:fbdev vs DRM/KMS

对比维度 fbdev DRM/KMS
API 复杂度 低,基于文件操作 高,对象模型复杂
多平面支持 不支持 原生支持硬件平面
VSync 控制 有限,通过 FBIO_WAITFORVSYNC 完整,支持 Page Flip 和 Atomic Commit
GPU 协作 困难,需额外同步机制 原生支持,通过 GEM/DMA-BUF
画面撕裂 常见,需应用层双缓冲 可避免,硬件 VSync 同步
驱动开发量 较少,约 500-1500 行 较多,约 2000-5000 行
适用场景 简单 UI、开机 logo、低端 MCU 复杂 GUI、视频播放、游戏
学习路径建议: 初学者建议先从 fbdev 入手,理解帧缓冲、mmap、pan display 等基础概念。掌握 fbdev 后再学习 DRM/KMS,你会发现 CRTC、Plane、Atomic 等概念是对 fbdev 局限性的系统性解决。

2.8 本章小结

  • Linux 显示子系统分为应用层、显示框架层、内核驱动框架层、硬件驱动层四个层次。
  • fbdev 框架简单但功能有限,适合嵌入式入门和简单场景。
  • DRM/KMS 框架功能强大,支持多平面合成、原子更新、GPU 协作,是当前主流方案。
  • 硬件驱动层是最终操作显示控制器的代码,负责时序、DMA、中断、面板初始化。
  • 数据流从应用层通过系统调用进入内核框架,经驱动回调到达硬件,最终输出到显示面板。