尧图精选

RK平台调屏必读:U-Boot为何直接沿用内核DTS,显示初始化机制全解析

🕒 发布时间:2026/9/27 6:31:31 📁 来源:尧图网络
做 RK 平台调屏绝大多数工程师的第一反应就是打开内核设备树 DTS把新屏的时序、背光引脚、复位 GPIO 填进去然后编译 DTB、打包、烧录完事。但如果你是老平台转过来的一定会有一个条件反射式的疑问U-Boot 不也要出开机 logo、不也要初始化显示吗它的 DTS 为什么不用同步改这个问题我在嵌入式交流群里被问过不下十次。我自己在 RK3399、RK3568、RK3588 几个平台上反复验证过之后可以给你一句结论RK 平台 U-Boot 的显示初始化用的根本就不是 U-Boot 固件里自己编译进去的那份 DTS而是运行时从存储分区加载的内核 DTB。换句话说内核 DTS 就是屏参的“唯一事实来源”U-Boot 只是借这份 DTB 干活。这句话背后牵扯到的启动链路、显示通路、资源打包、驱动兼容才是真正值得理解的东西。这篇文章就把这套机制完整拆一遍先看 RK 启动链路和 U-Boot 显示驱动的工作方式再讲清楚“为什么不用改 U-Boot DTS”的底层逻辑然后给出一份可以直接照着抄的调屏实操流程最后把我踩过的坑整理成排查清单。手上有 RK 平台开发板或者正在做 LCD 点亮移植的朋友应该能直接拿来用。1. 先搞清楚U-Boot 阶段显示初始化到底“吃”的什么1.1 RK 平台启动链路从 BootROM 到 Kernel要理解这个问题得先把 RK 平台从上电到进入系统的完整链路画出来。整个过程大概是这样的芯片内部的 BootROM 启动。这段代码固化在 SoC 内部用户改不了它根据启动引脚的配置或者 OTP 信息决定从 eMMC、SD 卡还是 USB 下载模式下加载下一级镜像。加载 idbloader.img。这个镜像一般包含 DDR 初始化代码和 SPL甚至更老的 MiniLoader它的任务是把 DDR 培训跑通、初始化存储控制器然后把 U-Boot proper 加载到 DDR 里。进入 U-Boot proper。U-Boot 要完成外设初始化、读取存储分区、加载内核镜像和 DTB同时如果要显示开机 logo还要在这一步把显示通路点亮。跳转进入 Linux 内核。内核拿到 U-Boot 传过来的 DTB初始化 DRM/KMS 子系统完成最终的显示框架搭建。这里面最关键的一点是U-Boot 阶段就已经存在一次“完整”的显示初始化。也就是说在 kernel 还没来得及跑起来之前U-Boot 这个轻量级引导程序必须先自己搞定 VOP 显示控制器、MIPI DSI 或 LVDS 发送器、背光才能在屏幕上打出那个标志性的 rk 字样 logo。所以问题的本质就变成了U-Boot 做这次显示初始化时它的配置从哪里来1.2 U-Boot 里的“迷你 DRM”是怎么干活的RK 的官方 vendor U-Boot 里有一套对标内核 DRM 的轻量显示框架常见路径是drivers/video/drm/老一点的分支在drivers/video/rockchip/。它虽然简化了很多但思路和内核 DRM 是一致的VOPVideo Output Processor—— 负责合成图层、输出像素时钟encoder 侧 —— 对应 MIPI DSI、eDP、LVDS、RGB 这类输出接口panel —— 对应具体的屏参配置包括时序、初始化命令、供电和复位。这套代码在运行时会去解析一个 FDTFlattened Device Tree二进制文件从中找出显示子系统的节点然后完成一系列动作解析 VOP 和 output 的类型、解析 panel 节点的compatible、读取display-timings里的时序参数、读取 GPIO 控制复位和背光、按需执行 panel 初始化序列、最后打开背光把 logo 刷上去。关键问题来了这个 FDT 到底是哪份答案就是它来自 resource.img 或者 boot.img 里携带的那份内核 DTB。这个 DTB 在源码层面是从内核的arch/arm/boot/dts/rockchip/或者arch/arm64/boot/dts/rockchip/编译出来的也就是你平时打开改的那份 DTS 的产物。1.3 显示通路上哪些信息来自设备树为了把后面的事情讲清楚这里需要明确一下设备树在显示链路里承担的角色。你改屏参时本质上是在改下面这几类信息panel 节点的compatible告诉驱动你的屏是什么型号走哪套驱动逻辑display-timings水平/垂直有效像素、前后肩、同步脉宽、像素时钟、极性标志电源和 GPIO 控制power-supply、reset-gpios、enable-gpios、背光节点引用接口相关属性比如 MIPI DSI 的 lane 数、双向模式LVDS 的 format 和映射方式status状态确认这个显示节点有没有被okay打开。这一坨信息U-Boot 的迷你 DRM 需要内核 DRM 也需要。RK 的做法是让它们共用同一份 DTB。你改的是同一份源码编译出来的 DTB 被两边的驱动消费这就直接解决了我开头提到的疑问。2. 核心机制U-Boot 用的是内核 DTB不是自己编进固件的 DTS2.1 两份 DTS 的分工板级初始化和显示配置是两回事很多人在这里有个混淆点U-Boot 源码里明明也有一份 dts 文件路径通常在u-boot/arch/arm/dts/比如rk3568-evb.dts而且里面也能看到 LCD、panel 相关的节点为什么说显示配置不靠它这里要区分两个概念U-Boot 自带的 DTS 是给“U-Boot 自己”用的负责的是板级硬件初始化。串口、eMMC/SD 控制器、I2C 总线、PMIC、稳压器、GPIO 复用、DDR 参数这些是 U-Boot 活着就必须搞定的事情缺一块它都跑不起来。而显示初始化虽然也是 U-Boot 完成的但它读的是启动时从存储介质加载进来的“外部 DTB”这个 DTB 恰好就是内核生成的那份。也就是说UBoot 固件本身包含了两个设备树来源一个编译进 uboot.img 用于自身启动一个从 resource/boot 分区动态加载用于内核和显示。RK 这么设计是有意为之uboot.img 里的 DTS 要是也承载屏参那每次调屏就要重编 U-Boot重烧 uboot 分区工厂维护成本和风险都高得多。那为什么有人会在 U-Boot 源码的 dts 里看到完整 panel 节点因为 SDK 那边经常直接拷贝内核 dts 过去方便两边保持一致。但在实际运行时显示驱动使用的具体 FDT 指针是指向“外部加载的 DTB”的。所以你在 U-Boot 源码里改那几个屏参节点往往根本不会生效——因为它压根不是显示初始化消费的那份数据。2.2 DTB 的“一源多投”resource.img 与 boot.img 打包流程为了让你对这个机制有画面感我画一个简化的数据流注意这里没有动用任何图表工具纯粹是文本描述内核 DTS 源码rkxxxx-board.dts │ │ make dtbs ▼ rkxxxx-board.dtb │ │ 打包工具resource_tool / mkbootimg 等 ▼ resource.img 或 boot.img含 kernel dtb │ ├──────────────► U-Boot 启动时读取 → 显示初始化logo │ └──────────────► 内核启动时读取 → DRM/KMS 初始化framebuffer老平台RK3288、RK3399 早期的 Linux 4.4 SDK通常会生成一个resource.img里面装着 DTB 和开机 logo 资源U-Boot 从 resource 分区加载这份 DTB。新平台RK3568、RK3588 的 Android 12/13 SDK则普遍把 DTB 直接打包进boot.img或者单独拆一个dtb.img/dtbo.img分区U-Boot 解析 boot.img 之后拿到 DTB。不管是哪一代机制都一样U-Boot 和内核消费的是同一份“出厂配置”。你改了内核 DTS重新编译出来的 DTB 会同时驱动 U-Boot 的 logo 和内核的显示驱动两边看到的屏参自然就是一致的。2.3 为什么这个设计是合理的从工程角度看这其实是解决了一个很现实的维护问题——配置漂移。如果 U-Boot 和内核各管一套屏参你调屏的时候就得改两个地方。新员工或者调试过程中很容易出现“内核改成 1080x1920U-Boot 还停留在 720x1280”结果就是 U-Boot logo 阶段比例不对内核起来之后又正常或者反过来。一旦只有一个事实来源这种问题就从根上消失了。内核 DTS 改动后U-Boot 侧自动同步两边的时序、极性、背光配置一定是同一套值。这也解释了为什么现在的 RK 调屏文档里几乎不会教你碰 U-Boot 源码。2.4 什么时候真的需要碰 U-Boot把话说满容易误导人。虽然 DTS 层面不用改但下面几种场景确实需要动 U-Boot 工程老 BSP 平台。大概在 2016 年之前部分 RK BSP 的屏参是直接写在 U-Boot 的 board 文件里的比如board_init里塞一个struct lcd_panel或者用旧式CONFIG_LCD驱动。那种平台调屏确实要改 U-Boot 源码、重新编译烧写 uboot.img。如果你接手的是 RK3128、RK3288 的 Linux 3.10 老 SDK别拿本文的结论硬套。U-Boot mini DRM 的 panel 驱动不支持你的屏。U-Boot 里的 panel 驱动数量是有限的它靠compatible字符串去匹配。如果你的屏是某个小厂型号内核里虽然写了专用驱动但 U-Boot 那套面板驱动列表里没有这个 compatible它就可能识别失败logo 出不来。这时你有两条路在 DTS 里给 compatible 加一个兜底字符串比如simple-panel让 U-Boot 能匹配上或者把屏的初始化逻辑移植进 U-Boot 的drivers/video/drm/panels/目录。MIPI DSI 屏的初始化序列没有同步。这个坑非常隐蔽。很多 MIPI 屏不光要时序对上电之后还要发一串厂商私有初始化命令U-Boot 的 panel 驱动和内核的 panel 驱动是两份独立代码。如果内核驱动里塞了初始化序列而 U-Boot 驱动没有同步就会出现 U-Boot logo 黑屏或者花屏但内核起来后一切正常的现象。这种情况你改 DTS 没用得去同步 U-Boot 里的 panel 驱动代码。看明白这些前面“为什么不用改 U-Boot DTS”的问题就解开了。接下来就是我平时真正做调屏时的完整操作流程。3. 调屏实操改 DTS、编 DTB、重打包、烧录验证全流程3.1 找到并修改 panel 节点时序、电源、复位、背光一个都不能少先找到你当前板子的 DTS 文件。以 RK 平台常见结构为例SoC 级配置在rk3568.dtsi里板级配置在rk3568-evb.dts这样的文件里。做调屏时重点操作的是板级 DTS通过dsi0这样的引用去修改或追加节点。一个典型的 MIPI DSI 屏 panel 节点长这样dsi0 { status okay; panel0 { compatible boe,tv080wum-nl6, simple-panel; reg 0; backlight backlight; enable-gpios gpio1 RK_PA2 GPIO_ACTIVE_HIGH; reset-gpios gpio1 RK_PA1 GPIO_ACTIVE_HIGH; power-supply vcc_lcd; pinctrl-names default; pinctrl-0 lcd_reset_gpio; display-timings { native-mode timing0; timing0: timing0 { clock-frequency 70000000; hactive 1080; vactive 1920; hback-porch 20; hfront-porch 20; hsync-len 20; vback-porch 16; vfront-porch 8; vsync-len 4; hsync-active 0; vsync-active 0; de-active 0; pixelclk-active 0; }; }; }; };这里有几个容易忽略的细节。compatible的顺序有讲究内核匹配厂商专用驱动时看第一个U-Boot 的简单面板驱动会尝试匹配后面的simple-panel。如果你确定 U-Boot 里没有你的屏保留这个兜底字符串会让 U-Boot 更容易识别成功。display-timings节点即使在屏驱动不依赖它的情况下也建议保留。因为 U-Boot 的 mini DRM 简化程度高很多屏在 U-Boot 阶段不读厂商驱动里的 fixed-mode而是直接靠 DTS 里的 timing 出图。你只在内核驱动里改屏参但没往 DTS 写 timing 的话U-Boot logo 依然起不来。还要注意电源链。power-supply引用的稳压器在 U-Boot 阶段未必已经初始化好。如果 U-Boot 阶段出现屏幕黑着但内核起来后又好了的诡异现象先怀疑 U-Boot 下这个 regulator 有没有使能。3.2 像素时钟怎么算一个 1080x1920 屏的完整计算示例屏参里最常出错的不是有效分辨率而是clock-frequency。这个值是像素时钟pixel clock单位是 Hz它和刷新率的关系是这样的h_total hactive hfront_porch hback_porch hsync_len v_total vactive vfront_porch vback_porch vsync_len pixel_clock h_total * v_total * refresh_rate拿上面这个 1080x1920、60Hz 的屏来算h_total 1080 20 20 20 1140v_total 1920 8 16 4 1948pixel_clock 1140 × 1948 × 60 ≈ 133.2 MHz所以 DTS 里应该写clock-frequency 133200000。注意这个计算值只是估算最准的数值应该来自屏厂提供的 datasheet 里的推荐 PCLK。如果照搬 133.2MHz 结果屏幕滚动或者偏色可以微调 porch 参数而不是死磕时钟这里讲究“以屏厂规格为准”。如果走 MIPI DSI还要估算一下 lane 速率能不能扛得住。常见 MIPI DSI 是 4 条 data lane、24bit RGB那么每条 lane 的数据速率大约是lane_rate ≈ pixel_clock × 24 / lane_count代入 133.2MHz、4 lane133.2 × 24 / 4 ≈ 799 Mbps这在 MIPI D-PHY 常规 1Gbps 以内的速率下是安全的。如果你把分辨率调高到 4K 60Hz像素时钟可能飙到 500MHz 以上lane 速率破 2Gbps就得考虑用更多 lane 或者降低刷新率了。3.3 编译与打包常见 SDK 的操作命令DTS 改完之后接下来的流程是编译 DTB、打包、烧录。不同 SDK 的命令有差异但逻辑很固定。先单编 DTB 看语法有没有错cd kernel make ARCHarm64 rockchip_defconfig # 或者你板子对应的 defconfig make ARCHarm64 rk3568-evb.dtb编出来的 DTB 在arch/arm64/boot/dts/rockchip/下面。改完 DTS 后建议随手验证一下确认你要的节点真的被编进去了dtc -I dtb -O dts -o /tmp/check.dts arch/arm64/boot/dts/rockchip/rk3568-evb.dtb grep -A 20 panel0 /tmp/check.dts老平台 SDK比如 RK3399 Linux 4.4一般用 resource 工具把 DTB 打包进 resource.imgcd kernel make ARCHarm64 rk3399-evb.dtb ../rkbin/tools/resource_tool --update-dtb arch/arm64/boot/dts/rockchip/rk3399-evb.dtb新平台 SDKRK3568/RK3588 Android通常直接用 SDK 的集成脚本它会自动把新编译的 DTB 塞进 boot.img./build.sh kernel ./build.sh bootimage以你手头 SDK 的 build.sh 实际支持为准。我不建议裸敲 mkbootimg 手动拼 boot.img除非你完全清楚 ramdisk、dtb 在镜像里的 offset 布局否则很容易把内核起来后的根分区路径弄坏。3.4 烧录与串口日志验证怎么确认 U-Boot 拿到新屏参打包完成后烧录验证。老平台烧 resource 分区fastboot flash resource resource.img新平台烧 boot 相关分区fastboot flash boot boot.img # 如果有单独的 dtb 分区 fastboot flash dtb dtb.img插上调试串口RK 平台的串口默认波特率一般是 1500000不是 115200这个必须注意。开机盯着 U-Boot 日志重点找这几类信息Rockchip U-Boot版本行显示相关日志关键词rockchip display、vop、dsi、panel、backlight面板匹配结果类似panel: ...或者match panel的打印。如果能看到“分辨率 1080x1920”之类的信息说明 U-Boot 确实读到了新的 DTB。如果 U-Boot 阶段日志里panel相关行直接报找不到驱动那大概率就是 2.4 节里说的 compatible 匹配问题。更硬核的验证方法是直接在 U-Boot 命令行里直接打印 DTB 节点内容。进 U-Boot console 后可以这样操作fdt addr 0x1000000 fdt ls /dsife060000 fdt print /dsife060000/panel0/display-timings具体地址以你 SDK 的 fdt 加载地址为准一般 U-Boot 日志里会打印fdt_addr_r。这个操作能直接确认 U-Boot 手里的屏参是不是你改过的那套。4. 实战踩坑合集调屏最容易出的 6 类问题4.1 改了 DTS 却没生效先查打包和分区这是最高频的问题没有之一。症状很明显DTS 明明改了也重新编了 DTB烧完了屏幕还是老的参数或者完全不亮。我的排查顺序固定是确认当前板子实际加载的 DTB 是哪一个。有些 SDK 默认烧的是 EVB 通用 DTB你改的是自己的定制 DTS两者根本不是同一个文件。确认打包环节真的执行了。很多人只make dtbs忘了跑 resource_tool 或者 build.sh 的打包步骤烧进去的 resource.img 还是老的。确认烧录分区没有搞错。老平台的 resource 分区、新平台的 boot 分区、Android 的 dtbo 分区烧错位置是典型的“自以为烧了”但实际没生效。检查 Android 平台下有没有 dtbo overlay 在启动时覆盖了你的 panel 节点。最快定位方法就是把烧进去的镜像解包跑一次dtc -I dtb -O dts看节点内容别凭感觉猜。4.2 U-Boot 有 logo、内核黑屏的诡异现象U-Boot logo 正常说明屏参本身没问题、链路能点亮但内核起来后黑屏问题大概率不在屏参数据而在“两边消费的不是同一份 DTB”。这种情况我遇到过好几次最常见原因是老平台 U-Boot 从 resource 分区读 DTB而内核实际用的是 boot.img 里另一个 DTB。你只更新了 resource 分区内核那边自然是老参数。解决办法就是两个分区的 DTB 同步更新或者统一从同一个分区取。另一个高频坑是内核 DTS 里显示节点status没有置成okay或者面板 compatible 没有对应到内核自带 driver。注意U-Boot 的匹配逻辑简单内核的匹配逻辑严格内核驱动节点树里根本没有你这款屏的驱动时就算 DTB 里有这个屏的节点也照样不起来。4.3 内核正常但 U-Boot logo 黑的通用解法反过来内核 framebuffer 完全正常只有 U-Boot logo 黑屏这几乎可以直接锁定 U-Boot 的 panel 匹配能力不足。优先按下面顺序排查U-Boot 日志里找 panel 相关的报错确认是“没找到节点”还是“节点存在但驱动不匹配”。如果 compatible 不匹配尝试在 DTS 的 compatible 末尾追加simple-panel。前提是屏只需要标准时序就能出图。如果屏必须执行厂商初始化序列那必须把内核 panel 驱动里的 init sequence 同步到 U-Boot 对应驱动里。这个没法偷懒只能改 U-Boot 源码后重新编译 uboot.img。这里有个小技巧U-Boot 的简单面板驱动一般只看display-timings所以如果你不确定 U-Boot 支不支持你的屏先不要用自带 init sequence 的专用 compatible用simple-panel配合手动写完整的display-timings验证链路是否通链路通了你再去补厂商驱动。4.4 花屏、闪屏、偏色先怀疑时序与链路配置花屏和闪屏的坑比较碎我一般按优先级排查先查display-timings里的极性标志。hsync-active、vsync-active、de-active、pixelclk-active这四个 bit 错了常见表现就是画面整体偏移、竖线干扰或者色彩异常。这些值在屏厂规格书里通常有明确说明别凭经验猜。再查时钟频率是否在 VOP 和接口的允许范围内。像素时钟太高时会闪屏或者直接无图这时宁可降刷新率或者调整 porch 让 pixel clock 落回安全区间。接着查输出位宽和接口配置。比如屏是 RGB666你却配成了 RGB888MIPI DSI 实际用了 4 laneDTS 里却配置成 2 laneLVDS 屏的映射模式JEIDA/VESA配错了。这一类问题不会让你完全黑屏但一定画面不对。最后查背光 PWM 频率。PWM 频率太低会出现肉眼可见的背光闪烁这不是屏参问题但调屏时经常被误判成时序问题。4.5 背光和复位U-Boot 阶段黑屏的隐蔽原因很多新手量屏的接口信号全部正常VOP 时序也没问题但 U-Boot logo 就是黑的。这时候要分开看是背光没亮还是屏没出图U-Boot 阶段背光默认由 DTS 里的backlight节点驱动。背光节点的brightness-levels、default-brightness-level、PWM 通道、使能 GPIO 都必须完整。U-Boot 的背光驱动比较“小气”如果 PWM 节点在某些 SDK 里只被内核驱动的pwm-backlight使用U-Boot 那边可能要额外开对应的 CONFIG 选项。还有复位脚时序。MIPI DSI 屏上电时需要先拉低复位、再拉高并保持一段时间的延时这个时序在专用驱动里实现。如果 U-Boot 用的 simple-panel 驱动没有执行你的复位序列屏幕就可能一直处于复位状态。此时在 DTS 里检查reset-gpios是否配置必要时也要往 U-Boot 驱动里补时序。4.6 新老平台 DTS 策略差异速查表我把不同阶段的 RK 平台调屏方式做成一个表方便你接手老项目时快速判断“要不要碰 U-Boot”平台/时代屏参存放位置调屏需要动的文件U-Boot 是否需要重编老 BSPRK3128/RK3288Linux 3.10 等U-Boot 源码结构体或旧式 DTSU-Boot 源码 内核代码通常需要统一 DTS 之后RK3399Linux 4.4内核 DTS → resource.img DTB内核 DTS 重打包 resource通常不需要新平台RK3568/RK3588Android 12内核 DTS → boot.img / dtb.img内核 DTS 重打包 boot通常不需要Android 新平台叠加 dtbo overlayDTS overlay 覆盖基础 DTB修改 overlay 源文件不需要这张表不是绝对标准因为 SDK 分支太多总有特殊情况。我的建议是拿到任何一份新 SDK先花十分钟确认“U-Boot 显示驱动到底读哪份 DTB”而不是上来就猜。5. 我给调屏工程师的几个实操习惯最后说几个我自己的工作习惯算不上真理但确实帮我少踩了不少坑。第一改 DTS 之前先确认“同源”。我调屏前一定会先做一次镜像解包确认要烧的 DTB 就是我要改的那个 DTS 编出来的。这个动作只要两分钟却能避免 4.1 节里那种“烧了但没烧对”的低级错误。第二一次只改一个变量。调屏最忌讳同时改时序、改背光、改复位、改 compatible。我的习惯是先让 U-Boot logo 出来再管内核 framebuffer最后才调背光 PWM 频率和色彩效果。每个阶段只动一组参数出问题时才能快速判断原因。第三把 U-Boot 串口日志当成第一诊断手段。很多人遇到屏幕异常直接去翻内核 dmesg但 U-Boot 阶段的显示日志比内核直观得多。串口波特率记得切到 1500000别再拿 115200 去读读出来全是乱码容易误导人。第四凡是换屏先把新旧屏的display-timings和供电引脚整理成一张对照表再动手。屏厂给的数据手册基本都有时序表照着抄不会错。真正容易出问题的是那些不按规格书写的“兼容屏”这时唯一可靠的办法就是用simple-panel先把链路点亮再慢慢调参数。调屏这件事说难不难说简单也不简单。掌握了“内核 DTS 是唯一事实来源”这个核心认知后大部分问题其实都集中在 DTB 打包、compatible 匹配和初始化序列同步这三个环节。下次再有人问你为什么 RK 调屏不用改 U-Boot DTS你可以直接把这篇转给他然后补充一句真正要改 U-Boot 的场合往往比改 DTS 麻烦得多。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →