文章目录
- [1. 背景](#1. 背景)
- [2. 入门](#2. 入门)
-
- [2.1 ov8858](#2.1 ov8858)
- [2.2 通信协议](#2.2 通信协议)
-
- [2.2.1 控制通道:为什么MIPI系统中需要I2C?](#2.2.1 控制通道:为什么MIPI系统中需要I2C?)
- [2.2.2 MIPI CSI-2硬件连接](#2.2.2 MIPI CSI-2硬件连接)
- [2.2.3 RK3588 如何识别与处理 MIPI 信号](#2.2.3 RK3588 如何识别与处理 MIPI 信号)
-
- 硬件基础:专用接收控制器
- 【拓】为什么需要"串行→并行→串行"的转换
- [rk3588s的芯片有2个独立的MIPI D-PHY控制器](#rk3588s的芯片有2个独立的MIPI D-PHY控制器)
- 【拓】抽象到设备树
- [2.2.4 数据流向:从线到内存的旅程](#2.2.4 数据流向:从线到内存的旅程)
- [2.3 软件与驱动的角色](#2.3 软件与驱动的角色)
- [3. 设备树和配置](#3. 设备树和配置)
-
- [3.1 怎么完成各个链路的连接](#3.1 怎么完成各个链路的连接)
- [3.2 整体配置](#3.2 整体配置)
-
- [3.2.1 Sensor配置](#3.2.1 Sensor配置)
- [3.2.2 MIPI/CIF 接收端](#3.2.2 MIPI/CIF 接收端)
- [3.2.3 ISP 节点](#3.2.3 ISP 节点)
- [3.2.4 总结](#3.2.4 总结)
- [4. 总结](#4. 总结)
-
- [4.1 理解硬件](#4.1 理解硬件)
- [4.2 理解通信](#4.2 理解通信)
- [4.3 理解数据流向与设备树配置](#4.3 理解数据流向与设备树配置)
- [4.4 实践要点](#4.4 实践要点)
1. 背景
本文旨在记录ov8858在rk3588s的bring up,主要是设备树部分,重要的是理解从Sensor到RK3588发生了什么
推荐的blog:RK3588:MIPI底层驱动学习------入门第一篇(万字开篇!真正零基础!)_rk3588 mipi-CSDN博客
需要明确以下几点
- 处理的对象是什么?
- 它使用什么方式与外界沟通?
- 以及我们如何控制它?
需要了解一下几点:
1.ov8858基本参数
2.mipi协议
3.图像如何从sensor到rk3588的链路(重要)
整个过程了解之后,就可以上手改设备树了;
2. 入门
2.1 ov8858
这是一个800万像素CMOS图像传感器,下面是一些比较重要的参数
| 参数类别 | 具体规格 | 备注与说明 |
|---|---|---|
| 最大帧率 | 30fps@(3264x2448) ;60fps@1080P (1920x1080);90fps@720P (1408x792) | 支持高速视频录制,1080P@60fps和720P@90fps是其优势,且为EIS(电子防抖)留出了额外像素。 |
| 输出接口 | MIPI CSI-2 (支持4通道) | 移动设备主流的高速串行接口,传输RAW数据。 |
| 输出数据格式 | 10-bit RAW RGB | 输出原始的拜耳数据,交由后端ISP进行图像处理。 |
2.2 通信协议
⭐️MIPI协议负责传输图像数据,IIC协议负责控制配置摄像头;图像信息通过编码成 MIPI信号 ,经过MIPI接口之后进入RK3588芯片
⭐️摄像头数据通过 MIPI 这根"水管" 传输。这根水管的物理规格 由 D-PHY 定义,水在里面流动的规则 由 CSI-2 定义。两者合作,才能把图像数据正确地从摄像头送到RK3588。
当OV8858以每秒30帧的速度生成800万像素的图像时,会产生巨大的数据量。如何将这些数据快速、稳定地从摄像头传输到RK3588这样的主处理器,是一个关键问题。MIPI协议就是为解决这个问题而生的。
定义:MIPI(移动行业处理器接口) 联盟是一个为移动设备(如智能手机)制定内部接口标准的组织。
目的:它旨在用更少的引脚(物理线路)、更低的功耗和更好的抗电磁干扰能力,来实现设备内部(如摄像头到处理器、处理器到显示屏)的高速数据传输。
MIPI联盟制定了多种接口规范,其中与摄像头和显示器最相关的是:
CSI (Camera Serial Interface) :相机串行接口,专门用于将图像数据从摄像头传输到处理器。
DSI (Display Serial Interface):显示器串行接口,专门用于将图像数据从处理器传输到显示屏。
OV8858摄像头作为图像输入设备,自然使用的是 MIPI CSI-2 协议。这里的"-2"代表其版本号。
2.2.1 控制通道:为什么MIPI系统中需要I2C?
我们已经知道,MIPI CSI-2是一条专为图像数据设计的高速、单向"数据通道"(数据从摄像头流向处理器)。但这引出了一个新的问题:处理器如何去配置和控制摄像头?
例如,处理器需要向摄像头发送指令,来完成以下操作:设置输出图像的分辨率;调整帧率,修改质量参数等工作;
MIPI CSI-2的高速数据线本身不承载这些控制指令。因此,系统需要一条独立的、双向的"控制通道"。
I2C协议 就是这个场景下的最佳选择:
双向通信:处理器可以通过I2C向摄像头写入配置数据,也可以从摄像头读取状态信息。
低速稳定:配置命令的发送频率不高,不需要很高的速度,I2C完全可以满足。
线路简单:I2C总线最少只需要两根线(SDA-数据线,SCL-时钟线),非常节省硬件资源。
🌿结论:在一个典型的摄像头系统中,MIPI CSI-2和I2C协同工作。MIPI CSI-2负责将庞大的图像数据从摄像头高速传输给处理器,而I2C则负责让处理器低速地发送控制命令给摄像头,两者各司其职,缺一不可。
逻辑线路:
摄像头需要控制接口
↓
选择I2C标准
↓
RK3588有多个I2C控制器
↓
板子设计选择了I2C7
↓
设备树配置&i2c7
2.2.2 MIPI CSI-2硬件连接
MIPI 数据传输如何保证数据的准确性,每个数据线独立工作,对原始数据进行编码,时钟信息"隐藏"在数据流中,每根数据线独立工作,不需要严格的外部同步
一对时钟差分线,几对数据差分线
| 特性维度 | 传统并行接口 | MIPI CSI-2 (串行接口) |
|---|---|---|
| 信号数量 | 1根专用时钟线 + N根数据线 (多) | 1对时钟差分线 + 最多4对数据差分线 (少) |
| 同步方式 | 依靠外部统一时钟,所有数据线严格同步 | 每根数据线独立恢复时钟,不依赖外部同步 |
| 高速适应性 | 差,线长差导致同步失效 | 优,差分信号+独立时钟恢复,抗干扰强 |
| 数据编码 | 通常不编码或简单编码 | 8B/10B 编码,保证信号跳变和直流平衡 |
-
问题 :在每秒几亿次的跳变速度下,即使0.1毫米的线长差异,都会导致信号到达接收端的时间不同步。如果所有数据线都共用一个外部时钟,高速下会完全乱套。
MIPI的方案 :每根数据线都独立工作 。接收端(RK3588)的硬件电路会从数据流本身的跳变中 ,利用锁相环(PLL) 技术,动态地提取出时钟信息,从而生成一个与该数据线完美同步的本地时钟。这就像每个乐手都自带了节拍器,并能根据音乐流实时调整,不再依赖指挥棒。
-
8B/10B编码的核心作用
如何确保数据流本身能够提供足够多的时钟跳变信号?
时钟恢复电路(PLL)需要信号有频繁的跳变 (0→1或1→0)才能"锁定"频率。但图像数据(如大片纯白或纯黑)会出现连续的"1"或"0",长串的无跳变信号会导致PLL失锁,无法恢复时钟。
2.2.3 RK3588 如何识别与处理 MIPI 信号
硬件基础:专用接收控制器
RK3588芯片内部集成了专门的硬件电路模块 ,称为 MIPI CSI-2 接收控制器 (或称 PHY 和 控制器 )。这个控制器是专门为理解和处理MIPI CSI-2协议而设计的,它包含了:
- D-PHY物理层接口 :负责与差分线进行物理层面的连接,处理模拟信号,并完成串行到并行(SerDes) 的转换,以及8B/10B解码。
- 协议逻辑层 :负责解析MIPI CSI-2协议的各种包(Packet) ,如短包(Short Packet,包含帧号、帧开始/结束标志)和长包(Long Packet,包含实际的图像数据)。它还负责虚拟通道(Virtual Channel)识别 、数据类型(Data Type)判断等高级协议功能。
【拓】为什么需要"串行→并行→串行"的转换
既然并行传输快,为什么MIPI不直接用并行线传输,还要这么麻烦地转换呢?
| 特性 | 并行传输 (Parallel) | 串行传输 (Serial) | 现代高速接口(如MIPI)的策略 |
|---|---|---|---|
| 线路数量 | 多 (如8根数据线) | 少 (如1对差分线) | 外部传输用串行 (节省线材、简化连接器、抗干扰) |
| 时钟同步 | 要求极高 (所有线必须精确对齐) | 要求低 (按顺序接收即可) | 内部处理用并行 (减少复杂度,提高效率) |
| 速度上限 | 低 (线间干扰(crosstalk)和偏移(skew)严重) | 高 (单线理论速度快,干扰小) | 串行解串后,用高速并行总线在芯片内部传输 |
| 抗干扰能力 | 差 (多线间易互相干扰) | 强 (差分信号抗干扰能力强) | 差分串行线负责"最后的一公里" |
| 典型应用 | 早期低速接口 (如老式并行ATA) | 现代高速接口 (如USB, SATA, PCIe, MIPI) | 结合两者优势 |
也就是说并行传输虽然快,但是时钟同步要求高,抗干扰能力较差;而串行传输虽然没有那么高的时钟要求,但是速度比较快,干扰小;
这是并行传输在高速下无法逾越的物理障碍。
- 想象:8辆卡车同时从起点出发,希望同时到达终点。
- 现实 :由于每条路(线路)的长度、材质、环境(温度)略有不同,每辆车的速度和到达时间不可能完全一致。
- 结果 :到了终点,车不再是并排到达,而是有的早,有的晚 ,乱成一团。接收端根本无法确定哪个是"第一位"的数据。这就是偏移。速度越快,这个问题越严重。
- 解决方案:要么在每根线上加复杂的可调延迟电路(成本高),要么放弃并行,改用串行。
现代接口的智慧:外串内并
所以,像MIPI CSI-2、USB、PCIe这样的高速接口,采用了**"外串内并"**的混合策略:
- 外部(芯片之间) :使用高速差分串行传输。只占1-2对线,抗干扰,速度快,解决了偏移问题。
- 内部(芯片内部) :在接收芯片的PHY层(物理层),通过串并转换器(SerDes) 将高速串行流转换成低速并行数据 (例如8位、16位或32位宽),然后通过芯片内部高速并行总线传输给核心逻辑进行处理。
总结一下
- 摄像头内部:图像传感器产生的并行数据,首先通过串行器转换为高速串行差分信号,通过MIPI线缆发送。
- RK3588接收端:
csi2_dcphy0物理层模块的解串器(Deserializer)接收这个串行流。 - 关键转换:解串器内部的移位寄存器在恢复的时钟驱动下,逐位接收串行数据。每接收满8个(或更多,取决于总线宽度)比特,锁存器就"抓拍"一下,一次性输出8个(或更多)并行比特。
- 后续处理:这些并行数据通过RK3588芯片内部的并行总线,被高效地传输到图像信号处理器(ISP)或其他核心模块进行后续处理。
rk3588s的芯片有2个独立的MIPI D-PHY控制器
https://wiki.t-firefly.com/zh_CN/Core-3588J/usage_camera.html
RK3588/RK3588S平台支持两个DPHY硬件, 分别是 dphy0_hw/ dphy1_hw, 两个dphy硬件都可以工作在两个模式: full mode 和split mode, 其中 dphy0_hw 拆分为 csi2_dphy0/ csi2_dphy1/ csi2_dphy2 三个逻辑dphy(参见rk3588s.dtsi)。
Full Mode 用于单路 4-lane 高带宽摄像头。
Split Mode 用于双路 2-lane 中等带宽摄像头。
【拓】抽象到设备树
bash
&csi2_dcphy0 { // 引用第0号D-PHY控制器 解码信号,变成并行信号
port@0 { // 控制器的输入端口
mipi_in_cam1: endpoint@1 { // 输入端口的第1号通道
reg = <1>; // 对应硬件的通道1
remote-endpoint = <&ov8858_out>; // 物理连接到OV13855
data-lanes = <1 2>; // 使用物理lane1和lane2
};
};
};
csi2_dcphy0↔ 芯片内存地址0xFED90000的硬件寄存器组endpoint@1↔ 该硬件的第1号接收通道data-lanes = <1 2>↔ 启用硬件的MIPI_RXD1和MIPI_RXD2引脚组mipi_in_cam1:- RK3588第0号MIPI D-PHY控制器
- 该控制器内部的第1号接收通道(cam1)
- 对应芯片封装上的特定MIPI物理引脚组
关于是如何具体作用的,可以参考:RK3588:MIPI底层驱动学习------入门第一篇(万字开篇!真正零基础!)_rk3588 mipi-CSDN博客
2.2.4 数据流向:从线到内存的旅程
上面说到了D-PHY用于解析MIPI的数据,下面说一下整个的过程:
整个链路是:
bash
OV8858 摄像头传感器
│
│ MIPI CSI-2 数据
↓
┌─────────────┐
│ MIPI D-PHY │ ← 物理层硬件(串行接收)
└─────────────┘
│
│ CSI-2 协议
↓
┌─────────────┐
│ CSI2 Host │ ← CSI2 接收器(协议解析)
└─────────────┘
│
↓
┌─────────────┐
│ CIF │ ← 摄像头接口控制器(数据捕获)
└─────────────┘
│
↓
┌─────────────┐
│ RKCIF │ ← Rockchip CIF 控制器(统一管理)
└─────────────┘
│
↓
┌─────────────┐
│ SDITF │ ← 数据转发桥接模块(跨域传输)
└─────────────┘
│
↓
┌─────────────┐
│ RKISP │ ← 图像信号处理器(色彩/曝光优化)
└─────────────┘
│
↓
/dev/video0 ← 用户空间访问节点
具体地:
| 模块 | 英文全称 | 中文 | 作用 |
|---|---|---|---|
| CIF | Camera Interface | 摄像头接口 | 接收 MIPI 数据流 |
| RKCIF | Rockchip CIF | Rockchip 摄像头接口 | 统一管理多个摄像头 |
| SDITF | Sensor Data Interface Transport Fabric | 数据转发桥接 | 跨硬件域传输数据 |
| RKISP | Rockchip ISP | 图像信号处理器 | 图像处理(锐化、降噪、色彩) |
摄像头数据通过MIPI CSI-2接口最终进入RK3588的内存,并可以被CPU读取,大致流程如下:

在Rockchip的内核中,下面描述了从物理传感器到最终图像处理单元的数据流与控制流,每一层都有明确的职责:
1. Sensor 层
- 实体:OV8858 图像传感器
- 职责:将光信号转换为电信号,通过内部 ADC 输出 RAW Bayer 格式数据,并按照 I2C 配置输出符合 MIPI CSI-2 协议的串行差分信号
- 设备树体现 :
rk3588s-lubancat-4-csi.dtsi中定义为&i2c1下的子节点dcphy0_ov8858@36reg = <0x36>:I2C 从机地址clocks = <&ext_cam_24m_clk>:外部时钟源pwdn-gpios、reset-gpios:电源和复位控制dovdd-supply、avdd-supply、dvdd-supply:数字、模拟、核心电压port子节点:通过remote-endpoint连接到 CSI2 Host
2. MIPI D-PHY(物理层)
- 实体 :
mipi_dcphy0或mipi_dphy0 - 职责:接收来自 Sensor 的 MIPI 串行差分信号,完成物理层的信号接收、串并转换(Serializer → Deserializer),输出并行数据给 CSI2 Host
- 设备树体现 :
rk3588s.dtsi中定义
dts
&mipi_dcphy0 {
rockchip,grf = <&mipidphy0_grf>;
rockchip,sys_grf = <&sys_grf>;
resets = <&cru SRST_CSIPHY0>;
};
3. CSI2 Host(协议层)
- 实体 :
csi2_dcphy0、csi2_dphy0 - 职责:解析 CSI-2 协议数据包,提取有效图像数据,处理数据通道(lane)配置、帧同步、CRC 校验等
- 设备树体现 :
rk3588s-lubancat-4-csi.dtsi中
dts
&csi2_dcphy0 {
port@0 {
dcphy0_in_ov8858: endpoint@2 {
reg = <2>;
remote-endpoint = <&ov8858_dcphy0_out>;
data-lanes = <1 2>;
};
};
};
4. CIF(Camera Interface,摄像头接口层)
- 实体 :
rkcif_mipi_lvds(CIF 控制器) - 职责:接收 CSI2 Host 的并行数据,进行帧同步、格式转换(RAW/YUV),将数据存入 DMA 缓冲区,是连接底层硬件接口和上层驱动框架的桥梁
- 设备树体现 :
rk3588s-lubancat-4-csi.dtsi中
dts
&rkcif_mipi_lvds {
port {
cif_mipi_in0: endpoint {
remote-endpoint = <&mipi0_csi2_output>;
};
};
};
5. RKCIF(Rockchip Camera Interface Framework,管理层)
- 实体 :
rkcif+rkcif_mmu - 职责:统一管理多个 CIF 硬件资源,处理时钟、中断、DMA 通道调度,提供统一的 API 给上层 ISP 或用户空间应用程序
- 设备树体现:
dts
&rkcif {
status = "okay";
power-domains = <&power RK3588_PD_VI>;
iommus = <&rkcif_mmu>;
};
&rkcif_mmu {
status = "okay";
};
6. SDITF(Sensor Data Interface Transport Fabric,跨域桥接层)
- 实体 :
rkcif_mipi_lvds_sditf - 职责:RK3588 新增的跨域数据转发模块,负责将数据从 CIF 域(Camera Domain)传输到 ISP 域(Image Processing Domain)。由于 CIF 和 ISP 位于不同的硬件时钟域,需要 SDITF 进行跨域数据传输和同步
- 设备树体现 :
rk3588s-lubancat-4-csi.dtsi中
dts
&rkcif_mipi_lvds_sditf {
port {
mipi_lvds_sditf: endpoint {
remote-endpoint = <&isp0_vir0>;
};
};
};
7. RKISP(Rockchip Image Signal Processor,处理层)
- 实体 :
rkisp0_vir0(ISP 虚拟通道) - 职责 :对 RAW Bayer 数据进行复杂的图像处理,包括:
- 去马赛克(Demosaic / Bayer → RGB)
- 自动白平衡(AWB)
- 自动曝光(AE)
- 自动对焦(AF)统计
- 降噪(NR)、锐化(Sharpen)
- 色彩校正(CCM)、Gamma 校正
- 最终输出 YUV/RGB 图像给用户空间
- 设备树体现:
dts
&rkisp0_vir0 {
port {
isp0_vir0: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi_lvds_sditf>;
};
};
};
完整链路图见下方 Mermaid 流程图:
bash
ov8858@36 (i2c1)
│
│ ov8858_dcphy0_out (endpoint)
▼
csi2_dcphy0 //1. 从信号跳变中恢复时钟; 2. 将串行数据转换为并行数据; 3. 解码8B/10B编码
│ dcphy0_in_ov8858 (endpoint@2)
│ csidcphy0_out
▼
mipi0_csi2 //分类;把数据信息和控制信息分离
│ mipi0_csi2_input / mipi0_csi2_output
▼
rkcif_mipi_lvds //MIPI CSI-2 原始数据采集
│ cif_mipi_in0
▼
rkcif_mipi_lvds_sditf //数据传输中介,可路由到不同 ISP 通道
│ mipi_lvds_sditf
▼
rkisp0_vir0 //isp处理(图像处理 )
│ isp0_vir0
▼
→ /dev/video0 (用户空间)
2.3 软件与驱动的角色
硬件完成了最核心的物理和协议解析工作,但要让系统"用"起来,还需要软件(特别是驱动)的配合:
-
设备树(Device Tree) :在Linux内核中,通过设备树文件(如你提到的
rk3588s-orangepi-5-camera2.dtsi)描述硬件连接信息,告诉内核哪个MIPI控制器连接了哪个摄像头,其配置参数(如时钟频率、 lanes 数)是什么 -
V4L2 驱动框架
内核中的
- 接收器驱动负责初始化并管理RK3588内部的MIPI控制器硬件。
- 传感器驱动负责通过I2C总线配置OV13855的工作模式 (分辨率、帧率、曝光等),并处理来自摄像头的中断(如帧同步信号)。
-
用户空间访问 :最终,用户空间的应用程序通过 V4L2 (Video for Linux 2) API 来请求和获取图像数据,驱动会准备好数据缓冲区,并通知应用有新的一帧数据可读。
3. 设备树和配置
对于lubancat-4-rk3588s,设备树位于:/kernel-stable-5.10-rk3588/arch/arm64/boot/dts/rockchip
3.1 怎么完成各个链路的连接
我们前面已经知道整个链路是怎么样的:
ov8858 传感器 → csi2_dcphy0 (硬件PHY) → mipi0_csi2 (CSI2控制器)
→ rkcif_mipi_lvds (Camera Interface) → rkcif_mipi_lvds_sditf (SDI TF)
→ rkisp0_vir0 (ISP 虚拟通道0)
bash
┌─────────────────────────────────────────────────────────────────────┐
│ 层级 设备树节点 作用 │
├─────────────────────────────────────────────────────────────────────┤
│ ┌──────────────────┐ │
│ Sensor │ dcphy0_ov8858@36 │ 传感器节点 (I2C地址0x36) │
│ └────────┬─────────┘ │
│ endpoint ┌────────▼─────────┐ │
│ ───────────────▶ │ ov8858_dcphy0_out │ 传感器输出端 │
│ └────────┬─────────┘ │
│ port@0 ┌────────▼─────────┐ │
│ ───────────────▶ │ dcphy0_in_ov8858 │ CSI2 输入端 (reg=2) │
│ └────────┬─────────┘ │
│ port@1 ┌────────▼─────────┐ │
│ ───────────────▶ │ csidcphy0_out │ CSI2 输出端 │
│ └────────┬─────────┘ │
│ port@0 ┌────────▼─────────┐ │
│ ───────────────▶ │ mipi0_csi2_input │ MIPI CSI2 输入端 │
│ └────────┬─────────┘ │
│ port@1 ┌────────▼─────────┐ │
│ ───────────────▶ │ mipi0_csi2_output│ MIPI CSI2 输出端 │
│ └────────┬─────────┘ │
│ ┌────────▼─────────┐ │
│ │ cif_mipi_in0 │ CIF 输入端 │
│ └────────┬─────────┘ │
│ ┌────────▼─────────┐ │
│ │ mipi_lvds_sditf │ SDITF 转发 │
│ └────────┬─────────┘ │
│ ┌────────▼─────────┐ │
│ │ isp0_vir0 │ ISP 虚拟通道0 │
│ └────────┬─────────┘ │
│ ▼ │
│ /dev/video0 ←─── 用户空间可访问 │
└─────────────────────────────────────────────────────────────────────┘
下面我们通过 port和endpoint,把这些模块都连接起来
bash
设备树结构 实际硬件连接
─────────────────────────────────────────────────
┌──────────────────┐ ┌─────────────────┐
│ ov8858 │ │ OV8858 摄像头 │
│ ┌────────────┐ │ │ ┌──────────┐ │
│ │ port │ │ │ │ MIPI OUT │──┼──┐
│ │ ┌──────┐ │ │ │ └──────────┘ │ │
│ │ │endpoint│ │ │ │ │ │
│ └──┴──────┴──┘ │ └─────────────────┘ │
└──────────────────┘ │
▼
┌─────────────────────────────────────────┐
│ CSI2 D-PHY │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ port@0 │ │ port@1 │ │
│ │ ┌─────────┐ │ │ │ │
│ │ │endpoint │◀┼──│ │ │
│ │ └─────────┘ │ │ │ │
│ └───────────────┘ └───────────────┘ │
└───────────────────────────────────────┘
具体的
port:代表一个硬件模块的数据接口(可以是输入或输出)
endpoint:代表port上的具体连接点
bash
/ {
/* 传感器 */
sensor: ov8858@36 {
port {
sensor_out: endpoint {
remote-endpoint = <&csi_in>; /* 连接 CSI */
data-lanes = <1 2>; /* 2 lane */
};
};
};
/* CSI2 控制器 - 有两个 port */
csi2: csi2@xxx {
ports {
/* Port 0: 接收端 */
port@0 {
csi_in: endpoint {
remote-endpoint = <&sensor_out>; /* 接收来自传感器 */
data-lanes = <1 2>;
};
};
/* Port 1: 输出端 */
port@1 {
csi_out: endpoint {
remote-endpoint = <&cif_in>; /* 连接到 CIF */
};
};
};
};
/* CIF 控制器 */
cif: cif@xxx {
port {
cif_in: endpoint {
remote-endpoint = <&csi_out>; /* 接收来自 CSI2 */
};
};
};
};
remote-endpoint:双向引用,endpointA.remote-endpoint = <&endpointB> 意味着 endpointB.remote-endpoint = <&endpointA>
reg 属性:区分同一 port 下的多个 endpoint(如 CSI2 支持多个传感器输入)
data-lanes:描述 MIPI 通道配置
3.2 整体配置
下面来看一下整个设备树的配置
以鲁班猫5为例:
整个设备树的层级为
bash
rk3588-lubancat-5.dts (主设备树)
└── include rk3588-lubancat-5-csi.dtsi (CSI接口配置)
└── include rk3588-lubancat-cam.dtsi (通用摄像头资源)
└── include rk3588-lubancat-5-cam0.dtsi (CAM0 默认配置)
3.2.1 Sensor配置
这段代码完整地描述了一个硬件:"我是一颗挂在 I2C 上的 OV8858,地址 0x36,需要 24M 时钟和三路供电,用 GPIO1_A3 和 A4 控制休眠复位,画面装反了要转180度,我带对焦马达,我通过 2 根 MIPI 数据线把画面发给 DCPHY0"
bash
dcphy0_ov8858: dcphy0-ov8858@36 {
compatible = "ovti,ov8858"; // 驱动匹配
status = "disabled"; // 默认禁用
reg = <0x36>; // I2C 从机地址
clocks = <&ext_cam_24m_clk>; // 外部时钟 24MHz; 时钟来源于CRU的第4路MIPI相机时钟
clock-names = "xvclk"; //时钟起个名字叫 "xvclk",驱动代码会通过这个来获取时钟
// 电源配置
dovdd-supply = <&cam_dovdd>; // 1.8V
avdd-supply = <&cam_avdd>; // 2.8V
dvdd-supply = <&cam_dvdd>; // 1.2V
// GPIO 控制
pwdn-gpios = <&gpio4 RK_PB0 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio4 RK_PB1 GPIO_ACTIVE_HIGH>;
// 摄像头模块信息
rockchip,camera-module-index = <0>;//摄像头编号(0 通常是主摄后摄)
rockchip,camera-module-facing = "back";//"back" 表示后置摄像头,"front" 表示前置
rockchip,camera-module-name = "HS5885-BNSM1018-V01";//iq文件
lens-focus = <&dcphy0_dw9714>; // 镜头名称 对焦马达用
//说明这个摄像头模组带有自动对焦功能,且使用的马达驱动芯片是 DW9714,该芯片也挂载在 I2C 上
port {
ov8858_dcphy0_out: endpoint {
remote-endpoint = <&dcphy0_in_ov8858>;//双向绑定,告诉内核这个输出端连到了 DCPHY0 的输入端。对应的,在 csi2_dphy0 节点里肯定有一个叫 dcphy0_in_ov8858 的端点回指过来
clock-lanes = <0>; //Lane 0 默认作为时钟通道
data-lanes = <1 2>; //表示使用 2 Lane 传输数据
};
};
};
0.关于设备树配置:
dcphy0_ov8858:这是节点的标签。设备树其他地方可以通过 &dcphy0_ov8858 来引用这个节点,方便后续修改状态或追加属性。
dcphy0-ov8858:节点的名称。通常格式是 设备名@地址,这里表明这是连接在 DCPHY0 上的 OV8858。
@36:表示该设备在 I2C 总线上的从机地址为 0x36(OV8858 的标准 I2C 地址,有些模组如果 ADDR 引脚拉高会是 0x10)
**1.关于时钟:**摄像头需要时钟 → RK3588s 的 CRU 产生时钟 → 时钟通过复用的 GPIO 引脚输出 → 设备树描述时钟来源和引脚功能切换。
【拓】 CRU
简单说:CRU就是芯片内部管理"什么时候给什么时钟"的控制器,它根据设备树配置决定哪些时钟开启、频率多少、输出到哪个引脚
CRU (Clock Reset Unit) 是Rockchip芯片内置的时钟和复位控制单元
①时钟生成
CRU内部有PLL(锁相环),可以将24MHz晶振输入倍频到各种工作频率
摄像头相关的典型输出:24MHz、480MHz(MIPI CSI时钟)等
②引脚复用 (IOMUX)
将内部时钟信号通过GPIO引脚输出到芯片外部
在设备树中体现为rockchip,pins配置
③在OV8858设备树中的体现
&cru {
assigned-clocks = <&cru PLL_GPLL>, <&cru PLL_VPLL>;
assigned-clock-rates = <1188000000>, <700000000>;
};
外部24MHz晶振 → RK3588 CRU → IOMUX复用 → GPIO引脚 → 摄像头XVCLK
2.关于电源:
OV13855 自身需要多个电压(如数字 1.8V、模拟 1.2V、I/O 2.8V)才能工作。
为了极致省电,当不使用摄像头时,不仅是摄像头芯片,甚至 RK3588 内部处理视频信号的相关模块(如 MIPI、ISP)都应该被完全断电。
3.2.2 MIPI/CIF 接收端
bash
&mipi_dcphy0 {
status = "disabled";
};
&csi2_dcphy0 {
status = "disabled";
ports {
port@0 {
dcphy0_in_ov8858: endpoint@2 {
reg = <2>;
remote-endpoint = <&ov8858_dcphy0_out>;
data-lanes = <1 2>;
};
dcphy0_in_imx415: endpoint@4 {
reg = <4>;
remote-endpoint = <&imx415_dcphy0_out>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
csidcphy0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi0_csi2_input>;
};
};
};
};
&mipi0_csi2 {
status = "disabled";
ports {
port@0 {
mipi0_csi2_input: endpoint@1 {
reg = <1>;
remote-endpoint = <&csidcphy0_out>;
};
};
port@1 {
mipi0_csi2_output: endpoint@0 {
reg = <0>;
remote-endpoint = <&cif_mipi_in0>;
};
};
};
};
3.2.3 ISP 节点
bash
&rkcif_mipi_lvds {
status = "disabled";
port {
cif_mipi_in0: endpoint {
remote-endpoint = <&mipi0_csi2_output>;
};
};
};
&rkcif_mipi_lvds_sditf {
status = "disabled";
port {
mipi_lvds_sditf: endpoint {
remote-endpoint = <&isp0_vir0>;
};
};
};
&rkisp0_vir0 {
status = "disabled";
port {
isp0_vir0: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi_lvds_sditf>;
};
};
};
3.2.4 总结
涉及文件
| 层级 | 文件 | 作用 |
|---|---|---|
| 1 | rk3588s-lubancat-4.dts | 主入口,包含所有其他文件 |
| 2 | rk3588s-lubancat-4-csi.dtsi | CSI接口和传感器节点定义 |
| 3 | rk3588-lubancat-cam.dtsi | 通用电源和时钟 |
| 4 | rk3588s.dtsi | 芯片级硬件定义 |
| 4 | rk3588s-pinctrl.dtsi | 引脚复用配置 |
| 5 | *overlay.dts | 动态启用设备(可选) |
主要流程
bash
┌─────────────────────────────────────────────────────────────────────────┐
│ Level 1: 主设备树文件 │
│ /rk3588s-lubancat-4.dts │
│ ├── #include "rk3588s.dtsi" ← 芯片级定义 │
│ ├── #include "rk3588-linux.dtsi" ← Linux通用配置 │
│ ├── #include "rk3588s-lubancat-4-csi.dtsi" ← CSI接口定义 │
│ └── #include "rk3588s-lubancat-4-cam0.dtsi" ← CAM0默认配置 │
│ ↓ │
│ (可能通过overlay覆盖) │
└─────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────┐
│ Level 2: CSI接口定义 │
│ /rk3588s-lubancat-4-csi.dtsi │
│ ├── #include "rk3588-lubancat-cam.dtsi" ← 通用摄像头资源 │
│ │ │
│ ├── &i2c1 { ... } ← DCPHY0 传感器定义 │
│ │ ├── dcphy0_dw9714 ← 对焦马达 │
│ │ ├── dcphy0_ov8858 ← OV8858传感器 ← [主要配置] │
│ │ └── dcphy0_ov5648 ... │
│ │ │
│ ├── &i2c5 { ... } ← DCPHY1 传感器定义 │
│ ├── &i2c6 { ... } ← DPHY0 传感器定义 │
│ │ │
│ ├── &csi2_dcphy0 {...} ← CSI2接收器(DCPHY0) │
│ ├── &csi2_dcphy1 {...} ← CSI2接收器(DCPHY1) │
│ ├── &csi2_dphy0_hw {...} ← CSI2接收器(DPHY0硬件) │
│ │ │
│ ├── &mipi0_csi2 {...} ← MIPI CSI2接口 │
│ ├── &mipi1_csi2 {...} ← MIPI CSI2接口 │
│ ├── &mipi2_csi2 {...} ← MIPI CSI2接口 │
│ │ │
│ ├── &rkcif {...} ← CIF控制器 │
│ ├── &rkcif_mipi_lvds {...} ← CIF MIPI接口 │
│ ├── &rkcif_mipi_lvds_sditf {...}← SDITF桥接 │
│ │ │
│ ├── &rkisp0 {...} ← ISP0控制器 │
│ └── &rkisp0_vir0 {...} ← ISP0虚拟接口 │
└─────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────┐
│ Level 3: 通用资源定义 │
│ /rk3588-lubancat-cam.dtsi │
│ ├── ext_cam_24m_clk ← 24MHz外部时钟源 │
│ ├── ext_cam_25m_clk ← 25MHz外部时钟源 │
│ ├── ext_cam_37m_clk ← 37MHz外部时钟源 │
│ ├── vdd_cam_5v ← 摄像头5V电源 │
│ ├── cam_dovdd ← 1.8V数字电源 │
│ ├── cam_avdd ← 2.8V模拟电源 │
│ └── cam_dvdd ← 1.2V核心电源 │
└─────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────┐
│ Level 4: 芯片级定义 │
│ /rk3588s.dtsi │
│ ├── &cru {...} ← 时钟和复位单元 │
│ ├── &sys_grf {...} ← 系统全局寄存器(地址0xFD58C000) │
│ ├── &pmu {...} ← 电源管理单元 │
│ ├── &mipi_dcphy0 {...} ← DCPHY0物理层 │
│ ├── &mipi_dcphy1 {...} ← DCPHY1物理层 │
│ └── &power {...} ← 电源域(RK3588_PD_VI) │
│ │
│ /rk3588s-pinctrl.dtsi │
│ ├── i2c1m2_xfer ← I2C1引脚复用 │
│ ├── mipim0_camera0_clk ← MIPI摄像头时钟输出引脚 │
│ └── ... (更多引脚配置) │
└─────────────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────────────┐
│ Level 5: Overlay覆盖层 (可选) │
│ /overlay/rk3588s-lubancat-4-cam0-ov8858-1632x1224-30fps-overlay.dts │
│ │
│ 用途: 动态启用/禁用设备,无需修改主设备树 │
│ ├── fragment@0: i2c1 status="okay" │
│ ├── fragment@1: dcphy0_ov8858 status="okay" │
│ ├── fragment@2: csi2_dcphy0 status="okay" │
│ ├── fragment@3: mipi0_csi2 status="okay" │
│ ├── fragment@4: mipi_dcphy0 status="okay" │
│ └── fragment@5-12: 其他模块启用 │
└─────────────────────────────────────────────────────────────────────────┘
| 信号类型 | 源头 | 目的地 | 作用 |
|---|---|---|---|
| 时钟 | CRU (24MHz) | OV8858 xvclk | 提供工作时钟 |
| 复位 | GPIO1 PA4 | OV8858 RESETB | 复位/启动 |
| 休眠 | GPIO1 PA3 | OV8858 PWDN | 开关机 |
| I2C | i2c1 | OV8858 0x36 | 配置寄存器 |
| 数据 | OV8858 MIPI | CSI2 → CIF → ISP | 图像数据 |
4. 总结
本文以 OV8858 + RK3588S 为实例,完整记录了从零开始完成一个 MIPI 摄像头 bring up 的全过程。回顾整个旅程,可以归纳为以下三个层次:
4.1 理解硬件
首先需要搞清楚「处理的对象是什么」------一颗 800 万像素的 OV8858 CMOS 图像传感器,它通过 MIPI CSI-2 接口输出 10-bit RAW 图像数据。理解它的基本参数(帧率、分辨率、数据格式)是后续一切工作的基础。
4.2 理解通信
接着要理解两条并行的通信通道:
- MIPI CSI-2(数据通道):负责高速、单向地将海量图像数据从摄像头传输到处理器。它采用差分串行传输,通过 D-PHY 定义物理层、CSI-2 定义协议层,配合 8B/10B 编码和 PLL 时钟恢复机制,在极少的物理线路上实现了几 Gbps 的高速传输。
- I2C(控制通道):负责低速、双向地让处理器配置摄像头的工作模式(分辨率、帧率、曝光等)。两者各司其职,缺一不可。
在此基础上,进一步理解了 RK3588 芯片内部的 MIPI CSI-2 接收控制器(D-PHY + 协议逻辑层)如何完成「串行→并行」的转换,以及「外串内并」策略为何是现代高速接口的共同选择。
4.3 理解数据流向与设备树配置
最后,梳理了从 OV8858 传感器到 RK3588 内存的完整数据链路:
Sensor → MIPI D-PHY → CSI2 → CIF → RKCIF → SDITF → RKISP → /dev/video0
核心模块:CIF(接收)+ RKCIF(控制)+ SDITF(桥接)+ RKISP(处理)
每一层都有明确的硬件实体和职责分工。而设备树正是通过 port 和 endpoint 以及 remote-endpoint 属性,将这条链路上的各个模块「串联」起来,使得 Linux 内核在启动时能正确识别并初始化整个摄像头子系统。
4.4 实践要点
在实际操作中,以下几个要点值得特别关注:
- 设备树是桥梁 :硬件连接信息全部通过设备树描述,正确配置
remote-endpoint的双向引用是关键。 - 电源与时钟不可忽视 :摄像头需要稳定的多路供电和外部时钟,设备树中的
*-supply和clocks属性必须与实际硬件设计一致。 - 驱动框架的配合:V4L2 驱动框架统一了对上(用户空间)和对下(硬件)的接口,理解 Sensor 驱动和接收器驱动的分工有助于排查问题。
- 调试从简到繁:先确保 I2C 通信正常(能读到 Sensor ID),再检查 MIPI 时钟和数据 lane 的配置,最后验证 ISP 输出。