4、MIPI CSI-2接收器驱动:D-PHY配置、协议层解析、VC处理、错误恢复机制
MIPI CSI-2 接收器,说白了就是摄像头和 SoC 之间的「翻译官」。物理层用 D-PHY 传差分信号,协议层把比特流拆成帧、行、像素。R-Car 的 CSI-2 模块集成度很高,但坑也不少。这一章我带你从物理层一路捅到协议层,最后聊聊怎么让系统在出错时自己缓过来。
4.1 D-PHY 配置:从时序到阻抗
D-PHY 配置是 CSI-2 驱动的第一道坎。R-Car 的 CSI-2 硬核支持 D-PHY v1.1,最高 1.5 Gbps/lane。但实际项目中,我很少跑满这个值——信号完整性才是瓶颈。
4.1.1 关键时序参数
| 参数 | 说明 | 典型值(1.2Gbps) |
|---|---|---|
| THS-SETTLE | HS 传输建立时间 | 145 ns + 4 UI |
| THS-ZERO | HS 传输前导零时间 | 105 ns + 6 UI |
| TCLK-ZERO | 时钟通道前导零时间 | 300 ns |
| TCLK-POST | 时钟通道后置时间 | 60 ns + 52 UI |
4.1.2 阻抗匹配与偏置电压
R-Car 的 D-PHY 接收端内置 100Ω 差分终端电阻,但 PCB 走线阻抗必须控制在 100Ω ±10%。偏置电压(Vterm)通常设为 200 mV,与发送端共模电压匹配。
我曾经遇到一个案子:摄像头模组和 SoC 分属两块板,通过 FPC 连接。FPC 阻抗只有 85Ω,导致眼图闭合。最后在接收端并联了外部 120Ω 电阻,才把等效阻抗拉回 100Ω。
4.2 协议层解析:从字节到图像
CSI-2 协议层分三层:底层协议(Low-Level Protocol, LLP)、像素/字节打包层、应用层。R-Car 的硬件加速器能自动处理大部分 LLP 工作,但驱动得配好打包格式。
4.2.1 长包与短包
- 短包(Short Packet):32 位,用于帧同步、行同步、ECC。驱动只需读取状态寄存器,判断帧起始/结束。
- 长包(Long Packet):包含 32 位包头、可变长数据负载、16 位 CRC。R-Car 的 DMA 引擎可以直接把负载搬运到内存。
核心流程:
- 接收短包 FS(Frame Start)→ 触发 VSYNC 中断
- 接收长包行数据 → DMA 写入 buffer
- 接收短包 FE(Frame End)→ 触发帧完成中断
4.2.2 数据类型(DT)与格式映射
CSI-2 用 6 位 Data Type 字段标识像素格式。R-Car 的 CSI-2 模块支持 RAW8/10/12、YUV422、RGB888 等常见格式。驱动需要把 sensor 输出的 DT 映射到接收器的解包配置。
/* R-Car CSI-2 数据类型配置示例 */
struct rcsi2_dt_cfg {
u8 data_type; /* 0x2A = RAW10, 0x1E = YUV422 8bit */
u8 bytes_per_pixel; /* 解包后每像素字节数 */
u8 packing; /* 0: 无打包, 1: RAW10 打包, 2: RAW12 打包 */
};
static const struct rcsi2_dt_cfg dt_table[] = {
{ 0x2A, 2, 1 }, /* RAW10 → 2字节/pixel, 10位打包 */
{ 0x1E, 2, 0 }, /* YUV422 → 2字节/pixel, 无打包 */
{ 0x2B, 3, 2 }, /* RAW12 → 3字节/pixel, 12位打包 */
};
4.3 VC(Virtual Channel)处理:多路复用的艺术
VC 是 CSI-2 的精髓。一条 MIPI 链路上最多 4 个 VC,每个 VC 可以传输独立的图像流。R-Car 的 CSI-2 接收器支持 4 个 VC 的独立解包和 DMA 通道。
4.3.1 VC 路由与过滤
驱动需要配置 VC 过滤寄存器,决定哪些 VC 的数据被接收。我习惯的做法:
- VC0:主图像流(RAW10)
- VC1:副图像流(YUV 预览)
- VC2:元数据(AE/AWB 统计)
- VC3:保留或调试
经验之谈:如果 sensor 同时输出 RAW 和 YUV,务必确认两个 VC 的帧同步是否对齐。我曾经遇到 VC0 帧率 30fps、VC1 帧率 15fps,结果 DMA 缓冲区互相踩踏。解决方案是给每个 VC 分配独立的环形 buffer。
4.3.2 VC 与 DMA 通道绑定
R-Car 的 CSI-2 模块有 4 个 DMA 请求通道,每个通道可以绑定一个 VC。驱动初始化时,需要建立 VC→DMA 通道的映射表。
/* VC 到 DMA 通道的绑定 */
struct rcsi2_vc_map {
u8 vc_id; /* 0-3 */
u8 dma_chan; /* 0-3 */
dma_addr_t buf_phys; /* DMA buffer 物理地址 */
u32 buf_size; /* 每个 buffer 大小 */
};
/* 配置示例:VC0 → DMA0, VC1 → DMA1 */
struct rcsi2_vc_map vc_config[] = {
{ .vc_id = 0, .dma_chan = 0, .buf_size = 0x200000 },
{ .vc_id = 1, .dma_chan = 1, .buf_size = 0x100000 },
};
4.4 错误恢复机制:让系统自己站起来
MIPI 链路在汽车环境下很容易受干扰。R-Car 的 CSI-2 模块提供了丰富的错误检测和恢复能力,但驱动得会用。
4.4.1 常见错误类型
| 错误类型 | 检测方式 | 典型原因 |
|---|---|---|
| ECC 错误 | 包头 3 位 ECC 校验失败 | 信号完整性差、走线过长 |
| CRC 错误 | 长包 16 位 CRC 校验失败 | 数据负载被干扰 |
| 帧同步丢失 | FS/FE 短包缺失或乱序 | sensor 异常、时钟抖动 |
| LP 状态异常 | LP→HS 切换超时 | D-PHY 时序配置错误 |
4.4.2 恢复策略
我总结了一套三级恢复策略:
- 软恢复:清错误标志位,重置 DMA 指针,丢弃当前帧。适用于偶发 ECC/CRC 错误。
- 硬恢复:复位 CSI-2 模块内部状态机,重新初始化 D-PHY。适用于帧同步丢失或 LP 状态异常。
- 链路重建:先复位 sensor(通过 I2C 或 GPIO),再复位 CSI-2 接收器。适用于连续错误或 sensor 无响应。
避坑指南:我曾经在硬恢复时忘了关闭 DMA,导致恢复后 DMA 从错误地址继续搬运数据,直接写穿了系统内存。正确的顺序是:停止 DMA → 复位 CSI-2 → 重新配置 VC/DMA → 启动传输。
4.4.3 错误统计与上报
驱动应该维护错误计数器,并在超过阈值时触发链路重建。我通常的做法:
/* 错误统计结构 */
struct rcsi2_err_stats {
u32 ecc_err_cnt;
u32 crc_err_cnt;
u32 sync_loss_cnt;
u32 lp_err_cnt;
unsigned long last_reset_jiffies;
};
/* 错误处理回调 */
static void rcsi2_handle_error(struct rcsi2_device *rcsi2,
enum rcsi2_err_type type)
{
struct rcsi2_err_stats *stats = &rcsi2->err_stats;
switch (type) {
case ECC_ERR:
stats->ecc_err_cnt++;
if (stats->ecc_err_cnt > 10)
rcsi2_soft_reset(rcsi2); /* 软恢复 */
break;
case SYNC_LOSS:
stats->sync_loss_cnt++;
rcsi2_hard_reset(rcsi2); /* 硬恢复 */
break;
}
}
4.5 知识结构总览
下面这张图把本章的核心逻辑串起来了。从 D-PHY 物理层到 VC 数据分发,再到错误恢复闭环,你可以把它当作调试时的检查清单。