设计说明
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,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 调谐器中获取音频数据。
-
-
V4L2 为用户空间提供了字符设备的通用接口,设备节点 /dev/videoX,主设备号 81,次设备号的分配跟设备类型有关,规则定义如下:
设备类型 次设备号 视频设备 0~63 Radio 设备 64~127 Teletext 设备 192~233 VBI 设备 224~255 用户态 APP 通过 ioctl 控制 video 设备,通过 mmap 进行内存映射。在/dev 目录中会产生 videoX、radioX 和 vbiX 设备节点。
- 在 V4L2 框架中,将每一个 Sensor、DVP 硬件设备都看作一个 subdev,相应的有一个字符设备节点 /dev/v4l-subdevX,用户态通过这些节点的 ioctl 接口可以完成 subdev 的格式协商、时序配置等功能。
- Notifier 子模块是为了解决多个设备之间的初始化顺序、以及媒体流对接的匹配检查,如 DVP 需要等 Sensor 初始化完成后,才能去真正完成 video device 的注册。可见,DVP 和 Sensor 要有一个绑定的关系,这个关系是由 DTS 中的 remote-endpoint 来指定的。Notifier 会调用 Fwnode 系列接口来解析和获取 remote-endpoint 的属性字段。
- 为了进行数据流的管理,V4L2 维护了一个 device 链表,每一个 video device、V4L2 device、V4L2-subdev 都是一个 Media device 实例,这些 Media device 在数据结构的设计上第一个成员变量都是一个 struct media_entity,其中有 list_head 成员,借此互相链接起来。见下一节详述。
V4L2 的 Buf 队列管理
V4L2 的 Buffer 管理由 videobuf 子模块实现,从源头看分为两种方式的 Buffer:
- 驱动申请 Buffer
用户态通过 VIDIOC_REQBUFS ioctl 命令触发 Buffer 申请,然后使用 mmap 接口来获取用户态的 Buffer 地址。这种方式,Buffer 个数一般有个最大值 32 VIDEO_MAX_FRAME。
- 用户态申请 Buffer
用户态根据实际需要知道要申请多少 Buffer,然后借助其他机制(可以是 dma-buf)在用户态完成 Buffer(当然必须是物理连续的)申请,并将物理地址告诉 Video 驱动。
按照 Buffer 的物理连续,又可以分为三种情况:(详见 Documentationmediakapiv4l2-videobuf.rst)
- 物理连续、不连续的 Buffer 混用
几乎所有用户空间的 Buffer 都属于这种情况,这样最大可能的发挥虚拟内存管理的灵活性。缺点也很明显,这些 Buffer 给硬件的话需要有支持 scatter 的 DMA。
- 物理不连续、虚拟地址连续的 Buffer
它们由 vmalloc()分配,也用于支持 scatter 接口的 DMA 硬件。
- 物理连续的 Buffer
非常适合 DMA 类硬件的访问。
驱动开发者必须三选一,对于我们的 DVP 模块来说,要选择 3。并且底层用到 dma-buf。
V4L 第二版不再支持 Overlay 类型,而 V4L 第一版不支持 DMABUF 类型。
V4L2 在数据流传输的时候需要多个 Buf 切换,通过 struct vb2_queue 结构中的 Qbuf 两个 Buf 队列来管理,DVP 驱动中还需要维护一个 buf_list 来配合 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 个子模块:
- Video Dvice 管理,主要实现和 device 相关的注册、ioctl 处理。
- Notifier 管理,主要处理和 Sensor 设备的初始化次序的依赖关系。
- Buf 管理,主要实现 Buf 的入队、出队,以及在中断响应时切换 DVP 控制器的输出地址等。
- Subdev 管理,主要实现输入格式相关的接口。
- 寄存器访问,封装了对 DVP 控制器寄存器的读写访问。