全志T7 v4l2从应用程序角度分析驱动的调用

在这里插入图片描述

标准调用流程

在这里插入图片描述

1、open

RecordInit()–>recordInit()–>initializeDev()–>connectCamera()–>connectDevice()–>openCameraDev()–>open()
应用程序首先调用open打开“/dev/video0”设备节点,获得方便后续操作的文件描述符:
在这里插入图片描述
注意这里的打开模式使用“O_RDWR | O_NONBLOCK”,即可读可写和非阻塞。

2、获取Camera驱动支持的功能(能力)

RecordInit()–>recordInit()–>initializeDev()–>connectCamera()–>connectDevice()–>openCameraDev()–>ioctl()
调用Ioctl方法,并使用VIDIOC_QUERYCAP命令标识,调用结果是填充一个v4l2_capability结构体变量。
在/drivers/media/v4l2-core/v4l2-ioctl.c中有:
在这里插入图片描述
IOCTL_INFO_FNC宏的意思是:
IOCTL_INFO_FNC(命令, 实现函数, 打印信息(debug使用), flag)
所以设置VIDIOC_QUERYCAP命令标识会调用v4l_querycap(),从而调用到vin驱动中的vidioc_querycap():
在这里插入图片描述
V4L2_CAP_VIDEO_CAPTURE_MPLANE:是支持多平面格式的视频捕获设备;
V4L2_CAP_STREAMING:支持流I/O控制。V4L2 Framework定义了三种不同的方式(read/write系统调用、异步I/O、流I/O控制),用于从设备中读取数据。 Streaming I/O设计的目的就是为了减少在数据处理的各个环节中,拷贝的次数,从而实现各阶段硬件的无缝配合,其用结合了内存映射和DMA传输方式。
V4L2_CAP_READWRITE:支持read/write系统调用的方式来读写数据。效率不高,一般使用Streaming I/O方式。

3、选择当前视频输入通道

RecordInit()–>recordInit()–>initializeDev()–>connectCamera()–>connectDevice()–>openCameraDev()–>ioctl()
调用Ioctl方法,并使用VIDIOC_S_INPUT命令标识:
在这里插入图片描述
最终调用到vin_video.c的vidioc_s_input(),实际上会对 csi device 进行初始化工作。这个函数通过应用程序传递下来的index序号,选择一条视频通道pipe,然后调用这个pipe涉及到的ISP、Scaler、Sensor等的“subdev–>ops–>core–>init”函数,例如nvp6134的sensor_init()。

static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
	__vin_sensor_setup_link(module, valid_idx, 1);
	ret = vin_pipeline_call(vinc, open, &cap->pipe, &cap->vdev.entity, true);
	//下行:调用sunxi_isp.c sunxi_isp_subdev_init(1)
	ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_ISP], core, init, 1);
	//下行:调用sunxi_scaler.c sunxi_csi_subdev_init(1)
	ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SCALER], core, init, 1);
	//下行:调用nvp6134的sensor_init()
	ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, init, 1);
}

__vin_sensor_setup_link():创建一个media link:[nvp6134] => [sunxi_csi.2]
vin_pipeline_call 那句代码:实际调用vinc->pipeline_ops->open(&cap->pipe, &cap->vdev.entity, true),即调用了vin.c __vin_pipeline_open(&cap->pipe, &cap->vdev.entity, true),具体参考《vin core驱动.docx》

最后一行代码将调用nvp6134的sensor_init(),它并没有真正的操作到nvp6134的寄存器,而是初始化驱动中的一些参数:
在这里插入图片描述
这里设置输出分辨率为1280*720,、帧率为30fps。

4、枚举帧大小

RecordInit()–>recordInit()–>initializeDev()–>Initialize()–>initDefaultParameters()–>getFullsize()–>ioctl()
调用Ioctl方法,并使用VIDIOC_ENUM_FRAMESIZES命令标识:
在这里插入图片描述
但是驱动没有实现该IOCTL,返回-ENOIOCTLCMD
在这里插入图片描述
最好在调试过程中打印这里的mFullSizeWidth、mFullSizeHeight是多少(initDefaultParameters()里面很多参数不直观,需要在线调试)

5、尝试设置一种数据格式

RecordInit()–>recordInit()–>initializeDev()–>Initialize()–>initDefaultParameters()–>tryFmtSize ()–>ioctl()
RecordInit()–> start()–>setParameters()–>tryFmtSize ()–>ioctl()
调用Ioctl方法,并使用VIDIOC_TRY_FMT命令标识:
在这里插入图片描述
在这里插入图片描述

这个ioctl用于协商驱动程序和应用程序之间交换的图像数据格式,所谓“try”,即大部分情况下等同于VIDIOC_S_FMT,但是它不会更改驱动程序状态。它可以在任何时候调用,永远不会返回EBUSY。驱动提供此功能是为了协商参数,了解硬件限制,而不禁用I / O或可能耗费时间的硬件准备。
本测试程序中,调用VIDIOC_TRY_FMT,根据捕捉视频的类型、格式和大小,判断模式、格式等是否被驱动支持,此过程不会改变任何硬件设置。对 于 VIN 设 备 , type 为 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE 。 使 用 struct v4l2_pix_format_mplane 进行参数传递。应用程序输入该结构体里面的 width、 height、 pixelformat、 field 等参数,驱动返回最接近的 width、 height;若 pixelformat、 field 不支持,则默认选择驱动支持的第一种格式。
但vin驱动实现了该方法,最终调用了vidioc_try_fmt_vid_cap_mplane()。
在这里插入图片描述
这个函数首先调用vin_find_format()从vin_formats[]数组中试图找到与应用程序提供的参数匹配的图像数据格式,例如应用程序默认要求的是V4L2_PIX_FMT_NV21格式,vin_formats[]中对应的格式是:
在这里插入图片描述
注意:这里的V4L2_PIX_FMT_NV21是表示像素格式
找到后调用vin_pipeline_try_format()遍历pipe用到的子设备、调用它们驱动的pad–>set_fmt(如果有的话),例如csi驱动的sunxi_csi_subdev_set_fmt()、ISP驱动的sunxi_isp_subdev_set_fmt()、nvp6134驱动的sensor_set_fmt()等。总的代码很多,但还是没有发现与nvp6134寄存器相关的操作。这里会分析sensor_set_fmt()。
注:每一个子设备都预备有一个输出格式的列表,设置好这些格式,使得sink pad(输入)与上一个source pad(输出)保持一致。nvp6134中sensor_formats[]、csi中sunxi_csi_formats[]、isp中sunxi_isp_formats[],不过scaler没有。
打印信息如下:

[   67.125670] [VIN_LOG_FMT]found nvp6134 in this pipeline
[   67.132201] [VIN_LOG_FMT]nvp6134 sensor_set_fmt 640*480 0x2008 0xe4ae9d40
[   67.150357] [VIN_LOG_FMT]found sunxi_csi.2 in this pipeline
[   67.167420] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[   67.185074] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[   67.202736] [VIN_LOG_FMT]found sunxi_isp.0 in this pipeline
[   67.219805] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[   67.237475] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[   67.255113] [VIN_LOG_FMT]found sunxi_scaler.0 in this pipeline
[   67.272487] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1 
[   67.290432] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   67.308660] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
(V) V4L2Camera::TryFmtSize: w: 640, h: 480
[   67.326772] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   67.337912] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   67.345176] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 640*480 2012 1
[   67.352129] [VIN_LOG_SCALER]para: xr = 384, yr = 384, w = 640, h = 480
[   67.359346] [VIN_LOG_SCALER]crop: left = 160, top = 0, w = 960, h = 720
[   67.366664] [VIN_LOG_SCALER]para: xr = 384, yr = 384, w = 640, h = 480
[   67.373901] [VIN_LOG_SCALER]crop: left = 160, top = 0, w = 960, h = 720
[   67.381238] [VIN_LOG_FMT]found nvp6134 in this pipeline
[   67.387024] [VIN_LOG_FMT]nvp6134 sensor_set_fmt 640*480 0x2012 0x1
[   67.393913] [VIN_LOG_FMT]found sunxi_csi.2 in this pipeline
[   67.400103] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[   67.406829] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[   67.413591] [VIN_LOG_FMT]found sunxi_isp.0 in this pipeline
[   67.419781] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[   67.426514] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[   67.433268] [VIN_LOG_FMT]found sunxi_scaler.0 in this pipeline
[   67.439748] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[   67.446781] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   67.454119] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   67.461357] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   67.468688] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   67.475924] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 640*480 2012 1
[   67.482885] [VIN_LOG_SCALER]para: xr = 384, yr = 384, w = 640, h = 480
[   67.490112] [VIN_LOG_SCALER]crop: left = 160, top = 0, w = 960, h = 720
[   67.497445] [VIN_LOG_SCALER]para: xr = 384, yr = 384, w = 640, h = 480
[   67.504663] [VIN_LOG_SCALER]crop: left = 160, top = 0, w = 960, h = 720
01-01 00:01:53.250  (V) V4L2Camera::TryFmtSize: w: 1280, h: 720
[   68.013790] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[   68.027617] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   68.034983] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   68.042223] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   68.049560] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   68.056777] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[   68.063829] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   68.071161] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   68.078406] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   68.085724] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   68.092968] [VIN_LOG_FMT]found nvp6134 in this pipeline
[   68.098767] [VIN_LOG_FMT]nvp6134 sensor_set_fmt 1280*720 0x2012 0x1
[   68.105695] [VIN_LOG_FMT]found sunxi_csi.2 in this pipeline
[   68.111882] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[   68.118643] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
[   68.125391] [VIN_LOG_FMT]found sunxi_isp.0 in this pipeline
[   68.131581] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[   68.138338] [VIN_LOG_FMT]sunxi_isp_subdev_set_fmt 1280*720 2012 1
[   68.145081] [VIN_LOG_FMT]found sunxi_scaler.0 in this pipeline
[   68.151556] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[   
68.158604] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w
 = 1280, h = 720
[   68.165937] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   68.173161] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   68.180478] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   68.187714] [VIN_LOG_FMT]sunxi_scaler_subdev_set_fmt 1280*720 2012 1
[   68.194747] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   68.202082] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   68.209317] [VIN_LOG_SCALER]para: xr = 256, yr = 256, w = 1280, h = 720
[   68.216647] [VIN_LOG_SCALER]crop: left = 0, top = 0, w = 1280, h = 720
[   68.224398] [VIN_LOG_FMT]found nvp6134 in this pipeline280, h: 720
[   68.235764] [VIN_LOG_FMT]nvp6134 sensor_set_fmt 1280*720 0x2008 0xc008f2ec
[   68.254279] [VIN_LOG_FMT]found sunxi_csi.2 in this pipeline
[   68.271354] [VIN_LOG_FMT]sunxi_csi_subdev_set_fmt 1280*720 2012 1
01-01 00:01:53.622  (V) V4L2Camera::TryFmtSize: w: 1280, h: 720

 
 

注意代码中有多个地方调用vin_find_format(),添加一些打印:
在这里插入图片描述
首次进入,调用vidioc_try_fmt_vid_cap_mplane() ,因为AP设置pixelformat=V4L2_PIX_FMT_NV21格式,所以打印:
在这里插入图片描述

[   14.087049] anthony vidioc_try_fmt_vid_cap_mplane() >>>enter vin_find_format()
[   14.105677] anthony vin_find_format() line=467 i=13

返回:
在这里插入图片描述
然后调用vin_pipeline_try_format(),该函数会多次调用vin_find_format()

[   14.121812] anthony vin_pipeline_try_format() >>>enter vin_find_format()
[   14.140405] anthony vin_find_format() line=475 i=29 id=0
[   14.157050] found nvp6134 in this pipeline sfmt.format.code=0x2008

在这里插入图片描述
这是根据各种条件刷选出来的默认格式:MEDIA_BUS_FMT_YVYU8_2X8=0x2008(这是总线数据格式),它符合V4L2_PIX_FMT_NV21格式(这是像素格式)等要求。然后调用nvp6134的set_fmt来尝试这种格式是否符合。其中调用sensor_set_fmt()–>sensor_try_format(),它首先从Sensor驱动中获得Sensor支持的格式:
在这里插入图片描述
上面V4L2_MBUS_FMT_YVYU8_1X16=0x2012,它在代码里面也表示为MEDIA_BUS_FMT_YVYU8_1X16。然后调用sensor_fill_mbus_fmt()重新设置格式为V4L2_MBUS_FMT_YVYU8_1X16
在这里插入图片描述

[   14.174847] anthony sensor_try_format() fmt->format.code=0x2008, code=0x2012

在这里插入图片描述
然后再次vin_find_format(),因mbus_code=MEDIA_BUS_FMT_YVYU8_1X16,所以获得:
在这里插入图片描述
于是通过VIDIOC_TRY_FMT命令标识、返回给AP的fmt最终是V4L2_MBUS_FMT_YVYU8_1X16。

修改vin_pipeline_try_format()打印代码:

found nvp6134 in this pipeline sfmt.format.code=0x2012
found sunxi_csi.2 in this pipeline sfmt.format.code=0x2012
found sunxi_isp.0 in this pipeline sfmt.format.code=0x2012
found sunxi_scaler.0 in this pipeline sfmt.format.code=0x2012

说明每个pipeline传递的是0x2012(MEDIA_BUS_FMT_YVYU8_1X16)格式的数据。

sensor_set_fmt()分析:
在这里插入图片描述
sensor_set_fmt() sensor fmt=0x2012
即nvp6134在代码里面最终设置为MEDIA_BUS_FMT_YVYU8_1X16格式作为原始数据格式,在https://linuxtv.org/downloads/v4l-dvb-apis/uapi/v4l/subdev-formats.html?highlight=v4l2_mbus_pixelcode#v4l2-mbus-pixelcode中解释到,这种格式表明一个像素点是通过一个16bit来表示,其中bit[715]是y0y7,bit[07]是v0v7或u0~u7:
在这里插入图片描述
它的格式实际是一种YUV420。
但是在nvp6324中:

sensor_set_fmt() sensor fmt=0x2008

即MEDIA_BUS_FMT_YUYV8_2X8,格式如下:
在这里插入图片描述

6、获取/设置图像数据格式

RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2SetVideoParams()–>ioctl()
调用Ioctl方法,并使用VIDIOC_G_FMT、VIDIOC_S_FMT命令标识:
在这里插入图片描述
对于VIDIOC_S_FMT命令标识:
用于设置捕捉视频的类型、格式和大小,设置之前会调用 VIDIOC_TRY_FMT。
对 于 VIN 设 备 , type 为 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE 。 使 用 struct v4l2_pix_format_mplane 进行参数传递。应用程序输入 width、height、pixelformat、field 等,驱动返回最接近的 width、height;若 pixelformat、field 不支持,则默认选择驱动支持的第一种格式。
应用程序应该以驱动返回的 width、 height、 pixelformat、 field 等作为后续使用传递的参数。
对于 OSD 设备, type 为 V4L2_BUF_TYPE_VIDEO_OVERLAY。使用 struct v4l2_window 进行参数传递。应用程序输入水印的个数、窗口位置和大小、 bitmap 地址、 bitmap 格式以及 global_alpha 等。驱动保存这些参数,并在 VIDIOC_OVERLAY 命令传递使能命令时生效。
驱动方面最终调用vin_video.c vidioc_s_fmt_vid_cap_mplane(),先try fmt测试(过程和第6小节类似),然后把要设置的格式/分辨率存起来。过程中会调用nvp6134.c sensor_ioctl(GET_CURRENT_WIN_CFG),设置info->current_wins。
info->current_wins结构体的值如下:

			[nvp6134]width=1280		 heitht=720
			[nvp6134]hoffset=0		 voffset=0
			[nvp6134]hts=0	 vts=0
			[nvp6134]pclk=0	 mipi_bps=0
			[nvp6134]fps_fixed=0 	 if_mode=0
			[nvp6134]wdr_mode=0		 bin_factor=0
			[nvp6134]intg_min=0		 intg_max=0
			[nvp6134]gain_min=0		 gain_max=0
			[nvp6134]width_input=1280		 gain_max=720
			[nvp6134]vipp_hoff=0 	 vipp_voff=0

对于VIDIOC_G_FMT命令标识:
获取捕捉视频的 width、 height、 pixelformat、 field、 bytesperline、 sizeimage 等参数。
应用程序一般在调用VIDIOC_S_FMT命令标识之后,调用VIDIOC_G_FMT命令标识再获取一次视频参数,并以驱动返回的 width、 height、 pixelformat、 field 等为后续使用。
驱动方面最终调用vin_video.c vidioc_g_fmt_vid_cap_mplane()。

AP调用VIDIOC_S_FMT命令时,参数ormat.fmt.pix_mp.pixelformat=V4L2_PIX_FMT_NV21,保存到驱动里面:
在这里插入图片描述

7、设置流参数

RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2setCaptureParams()–>ioctl()
调用Ioctl方法,并使用VIDIOC_S_PARM命令标识:
在这里插入图片描述
第三个参数是指向struct v4l2_streamparm的指针,该结构包含一个保存输入设备或保存输出设备的联和结构体。在本测试程序中CSI作为输入设备,所以只初始化输入设备(v4l2_streamparm.param.capture)、parms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,而通 过 设 定 parms.capture.capturemode ( V4L2_MODE_VIDEO 或V4L2_MODE_IMAGE),实现视频或图片的采集,通过设定 parms->capture.timeperframe,可以设置帧率。
这个VIDIOC_S_PARM命令标识的调用会先调用驱动vin_video.c vidioc_s_parm(),然后逐步调用sensor、CSI、ISP驱动的video.s_parm所指向的函数,例如nvp6134驱动里的sensor_s_parm()
相对于VIDIOC_S_FMT,这里设置的parm——参数主要是针对功能(有capture和output两种)来说的,例如这里使用capture功能,它由以下参数:
在这里插入图片描述
应用程序设置这些参数,通过VIDIOC_S_PARM将参数保存到驱动中。

8、分配设备缓冲区

RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2ReqBufs()–>ioctl()
调用Ioctl方法,并使用VIDIOC_REQBUFS命令标识:
在这里插入图片描述
VIDIOC_REQBUFS命令标识的ioctl调用用于启动内存映射、用户指针或基于I / O流的DMABUF。内存映射缓冲区位于设备内存中,必须先使用调用此ioctl让驱动程序分配内存,然后才能映射到应用程序的地址空间。用户缓冲区则由应用程序本身分配,调用此ioctl可将驱动程序切换到用户指针I / O模式并设置一些内部结构。类似地,DMABUF缓冲区由应用程序通过设备驱动程序分配,此ioctl调用时驱动程序仅配置为DMABUF I / O模式,而不执行任何直接分配。
应用程序为了让驱动分配设备缓冲区,需先初始化struct v4l2_requestbuffers结构的所有字段(要memset为0)。它们将type字段设置为相应的流或缓冲区类型(例如本测试程序“rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE”,即“单平面视频捕获流的缓冲区”,因为测试程序用到设备的capture(捕获、截图)功能),将count字段设置为所需的缓冲区数,必须将memory设置为所请求的I / O方法(本测试程序设置为V4L2_MEMORY_MMAP,即内存映射)。
当使用指向struct v4l2_requestbuffers的指针作为参数来这个ioctl时,驱动程序将尝试分配所请求数量的缓冲区,并实际分配的缓冲区数量存储在count字段。当驱动程序耗尽空闲内存时,它可能小于请求的数量,甚至为零。当驱动程序需要更多缓冲区才能正常运行时,也可以使用更大的数字。例如,视频输出需要至少两个缓冲区,一个显示,一个由应用程序填充。(所以本测试程序在函数最后返回实际分配的buf数量)
应用程序可以再次调用ioctl VIDIOC_REQBUFS来更改缓冲区的数量,但是如果缓冲区还在被映射时会失败。在异常中止或完成DMA操作、或调用VIDIOC_STREAMOFF后,count将为零并释放所有缓冲区,。
本测试程序中,在v4l2_requestbuffers 结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立 FIFO,来提高视频采集的效率。 这些 buffer 通过内核申请,申请后需要通过 mmap 方法,映射到 User 空间。
Count:定义需要申请的 video buffer 数量
Type:对于 VIN 设备,为 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
Memory : 目 前 支 持 V4L2_MEMORY_MMAP 、 V4L2_MEMORY_USERPTR 、V4L2_MEMORY_DMABUF 方式,当前使用V4L2_MEMORY_MMAP。
vin驱动中,这个Ioctl最终调用videobuf2-core.c vb2_ioctl_reqbufs()。驱动会根据应用程序中 VIDIOC_S_FMT 参数设置的格式计算供需要buffer的大小,并返回buf的实际数量—— count ,这应该和cap->frame.fmt.memplanes有关,即vin_formats[]中对应图像格式的memplanes。

 [   27.653947] vb2_ioctl_reqbufs()>>>
 [   27.663842] [VIN_LOG_VIDEO]queue_setup, buf count = 6, nplanes = 1, size = 1384448
 [   27.684104] vb2_ioctl_reqbufs()<<<

9、查询缓冲区的状态

RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2QueryBuf()–>ioctl()
调用Ioctl方法,并使用VIDIOC_QUERYBUF命令标识:
在这里插入图片描述
此ioctl是流式I / O方法的一部分。在使用ioctl VIDIOC_REQBUFS ioctl分配缓冲区后,它可以用于查询缓冲区的状态。
应用程序将struct v4l2_buffer的type字段设置为与之前struct v4l2_format和struct v4l2_requestbuffers使用的缓冲区类型,以及index字段。index的有效范围从0到使用ioctl VIDIOC_REQBUFS分配的缓冲区数减1(即v4l2_requestbuffers.count-1,本测试程序使用for循环,index被设为0到mBufferCnt-1)。使用多平面API时,m.planes字段必须包含指向struct v4l2_plane数组的用户空间指针,并且length字段必须设置为其中的元素数量。在使用指向此结构的指针作为参数、调用ioctl VIDIOC_QUERYBUF之后,驱动程序返回错误代码或填充结构的其余部分。
flags字段将设置V4L2_BUF_FLAG_MAPPED,V4L2_BUF_FLAG_PREPARED,V4L2_BUF_FLAG_QUEUED和V4L2_BUF_FLAG_DONE标志。memory字段将设置为当前I / O方法。对于单平面API,m.offset指缓冲区起始地址到设备内存起始地址的偏移量,length字段是缓冲区大小。对于多平面API,则使用m.planes数组元素中的字段m.mem_offset和length来表示一块缓冲区。
在本测试程序中,运用多个缓冲区来存储数据,且上一步调用VIDIOC_REQBUFS时已经返回了驱动分配了count个缓冲区,所以这里通过for循环从驱动中查询到各个缓冲区的地址和大小。在VIDIOC_REQBUFS调用中使用V4L2_MEMORY_MMAP的I/O方式,所以在获得缓冲区地址和大小后,程序再通过mmap()方法将缓冲区映射到应用程序中。

驱动方面最终调用videobuf2-core.c vb2_querybuf()。

10、应用程序与驱动程序交换缓冲区

RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2QueryBuf()–>ioctl()
另一处:V4L2CameraDevice::releasePreviewFrame()–>ioctl()
接上一步调用VIDIOC_QUERYBUF并使用mmap()映射缓冲区后,程序再次调用Ioctl方法,并使用VIDIOC_QBUF命令标识:
在这里插入图片描述
应用程序调用VIDIOC_QBUF ioctl将队列中的空(捕获)或填充(输出)缓冲区传入给驱动程序,意为“入队”操作。
为了要将缓冲区入队,应用程序初始化struct v4l2_buffer,类似调用VIDIOC_QUERYBUF时的初始化,或者直接调用VIDIOC_QUERYBUF返回的struct v4l2_buffer。当缓冲区用于输出(类型为V4L2_BUF_TYPE_VIDEO_OUTPUT,V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE或V4L2_BUF_TYPE_VBI_OUTPUT)时,应用程序还必须初始化bytesused,field和timestamp字段。
应用程序要将内存映射缓冲区排入队列,将memory字段设置为V4L2_MEMORY_MMAP。当使用指向此结构的指针调用VIDIOC_QBUF时,驱动程序将设置V4L2_BUF_FLAG_MAPPED和V4L2_BUF_FLAG_QUEUED标志并清除flags字段中的V4L2_BUF_FLAG_DONE标志,否则它将返回EINVAL错误代码。

在本测试程序中,直接将VIDIOC_QUERYBUF返回的struct v4l2_buffer作为VIDIOC_QBUF参数传递给驱动。驱动方面最终调用videobuf2-core.c vb2_qbuf()。

11、启动流式I / O传输

RecordInit()–> start()–>startPreview ()–>startDevice ()–>v4l2StartStreaming()–>ioctl()
调用Ioctl方法,并使用VIDIOC_STREAMON命令标识:
在这里插入图片描述
在流(内存映射,用户指针或DMABUF)I / O控制期间,调用VIDIOC_STREAMON或VIDIOC_STREAMOFF ioctl启动或停止捕获(或输出)过程。参数是一个指向整数的指针,即所需的缓冲区或流类型。这与struct v4l2_requestbuffers类型相同。
在VIDIOC_STREAMON之前,捕获硬件是禁用的,并且没有填充输入缓冲区(如果传入队列中有任何空缓冲区)。而输出硬件是禁用,不会产生视频信号。当最新的一个输出缓冲区传入队列中时,表示ioctl调用成功。
在为捕获和输出流类型调用VIDIOC_STREAMON之前,memory-to-memory设备不会启动。
如果VIDIOC_STREAMON失败,则任何在排队的缓冲区将保持排队。
如果缓冲区已使用ioctl VIDIOC_QBUF进行排队,则调用VIDIOC_DQBUF和VIDIOC_STREAMOFF而不调用VIDIOC_STREAMON,那么这些排队缓冲区也将从传入队列中删除,并且所有队列缓冲区都返回到调用ioctl VIDIOC_REQBUFS之后的状态,并且可以重新启动。
如果在流式传输正在进行时调用VIDIOC_STREAMON,或者在流式传输已停止时调用VIDIOC_STREAMOFF,则返回0。
本测试程序中, buffer type 为 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE。运行此 IOCTL,将 buffer队列中所有 buffer 入队,并开启 CSIC DMA 硬件中断,每次中断便表示完成一帧 buffer 数据的填入。

驱动最终调用vin_video.c videoc_streamon(),这是一个重要的函数,其内部会调用sensor_s_stream()–>sensor_reg_init(),从而初始化sensor的寄存器。

打印:

[VIN]vidioc_streamon()>>>
[VIN]vidioc_streamon()<<<
[VIN]__vin_s_stream_handle
[VIN_LOG_MD]__vin_pipeline_s_stream() >>> on_idx=1 
[VIN_LOG_MD]__vin_pipeline_s_stream() total_rx_ch=4
[VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 isp_sel=0 csi_sel=2 i=0
[VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 isp_sel=0 csi_sel=2 i=1
[VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 isp_sel=0 csi_sel=2 i=2
[VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 isp_sel=0 csi_sel=2 i=3
[VIN_LOG_MD]__vin_pipeline_s_stream() vind->id=0 vipp_sel=0 isp_tx_ch=0
[VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:nvp6134
[VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:dmafd=48
[VIN_LOG_MD]__vin_subdev_set_stream()>>> on=1 stream_count=0
[nvp6134]sensor_s_stream on = 1, 1280*720 2012
[nvp6134]sensor_reg_init +
[nvp6134]sensor_reg_init -
[VIN_LOG_MD]__vin_subdev_set_stream() <<<
[VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:sunxi_isp.0
[VIN_LOG_MD]__vin_subdev_set_stream()>>> on=1 stream_count=0
[VIN_LOG_FMT]sunxi_isp_subdev_s_stream isp->use_isp=0
[VIN_LOG_MD]__vin_subdev_set_stream() <<<
[VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:sunxi_scaler.0
[VIN_LOG_MD]__vin_subdev_set_stream()>>> on=1 stream_count=0
[VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() cscaler->id=0
[VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() crop.hor=0 crop.ver=0
[VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() crop.width=1280 crop.height=720
[VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() out_fmt=0 is_osd_en=0
[VIN_LOG_SCALER]sunxi_scaler_subdev_s_stream() scaler_cfg.sc_x_ratio=256 sc_y_ratio=256 sc_w_shift=1
[VIN_LOG_FMT]vipp0 stream on, 1280*720 hoff: 0 voff: 0 xr: 256 yr: 256
[VIN_LOG_MD]__vin_subdev_set_stream() <<<
[VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:vin_cap.0
[VIN_LOG_MD]__vin_subdev_set_stream() >>>on=1 stream_count=0
[VIN_LOG_FMT]csic_dma0 stream on, 1280*720 hoff: 0 voff: 0
[VIN_LOG_MD]__vin_subdev_set_stream() <<<
[VIN_LOG_MD] __vin_subdev_set_stream()>>>sd name:sunxi_csi.2
[VIN_LOG_MD]__vin_subdev_set_stream() >>>on=1 stream_count=0
[VIN_LOG_FMT]sunxi_csi_subdev_s_stream()>>> csi->id=2
[VIN_LOG_FMT]__csi_set_fmt_hw() csi->csi_fmt->seq=1 data_width=16
[VIN_LOG_FMT]__csi_set_fmt_hw() mf->field=1
[VIN_LOG_FMT]__csi_set_fmt_hw() csi->bus_info.bus_if=1
[VIN_LOG_FMT]__csi_set_fmt_hw() csi->capture_mode=2
[VIN_LOG_FMT]__csi_set_fmt_hw() out_size.hor_len=1280 out_size.ver_len=720
[VIN_LOG_FMT]__csi_set_fmt_hw() out_size.hor_start=0 out_size.ver_start=0
[VIN_LOG_FMT]__csi_set_fmt_hw() csi->bus_info.ch_total_num=4
[VIN_LOG_FMT]parser2 stream on, 1280*720 hoff: 0 voff: 0 code: 2012 field: 1
[VIN_LOG_MD]__vin_subdev_set_stream() <<<
[VIN_LOG_MD]__vin_pipeline_s_stream() ptn_cfg.ptn_en=0 ptn_type=0 ptn_on_cnt=0
[VIN_LOG_MD]__vin_pipeline_s_stream() <<<
[VIN_LOG_VIDEO]__vin_s_stream_handle done, id = 0!
[VIN_LOG_VIDEO]video0 first frame done! 

   
   

vidioc_streamon()函数主要代码如下:

static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
	
	ret = vb2_ioctl_streamon(file, priv, i);//没有特别重要的内容,略过
	
		buf = list_entry(cap->vidq_active.next, struct vin_buffer, list);
		vin_set_addr(vinc, &buf->vb, &vinc->vid_cap.frame, &vinc->vid_cap.frame.paddr);

	schedule_work(&vinc->vid_cap.s_stream_task);
}

函数最后要求调度运行s_stream_task 任务。在vin_init_video()有:
在这里插入图片描述
于是过一会CPU将运行__vin_s_stream_handle()函数:

void __vin_s_stream_handle(struct work_struct *work)
{
	int ret = 0;
	struct vin_vid_cap *cap =
			container_of(work, struct vin_vid_cap, s_stream_task);
	ret = vin_pipeline_call(cap->vinc, set_stream, &cap->pipe, cap->vinc->stream_idx);

	/*set saved exp and gain for reopen*/
	if (cap->vinc->exp_gain.exp_val && cap->vinc->exp_gain.gain_val) {
		v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, ioctl,
			VIDIOC_VIN_SENSOR_EXP_GAIN, &cap->vinc->exp_gain);
	}
}

#define vin_pipeline_call(f, op, p, args...)				\
	(!(f) ? -ENODEV : (((f)->pipeline_ops && (f)->pipeline_ops->op) ? \
			    (f)->pipeline_ops->op((p), ##args) : -ENOIOCTLCMD))

      
      

vin_pipeline_call()那一句语句,相当于:

cap->vinc->pipelline_ops->set_stream((&cap->pipe), cap->vinc->stream_idx);
即__vin_pipeline_s_stream((&cap->pipe), cap->vinc->stream_idx)

__vin_pipeline_s_stream()主要完成以下任务:

static int __vin_pipeline_s_stream(struct vin_pipeline *p, int on_idx)
{
	//1、分别获得vin_md和vin_vid_cap结构体变量
	//2、根据DST,配置ISP输入通道和VIPP输入通道:
		//选择CH0的Input Parser2
		//选择CH1的Input Parser2
		//选择CH2的Input Parser2
		//选择CH3的Input Parser2
		//选择ISP0 CH0
	//3、for循环调用__vin_subdev_set_stream(),调用当前pipe所属的子设备的v4l2_subdev_video_ops.s_stream所指向的函数。
}

遍历cap->pipe所包含的Link的subdev,然后调用他们里面的set_stream指定的函数,例如vinc0的pipeline是sensor0(NCSI0)–>CSI2–>ISP0–>VIPP0(包括stat0–>scaler0)–>vin_cap.0 ,即它的subdev有sensor0(即nvp6134)、csi2、ISP0、VIPP0和vin_cap.0
对应的s_stream 是:
subdev为sensor0(即nvp6134):sensor_s_stream()
subdev为isp0:sunxi_isp_subdev_s_stream()
subdev为scaler:sunxi_scaler_subdev_s_stream()
subdev为vin_cap.0:vin_subdev_s_stream ()
subdev为csi:sunxi_csi_subdev_s_stream()
这些s_stream函数调用的顺序是根据seq[]来决定的:
在这里插入图片描述
但下面不按照调用顺序来分析这几个函数,而按照pipe的顺序来分析,因为前面模块的输出格式将是后面模块的输入,所以按照pipe来分析:
1)sensor_s_stream()
在这里插入图片描述
sensor_s_stream()会调用sensor_reg_init()进行nvp6134寄存器初始化,也只有enable时才进行寄存器初始化。sensor_reg_init()的分析参考《nvp6134驱动》。这里需要知道的是,nvp6134被设置为输出1280*720分辨率、使用BT1120总线格式输出,YUV422格式数据:
在这里插入图片描述

2)sunxi_csi_subdev_s_stream()
在这里插入图片描述

如上图,sunxi_csi_subdev_s_stream()中最重要的是__csi_set_fmt_hw()。
这里特别说一下,虽然在vin_pipeline_set_mbus_config–>sensor_g_mbus_config中有设置了type:
在这里插入图片描述
即设置nvp6134支持的总线格式,即V4L2_MBUS_BT656,支持4个通道的数据传输。而vin_pipeline_set_mbus_config–>sunxi_csi_s_mbus_config中也有“csi2 cfg->type== V4L2_MBUS_BT656“”,设置csi输入总线格式是V4L2_MBUS_BT656格式的,且说明四个通道的数据时如何放到一个buffer的,如下:
在这里插入图片描述
不过这不是T7的做法(H5就是这样做的,然后把计算每个通道在buffer中的位置,赋值给DMA),T7只在__csi_set_fmt_hw()中简单设置NCSI和Parse。因为csi_fmt->data_width = 16,所以NCSI被设置为BT1120的(NVP实际输出也是BT1120的,所以上面代码中设置为V4L2_MBUS_BT656反而会误导):
在这里插入图片描述
上图中csi->bus_info.ch_total_num = 4,所以csi->ncsi_if.intf = PRS_IF_BT1120_4CH,表示一个BT1120总线有4个通道的数据。然后配置NCSI:
在这里插入图片描述
最后设置Parser:

for (i = 0; i < csi->bus_info.ch_total_num; i++) {
		//设置Parser模块的输入和输出格式
		csic_prs_input_fmt_cfg(csi->id, i, csi->csi_fmt->infmt);
		csic_prs_output_size_cfg(csi->id, i, &csi->out_size);
	}

打印信息如下:

[   23.982768] [VIN_LOG_FMT]__csi_set_fmt_hw() csi->csi_fmt->infmt= FMT_YUV422
[   23.989641] [VIN_LOG_FMT]__csi_set_fmt_hw() csi->out_size.hor_start=0 hor_len=1280 ver_start=0 ver_len=720
[   24.007229] [VIN_LOG_FMT]__csi_set_fmt_hw() csi->out_size.hor_start=0 hor_len=1280 ver_start=0 ver_len=720
[   24.024833] [VIN_LOG_FMT]__csi_set_fmt_hw() csi->out_size.hor_start=0 hor_len=1280 ver_start=0 ver_len=720
[   24.042433] [VIN_LOG_FMT]__csi_set_fmt_hw() csi->out_size.hor_start=0 hor_len=1280 ver_start=0 ver_len=720

为什么这里是FMT_YUV422格式?
在这里插入图片描述
__csi_find_format()根据mf->code来查找,而code最初是nvp6134的sensor_formats[].mbus_code的值,刚好对应这里sunxi_csi_formats[]的:
在这里插入图片描述
所以格式是FMT_YUV422。
根据代码总结得知:CSI2模块最终输出1280*720分辨率,FMT_YUV422格式的数据

3)sunxi_isp_subdev_s_stream()
在这里插入图片描述

根据打印信息,sunxi_isp_subdev_s_stream()在73行直接返回,难道不需要使用isp?这个use_isp在哪里被设置的?
在DST中设置了不使用ISP,所以csi2与VIPP0相当于直接连通:
在这里插入图片描述
4)sunxi_scaler_subdev_s_stream()

static int sunxi_scaler_subdev_s_stream(struct v4l2_subdev *sd, int enable)
{
	if (enable) {
		crop.hor = scaler->crop.active.left;
		crop.ver = scaler->crop.active.top;
		crop.width = scaler->crop.active.width;
		crop.height = scaler->crop.active.height;

		vipp_set_crop(scaler->id, &crop);
		scaler_size.sc_width = scaler->para.width;
		scaler_size.sc_height = scaler->para.height;
		vipp_scaler_output_size(scaler->id, &scaler_size);

		
		out_fmt = YUV420;
	
		if (scaler->is_osd_en)
			scaler_cfg.sc_out_fmt = YUV422;
		else
			scaler_cfg.sc_out_fmt = out_fmt;
		scaler_cfg.sc_x_ratio = scaler->para.xratio;
		scaler_cfg.sc_y_ratio = scaler->para.yratio;
		scaler_cfg.sc_w_shift = __scaler_w_shift(scaler->para.xratio, scaler->para.yratio);

		vipp_scaler_cfg(scaler->id, &scaler_cfg);
		vipp_output_fmt_cfg(scaler->id, out_fmt);
		vipp_scaler_en(scaler->id, 1);
		vipp_set_para_ready(scaler->id, HAS_READY);
		vipp_set_osd_ov_update(scaler->id, HAS_UPDATED);
		vipp_set_osd_cv_update(scaler->id, HAS_UPDATED);
		vipp_top_clk_en(scaler->id, enable);
		vipp_enable(scaler->id);
	}
}

            
            

格外留意一下,scaler输出的格式是YUV420.
Scaler的输入参数是和ISP(或CSI保持一致的),而输出是应用设置的,应用设置了V4L2_PIX_FMT_NV21格式,所以最终输出YVU420(注意V在U前面)

5)vin_subdev_s_stream()
这个方法主要是设置DMA的属性,并使能它,在代码里这个DMA命名为“csic_dma0”,意值打开Sensor的第一路摄像头数据。

[VIN_LOG_FMT]csic_dma0 stream on, 1280*720 hoff: 0 voff: 0
[VIN_LOG_FMT]size.hor_start=0 hor_len=1280 ver_start=0 ver_len=720
[VIN_LOG_FMT]buf_len=1280
[VIN_LOG_FMT]flip_size.ver_len=720 hor_len=2560
[VIN_LOG_FMT]flip.hflip_en=0 vflip_en=0
[VIN_LOG_FMT](cap->frame.o_height / 16 * 12)=540
[VIN_LOG_FMT]vinc->vipp_sel=0

在这里插入图片描述
不过这里没有设置DMA buffer的地址,那在哪里设置呢?为什么csic_dma0保存sensor第一路数据?
vidioc_streamon–> vin_set_addr():
在这里插入图片描述

[VIN]csic_dma vin_set_addr 227 vipp_sel=0 paddr->y=0x586e6000
[VIN]csic_dma vin_set_addr 228 vipp_sel=0 paddr->cb=0x587c7000
[VIN]csic_dma vin_set_addr 229 vipp_sel=0 paddr->cr=0x0

因应用最终申请到6个buffer,并存入一个list中。在启动流传输数据后,不断的调用vin_set_addr()重新设置buffer地址。其余5个buffer地址如下:

[   54.095929] [VIN_LOG_VIDEO]csic_dma vin_isr 1012 go to vin_set_addr
[   54.102849] [VIN]csic_dma vin_set_addr 227 vipp_sel=0 paddr->y=0x58838000
[   54.110338] [VIN]csic_dma vin_set_addr 228 vipp_sel=0 paddr->cb=0x58919000
[   54.117921] [VIN]csic_dma vin_set_addr 229 vipp_sel=0 paddr->cr=0x0
[   54.124890] [VIN_LOG_VIDEO]video0 first frame done!
[   54.130318] [VIN_LOG_VIDEO]csic_dma vin_isr 1012 go to vin_set_addr
[   54.137237] [VIN]csic_dma vin_set_addr 227 vipp_sel=0 paddr->y=0x5898a000
[   54.144727] [VIN]csic_dma vin_set_addr 228 vipp_sel=0 paddr->cb=0x58a6b000
[   54.152313] [VIN]csic_dma vin_set_addr 229 vipp_sel=0 paddr->cr=0x0
[   54.159306] [VIN_LOG_VIDEO]Nobody is waiting on video0 buffer1
[   54.174376] [VIN_LOG_VIDEO]csic_dma vin_isr 1012 go to vin_set_addr
[   54.185594] [VIN]csic_dma vin_set_addr 227 vipp_sel=0 paddr->y=0x58adc000
[   54.193084] [VIN]csic_dma vin_set_addr 228 vipp_sel=0 paddr->cb=0x58bbd000
[   54.200668] [VIN]csic_dma vin_set_addr 229 vipp_sel=0 paddr->cr=0x0
01-01 00:01:35.951  (E) onNextFrameAvailable------------DONE
[   54.208166] [VIN_LOG_VIDEO]csic_dma vin_isr 1012 go to vin_set_addr
[   54.219910] [VIN]csic_dma vin_set_addr 227 vipp_sel=0 paddr->y=0x58c2e000
[   54.227403] [VIN]csic_dma vin_set_addr 228 vipp_sel=0 paddr->cb=0x58d0f000
[   54.234986] [VIN]csic_dma vin_set_addr 229 vipp_sel=0 paddr->cr=0x0
[   54.247531] [VIN_LOG_VIDEO]csic_dma vin_isr 1012 go to vin_set_addr
[   54.254478] [VIN]csic_dma vin_set_addr 227 vipp_sel=0 paddr->y=0x58d80000
[   54.261969] [VIN]csic_dma vin_set_addr 228 vipp_sel=0 paddr->cb=0x58e61000
[   54.269552] [VIN]csic_dma vin_set_addr 229 vipp_sel=0 paddr->cr=0x0
[   54.286891] [VIN_LOG_VIDEO]csic_dma vin_isr 1012 go to vin_set_addr

                
                

12、获得驱动填充好的buf

V4L2CameraDevice构造器–>“mCaptureThread->startThread()”–>threadLoop ()–>captureThread ()–>getPreviewFrame ()–>ioctl()
调用Ioctl方法,并使用VIDIOC_DQBUF命令标识:
在这里插入图片描述
将 driver 已经填充好数据的 buffer 出列,供应用使用。
应用程序根据 index 来识别 buffer,此时 m.offset 表示 buffer 对应的物理地址。

vin_capture_subdev_registered–>vin_init_video–>__vin_s_stream_handle

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
全志R16平台 parrotv1.1(Android4.4.2) /* * Store information about the video data format. */ static struct sensor_format_struct { __u8 *desc; //__u32 pixelformat; enum v4l2_mbus_pixelcode mbus_code;//linux-3.0 struct regval_list *regs; int regs_size; int bpp; /* Bytes per pixel */ } sensor_formats[] = { //{ // .desc = "YUYV 4:2:2", // .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,//linux-3.0 // .regs = sensor_fmt_yuv422_yuyv, // .regs_size = ARRAY_SIZE(sensor_fmt_yuv422_yuyv), // .bpp = 2, //}, //{ // .desc = "YVYU 4:2:2", // .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,//linux-3.0 // .regs = sensor_fmt_yuv422_yvyu, // .regs_size = ARRAY_SIZE(sensor_fmt_yuv422_yvyu), // .bpp = 2, //}, { .desc = "UYVY 4:2:2", .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,//linux-3.0 .regs = sensor_fmt_yuv422_uyvy, .regs_size = ARRAY_SIZE(sensor_fmt_yuv422_uyvy), .bpp = 2, }, //{ // .desc = "VYUY 4:2:2", // .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,//linux-3.0 // .regs = sensor_fmt_yuv422_vyuy, // .regs_size = ARRAY_SIZE(sensor_fmt_yuv422_vyuy), // .bpp = 2, //}, //{ // .desc = "Raw RGB Bayer", // .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,//linux-3.0 // .regs = sensor_fmt_raw, // .regs_size = ARRAY_SIZE(sensor_fmt_raw), // .bpp = 1 //}, }; #define N_FMTS ARRAY_SIZE(sensor_formats) /* * Then there is the issue of window sizes. Try to capture the info here. */ static struct sensor_win_size sensor_win_sizes[] = { // /* UXGA */ // { // .width = UXGA_WIDTH, // .height = UXGA_HEIGHT, // .hoffset = 0, // .voffset = 0, // .regs = sensor_uxga_regs, // .regs_size = ARRAY_SIZE(sensor_uxga_regs), // .set_size = NULL, // }, //// /* 720p */ //// { //// .width = HD720_WIDTH, //// .height = HD720_HEIGHT, //// .hoffset = 0, //// .voffset = 0, //// .regs = Gc2015_sensor_hd720_regs, //// .regs_size = ARRAY_SIZE(Gc2015_sensor_hd720_regs), //// .set_size = NULL, //// }, // /* SVGA */ // { // .width = SVGA_WIDTH, // .height = SVGA_HEIGHT, // .hoffset = 0, // .voffset = 0, // .regs = sensor_svga_regs, // .regs_size = ARRAY_SIZE(sensor_svga_regs), // .set_size = NULL, // }, // /* VGA */ // { // .width = VGA_WIDTH, // .height = VGA_HEIGHT, // .hoffset = 0, // .voffset = 0, // .regs = sensor_vga_regs, // .regs_size = ARRAY_SIZE(sensor_vga_regs), // .set_size = NULL, // }, /* VGA */ { .width = VGA_WIDTH, .height = VGA_HEIGHT, .hoffset = 0, .voffset = 0, .regs = sensor_default_regs, .regs_size = ARRAY_SIZE(sensor_default_regs), .set_size = NULL, }, };

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值