RK3399 Android 11:解决DTS中panel节点配置缺失导致的显示问题
1. 问题背景:当屏幕遇上"找不到家"的错误
最近在RK3399平台上调试Android 11系统时,遇到了一个让人头疼的显示问题。我们新增了一块MIPI屏幕作为副屏,在uboot阶段已经做好了兼容性配置,也在DTS文件中添加了panel@1节点,但新屏幕死活不亮。查看内核日志时,发现了几个关键错误信息:
dw-mipi-dsi ff960000.dsi: [drm:dw_mipi_dsi_bind]ERRORFailed to find panel1 or bridge: -19 rockchip-drm display-subsystem: failed to bind ff960000.dsi (ops dw_mipi_dsi_ops): -19 rockchip-drm display-subsystem: master bind failed: -19这些错误信息就像是在说:"我找不到新屏幕在哪里!"经过仔细排查,发现问题出在DTS配置上——我们漏掉了关键的ports节点配置。这就好比给新屏幕建好了房子(panel节点),却忘了修通到房子的路(ports节点),导致显示子系统找不到新屏幕的位置。
2. DTS文件结构解析:显示系统的"建筑图纸"
在RK3399平台上,DTS(Device Tree Source)文件就像是整个硬件系统的建筑图纸,它详细描述了各个硬件组件及其连接关系。对于显示系统来说,panel节点和ports节点的配置尤为重要。
panel节点相当于屏幕的"身份证",它包含了屏幕的初始化序列、时序参数等关键信息。而ports节点则像是连接屏幕和显示控制器的"道路系统",它定义了数据流的传输路径。常见的配置错误包括:
- 只添加panel节点却忘记ports节点
- ports节点中的endpoint连接关系配置错误
- address-cells和size-cells参数缺失或错误
- 端口编号(reg)与实际情况不匹配
在实际项目中,我发现很多开发者(包括我自己)常常只关注panel节点本身的配置,而忽略了ports节点的必要性。这就导致了很多"明明配置了却不起作用"的情况。
3. 完整解决方案:从零开始配置panel节点
下面以Kernel 4.19版本为例,详细介绍如何在DTS文件中正确添加新的panel节点。假设我们已经有panel@0(主屏),现在要添加panel@1(副屏)。
首先,确保dsi节点基础结构完整:
&dsi { status = "okay"; // 已有panel@0配置 panel@0 { // 原有配置保持不变 ... }; // 新增panel@1配置 panel@1 { compatible = "your-panel-compatible"; reg = <1>; // 注意这里的编号 backlight = <&backlight>; // 屏幕时序配置 display-timings { ... }; // 初始化序列 panel-init-sequence = [ ... ]; // 关键部分:ports配置 ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; panel_in_dsi: endpoint { remote-endpoint = <&dsi_out_panel>; }; }; }; }; // 在dsi节点中添加ports配置 ports { #address-cells = <1>; #size-cells = <0>; port@1 { reg = <1>; dsi_out_panel: endpoint { remote-endpoint = <&panel_in_dsi>; }; }; // 新增副屏port port@2 { reg = <2>; dsi_out_panel2: endpoint { remote-endpoint = <&panel2_in_dsi>; }; }; }; };这里有几个容易踩坑的地方:
- panel@1的reg值必须与port编号对应
- endpoint的remote-endpoint必须成对出现且名称匹配
- address-cells和size-cells不能遗漏
- 端口编号(reg)不能重复
4. 调试技巧:当屏幕还是不亮时怎么办
即使按照上面的步骤配置好了,有时候屏幕可能还是不亮。这时候就需要一些调试技巧了。我总结了几种常见情况和解决方法:
情况一:内核启动时没有任何报错,但屏幕不亮
- 检查/sys/kernel/debug/dri/0/DSI-1下的状态
- 使用示波器测量MIPI信号线是否有数据
- 确认背光控制是否正常
情况二:内核报错"failed to find panel"
- 确认panel的compatible字符串是否正确
- 检查驱动中是否有对应的of_device_id表项
- 使用of_dump工具查看解析后的设备树
情况三:屏幕有背光但无图像
- 检查display-timings参数是否正确
- 确认panel-init-sequence是否适合当前屏幕
- 测量MIPI时钟频率是否符合屏幕要求
一个实用的调试命令是:
cat /sys/kernel/debug/dri/0/DSI-1/status这个命令可以查看DSI接口的当前状态,包括时钟频率、lane数等关键信息。
5. 深入理解:RK3399显示子系统工作原理
要彻底解决显示问题,我们需要稍微深入了解一下RK3399的显示子系统架构。RK3399的显示系统主要由以下几个部分组成:
- VOP(Video Output Processor):负责图像合成和输出
- DSI(Display Serial Interface):MIPI显示接口控制器
- PHY:物理层接口
数据流向通常是: 应用层 → DRM/KMS → VOP → DSI → PHY → 面板
DTS中的ports节点就是用来描述这个数据通路的。当我们在panel节点中添加ports配置时,实际上是在建立VOP、DSI和面板之间的连接关系。
在RK3399上,一个常见的多屏配置架构是:
- VOP_LITE负责HDMI输出
- VOP_BIG负责MIPI DSI输出
- 通过display-subsystem节点将VOP和DSI关联起来
理解这个架构后,就能明白为什么ports节点的配置如此重要——它直接关系到数据能否正确流向目标屏幕。
6. 实际案例:解决ports节点缺失问题
回到最初的问题,当时我们在新增panel@1节点后,屏幕不亮且内核报错。通过对比正常工作的配置,发现了以下差异:
- 在panel@1节点中缺少ports配置
- dsi节点中虽然有port@1,但没有port@2
- endpoint的命名不一致
修正后的关键改动包括:
// 在panel@1中添加ports panel@1 { ... ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; panel2_in_dsi: endpoint { remote-endpoint = <&dsi_out_panel2>; }; }; }; }; // 在dsi的ports中添加新port ports { ... port@2 { reg = <2>; dsi_out_panel2: endpoint { remote-endpoint = <&panel2_in_dsi>; }; }; };这个案例让我深刻认识到,在RK3399的显示系统配置中,ports节点和panel节点是同等重要的。就像交通系统中的道路和建筑物一样,缺一不可。
7. 进阶建议:多屏配置的注意事项
当在RK3399上配置多个显示面板时,还需要注意以下几点:
- 时钟配置:确保dsi节点的clock和clock-names属性正确
clocks = <&cru PCLK_MIPI_DSI0>, <&cru SCLK_MIPI_DSI0_24M>; clock-names = "pclk", "sclk";- 电源管理:正确配置panel的电源相关属性
power-supply = <&vcc_lcd>; enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>; reset-gpios = <&gpio4 30 GPIO_ACTIVE_LOW>;- 显示模式:在多屏场景下,需要正确配置display-subsystem的路由
display-subsystem { ports = <&vopb_out>, <&vopl_out>; route { route_dsi: route-dsi { connect = <&vopb_out_dsi>; }; }; };- 内存带宽:多屏使用时需要注意带宽分配,避免出现性能问题
在实际项目中,我建议每次修改DTS后,都使用dtc工具验证语法是否正确:
dtc -I dtb -O dts -o extracted.dts /boot/dtb.img这样可以避免因为语法错误导致的内核启动问题。
