Edit online

设计说明

2 Dec 2024
Read time: 4 minute(s)
本模块源代码在内核目录 linux-5.4/drivers/media/platform/artinchip下,目录结构如下:
drivers/media/platform/artinchip/
├── aic_buf.c  // 和 buf 管理相关的处理代码
├── aic_dvp.c  // DVP 驱动的初始化入口,主要实现了 probe、Notifier 接口
├── aic_dvp.h  // DVP 驱动共用的头文件,其中定义了寄存器、共用数据结构、全局函数等
├── aic_dvp_hw.c  // 对寄存器的访问封装
├── aic_video.c // 和 L2 框架强相关的一些接口定义,如 ops、ioctl_ops 的接口实现等
├── Kconfig
└── Makefile

V4L2 软件框架

Linux 中的 V4L2 框架是一个专门为视频输入输出设备而设计的成熟方案,DVP 驱动需要基于 V4L2。


v4l2_system

1. Linux V4L2 子系统架构图
  1. V4L2,Video For Linux 第 2 版,最早出现在 1998 年,一个针对无线广播(收音机)、视频捕获、视频输出设备的通用框架,源码目录 drivers/media/v4l2-core。V4L2 中支持的 5 大类接口设备:
    • Video capture interface:影像捕获接口。

    • Video output interface:视频输出接口,主要用于电视信号类。

    • Video overlay interface:视频覆盖接口,方便视频显示设备直接从捕获设备上获取数据。

    • VBI interface:垂直消隐接口,可提供垂直消隐区的数据接入,包括 raw 和 sliced 两种。

    • Radio interface:广播接口,主要是从 AM 或 FM 调谐器中获取音频数据。

  2. V4L2 为用户空间提供了字符设备的通用接口,设备节点 /dev/videoX,主设备号 81,次设备号的分配跟设备类型有关,规则定义如下:

    设备类型 次设备号
    视频设备 0~63
    Radio 设备 64~127
    Teletext 设备 192~233
    VBI 设备 224~255

    用户态 APP 通过 ioctl 控制 video 设备,通过 mmap 进行内存映射。在/dev 目录中会产生 videoX、radioX 和 vbiX 设备节点。

  3. 在 V4L2 框架中,将每一个 Sensor、DVP 硬件设备都看作一个 subdev,相应的有一个字符设备节点 /dev/v4l-subdevX,用户态通过这些节点的 ioctl 接口可以完成 subdev 的格式协商、时序配置等功能。
  4. Notifier 子模块是为了解决多个设备之间的初始化顺序、以及媒体流对接的匹配检查,如 DVP 需要等 Sensor 初始化完成后,才能去真正完成 video device 的注册。可见,DVP 和 Sensor 要有一个绑定的关系,这个关系是由 DTS 中的 remote-endpoint 来指定的。Notifier 会调用 Fwnode 系列接口来解析和获取 remote-endpoint 的属性字段。
  5. 为了进行数据流的管理,V4L2 维护了一个 device 链表,每一个 video device、V4L2 device、V4L2-subdev 都是一个 Media device 实例,这些 Media device 在数据结构的设计上第一个成员变量都是一个 struct media_entity,其中有 list_head 成员,借此互相链接起来。见下一节详述。

V4L2 的 Buf 队列管理

V4L2 的 Buffer 管理由 videobuf 子模块实现,从源头看分为两种方式的 Buffer:

  1. 驱动申请 Buffer

    用户态通过 VIDIOC_REQBUFS ioctl 命令触发 Buffer 申请,然后使用 mmap 接口来获取用户态的 Buffer 地址。这种方式,Buffer 个数一般有个最大值 32 VIDEO_MAX_FRAME。

  2. 用户态申请 Buffer

    用户态根据实际需要知道要申请多少 Buffer,然后借助其他机制(可以是 dma-buf)在用户态完成 Buffer(当然必须是物理连续的)申请,并将物理地址告诉 Video 驱动。

按照 Buffer 的物理连续,又可以分为三种情况:(详见 Documentationmediakapiv4l2-videobuf.rst

  1. 物理连续、不连续的 Buffer 混用

    几乎所有用户空间的 Buffer 都属于这种情况,这样最大可能的发挥虚拟内存管理的灵活性。缺点也很明显,这些 Buffer 给硬件的话需要有支持 scatter 的 DMA。

  2. 物理不连续、虚拟地址连续的 Buffer

    它们由 vmalloc()分配,也用于支持 scatter 接口的 DMA 硬件。

  3. 物理连续的 Buffer

    非常适合 DMA 类硬件的访问。

注:

驱动开发者必须三选一,对于我们的 DVP 模块来说,要选择 3。并且底层用到 dma-buf。

V4L 第二版不再支持 Overlay 类型,而 V4L 第一版不支持 DMABUF 类型。

V4L2 在数据流传输的时候需要多个 Buf 切换,通过 struct vb2_queue 结构中的 Qbuf 两个 Buf 队列来管理,DVP 驱动中还需要维护一个 buf_list 来配合 DVP 控制器的地址更新。整个 Buf 流转的过程如下图:


dvp_buf_list

2. DVP 驱动中的 Buf 队列管理
  • Qbuf 队列(在代码中见 struct vb2_queue->queued_list):是一些空闲 Buf,等待 Sensor 的数据到来后,DVP 驱动会从这个队列中找可用 Buf 来保存下一帧数据。
  • DQbuf 队列(在代码中见 struct vb2_queue->done_list):是一些填了视频数据的 Buf,等待用户来处理这些数据,一般用户处理完后需要将 Buf 还给驱动,也就是还给 Qbuf。
  • 从 Qbuf 和 DQbuf 来的 buf 格式是 buffer,其中没有字段可以保存物理地址(DVP 控制器需要),同时还需要为每个 buf 记录一个是否正在被 DVP 使用的状态,所以 DVP 驱动中定义了基于 vb2_buffer 结构的封装 buffer,并且维护一个和 Qbuf 几乎同步的队列。
  • 从图中的流转过程看,运行期间,在某一时刻,DVP 需要使用一个 Buf,APP 需要使用一个 Buf,QBuf 需要有一个 Buf 在等待(否则 DVP 的 done 中断来了后发现没有等待的 Qbuf 会发生丢帧),一共至少要有 3 个 Buf。
  • 以 YUV422 格式计算,有两个 plane,在 L2 框架中这一组 plane 算一个 Buf,3 个 Buf 就需要申请 6 个 plane,总大小 = 长 * 宽 * 2 * 3
  • DVP 驱动中需要定义一个 queue 实例,Video device 中会有一个指针*queue 指向该实例。

DVP 驱动的子模块结构

基于以上对 V4L2 框架的分析,DVP 驱动内部可以分为以下 5 个子模块:


dvp_drv_structure

3. DVP 驱动的子模块结构
  • Video Dvice 管理,主要实现和 device 相关的注册、ioctl 处理。
  • Notifier 管理,主要处理和 Sensor 设备的初始化次序的依赖关系。
  • Buf 管理,主要实现 Buf 的入队、出队,以及在中断响应时切换 DVP 控制器的输出地址等。
  • Subdev 管理,主要实现输入格式相关的接口。
  • 寄存器访问,封装了对 DVP 控制器寄存器的读写访问。