引言
当我们打开摄像头、看到一张清晰的图像时,很少有人意识到它其实经历了无数次格式变换和硬件转接——从传感器吐出原始电信号,到屏幕显示彩色像素,这个过程比任何快递分拣系统都复杂得多。
为了更好的理顺ISP的章节内容,本章节将会再次梳理这个流程
第一部分:软件视角:物理信号到像素阵列 —— ISP前级数据流解析
本部分从数据流动的角度出发,讲解一帧原始图像如何经过层层协议封装和解析,最终准备好被ISP处理。
1.1 核心流程:从Sensor到ISP
一帧图像从摄像头传感器到应用程序,经历的完整硬件路径:
Camera Sensor输出: 模拟光强 → MIPI差分电压信号 (模拟)
↓
D-PHY转换: 模拟差分信号 → 数字字节流 (数字)
↓
MIPI CSI-2 Host解析: 字节流 → MIPI数据包 (协议层)
↓
VICAP格式转换: MIPI数据包 → RAW像素数组 (Bayer格式)
↓
ISP图像处理: RAW Bayer → YUV/RGB彩色图像
↓
Memory存储: YUV图像 → 内存Buffer地址
↓
应用程序: 读取内存 → 显示/编码/分析
这个路径概括性比较强,但是你看完一遍大概率还是会忘记,或者理解不透彻。因为对从Sensor数据格式到ISP输出的数据格式没有了解,这篇文章一次性讲透这个内容。
1.2 数据的四次“变身”
假设OV13855拍摄一张照片:4224×3136像素,RAW10格式。
这张照片要经历4次"变身"才能到达ISP:
变身1: 传感器输出 → 加上MIPI协议包装
变身2: MIPI包装 → D-PHY转成字节流
变身3: 字节流 → CSI2-HOST解包还原结构
变身4: 结构化数据 → VICAP格式转换并存储
1.2.1 变身1:传感器的MIPI协议打包
1.2.1.1 传感器原始输出(没有协议)
传感器只是按顺序吐出像素值:
第1行: R G R G R G R G ... (4224个像素)
第2行: G B G B G B G B ... (4224个像素)
第3行: R G R G R G R G ...
...
第3136行
问题:接收方不知道:
- 哪里是一行的开始?
- 哪里是一行的结束?
- 哪里是一帧的开始?
- 数据是什么格式(RAW8?RAW10?YUV?)
1.2.1.2 MIPI CSI-2的解决方案:打包成"快递包裹"
MIPI协议把数据打包成一个个"包裹",每个包裹贴上"标签":
┌─────────────────────────────────────────────────┐
│ 快递包裹结构 │
├─────────────────────────────────────────────────┤
│ │
│ 包裹1: [标签: 帧开始] │
│ 内容: 无 │
│ │
│ 包裹2: [标签: 第1行开始] │
│ 内容: 无 │
│ │
│ 包裹3: [标签: RAW10数据, 5280字节] │
│ 内容: [第1行4224个像素的数据] │
│ │
│ 包裹4: [标签: 第1行结束] │
│ 内容: 无 │
│ │
│ 包裹5: [标签: 第2行开始] │
│ │
│ 包裹6: [标签: RAW10数据, 5280字节] │
│ 内容: [第2行4224个像素的数据] │
│ │
│ 包裹7: [标签: 第2行结束] │
│ │
│ ... (重复3136次) │
│ │
│ 包裹N: [标签: 帧结束] │
│ 内容: 无 │
│ │
└─────────────────────────────────────────────────┘
1.2.1.3 标签上写的内容(包头)
每个包裹的标签包含:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ 包裹标签(4字节) ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ ┃
┃ 包裹类型编号: ┃
┃ 00 = 帧开始 ┃
┃ 01 = 帧结束 ┃
┃ 02 = 行开始 ┃
┃ 03 = 行结束 ┃
┃ 29 = RAW10像素数据 ┃
┃ ┃
┃ 内容重量: 5280字节 ┃
┃ ┃
┃ ✓ 防伪码: ECC校验 ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
关键理解:
- 标记包(帧开始/行开始/行结束/帧结束):只有标签,没有内容
- 数据包:既有标签,又有实际像素数据
1.2.2 变身2:D-PHY把包裹打碎成字节流
1.2.2.1 D-PHY的工作
D-PHY接收差分信号,转换成字节流。但它不认识包裹,只是盲目地输出字节:
摄像头通过4条Lane发送差分信号:
Lane0: +- +- -- ++ -- +- ... (电压波动)
Lane1: -- ++ +- -- ++ -- ...
Lane2: +- -- ++ -- +- ++ ...
Lane3: ++ -- +- ++ -- -- ...
D-PHY转换成字节流:
↓
连续的字节流:
B8 00 00 00 A7 12 FF B8 02 00 00 C3 9E 44 B8 29 14 10 65 [5280个字节] 3A 7F B8 03 00 00 ...
│ │ │ │
包裹1的标签 包裹2的标签 包裹3的标签+内容 包裹4的标签
D-PHY输出的就是所有包裹的标签和内容按顺序拼在一起的字节流。
问题:接收方看到的是一长串字节,分不清哪里是标签,哪里是内容!
1.2.3 变身3:CSI2-HOST识别包裹边界
1.2.3.1 CSI2-HOST的第一个任务:找到包裹
CSI2-HOST像快递分拣员,从连续的字节流中识别出一个个包裹:
输入的字节流:
B8 00 00 00 A7 12 FF B8 02 00 00 C3 9E 44 B8 29 14 10 65 [数据] 3A 7F ...
↓
CSI2-HOST识别:
找到分隔符 B8 → 这是包裹1的起始
├─ 读4字节: B8 00 00 00 → 标签内容
├─ 读1字节: A7 → 错误校验
├─ 读2字节: 12 FF → 完整性校验
└─ 包裹1识别完成 ✓
找到下一个 B8 → 这是包裹2的起始
├─ 读4字节: B8 02 00 00 → 标签内容
├─ ...
└─ 包裹2识别完成 ✓
找到下一个 B8 → 这是包裹3的起始
├─ 读4字节: B8 29 14 10 → 标签(类型29=数据, 重量5280字节)
├─ 读5280字节: [像素数据]
├─ 读2字节: 3A 7F → 完整性校验
└─ 包裹3识别完成 ✓
1.2.3.2 CSI2-HOST的第二个任务:读取标签信息
CSI2-HOST读懂每个包裹的标签:
┌────────────────────────────────────────────────┐
│ 包裹1的标签: B8 00 00 00 │
│ ↓ │
│ 解读结果: │
│ ✓ 类型编号 00 → 这是"帧开始"信号 │
│ ✓ 内容重量 0 → 没有实际数据 │
│ → 告诉VICAP: 新的一帧开始了! │
└────────────────────────────────────────────────┘
┌────────────────────────────────────────────────┐
│ 包裹2的标签: B8 02 00 00 │
│ ↓ │
│ 解读结果: │
│ ✓ 类型编号 02 → 这是"行开始"信号 │
│ → 告诉VICAP: 新的一行开始了! │
└────────────────────────────────────────────────┘
┌────────────────────────────────────────────────┐
│ 包裹3的标签: B8 29 14 10 │
│ ↓ │
│ 解读结果: │
│ ✓ 类型编号 29 → RAW10格式的像素数据 │
│ ✓ 内容重量 5280字节 → 后面跟着5280字节数据 │
│ → 告诉VICAP: 这是实际像素,快来接收! │
└────────────────────────────────────────────────┘
1.2.3.3 CSI2-HOST的输出
CSI2-HOST输出结构化的信息给VICAP:
┌─────────────────────────────────────────────────┐
│ CSI2-HOST的输出(通过硬件信号线) │
├─────────────────────────────────────────────────┤
│ │
│ 时刻1: [信号] 帧开始 (frame_start脉冲) │
│ │
│ 时刻2: [信号] 行开始 (line_start脉冲) │
│ │
│ 时刻3: [数据线] 像素数据开始传输 │
│ [标识] data_type = RAW10 │
│ ↓ │
│ 像素0的值: 10bit │
│ 像素1的值: 10bit │
│ 像素2的值: 10bit │
│ ... (4224个像素) │
│ │
│ 时刻4: [信号] 行结束 (line_end脉冲) │
│ │
│ 时刻5: [信号] 行开始 (新的一行) │
│ 时刻6: [数据线] 第2行的像素数据... │
│ 时刻7: [信号] 行结束 │
│ │
│ ... (重复3136行) │
│ │
│ 时刻N: [信号] 帧结束 (frame_end脉冲) │
│ │
└─────────────────────────────────────────────────┘
CSI2-HOST做的总结:
- 从无序变有序: 字节流 → 识别出包裹边界
- 从标签到信号: 读标签内容 → 发出对应的控制信号(帧开始/行开始/数据/行结束/帧结束)
- 告知格式: 提取数据类型(RAW10)告诉下一级
1.2.4 变身4:VICAP格式转换和存储
1.2.4.1 VICAP接收到的数据
VICAP从CSI2-HOST接收:
┌─────────────────────────────────────┐
│ 硬件信号线上传来: │
├─────────────────────────────────────┤
│ │
│ 控制信号: │
│ ├─ frame_start (脉冲) │
│ ├─ line_start (脉冲) │
│ ├─ line_end (脉冲) │
│ └─ frame_end (脉冲) │
│ │
│ 数据总线: │
│ ├─ pixel_data[31:0] (32位宽) │
│ │ 每个周期传输32bit数据 │
│ │ │
│ └─ data_type = RAW10 │
│ │
└─────────────────────────────────────┘
但有3个问题需要VICAP解决:
1.2.4.2 问题1:数据打包方式不对
CSI2-HOST发来的RAW10数据打包方式(MIPI规范):
每4个像素占5个字节,数据是"挤在一起"的:
4个像素的数据:
Pixel0 = 10bit值
Pixel1 = 10bit值
Pixel2 = 10bit值
Pixel3 = 10bit值
MIPI打包方式(省空间):
┌────────┬────────┬────────┬────────┬──────────────────┐
│ Byte0 │ Byte1 │ Byte2 │ Byte3 │ Byte4 │
├────────┼────────┼────────┼────────┼──────────────────┤
│ P0高8位│ P1高8位│ P2高8位│ P3高8位│ P3低2|P2低2|P1低2|P0低2 │
│ [9:2] │ [9:2] │ [9:2] │ [9:2] │ [1:0][1:0][1:0][1:0]│
└────────┴────────┴────────┴────────┴──────────────────┘
↑ 8bit ↑ 8bit ↑ 最后一个字节塞了4个像素的低2位
示例数据:
Byte0 = 0xA5 (像素0的bit[9:2])
Byte1 = 0x3C (像素1的bit[9:2])
Byte2 = 0x7F (像素2的bit[9:2])
Byte3 = 0x12 (像素3的bit[9:2])
Byte4 = 0xB4 = 10|11|01|00
↑ ↑ ↑ ↑
P3 P2 P1 P0的低2bit
但ISP需要的格式(方便处理):
每个像素单独占2个字节(16bit对齐):
16bit对齐方式:
┌──────────────┬──────────────┬──────────────┬──────────────┐
│ Pixel0 │ Pixel1 │ Pixel2 │ Pixel3 │
├──────────────┼──────────────┼──────────────┼──────────────┤
│ [15:10]=0 │ [15:10]=0 │ [15:10]=0 │ [15:10]=0 │
│ [9:0]=数据 │ [9:0]=数据 │ [9:0]=数据 │ [9:0]=数据 │
└──────────────┴──────────────┴──────────────┴──────────────┘
2字节 2字节 2字节 2字节
示例数据:
Pixel0 = 0x0297 (高6位补0, 低10位是实际数据)
Pixel1 = 0x00F1
Pixel2 = 0x01FF
Pixel3 = 0x004A
VICAP做格式转换:
输入(MIPI打包):
[A5][3C][7F][12][B4] → 4个像素紧密打包, 5字节
↓
VICAP解包重组
↓
输出(16bit对齐):
[02][97][00][F1][01][FF][00][4A] → 4个像素分开对齐, 8字节
1.2.4.3 问题2:可能只需要部分区域
用户可能只要图像的一部分(ROI区域):
完整图像: 4224×3136
┌─────────────────────────────────────┐
│ │
│ 只要这部分 ↓ │
│ ┌──────────────┐ │
│ │ 2000×1500 │ ← ROI区域 │
│ │ │ │
│ └──────────────┘ │
│ │
└─────────────────────────────────────┘
VICAP裁剪(Crop):
- 丢弃前面的行
- 每行只保留中间2000个像素
- 丢弃后面的行
最终输出: 2000×1500的图像
1.2.4.4 问题3:需要写入内存或直通ISP
VICAP提供两种路径:
路径A:回读模式(经过DDR内存)
VICAP的DMA引擎写入内存:
┌────────────────────────────────────────┐
│ DDR内存 │
├────────────────────────────────────────┤
│ │
│ 地址 0x10000000: │
│ ┌────────────────────────────────┐ │
│ │ 第1行: [P0][P1][P2]...[P4224] │ │
│ ├────────────────────────────────┤ │
│ │ 第2行: [P0][P1][P2]...[P4224] │ │
│ ├────────────────────────────────┤ │
│ │ ... │ │
│ ├────────────────────────────────┤ │
│ │ 第3136行: [P0][P1]...[P4224] │ │
│ └────────────────────────────────┘ │
│ │
│ 完整帧大小: 26.5 MB │
│ │
└────────────────────────────────────────┘
↓
ISP从内存读取处理
路径B:直通模式(不经过内存)
VICAP直接输出给ISP:
┌───────────┐
VICAP ────→│ ISP硬件 │
(实时) │ 边收边处理 │
└───────────┘
优点: 延迟低,不占用内存带宽
缺点: 只能连一个摄像头到一个ISP
第二部分 物理接口到处理核心 —— RK3588视频通路硬件全解析
本部分深入RK3588芯片内部,逐一解析从物理接口(PHY)到图像处理核心(ISP)的各个硬件模块的资源、特性和配置方法。
2.1 物理层(PHY):数据接入的“端口”
2.1.1 RK3588的PHY硬件全景
RK3588芯片内部有4个PHY物理硬件模块:
┌─────────────────────────────────────────────┐
│ RK3588 PHY硬件布局 │
├─────────────────────────────────────────────┤
│ │
│ DCPHY0硬件 (0xFEDC0000) ← 高端硬件 │
│ ├─ RX: 支持D-PHY/C-PHY │
│ └─ TX: 支持D-PHY/C-PHY │
│ │
│ DCPHY1硬件 (0xFEDC8000) ← 高端硬件 │
│ ├─ RX: 支持D-PHY/C-PHY │
│ └─ TX: 支持D-PHY/C-PHY │
│ │
│ ──────────────────────────────────── │
│ │
│ DPHY0硬件 (0xFEDC0000) ← 普通硬件 │
│ └─ RX: 仅支持D-PHY │
│ │
│ DPHY1硬件 (0xFEDC8000) ← 普通硬件 │
│ └─ RX: 仅支持D-PHY │
│ │
└─────────────────────────────────────────────┘
硬件定位:
- DCPHY: 高端、灵活、双向、协议可切换
- DPHY: 普通、单向、仅D-PHY协议
2.1.2 DCPHY硬件的核心特性
2.1.2.1 物理结构
DCPHY = Dual Combo PHY (双重组合物理层)
每个DCPHY硬件包含两套独立的收发电路:
┌──────────────────────────────────────────┐
│ DCPHY0硬件模块 │
├──────────────────────────────────────────┤
│ │
│ RX接收电路 (用于Camera输入) │
│ ┌────────────────────────────┐ │
│ │ Lane0 ─┐ │ │
│ │ Lane1 ─┤ 差分接收器 │ │
│ │ Lane2 ─┤ + │ │
│ │ Lane3 ─┘ 协议解析器 │ │
│ │ (D-PHY/C-PHY) │ │
│ └────────────────────────────┘ │
│ │
│ ───────────────────────────── │
│ │
│ TX发送电路 (用于Display输出) │
│ ┌────────────────────────────┐ │
│ │ Lane0 ─┐ │ │
│ │ Lane1 ─┤ 差分驱动器 │ │
│ │ Lane2 ─┤ + │ │
│ │ Lane3 ─┘ 协议编码器 │ │
│ │ (D-PHY/C-PHY) │ │
│ └────────────────────────────┘ │
│ │
│ 全局配置寄存器: │
│ - 协议选择: D-PHY/C-PHY │
│ - Lane数量配置 │
│ - 电气参数调整 │
└──────────────────────────────────────────┘
2.1.2.2 协议切换机制
D-PHY协议 vs C-PHY协议的物理差异:
D-PHY (差分物理层):
物理连接: 每Lane使用1对差分线
Lane0: DP0+ / DP0- ←差分对
Lane1: DP1+ / DP1-
Lane2: DP2+ / DP2-
Lane3: DP3+ / DP3-
时钟: CLK+ / CLK- ←独立时钟线
编码方式: 差分信号, 高低电平差值代表0/1
最大速率: 4.5Gbps/Lane (RK3588的DCPHY)
总Lane数: 4条数据Lane + 1条时钟Lane = 5条线对
C-PHY (三相物理层):
物理连接: 3根线组成1个Trio
Trio0: A / B / C ←三根信号线
Trio1: A / B / C
Trio2: A / B / C
编码方式: 三相信号, 通过3根线的电平组合传输数据
状态编码: 有6种有效状态 (+1/0/-1的组合)
最大速率: 2.5Gsps/Trio (symbol per second)
无需时钟线: 数据中内嵌时钟信息
优势: 更少的物理线缆, 抗干扰能力强
硬件如何切换协议:
通过写入DCPHY的GRF寄存器来配置:
// 驱动代码示例 (简化)
// 文件: drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
// 配置为D-PHY模式
void dcphy_set_dphy_mode(struct dcphy *phy) {
// 写入GRF寄存器
regmap_write(phy->grf, DCPHY_GRF_CON0,
DCPHY_MODE_DPHY | DCPHY_LANES_4);
// 配置Lane数量和速率
regmap_write(phy->grf, DCPHY_GRF_CON1,
DCPHY_DATARATE_4500M);
}
// 配置为C-PHY模式
void dcphy_set_cphy_mode(struct dcphy *phy) {
// 写入GRF寄存器
regmap_write(phy->grf, DCPHY_GRF_CON0,
DCPHY_MODE_CPHY | DCPHY_TRIOS_3);
// 配置Trio数量和速率
regmap_write(phy->grf, DCPHY_GRF_CON1,
DCPHY_SYMBOLRATE_2500M);
}
配置后硬件内部发生的变化:
D-PHY模式:
物理Pin → 差分接收器 → 时钟数据恢复 → 字节对齐 → 输出
C-PHY模式:
物理Pin → 三相解码器 → 符号映射 → 字节转换 → 输出
2.1.2.3 RX/TX同时工作的限制
硬件约束:
虽然DCPHY有独立的RX和TX电路,但它们共享协议配置寄存器。
限制规则:
允许: RX用D-PHY + TX用D-PHY ✓
允许: RX用C-PHY + TX用C-PHY ✓
禁止: RX用D-PHY + TX用C-PHY ✗
禁止: RX用C-PHY + TX用D-PHY ✗
原因:
协议切换会改变整个DCPHY模块的时钟域和电气参数,RX和TX必须使用相同的基础时钟。
典型应用场景:
场景1: Camera输入 + MIPI显示屏输出
┌────────────┐ ┌──────────┐ ┌────────────┐
│ Camera │ MIPI │ DCPHY0 │ MIPI │ Display │
│ (D-PHY) ├─────→│ RX/TX ├─────→│ Panel │
│ 4 Lane RX │ │ (D-PHY) │ │ 4 Lane TX │
└────────────┘ └──────────┘ └────────────┘
↑ ↑
└──────── 两端都使用D-PHY协议 ─────────┘
场景2: 工业相机 + C-PHY显示
┌────────────┐ ┌──────────┐ ┌────────────┐
│ Camera │ MIPI │ DCPHY0 │ MIPI │ Display │
│ (C-PHY) ├─────→│ RX/TX ├─────→│ Panel │
│ 3 Trio RX │ │ (C-PHY) │ │ 3 Trio TX │
└────────────┘ └──────────┘ └────────────┘
↑ ↑
└──────── 两端都使用C-PHY协议 ─────────┘
2.1.3 DCPHY vs DPHY 对比
| 特性 | DCPHY | DPHY |
|---|---|---|
| 物理硬件 | 2个 (dcphy0/1) | 2个 (dphy0/1) |
| 寄存器地址 | 0xFEDC0000 / 0xFEDC8000 | 0xFEDC0000 / 0xFEDC8000 |
| 支持协议 | D-PHY + C-PHY可切换 | 仅D-PHY |
| 最大速率 | D-PHY: 4.5Gbps/Lane C-PHY: 2.5Gsps/Trio | D-PHY: 2.5Gbps/Lane |
| 数据方向 | RX + TX双向 | 仅RX单向 |
| Split模式 | 不支持 | 支持 (Full/Split) |
| 软件节点名 | csi2_dcphy0/1 | csi2_dphy0/1/2/3/4/5 |
| 典型应用 | 高端摄像头 + 显示输出 | 普通摄像头输入 |
| 驱动文件 | phy-rockchip-samsung-dcphy.c | phy-rockchip-csi2-dphy.c |
2.1.4 DPHY的Full Mode和Split Mode
2.1.4.1 硬件设计约束
DPHY硬件特点:
- 每个DPHY硬件包含4条Lane的接收电路
- 仅支持D-PHY协议
- 仅有RX接收功能
物理结构:
┌─────────────────────────┐
│ DPHY0硬件 (0xFEDC0000) │
├─────────────────────────┤
│ Lane0接收电路 │
│ Lane1接收电路 │
│ Lane2接收电路 │
│ Lane3接收电路 │
│ │
│ Lane路由选择器 ←────────┤ GRF配置寄存器
│ ├─ 全部4条 → CSI-2 Host │ 控制路由模式
│ └─ 拆分2+2 → 两个Host │
└─────────────────────────┘
2.1.4.2 Full Mode (全模式)
使用场景:
一个摄像头需要全部4条Lane的带宽。
硬件连接:
┌──────────────┐ ┌─────────────┐ ┌───────────────┐
│ OV13855 │ 4 Lane │ DPHY0硬件 │ 字节流 │ MIPI1_CSI2 │
│ Camera ├────────→│ (Full) ├────────→│ Host │
│ 4224x3136 │ MIPI │ 全部Lane │ │ 协议解析 │
└──────────────┘ └─────────────┘ └───────────────┘
软件节点名: csi2_dphy0 (表示dphy0_hw工作在Full模式)
寄存器配置:
// GRF寄存器配置Full Mode
regmap_write(grf, GRF_VI_CON0,
DPHY0_MODE_FULL | // 全部4条Lane
DPHY0_ENABLE); // 使能DPHY0
2.1.4.3 Split Mode (拆分模式)
使用场景:
两个低分辨率摄像头,每个使用2条Lane。
硬件连接:
┌─────────────┐ 2 Lane ┌──────────────────┐
│ Camera1 ├─────────→│ DPHY0硬件 │
│ 1920x1080 │ │ Lane0/1 ────────┼─→ MIPI1_CSI2
└─────────────┘ │ ↓ │
│ 路由选择器 │
┌─────────────┐ 2 Lane │ ↓ │
│ Camera2 ├─────────→│ Lane2/3 ────────┼─→ MIPI2_CSI2
│ 1920x1080 │ │ │
└─────────────┘ └──────────────────┘
软件节点名:
- csi2_dphy1 (dphy0_hw的前半部分, Lane0/1)
- csi2_dphy2 (dphy0_hw的后半部分, Lane2/3)
寄存器配置:
// GRF寄存器配置Split Mode
regmap_write(grf, GRF_VI_CON0,
DPHY0_MODE_SPLIT | // 拆分为2+2
DPHY0_SPLIT_LANE01 | // Lane0/1 → CSI2_1
DPHY0_SPLIT_LANE23 | // Lane2/3 → CSI2_2
DPHY0_ENABLE);
2.1.4.4 软件节点命名的逻辑
为什么Full模式叫dphy0,Split模式叫dphy1/dphy2?
驱动通过PHY的ID序号判断模式:
// 驱动代码逻辑 (简化)
// 文件: drivers/phy/rockchip/phy-rockchip-csi2-dphy.c
if (phy_id == 0) {
// ID=0 → Full模式, 使用全部4条Lane
mode = DPHY_FULL_MODE;
lane_count = 4;
} else if (phy_id == 1 || phy_id == 2) {
// ID=1/2 → Split模式, 使用2条Lane
mode = DPHY_SPLIT_MODE;
lane_count = 2;
if (phy_id == 1)
lanes = LANE_0_1; // 前半部分
else
lanes = LANE_2_3; // 后半部分
}
完整映射表:
| 物理硬件 | 工作模式 | 软件节点名 | PHY ID | Lane分配 | 连接CSI-2 Host |
|---|---|---|---|---|---|
| dphy0_hw | Full | csi2_dphy0 | 0 | 0/1/2/3 | mipi1_csi2 |
| dphy0_hw | Split前半 | csi2_dphy1 | 1 | 0/1 | mipi1_csi2 |
| dphy0_hw | Split后半 | csi2_dphy2 | 2 | 2/3 | mipi2_csi2 |
| dphy1_hw | Full | csi2_dphy3 | 3 | 0/1/2/3 | mipi3_csi2 |
| dphy1_hw | Split前半 | csi2_dphy4 | 4 | 0/1 | mipi4_csi2 |
| dphy1_hw | Split后半 | csi2_dphy5 | 5 | 2/3 | mipi5_csi2 |
2.1.5 完整的PHY硬件拓扑
RK3588最多支持的摄像头配置:
方案1: 2个DCPHY + 4个DPHY(Split) = 6路摄像头
┌─────────┐
│ DCPHY0 │ → Camera1 (D-PHY 4Lane, 高端)
│ (4Lane) │
└─────────┘
┌─────────┐
│ DCPHY1 │ → Camera2 (D-PHY 4Lane, 高端)
│ (4Lane) │
└─────────┘
┌─────────┐
│ DPHY0 │ → Camera3 (2Lane, 普通)
│ Lane0/1 │
│ Lane2/3 │ → Camera4 (2Lane, 普通)
└─────────┘
┌─────────┐
│ DPHY1 │ → Camera5 (2Lane, 普通)
│ Lane0/1 │
│ Lane2/3 │ → Camera6 (2Lane, 普通)
└─────────┘
方案2: 2个DCPHY + 2个DPHY(Full) = 4路摄像头
┌─────────┐
│ DCPHY0 │ → Camera1 (C-PHY 3Trio, 工业级)
│ (3Trio) │
└─────────┘
┌─────────┐
│ DCPHY1 │ → Camera2 (D-PHY 4Lane)
│ (4Lane) │
└─────────┘
┌─────────┐
│ DPHY0 │ → Camera3 (D-PHY 4Lane)
│ (4Lane) │
└─────────┘
┌─────────┐
│ DPHY1 │ → Camera4 (D-PHY 4Lane)
│ (4Lane) │
└─────────┘
2.1.6 设备树配置完整示例
2.1.6.1 DCPHY配置 (D-PHY模式)
// DCPHY物理硬件节点
&csi2_dcphy0_hw {
status = "okay";
};
// DCPHY软件节点 (D-PHY模式)
&csi2_dcphy0 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
// RX端口: 接收Camera数据
port@0 {
reg = <0>;
dcphy0_in: endpoint {
remote-endpoint = <&ov13855_out>;
data-lanes = <1 2 3 4>; // D-PHY 4 Lane
};
};
// TX端口: 输出到CSI-2 Host
port@1 {
reg = <1>;
dcphy0_out: endpoint {
remote-endpoint = <&mipi0_csi2_input>;
};
};
};
};
2.1.6.2 DPHY Full Mode配置
// DPHY物理硬件节点
&csi2_dphy0_hw {
status = "okay";
};
// DPHY软件节点 (Full Mode)
&csi2_dphy0 { // 注意: Full模式使用ID=0
status = "okay";
ports {
port@0 {
dphy0_in: endpoint {
remote-endpoint = <&camera_4lane_out>;
data-lanes = <1 2 3 4>; // 全部4条Lane
};
};
port@1 {
dphy0_out: endpoint {
remote-endpoint = <&mipi1_csi2_input>;
};
};
};
};
2.1.6.3 DPHY Split Mode配置
// DPHY物理硬件节点
&csi2_dphy0_hw {
status = "okay";
};
// DPHY软件节点1 (Split前半)
&csi2_dphy1 { // 注意: Split模式使用ID=1
status = "okay";
ports {
port@0 {
dphy1_in: endpoint {
remote-endpoint = <&camera1_2lane_out>;
data-lanes = <1 2>; // 只用Lane 0/1
};
};
port@1 {
dphy1_out: endpoint {
remote-endpoint = <&mipi1_csi2_input>;
};
};
};
};
// DPHY软件节点2 (Split后半)
&csi2_dphy2 { // 注意: Split模式使用ID=2
status = "okay";
ports {
port@0 {
dphy2_in: endpoint {
remote-endpoint = <&camera2_2lane_out>;
data-lanes = <1 2>; // 只用Lane 2/3
};
};
port@1 {
dphy2_out: endpoint {
remote-endpoint = <&mipi2_csi2_input>;
};
};
};
};
2.1.7 驱动文件路径与补充资料
DCPHY驱动:
- 硬件层:
drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c - 软件封装:
drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c - 设备树:
arch/arm64/boot/dts/rockchip/rk3588s.dtsi
DPHY驱动:
- 硬件层:
drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c - 软件封装:
drivers/phy/rockchip/phy-rockchip-csi2-dphy.c - 设备树:
arch/arm64/boot/dts/rockchip/rk3588s.dtsi
补充:香橙派我用的是OV13855的CAM2接口
查询原理图发现接入的是D-PHY0节点
同时为了作为以后参考资料,我这里也记录一下其他的节点:
2.2 MIPI CSI-2 Host:协议解析核心
RK3588有6个MIPI CSI-2 Host硬件:
| CSI-2 Host | 物理地址 | 对应PHY |
|---|---|---|
| mipi0_csi2 | 0xFDD10000 | csi2_dcphy0 |
| mipi1_csi2 | 0xFDD20000 | csi2_dphy1 |
| mipi2_csi2 | 0xFDD30000 | csi2_dphy2 |
| mipi3_csi2 | 0xFDD40000 | csi2_dphy3 |
| mipi4_csi2 | 0xFDD50000 | csi2_dphy4 |
| mipi5_csi2 | 0xFDD60000 | csi2_dphy5 |
2.3 VICAP:视频采集与格式转换
2.3.1 硬件唯一性与软件虚拟化
硬件现实:
RK3588只有1个VICAP物理硬件,寄存器地址0xFDCE0000。
但需要支持7路输入:
- 6路MIPI (来自6个CSI-2 Host)
- 1路DVP并口
软件虚拟化方案:
将1个VICAP硬件虚拟成7个软件节点:
rkcif_mipi_lvds (对应 mipi0_csi2)
rkcif_mipi_lvds1 (对应 mipi1_csi2)
rkcif_mipi_lvds2 (对应 mipi2_csi2)
rkcif_mipi_lvds3 (对应 mipi3_csi2)
rkcif_mipi_lvds4 (对应 mipi4_csi2)
rkcif_mipi_lvds5 (对应 mipi5_csi2)
rkcif_dvp (对应 DVP接口)
虚拟化的实现:
- 每个虚拟节点配置VICAP硬件的不同输入MUX选择
- 通过寄存器切换数据源
- 驱动代码中用ID标识不同的虚拟通道
2.3.2 SDITF:软件接口虚拟设备
每个rkcif_mipi_lvds对应一个sditf节点:
rkcif_mipi_lvds_sditf
rkcif_mipi_lvds1_sditf
...
sditf的作用:
指明VICAP与ISP之间的连接关系,是纯软件概念,不对应物理硬件。
2.3.3 驱动文件路径
设备树定义:arch/arm64/boot/dts/rockchip/rk3588s.dtsi
驱动代码:
drivers/media/platform/rockchip/cif/hw.c(VICAP硬件操作)drivers/media/platform/rockchip/cif/dev.c(rkcif设备)drivers/media/platform/rockchip/cif/subdev-itf.c(sditf设备)
2.4 ISP:图像信号处理核心
2.4.1 硬件组成
RK3588有2个独立的ISP硬件:
| ISP | 寄存器基地址 | 中断号 | 电源域 |
|---|---|---|---|
| ISP0 | 0xFDCB0000 | 131,133,134 | PD27 |
| ISP1 | 0xFDCC0000 | 135,137,138 | PD28 |
每个ISP包含的硬件模块:
- 输入控制模块(输入MUX)
- 镜头阴影校正(LSC)硬件
- 黑电平校正(BLC)硬件
- 坏点校正(DPCC)硬件
- 去噪模块(2DNR/3DNR)硬件
- 色彩矫正矩阵(CCM)硬件
- 伽马校正(Gamma)硬件
- 锐化(Sharpening)硬件
- HDR合成硬件
- 统计数据提取硬件(AE/AWB/AF)
2.4.2 两条输出路径
每个ISP硬件内部有两条独立的数据输出通路:
Main Path (主路径):
- 最大分辨率: 4416x3312
- 用途: 拍照、录像等高质量输出
- 支持格式: YUV/RAW/JPEG
Self Path (自路径):
- 最大分辨率: 1920x1920
- 用途: 预览、视频通话等实时场景
- 支持格式: YUV/RGB
硬件实现:
两条路径共享前端ISP处理模块,在输出阶段分成两路,各自有独立的缩放器和输出DMA。
2.4.3 ISP虚拟化:为什么需要虚拟节点
硬件限制:
2个ISP硬件只能同时处理2路摄像头。
实际需求:
可能需要连接6个摄像头(6路MIPI)。
软件解决方案:时分复用
每个ISP硬件虚拟出多个ISP节点(rkisp0_vir0/vir1/vir2/vir3):
rkisp0_vir0 ┐
rkisp0_vir1 ├─→ 共用ISP0硬件 (时分复用)
rkisp0_vir2 ┘
rkisp1_vir0 ┐
rkisp1_vir1 ├─→ 共用ISP1硬件 (时分复用)
rkisp1_vir2 ┘
时分复用的实现原理:
回读模式(ReadBack):
- VICAP先将RAW数据写入DDR
- ISP依次从DDR读取不同camera的数据处理
- ISP0处理camera0 → camera1 → camera2
- ISP1处理camera3 → camera4 → camera5
直通模式(Bypass):
- VICAP直接将数据发给ISP,不经过DDR
- 延迟最低,性能最好
- 只支持一个虚拟节点
模式自动选择:
if (一个ISP只配置1个虚拟节点)
→ 使用直通模式
else
→ 使用回读模式
2.4.4 ISP Unite:双ISP联合模式
当单个ISP性能不足时,可以将两个ISP合并成一个超级ISP:
rkisp_unite节点:
- 同时使用ISP0和ISP1的硬件资源
- 最大支持48M像素处理(8064x6048@15fps)
- 寄存器覆盖两个ISP的地址空间
2.4.5 驱动文件路径
设备树定义:arch/arm64/boot/dts/rockchip/rk3588s.dtsi
驱动代码:
drivers/media/platform/rockchip/isp/dev.c(ISP设备总入口)drivers/media/platform/rockchip/isp/hw.c(ISP硬件操作)drivers/media/platform/rockchip/isp/rkisp1.c(ISP子设备)drivers/media/platform/rockchip/isp/capture.c(视频输出)drivers/media/platform/rockchip/isp/isp_params.c(参数配置)drivers/media/platform/rockchip/isp/isp_stats.c(统计数据)
2.5 ISP内部:图像处理算法模块
2.5.1 数据流经的硬件处理单元
RAW图像在ISP内部的处理顺序:
RAW输入 → BLC → DPCC → LSC → Bayer 2DNR → Bayer 3DNR →
Debayer → CCM → Gamma → YUV 2DNR → Sharp → 输出
↓
统计数据提取(3A)
2.5.2 黑电平校正 (BLC - Black Level Correction)
硬件原理:
传感器即使不接收光线,也会输出一个非零的暗电流值。
BLC硬件功能:
从每个像素值减去一个固定偏移量。
寄存器地址:RKISP1_CIF_ISP_BLS_* (定义在drivers/media/platform/rockchip/isp1/regs.h)
驱动配置函数:rkisp1_bls_config() (文件isp_params.c)
// 为4个Bayer通道分别设置黑电平
writel(arg->fixed_val.r, base + RKISP1_CIF_ISP_BLS_A_FIXED); // R通道
writel(arg->fixed_val.gr, base + RKISP1_CIF_ISP_BLS_B_FIXED); // Gr通道
writel(arg->fixed_val.gb, base + RKISP1_CIF_ISP_BLS_C_FIXED); // Gb通道
writel(arg->fixed_val.b, base + RKISP1_CIF_ISP_BLS_D_FIXED); // B通道
2.5.3 坏点校正 (DPCC - Defect Pixel Cluster Correction)
硬件原理:
传感器制造时存在物理缺陷,某些像素点永久损坏,输出值异常。
DPCC硬件功能:
- 检测异常像素点(与周围像素值差异过大)
- 用周围像素的平均值替代
两种模式:
- 静态坏点表: 出厂时标定的固定坏点位置
- 动态检测: 实时检测可能的坏点
寄存器地址:RKISP1_CIF_ISP_DPCC_*
驱动配置函数:rkisp1_dpcc_config() (文件isp_params.c)
2.5.4 镜头阴影校正 (LSC - Lens Shading Correction)
硬件原理:
镜头边缘的光线通量小于中心,导致图像四周发暗(称为"暗角")。
LSC硬件功能:
- 将图像划分成17x17共289个区域
- 每个区域有独立的增益系数
- 中心区域增益=1.0,边缘区域增益>1.0
数据结构:
4个17x17的增益表(R/Gr/Gb/B四个通道)
寄存器地址:RKISP1_CIF_ISP_LSC_*
驱动配置函数:rkisp1_lsc_config() (文件isp_params.c)
// 将校正表写入ISP的SRAM
for (i = 0; i < 17; i++) {
for (j = 0; j < 17; j++) {
data = (r_data_tbl[i][j] << 12) | r_data_tbl[i][j+1];
writel(data, base + RKISP1_CIF_ISP_LSC_R_TABLE_DATA);
}
}
2.5.5 2D去噪 (2DNR - 2D Noise Reduction)
硬件原理:
传感器读取电路产生的随机噪声在空间上是孤立的。
2DNR硬件功能:
分析当前像素与周围像素的相似度,对差异小的区域进行空间滤波平滑。
Bayer 2DNR vs YUV 2DNR:
- Bayer 2DNR: 在Debayer之前处理,保留原始信息
- YUV 2DNR: 在Debayer之后处理,计算量更小
2.5.6 3D去噪 (3DNR - 3D Noise Reduction)
硬件原理:
噪声在时间维度上是随机的,但真实物体在连续帧之间是相关的。
3DNR硬件功能:
- 比较当前帧与前一帧的对应像素
- 对变化小的区域进行时间域滤波
- 需要缓存前一帧数据
硬件实现:
ISP内部有专用的frame buffer存储参考帧。
2.5.7 3A统计数据提取
3A定义:
- AE (Auto Exposure): 自动曝光
- AWB (Auto White Balance): 自动白平衡
- AF (Auto Focus): 自动对焦
硬件统计模块功能:
AE统计硬件:
- 将图像分成25x25个窗口
- 每个窗口计算亮度平均值和直方图
- 输出数据供用户空间算法计算曝光参数
AWB统计硬件:
- 将图像分成多个测光区域
- 每个区域统计R/G/B三个通道的平均值
- 识别可能是白色的区域
AF统计硬件:
- 计算图像的高频成分(清晰度指标)
- 输出锐度值供对焦算法使用
统计数据输出:
通过/dev/video18(ISP0)和/dev/video27(ISP1)输出给用户空间的rkaiq_3A_server进程。
寄存器地址:RKISP1_CIF_ISP_AWB_*、RKISP1_CIF_ISP_AFM_*、RKISP1_CIF_ISP_HIST_*
驱动读取函数:rkisp1_stats_isr() (文件isp_stats.c)
2.6 Pipeline概念:连接硬件的软件脉络
2.6.1 Pipeline的本质
Pipeline不是硬件,是软件的数据流管理机制。
定义:
从摄像头sensor到最终video设备节点的完整数据通路链条。
一条完整的Pipeline:
m01_b_ov13855 (sensor)
→ csi2_dphy0 (PHY)
→ mipi2_csi2 (CSI-2 Host)
→ rkcif_mipi_lvds2 (VICAP)
→ rkcif_mipi_lvds2_sditf (虚拟接口)
→ rkisp0_vir0 (ISP虚拟节点)
→ video11 (mainpath输出)
→ video12 (selfpath输出)
2.6.2 Pipeline的软件实现
文件路径:drivers/media/platform/rockchip/isp/dev.c
关键数据结构:
struct rkisp1_pipeline {
struct v4l2_subdev *subdevs[RKISP1_MAX_PIPELINE]; // 子设备数组
int num_subdevs; // 子设备数量
};
Pipeline管理函数:
rkisp1_pipeline_open() // 打开pipeline,启用所有子设备
rkisp1_pipeline_close() // 关闭pipeline
rkisp1_pipeline_set_stream() // 控制整条pipeline的数据流
2.6.3 Media Controller框架
Linux内核用Media Controller描述Pipeline:
查看命令:
media-ctl -d /dev/media0 --print-topology
输出示例:
- entity 1: m01_b_ov13855 (sensor)
pad0: Source → csi2_dphy0:0
- entity 2: csi2_dphy0 (PHY)
pad0: Sink ← m01_b_ov13855:0
pad1: Source → mipi2_csi2:0
... (完整拓扑链路)
2.7 实践篇:驱动开发的重点文件
当你需要移植新的摄像头时,重点关注:
设备树配置 (你已完成)
定义硬件连接关系sensor驱动 (如果官方没有)
drivers/media/i2c/ov13855.c
实现上电、寄存器配置、格式协商ISP参数调优 (图像质量优化)
需要IQ工程师使用Rockchip的Tuner工具调整ISP参数文件
不需要修改的部分:
- PHY驱动(rockchip已实现)
- CSI-2驱动(rockchip已实现)
- VICAP驱动(rockchip已实现)
- ISP驱动(rockchip已实现)
2.8 总结:完整硬件拓扑图
┌──────────┐
│ Camera │(OV13855)
│ Sensor │
└────┬─────┘
│ MIPI差分信号 (4 Lane)
↓
┌────────────┐
│ D-PHY HW │ csi2_dphy0 (物理层)
│ 0xFEDC0000 │ 差分→数字转换
└─────┬──────┘
│ 字节流
↓
┌──────────────┐
│ MIPI CSI-2 │ mipi2_csi2 (协议层)
│ Host │ 0xFDD30000
│ 解包+错误检测│
└──────┬───────┘
│ 结构化数据包
↓
┌─────────────┐
│ VICAP │ rkcif_mipi_lvds2 (唯一硬件)
│ 0xFDCE0000 │ 格式转换
└──────┬──────┘
│ RAW pixel数据
├→ 直通模式 ─────────┐
└→ 回读模式(DDR) ──┐ │
↓ ↓
┌──────────┐
│ISP0 HW │ rkisp0_vir0 (虚拟节点)
│0xFDCB0000│
│图像处理 │
└────┬─────┘
├→ Main Path → video11 (高分辨率)
└→ Self Path → video12 (低分辨率)
├→ Stats → video18 (统计数据)
└→ Params → video19 (参数输入)
浙公网安备 33010602011771号