3、RKISP驱动核心数据结构:rkisp_device、rkisp_pipeline、rkisp_stream深度解析

好,咱们今天来啃一块硬骨头——RKISP驱动里的三个核心数据结构。说实话,我刚接触RKISP驱动时,也被这三个结构体绕得晕头转向。它们就像三兄弟,长得像,但各司其职。搞懂了它们,你就掌握了ISP驱动的半壁江山。

我个人习惯把这三个结构体比作一个摄影团队:rkisp_device是导演,rkisp_pipeline是摄影组,rkisp_stream是具体的摄像机。这样想,是不是清晰多了?

3.1 rkisp_device:整个ISP的“大管家”

先看老大——rkisp_device。它代表一个完整的ISP硬件设备。在RK3399、RK3288这些芯片上,每个ISP硬件单元对应一个这样的结构体。

struct rkisp_device {
    struct device *dev;
    struct v4l2_device v4l2_dev;
    struct media_device media_dev;
    struct rkisp_pipeline pipeline;
    struct rkisp_stream stream[RKISP_MAX_STREAM];
    void __iomem *base_addr;
    int irq;
    struct clk *clks[RKISP_MAX_CLKS];
    struct rkisp_match_data *match_data;
    spinlock_t slock;
    struct mutex lock;
    // ... 还有不少成员
};

这个结构体里,我重点说几个关键字段:

  • dev:指向Linux设备模型中的device结构体。说白了,就是告诉内核“嘿,我在这儿呢”。
  • v4l2_dev和media_dev:这是V4L2和Media Controller框架的接口。没有它们,用户空间就找不到这个设备。
  • pipeline:这个很重要!它管理着ISP内部的数据流管道。
  • stream:一个数组,每个元素代表一个视频流。为什么是数组?因为一个ISP可以同时处理多个流,比如主路和子路。
  • base_addr:寄存器的基地址。所有硬件操作都通过它来读写。

核心要点:rkisp_device是全局唯一的。一个ISP硬件对应一个实例。它负责管理所有子模块的生命周期。

我记得第一次调试时,发现中断一直不触发。查了半天,原来是irq号没注册对。嗯,这种低级错误,犯过一次就记住了。

3.2 rkisp_pipeline:数据流的“调度中心”

接下来是老二——rkisp_pipeline。它描述的是ISP内部的数据处理流水线。你想想看,图像数据从传感器进来,要经过DPCC(坏点校正)、BLS(黑电平)、LSC(镜头阴影校正)……这一整套流程,就是pipeline来管理的。

struct rkisp_pipeline {
    struct media_pipeline pipe;
    struct rkisp_device *dev;
    struct rkisp_stream *stream;
    struct v4l2_subdev *sensor;
    struct v4l2_subdev *mipi_csi;
    struct rkisp_isp_subdev isp_sd;
    struct rkisp_params_subdev params_sd;
    struct rkisp_stats_subdev stats_sd;
    bool streaming;
    // ...
};

这里有几个关键点:

  • sensor、mipi_csi:指向实际的硬件子设备。pipeline负责把它们串联起来。
  • isp_sd、params_sd、stats_sd:这三个是ISP内部的核心子设备。分别处理图像数据、参数配置、统计信息。
  • streaming:一个布尔值,标记当前是否在推流。我曾经踩过一个坑:在streaming为true时修改了pipeline配置,结果系统直接挂了。所以,改配置前一定要检查这个标志位。

个人经验:调试pipeline时,我习惯先检查streaming状态。如果发现流已经启动,但图像出不来,多半是pipeline里的某个子设备没有正确链接。用media-ctl工具打印一下拓扑图,一目了然。

3.3 rkisp_stream:用户看到的“视频流”

最后是小弟——rkisp_stream。这个结构体直接面向用户空间。应用程序通过V4L2接口打开一个video节点,背后就是这个结构体在工作。

struct rkisp_stream {
    struct rkisp_device *dev;
    struct video_device vdev;
    struct vb2_queue buf_queue;
    struct rkisp_buffer *curr_buf;
    struct list_head buf_list;
    enum rkisp_stream_mode mode;
    struct v4l2_pix_format_mplane pixm;
    struct v4l2_rect crop;
    // ...
};

重点字段解析:

  • vdev:V4L2视频设备节点。用户空间通过/dev/videoX操作的就是它。
  • buf_queue:缓冲区队列。应用程序通过QBUF/DQBUG来管理缓冲区,底层就是操作这个队列。
  • curr_buf:当前正在处理的缓冲区。中断处理时,我们会把数据填到这个缓冲区里。
  • mode:流模式。是RAW数据还是YUV?是主路还是子路?这个字段决定了后续的处理逻辑。
  • crop:裁剪区域。用户可以通过V4L2的S_SELECTION来设置,只输出感兴趣的区域。

避坑指南:我曾经遇到过一个问题——应用程序设置了裁剪区域,但输出的图像还是全尺寸的。查了两天才发现,crop的值虽然设置了,但pipeline里的scaler模块没有同步更新。所以,修改stream参数时,一定要确保pipeline里的所有相关模块都同步了。

3.4 三个结构体的“三角关系”

好了,三个结构体都介绍完了。但它们之间到底怎么配合的?我画了一张图,帮你理清关系。

rkisp_device (导演) rkisp_pipeline (摄影组) sensor → mipi_csi → isp_sd params_sd (参数配置) stats_sd (统计信息) streaming: true/false rkisp_stream (摄像机) vdev: /dev/videoX buf_queue (缓冲区队列) mode: RAW / YUV crop: 裁剪区域 包含 包含 数据流向 数据流向 关系:rkisp_device 包含 rkisp_pipeline 和 rkisp_stream,pipeline 处理数据,stream 面向用户

从这张图可以看出:

  1. rkisp_device是顶层容器。它创建并管理pipeline和stream。
  2. rkisp_pipeline负责数据流的内部处理。它把sensor、MIPI CSI、ISP子设备串联成一条完整的处理链路。
  3. rkisp_stream是用户空间的接口。应用程序通过它来配置参数、获取图像数据。
  4. 数据流向是:sensor → pipeline处理 → stream输出给用户。

关键理解:一个device可以有多个stream(比如主路和子路),但只有一个pipeline。所有stream共享同一个pipeline的处理能力。这就是为什么修改pipeline配置时,要确保所有stream都处于空闲状态。

3.5 实际项目中的“三角关系”

我在一个项目中遇到过这样的场景:客户要求同时输出RAW和YUV两个流。RAW用于算法处理,YUV用于预览显示。

当时我配置了两个rkisp_stream实例:

  • stream[0]:模式设为RKISP_STREAM_RAW,输出RAW12数据。
  • stream[1]:模式设为RKISP_STREAM_YUV,输出YUV420数据。

但问题来了——两个stream共享同一个pipeline。pipeline里的ISP模块只能以一种配置运行。RAW流需要关闭一些图像增强模块,而YUV流需要开启。怎么办?

我的解决方案是:在pipeline里增加一个mode字段,根据当前活跃的stream动态切换配置。当只有RAW流时,pipeline走“直通”模式;当YUV流加入时,pipeline切换到“完整处理”模式。

小技巧:处理多stream时,我建议在rkisp_pipeline里维护一个active_streams位图。每次stream启动或停止时,更新这个位图,然后根据位图动态调整pipeline配置。这样既灵活又安全。

3.6 总结一下

这三个结构体,说白了就是ISP驱动的骨架:

结构体 角色 核心职责 关键字段
rkisp_device 导演 管理整个ISP硬件,生命周期控制 dev, v4l2_dev, pipeline, stream[]
rkisp_pipeline 摄影组 数据流调度,子设备串联 sensor, isp_sd, params_sd, streaming
rkisp_stream 摄像机 用户接口,缓冲区管理 vdev, buf_queue, mode, crop

嗯,这三个结构体搞明白了,RKISP驱动的框架你就掌握了七七八八。剩下的就是各个子模块的具体实现了。下次我们聊ISP子设备的时候,你会发现,它们都是在为这三个核心结构体服务的。

最后提醒一句:写驱动时,千万别把这三个结构体的生命周期搞混了。device在probe时创建,remove时销毁。pipeline和stream在open时初始化,close时清理。我曾经见过一个驱动,在remove之后还在访问pipeline,结果内核直接oops了。血的教训啊。

可可精品资料库