4、DSI 主机控制器驱动开发:主机控制器初始化流程、PHY 配置与时钟设置、DMA 与数据包传输、中断处理与错误恢复
DSI 主机控制器,说白了就是 SoC 这边负责把显示数据打包成 DSI 协议格式,然后通过 MIPI D-PHY 物理层发出去的那个硬件模块。驱动开发的核心,就是把它从复位状态一步步唤醒,配好时钟和 PHY,让它能正常收发数据包。
我个人习惯把主机控制器的驱动开发分成四个阶段:初始化、PHY 与时钟、数据传输、中断与恢复。下面我们一个一个来过。
4.1 主机控制器初始化流程
初始化流程,我一般按下面几步走。顺序错了,后面大概率跑不起来。
- 获取设备资源:从设备树里拿到寄存器基地址、中断号、时钟、复位 GPIO 等。这一步用
devm_platform_ioremap_resource()和platform_get_irq()就行。 - 复位控制器:很多 SoC 的 DSI 控制器在上电后处于复位状态。需要先释放复位,等待至少 1ms 让内部逻辑稳定。
- 使能时钟:打开 DSI 的 AHB/AXI 总线时钟、PCLK(外设时钟)、以及 D-PHY 参考时钟。顺序上,我建议先开总线时钟,再开 PHY 时钟。
- 配置版本寄存器:读一下版本号,确认硬件 IP 核是我们预期的那一款。我在项目中遇到过两次设备树配错导致读到全 0 的情况,所以这一步别省。
- 初始化 DSI 核心寄存器:设置 lane 数量(1/2/3/4 lane)、视频模式还是命令模式、像素格式(RGB565/666/888)等。
- 配置视频时序参数:如果走视频模式,需要把 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 初始化步骤:
- 配置 PHY 测试模式:先把 PHY 置于非工作状态,关闭所有 lane 的 HS 驱动。
- 设置 PHY PLL:写入分频系数和倍频系数,启动 PLL 锁定。等待 PLL 锁定标志位置位。
- 校准 PHY:有些 PHY 需要做阻抗校准和偏置校准。我记得在某个项目里,校准没做导致 HS 模式下信号幅度只有 150mV,屏幕直接黑屏。
- 使能 lane:按顺序使能 clock lane 和 data lane,先使能 clock lane,再使能 data lane 0~3。
- 进入高速模式:发送 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 模式超时 | 重置控制器状态机 |
中断处理流程:
- 读取中断状态寄存器,判断中断源。
- 如果是 DMA 完成中断,调用回调函数通知上层应用。
- 如果是错误中断,先清除中断标志,然后根据错误类型做恢复操作。
- 对于严重错误(如 PHY 失锁),需要停止当前传输,重新执行 PHY 初始化流程。
我曾经在一个项目中遇到屏幕偶尔闪一下的问题。查了很久,发现是 FIFO 下溢中断偶尔触发,但驱动里只清标志没做恢复,导致后续数据错位。后来我在 FIFO 错误中断里加了 FIFO 复位和重新启动传输的逻辑,问题就解决了。
错误恢复策略:
- 轻度错误(如单包 CRC 错误):直接丢弃,不处理。因为视频流里丢一两个像素看不出来。
- 中度错误(如 FIFO 异常):复位 FIFO,重新开始当前行的传输。
- 重度错误(如 PHY 失锁):停止所有传输,重新初始化 PHY 和控制器,然后从当前帧的起始位置重新开始。
核心要点:中断处理要快,不要在中断上下文里做耗时操作。错误恢复的逻辑要放在工作队列或内核线程里执行。我习惯在中断里只做标志位记录和唤醒,实际恢复操作交给下半部处理。
这张图把四个核心模块的关系画清楚了。你写驱动的时候,脑子里要有这张图。初始化是基础,PHY 是物理通道,DMA 是数据搬运工,中断是异常哨兵。缺了哪一个,系统都跑不稳。