一、前言
当前板子有三块摄像头接口,两处imx415,一处ov13855
配置的核心是确保数据流路径畅通:Sensor → DPHY → MIPI CSI-2 → RKCIF → ISP → VPSS
二、DTS配置
第一层:选接口 → 定 dphy → 定 pipeline
J25 / J19 / J20 分别用了 dphy0 / dphy1 / dphy3。"J25 → dphy0" 这层是硬件连线决定的,dts 只能照抄对应关系;选错 dphy = 永远 link 不上,以原理图为主,类似下图:
数据流(以 J25 为例):
imx415 → csi2_dphy0 → mipi0_csi2 → rkcif_mipi_lvds ↓ (_sditf) rkisp_vir0 → rkvpss_vir0 ↑ CIF 抓 RAW ↑ ISP 处理 ↑ 后处理第二层:sensor 节点(挂在 I2C 上)
以 J25 的 imx415 为例,逐项讲:
&i2c6 { status = "okay"; pinctrl-0 = <&i2c6m0_xfer>; imx415_0: imx415@1a { compatible = "sony,imx415"; status = "okay"; reg = <0x1a>; clocks = <&cru CLK_MIPI_CAMERAOUT_M0>; clock-names = "xvclk"; pinctrl-names = "default"; pinctrl-0 = <&cam_clk0m0_clk0>; power-domains = <&power RK3576_PD_VI>; reset-gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio3 RK_PC0 GPIO_ACTIVE_HIGH>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "CMK-OT2022-PX1"; rockchip,camera-module-lens-name = "IR0147-50IRC-8M-F20"; port { imx415_out2: endpoint { remote-endpoint = <&mipi_in_ucam4>; />![]()
⑤ power-domains = <&power RK3576_PD_VI>
VI 电源域,必填,漏了会在 runtime PM 时报 -13/挂死。
⑥ reset-gpios / pwdn-gpios
极性按模组规格书,这是最容易搞反的:
- 此次imx415:reset = LOW 有效、pwdn = HIGH 有效
- 此次ov13855:reset = HIGH 有效、pwdn = HIGH 有效
同为 imx415,J25 用 GPIO3_PB7/PC0,J19 用 GPIO3_PC5/PC7 —— 两组独立 GPIO,不能两路共用同一组(同时拉复位会互相干扰)
⑦ 供电(当前全部注释掉了)
// avdd-supply = <&vcc_mipidcphy0>;
若模组需要可控的 AVDD(2.8V) / DOVDD(1.8V) / DVDD(1.2V),要改成真实 regulator 节点;如果是板子直供常电,保持注释即可(目前是这种)
⑧ rockchip,camera-module-index
多路摄像头时必须同型号内唯一(0/1/2/3)。HAL 用它区分 cameraId。现在:J25 imx415_0 = 0、J19 imx415_1 = 0(重复)(因为imx415都是后摄,只用上一个就行);J20 ov13855_0 = 1。
HAL 的匹配键是sensor 名 + module-index
驱动用rockchip,camera-module-index和rockchip,camera-module-facing拼出 media entity 名,例如 m01_f_ov13855 6-0036
![]()
当前如果想同时插两个415则需要改
1、DTS:改 index
rockchip,camera-module-index = <0> → <1>
2、 HAL:profiles 必须同时加一段
hardware/rockchip/camera/etc/camera/camera3_profiles_rk3576.xml
现在 rk3576 的 profiles 里每种 sensor 只有一段
791 <Profiles cameraId="0" name="imx415" moduleId="m00"> ← lens.facing BACK 2116 <Profiles cameraId="0" name="ov13855" moduleId="m01"> ← lens.facing FRONT
所以要把 791 那段整段复制一份,改成:
<Profiles cameraId="1" name="imx415" moduleId="m01">
cameraId 0→1,moduleId m00→m01,lens.facing 保持 BACK。
![]()
⑨ facing / module-name / lens-name
- facing:"back" / "front",决定 Android 前后摄。
- 后两个决定 IQ 文件名:<sensor>_<module-name>_<lens-name>.json 填 CMK-OT2022-PX1 + IR0147-50IRC-8M-F20 正好对应 external/camera_engine_rkaiq/iqfiles/isp39/imx415_CMK-OT2022-PX1_IR0147-50IRC-8M-F20.json
⑩ port / endpoint / />
要点:
- port@0 里 endpoint@1/2 的 reg 是同一 dphy 上的多输入编号。你的 dphy1 上就挂了两路:mipi_in_ucam2 (reg=1)、mipi_in_ucam3 (reg=2) —— 但实际一路 dphy 同时只能跑一颗 sensor(lane 复用冲突),所以 ov13855_1 是 disabled,只保留 imx415_1。
- 任一对 remote-endpoint 写漏/写错,内核只会打印 Fixed dependency cycle(s) 然后 link 失败(media-ctl -p 看不到 sensor entity)。
第三层到底在干什么:接水管
RK3576 的摄像头不是"sensor 直接连到 CPU",数据要依次穿过 6 个硬件模块才能变成图像。每个模块都有入口和出口,第三层做的事就是把上一个模块的出口,用 remote-endpoint 接到下一个模块的入口。
摄像头模组 │ MIPI CSI-2 差分信号(4 对数据线 + 1 对时钟线) ▼ ┌─────────────┐ │ csi2_dphy0 │ ① PHY 物理层:把差分电平变成数字信号,做解包 └─────────────┘ │ ▼ ┌─────────────┐ │ mipi0_csi2 │ ② CSI-2 协议控制器:解析数据包、按虚拟通道分离 └─────────────┘ │ ▼ ┌──────────────────┐ │ rkcif_mipi_lvds │ ③ CIF 采集口:收 RAW、DMA 写内存(/dev/videoX) └──────────────────┘ │ ▼ (内部直连,不过内存) ┌─────────────────────────┐ │ rkcif_mipi_lvds_sditf │ ④ 直通通道:CIF → ISP └─────────────────────────┘ │ ▼ ┌─────────────┐ │ rkisp_vir0 │ ⑤ ISP:3A / 去噪 / 拜耳插值 / 缩放(出 YUV) └─────────────┘ │ ▼ ┌──────────────┐ │ rkvpss_vir0 │ ⑥ VPSS 后处理:裁剪/缩放/旋转 → 交给上层 └──────────────┘
remote-endpoint就是这些箭头。内核的 media framework 靠它建立 link;两边只要有一边没写或写错,这条链就断,media-ctl -p 里就看不到 sensor。
① &csi2_dphy0(7–40 行)—— MIPI 物理层
&csi2_dphy0 { status = "okay"; ports { port@0 { mipi_in_ucam4: endpoint@1 { reg = <1>; remote-endpoint = <&imx415_out2>; />判断方法很简单:名字里带数字/后缀的(_mipi_lvds、_vir0、_lvds1、_vir3)= 每条 pipeline 一个实例;所以 J19 那条要开的是 rkcif_mipi_lvds1 / rkisp_vir1 / rkvpss_vir1,J20 是 ..._lvds3 / rkisp_vir3 / rkvpss_vir3
![]()
![]()
第四层:全局使能
&rkcif { status = "okay"; }; // 整个 CIF 模块(父设备,全芯片就一个) &rkisp { status = "okay"; }; // 整个 ISP 模块(父设备) &rkvpss { status = "okay"; }; // 整个 VPSS 模块(父设备) &rkcif_mmu { status = "okay"; }; // CIF 的 IOMMU &rkisp_mmu { status = "okay"; }; // ISP 的 IOMMU &rkvpss_mmu { status = "okay"; }; // VPSS 的 IOMMU
配置摄像头的 dts 本质就三步:
- sensor 挂到 i2c(第一层)
- 按接口选好这一串模块实例,全部 status = "okay"(第二层 + 73–114 + 116–138)
- 用 remote-endpoint 把 6 个模块首尾接起来(第三层)
硬件逻辑(PHY 干什么、ISP 干什么)内核驱动里都写好了
三、驱动层
![]()
![]()
驱动和 dts 怎么连上
dts 里你写的 compatible = "sony,imx415" 会去匹配驱动里的 of_match_table → 匹配成功才 probe → 注册 V4L2 subdev → 出现在 media graph 里。compatible 和驱动名字对不上 = 静默失败,什么都不报。
四、HAL 层:profiles
编译期:哪个 xml 文件会被装进板子
在 hardware/rockchip/camera/etc/camera_etc.mk
$(CUR_PATH)/camera/camera3_profiles_$(TARGET_BOARD_PLATFORM).xml:$(TARGET_COPY_OUT_VENDOR)/etc/camera/camera3_profiles.xml \
(TARGET_BOARD_PLATFORM := rk3576)展开后就是:
![]()
hardware/rockchip/camera/etc/camera/camera3_profiles_rk3576.xml
运行时:xml 里哪几段会被启用
板子上那个 xml 里有 9 段 <Profiles>,HAL 怎么挑?匹配条件:name + moduleId 双匹配
hardware/rockchip/camera/psl/rkisp2/RKISP2PSLConfParser.cpp
for (size_t i = 0; i < mDetectedSensors.size(); i++) { if (mDetectedSensors[i].mSensorName == sensorName && strcmp(mDetectedSensors[i].mModuleIndexStr.c_str(), moduleId) == 0) { return true; } } return false;
- mSensorName:内核上报的 sensor 名 → 对应 xml 的 name="imx415"
- mModuleIndexStr:模块编号 → 对应 xml 的 moduleId="m00"
![]()
五、IQ 调校(画质参数)
RK3576 → ISP 版本 isp39,装到 /vendor/etc/camera/rkisp2/。
对照其他平台:rk3588→isp3x、rk3562→isp32_lite、rk356x/rk1126→isp21。放错目录等于没有。
isp39 目录里现成的:
external/camera_engine_rkaiq/iqfiles/isp39
imx415_CMK-OT2022-PX1_IR0147-50IRC-8M-F20.json ov13855_CMK-OT2016-FV1_default.json ov13855_KYT-10203-v1_default.json CAC_imx415_CMK-OT2022-PX1_IR0147-50IRC-8M-F20/ (色差校正网格)
![]()
![]()
六、常见问题与排查
问题现象 可能原因 排查方向 驱动加载失败 compatible不匹配;I2C地址错误检查DTS与驱动;用i2cdetect扫描 无图像/黑屏 MIPI链路断裂;时钟/电源异常 检查remote-endpoint;测量电源和MCLK 图像花屏/条纹 MIPI信号完整性差;Lane配置错误 检查差分线等长/阻抗;核对data-lanes 画面方向不对 rotation或orientation配置错误调整DTSrotation或HAL层orientation 多摄冲突 camera-module-index重复为每个摄像头分配唯一的 index
多摄配置:多个摄像头时,rockchip,camera-module-index必须全局唯一,否则HAL层会因ID冲突而无法正确识别。
配置完成后,可以通过ls /dev/video*查看设备节点是否生成,或使用dumpsys media.camera确认HAL层识别是否正常