ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

[ZYNQ Linux] V4L2视频驱动

[ZYNQ Linux] V4L2视频驱动

[ZYNQ Linux] V4L2视频驱动

文章目录

  • [ZYNQ Linux] V4L2视频驱动
    • 数据流搭建过程及问题
    • AXI VDMA图像接收思路
    • V4L2驱动
      • V4L2驱动——视频设备
        • ioctl_ops、vb2层与vb2_ops的调用关系
      • AXI VDMA实现细节
        • 将缓冲区加入图像队列——buf_queue
        • 启动流式传输——start_streaming
        • 停止流式传输,清空缓冲区队列——stop_streaming
        • 设备树设计
    • 用户空间使用
      • 代码使用步骤
      • 命令行工具 v4l2-ctl
    • TODO
    • 其它问题

数据流搭建过程及问题

HDMI输入 → ADV7611解码 →AXIVDMA →PS

  1. ADV7611复位接到了PL需要进行复位
    使用一个axi gpio进行复位,axi gpio会注册一个gpiochip出来。

    echo507>/sys/class/gpio/exportechoout>/sys/class/gpio/gpio507/directionecho0>/sys/class/gpio/gpio507/valuesleep1echo1>/sys/class/gpio/gpio507/value
  2. ADV7611初始化
    配置一些寄存器

  3. i2cdetect -r -y扫描不到0x4C等地址,不过可以读到数据
    可能是设备没有对i2cdetect的扫描进行响应
    不要过度依赖和相信工具

    i2cget-y10x4c 0x42

    重新尝试了下,死活配置不成功,改用AXI IIC后i2cdetect能扫描到,初步判断PS I2C1转EMIO出来的问题,不知道是不是需要一些特殊处理。

  4. EDID设置
    问题:插电脑不能识别屏幕。
    HDMI接口的I2C没连接到解码芯片,需要连接到一个EEPROM的IP核。
    EDID IP核需要配置1080的文件,里面的名称不能空着
    添加之后连接到电脑,电脑会识别到一个屏幕。

  5. 时钟和缓冲问题
    问题:VDMA传输的一帧图像只有前几行有效,其余全0。
    数据流程中有一个IP核把视频时序转为了AXI Stream格式,需要考虑写入速率(写入时钟)和读取速率(读出时钟)的问题。
    位宽一样的话,读出时钟的频率要高于写入时钟。
    输入是148.5M(1080p60帧),AXIVDMA的时钟也要高于或者等于该时钟。
    还要考虑缓冲区设置多大比较合适。

AXI VDMA图像接收思路

  1. 通过DMA接口申请连续的内存块(DMA传输需要特殊的内存块,不能是malloc常规申请的)
  2. 调用DMA接口接收数据到内存空间
  3. 内核空间提供mmap接口供用户空间使用
  4. 用户空间调用mmap获取图像

常规的图像接收思路原理都是类似的,提供的API接口或者使用方式有所区别。
还会有缓冲池、缓冲区、队列等等的区别。

V4L2驱动

V4L2驱动——视频设备

接口说明
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);注册 V4L2 设备实例,管理设备的数据结构,用户空间不可见
int video_register_device(struct video_device *vdev,int type, int nr);注册视频设备,由V4L2设备进行管理即和上面注册v4l2设备关联,创建/dev/videoX的设备节点
struct video_device视频设备结构体,设备的核心操作接口fops和ioctl_ops,图像队列queue
const struct v4l2_file_operations *fops;open、release、mmap等,内核有通用的vb2_fop_mmap操作接口等等
const struct v4l2_ioctl_ops *ioctl_ops;ioctl操作,一大堆,需要实现一些关键的
structvb2_queue*queue;管理视频缓冲区的队列,一个缓冲区或者缓冲池
内核fops通用接口
v4l2_fh_open打开文件
vb2_fop_release释放与视频缓冲区相关的资源
vb2_fop_mmap内存映射
vb2_fop_pollpoll机制
内核ioctl_ops通用接口
vb2_ioctl_create_bufs创建视频缓冲区
vb2_ioctl_expbuf导出视频缓冲区,可以与其他设备或进程共享内存
ioctl_ops用户实现
vidioc_querycap查询设备能力
vidioc_g_fmt_vid_cap当前数据格式,包括分辨率、像素格式等参数 get format
vidioc_try_fmt_vid_cap尝试设置视频捕获格式,但不实际应用
vidioc_s_fmt_vid_cap设置视频捕获设备的格式(如分辨率、像素格式)set format
vidioc_enum_fmt_vid_cap枚举视频捕获设备支持的像素格式 enum format
vidioc_reqbufs申请视频缓冲区内存
vidioc_querybuf查询已申请缓冲区的详细信息,包括内存偏移、长度等
vidioc_qbuf将缓冲区放入队列或队列 queue buffer
vidioc_dqbuf从队列中取出缓冲区 dequeue buffer
vidioc_streamon启动视频流传输
vidioc_streamoff停止视频流传输
struct vb2_queue缓冲区队列需要实现对队列的操作vb2_ops,内存操作mem_ops
queue_setup初始化视频缓冲区队列,分配缓冲区内存
buf_prepare准备缓冲区数据
buf_queue将缓冲区加入队列
start_streaming启动流式传输
stop_streaming停止流式传输,清空缓冲区队列
wait_prepare等待缓冲区准备完成
wait_finish等待缓冲区处理完
vb2(video buffer)的通用接口vb2_fop层、vb2_ioctls层、vb2层、vb2_ops层
vb2_ops_wait_finish等待所有缓冲区操作完成
vb2_ops_wait_prepare等待所有缓冲区准备完成
vb2_plane_vaddr获取缓冲区的虚拟地址
vb2_plane_size获取缓冲区平面的大小
vb2_set_plane_payload设置缓冲区平面的有效载荷大小
vb2_is_busy检查缓冲区队列是否正忙
vb2_reqbufs请求分配缓冲区
vb2_querybuf查询缓冲区信息
vb2_qbuf将缓冲区加入队列
vb2_dqbuf从队列中取出缓冲区
vb2_streamon启动流式传输
vb2_streamoff停止流式传输
vb2_buffer_done标记缓冲区处理完成
vb2_dma_contig_plane_dma_addr获取DMA连续缓冲区的物理地址
vb2_is_streaming检查是否正在流式传输
vb2_queue_init初始化视频缓冲区队列
vb2_dma_contig_memopsDMA内存操作集合,可传给mem_ops
ioctl_ops、vb2层与vb2_ops的调用关系

方向:用户空间 → ioctl_ops → vb2层 → vb2_ops。

  • 用户空间通过ioctl系统调用,传递到驱动中定义的ioctl_ops结构体
  • ioctl_ops调用vb2层,如vidioc_streamon等会进一步调用vb2层的通用接口vb2_streamon
  • vb2层回调驱动实现的vb2_ops,vb2_streamon回调start_streaming

分工:

  • ioctl_ops处理用户请求的解析与转发。
  • vb2层实现通用缓冲区管理逻辑。
  • vb2_ops由驱动实现硬件细节。

AXI VDMA实现细节

将缓冲区加入图像队列——buf_queue
  1. 获取缓冲区的物理地址,用于DMA传输——vb2_dma_contig_plane_dma_addr
  2. 准备交错传输DMA描述符,需要设置DMA地址等参数——dmaengine_prep_interleaved_dma
  3. 设置描述符的回调函数,如果DMA传输完成就把缓冲区标记为有图像数据(VB2_BUF_STATE_DONE)
  4. 将描述符提交到 DMA 控制器的工作队列(提交未开始)——dmaengine_submit
  5. 把缓冲区放到活动缓冲区队列里面(后续获取图像就是从这个队列里面获取)
  6. 触发 DMA 控制器开始执行已提交的传输——dma_async_issue_pending
启动流式传输——start_streaming
  1. 让DMA开始工作就行——dma_async_issue_pending
停止流式传输,清空缓冲区队列——stop_streaming
  1. 结束DMA传输——dmaengine_terminate_all
  2. 将活动缓冲区队列里面的缓冲区设置为无效(VB2_BUF_STATE_ERROR)
设备树设计
vdma_capture:vdma-capture{compatible="xlnx,axi-vdma-capture";dmas=<&axi_vdma_11>;dma-names="port0";};

用户空间使用

代码使用步骤

  1. 打开设备:使用open()函数打开V4L2设备文件,通常是/dev/video0。根据需要,可以选择阻塞或非阻塞模式。
  2. 查询设备能力:通过VIDIOC_QUERYCAP命令获取设备的能力,例如是否支持视频采集(V4L2_CAP_VIDEO_CAPTURE)和流式I/O(V4L2_CAP_STREAMING)。
  3. 设置图像格式:使用VIDIOC_S_FMT命令配置采集的图像格式,包括宽度、高度和像素格式(如YUYV、MJPEG等)。这是决定图像数据如何组织的关键一步。
  4. 申请与映射缓冲区:
  • 申请缓冲区:通过VIDIOC_REQBUFS向驱动程序申请若干个视频缓冲区。
  • 映射缓冲区:对于内存映射(mmap)方式,使用VIDIOC_QUERYBUF查询每个缓冲区信息,然后通过mmap()系统调用将内核空间的缓冲区映射到用户空间,以便应用程序可以直接访问。这是最常用且高效的方式。
  • 缓冲区入队:在开始采集前,需要通过VIDIOC_QBUF命令将所有空闲的缓冲区放入驱动层的输入队列中。
  1. 开始/停止视频流:
  • 通过VIDIOC_STREAMON命令启动视频采集,驱动程序开始向队列中的缓冲区填充数据。
  • 在循环中,使用VIDIOC_DQBUF从驱动层的输出队列中取出已填充好数据的缓冲区。应用程序处理完该帧数据后,必须再次调用VIDIOC_QBUF将其放回输入队列,以便驱动程序循环使用。
  • 采集完成后,通过VIDIOC_STREAMOFF命令停止视频流。
  1. 关闭设备:使用close()函数关闭设备文件,并释放所有资源。

命令行工具 v4l2-ctl

TODO

  1. 解码芯片在用户空间单独进行初始化和配置,未加入到驱动框架里边。可以使能内核相关驱动,作为v4l2子设备添加。
  2. 联动Framebuffer、DRM进行图像输出。

其它问题

内核有个VVIP的驱动,不是给AXI-VDMA用的,是给另外的IP核用的,不能直接使用。

  1. no VDMA channel found(VVIP驱动)

没找到DMA通道,设备树通道有问题,1是写,从DMA向外写(S2MM)。

[2.246473]xilinx-video axi-video-cap: no VDMA channel found[2.251733]xilinx-video axi-video-cap: /axi-video-cap/ports/port@0 initialization failed[2.259907]xilinx-video axi-video-cap: DMA initialization failed

\linux-xlnx\Documentation\devicetree\bindings\dma\xilinx\xilinx_dma.txt

* DMA client Required properties: - dmas: a list of<[Video DMA device phandle][Channel ID]>pairs, where Channel ID is'0'forwrite/tx and'1'forread/rx channel. For MCMDA, MM2S channel(write/tx)ID start from'0'and isin[0-15]range. S2MM channel(read/rx)ID start from'16'and isin[16-31]range. These channels ID are fixed irrespective of IP configuration. - dma-names: a list of DMA channel names, one per"dmas"entry
  1. no subdev found in graph(VVIP驱动)

没有子设备,v4l2驱动需要子设备构成一个完整的数据流

[2.253037]xilinx-video axi-video-cap: /axi-video-cap/ports/port@0 initialization failed[2.260727]xilinx-video axi-video-cap: DMA initialization failed[4.528033]xilinx-video axi-video-cap: no subdev foundingraph[4.539722]xilinx-video: probe of axi-video-cap failed with error-2
  1. 创建一个虚拟的子设备可以创建成功,但还是不能用(VVIP驱动)
    驱动里面还是用不了,没能成功获取到图像格式,需要另一个IP核的支持。
[2.373491]xilinx-video axi-video-cap: /axi-video-cap/ports/port@0 initialization failed[2.381152]xilinx-video axi-video-cap: DMA initialization failed[4.646649]xilinx-video axi-video-cap: device registered
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进