V4L2 / VI DMA Buffer Creation Issue on Jetpack6

We recently moved our custom Orin AGX Industrial board from Jetpack 5.1.4 (R35.6.0) to Jetpack 6.2.0 (R36.4.3) and noticed a regression in our camera pipeline, whereby we can’t get the VI driver stack to capture frames into DMA compatible buffers for other downstream processing tasks.

We’ve been using the direct v4l2 ioctl path along with the Jetson Accelerated Gstreamer library plugins in our pipeline. We do not use the libargus / nvarguscamerasrc flow, nor the ISP within the Jetson SoC.

Specifically, we’ve been using the gst-nvv4l2camerasrc gstreamer plugin installed from apt, but this does not work anymore in JP6. The issues observed are the same for plugins built from source as well. We suspect this is because of changes in the Nvidia VI camera driver stack, which leads to issues with DMA buffer importing in the v4l2 driver stack, but we haven’t been able to find a stable patch fix that doesn’t affect other subsystems.

Although we do use a proprietary camera driver, and a custom device tree, these are functional and enumerate the camera the same way between Jetpack releases on the Orin. There are components of v4l2/gstreamer that work in both releases too. For context, using v4l2src (without DMA and zero-copy mechanisms) works fine and so do other Accelerated Gstreamer plugins like nvvidconv which use the VIC in the SoC.

# This pipeline works on both JP5 and JP6 with AGX Orin.
gst-launch-1.0 v4l2src device=/dev/video4 ! "video/x-raw, width=3840, height=2160, format=UYVY" ! nvvidconv flip-method=2 ! "video/x-raw(memory:NVMM), format=NV12" ! nvv4l2h264enc bitrate=28000000 insert-sps-pps=1 ! filesink location=output.h264
# A sample command to reproduce the issue in gstreamer with nvv4l2camerasrc on Jetpack6. 
gst-launch-1.0 nvv4l2camerasrc device=/dev/video3 ! 'video/x-raw(memory:NVMM), width=3840, height=2160, format=UYVY, framerate=30/1' ! fakesink
# Original nvv4l2camerasrc plugin output on Jetpack6:
 
$ GST_DEBUG=3 GST_PLUGIN_PATH=. gst-launch-1.0 nvv4l2camerasrc device=/dev/video3 ! 'video/x-raw(memory:NVMM), width=3840, height=2160, format=UYVY, framerate=30/1' ! fakesink

Setting pipeline to PAUSED ...
Pipeline is live and does not need PREROLL ...
0:00:02.445469071  5916 0xaaab10562000 FIXME                default gstutils.c:4025:gst_pad_create_stream_id_internal:<nvv4l2camerasrc0:src> Creating random stream-id, consider implementing a deterministic way of creating a stream-id
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
0:00:02.445708305  5916 0xaaab10562000 WARN         nvv4l2camerasrc gstnvv4l2camerasrc.cpp:684:gst_nv_v4l2_camera_fixate:<nvv4l2camerasrc0> Unable to fixate caps structure
New clock: GstSystemClock
0:00:02.611785893  5916 0xaaab10562000 ERROR        nvv4l2camerasrc gstnvv4l2camerasrc.cpp:525:gst_nvv4l2camera_buffer_pool_release_buffer:<nvv4l2camerabufferpool0> VIDIOC_QBUF failed :Invalid argument
0:00:02.612032901  5916 0xaaab10562000 ERROR        nvv4l2camerasrc gstnvv4l2camerasrc.cpp:525:gst_nvv4l2camera_buffer_pool_release_buffer:<nvv4l2camerabufferpool0> VIDIOC_QBUF failed :Invalid argument
0:00:02.612220974  5916 0xaaab10562000 ERROR        nvv4l2camerasrc gstnvv4l2camerasrc.cpp:525:gst_nvv4l2camera_buffer_pool_release_buffer:<nvv4l2camerabufferpool0> VIDIOC_QBUF failed :Invalid argument
0:00:02.612411990  5916 0xaaab10562000 ERROR        nvv4l2camerasrc gstnvv4l2camerasrc.cpp:525:gst_nvv4l2camera_buffer_pool_release_buffer:<nvv4l2camerabufferpool0> VIDIOC_QBUF failed :Invalid argument
0:00:02.612607325  5916 0xaaab10562000 ERROR        nvv4l2camerasrc gstnvv4l2camerasrc.cpp:525:gst_nvv4l2camera_buffer_pool_release_buffer:<nvv4l2camerabufferpool0> VIDIOC_QBUF failed :Invalid argument
0:00:02.612791142  5916 0xaaab10562000 ERROR        nvv4l2camerasrc gstnvv4l2camerasrc.cpp:525:gst_nvv4l2camera_buffer_pool_release_buffer:<nvv4l2camerabufferpool0> VIDIOC_QBUF failed :Invalid argument

I see a similar issue with the Jetson Multimedia API Samples 12_v4l2_camera_cuda, where setting
up the DMA buffers fails there as well, returning EFAULT. This sample and the gstreamer pipeline both
use the V4L2_MEMORY_DMABUF type for the underlying V4L2 buffer memory type, so the issues seem
related.

I’ve attached a simpler test program (v412_dma_test) that reproduces this EFAULT error on JP6 and uses the same sequence of ioctls.

v4l2_dma_issue_repro_jp6.zip (12.8 KB)

Here is a comparison of the output I see on JP5 and JP6 using my test script in the zip with the IMX490 attached.

# On Jetpack5: 
$ ./v412_dma_test
VIDIOC_G_FMT: Current format: 3840x2160, FourCC=UYVY, Size=16588800
Created NvBufSurface w,h=(3840x2160), pitch=7680, isContiguous=0
	NvBufSurfaceParams dma_fd=5, dataSize=16646144, dataPtr=0xaaab034125a0
	NvBufSurfaceMappedAddr ptr=0xffffebe3db60
Requesting 1 dma buffer(s)
VIDIOC_REQBUFS: Allocated 1 buffer(s), is V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, is V4L2_MEMORY_DMABUF = 1
VIDIOC_QUERYBUF returned buf with dma_fd=0! Setting to 5 again before queuing.
VIDIOC_QUERYBUF buf: index=0, length=16588800, dma_fd=5, flags=0x2000, bytesused=0
VIDIOC_QBUF succeeded.
VIDIOC_STREAMON succeeded
VIDIOC_DQBUF dequeued buffer 0
VIDIOC_STREAMOFF succeeded

# On Jetpack6: 
$ ./v412_dma_test

VIDIOC_G_FMT: Current format: 3840x2160, FourCC=UYVY, Size=16588800
Created NvBufSurface w,h=(3840x2160), pitch=7680, isContiguous=0
	NvBufSurfaceParams dma_fd=5, dataSize=16646144, dataPtr=0xaaaaff985230
	NvBufSurfaceMappedAddr ptr=0xffffd93de310
Requesting 1 dma buffer(s)
VIDIOC_REQBUFS: Allocated 1 buffer(s), is V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, is V4L2_MEMORY_DMABUF = 1
VIDIOC_QUERYBUF returned buf with dma_fd=0! Setting to 5 again before queuing.
VIDIOC_QUERYBUF buf: index=0, length=17113344, dma_fd=5, flags=0x2000, bytesused=0
VIDIOC_QBUF failed: Bad address (errno=14)

Note that the length of the v4l2_buffer that VIDIOC_QUERYBUF ioctl fills in after returning in JP5 is equal to the size of the image, whereas in JP6 it is larger than the allocated NvBufSurface – 17113344 vs 16588800 bytes. The NvBufSurface allocation size is consistent between releases. I believe this causes an internal size-check to fail when the DMA buffer is imported in the v4l2 driver stack.

I suspect the difference could be due to a change in the VI channel driver in Jetpack6. I’ve attached a snippet below, showing how it now expects extra space for embedded metadata in channel.c: tegra_channel_queue_setup.

static int
tegra_channel_queue_setup(struct vb2_queue *vq,
		     unsigned int *nbuffers, unsigned int *nplanes,
		     unsigned int sizes[], struct device *alloc_devs[])
{
...
	if (*nplanes) {
		if (sizes[0] < chan->format.sizeimage) {
			pr_err("%s: sizes[0] = %d chan->format.sizeimage = %d ...\n"
					,__func__,sizes[0],chan->format.sizeimage);
			return -EINVAL;
		}
	} else {
       # On JP5 this was just sizes[0] = chan->format.sizeimage;
		sizes[0] = chan->format.sizeimage + EMBEDDED_DATA_MAX_SIZE + EMBEDDED_DATA_BUFFER_ZONE_SIZE;
	}

	*nplanes = 1;
....
}

Embedded metadata is not a feature we use in our camera pipeline, and it has been disabled in the device tree entry by setting “embedded_metadata_height=0", as suggested by the nvidia docs.

A second potentially related issue is that the VIDIOC_QUERYBUF ioctl sets the returned file descriptor of the struct v4l2_buffer to zero, although it returns successfully. In the v412_dma_test program, I workaround this to ensure the file descriptor exported by libnvbufsurface is passed into the subsequent VIDIOC_QBUF call. This is what the gstreamer plugin does as well after calling VIDIOC_QUERYBUF.

Temporarily, I’ve been using the v4l2src plugin without DMA instead of nvv4l2camerasrc to work around these issues, but this has a huge performance hit for the overall pipeline in terms of end-to-end latency and CPU usage.

I’ve tried patching this change in channel.c and vi5_fops.c to remove the addition of embedded metadata and revert to how JP5 VI driver works, but this crashes nvgpu driver when I load nvvidconv plugin due to unserviced DMA interrupts / errors. See v4l2_revert.patch in the uploaded zip.

$ gst-inspect-1.0 nvvidconv # makes nvgpu crash with my patch. 

Hi,
We have the reference set up: AGX Orin developer kit + AR1335 which can be enabled through jetson-io.py. We test it with r36.4.3, r36.4.4 and it works well. The test app output is(manually modify V4L2_DEVICE_PATH to video0)

nvidia@nvidia-desktop:~/v4l2_dma_issue_repro_jp6$ ./v4l2_dma_test
VIDIOC_G_FMT: Current format: 3840x2160, FourCC=UYVY, Size=16588800
Created NvBufSurface w,h=(3840x2160), pitch=7680, isContiguous=0
        NvBufSurfaceParams dma_fd=5, dataSize=16646144, dataPtr=0xaaab082f1230
        NvBufSurfaceMappedAddr ptr=0xffffd4c9ca30
Requesting 1 dma buffer(s)
VIDIOC_REQBUFS: Allocated 1 buffer(s), is V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, is V4L2_MEMORY_DMABUF = 1
VIDIOC_QUERYBUF returned buf with dma_fd=0! Setting to 5 again before queuing.
VIDIOC_QUERYBUF buf: index=0, length=16588800, dma_fd=5, flags=0x2000, bytesused=0
VIDIOC_QBUF succeeded.
VIDIOC_STREAMON succeeded
VIDIOC_DQBUF dequeued buffer 0
VIDIOC_STREAMOFF succeeded

Also no issue with 12_camera_v4l2_cuda sample and gstreamer nvv4l2camerasrc plugin.

The default VI driver of Jetpack 6 should be good. Please check again if you follow the steps:

Kernel Customization — NVIDIA Jetson Linux Developer Guide

To build upstream kernel, nvidia-oot and device tree. Please manually copy the files:
Display drivers install to the wrong path with official document instructions - #17 by wxsrite
manually copy nvidia-drm.ko nvidia.ko nvidia-modeset.ko to opensrc-disp/ folder

And you are using custom kernel, so please do

Making sure you're not a bot!
[Jetpack 6.1/r36.4.0] Self-built kernel is overwritten after sudo apt update && sudo apt upgrade

Not sure if it helps but please also apply the fix:

Making sure you're not a bot!
[gstreamer] Segmentation fault after restarting gstreamer pipeline

Hi,
For reference, please run the command and share the prints:

$ v4l2-ctl --list-formats-ext -d /dev/video4

Would like to know what formats the camera sensor supports.

$ v4l2-ctl --list-formats-ext -d /dev/video4
ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture

	[0]: 'UYVY' (UYVY 4:2:2)
		Size: Discrete 3840x2160
			Interval: Discrete 0.033s (30.000 fps)
	[1]: 'NV16' (Y/CbCr 4:2:2)
		Size: Discrete 3840x2160
			Interval: Discrete 0.033s (30.000 fps)
	[2]: 'UYVY' (UYVY 4:2:2)
		Size: Discrete 3840x2160
			Interval: Discrete 0.033s (30.000 fps)

Will check the other links in the mean time, thanks @DaneLLL. Anything else you suggest I could run on my setup to narrow down the issue? If possible, could you share the device tree settings used in your reference setup too please?

Hi,
For comparison, please run the same command on Jetpack 5. Seems like NV16 should not be listed. And somehow UYVY is listed twice.

There are multiple UYVY formats in

nvidia-oot/drivers/media/platform/tegra/camera/camera_common.c

	{
		MEDIA_BUS_FMT_UYVY8_1X16,
		V4L2_COLORSPACE_SRGB,
		V4L2_PIX_FMT_UYVY,
	},

	{
		MEDIA_BUS_FMT_UYVY8_2X8,
		V4L2_COLORSPACE_SRGB,
		V4L2_PIX_FMT_UYVY,
	},
    

nvidia-oot/drivers/media/platform/tegra/camera/vi/vi5_formats.h

        TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 2, 1, T_U8_Y8__V8_Y8,
                                YUV422_8, UYVY, "YUV 4:2:2"),
        TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 1, 1, T_Y8__V8U8_N422,
                                YUV422_8, NV16, "NV16"),
        TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_2X8, 2, 1, T_U8_Y8__V8_Y8,
                                YUV422_8, UYVY, "YUV 4:2:2 UYVY"),

Please confirm which one the camera source supports, and remove the other one. See if it works by keeping only one format.

Please modify the ../drivers/media/platform/tegra/camera/vi/vi5_formats.h to remove some format like below to try.

Please modify the drivers/media/platform/tegra/camera/vi/vi5_formats.h to remove some format like below to try.

        /* YUV422 */
        TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 2, 1, T_U8_Y8__V8_Y8,
                                YUV422_8, UYVY, "YUV 4:2:2"),
        TEGRA_VIDEO_FORMAT(YUV422, 16, VYUY8_1X16, 2, 1, T_V8_Y8__U8_Y8,
                                YUV422_8, VYUY, "YUV 4:2:2"),
        TEGRA_VIDEO_FORMAT(YUV422, 16, YUYV8_1X16, 2, 1, T_Y8_U8__Y8_V8,
                                YUV422_8, YUYV, "YUV 4:2:2"),
        TEGRA_VIDEO_FORMAT(YUV422, 16, YVYU8_1X16, 2, 1, T_Y8_V8__Y8_U8,
                                YUV422_8, YVYU, "YUV 4:2:2"),
/*      TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_1X16, 1, 1, T_Y8__V8U8_N422,
                                YUV422_8, NV16, "NV16"),
        TEGRA_VIDEO_FORMAT(YUV422, 16, UYVY8_2X8, 2, 1, T_U8_Y8__V8_Y8,
                                YUV422_8, UYVY, "YUV 4:2:2 UYVY"),
        TEGRA_VIDEO_FORMAT(YUV422, 16, VYUY8_2X8, 2, 1, T_V8_Y8__U8_Y8,
                                YUV422_8, VYUY, "YUV 4:2:2 VYUY"),
        TEGRA_VIDEO_FORMAT(YUV422, 16, YUYV8_2X8, 2, 1, T_Y8_U8__Y8_V8,
                                YUV422_8, YUYV, "YUV 4:2:2 YUYV"),
        TEGRA_VIDEO_FORMAT(YUV422, 16, YVYU8_2X8, 2, 1, T_Y8_V8__Y8_U8,
                                YUV422_8, YVYU, "YUV 4:2:2 YVYU"),
*/
};

Hi @DaneLLL, So the listed formats output in JP5 was similar to the three supported formats reported in JP6. The camera module used is the same in both.

I’d then patched vi5_formats.h like you’d suggested @ShaneCCC, and I see only one format now.

$  v4l2-ctl --list-formats-ext -d /dev/video4
ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture

	[0]: 'UYVY' (UYVY 4:2:2)
		Size: Discrete 3840x2160
			Interval: Discrete 0.033s (30.000 fps)

Though the gstreamer behaviour is the same with this patch as before where v4l2src works, but nvv4l2camerasrc fails with the same error from the VIDIOC_QBUFioctl.

Re-running the test script I provided, I see the same output where length=17113344 and VIDIOC_QBUF failed: Bad address (errno=14).

Hi,
So the issue should be why length=17113344. Correct value of 4K UYVY is 3840x2160x2=16588800 bytes.

On Jetpack 6, the VI driver code is in

nvidia-oot/drivers/media/platform/tegra/camera/vi/vi5_fops.c
nvidia-oot/drivers/media/platform/tegra/camera/vi/channel.c
nvidia-oot/drivers/media/platform/tegra/camera/camera_common.c

Please add prints to the driver code to check sizeimage, size in vi5_setup_surface(). To confirm the values are expected.

Hi,
And please ensure you install nvidia-l4t-jetson-multimedia-api on Jetpack 6.2.1 and compile the samples with the files in

/usr/src/jetson_multimedia_api

A same error in the old topic and it works by matching the version of samples and Jetpack release:
VIDIOC_QBUF Buffer error when running Multimedia API sample 12_camera_v4l2_cuda - #5 by DaneLLL

Thanks @DaneLLL. The size mismatch comes from a vendor-specific tweak in the driver stack that I misattributed to an Nvidia JP5 vs JP6 change. This was useful to narrow down the location. I think we can close this as it’s not a JP6 issue per-se.

On a side-note, are there parts of the nvgpu driver / nvvidconv srcs that could have dependencies on the VI driver stack that could explain the crash in my initial post?

1 Like

Hi,
Ideally the drivers are independent and should not interfere with each other. Could you confirm again if the version of kernel source code is correct. I can see the code in channel.c of r36.4.3/r36.4.4:

	if (*nplanes) {
		if (sizes[0] < chan->format.sizeimage) {
			pr_err("%s: sizes[0] = %d chan->format.sizeimage = %d ...\n"
					,__func__,sizes[0],chan->format.sizeimage);
			return -EINVAL;
		}
	} else {
		sizes[0] = chan->format.sizeimage;
	}

But the patch does:

@@ -711,23 +711,27 @@ tegra_channel_queue_setup(struct vb2_queue *vq,
 	 * of the requested image size. Although this did not harm the
 	 * flow, according to "v4l2-compliance", we need to check if
 	 * the requested size is invalid.
+
+	 // reverting to jp5 ways. 
 	 */
-	if (*nplanes) {
-		if (sizes[0] < chan->format.sizeimage) {
-			pr_err("%s: sizes[0] = %d chan->format.sizeimage = %d ...\n"
-					,__func__,sizes[0],chan->format.sizeimage);
-			return -EINVAL;
-		}
-	} else {
-		sizes[0] = chan->format.sizeimage + EMBEDDED_DATA_MAX_SIZE + EMBEDDED_DATA_BUFFER_ZONE_SIZE;
-	}
+	printk("tegra_channel_queue_setup: nplanes=%d, sizes[0]=%d, ch->fmt.szimg=%u\n", *nplanes, sizes[0], chan->format.sizeimage);
+
+	// if (*nplanes) {
+	// 	if (sizes[0] < chan->format.sizeimage) {
+	// 		pr_err("%s: sizes[0] = %d chan->format.sizeimage = %d ...\n"
+	// 				,__func__,sizes[0],chan->format.sizeimage);
+	// 		return -EINVAL;
+	// 	}
+	// } else {
+		sizes[0] = chan->format.sizeimage;// + EMBEDDED_DATA_MAX_SIZE + EMBEDDED_DATA_BUFFER_ZONE_SIZE;
+	// }
 
 	*nplanes = 1;
 	alloc_devs[0] = tegra_channel_get_vi_unit(chan);
 
 	if (vi->fops && vi->fops->vi_setup_queue)
 		return vi->fops->vi_setup_queue(chan, nbuffers);
-	return ret;
+	return -EINVAL; // return ret;
 }
 
 int tegra_channel_alloc_buffer_queue(struct tegra_channel *chan,

Looks like your kernel code is a different version.

Yes, that patch was meant to revert some vendor specific changes to the JP6 kernel back to how it was on JP5. Only after I diffed the Nvidia JP6 kernel src and vendor JP6 src did I realise that additional EMBEDDED_DATA_MAX_SIZE + EMBEDDED_DATA_BUFFER_ZONE_SIZE was not an Nvidia addition in JP6, but from the vendor. Thanks.

Hi,
Got it. Would suggest check with the vendor to align with our kernel code of r36.4.3/r36.4.4 first.