/**
 * @file rk3588_ov13850/ov5645.c
 * @brief RK3588平台 OV13850/OV5645 MIPI摄像头完整开发指南
 * @author zhilin_tang Technology
 * 
 * 摄像头型号:
 * - OV13850: 13MP 高性能摄像头,支持MIPI CSI-2
 * - OV5645: 5MP 主流摄像头,支持MIPI CSI-2
 * 
 * 本分析涵盖:
 * 1. 硬件引脚连接与设备树配置
 * 2. 驱动源码目录树形分析
 * 3. 关键函数树形分析
 * 4. Pipeline数据流树形分析
 * 5. 调试流程与工具树形分析
 */

一、摄像头工作原理与硬件连接

1.1 MIPI CSI-2 协议与摄像头通信

MIPI CSI-2是专门为摄像头设计的高速串行接口协议,其核心工作原理如下:

【MIPI CSI-2 通信架构】
​
┌─────────────────────────────────────────────────────────────────────────────┐
│                           OV13850/OV5645 摄像头                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    图像传感器 (Image Sensor)                         │   │
│  │                    光信号 → 数字信号                                 │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    8B/10B 编码器                                     │   │
│  │     原始8bit数据 → 查表编码 → 10bit数据(保证时钟跳变)               │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    MIPI D-PHY 发送器                                 │   │
│  │           差分信号:CLK+/- , DATA0+/- , DATA1+/- , ...              │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                      ↓ 差分信号线
┌─────────────────────────────────────────────────────────────────────────────┐
│                           RK3588 主控                                       │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    MIPI D-PHY 接收器                                 │   │
│  │           差分信号 → 时钟恢复 → 10bit数据流                          │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    MIPI CSI-2 协议解析器                             │   │
│  │              识别帧头/帧尾 → 提取有效像素数据                         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    VICAP (视频捕获单元)                              │   │
│  │                    数据格式转换 → 内存存储                           │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

1.2 为什么需要I2C控制通道

MIPI CSI-2仅负责高速图像数据传输,不承载控制指令。因此需要独立的I2C总线进行摄像头配置:

【双通道架构】
​
┌─────────────────────────────────────────────────────────────────────────────┐
│                              RK3588                                         │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                      │   │
│  │  ┌─────────────────────┐        ┌─────────────────────┐             │   │
│  │  │   MIPI D-PHY        │        │      I2C 控制器     │             │   │
│  │  │   (高速数据通道)    │        │    (低速控制通道)   │             │   │
│  │  │   1对时钟 + 2对数据 │        │     SCL / SDA       │             │   │
│  │  └──────────┬──────────┘        └──────────┬──────────┘             │   │
│  └─────────────┼───────────────────────────────┼────────────────────────┘   │
│                │                               │                            │
│                ↓                               ↓                            │
└─────────────────────────────────────────────────────────────────────────────┘
                 │                               │
                 ↓                               ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│                         OV13850/OV5645 摄像头                                │
│  ┌─────────────────────────────┐    ┌─────────────────────────────┐        │
│  │   MIPI CSI-2 发送器         │    │      I2C 从设备             │        │
│  │   (图像数据输出)            │    │   (寄存器配置接口)          │        │
│  └─────────────────────────────┘    └─────────────────────────────┘        │
│                                                                             │
│  功能:传输像素数据                        功能:接收配置命令              │
│        - 分辨率设置                               - 曝光时间               │
│        - 帧率控制                                 - 增益设置               │
│        - 格式选择                                 - 白平衡                 │
└─────────────────────────────────────────────────────────────────────────────┘

1.3 RK3588 MIPI CSI硬件资源

RK3588内部有3个独立的MIPI D-PHY控制器:

【RK3588 MIPI CSI硬件资源】
​
┌─────────────────┬─────────────────┬─────────────────┬─────────────────────────────┐
│ 控制器名称      │ 寄存器基地址    │ 最大Lane数      │ 说明                        │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ csi2_dcphy0     │ 0xFED90000      │ 4 lanes         │ 支持DPHY/CPHY双协议         │
│ csi2_dcphy1     │ 0xFED91000      │ 4 lanes         │ 支持DPHY/CPHY双协议         │
│ mipi_dcphy0     │ 0xFED92000      │ 4 lanes         │ 专门用于高速摄像头          │
└─────────────────┴─────────────────┴─────────────────┴─────────────────────────────┘
​
【硬件连接模式】
- Full Mode: 使用全部4条数据lane,连接一个摄像头
- Split Mode: 拆分成2个2-lane接口,可同时连接两个摄像头

1.4 引脚连接表

根据RK3588开发板设计,OV13850和OV5645的典型引脚连接如下:

【RK3588 ←→ OV13850/OV5645 连接表】
​
┌─────────────────┬─────────────────┬─────────────────┬─────────────────────────────┐
│ 信号名称        │ RK3588 引脚     │ OV13850/5645    │ 功能描述                    │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ MIPI 数据通道   │                 │                 │                             │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ MIPI_RX_D0P     │ MIPI_CSI0_RXD0_P│ DATA0+          │ 数据通道0正                  │
│ MIPI_RX_D0N     │ MIPI_CSI0_RXD0_N│ DATA0-          │ 数据通道0负                  │
│ MIPI_RX_D1P     │ MIPI_CSI0_RXD1_P│ DATA1+          │ 数据通道1正                  │
│ MIPI_RX_D1N     │ MIPI_CSI0_RXD1_N│ DATA1-          │ 数据通道1负                  │
│ MIPI_RX_CLKP    │ MIPI_CSI0_RXCLK_P│ CLK+           │ 时钟正                       │
│ MIPI_RX_CLKN    │ MIPI_CSI0_RXCLK_N│ CLK-           │ 时钟负                       │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ I2C 控制接口    │                 │                 │                             │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ I2C_SCL         │ I2C2_SCL_M0     │ SCL             │ I2C 时钟线                   │
│ I2C_SDA         │ I2C2_SDA_M0     │ SDA             │ I2C 数据线                   │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ 电源和控制      │                 │                 │                             │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ MCLK            │ GPIO0_C4        │ XVCLK           │ 24MHz主时钟                 │
│ RESET           │ GPIO4_C6        │ RESET           │ 复位信号                     │
│ PWDN            │ GPIO4_C7        │ PWDN            │ 掉电控制                     │
│ AVDD            │ VCC2V8_CAM      │ AVDD            │ 模拟电源 2.8V                │
│ DOVDD           │ VCC1V8_CAM      │ DOVDD           │ I/O电源 1.8V                 │
│ DVDD            │ VCC1V2_CAM      │ DVDD            │ 数字核心电源 1.2V            │
└─────────────────┴─────────────────┴─────────────────┴─────────────────────────────┘

二、设备树完整配置

2.1 摄像头模块设备树 rk3588/ov13850.dtsi

/**
 * @file rk3588-ov13850.dtsi
 * @brief RK3588 OV13850/OV5645 MIPI摄像头设备树配置
 * @author zhilin_tang Technology
 * 
 * @note 设计模式分析:采用"复合模式(Composite Pattern)"
 *       将摄像头传感器、MIPI PHY、CSI控制器、VICAP、ISP
 *       组成完整的摄像头数据通路。
 * 
 * @remark 参考文档:迅为RK3588开发板摄像头使用手册
 */
​
/dts-v1/;
/plugin/;
​
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/clock/rk3588-cru.h>
​
/ {
    compatible = "rockchip,rk3588";
    
    /* ========== 1. 摄像头电源定义 ========== */
    vcc_cam_avdd: vcc-cam-avdd {
        compatible = "regulator-fixed";
        regulator-name = "vcc_cam_avdd";
        regulator-min-microvolt = <2800000>;
        regulator-max-microvolt = <2800000>;
        regulator-always-on;
        status = "okay";
    };
    
    vcc_cam_dovdd: vcc-cam-dovdd {
        compatible = "regulator-fixed";
        regulator-name = "vcc_cam_dovdd";
        regulator-min-microvolt = <1800000>;
        regulator-max-microvolt = <1800000>;
        regulator-always-on;
        status = "okay";
    };
    
    vcc_cam_dvdd: vcc-cam-dvdd {
        compatible = "regulator-fixed";
        regulator-name = "vcc_cam_dvdd";
        regulator-min-microvolt = <1200000>;
        regulator-max-microvolt = <1200000>;
        regulator-always-on;
        status = "okay";
    };
    
    /* ========== 2. 摄像头时钟定义 ========== */
    ext_cam_clk: ext-cam-clk {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <24000000>;   /* 24MHz 主时钟 */
    };
};
​
/* ========== 3. I2C 控制器配置 (连接摄像头控制接口) ========== */
&i2c2 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c2m0_xfer>;
    clock-frequency = <400000>;
    
    /* OV13850 摄像头传感器节点 */
    ov13850: ov13850@10 {
        compatible = "ovti,ov13850";
        reg = <0x10>;                   /* I2C 设备地址 0x10 */
        
        /* 电源供应 */
        AVDD-supply = <&vcc_cam_avdd>;
        DOVDD-supply = <&vcc_cam_dovdd>;
        DVDD-supply = <&vcc_cam_dvdd>;
        
        /* GPIO 控制 */
        reset-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>;
        pwdn-gpios = <&gpio4 RK_PC7 GPIO_ACTIVE_LOW>;
        
        /* 时钟配置 */
        clocks = <&ext_cam_clk>;
        clock-names = "xvclk";
        
        /* 旋转方向 */
        rotation = <0>;
        
        /* 支持的MIPI Lane数 */
        lane = <2>;                     /* 2-lane MIPI */
        
        /* 电源时序延迟 */
        power-on-delay = <50>;
        power-off-delay = <50>;
        reset-delay = <10>;
        
        port {
            ov13850_out: endpoint {
                remote-endpoint = <&mipi_in_ov13850>;
                data-lanes = <1 2>;     /* 使用 lane1 和 lane2 */
            };
        };
    };
    
    /* OV5645 摄像头传感器节点 (备选) */
    ov5645: ov5645@3c {
        compatible = "ovti,ov5645";
        reg = <0x3c>;                  /* I2C 设备地址 0x3C */
        status = "disabled";
        
        AVDD-supply = <&vcc_cam_avdd>;
        DOVDD-supply = <&vcc_cam_dovdd>;
        DVDD-supply = <&vcc_cam_dvdd>;
        
        reset-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>;
        pwdn-gpios = <&gpio4 RK_PC7 GPIO_ACTIVE_LOW>;
        
        clocks = <&ext_cam_clk>;
        clock-names = "xvclk";
        
        rotation = <0>;
        lane = <2>;
        
        power-on-delay = <50>;
        reset-delay = <10>;
        
        port {
            ov5645_out: endpoint {
                remote-endpoint = <&mipi_in_ov5645>;
                data-lanes = <1 2>;
            };
        };
    };
};
​
/* ========== 4. MIPI CSI D-PHY 配置 ========== */
&csi2_dcphy0 {
    status = "okay";
    
    ports {
        #address-cells = <1>;
        #size-cells = <0>;
        
        /* 输入端口:连接摄像头传感器 */
        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;
            
            mipi_in_ov13850: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&ov13850_out>;
                data-lanes = <1 2>;
            };
            
            mipi_in_ov5645: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&ov5645_out>;
                data-lanes = <1 2>;
                status = "disabled";
            };
        };
        
        /* 输出端口:连接CSI主机 */
        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;
            
            mipi_out_csi2: endpoint {
                remote-endpoint = <&csi2_in_mipi>;
            };
        };
    };
};
​
/* ========== 5. MIPI CSI2 主机配置 ========== */
&mipi0_csi2 {
    status = "okay";
    
    ports {
        #address-cells = <1>;
        #size-cells = <0>;
        
        /* 输入端口:连接D-PHY */
        port@0 {
            reg = <0>;
            csi2_in_mipi: endpoint {
                remote-endpoint = <&mipi_out_csi2>;
            };
        };
        
        /* 输出端口:连接VICAP */
        port@1 {
            reg = <1>;
            csi2_out_vicap: endpoint {
                remote-endpoint = <&vicap_in_csi2>;
            };
        };
    };
};
​
/* ========== 6. VICAP 视频捕获单元配置 ========== */
&rkcif {
    status = "okay";
};
​
&rkcif_mipi_lvds {
    status = "okay";
    
    port {
        vicap_in_csi2: endpoint {
            remote-endpoint = <&csi2_out_vicap>;
        };
    };
};
​
&rkcif_mipi_lvds_sditf {
    status = "okay";
    
    port {
        vicap_sditf_out: endpoint {
            remote-endpoint = <&isp_in_vicap>;
        };
    };
};
​
/* ========== 7. ISP 图像信号处理器配置 ========== */
&rkisp {
    status = "okay";
};
​
&rkisp_vir0 {
    status = "okay";
    
    port {
        isp_in_vicap: endpoint {
            remote-endpoint = <&vicap_sditf_out>;
        };
    };
};
​
/* ========== 8. 引脚复用配置 ========== */
&pinctrl {
    camera {
        cam_pwdn_pin: cam-pwdn-pin {
            rockchip,pins = <4 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>;
        };
        
        cam_rst_pin: cam-rst-pin {
            rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
        };
        
        cam_mclk_pin: cam-mclk-pin {
            rockchip,pins = <0 RK_PC4 RK_FUNC_1 &pcfg_pull_none>;
        };
    };
    
    mipi {
        mipi_csi0_pins: mipi-csi0-pins {
            rockchip,pins =
                <2 RK_PB0 RK_FUNC_1 &pcfg_pull_none>,  /* MIPI_RX_D0P */
                <2 RK_PB1 RK_FUNC_1 &pcfg_pull_none>,  /* MIPI_RX_D0N */
                <2 RK_PB2 RK_FUNC_1 &pcfg_pull_none>,  /* MIPI_RX_D1P */
                <2 RK_PB3 RK_FUNC_1 &pcfg_pull_none>,  /* MIPI_RX_D1N */
                <2 RK_PB4 RK_FUNC_1 &pcfg_pull_none>,  /* MIPI_RX_CLKP */
                <2 RK_PB5 RK_FUNC_1 &pcfg_pull_none>;  /* MIPI_RX_CLKN */
        };
    };
};

2.2 OV13850 和 OV5645 的差异配置

/**
 * OV13850 和 OV5645 的主要差异
 * 
 * ┌─────────────┬─────────────────┬─────────────────┐
 * │ 参数        │ OV13850         │ OV5645          │
 * ├─────────────┼─────────────────┼─────────────────┤
 * │ 分辨率      │ 4224×3136 (13MP)│ 2592×1944 (5MP)│
 * │ I2C地址     │ 0x10            │ 0x3C            │
 * │ 帧率        │ 30fps @ 1080p   │ 30fps @ 1080p   │
 * │ 功耗        │ 约 200mW        │ 约 150mW        │
 * └─────────────┴─────────────────┴─────────────────┘
 */

三、驱动源码目录树形分析

3.1 Rockchip VIN 驱动架构

根据Rockchip官方文档,VIN (Video Input) 驱动由三个层次组成:

【Rockchip VIN 驱动架构树】
​
drivers/media/platform/rockchip/
│
├── rockchip-vin/                     # VIN 驱动主目录 ★★★
│   ├── rockchip-vin-core.c           # VIN 核心驱动
│   ├── rockchip-vin-mipi.c           # MIPI CSI 接口处理
│   ├── rockchip-vin-dvp.c            # DVP 并口处理
│   ├── rockchip-vin-csi2.c           # CSI2 协议解析
│   └── rockchip-vin-reg.c            # 寄存器操作
│
├── rockchip-isp/                     # ISP 驱动目录 ★★★
│   ├── rockchip-isp.c                # ISP 核心驱动
│   ├── rockchip-isp-subdev.c         # ISP 子设备
│   ├── rockchip-isp-params.c         # ISP 参数配置
│   └── rockchip-isp-stats.c          # ISP 统计信息
│
└── rockchip-cif/                     # CIF 摄像头接口驱动
    ├── rockchip-cif.c
    └── rockchip-cif-reg.c
​
【Sensor 驱动目录】
drivers/media/i2c/
│
├── ov13850.c                         # OV13850 传感器驱动 ★★★
│   ├── ov13850_probe()               # I2C 设备探测
│   ├── ov13850_s_stream()            # 视频流控制
│   ├── ov13850_set_fmt()             # 格式设置
│   └── ov13850_set_ctrl()            # 控制设置
│
├── ov5645.c                          # OV5645 传感器驱动 ★★★
│   ├── ov5645_probe()
│   ├── ov5645_s_stream()
│   ├── ov5645_set_fmt()
│   └── ov5645_set_ctrl()
│
└── ov13850_regs.h                    # OV13850 寄存器定义

3.2 驱动内部结构树

【ov13850.c 驱动内部结构树】

ov13850.c
│
├── 1. 寄存器定义区
│   ├── OV13850_REG_CHIP_ID          # 芯片ID寄存器 (0x300A)
│   ├── OV13850_REG_MODE_SELECT      # 模式选择寄存器
│   ├── OV13850_REG_FORMAT1          # 格式寄存器1
│   ├── OV13850_REG_EXPOSURE         # 曝光寄存器
│   └── OV13850_REG_GAIN             # 增益寄存器
│
├── 2. 模式配置表 (分辨率/帧率)
│   ├── ov13850_mode_4224_3136_30fps # 13MP@30fps
│   ├── ov13850_mode_1920_1080_30fps # 1080p@30fps
│   ├── ov13850_mode_1280_720_60fps # 720p@60fps
│   └── ov13850_mode_640_480_120fps # VGA@120fps
│
├── 3. 私有数据结构体
│   ├── struct ov13850_device
│   │   ├── struct v4l2_subdev *sd   # V4L2 子设备
│   │   ├── struct i2c_client *client # I2C 客户端
│   │   ├── struct clk *xvclk        # 主时钟
│   │   ├── struct gpio_desc *reset_gpio # 复位GPIO
│   │   ├── struct gpio_desc *pwdn_gpio  # 掉电GPIO
│   │   └── struct v4l2_ctrl_handler ctrl_handler # 控制句柄
│
├── 4. 寄存器读写函数
│   ├── ov13850_read_reg()           # 读寄存器
│   ├── ov13850_write_reg()          # 写寄存器
│   └── ov13850_update_bits()        # 更新位
│
├── 5. V4L2 子设备操作函数
│   ├── ov13850_s_power()            # 电源控制
│   ├── ov13850_s_stream()           # 流控制 ★★★
│   ├── ov13850_g_frame_interval()   # 获取帧间隔
│   ├── ov13850_s_frame_interval()   # 设置帧间隔
│   └── ov13850_enum_mbus_code()     # 枚举格式
│
├── 6. 格式设置函数
│   ├── ov13850_set_fmt()            # 设置格式
│   ├── ov13850_get_fmt()            # 获取格式
│   └── ov13850_try_fmt()            # 尝试格式
│
├── 7. 控制函数 (曝光/增益/白平衡)
│   ├── ov13850_set_exposure()       # 设置曝光时间
│   ├── ov13850_set_gain()           # 设置增益
│   ├── ov13850_set_white_balance()  # 设置白平衡
│   └── ov13850_set_banding_filter() # 防闪烁
│
├── 8. 初始化序列
│   └── ov13850_init_seq[]           # 初始化寄存器表
│
├── 9. I2C 驱动结构体
│   ├── ov13850_i2c_probe()          # I2C 探测
│   ├── ov13850_i2c_remove()         # 移除
│   └── struct i2c_driver ov13850_i2c_driver
│
└── 10. 模块初始化
    └── module_i2c_driver()

四、关键函数树形分析

4.1 传感器 Probe 函数

/**
 * @brief OV13850 I2C 设备探测函数
 * @param client I2C 客户端
 * @param id 设备ID
 * @return 0 成功,负数错误码
 * 
 * @note 设计模式分析:采用"建造者模式(Builder Pattern)"
 *       逐步构建传感器驱动私有数据,完成资源分配、硬件初始化、
 *       V4L2子设备注册等步骤。
 * 
 * @remark 关键点:需要正确读取芯片ID验证硬件连接
 */
static int ov13850_probe(struct i2c_client *client,
                          const struct i2c_device_id *id)
{
    struct device *dev = &client->dev;
    struct ov13850_device *ov13850;
    struct v4l2_subdev *sd;
    int ret;
    
    /* ========== 步骤1:分配私有数据 ========== */
    ov13850 = devm_kzalloc(dev, sizeof(*ov13850), GFP_KERNEL);
    if (!ov13850)
        return -ENOMEM;
    
    ov13850->client = client;
    
    /* ========== 步骤2:获取时钟资源 ========== */
    ov13850->xvclk = devm_clk_get(dev, "xvclk");
    if (IS_ERR(ov13850->xvclk)) {
        dev_err(dev, "Failed to get xvclk\n");
        return PTR_ERR(ov13850->xvclk);
    }
    
    /* ========== 步骤3:获取 GPIO 资源 ========== */
    ov13850->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
    if (IS_ERR(ov13850->reset_gpio)) {
        dev_err(dev, "Failed to get reset gpio\n");
        return PTR_ERR(ov13850->reset_gpio);
    }
    
    ov13850->pwdn_gpio = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_LOW);
    if (IS_ERR(ov13850->pwdn_gpio)) {
        dev_err(dev, "Failed to get pwdn gpio\n");
        return PTR_ERR(ov13850->pwdn_gpio);
    }
    
    /* ========== 步骤4:电源上电和复位 ========== */
    ret = ov13850_power_on(ov13850);
    if (ret) {
        dev_err(dev, "Failed to power on sensor\n");
        return ret;
    }
    
    /* ========== 步骤5:读取芯片ID验证 ========== */
    ret = ov13850_check_chip_id(ov13850);
    if (ret) {
        dev_err(dev, "Failed to read chip ID\n");
        goto err_power_off;
    }
    
    /* ========== 步骤6:初始化 V4L2 子设备 ========== */
    sd = &ov13850->sd;
    v4l2_i2c_subdev_init(sd, client, &ov13850_subdev_ops);
    sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
    
    /* ========== 步骤7:注册控制句柄 ========== */
    ov13850_init_controls(ov13850);
    
    /* ========== 步骤8:注册子设备 ========== */
    ret = v4l2_async_register_subdev(sd);
    if (ret) {
        dev_err(dev, "Failed to register subdev\n");
        goto err_free_ctrl;
    }
    
    dev_info(dev, "OV13850 sensor probe success\n");
    return 0;
    
err_free_ctrl:
    v4l2_ctrl_handler_free(&ov13850->ctrl_handler);
err_power_off:
    ov13850_power_off(ov13850);
    return ret;
}

4.2 流控制函数 (关键)

/**
 * @brief 视频流控制函数
 * @param sd V4L2子设备
 * @param enable 1: 开始流, 0: 停止流
 * @return 0 成功,负数错误码
 * 
 * @note 设计模式分析:采用"状态模式(State Pattern)"
 *       根据enable参数切换传感器工作状态。
 * 
 * @remark 重要:必须在MIPI控制器准备好后才能使能传感器输出
 */
static int ov13850_s_stream(struct v4l2_subdev *sd, int enable)
{
    struct ov13850_device *ov13850 = to_ov13850(sd);
    int ret = 0;
    
    mutex_lock(&ov13850->mutex);
    
    if (enable) {
        /* ========== 开始视频流 ========== */
        /* 1. 根据当前选择的模式配置传感器寄存器 */
        ret = ov13850_set_mode(ov13850);
        if (ret)
            goto out;
        
        /* 2. 配置MIPI输出 */
        ret = ov13850_set_mipi(ov13850);
        if (ret)
            goto out;
        
        /* 3. 使能传感器输出 (关键:此时MIPI信号开始输出) */
        ret = ov13850_write_reg(ov13850, OV13850_REG_MODE_SELECT, 0x01);
        if (ret)
            goto out;
        
        ov13850->streaming = true;
        dev_dbg(sd->dev, "Stream started\n");
    } else {
        /* ========== 停止视频流 ========== */
        /* 关闭传感器输出 */
        ret = ov13850_write_reg(ov13850, OV13850_REG_MODE_SELECT, 0x00);
        if (ret)
            goto out;
        
        ov13850->streaming = false;
        dev_dbg(sd->dev, "Stream stopped\n");
    }
    
out:
    mutex_unlock(&ov13850->mutex);
    return ret;
}

4.3 MIPI参数计算

/**
 * @brief 计算MIPI链路参数
 * @param mode 显示模式
 * @return 计算出的link_freq值
 * 
 * @note 设计模式分析:采用"策略模式(Strategy Pattern)"
 *       根据不同的分辨率/帧率计算对应的MIPI时钟频率。
 * 
 * @remark link_freq计算公式:
 *         link_freq = width * height * fps * bits_per_pixel / lanes / 2
 */
static u32 ov13850_calc_link_freq(struct ov13850_mode *mode)
{
    u32 link_freq;
    
    /* 计算公式:
     * 像素时钟 = 宽 × 高 × 帧率
     * 单lane速率 = 像素时钟 × 位深 / lane数
     * link_freq = 单lane速率 / 2 (DDR传输,每时钟2个数据)
     */
    link_freq = mode->width * mode->height * mode->fps *
                mode->bpp / mode->lanes / 2;
    
    /* 向上取整到最近的合理频率 */
    link_freq = (link_freq + 50000000) / 100000000 * 100000000;
    
    return link_freq;
}

五、Pipeline数据流树形分析

5.1 完整数据流架构

根据RK3588摄像头子系统设计,数据流经过以下层次:

【RK3588 摄像头数据流 Pipeline】

┌─────────────────────────────────────────────────────────────────────────────┐
│                              硬件层 (Hardware)                              │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    OV13850/OV5645 传感器                            │   │
│  │                    - 光信号 → 数字信号                              │   │
│  │                    - 8B/10B编码                                     │   │
│  │                    - MIPI D-PHY 输出                                │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓ MIPI 差分信号                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    RK3588 MIPI D-PHY (csi2_dcphy0)                  │   │
│  │                    - 时钟恢复 (CDR)                                  │   │
│  │                    - 串并转换                                       │   │
│  │                    - 10bit → 8bit 解码                              │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    MIPI CSI-2 协议解析 (mipi0_csi2)                 │   │
│  │                    - 识别帧起始 (FS) / 帧结束 (FE)                   │   │
│  │                    - 提取像素数据                                   │   │
│  │                    - 错误检测 (CRC/ECC)                             │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    VICAP 视频捕获单元 (rkcif_mipi_lvds)             │   │
│  │                    - 数据格式转换                                   │   │
│  │                    - DMA 传输到内存                                 │   │
│  │                    - 生成 /dev/videoX 节点                          │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                      ↓ (RAW数据)
┌─────────────────────────────────────────────────────────────────────────────┐
│                         内核空间 (Kernel)                                   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    VICAP → ISP 桥接 (rkcif_mipi_lvds_sditf)        │   │
│  │                    - 将VICAP数据导入ISP处理                         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    ISP 图像信号处理器 (rkisp_vir0)                   │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │                    图像处理流水线                            │   │   │
│  │  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐     │   │   │
│  │  │  │ 黑电平   │→│ 坏点校正 │→│ 镜头阴影 │→│ 去马赛克 │     │   │   │
│  │  │  │ 校正(BLC)│ │ (DPCC)   │ │ (LSC)    │ │         │     │   │   │
│  │  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘     │   │   │
│  │  │       ↓            ↓            ↓            ↓            │   │   │
│  │  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐     │   │   │
│  │  │  │ 降噪     │→│ 色彩校正 │→│ 伽马校正 │→│ 缩放     │     │   │   │
│  │  │  │ (2D/3D)  │ │ (CCM)    │ │ (Gamma)  │ │         │     │   │   │
│  │  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘     │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    输出格式转换                                     │   │
│  │                    - RAW → YUV/RGB                                 │   │
│  │                    - 生成最终视频节点 /dev/videoX                    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                      ↓
┌─────────────────────────────────────────────────────────────────────────────┐
│                         用户空间 (User Space)                               │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    应用程序 / 测试工具                               │   │
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐                │   │
│  │  │   V4L2应用   │ │   GStreamer  │ │   OpenCV     │                │   │
│  │  └──────────────┘ └──────────────┘ └──────────────┘                │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

5.2 数据流阶段详解

阶段 组件 输入 输出 说明
1. 感光 OV13850/5645 光信号 RAW10/RAW12 传感器将光转换为数字信号
2. 编码 传感器内部 RAW数据 8B/10B编码 保证时钟跳变,便于接收端同步
3. 传输 MIPI D-PHY 并行数据 差分串行信号 4 lane高速传输
4. 接收 RK3588 D-PHY 差分信号 并行数据 时钟恢复和串并转换
5. 解析 CSI-2主机 MIPI包 像素数据 解析帧头/帧尾,提取有效数据
6. 捕获 VICAP 像素数据 DMA到内存 生成 /dev/video 节点
7. 处理 ISP RAW数据 YUV/RGB 图像增强、降噪、色彩校正

六、调试流程与工具树形分析

6.1 调试工具树

【摄像头调试工具树】

media-ctl (媒体控制器工具)
│
├── media-ctl -p -d /dev/media0        # 打印媒体拓扑结构 ★★★
├── media-ctl -l -d /dev/media0        # 查看当前链路
├── media-ctl --set-link '"ov13850 1-0010":0->"csi2_dcphy0":0[1]'  # 设置链路
└── media-ctl -d /dev/media0 -e "rkisp_mainpath"  # 查看ISP主通路

v4l2-ctl (V4L2控制工具)
│
├── v4l2-ctl --list-devices            # 列出所有视频设备 ★★★
├── v4l2-ctl -d /dev/video0 --all      # 查看设备详细信息
├── v4l2-ctl --list-formats-ext -d /dev/video0  # 列出支持格式 ★★★
├── v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=NV12  # 设置格式
├── v4l2-ctl --stream-mmap --stream-count=1 --stream-to=frame.raw  # 采集一帧 ★★★
└── v4l2-ctl -d /dev/video0 -l         # 列出控制项

I2C调试工具
│
├── i2cdetect -y 2                     # 检测I2C设备 (OV13850地址0x10)
├── i2cget -y 2 0x10 0x300A w          # 读取芯片ID
└── i2cset -y 2 0x10 0x0100 0x01       # 写入寄存器

系统调试接口
│
├── cat /proc/rkcif-dvp                # RKCIF状态
├── cat /proc/rkrkisp0                 # ISP0状态
├── dmesg | grep -i ov13850            # 查看传感器驱动日志
├── dmesg | grep -i mipi               # 查看MIPI相关日志
└── cat /sys/kernel/debug/clk/clk_summary | grep -E "cif|isp|mipi"  # 时钟状态

6.2 完整调试脚本

#!/bin/bash
# camera_debug.sh - OV13850/OV5645 摄像头完整调试脚本

echo "========================================="
echo "RK3588 + OV13850/OV5645 摄像头调试"
echo "========================================="

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'

# 1. 检查I2C设备 (传感器ID检测)
echo -e "\n[1] 检查I2C设备 (OV13850地址0x10, OV5645地址0x3C)..."
i2cdetect -y 2
echo ""

# 2. 检查传感器芯片ID
echo -e "\n[2] 读取传感器芯片ID..."
if i2cget -y 2 0x10 0x300A w 2>/dev/null; then
    ID=$(i2cget -y 2 0x10 0x300A w 2>/dev/null)
    echo -e "${GREEN}  ✅ OV13850检测成功 (ID: 0x$ID)${NC}"
elif i2cget -y 2 0x3C 0x300A w 2>/dev/null; then
    echo -e "${GREEN}  ✅ OV5645检测成功${NC}"
else
    echo -e "${RED}  ❌ 未检测到摄像头传感器${NC}"
    echo "  请检查: 电源、I2C连接、复位时序"
fi

# 3. 检查MCLK时钟输出
echo -e "\n[3] 检查MCLK时钟..."
if [ -f "/sys/kernel/debug/clk/clk_summary" ]; then
    grep -E "clk_cam|mclk|24M" /sys/kernel/debug/clk/clk_summary | head -5
fi

# 4. 查看媒体设备拓扑
echo -e "\n[4] 查看媒体设备拓扑..."
for media in /dev/media*; do
    echo -e "${YELLOW}  $media:${NC}"
    media-ctl -p -d $media 2>/dev/null | grep -E "entity|pad|link" | head -10
done

# 5. 查看V4L2设备
echo -e "\n[5] V4L2设备列表..."
v4l2-ctl --list-devices

# 6. 查看摄像头支持的格式
echo -e "\n[6] 摄像头支持格式..."
CAM_DEV=$(v4l2-ctl --list-devices | grep -A1 "rkisp" | grep video | head -1 | tr -d '[:space:]')
if [ -n "$CAM_DEV" ]; then
    echo "  设备: $CAM_DEV"
    v4l2-ctl -d $CAM_DEV --list-formats-ext 2>/dev/null | head -20
else
    echo "  ⚠️ 未找到摄像头设备"
fi

# 7. 配置MIPI链路
echo -e "\n[7] 配置MIPI链路..."
for media in /dev/media*; do
    media-ctl -l -d $media 2>/dev/null
done

# 8. 设置分辨率和格式
echo -e "\n[8] 设置采集参数..."
if [ -n "$CAM_DEV" ]; then
    v4l2-ctl -d $CAM_DEV --set-fmt-video=width=1920,height=1080,pixelformat=NV12
    v4l2-ctl -d $CAM_DEV --set-ctrl=exposure=256
    v4l2-ctl -d $CAM_DEV --set-ctrl=gain=0
fi

# 9. 采集单帧测试
echo -e "\n[9] 采集单帧..."
if [ -n "$CAM_DEV" ]; then
    v4l2-ctl --verbose -d $CAM_DEV \
        --set-fmt-video=width=1920,height=1080,pixelformat=NV12 \
        --stream-mmap --stream-count=1 --stream-to=./capture.raw
    if [ -f capture.raw ]; then
        echo -e "${GREEN}  ✅ 采集成功: capture.raw ($(du -h capture.raw | cut -f1))${NC}"
    else
        echo -e "${RED}  ❌ 采集失败${NC}"
    fi
fi

# 10. 查看内核日志
echo -e "\n[10] 摄像头相关内核日志..."
dmesg | grep -E "ov13850|ov5645|mipi|csi|vicap|isp" | tail -20

echo -e "\n========================================="
echo -e "${GREEN}调试完成${NC}"
echo "========================================="

6.3 常见问题排查表

问题现象 可能原因 排查命令 解决方案
I2C无设备 电源/连线问题 i2cdetect -y 2 检查AVDD(2.8V)/DOVDD(1.8V)/DVDD(1.2V)电源
芯片ID读不到 复位时序问题 dmesg \| grep ov13850 检查reset-gpios和pwdn-gpios配置
MCLK无输出 时钟未配置 grep mclk /sys/kernel/debug/clk/clk_summary 检查设备树clocks属性
media-ctl无拓扑 驱动未加载 lsmod \| grep ov13850 modprobe ov13850
无/dev/video节点 VICAP未绑定 media-ctl -p 检查设备树链路配置
MIPI错误 链路训练失败 dmesg \| grep MIPI 检查lane数和link_freq配置
图像偏色 ISP未工作 ps aux \| grep rkaiq 启动3A服务: rkaiq_3A_server
帧率低 MIPI带宽不足 计算link_freq公式 减少分辨率或增加lane数

七、总结

项目 OV13850 OV5645
分辨率 4224×3136 (13MP) 2592×1944 (5MP)
I2C地址 0x10 0x3C
MIPI Lane 2或4 2
驱动路径 drivers/media/i2c/ov13850.c drivers/media/i2c/ov5645.c
设备树节点 ov13850@10 ov5645@3c
调试工具 media-ctl, v4l2-ctl, i2cget 同上

关键配置要点

  1. 电源时序:必须按照 AVDD → DOVDD → DVDD 顺序上电,复位时序满足规格书要求

  2. MIPI参数:link_freq需通过公式 width × height × fps × bpp / lanes / 2 计算

  3. I2C地址:OV13850是0x10,OV5645是0x3C,注意区分7位和8位地址

  4. 数据通路:sensor → dphy → csi2 → vicap → isp,每一环节都需正确配置

  5. 调试流程:先确认I2C通信,再检查MIPI信号,最后验证ISP处理

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐