2. Sensor驱动框架解析:Linux V4L2框架核心、RK平台Camera驱动分层与设备树配置

做Camera驱动,绕不开V4L2。很多新人一上来就被这堆抽象层搞懵了。我刚开始接触时也差不多,对着代码看了三天,感觉每个函数都认识,连起来就不知道在干嘛。后来调了几个Sensor,踩了些坑,才慢慢摸清楚这套框架到底是怎么转起来的。

这一章,我们就从V4L2的核心概念讲起,然后深入到瑞芯微平台的分层设计,最后落到设备树和pinctrl的配置上。说白了,就是让你知道驱动怎么写、怎么配、怎么跟硬件对上。

2.1 Linux V4L2框架核心

V4L2,全称Video for Linux 2,是Linux内核里处理视频设备的统一框架。它定义了一套标准接口,让应用层可以通过open/ioctl/poll这些系统调用来操作Camera设备。

核心概念就三个:设备节点、Video Buffer、Control。

概念 说明 我遇到过的坑
设备节点 /dev/videoX,用户空间操作Camera的入口 多个Sensor共用同一个video节点,导致打开失败
Video Buffer 用于存放图像数据的内存缓冲区,支持MMAP/DMABUF/USERPTR DMABUF方式下,buffer对齐要求没满足,图像花屏
Control 曝光、增益、白平衡等参数的控制接口 Control ID冲突,导致3A算法写不进去

V4L2的驱动模型是分层的。底层是具体的硬件驱动,比如Sensor驱动、ISP驱动;上层是V4L2核心框架,负责管理设备节点和buffer流转。中间通过v4l2_subdev和video_device这两个核心结构体来连接。

关键点:V4L2把Camera设备抽象成一个video_device,而Sensor、ISP这些子模块则抽象成v4l2_subdev。subdev之间通过media_entity和media_pad建立数据通路。这就是所谓的Media Controller框架。

举个例子,一个典型的Camera数据流是这样的:

Sensor (subdev) -> MIPI CSI (subdev) -> ISP (subdev) -> DMA Engine (video_device) -> 用户空间

每个箭头都对应一个media_link。你想想看,如果没有这套抽象,每次换一个Sensor或者改一下ISP配置,都得重写整个驱动,那得多痛苦。

2.2 RK平台Camera驱动分层

瑞芯微的Camera驱动,在V4L2框架基础上做了进一步分层。我个人习惯把它分成三层:

  1. Sensor驱动层:负责具体的Sensor初始化、寄存器配置、曝光/增益控制。通常由Sensor厂商提供,我们只需要做适配。
  2. CSI/ISP驱动层:负责MIPI接口的接收、ISP图像处理。这部分是RK平台的核心,由瑞芯微提供。
  3. DMA驱动层:负责将ISP处理完的图像数据搬运到内存,生成/dev/videoX节点。也是RK平台提供的。

这三层通过v4l2_subdev和media_entity串联起来。我在项目中遇到过一个问题:Sensor驱动已经注册成功了,但应用层就是拿不到图像。查了半天,发现是CSI和ISP之间的link没有enable。嗯,这种问题在调试初期特别容易碰到。

调试技巧:用media-ctl工具可以查看当前的media graph拓扑,确认各个subdev之间的连接状态。命令示例:media-ctl -p -d /dev/media0

下面这张图展示了RK平台Camera驱动的分层结构和数据流:

RK平台Camera驱动分层与数据流 应用层 (App / Camera HAL) V4L2核心框架 / Media Controller DMA驱动 (video_device) ISP驱动 (v4l2_subdev) MIPI CSI驱动 (v4l2_subdev) Sensor驱动 (v4l2_subdev) 数据流方向 Sensor → CSI → ISP → DMA → 用户空间 控制流方向相反

2.3 设备树配置与pinctrl

设备树,说白了就是描述硬件信息的配置文件。在RK平台上,Camera相关的设备树配置主要涉及三个方面:

  • Sensor节点:描述Sensor的I2C地址、复位/电源引脚、时钟频率等。
  • CSI节点:描述MIPI接口的lane数、时钟极性、虚拟通道等。
  • ISP节点:描述ISP的工作模式、输入源选择等。

我曾经在一个项目里,Sensor死活初始化不成功。用示波器量了I2C波形,发现SCL和SDA都是对的,但Sensor就是没响应。最后查设备树,发现reset-gpios的极性配反了。嗯,这种低级错误,排查起来最费时间。

注意:设备树中的pinctrl配置决定了GPIO的复用功能。如果pinctrl配错了,I2C或者MIPI信号可能根本出不来。我曾经遇到过CSI的时钟信号被复用成了普通GPIO,导致MIPI链路完全不通。

下面是一个典型的Sensor设备树配置示例:

// 设备树中Sensor节点的配置
&i2c4 {
    status = "okay";
    clock-frequency = <400000>;  // I2C时钟400KHz

    ov5645: ov5645@3c {
        compatible = "ovti,ov5645";
        reg = <0x3c>;  // I2C地址
        clocks = <&cru CLK_MIPI_CAMARAOUT_M4>;
        clock-names = "xvclk";
        power-domains = <&power RK3588_PD_VI>;

        // pinctrl配置:将GPIO4_A5复用为I2C4_SDA
        pinctrl-names = "default";
        pinctrl-0 = <&i2c4m1_xfer>;

        // 复位引脚,低电平有效
        reset-gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>;
        // 电源引脚
        power-gpios = <&gpio1 RK_PB3 GPIO_ACTIVE_HIGH>;

        // MIPI CSI配置
        port {
            ov5645_out: endpoint {
                remote-endpoint = <&csi_in>;
                data-lanes = <1 2>;  // 使用2 lane MIPI
                clock-noncontinuous;
                link-frequencies = /bits/ 64 <480000000>;
            };
        };
    };
};

// CSI接收端配置
&csi2_dphy0 {
    status = "okay";
    ports {
        #address-cells = <1>;
        #size-cells = <0>;
        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;
            csi_in: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&ov5645_out>;
                data-lanes = <1 2>;
            };
        };
    };
};

这里有几个关键点,我特别提醒一下:

  • compatible:必须和驱动里的of_match_table匹配,否则驱动加载不了。
  • data-lanes:MIPI的lane数量必须和硬件设计一致,多配或少配都会导致图像异常。
  • reset-gpios:极性一定要确认清楚,我曾经因为GPIO_ACTIVE_LOW和GPIO_ACTIVE_HIGH搞反,浪费了一整天。

经验之谈:调试设备树时,建议先用ls /sys/firmware/devicetree/base/查看设备树是否被正确解析。然后用cat /sys/kernel/debug/gpio检查GPIO的复用状态。这两个命令能帮你快速定位大部分配置问题。

pinctrl这块,说白了就是管脚复用。RK平台的pinctrl驱动会解析设备树中的pinctrl-0属性,然后配置对应的寄存器,把GPIO切换到需要的功能模式。比如I2C功能、MIPI功能、PWM功能等。

举个例子,如果Sensor的I2C引脚被复用成了普通GPIO,那I2C通信肯定失败。你想想看,这种情况下,Sensor驱动加载时执行i2c_transfer,返回的永远是-ENODEV。很多新手遇到这个问题,第一反应是I2C地址错了,或者Sensor坏了,很少有人会去查pinctrl。