幽灵Bug调试实录:当 Rockchip DSI 接口只有背光,没有图像

道锋潜鳞
2025-10-29 / 0 评论 / 10 阅读 / 正在检测是否收录...

幽灵Bug调试实录:当 Rockchip DSI 接口只有背光,没有图像

在嵌入式 Linux 开发中,没有什么比“点屏”更能带来即时满足感了。相反,也没有什么比“背光已亮,屏幕一片漆黑”更令人沮c丧。

这个经典的故障现象意味着:电源通了、复位信号对了、I2C 通信(如果有的话)可能也通了,背光驱动也工作了。所有低速外设全部正常,唯独高速的 MIPI DSI 视频数据流“人间蒸发”了。

本文将详细复盘一次针对 Rockchip (RK) 平台的 DSI 接口调试,我们遇到了一个极其隐蔽且反直觉的 Bug:同一款屏幕,在 dsi1 接口上完美点亮,但在 dsi0 接口上却“黑屏”

0x01: 问题的“幽灵”形态

我们的开发平台搭载了一颗 Rockchip SoC,它拥有两个独立的 MIPI DSI 接口(dsi0, dsi1),并分别由两个独立的视频处理器(Video Processor,vp2vp3)来驱动。这是一个典型的双屏异显硬件架构。

我们的目标是:

  • dsi0 接口 (由 vp2 驱动) -\> 连接 ST7797 屏幕
  • dsi1 接口 (由 vp3 驱动) -\> 连接 另一块 ST7797 屏幕

然而,在调试中,我们却遇到了以下奇怪的“排列组合”现象:

视频处理器 (VP)DSI 接口屏幕型号 (规格)结果
vp2dsi05.5寸屏 (4-Lane, 131MHz)✅ 成功
vp3dsi1ST7797 (1-Lane, 10.8MHz)✅ 成功
vp2dsi0ST7797 (1-Lane, 10.8MHz)❌ 失败 (黑屏)

这个测试矩阵提供了决定性的线索:

  1. dsi0 硬件没坏:既然它能点亮 4-Lane、131MHz 的 5.5 寸屏,证明 vp2 处理器、dsi0 控制器和 mipi_dcphy0 物理层本身都是好的。
  2. ST7797 屏幕配置没错:既然它能在 dsi1 上被 vp3 完美点亮,证明它的 DTS 配置(panel-init-sequencedisplay-timings)是完全正确的。

结论:Bug 既不在 dsi0 硬件本身,也不在 ST7797 的配置本身。Bug 出现在 vp2 处理器驱动 1-Lane 低频 DSI 信号 这个特定的 组合 上。

0x02: 深入 DSI 架构:VOP, Clock 与 Bit Rate

要理解为什么会这样,我们必须简单了解一下数据流:

[VOP (vp2)] -\> [DSI Controller (dsi0)] -\> [DSI PHY (mipi_dcphy0)] -\> [屏幕]

  • VOP (Video Output Processor):负责从内存中抓取图像数据,并按照 display-timings 生成像素时钟(clock-frequency)和视频流。
  • DSI PHY (物理层):负责将 VOP 过来的像素数据(并行)转换成高速串行差分信号(DSI Bit Stream)。

这里的关键是 DSI PHY 的比特率 (Bit Rate),它由像素时钟、色深和通道数共同决定:

DSI PHY Bit Rate (每通道) ≈ (clock_frequency * bits_per_pixel) / num_lanes

我们来计算一下我们案例中的 DSI 比特率(假设色深为 RGB888,即 24 bits):

  1. dsi0 成功案例 (5.5寸屏)

    • clock_frequency = 131.4 MHz
    • num_lanes = 4
    • Bit Rate ≈ (131.4M * 24) / 4 = 788 Mbps (每通道)
    • vp2 在此速率下工作 正常
  2. dsi1 成功案例 (ST7797)

    • clock_frequency = 10.8 MHz
    • num_lanes = 1
    • Bit Rate ≈ (10.8M * 24) / 1 = 259 Mbps
    • vp3 在此速率下工作 正常
  3. dsi0 失败案例 (ST7797)

    • clock_frequency = 10.8 MHz
    • num_lanes = 1
    • Bit Rate ≈ (10.8M * 24) / 1 = 259 Mbps
    • vp2 在此速率下工作 失败

假设被证实vp2 处理器(或其关联的 mipi_dcphy0)在 259 Mbps 这样的低 DSI 比特率下无法正常工作,它很可能存在一个最低速率门限。而 vp3 则没有这个限制。

0x03: 柳暗花明:来自 SDK 的“官方线索”

在我们深入研究 vp2 的寄存器之前,我们在 SDK 的其他项目中发现了另一款 1-Lane 屏幕的 DTS。这个文件提供了一个“石破天惊”的线索:

/* SDK 中另一款 1-Lane 屏幕的 DTS */
fragment@1 {
    target = <&route_dsi0>;
    __overlay__ {
        status = "okay";
        /* 注意!它把 dsi0 的数据源连接到了 vp3! */
        connect = <&vp3_out_dsi0>; 
    };
};

fragment@2 {
    target = <&dsi0_in_vp3>;
    __overlay__ {
        status = "okay";
    };
};

这几乎是一个“官方承认”:Rockchip 的工程师也知道 vp2 驱动 1-Lane DSI 时存在 Bug,他们的官方“绕过方案”(Workaround)就是dsi0 也去使用 vp3 作为数据源

但这无法满足我们“双屏同显”的需求——vp3 已经被 dsi1 占用了。我们无路可退,必须修复 vp2

0x04: 绝妙的思路:提高时钟,“欺骗” VOP

既然 vp2 无法工作在 10.8MHz 的低像素时钟下,我们能否在不改变屏幕刷新率(FPS)和分辨率的前提下,强行提高 clock_frequency

答案是肯定的

clock-frequency 是由 display-timings 中的所有参数(包括分辨率和消隐期)共同决定的:

Clock = (hactive + hsync + hbp + hfp) * (vactive + vsync + vbp + vfp) * FPS

我们可以保持 hactive (400), vactive (400) 和 FPS (60Hz) 不变,通过大幅增加消隐期(Blanking)(即 hsync, hbp, hfp 这些值),来“撑大” HTOTAL,从而拉高 clock-frequency

这就好比一条必须高速运转的传送带(vp2 的时钟限制)。为了保证每分钟只运送 60 个包裹(60Hz 刷新率),我们不能让传送带变慢,但我们可以在每个包裹之间留出巨大的空隙(增加消隐期)。

1. 修改前的时序 (DSI0 - ST7797)

  • clock-frequency = <10820160> (10.8 MHz)
  • hactive = <400>
  • vactive = <400>
  • hsync-len = <2>, hback-porch = <20>, hfront-porch = <20>
  • vsync-len = <2>, vback-porch = <4>, vfront-porch = <2>

时钟验证:

  • HTOTAL = 400 + 2 + 20 + 20 = 442
  • VTOTAL = 400 + 2 + 4 + 2 = 408
  • Clock = 442 * 408 * 60Hz = 10,820,160 (约 10.8MHz)
  • DSI Bit Rate ≈ 259 Mbpsvp2 罢工

2. 修改后的时序 (DSI0 - ST7797)

我们的目标是将时钟提高一倍,到 21.6MHz 左右,DSI 比特率也随之翻倍到 518 Mbps,这应该足以让 vp2 满意。

  • 保持 VTOTAL = 408 不变。
  • 目标 HTOTAL_new ≈ HTOTAL_old * 2 = 442 * 2 = 884
  • 我们将增加的 884 - 442 = 442 个点钟周期,全部分配给水平消隐期(hbphfp),并微调 hsync

最终修改的 DTS display-timings 节点如下:

                disp0_timings0: display-timings {
                    native-mode = <&dsi0_timing0>;
                    dsi0_timing0: timing0 {
-                        clock-frequency = <10820160>;
+                        clock-frequency = <21640320>; /* 目标时钟翻倍 */
                        hactive = <400>;
                        vactive = <400>;
-                        hsync-len = <2>;
-                        hback-porch = <20>;
-                        hfront-porch = <20>;
+                        hsync-len = <10>;     /* 随意给一个合理的值 */
+                        hback-porch = <237>;  /* 大幅增加 hbp */
+                        hfront-porch = <237>; /* 大幅增加 hfp */
                        vsync-len = <2>;
                        vback-porch = <4>;
                        vfront-porch = <2>;
                        hsync-active = <0>;
                        vsync-active = <0>;
                        de-active = <0>;
                        pixelclk-active = <0>;
                    };
                };

时钟验证:

  • HTOTAL_new = 400 + 10 + 237 + 237 = 884
  • VTOTAL_new = 400 + 2 + 4 + 2 = 408
  • Clock_new = 884 * 408 * 60Hz = 21,640,320 (约 21.6MHz)
  • DSI Bit Rate ≈ 518 Mbps

刷入修改后的固件,dsi0 上的 ST7797 屏幕被成功点亮! vp2 对这个速率非常满意。

0x05: 总结与启示

这次调试是一次完美的“逻辑推理”胜利。我们从一个看似诡异的“幽灵”Bug 出发,通过严谨的“排列组合”测试,成功地将问题定位到 SoC 的一个特定 IP(vp2)在一个特定工作模式(1-Lane 低比特率)下的 Bug。

最终,我们没有修改一行内核驱动代码,而是利用了 display-timings 这一“阳谋”,在不影响屏幕实际显示(400x400@60Hz)的前提下,“欺骗” vp2 工作在它所喜欢的高频区域,完美绕过了这个 Bug。

关键启示:

  1. “背光亮、黑屏”= 高速链路问题:这几乎是 DSI/LVDS 调试的黄金法则。请聚焦 PHY、时钟和数据路由。
  2. 交叉验证是王道dsi0/dsi1 vs 屏A/屏B 的 2x2 测试矩阵,是快速定位问题的最强武器。
  3. 深挖 SDK:SDK 中其他“看似不相关”的配置文件,往往隐藏着厂商(如 Rockchip)留下的“官方提示”和“绕过方案”。
  4. display-timings 不只是时序,更是控制旋钮:不要把它当成一组固定的参数。它是你与 VOP 和 DSI PHY 沟通的“语言”,通过它可以微调硬件的物理层行为。
0

评论 (0)

取消