RK3588:MIPI底层驱动学习——入门第六篇(摄像头数据处理全流程、软件\硬件双视角分析)
引言
当我们打开摄像头、看到一张清晰的图像时,很少有人意识到它其实经历了无数次格式变换和硬件转接
——从传感器吐出原始电信号,到屏幕显示彩色像素,这个过程比任何快递分拣系统都复杂得多。
为了更好的理顺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条LaneDPHY0_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+2DPHY0_SPLIT_LANE01 | // Lane0/1 → CSI2_1DPHY0_SPLIT_LANE23 | // Lane2/3 → CSI2_2DPHY0_ENABLE);
2.1.4.4 软件节点命名的逻辑
为什么Full模式叫dphy0,Split模式叫dphy1/dphy2?
驱动通过PHY的ID序号判断模式:
// 驱动代码逻辑 (简化)
// 文件: drivers/phy/rockchip/phy-rockchip-csi2-dphy.cif (phy_id == 0) {// ID=0 → Full模式, 使用全部4条Lanemode = DPHY_FULL_MODE;lane_count = 4;
} else if (phy_id == 1 || phy_id == 2) {// ID=1/2 → Split模式, 使用2条Lanemode = DPHY_SPLIT_MODE;lane_count = 2;if (phy_id == 1)lanes = LANE_0_1; // 前半部分elselanes = 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 Hostport@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=0status = "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=1status = "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=2status = "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:0pad1: 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 (参数输入)