4. Linux设备驱动与摄像头子系统

在 TI Sitara(如 AM62x、AM57x)这类机器视觉边缘终端上,摄像头从“插上就能出图”到“稳定低延迟出图”,中间隔着一整套 Linux 摄像头子系统。理解这套子系统的关键,不是记住某个 ioctl 的名字,而是看清一条图像数据从传感器感光到应用拿到帧缓冲的完整路径:谁负责配置传感器、谁负责搬运像素、谁负责把缓冲区交给用户态。本节围绕 V4L2 框架、CSI-2 与并行接口驱动、设备树配置以及 DMA-BUF 零拷贝链路展开。

一、V4L2 框架:把摄像头抽象成一条可编程的管线

V4L2(Video for Linux 2)是 Linux 内核中统一的视频采集接口。它并不直接“驱动摄像头”,而是定义了一套设备节点与操作语义,让应用通过 /dev/videoX 完成格式协商、缓冲区申请、入队、出队和流开关。对机器视觉应用而言,最常用的调用序列是:VIDIOC_QUERYCAP 确认能力,VIDIOC_S_FMT 设定分辨率与像素格式,VIDIOC_REQBUFS 申请缓冲区,mmap 映射到用户态,随后循环执行 VIDIOC_QBUF 入队与 VIDIOC_DQBUF 出队,最后 VIDIOC_STREAMON 启动。

需要特别区分的是:V4L2 设备节点可能对应“采集节点”(video node)或“元数据/参数节点”(meta/subdev node)。在 Sitara 的媒体控制器(Media Controller)模型下,传感器、CSI-2 接收器、DMA 引擎被拆成多个 subdev,通过 media pipeline 用 pad 和 link 连接。应用若只操作 /dev/video0,往往还需要先用 media-ctl 配置各 subdev 的格式与链路,否则会出现“节点存在但出图格式不对”的典型问题。

二、CSI-2 与并行接口:两种物理接入方式

Sitara 的摄像头接入主要有两类。CSI-2(MIPI CSI-2)是差分串行接口,使用 1~4 条 data lane 加一条 clock lane,具备高带宽、抗干扰强、线缆少的特点,适合高分辨率工业相机。并行接口(Parallel/DVP)则是传统的 PCLK、HSYNC、VSYNC 加 8~16 位数据线,时序直观、调试简单,但引脚多、带宽有限,常见于低分辨率或 legacy 传感器。

在驱动层面,CSI-2 通常由“传感器驱动 + CSI-2 接收控制器驱动(如 Cadence CSI2RX)+ DMA 写引擎”三段组成;并行接口则常由“传感器驱动 + VPFE/VIN 采集控制器”组成。无论哪种,传感器驱动都要实现 v4l2_subdev_ops,提供 s_stream、get_fmt、set_fmt 等回调,并通过 I2C 配置寄存器。CSI-2 还需正确设置 lane 数、数据速率与虚拟通道,否则会出现“有 clock 无数据”或“图像错位”的现象。

Sitara 摄像头采集链路:从传感器到用户态零拷贝 图像传感器 OV5640 / IMX219 I2C 寄存器配置 物理接口 CSI-2:1~4 lane 差分 并行:PCLK/HSYNC/VSYNC 接收控制器 Cadence CSI2RX 或 VPFE / VIN DMA 写引擎 写入 DDR 帧缓冲 dma-buf 导出 设备树(Device Tree)描述硬件拓扑 i2c 节点 → sensor;csi2rx 节点 → remote-endpoint;dma 节点 → dma-names;port/endpoint 建立 media link V4L2 / Media Controller 框架 /dev/videoX 采集节点 · subdev 格式协商 · media-ctl 配置 pipeline · REQBUFS/QBUF/DQBUF 用户态应用(机器视觉) mmap 映射 · DMA-BUF fd 传递 · 零拷贝送入 GPU/VPU/推理引擎 · 避免 memcpy 零拷贝收益 省去 1~2 次 DDR 往返拷贝

三、设备树配置:把硬件连接翻译成内核可识别的拓扑

设备树是摄像头能否被正确枚举的第一道关卡。以 CSI-2 为例,典型结构包含三部分:I2C 总线下的 sensor 节点、CSI2RX 控制器节点、以及连接两者的 port/endpoint。sensor 节点需要 compatible、reg、clocks、reset-gpios、port 等属性;CSI2RX 节点需要 phys、dma-names、ports。下面是一个简化示例:

&i2c2 {
    status = "okay";
    ov5640: camera@3c {
        compatible = "ovti,ov5640";
        reg = <0x3c>;
        clocks = <&clk_cam0>;
        reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
        port {
            ov5640_out: endpoint {
                remote-endpoint = <&csi2rx_in>;
                data-lanes = <1 2>;
                clock-noncontinuous;
            };
        };
    };
};

&csi2rx {
    status = "okay";
    ports {
        port@0 {
            csi2rx_in: endpoint {
                remote-endpoint = <&ov5640_out>;
                data-lanes = <1 2>;
            };
        };
    };
};

配置完成后,用 media-ctl -p 查看 pipeline,应能看到 sensor → csi2rx → dma 的 link 全部为 ENABLED。若 link 为 IMMUTABLE 或缺失,说明 endpoint 引用写错或节点未使能。随后用 v4l2-ctl --list-formats-ext 确认格式,再用 v4l2-ctl --stream-mmap --stream-count=10 抓 10 帧验证通路。

四、DMA-BUF 与零拷贝采集链路

传统采集链路中,DMA 把图像写入内核缓冲区,用户态 read() 或 mmap 后再拷贝到应用缓冲,若还要送 GPU 推理,又得再拷贝一次。每次拷贝都消耗 DDR 带宽和 CPU 时间。DMA-BUF 的作用是让不同驱动/设备共享同一块物理缓冲区,通过文件描述符(fd)传递,避免重复拷贝。

在 V4L2 中启用 DMA-BUF 的典型做法是:应用先用 VIDIOC_REQBUFS 申请缓冲区,再通过 VIDIOC_EXPBUF 把缓冲区导出为 dma-buf fd,然后把这个 fd 传给 GPU、VPU 或 NPU 驱动。这样采集、显示、推理三方共享同一块内存,链路中不再出现像素级 memcpy。判断是否真正零拷贝,可以观察 /proc/interrupts 中 DMA 中断频率、perf 统计的 memcpy 占比,以及端到端延迟是否随分辨率线性恶化。

调试建议:先用 media-ctl -p 确认 pipeline,再用 v4l2-ctl --stream-mmap 确认出图,最后才引入 DMA-BUF 与推理引擎。分层排查能快速定位是设备树、格式协商还是缓冲区共享的问题。
常见坑:CSI-2 lane 数与设备树 data-lanes 不一致会导致图像撕裂或全黑;clock-noncontinuous 缺失可能让部分传感器无法出流;DMA-BUF 导出后若未正确同步(如缺少 fence),会出现推理读到半帧数据的现象。

把这条链路串起来看:设备树负责“描述连接”,V4L2/Media Controller 负责“协商格式与建立管线”,CSI-2/并行驱动负责“把电信号变成像素”,DMA-BUF 负责“让像素只搬一次”。四者配合,才能让 Sitara 边缘终端在高分辨率下保持低延迟、低 CPU 占用的稳定采集。