4、DSI 主机控制器驱动开发:主机控制器初始化流程、PHY 配置与时钟设置、DMA 与数据包传输、中断处理与错误恢复

DSI 主机控制器,说白了就是 SoC 这边负责把显示数据打包成 DSI 协议格式,然后通过 MIPI D-PHY 物理层发出去的那个硬件模块。驱动开发的核心,就是把它从复位状态一步步唤醒,配好时钟和 PHY,让它能正常收发数据包。

我个人习惯把主机控制器的驱动开发分成四个阶段:初始化、PHY 与时钟、数据传输、中断与恢复。下面我们一个一个来过。

4.1 主机控制器初始化流程

初始化流程,我一般按下面几步走。顺序错了,后面大概率跑不起来。

  1. 获取设备资源:从设备树里拿到寄存器基地址、中断号、时钟、复位 GPIO 等。这一步用 devm_platform_ioremap_resource() 和 platform_get_irq() 就行。
  2. 复位控制器:很多 SoC 的 DSI 控制器在上电后处于复位状态。需要先释放复位,等待至少 1ms 让内部逻辑稳定。
  3. 使能时钟:打开 DSI 的 AHB/AXI 总线时钟、PCLK(外设时钟)、以及 D-PHY 参考时钟。顺序上,我建议先开总线时钟,再开 PHY 时钟。
  4. 配置版本寄存器:读一下版本号,确认硬件 IP 核是我们预期的那一款。我在项目中遇到过两次设备树配错导致读到全 0 的情况,所以这一步别省。
  5. 初始化 DSI 核心寄存器:设置 lane 数量(1/2/3/4 lane)、视频模式还是命令模式、像素格式(RGB565/666/888)等。
  6. 配置视频时序参数:如果走视频模式,需要把 HSA、HBP、HFP、VSA、VBP、VFP 这些时序参数写进控制器的时序寄存器。

初始化流程检查清单

步骤 关键动作 常见坑
1 获取资源 设备树 reg 属性长度不够
2 复位释放 复位后等待时间不足
3 时钟使能 时钟顺序颠倒导致 PHY 锁死
4 版本校验 读到 0x0 或 0xFFFFFFFF
5 核心配置 lane 数与硬件不匹配
6 时序写入 HBP/HFP 值超出硬件范围

4.2 PHY 配置与时钟设置

PHY 配置是 DSI 驱动里最敏感的部分。PHY 没配好,信号眼图就是一团糟,屏幕闪或者干脆不亮。

时钟架构:DSI 的时钟源通常来自 SoC 内部的 PLL,经过分频后得到 D-PHY 需要的参考时钟(一般是 26MHz 或 19.2MHz)。然后 PHY 内部会通过 PLL 倍频出高速时钟(HS clock),这个时钟频率决定了 DSI 链路的比特率。

比特率的计算公式很简单:

比特率 = (像素时钟 × 每像素位数) / lane 数

举个例子,1080p@60 的屏幕,像素时钟约 148.5MHz,RGB888 每像素 24bit,用 4 lane 传输:

比特率 = (148.5 × 24) / 4 = 891 Mbps/lane

嗯,这里要注意,实际 PHY 的 PLL 不一定能精确输出 891MHz,需要根据 PHY 的 VCO 范围和分频系数来调整。

PHY 初始化步骤:

  1. 配置 PHY 测试模式:先把 PHY 置于非工作状态,关闭所有 lane 的 HS 驱动。
  2. 设置 PHY PLL:写入分频系数和倍频系数,启动 PLL 锁定。等待 PLL 锁定标志位置位。
  3. 校准 PHY:有些 PHY 需要做阻抗校准和偏置校准。我记得在某个项目里,校准没做导致 HS 模式下信号幅度只有 150mV,屏幕直接黑屏。
  4. 使能 lane:按顺序使能 clock lane 和 data lane,先使能 clock lane,再使能 data lane 0~3。
  5. 进入高速模式:发送 LP->HS 切换序列,让 PHY 进入高速数据传输状态。

个人经验:PHY 配置完成后,强烈建议用示波器量一下 clock lane 的差分信号。正常的高速时钟应该是 200mV~400mV 的摆幅,频率稳定。如果看到波形抖动大或者幅度不对,多半是 PLL 配置或者 PCB 走线的问题。

4.3 DMA 与数据包传输

DSI 主机控制器通常集成了 DMA 引擎,用来把帧缓冲区的数据搬运到控制器的 FIFO 里,然后由硬件自动打包成 DSI 包发送出去。

DMA 配置要点:

  • 源地址:帧缓冲区的物理地址,必须按 8 字节对齐。
  • 目的地址:DSI 控制器的数据 FIFO 地址。
  • 传输宽度:一般配置为 32 位或 64 位,取决于总线宽度。
  • 突发长度:我习惯设成 8 或 16,太大容易占总线太久,太小效率低。
  • 传输完成中断:使能 DMA 完成中断,用于触发下一帧的传输。

数据包格式:DSI 包分为短包(4 字节)和长包(可变长度)。视频模式下,主机控制器会自动把像素数据打包成长包,并在每行结束时插入短包(如 VSA、HSA 等)。

下面是一个典型的视频模式数据流:

HSA 短包 → HBP 短包 → 像素数据长包(整行) → HFP 短包 → ... 重复下一行

命令模式下,我们需要手动构造 DSI 包。比如发送 write_memory_start 命令:

// 构造 DSI 短包:0x2C 是 write_memory_start 命令
u32 dsi_pkt = (0x2C << 0) |  // 数据类型
              (0x00 << 8) |  // 虚拟通道
              (0x00 << 16); // 参数

writel(dsi_pkt, dsi_base + DSI_GEN_HDR);

注意:DMA 传输过程中,千万不要去修改帧缓冲区的数据。我见过有人为了做动态效果,在 DMA 正在搬运时更新缓冲区,结果屏幕出现撕裂。解决办法是用双缓冲或者等待 DMA 完成后再更新。

4.4 中断处理与错误恢复

DSI 主机控制器的中断类型很多,我列几个常见的:

中断类型 触发条件 处理方式
DMA 完成 一帧数据传输完毕 更新帧计数,触发下一帧
FIFO 上溢/下溢 FIFO 读写指针异常 复位 FIFO,重新启动传输
CRC 错误 接收端 CRC 校验失败 重传数据包
ECC 错误 包头 ECC 校验失败 丢弃当前包,等待下一个
PHY 错误 PHY 检测到 lane 异常 重新初始化 PHY
超时错误 BTA 超时或 LP 模式超时 重置控制器状态机

中断处理流程:

  1. 读取中断状态寄存器,判断中断源。
  2. 如果是 DMA 完成中断,调用回调函数通知上层应用。
  3. 如果是错误中断,先清除中断标志,然后根据错误类型做恢复操作。
  4. 对于严重错误(如 PHY 失锁),需要停止当前传输,重新执行 PHY 初始化流程。

我曾经在一个项目中遇到屏幕偶尔闪一下的问题。查了很久,发现是 FIFO 下溢中断偶尔触发,但驱动里只清标志没做恢复,导致后续数据错位。后来我在 FIFO 错误中断里加了 FIFO 复位和重新启动传输的逻辑,问题就解决了。

错误恢复策略:

  • 轻度错误(如单包 CRC 错误):直接丢弃,不处理。因为视频流里丢一两个像素看不出来。
  • 中度错误(如 FIFO 异常):复位 FIFO,重新开始当前行的传输。
  • 重度错误(如 PHY 失锁):停止所有传输,重新初始化 PHY 和控制器,然后从当前帧的起始位置重新开始。

核心要点:中断处理要快,不要在中断上下文里做耗时操作。错误恢复的逻辑要放在工作队列或内核线程里执行。我习惯在中断里只做标志位记录和唤醒,实际恢复操作交给下半部处理。

DSI 主机控制器驱动开发知识体系 DSI 主机控制器 初始化流程 PHY 与时钟 DMA 数据传输 中断与恢复 中断与错误恢复 资源获取 复位与时钟 核心配置 PLL 配置 校准与使能 HS 模式切换 DMA 配置 包格式 双缓冲 中断类型 错误恢复 初始化 → PHY → DMA → 中断,环环相扣 任何一个环节出错,屏幕都可能不亮

这张图把四个核心模块的关系画清楚了。你写驱动的时候,脑子里要有这张图。初始化是基础,PHY 是物理通道,DMA 是数据搬运工,中断是异常哨兵。缺了哪一个,系统都跑不稳。