📺B站 嵌入式孙老师:博主个人介绍
📘博主书籍-京东购买链接*:Yocto项目实战教程
📘加博主微信,进技术交流群:jerrydev
Jetson 启动 LOGO 无黑屏切换:从 Bootloader 到 APP 首屏的显示接力机制解析
前言
最近在分析 Jetson Thor 的 UEFI 启动显示配置时,看到两个比较关键的选项:
SOC Display Hand-Off Mode <Always> SOC Display Hand-Off <simplefb / efifb>这个配置看起来只是和启动 LOGO 有关,但它背后对应的是一个很典型的产品启动体验问题:
Jetson 从 Bootloader 阶段启动到 Linux,再到 APP 首屏显示,如何避免中间黑屏、闪屏?
这篇文章结合我的理解,梳理一下 Jetson 启动显示的接力过程,以及simplefb、efifb、simpledrm、DRM/KMS在这个过程中分别起什么作用。
1. 启动显示的核心问题
很多嵌入式 Linux 设备启动时,显示流程可能是这样:
上电 ↓ Bootloader 显示 LOGO ↓ Linux Kernel 启动 ↓ 屏幕黑一下 ↓ 图形系统启动 ↓ APP 首屏显示用户看到的现象就是:
LOGO 消失 屏幕黑屏 画面闪烁 APP 起来后重新显示更理想的启动体验应该是:
上电 ↓ Bootloader 显示 LOGO ↓ Linux early display 接住画面 ↓ DRM/KMS 正式显示驱动接管 ↓ APP 首屏自然显示也就是说,显示画面不是每个阶段重新开始,而是一个阶段接一个阶段完成显示接力。
2. Jetson 启动显示链路
Jetson 的启动链路可以简化成:
BootROM ↓ MB1 / MB2 ↓ UEFI ↓ Linux Kernel ↓ rootfs ↓ APP如果只看显示相关部分,可以简化为:
UEFI LOGO ↓ simplefb / efifb / simpledrm ↓ DRM / KMS ↓ Weston / Qt / APP更直观一点:
┌──────────────────────────────┐ │ Bootloader / UEFI │ │ 初始化显示硬件,绘制 LOGO │ └───────────────┬──────────────┘ │ │ Display Hand-Off ↓ ┌──────────────────────────────┐ │ Linux Early Display │ │ simplefb / efifb / simpledrm │ │ 继续使用已有 framebuffer │ └───────────────┬──────────────┘ │ │ DRM/KMS 接管 ↓ ┌──────────────────────────────┐ │ Linux 正式显示驱动 │ │ 建立完整显示管线 │ └───────────────┬──────────────┘ ↓ ┌──────────────────────────────┐ │ 用户空间 APP │ │ Weston / Qt / 应用首屏 │ └──────────────────────────────┘这里要注意一点:
Display Hand-Off 严格来说主要发生在 UEFI 到 Linux early display 阶段。
从 Linux Kernel 到 APP 首屏,则是 DRM/KMS 和用户空间显示服务继续完成显示接力。
所以这里说的“Bootloader 到 APP 首屏”,指的是完整用户可见显示链路,而不是 Bootloader 直接把 framebuffer 交给 APP。
3. 什么是 Display Hand-Off?
Display Hand-Off 可以理解为:
Bootloader 把已经初始化好的显示现场交给 Linux,Linux 早期阶段继续使用它。
在 UEFI 阶段,系统通常已经完成了这些工作:
初始化显示控制器 初始化 HDMI / DP / eDP 分配 framebuffer 绘制启动 LOGO 启动显示扫描输出如果 Linux 启动后立刻重置显示硬件,就容易出现黑屏或闪屏。
Display Hand-Off 的思路是:
UEFI 已经把屏幕点亮 Linux early boot 先不要打断它 而是继续使用 UEFI 留下来的 framebuffer它通常需要传递这些关键信息:
| 信息 | 作用 |
|---|---|
| framebuffer 地址 | Linux 知道图像数据在哪里 |
| framebuffer 大小 | Linux 知道显示内存范围 |
| 分辨率 | Linux 知道屏幕宽高 |
| stride / pitch | Linux 知道每一行占多少字节 |
| pixel format | Linux 知道像素格式 |
| 显示状态 | 尽量保持当前显示不中断 |
一句话总结:
Display Hand-Off 的本质,就是让 Linux 接住 Bootloader 已经点亮的那一帧画面。
4. 为什么不能直接让 Linux 重新初始化显示?
当然可以重新初始化,但代价是启动显示体验变差。
重新初始化显示通常涉及:
关闭当前显示输出 释放旧 framebuffer 重新配置 display controller 重新配置 HDMI / DP / eDP 重新申请显示 buffer 重新设置显示时序 重新点亮屏幕这个过程中容易出现:
黑屏 闪屏 LOGO 消失 显示器重新同步 HDMI/DP 信号重新识别对开发板调试来说,这可能只是一个小现象;但对产品来说,这是很明显的体验问题。
例如车载、机器人、工业屏、边缘 AI 终端等设备,用户更希望看到:
上电就有画面 启动 LOGO 平滑过渡 APP 首屏自然接上 中间没有明显黑屏所以 Display Hand-Off 并不是单纯为了显示一张 LOGO,而是为了让启动显示链路更连续。
5. simplefb、efifb、simpledrm 的作用
在 Jetson UEFI 显示交接中,经常会看到几个概念:
simplefb efifb simpledrm它们的共同目标是:
在正式 DRM/KMS 显示驱动加载之前,Linux 先继续显示 Bootloader 留下来的 framebuffer。
可以简单对比:
| 名称 | 信息来源 | 作用 |
|---|---|---|
| simplefb | Device Tree | 通过 DT 描述 framebuffer |
| efifb | UEFI GOP | 通过 EFI 图形信息接管 framebuffer |
| simpledrm | 固件 framebuffer | 用 DRM 框架接管早期显示 |
6. simplefb:通过 Device Tree 交接
simplefb通常依赖 Device Tree。
大致流程是:
UEFI / Bootloader ↓ 把 framebuffer 信息写入 Device Tree ↓ Linux 解析 /chosen 下的 framebuffer 节点 ↓ simplefb 接管显示示意节点如下:
chosen { framebuffer { compatible = "simple-framebuffer"; reg = <0x0 0x90000000 0x0 0x00800000>; width = <1920>; height = <1080>; stride = <7680>; format = "a8r8g8b8"; }; };这里最关键的是:
地址要对 大小要对 分辨率要对 stride 要对 format 要对否则 Linux 虽然接管了 framebuffer,但可能出现花屏、错位、颜色异常甚至黑屏。
7. efifb:通过 UEFI GOP 交接
efifb更偏 UEFI 标准路径。
UEFI 中有一个图形输出协议:
GOP: Graphics Output Protocol它的逻辑是:
UEFI GOP 初始化显示 ↓ UEFI 绘制 LOGO ↓ Linux 读取 EFI framebuffer 信息 ↓ efifb 继续显示所以在 UEFI 平台上,efifb是比较自然的 early display 路径。
8. DRM/KMS:正式显示驱动接管
simplefb、efifb、simpledrm只是早期显示接管,它们不是最终显示方案。
Linux 正式起来后,还是要由 DRM/KMS 显示驱动建立完整显示管线:
plane crtc encoder connector mode timing framebuffer display controller完整接力关系可以理解成:
Bootloader framebuffer ↓ simplefb / efifb / simpledrm ↓ DRM/KMS 正式显示驱动 ↓ Weston / Qt / APP如果 DRM/KMS 接管过程不平滑,即使前面的 Display Hand-Off 正常,后面也可能出现短暂闪屏。
所以完整的“无黑屏切换”,实际包含两次关键交接:
UEFI → Linux early framebuffer Linux early framebuffer → DRM/KMS9. APP 首屏和 Display Hand-Off 的关系
这里有一个容易误解的点:
Display Hand-Off ≠ Bootloader 直接交给 APPAPP 是用户空间程序,它要等下面这些阶段完成后才会启动:
Linux Kernel 启动 rootfs 挂载 init/systemd 启动 图形服务启动 APP 启动所以更准确的链路是:
Bootloader LOGO ↓ Display Hand-Off ↓ Linux early framebuffer ↓ DRM/KMS ↓ Weston / Qt ↓ APP 首屏也就是说:
| 阶段 | 负责内容 |
|---|---|
| UEFI | 点亮屏幕,显示 LOGO |
| Display Hand-Off | 把 framebuffer 信息交给 Linux |
| simplefb / efifb | Linux 早期接住画面 |
| DRM/KMS | 正式显示驱动接管 |
| APP | 显示最终业务界面 |
10. Jetson 中的关键配置
在 Jetson UEFI 中,类似下面的配置很关键:
SOC Display Hand-Off Mode <Always> SOC Display Hand-Off <simplefb / efifb>可以这样理解:
| 配置 | 含义 |
|---|---|
SOC Display Hand-Off Mode | 是否执行显示交接 |
Always | 始终尝试把 UEFI 显示状态交给 Linux |
SOC Display Hand-Off | 选择交接方式 |
simplefb | 通过 Device Tree 交接 |
efifb | 通过 EFI framebuffer 交接 |
如果没有正确开启,可能出现:
UEFI 阶段 LOGO 正常 Linux Kernel 一启动屏幕黑掉 等 DRM/KMS 或 APP 起来后才重新显示如果配置正确,显示链路会更加平滑:
UEFI LOGO ↓ Linux early display 接住 ↓ DRM/KMS 接管 ↓ APP 首屏显示11. 为什么开启后仍然可能黑屏?
Display Hand-Off 不是万能开关。实际 bring-up 中,仍然可能遇到黑屏、花屏或闪屏。
11.1 framebuffer 信息错误
常见问题:
framebuffer 地址错误 framebuffer size 不够 stride 不正确 format 不匹配 width / height 不匹配可能现象:
花屏 颜色异常 画面错位 LOGO 显示异常11.2 framebuffer 内存被覆盖
UEFI 绘制 LOGO 使用的是一块内存。
如果 Linux 启动后没有正确保留这块内存,它可能被内核重新分配,导致:
LOGO 被破坏 画面突然变乱 启动中途花屏11.3 Linux 没有对应 early framebuffer 驱动
需要确认内核是否支持相关驱动,例如:
CONFIG_FB_SIMPLE CONFIG_FB_EFI CONFIG_DRM_SIMPLEDRM如果 Bootloader 传了信息,但 Linux 没有驱动接收,屏幕仍然可能黑。
11.4 DRM/KMS 接管时闪屏
即使 early display 阶段正常,DRM/KMS 正式接管时也可能重新配置显示管线,导致短暂黑屏或闪屏。
这类问题通常要继续看 DRM/KMS 日志和显示模式设置过程。
12. 工程调试方法
遇到 Jetson 启动显示问题,可以按阶段排查。
| 现象 | 可能阶段 | 排查方向 |
|---|---|---|
| 上电后一直无显示 | UEFI 前后 | 电源、屏幕、HPD、接口、pinmux |
| UEFI 有 LOGO,Linux 启动后黑 | Display Hand-Off | simplefb/efifb、DT、kernel config |
| LOGO 花屏 | framebuffer 参数 | 地址、stride、format、size |
| Kernel 早期正常,后面闪一下 | DRM/KMS | mode setting、connector、驱动切换 |
| 系统起来后无 APP 画面 | 用户空间 | Weston、Qt、APP、自启动服务 |
| APP 首屏慢 | rootfs/应用 | systemd、图形服务、APP 初始化 |
常用命令:
dmesg|grep-iframebufferdmesg|grep-isimpledmesg|grep-iefifbdmesg|grep-isimpledrmdmesg|grep-idrm查看 framebuffer:
cat/proc/fbls-l/dev/fb*如果走 simplefb,可以检查 Device Tree:
ls/proc/device-tree/chosen/ dtc-Idtb-Odts-oout.dts kernel.dtbgrep-iframebuffer out.dtsgrep-isimple-framebuffer out.dts13. 我的理解
以前看启动 LOGO,容易理解成:
Bootloader 显示一张图 Linux 起来后再显示一张图 APP 起来后再显示自己的界面但 Jetson UEFI 的 Display Hand-Off 让我更清楚地看到:
好的启动显示体验,不是每个阶段各画各的,而是显示状态在不同阶段之间连续传递。
可以把它理解成一次接力:
UEFI: 我先点亮屏幕,显示 LOGO。 Linux early display: 我先接住 framebuffer,不让屏幕黑。 DRM/KMS: 我建立正式显示管线,接管长期显示。 APP: 我显示业务首屏,替换启动 LOGO。这就是从 Bootloader 到 APP 首屏的显示接力机制。
总结
Jetson 启动 LOGO 无黑屏切换,本质依赖一条完整的显示接力链路:
Bootloader / UEFI ↓ Display Hand-Off ↓ Linux early framebuffer ↓ DRM/KMS ↓ APP 首屏其中最关键的一步是:
UEFI 把 framebuffer 信息交给 Linux early display这个过程可能通过:
simplefb efifb simpledrm完成。
它解决的问题是:
Linux 启动时不要打断 Bootloader 已经点亮的显示带来的价值是:
减少黑屏 减少闪屏 提升启动体验 方便 bring-up 调试 让 Bootloader 到 APP 首屏显示更加连续一句话总结:
Jetson 启动 LOGO 无黑屏切换,不是某个阶段单独完成的,而是 Bootloader、Linux early display、DRM/KMS 和 APP 首屏之间完成了一次连续的显示接力。