3、驱动框架入门:Linux DRM框架与FBDEV框架的对比与选择

做显示驱动开发,绕不开两个框架:老牌的 FBDEV 和后来居上的 DRM。很多刚入行的朋友问我:「到底学哪个?项目里该用哪个?」

我的回答很直接:新项目无脑选 DRM,除非你在维护十年前的 legacy 代码。但这话不能说得太绝对,咱们得把背后的逻辑掰开揉碎了讲清楚。

3.1 FBDEV:那个曾经辉煌的「帧缓冲」时代

FBDEV(Framebuffer Device)是 Linux 内核里最古老的显示驱动框架。它的设计哲学很简单——给你一块内存区域,你把像素数据填进去,它负责显示出来。

我记得 2012 年做第一个嵌入式项目时,用的就是 FBDEV。那时候觉得这东西真方便,一个 open("/dev/fb0"),再 mmap 一下,就能直接操作屏幕了。

FBDEV 的核心模型:

  • 用户空间直接操作 framebuffer 内存
  • 内核只负责简单的显示时序控制
  • 不支持硬件图层叠加、不支持 VSYNC 同步
  • 多显示器管理基本靠「猜」

说白了,FBDEV 就是个「傻白甜」——你给它什么,它就显示什么。没有图层管理,没有合成引擎,更没有现代显示框架必备的 atomic 操作。

避坑指南:我曾经在一个项目里用 FBDEV 做双屏异显,结果发现要自己维护两个 framebuffer 的同步。更坑的是,切换分辨率时整个显示系统会闪一下,因为 FBDEV 不支持平滑的模式切换。那段时间真是被折腾得够呛。

3.2 DRM:现代显示驱动的「瑞士军刀」

DRM(Direct Rendering Manager)最初是为 GPU 设计的,后来逐渐演变成了 Linux 上标准的显示驱动框架。它的设计思路和 FBDEV 完全不同——DRM 把显示硬件抽象成一组可管理的资源。

你想想看,现代 SoC 的显示模块有多复杂?多个图层、旋转、缩放、色彩转换、HDR...FBDEV 那套「一块内存打天下」的思路根本玩不转。

DRM 引入了几个核心概念:

  • CRTC:显示控制器,负责生成显示时序
  • Encoder:编码器,把像素数据转换成特定接口信号(HDMI、DP、MIPI DSI 等)
  • Connector:物理连接器,代表实际的显示设备
  • Plane:硬件图层,每个 plane 可以独立配置
  • Framebuffer:内存中的像素数据缓冲区

这些概念组合起来,构成了一个完整的显示 pipeline。我习惯用一张图来理解它:

DRM 核心架构图 DRM 显示 Pipeline 架构 用户空间 (libdrm / Mesa / Weston) DRM Core (drm.ko) CRTC 显示控制器 Encoder 编码器 Connector 连接器 Plane 硬件图层 Framebuffer 像素缓冲区 硬件层 (GPU / Display Controller / PHY) 💡 每个 Connector 绑定一个 Encoder,每个 Encoder 绑定一个 CRTC Plane 可以叠加在 CRTC 上,实现硬件图层合成

DRM 最让我欣赏的一点是它的 atomic 接口。什么意思呢?就是你可以一次性提交多个显示状态的变更,内核保证这些变更要么全部生效,要么全部回滚。不会出现「屏幕一半刷新了,一半还是旧的」这种尴尬情况。

个人经验:我建议你在写 DRM 驱动时,优先使用 drm_atomic_helper 系列函数。这些 helper 帮你处理了大部分状态管理的脏活累活。我自己早期手写过 atomic commit 的逻辑,结果发现各种竞态条件,后来老老实实换回了 helper。

3.3 正面硬刚:DRM vs FBDEV 对比表

咱们直接上表格,一目了然:

对比维度 FBDEV DRM
内核版本 2.1 引入(1997年) 2.6.29 引入(2009年)
硬件抽象 仅 framebuffer CRTC + Encoder + Connector + Plane
图层支持 无(需用户空间合成) 硬件图层(Plane)
VSYNC 同步 不支持 原生支持(page flip)
多显示器 多个 /dev/fbX,各自独立 统一管理,支持克隆/扩展
模式切换 可能闪烁 atomic 平滑切换
GPU 加速 无 原生支持(GEM/Dumb Buffer)
用户空间 API ioctl + mmap libdrm (drmMode*)
维护状态 维护模式(不推荐新驱动) 活跃开发(推荐)

3.4 实际项目中的选择策略

好了,理论说完了,咱们聊聊实际项目里怎么选。我个人总结了一个简单的决策树:

  1. 新项目,新硬件 → 无脑 DRM。别犹豫,FBDEV 已经不适合现代显示硬件了。
  2. 维护老项目,硬件不支持 DRM → 继续用 FBDEV。但可以考虑加一个 drm_fb_helper 做兼容层。
  3. 极简场景,比如单色屏、低分辨率 → FBDEV 可能更简单。但说实话,现在连单片机都开始用 DRM 了。
  4. 需要 GPU 加速、游戏、Wayland → 必须是 DRM。FBDEV 根本跑不动现代图形栈。

我的建议:如果你正在写一个新的显示驱动,直接基于 DRM 的 drm_simple_display_pipe 开始。这个 helper 把 CRTC、Encoder、Connector 打包成了一个简单的管道,非常适合嵌入式场景。我最近三个项目都是这么干的,开发效率至少提升 30%。

3.5 代码示例:FBDEV vs DRM 的「Hello World」

咱们用代码说话。同样是初始化一个简单的显示驱动:

FBDEV 方式:

static int fb_probe(struct platform_device *pdev)
{
    struct fb_info *fbi;
    struct resource *res;
    
    fbi = framebuffer_alloc(0, &pdev->dev);
    if (!fbi)
        return -ENOMEM;
    
    // 设置固定参数
    fbi->fix.smem_start = res->start;
    fbi->fix.smem_len = resource_size(res);
    fbi->fix.type = FB_TYPE_PACKED_PIXELS;
    
    // 设置可变参数
    fbi->var.xres = 800;
    fbi->var.yres = 480;
    fbi->var.bits_per_pixel = 32;
    
    // 注册 framebuffer
    if (register_framebuffer(fbi) < 0) {
        framebuffer_release(fbi);
        return -EINVAL;
    }
    
    return 0;
}

DRM 方式(使用 simple pipe helper):

static const struct drm_simple_display_pipe_funcs pipe_funcs = {
    .check = my_pipe_check,
    .update = my_pipe_update,
    .enable = my_pipe_enable,
    .disable = my_pipe_disable,
};

static int drm_probe(struct platform_device *pdev)
{
    struct drm_device *drm;
    struct drm_connector *connector;
    
    drm = drm_dev_alloc(&my_driver, &pdev->dev);
    if (IS_ERR(drm))
        return PTR_ERR(drm);
    
    // 使用 simple pipe 替代手动管理 CRTC/Encoder
    ret = drm_simple_display_pipe_init(drm, &pipe, &pipe_funcs,
                                       drm_mode_format_list,
                                       ARRAY_SIZE(drm_mode_format_list),
                                       NULL, connector);
    
    drm_dev_register(drm, 0);
    return 0;
}

看到了吗?DRM 的代码量并没有比 FBDEV 多多少,但你能获得的硬件控制能力是天壤之别。

小技巧:调试 DRM 驱动时,多用 modetest 工具。它来自 libdrm 的测试套件,可以枚举所有 CRTC、Encoder、Connector 和 Plane,还能直接测试模式设置。我每次写完驱动,第一件事就是跑 modetest -M mydriver 看看硬件拓扑对不对。

3.6 总结:别纠结,选 DRM

说了这么多,其实就一句话:除非你有非常特殊的理由,否则新驱动请用 DRM。

FBDEV 就像当年的功能手机——打电话发短信没问题,但你要用它刷抖音、玩王者荣耀,那就别想了。DRM 是智能手机,功能强大,生态完善,而且还在不断进化。

我记得有一次给客户做方案评审,对方坚持要用 FBDEV,理由是「简单稳定」。我当场打开内核源码,把 drivers/gpu/drm 和 drivers/video/fbdev 两个目录的代码行数对比给他看——DRM 的维护者数量和提交频率是 FBDEV 的 10 倍以上。哪个更「稳定」,不言而喻。