V4L2 Multi-Planar Capture: Separate Memory Planes from YUV Color Planes
An embedded camera pipeline can report an NV12 image with luma and chroma while exposing only one V4L2 memory plane. That is consistent: color layout and independently addressed storage are different descriptions. Selecting a buffer type ending in _MPLANE does not, by itself, mean that every frame needs two allocations.
This guide pins Linux v6.12 userspace documentation and linear, eight-bit NV12/NV12M capture. The worked layout is deliberately tiny and unpadded. It is a reasoning fixture, not a camera measurement, an allocation recommendation or a claim about a particular BSP.
Ask two separate format questions
First identify the queue API the driver supports. The single- and multi-planar API guide permits single-planar formats through multi-planar structures; the reverse does not work for multi-planar formats. Query capabilities and enumerate formats for the intended queue type instead of inferring support from a camera’s advertised resolution.
Then identify the returned pixel format. In the linear YUV description, NV12 has a luma region followed by interleaved Cb/Cr samples in contiguous storage. NV12M allows those regions in separate memory buffers. Their FourCC values differ, even though both describe the same general chroma subsampling arrangement.
An application contract should therefore record queue type, FourCC, dimensions, memory-plane count and each plane’s size and stride. A field called simply “planes” is ambiguous unless its meaning is documented.
Trace a twelve-byte frame
Use a hypothetical image four pixels wide and two pixels high, with no row padding. Its luma region contains eight bytes. Its one chroma row contains four bytes, with two Cb/Cr pairs. The arithmetic is 8 + 4 = 12 bytes.
For contiguous NV12, both regions belong to one memory buffer: offsets zero through seven contain luma, and offsets eight through eleven contain chroma. Through the multi-planar API, that format still uses one memory-plane description. The chroma start is an internal location within that buffer, not a second independent V4L2 plane handle.
For NV12M, the same illustrative sample values occupy two memory buffers: eight luma bytes in the first and four chroma bytes in the second. The second buffer need not follow the first in virtual or physical address space. Do not find it by adding eight to the first pointer.

Original illustrative layout. The counts assume exactly the stated dimensions and no padding; they are not returned driver parameters.
Build descriptors from the negotiated result
The multi-planar format structure defines num_planes as the number of separate memory buffers. It also supplies plane_fmt entries with sizeimage and bytesperline. Read the result after format negotiation rather than preserving an application’s requested values unconditionally.
For an actual camera, the twelve-byte fixture must give way to that returned contract. Alignment, padding and supported dimensions can change the storage requirement. In contiguous NV12, the chroma region’s placement follows the specified layout and luma storage, not a width-only calculation that ignores row pitch. For noncontiguous formats, do not assume independently described planes share an identical stride.
The queue documentation uses v4l2_buffer.m.planes to point to an array of v4l2_plane descriptors for multi-planar queues. Here v4l2_buffer.length is the array’s entry count. It is not the aggregate byte capacity of the frame. Individual plane descriptors carry their own lengths and memory-method-specific information.
Apply the same structural discipline to dequeue operations. Provide the plane array when the API requires it, preserve its lifetime through the call, and interpret returned fields under the selected memory method. Mixing a single-planar union member with a multi-planar queue is a descriptor error even if the underlying pixels are contiguous.
Review failures at the correct boundary
A proposed descriptor-only check can use two records with the same four-by-two visible geometry. The NV12 record has one memory entry; the NV12M record has two. Assert that the queue builder follows each record instead of a hard-coded count derived from “Y plus UV.” No device access is needed to review that distinction.
Next introduce deliberately padded rows in the proposed fixture and verify that width-only address calculations are rejected. Finally, on an approved target, compare negotiated format fields with queried buffer capacities and dequeue results before interpreting pixels.
Save the exact queue type and FourCC alongside any image dump. A purple preview alone cannot distinguish a chroma-order error from a bad plane pointer or ignored stride. This storage contract narrows the investigation without promising DMA sharing, GPU import or zero-copy compatibility, each of which needs additional driver-specific evidence.