📺 B站 嵌入式孙老师:博主个人介绍
📘 博主书籍-京东购买链接:Yocto项目实战教程
📘 加博主微信,进技术交流群:jerrydev
最近在调试 RK3588 开机 Logo 时,我遇到了一组看起来互相矛盾的现象:eDP Link Training 已经成功,VOP2 也配置到了 2560×1600@60Hz,Kernel DRM 最终能够正常启动,但 U-Boot 阶段的 Logo 仍然无法显示。

排查过程中,我先后碰到了几个比较典型的问题:
- U-Boot 已经读取到 BMP,为什么屏幕仍然没有 Logo?
- Linux Kernel 尚未启动,U-Boot 是如何独立完成显示初始化的?
- U-Boot 使用的 Framebuffer,与 Linux 中的
/dev/fb0是什么关系? - VOP2、Framebuffer、eDP 和 DRM 分别负责什么?
- Kernel DRM 重新初始化显示时,U-Boot Logo 为什么可以继续保持?
- 一张约 11.7 MiB 的 2560×1600 BMP,为什么在 32 MiB 内存配置下仍然解码失败?
这些问题表面上分散在 U-Boot、DRM、VOP2、Framebuffer 和背光等不同模块中,实际都属于同一条开机显示链路。如果没有先理清各层之间的关系,很容易在 DTS、显存、Kernel 驱动和硬件之间反复排查。
最终定位发现,这次 Logo 不显示的直接原因并不在 VOP2 或 eDP,而是大分辨率 BMP 解码时,U-Boot 通用堆的峰值内存不足。
这篇文章就是对这次实战调试的一次总结。下面从驱动原理入手,梳理 U-Boot 如何显示 Logo、VOP2 如何读取 Framebuffer,以及画面如何平滑交给 Kernel DRM。
一、U-Boot 为什么能够显示 Logo
显示不是 Kernel 的专属能力
显示一张静态图片,本质上只需要完成以下工作:
读取并解码 BMP
↓
写入 DDR Framebuffer
↓
配置 VOP2 读取 Framebuffer
↓
配置 eDP 和 Panel
↓
打开背光
Rockchip SDK 的 U-Boot 内置了一套精简的 DRM 显示驱动,可以完成以上流程。因此,在 Linux Kernel 尚未启动时,U-Boot 就能够显示开机 Logo。
相关主流程可以简化为:
rockchip_show_logo()
├── load_bmp_logo() 读取并解码BMP
└── display_logo() 配置Framebuffer和VOP2
U-Boot 负责准备第一帧画面。配置完成后,VOP2 会自行从 DDR 中持续读取像素并输出,CPU 不需要逐帧刷新。
U-Boot 可以复用 Kernel DTB
在 Rockchip SDK 中,U-Boot 可以使用 Kernel 构建出的 DTB,从中读取:
- Panel 显示时序;
- VOP2 到 eDP 的显示通路;
- Panel enable GPIO;
- PWM backlight;
- regulator 和 pinctrl 配置。
但是,U-Boot 和 Kernel 使用的并不是同一套驱动:
同一份DTB
├── U-Boot自己的DRM、GPIO、PWM驱动解析
└── Kernel自己的DRM、GPIO、PWM驱动解析
所以准确来说:
配置可以共用,驱动不能共用,显示硬件由 U-Boot 和 Kernel 分阶段接管。
这也意味着,如果 DTB 中已经使能 pwm-backlight,U-Boot 显示 Logo 时就可能拉高背光 GPIO、启动 PWM 和背光电源,而不是等到 Kernel 启动后才操作背光。
二、Framebuffer、VOP2 与 DRM 的关系
VOP2 是显示控制器硬件
VOP2 全称为 Video Output Processor 2,是集成在 RK3588 SoC 内部的显示控制器硬件。
它位于 DDR Framebuffer 和显示接口之间:
CPU / GPU / RGA生成画面
↓
DDR Framebuffer
↓
VOP2 Plane读取图像
↓
VOP2 VP完成合成、缩放和格式转换
↓
eDP / HDMI / DP / MIPI-DSI
↓
屏幕
Framebuffer 只是一块保存像素的内存。
如果没有 VOP2 这样的显示控制器持续读取,即使 Framebuffer 中已经存在一张完整图片,也不会自动显示到屏幕上。
VOP2 主要负责:
- 从 DDR 读取 Framebuffer;
- 管理多个显示 Plane;
- 图层合成和 Alpha 混合;
- 图像缩放;
- RGB、YUV 格式转换;
- 色彩空间转换;
- 生成显示时序;
- 把最终画面送到 eDP、HDMI、DP 或 MIPI-DSI。
下面是一组典型的 U-Boot 日志:
VOP update mode to: 2560x1600p60, type: eDP0 for VP0
VOP VP0 enable Esmart0[2560x1600->2560x1600@0x0] fmt[0] addr[0xedf00000]
Link Training success!
可以拆解为:
| 日志内容 | 含义 |
|---|---|
2560x1600p60 | 输出分辨率为2560×1600@60Hz |
eDP0 for VP0 | VOP2的VP0连接到eDP0 |
Esmart0 | 使用VOP2的一个显示Plane |
2560x1600->2560x1600 | 输入和输出尺寸相同,没有缩放 |
addr[0xedf00000] | Framebuffer物理地址 |
Link Training success | eDP高速链路训练成功 |
这说明 U-Boot 已经把 Framebuffer 地址、显示尺寸和输出通路配置给 VOP2,并成功建立了 eDP 链路。
Framebuffer 不等于 /dev/fb0
Framebuffer 的广义含义是一块存放像素的内存,而 /dev/fb0 是 Linux 传统 fbdev 框架暴露出来的设备节点。
现代 RK3588 系统通常使用 DRM/KMS:
Weston / 图形应用
↓
/dev/dri/card0
↓
DRM GEM / DMA-BUF
↓
DRM Framebuffer + Plane + CRTC
↓
VOP2
因此:
没有 /dev/fb0
不代表没有 Framebuffer
也不代表 Kernel DRM 没有工作
/dev/fb0 如果存在,很多时候也是 DRM fb-helper 提供的兼容接口。现代 Weston 系统一般直接使用 /dev/dri/card0,不依赖 /dev/fb0。
U-Boot Framebuffer 和 Kernel DRM 的效率
U-Boot Framebuffer 与 Kernel DRM Framebuffer 的底层原理相同:
DDR Framebuffer → VOP2 → eDP → LCD
对于一张静态 Logo,VOP2 开始工作后,由硬件持续扫描 Framebuffer。无论最初是 U-Boot 还是 Kernel DRM 配置,持续输出效率都没有本质区别。
Kernel DRM 的优势主要体现在:
- 多 Buffer 切换;
- Atomic Commit;
- VBlank 同步;
- DMA-BUF 零拷贝;
- 多 Plane 合成;
- GPU、RGA、VPU 共享 Buffer;
- 动态电源和带宽管理。
因此,U-Boot 适合尽早显示静态 Logo,Kernel DRM 则适合桌面、动画和视频等动态显示场景。
三、Logo 如何从 U-Boot 交给 Kernel
两套驱动之间传递的是物理内存
U-Boot 和 Kernel 是两个独立的软件阶段,它们不能共享驱动对象,但是可以通过 DTB 传递 Logo Framebuffer 的物理地址和大小。
设备树中通常预留一个占位节点:
reserved-memory {
drm_logo: drm-logo@0 {
compatible = "rockchip,drm-logo";
reg = <0x0 0x0 0x0 0x0>;
};
};
这里的:
reg = <0x0 0x0 0x0 0x0>;
只是一个占位值。
U-Boot 成功解码并显示 Logo 后,会在跳转 Kernel 前,把真实的 Framebuffer 地址和大小写回 DTB。
完整过程如下:
U-Boot读取并解码Logo
↓
将像素写入DDR
↓
配置VOP2开始显示
↓
把Logo地址和大小写入Kernel DTB
↓
Kernel保护drm-logo reserved-memory
↓
Kernel DRM初始化并接管VOP2
↓
Weston提交新的DRM Buffer
↓
Kernel释放U-Boot Logo内存
实测日志中可以看到:
VOP ... addr[0xedf00000]
随后 U-Boot 打印:
## reserved-memory:
drm-logo@0: addr=edf00000 size=fa0000
Kernel 接管完成后又打印:
[ 7.303767] Freeing drm_logo memory: 16000K
三处信息完全对应:
Framebuffer地址:0xEDF00000
Framebuffer大小:0xFA0000
换算大小:16000 KiB
说明这块内存最初由 U-Boot 保存并显示 Logo,Kernel 启动时先保护它,完成 DRM 接管后再释放。
无缝显示不等于 Kernel 不初始化 DRM
所谓无缝显示,不是:
U-Boot初始化显示
↓
Kernel跳过DRM初始化
而是:
U-Boot建立显示
↓
Kernel启动时保持旧画面
↓
Kernel DRM重新初始化并接管
Linux Kernel 仍然必须初始化自己的 DRM 驱动,因为后续 Weston、Qt 和视频应用都需要完整的 DRM/KMS 能力。
为了减少黑屏和闪屏,需要满足几个条件:
- U-Boot 跳转前不能主动关闭 VOP2;
- eDP、Panel 和背光状态需要保持;
- U-Boot 与 Kernel 使用相同的分辨率和时序;
- Logo Framebuffer 必须通过
reserved-memory保护; - Kernel DRM 接管前不能覆盖这块内存。
背光如何接管
背光不像 Framebuffer 那样有一块内存可以传递,它主要依赖硬件状态保持:
U-Boot:
GPIO = High
PWM = Enable
背光电源 = On
↓
进入Kernel时保持状态
↓
Kernel pwm-backlight驱动重新接管
如果 Kernel probe 时执行了一次:
Disable → 重新配置 → Enable
屏幕就可能短暂变黑。
因此,Logo 无缝显示不仅取决于 Framebuffer,还取决于 Panel、PWM、GPIO 和背光电源的接管过程。
四、一次 2K Logo 解码失败的实战分析
BMP 文件本身没有损坏
本次使用的是一张 2560×1600、24bpp BMP,文件大小为:
2560 × 1600 × 3 + 54
= 12,288,054 Byte
≈ 11.7 MiB
U-Boot 日志为:
DBG: bmp[logo.bmp] read len=12288054
实际读取长度与理论文件大小完全一致。
进一步检查 BMP Header:
42 4d
对应 ASCII 字符:
BM
宽度、有效高度、位深和像素偏移也都正确,因此可以排除文件损坏和读取不完整的问题。
解码后的内存比BMP文件更大
虽然原始 BMP 只有约 11.7 MiB,但 U-Boot 解码后统一按 32bpp 保存:
2560 × 1600 × 4
= 16,384,000 Byte
≈ 15.625 MiB
一次 Logo 解码过程中,同时存在三块 Buffer:
| Buffer | 大小 | 所属内存池 |
|---|---|---|
| 原始BMP读取缓冲 | 20 MiB | U-Boot通用堆 |
| libnsbmp解码缓冲 | 约15.625 MiB | U-Boot通用堆 |
| 最终显示Buffer | 约15.625 MiB | DRM保留内存池 |
关键问题在于,原始 BMP 缓冲和 libnsbmp 解码缓冲会同时存在:
20 MiB + 15.625 MiB
≈ 35.625 MiB
而平台原来的 U-Boot 通用堆只有:
#define CONFIG_SYS_MALLOC_LEN (32 << 20)
因此在 libnsbmp 执行:
calloc(width * height, 4);
时,通用堆已经无法再提供约 15.6 MiB 连续内存,最终返回:
BMP_INSUFFICIENT_MEMORY
这就是 Logo 已经成功读取,但仍然无法完成显示的直接原因。
两个32 MiB不是同一块内存
这里最容易混淆的是两个配置:
| 配置 | 用途 |
|---|---|
CONFIG_SYS_MALLOC_LEN | U-Boot通用堆 |
CONFIG_DRM_MEM_RESERVED_SIZE_MBYTES | DRM最终显示Buffer使用的内存池 |
它们即使都配置为 32 MiB,也属于两个不同的内存池。
本次 BMP 解码失败发生在 U-Boot 通用堆中,因此单独增大 DRM 显存池并不能解决问题。
同样,MAX_IMAGE_BYTES 主要决定原始 BMP 读取缓冲的申请上限;解码后的:
宽 × 高 × 4
是另外一笔内存。
所以不能只根据 BMP 文件大小判断 U-Boot 堆是否足够。
修复方法
对于 DDR 容量充足的 RK3588,最直接的处理是将板级 U-Boot 通用堆提高到 64 MiB:
#undef CONFIG_SYS_MALLOC_LEN
#define CONFIG_SYS_MALLOC_LEN (64 << 20)
修改后重点验证:
1. BMP读取长度正确
2. 不再出现failed to parse bmp
3. VOP2打印Plane和Framebuffer地址
4. eDP Link Training成功
5. Kernel识别到非零的drm-logo reserved-memory
6. Kernel DRM接管后释放旧Logo内存
7. 屏幕实际显示正常且没有闪屏
如果已经看到:
VOP update mode...
VOP VP0 enable...
Link Training success!
但屏幕仍然黑,就不应该继续只检查 BMP 和 DRM,而应转向:
- Panel供电;
- Panel enable GPIO;
- 背光 enable GPIO;
- PWM输出;
- 背光升压电源;
- eDP实际硬件连接。
总结
RK3588 的开机 Logo 显示可以归纳为一条清晰的驱动链路:
BMP文件
↓ U-Boot读取和解码
DDR Framebuffer
↓ VOP2扫描
eDP / Panel显示
↓ DTB传递物理内存
Kernel DRM接管
其中:
- Framebuffer 是保存像素的内存;
- VOP2 是读取、处理并输出画面的显示控制器硬件;
- U-Boot DRM 负责启动早期显示;
- Kernel DRM 负责系统运行阶段的完整显示管理;
/dev/fb0只是传统 fbdev 接口,并不是现代 DRM 显示的必要条件;- 无缝显示依赖 Logo 内存保护和硬件状态接管,而不是 Kernel 跳过初始化。
这次问题的根因也说明,大分辨率 Logo 调试不能只看图片文件大小,还需要同时计算:
原始文件读取缓冲
+ 解码中间缓冲
+ 最终显示Buffer
把 U-Boot、Framebuffer、VOP2、eDP 和 Kernel DRM 这几个层次分开以后,很多黑屏、闪屏和 Logo 解码失败问题,就可以沿着显示链路逐层定位,而不必在 DTS、DRM 和硬件之间反复猜测。
1307

被折叠的 条评论
为什么被折叠?



