linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls
@ 2021-01-14 18:07 Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 01/11] media: v4l2-common: add normalized pixelformat field to struct v4l2_format_info Helen Koike
                   ` (11 more replies)
  0 siblings, 12 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Hello,

This is v6 of the Extended API for formats and buffers (see below the new API).

The new API comes for free for old drivers through the conversion layer, which
is independent of vb2.

I completly refactored several patches. I would like to request comments not
only in the uAPI, but also the kAPI for drivers, and I would appreciate any
ideas on improving the quality of the code (in short: please review everything).

NOTE: The Ext API wans't tested yet. My next step is to patch v4l2-compliance.

Regression tests - v4l2-compliance with test-media script:
	vivid: http://ix.io/2M0G - Final Summary: 1856, Succeeded: 1856, Failed: 0, Warnings: 0)
	vimc: http://ix.io/2M0I - Final Summary: 488, Succeeded: 488, Failed: 0, Warnings: 0

Git: https://gitlab.collabora.com/koike/linux/-/tree/v4l2/ext-api/v6

v5: https://patchwork.linuxtv.org/project/linux-media/cover/20200804192939.2251988-1-helen.koike@collabora.com/
v4: https://patchwork.linuxtv.org/project/linux-media/cover/20200717115435.2632623-1-helen.koike@collabora.com/
v3: https://patchwork.linuxtv.org/cover/59345/
v2: https://patchwork.kernel.org/project/linux-media/list/?series=101153
v1: https://patchwork.kernel.org/project/linux-media/list/?series=93707

Conversion layer:
=================

* Old drivers implementing only ops->vidioc_*_fmt_vid_cap supports
  VIDIOC_*_EXT_PIX_FMT automatically with limitations[1].

* New drivers implementing only ops->vidioc_*_ext_pix_fmt_vid_cap supports
  VIDIOC_*_FMT automatically.

* Old drivers implementing only ops->vidioc_*buf support
  VIDIOC_EXT_*BUF automatically with limitations[2].

* New drivers should implement both ops->vidioc_*buf and ops->vidioc_*buf
  to overcome limitations[2] and support both APIs.
  Which is easy with vb2:
     static const struct v4l2_ioctl_ops ioctl_ops = {
     ...
     +      .vidioc_ext_qbuf = vb2_ioctl_ext_qbuf,
     +      .vidioc_ext_dqbuf = vb2_ioctl_ext_dqbuf,
     ...
     }
     ...
     +      /* Inform vb2 how to split the memory buffer in case a single one is used */
     +      vb2_set_pixelformat(dev->pixelformat)

[1] There are some limitations in the conversion such as modifiers that are
    ignored when converting v4l2_ext_pix_format to v4l_format

[2] Ext API allows a single buffer with planes placed in random locations,
    which is not possible with v4l2_buffer.


Major changes in v6:
====================

Fixed color planes vs memory planes handling.

Removed VIDIOC_EXT_PREPARE_BUF, since this is an optimization, it doesn't blocks
the API, we can add it later (my goal was to simplify this patchset).

Removed VIDIOC_EXT_CREATE_BUFS, since this is useful only to MMAP (thus low priority)
with the new format.
Classic VIDIOC_CREATE_BUFS and VIDIOC_REQBUFS can still be used.

Reformulated conversion layer as per above.

Removed conversions in vb2, it is easier to add hooks to drivers.

Fixed vb2 to allow Ext API only to Video types.

API updates:
* remove buffer and plane lengths
* move `memory` field to v4l2_ext_buffer instead of v4l2_ext_plane
* remove struct v4l2_plane_ext_pix_format
* reordering

Make Ext API valid only for Video types, and not for touch, vbi, meta, etc.

Sereval code refactoring, simplification, fixes and applied suggestions from v5.

New API (for convenience):
==========================

int ioctl(int fd, VIDIOC_G_EXT_PIX_FMT, struct v4l2_ext_pix_format *argp)
int ioctl(int fd, VIDIOC_S_EXT_PIX_FMT, struct v4l2_ext_pix_format *argp)
int ioctl(int fd, VIDIOC_TRY_EXT_PIX_FMT, struct v4l2_ext_pix_format *argp)
int ioctl(int fd, VIDIOC_EXT_QBUF, struct v4l2_ext_buffer *argp)
int ioctl(int fd, VIDIOC_EXT_DQBUF, struct v4l2_ext_buffer *argp)

struct v4l2_ext_pix_format {
	__u32 type;
	__u32 width;
	__u32 height;
	__u32 field;
	struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
	__u32 pixelformat;
	__u64 modifier;
	__u32 colorspace;
	__u32 xfer_func;
	union {
		__u32 ycbcr_enc;
		__u32 hsv_enc;
	};
	__u32 quantization;
	__u32 reserved[9];
};

struct v4l2_ext_buffer {
	__u32 index;
	__u32 type;
	__u32 field;
	__u32 sequence;
	__u64 flags;
	__u64 timestamp;
	__u32 memory;
	__s32 request_fd;
	struct v4l2_ext_plane planes[VIDEO_MAX_PLANES];
	__u32 reserved[10];
};

struct v4l2_ext_plane {
	__u32 offset;
	__u32 bytesused;
	union {
		__u32 mmap_offset;
		__u64 userptr;
		__s32 dmabuf_fd;
	} m;
	__u32 reserved[6];
};

Helen Koike (11):
  media: v4l2-common: add normalized pixelformat field to struct
    v4l2_format_info
  media: v4l2: Extend pixel formats to unify single/multi-planar
    handling (and more)
  media: v4l2: Add extended buffer (de)queue operations for video types
  media: videobuf2-v4l2: reorganize flags handling
  media: videobuf2: Expose helpers for Ext qbuf/dqbuf
  media: vivid: use vb2_ioctls_ext_{d}qbuf hooks
  media: vimc: use vb2_ioctls_ext_{d}qbuf hooks
  media: mediabus: Add helpers to convert a ext_pix format to/from a
    mbus_fmt
  media: vivid: Convert to v4l2_ext_pix_format
  media: vimc: Convert to v4l2_ext_pix_format
  media: docs: add documentation for the Extended API

 .../userspace-api/media/v4l/buffer.rst        |   5 +
 .../userspace-api/media/v4l/common.rst        |   1 +
 .../userspace-api/media/v4l/dev-capture.rst   |   6 +
 .../userspace-api/media/v4l/dev-output.rst    |   6 +
 .../userspace-api/media/v4l/ext-api.rst       |  89 +++
 .../userspace-api/media/v4l/format.rst        |  18 +-
 .../userspace-api/media/v4l/user-func.rst     |   5 +
 .../media/v4l/vidioc-ext-qbuf.rst             | 188 +++++
 .../media/v4l/vidioc-g-ext-pix-fmt.rst        | 116 +++
 .../userspace-api/media/v4l/vidioc-qbuf.rst   |   2 +-
 .../media/common/videobuf2/videobuf2-core.c   |  46 +-
 .../media/common/videobuf2/videobuf2-v4l2.c   | 500 +++++++++---
 .../media/test-drivers/vimc/vimc-capture.c    |  57 +-
 drivers/media/test-drivers/vimc/vimc-common.c |   6 +-
 drivers/media/test-drivers/vimc/vimc-common.h |   2 +-
 drivers/media/test-drivers/vivid/vivid-core.c | 209 ++---
 .../media/test-drivers/vivid/vivid-vid-cap.c  | 203 ++---
 .../media/test-drivers/vivid/vivid-vid-cap.h  |  15 +-
 .../media/test-drivers/vivid/vivid-vid-out.c  | 198 ++---
 .../media/test-drivers/vivid/vivid-vid-out.h  |  15 +-
 drivers/media/v4l2-core/v4l2-common.c         |  16 +-
 drivers/media/v4l2-core/v4l2-dev.c            |  31 +-
 drivers/media/v4l2-core/v4l2-ioctl.c          | 722 +++++++++++++++++-
 include/media/v4l2-common.h                   |   3 +
 include/media/v4l2-ioctl.h                    |  36 +
 include/media/v4l2-mediabus.h                 |  42 +
 include/media/videobuf2-core.h                |  33 +-
 include/media/videobuf2-v4l2.h                |   8 +-
 include/uapi/linux/videodev2.h                |  96 +++
 29 files changed, 2131 insertions(+), 543 deletions(-)
 create mode 100644 Documentation/userspace-api/media/v4l/ext-api.rst
 create mode 100644 Documentation/userspace-api/media/v4l/vidioc-ext-qbuf.rst
 create mode 100644 Documentation/userspace-api/media/v4l/vidioc-g-ext-pix-fmt.rst

-- 
2.29.2


^ permalink raw reply	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 01/11] media: v4l2-common: add normalized pixelformat field to struct v4l2_format_info
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-02-10 12:37   ` Dafna Hirschfeld
  2021-01-14 18:07 ` [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more) Helen Koike
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Add normalization to pixelformats, so we can fallback to it when using
Ext API, and eliminating the handling of two variantes (M and non-M
formats).

Signed-off-by: Helen Koike <helen.koike@collabora.com>

---
Changes in v6:
- New patch
---
 drivers/media/v4l2-core/v4l2-common.c | 16 ++++++++--------
 include/media/v4l2-common.h           |  3 +++
 2 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 78007dba4677..002051b9dc0c 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -276,17 +276,17 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
 		{ .format = V4L2_PIX_FMT_GREY,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
 
 		/* YUV planar formats, non contiguous variant */
-		{ .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
-		{ .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
-		{ .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
+		{ .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2, .norm= V4L2_PIX_FMT_YUV420 },
+		{ .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2, .norm= V4L2_PIX_FMT_YVU420 },
+		{ .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1, .norm= V4L2_PIX_FMT_YUV422P },
 		{ .format = V4L2_PIX_FMT_YVU422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
-		{ .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
+		{ .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1, .norm= V4L2_PIX_FMT_YUV444 },
 		{ .format = V4L2_PIX_FMT_YVU444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
 
-		{ .format = V4L2_PIX_FMT_NV12M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
-		{ .format = V4L2_PIX_FMT_NV21M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
-		{ .format = V4L2_PIX_FMT_NV16M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
-		{ .format = V4L2_PIX_FMT_NV61M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
+		{ .format = V4L2_PIX_FMT_NV12M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2, .norm = V4L2_PIX_FMT_NV12 },
+		{ .format = V4L2_PIX_FMT_NV21M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2, .norm = V4L2_PIX_FMT_NV21 },
+		{ .format = V4L2_PIX_FMT_NV16M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1, .norm = V4L2_PIX_FMT_NV16 },
+		{ .format = V4L2_PIX_FMT_NV61M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1, .norm = V4L2_PIX_FMT_NV61 },
 
 		/* Bayer RGB formats */
 		{ .format = V4L2_PIX_FMT_SBGGR8,	.pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index be36cbdcc1bd..7236af1cfa2f 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -483,6 +483,8 @@ enum v4l2_pixel_encoding {
  * @vdiv: Vertical chroma subsampling factor
  * @block_w: Per-plane macroblock pixel width (optional)
  * @block_h: Per-plane macroblock pixel height (optional)
+ * @norm: The normalized format that should be used in Ext API. Should be set
+ *	  to zero if @format is already the normalized version.
  */
 struct v4l2_format_info {
 	u32 format;
@@ -494,6 +496,7 @@ struct v4l2_format_info {
 	u8 vdiv;
 	u8 block_w[4];
 	u8 block_h[4];
+	u32 norm;
 };
 
 static inline bool v4l2_is_format_rgb(const struct v4l2_format_info *f)
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 01/11] media: v4l2-common: add normalized pixelformat field to struct v4l2_format_info Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-02-10 15:02   ` Dafna Hirschfeld
  2021-02-23 12:35   ` Hans Verkuil
  2021-01-14 18:07 ` [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types Helen Koike
                   ` (9 subsequent siblings)
  11 siblings, 2 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

This is part of the multiplanar and singleplanar unification process.
v4l2_ext_pix_format is supposed to work for both cases.

We also add the concept of modifiers already employed in DRM to expose
HW-specific formats (like tiled or compressed formats) and allow
exchanging this information with the DRM subsystem in a consistent way.

Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
v4l2_ext_format, other types will be rejected if you use the
{G,S,TRY}_EXT_PIX_FMT ioctls.

New hooks have been added to v4l2_ioctl_ops to support those new ioctls
in drivers, but, in the meantime, the core takes care of converting
{S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers can
still work if the userspace app/lib uses the new ioctls.

The conversion is also done the other around to allow userspace
apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
_ext_ hooks.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Helen Koike <helen.koike@collabora.com>
---

Changes in v6:
 The main change here was fixing the conversion, so planes reflects color planes,
 and to implement this properly I made major refactors compared to the previous
 version.
- struct v4l2_plane_ext_pix_format removed, using struct v4l2_plane_pix_format instead (Tomasz)
- refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
- reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
- do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
- refactor conversion functions, so planes are color planes (Tomasz)
- Don't explicitly check for e->modifier != 0 in v4l2_ext_pix_format_to_format() (Tomasz)
- Use "ef" for extended formats in the framework for consistency (Tomasz)
- Handle xfer_func field in conversions (Tomasz)
- Zero reserved fields in v4l_s_ext_pix_fmt() and v4l_try_ext_pix_fmt() (Tomasz)
- Refactor format functions to use v4l_fmt_ioctl_via_ext()
- Several fixes/refactoring/changes
- Remove EXT API for touch devices

Changes in v5:
- change sizes and reorder fields to avoid holes in the struct and make
  it the same for 32 and 64 bits
- removed __attribute__ ((packed)) from uapi structs
- Fix doc warning from make htmldocs
- Updated commit message with EXT_PIX prefix for the ioctls.

Changes in v4:
- Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
- Add reserved fields
- Removed num_planes from struct v4l2_ext_pix_format
- Removed flag field from struct v4l2_ext_pix_format, since the only
  defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
  where we can use modifiers, or add it back later through the reserved
  bits.
- In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
  != MOD_INVALID
- Fix type assignment in v4l_g_fmt_ext_pix()
- Rebased on top of media/master (post 5.8-rc1)

Changes in v3:
- Rebased on top of media/master (post 5.4-rc1)

Changes in v2:
- Move the modifier in v4l2_ext_format (was formerly placed in
  v4l2_ext_plane)
- Fix a few bugs in the converters and add a strict parameter to
  allow conversion of uninitialized/mis-initialized objects
---
 drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
 drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
 include/media/v4l2-ioctl.h           |  28 ++
 include/uapi/linux/videodev2.h       |  41 ++
 4 files changed, 602 insertions(+), 32 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index f9cff033d0dc..5add58cb6d45 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct video_device *vdev)
 			       ops->vidioc_enum_fmt_vid_overlay)) ||
 		    (is_tx && ops->vidioc_enum_fmt_vid_out))
 			set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
+		if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
+		    (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
+			 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
 		if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
 			       ops->vidioc_g_fmt_vid_cap_mplane ||
-			       ops->vidioc_g_fmt_vid_overlay)) ||
+			       ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
 		    (is_tx && (ops->vidioc_g_fmt_vid_out ||
 			       ops->vidioc_g_fmt_vid_out_mplane ||
-			       ops->vidioc_g_fmt_vid_out_overlay)))
+			       ops->vidioc_g_ext_pix_fmt_vid_out))) {
 			 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+			 set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
+		}
+		if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
+		    (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
+			 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
 		if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
 			       ops->vidioc_s_fmt_vid_cap_mplane ||
-			       ops->vidioc_s_fmt_vid_overlay)) ||
+			       ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
 		    (is_tx && (ops->vidioc_s_fmt_vid_out ||
 			       ops->vidioc_s_fmt_vid_out_mplane ||
-			       ops->vidioc_s_fmt_vid_out_overlay)))
+			       ops->vidioc_s_ext_pix_fmt_vid_out))) {
 			 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+			 set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
+		}
+		if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
+		    (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
+			 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
 		if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
 			       ops->vidioc_try_fmt_vid_cap_mplane ||
-			       ops->vidioc_try_fmt_vid_overlay)) ||
+			       ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
 		    (is_tx && (ops->vidioc_try_fmt_vid_out ||
 			       ops->vidioc_try_fmt_vid_out_mplane ||
-			       ops->vidioc_try_fmt_vid_out_overlay)))
+			       ops->vidioc_try_ext_pix_fmt_vid_out))) {
 			 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
+			 set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
+		}
 		SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
 		SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
 		SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 848286a284f6..a9c07c0a73ec 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -18,6 +18,8 @@
 
 #include <linux/videodev2.h>
 
+#include <drm/drm_fourcc.h>
+
 #include <media/v4l2-common.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-ctrls.h>
@@ -38,6 +40,11 @@
 
 #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), (vfd)->valid_ioctls)
 
+#define V4L2_IS_CAP_MULTIPLANAR(vdev)	(vdev->device_caps & \
+					 (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
+					 V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
+					 V4L2_CAP_VIDEO_M2M_MPLANE))
+
 struct std_descr {
 	v4l2_std_id std;
 	const char *descr;
@@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg, bool write_only)
 	}
 }
 
+static void v4l_print_ext_pix_format(const void *arg, bool write_only)
+{
+	const struct v4l2_ext_pix_format *ef = arg;
+	unsigned int i;
+
+	pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u, xfer_func=%u\n",
+		prt_names(ef->type, v4l2_type_names),
+		ef->width, ef->height,
+		(ef->pixelformat & 0xff),
+		(ef->pixelformat >>  8) & 0xff,
+		(ef->pixelformat >> 16) & 0xff,
+		(ef->pixelformat >> 24) & 0xff,
+		ef->modifier, prt_names(ef->field, v4l2_field_names),
+		ef->colorspace, ef->ycbcr_enc,
+		ef->quantization, ef->xfer_func);
+	for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
+		pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
+			 i, ef->plane_fmt[i].bytesperline,
+			 ef->plane_fmt[i].sizeimage);
+}
+
 static void v4l_print_framebuffer(const void *arg, bool write_only)
 {
 	const struct v4l2_framebuffer *p = arg;
@@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
 	switch (type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 		if ((is_vid || is_tch) && is_rx &&
-		    (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane))
+		    (ops->vidioc_g_fmt_vid_cap ||
+		     ops->vidioc_g_ext_pix_fmt_vid_cap ||
+		     ops->vidioc_g_fmt_vid_cap_mplane))
 			return 0;
 		break;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		if ((is_vid || is_tch) && is_rx && ops->vidioc_g_fmt_vid_cap_mplane)
+		if ((is_vid || is_tch) && is_rx &&
+		    (ops->vidioc_g_fmt_vid_cap_mplane ||
+		     ops->vidioc_g_ext_pix_fmt_vid_cap))
 			return 0;
 		break;
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
@@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
 		break;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 		if (is_vid && is_tx &&
-		    (ops->vidioc_g_fmt_vid_out || ops->vidioc_g_fmt_vid_out_mplane))
+		    (ops->vidioc_g_fmt_vid_out ||
+		     ops->vidioc_g_ext_pix_fmt_vid_out ||
+		     ops->vidioc_g_fmt_vid_out_mplane))
 			return 0;
 		break;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
+		if (is_vid && is_tx &&
+		    (ops->vidioc_g_ext_pix_fmt_vid_out ||
+		     ops->vidioc_g_fmt_vid_out_mplane))
 			return 0;
 		break;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
@@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct v4l2_format *fmt)
 	       sizeof(fmt->fmt.pix) - offset);
 }
 
+static void
+v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
+				  struct v4l2_pix_format *pix)
+{
+	unsigned int i;
+
+	pix->width = ef->width;
+	pix->height = ef->height;
+	pix->field = ef->field;
+	pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
+	pix->colorspace = ef->colorspace;
+	pix->ycbcr_enc = ef->ycbcr_enc;
+	pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
+	pix->quantization = ef->quantization;
+	pix->pixelformat = ef->pixelformat;
+	pix->bytesperline = ef->plane_fmt[0].bytesperline;
+	pix->sizeimage = ef->plane_fmt[0].sizeimage;
+	for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
+		pix->sizeimage += ef->plane_fmt[i].sizeimage;
+}
+
+static void
+v4l2_ext_pix_format_to_pix_mp_format(const struct v4l2_ext_pix_format *ef,
+				     struct v4l2_pix_format_mplane *pix_mp)
+{
+	const struct v4l2_format_info *info =
+					v4l2_format_info(ef->pixelformat);
+	unsigned int i;
+
+	pix_mp->width = ef->width;
+	pix_mp->height = ef->height;
+	pix_mp->field = ef->field;
+	pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
+	pix_mp->colorspace = ef->colorspace;
+	pix_mp->ycbcr_enc = ef->ycbcr_enc;
+	pix_mp->quantization = ef->quantization;
+	pix_mp->pixelformat = ef->pixelformat;
+
+	/* This is true when converting to non-M-variant */
+	if (info && info->mem_planes == 1) {
+		pix_mp->plane_fmt[0] = ef->plane_fmt[0];
+		pix_mp->num_planes = 1;
+		for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
+			pix_mp->plane_fmt[0].sizeimage += ef->plane_fmt[i].sizeimage;
+
+		return;
+	}
+
+	for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
+		pix_mp->plane_fmt[i] = ef->plane_fmt[i];
+	pix_mp->num_planes = i;
+}
+
+/*
+ * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to v4l2_format
+ *
+ * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
+ * @f: A pointer to struct v4l2_format to be filled.
+ * @is_mplane: Bool indicating if multiplanar API should be used in @f.
+ *
+ * If pixelformat should be converted to M-variant, change ef->pixelformat
+ * to the M-variant before calling this function.
+ */
+static void v4l2_ext_pix_format_to_format(const struct v4l2_ext_pix_format *ef,
+					  struct v4l2_format *f, bool is_mplane)
+{
+	memset(f, 0, sizeof(*f));
+
+	if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
+	    ef->modifier != DRM_FORMAT_MOD_INVALID)
+		pr_warn("Modifiers are not supported in v4l2_format, ignoring %llx\n",
+			ef->modifier);
+
+	if (!is_mplane) {
+		f->type = ef->type;
+		v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
+		return;
+	}
+
+	if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	else
+		f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+	v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
+}
+
+static void
+v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
+				  struct v4l2_ext_pix_format *ef)
+{
+	const struct v4l2_format_info *info =
+					v4l2_format_info(pix->pixelformat);
+	unsigned int i;
+
+	ef->width = pix->width;
+	ef->height = pix->height;
+	ef->field = pix->field;
+	ef->colorspace = pix->colorspace;
+	ef->ycbcr_enc = pix->ycbcr_enc;
+	ef->quantization = pix->quantization;
+	ef->xfer_func = pix->xfer_func;
+	if (pix->flags)
+		pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
+
+	/* We assume M-variants won't be used in this function */
+	ef->pixelformat = pix->pixelformat;
+
+	ef->plane_fmt[0].bytesperline = pix->bytesperline;
+	ef->plane_fmt[0].sizeimage = pix->sizeimage;
+
+	if (!info)
+		return;
+
+	for (i = 1; i < info->comp_planes; i++) {
+		ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
+		ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
+					     ef->height / info->vdiv;
+		ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
+	}
+}
+
+static void
+v4l2_pix_mp_format_to_ext_pix_format(const struct v4l2_pix_format_mplane *pix_mp,
+				     struct v4l2_ext_pix_format *ef)
+{
+	const struct v4l2_format_info *info =
+					v4l2_format_info(pix_mp->pixelformat);
+	unsigned int i;
+
+	ef->width = pix_mp->width;
+	ef->height = pix_mp->height;
+	ef->field = pix_mp->field;
+	ef->colorspace = pix_mp->colorspace;
+	ef->ycbcr_enc = pix_mp->ycbcr_enc;
+	ef->quantization = pix_mp->quantization;
+	ef->xfer_func = pix_mp->xfer_func;
+	if (pix_mp->flags)
+		pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
+
+	if (!info)
+		return;
+
+	ef->pixelformat = info && info->norm ?
+			  info->norm : pix_mp->pixelformat;
+
+	if (info->comp_planes == info->mem_planes) {
+		for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES; i++)
+			ef->plane_fmt[i] = pix_mp->plane_fmt[i];
+
+		return;
+	}
+
+	/* case where mem_planes is 1 and comp_planes > 1 */
+	ef->plane_fmt[0] = pix_mp->plane_fmt[0];
+	for (i = 1; i < info->comp_planes; i++) {
+		ef->plane_fmt[i].bytesperline =
+			pix_mp->plane_fmt[0].bytesperline / info->hdiv;
+		ef->plane_fmt[i].sizeimage =
+			ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
+		ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
+	}
+}
+
+/*
+ * v4l2_format_to_ext_pix_format - convert to v4l2_format to v4l2_ext_pix_format
+ *
+ * @f: A pointer to struct v4l2_format to be converted.
+ * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
+ *
+ * This method normalize the pixelformat to non-M variant.
+ */
+static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
+					  struct v4l2_ext_pix_format *ef)
+{
+	memset(ef, 0, sizeof(*ef));
+
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		ef->type = f->type;
+		v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+		v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
+		break;
+	default:
+		WARN("Converting to Ext Pix Format with wrong buffer type %s\n",
+		     prt_names(f->type, v4l2_type_names));
+		break;
+	}
+}
+
 static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
@@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct v4l2_pix_format *p)
 	p->xfer_func = 0;
 }
 
+static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
+				 struct file *file, void *fh,
+				 struct v4l2_format *f,
+				 unsigned int ioctl)
+{
+	bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
+	struct video_device *vdev = video_devdata(file);
+	struct v4l2_ext_pix_format ef = {0};
+	u32 original_pixfmt = 0;
+	u32 cap_mask;
+	int ret;
+
+	if (ioctl != VIDIOC_G_FMT) {
+		/*
+		 * If CSC attributes are read only, set them to DEFAULT
+		 * to avoid changes by the driver.
+		 */
+		if (is_multiplanar) {
+			if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
+				f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
+				f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+				f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+				f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+			}
+			/* Unset the flag to avoid warning in the convertion */
+			f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
+
+			/* Save pixelformat in case M-variant is being used */
+			original_pixfmt = f->fmt.pix_mp.pixelformat;
+		} else {
+			if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
+				f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
+				f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+				f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
+				f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+			}
+			/* Unset the flag to avoid warning in the convertion */
+			f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
+		}
+		v4l2_format_to_ext_pix_format(f, &ef);
+	}
+
+	switch (f->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+			   V4L2_CAP_VIDEO_M2M_MPLANE;
+		if (!!(vdev->device_caps & cap_mask) !=
+		    (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
+			return -EINVAL;
+
+		ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		if (ioctl == VIDIOC_G_FMT)
+			ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
+		else if (ioctl == VIDIOC_S_FMT)
+			ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
+		else
+			ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
+								  &ef);
+		break;
+
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+			   V4L2_CAP_VIDEO_M2M_MPLANE;
+		if (!!(vdev->device_caps & cap_mask) !=
+		    (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+			return -EINVAL;
+
+		ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+		if (ioctl == VIDIOC_G_FMT)
+			ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
+		else if (ioctl == VIDIOC_S_FMT)
+			ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
+		else
+			ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
+								  &ef);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (ret)
+		return ret;
+
+	if (original_pixfmt != ef.pixelformat &&
+	    v4l2_format_info(original_pixfmt))
+		ef.pixelformat = original_pixfmt;
+
+	v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
+	return 0;
+}
+
 static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
@@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!ops->vidioc_g_fmt_vid_cap))
+		if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
+			     !ops->vidioc_g_ext_pix_fmt_vid_cap))
 			break;
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
-		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
+		ret = ops->vidioc_g_fmt_vid_cap ?
+		      ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
+		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
+					    VIDIOC_G_FMT);
 		/* just in case the driver zeroed it again */
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		if (vfd->vfl_type == VFL_TYPE_TOUCH)
 			v4l_pix_format_touch(&p->fmt.pix);
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
+		if (ops->vidioc_g_fmt_vid_cap_mplane)
+			return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
+		else if (ops->vidioc_g_ext_pix_fmt_vid_cap)
+			return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
+						     VIDIOC_G_FMT);
+		break;
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 		return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
 	case V4L2_BUF_TYPE_VBI_CAPTURE:
@@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
 		return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		if (unlikely(!ops->vidioc_g_fmt_vid_out))
+		if (unlikely(!ops->vidioc_g_fmt_vid_out &&
+			     !ops->vidioc_g_ext_pix_fmt_vid_out))
 			break;
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
-		ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
+		ret = ops->vidioc_g_fmt_vid_out ?
+		      ops->vidioc_g_fmt_vid_out(file, fh, arg) :
+		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
 		/* just in case the driver zeroed it again */
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
+		if (ops->vidioc_g_fmt_vid_out_mplane)
+			return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
+		else if (ops->vidioc_g_ext_pix_fmt_vid_out)
+			return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
+						     VIDIOC_G_FMT);
+		break;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 		return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
 	case V4L2_BUF_TYPE_VBI_OUTPUT:
@@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 	return -EINVAL;
 }
 
+static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
+			     struct file *file, void *fh, void *arg)
+{
+	struct v4l2_ext_pix_format *ef = arg;
+	struct v4l2_format f = {
+		.type = ef->type,
+	};
+	int ret = check_fmt(file, ef->type);
+
+	if (ret)
+		return ret;
+
+	memset(ef, 0, sizeof(*ef));
+	ef->type = f.type;
+
+	switch (f.type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (ops->vidioc_g_ext_pix_fmt_vid_cap)
+			return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		if (ops->vidioc_g_ext_pix_fmt_vid_out)
+			return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = v4l_g_fmt(ops, file, fh, &f);
+	if (ret)
+		return ret;
+
+	v4l2_format_to_ext_pix_format(&f, ef);
+	return 0;
+}
+
 static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
@@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
+		if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
+			     !ops->vidioc_s_ext_pix_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
-		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
+		ret = ops->vidioc_s_fmt_vid_cap ?
+		      ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
+		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
 		/* just in case the driver zeroed it again */
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		if (vfd->vfl_type == VFL_TYPE_TOUCH)
 			v4l_pix_format_touch(&p->fmt.pix);
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
+		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
+			     !ops->vidioc_s_ext_pix_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
 		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
 			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
 					  bytesperline);
-		return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
+		return ops->vidioc_s_fmt_vid_cap_mplane ?
+		       ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
+		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,  VIDIOC_S_FMT);
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 		if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
 			break;
@@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 		CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
 		return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		if (unlikely(!ops->vidioc_s_fmt_vid_out))
+		if (unlikely(!ops->vidioc_s_fmt_vid_out &&
+			     !ops->vidioc_s_ext_pix_fmt_vid_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
-		ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
+		ret = ops->vidioc_s_fmt_vid_out ?
+		      ops->vidioc_s_fmt_vid_out(file, fh, arg) :
+		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
 		/* just in case the driver zeroed it again */
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
+		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
+			     !ops->vidioc_s_ext_pix_fmt_vid_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
 		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
 			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
 					  bytesperline);
-		return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
+		return ops->vidioc_s_fmt_vid_out_mplane ?
+		       ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
+		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 		if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
 			break;
@@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 	return -EINVAL;
 }
 
+static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
+			     struct file *file, void *fh, void *arg)
+{
+	struct video_device *vfd = video_devdata(file);
+	struct v4l2_ext_pix_format *ef = arg;
+	struct v4l2_format f;
+	int ret = check_fmt(file, ef->type);
+
+	if (ret)
+		return ret;
+
+	memset(ef->reserved, 0, sizeof(ef->reserved));
+
+	switch (ef->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (ops->vidioc_s_ext_pix_fmt_vid_cap)
+			return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		if (ops->vidioc_s_ext_pix_fmt_vid_out)
+			return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
+
+	ret = v4l_s_fmt(ops, file, fh, &f);
+	if (ret)
+		/* TODO: retry with M-variant of ef->pixelformat? */
+		return ret;
+
+	v4l2_format_to_ext_pix_format(&f, ef);
+	return 0;
+}
+
 static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
@@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!ops->vidioc_try_fmt_vid_cap))
+		if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
+			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
-		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
+		ret = ops->vidioc_try_fmt_vid_cap ?
+		      ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
+		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
 		/* just in case the driver zeroed it again */
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		if (vfd->vfl_type == VFL_TYPE_TOUCH)
 			v4l_pix_format_touch(&p->fmt.pix);
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
+		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
+			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
 		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
 			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
 					  bytesperline);
-		return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
+		return ops->vidioc_try_fmt_vid_cap_mplane ?
+		       ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
+		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
+					     VIDIOC_TRY_FMT);
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 		if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
 			break;
@@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 		CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
 		return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		if (unlikely(!ops->vidioc_try_fmt_vid_out))
+		if (unlikely(!ops->vidioc_try_fmt_vid_out &&
+			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
-		ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
+		ret = ops->vidioc_try_fmt_vid_out ?
+		      ops->vidioc_try_fmt_vid_out(file, fh, arg) :
+		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
 		/* just in case the driver zeroed it again */
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
+		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
+			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
 		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
 			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
 					  bytesperline);
-		return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
+		return ops->vidioc_try_fmt_vid_out_mplane ?
+		       ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
+		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
+					     VIDIOC_TRY_FMT);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 		if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
 			break;
@@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 	return -EINVAL;
 }
 
+static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
+			       struct file *file, void *fh, void *arg)
+{
+	struct video_device *vfd = video_devdata(file);
+	struct v4l2_ext_pix_format *ef = arg;
+	struct v4l2_format f;
+	int ret = check_fmt(file, ef->type);
+
+	if (ret)
+		return ret;
+
+	memset(ef->reserved, 0, sizeof(ef->reserved));
+
+	switch (ef->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+		if (ops->vidioc_try_ext_pix_fmt_vid_cap)
+			return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
+								   ef);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+		if (ops->vidioc_try_ext_pix_fmt_vid_out)
+			return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
+								   ef);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
+
+	ret = v4l_try_fmt(ops, file, fh, &f);
+	if (ret)
+		/* TODO: retry with M-variant of ef->pixelformat? */
+		return ret;
+
+	v4l2_format_to_ext_pix_format(&f, ef);
+	return 0;
+}
+
 static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
@@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
 	IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
 	IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
 	IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
+	IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, v4l_print_ext_pix_format, 0),
+	IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, v4l_print_ext_pix_format, INFO_FL_PRIO),
+	IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, v4l_print_ext_pix_format, 0),
 };
 #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
 
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index edb733f21604..c44708dc9355 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -48,11 +48,17 @@ struct v4l2_fh;
  * @vidioc_g_fmt_vid_cap: pointer to the function that implements
  *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
  *	in single plane mode
+ * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that implements
+ *	:ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
+ *	capture
  * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
  *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
  * @vidioc_g_fmt_vid_out: pointer to the function that implements
  *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
  *	in single plane mode
+ * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that implements
+ *	:ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
+ *	out
  * @vidioc_g_fmt_vid_out_overlay: pointer to the function that implements
  *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
  * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
@@ -82,11 +88,16 @@ struct v4l2_fh;
  * @vidioc_s_fmt_vid_cap: pointer to the function that implements
  *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
  *	in single plane mode
+ * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that implements
+ *	:ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
+ *	capture
  * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
  *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
  * @vidioc_s_fmt_vid_out: pointer to the function that implements
  *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
  *	in single plane mode
+ * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that implements
+ *	:ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out
  * @vidioc_s_fmt_vid_out_overlay: pointer to the function that implements
  *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
  * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
@@ -116,11 +127,16 @@ struct v4l2_fh;
  * @vidioc_try_fmt_vid_cap: pointer to the function that implements
  *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video capture
  *	in single plane mode
+ * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that implements
+ *	:ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for
+	video capture
  * @vidioc_try_fmt_vid_overlay: pointer to the function that implements
  *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
  * @vidioc_try_fmt_vid_out: pointer to the function that implements
  *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
  *	in single plane mode
+ * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that implements
+ *	:ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out
  * @vidioc_try_fmt_vid_out_overlay: pointer to the function that implements
  *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
  *	output
@@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
 	/* VIDIOC_G_FMT handlers */
 	int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
 				    struct v4l2_format *f);
+	int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
+					    struct v4l2_ext_pix_format *ef);
 	int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
 					struct v4l2_format *f);
 	int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
 				    struct v4l2_format *f);
+	int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
+					    struct v4l2_ext_pix_format *ef);
 	int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
 					    struct v4l2_format *f);
 	int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
@@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
 	/* VIDIOC_S_FMT handlers */
 	int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
 				    struct v4l2_format *f);
+	int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
+					    struct v4l2_ext_pix_format *ef);
 	int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
 					struct v4l2_format *f);
 	int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
 				    struct v4l2_format *f);
+	int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
+					    struct v4l2_ext_pix_format *ef);
 	int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
 					    struct v4l2_format *f);
 	int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
@@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
 	/* VIDIOC_TRY_FMT handlers */
 	int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
 				      struct v4l2_format *f);
+	int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
+					      struct v4l2_ext_pix_format *ef);
 	int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
 					  struct v4l2_format *f);
 	int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
 				      struct v4l2_format *f);
+	int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
+					      struct v4l2_ext_pix_format *ef);
 	int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
 					     struct v4l2_format *f);
 	int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index d9b7c9177605..a2d850513708 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
 	__u8				reserved[7];
 } __attribute__ ((packed));
 
+/**
+ * struct v4l2_ext_pix_format - extended single/multiplanar format definition
+ * @type:		type of the data stream; V4L2_BUF_TYPE_VIDEO_CAPTURE or
+ *			V4L2_BUF_TYPE_VIDEO_OUTPUT
+ * @width:		image width in pixels
+ * @height:		image height in pixels
+ * @field:		enum v4l2_field; field order (for interlaced video)
+ * @plane_fmt:		per-plane information
+ * @pixelformat:	little endian four character code (fourcc)
+ * @modifier:		modifier applied to the format (used for tiled formats
+ *			and other kind of HW-specific formats, like compressed
+ *			formats) as defined in drm_fourcc.h
+ * @colorspace:		enum v4l2_colorspace; supplemental to pixelformat
+ * @xfer_func:		enum v4l2_xfer_func, colorspace transfer function
+ * @ycbcr_enc:		enum v4l2_ycbcr_encoding, Y'CbCr encoding
+ * @hsv_enc:		enum v4l2_hsv_encoding, HSV encoding
+ * @quantization:	enum v4l2_quantization, colorspace quantization
+ * @reserved:		extra space reserved for future fields, must be set to 0
+ */
+struct v4l2_ext_pix_format {
+	__u32 type;
+	__u32 width;
+	__u32 height;
+	__u32 field;
+	struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
+	__u32 pixelformat;
+	__u64 modifier;
+	__u32 colorspace;
+	__u32 xfer_func;
+	union {
+		__u32 ycbcr_enc;
+		__u32 hsv_enc;
+	};
+	__u32 quantization;
+	__u32 reserved[9];
+};
+
 /**
  * struct v4l2_sdr_format - SDR format definition
  * @pixelformat:	little endian four character code (fourcc)
@@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
 
 #define VIDIOC_QUERY_EXT_CTRL	_IOWR('V', 103, struct v4l2_query_ext_ctrl)
 
+#define VIDIOC_G_EXT_PIX_FMT	_IOWR('V', 104, struct v4l2_ext_pix_format)
+#define VIDIOC_S_EXT_PIX_FMT	_IOWR('V', 105, struct v4l2_ext_pix_format)
+#define VIDIOC_TRY_EXT_PIX_FMT	_IOWR('V', 106, struct v4l2_ext_pix_format)
+
 /* Reminder: when adding new ioctls please add support for them to
    drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
 
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 01/11] media: v4l2-common: add normalized pixelformat field to struct v4l2_format_info Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more) Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-02-23 12:58   ` Hans Verkuil
       [not found]   ` <20230125200026.16643-1-ayaka@soulik.info>
  2021-01-14 18:07 ` [RFC PATCH v6 04/11] media: videobuf2-v4l2: reorganize flags handling Helen Koike
                   ` (8 subsequent siblings)
  11 siblings, 2 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Those extended buffer ops have several purpose:
1/ Fix y2038 issues by converting the timestamp into an u64 counting
   the number of ns elapsed since 1970
2/ Unify single/multiplanar handling
3/ Add a new start offset field to each v4l2 plane buffer info struct
   to support the case where a single buffer object is storing all
   planes data, each one being placed at a different offset

New hooks are created in v4l2_ioctl_ops so that drivers can start using
these new objects.

Note that the timecode field is gone, since there doesn't seem to be
in-kernel users. We can be added back in the reserved area if needed or
use the Request API to collect more metadata information from the
frame.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Helen Koike <helen.koike@collabora.com>
---

Changes in v6:
This patch was completely refactored, and based on previous version from
Hans and Boris.
- Refactor conversions v4l2_buffer <-> v4l2_ext_buffer for (d)qbuf
- I removed EXT_CREATE_BUFS since it is basically only usefull to MMAP.
  If this is going towards DMA-fd centric, then we can use the current
  REQUESTBUF to switch to it, and we can think a better way to support
  MMAP later if there are usecases.
  I also moved memory field from v4l2_ext_plane to v4l2_ext_buffer,
  since it is very unlikely to mix memory types, and REQUESTBUF can
  switch the whole buffer object to a given type.
- I removed EXT_QUERYBUF, since it is only useful to MMAP, for the
  same reason above.
- I removed EXT_PREPARE_BUF, since it is basically just an optimization,
  we can add it later (my intention is to simplify this patchset).
- These ioctls are only valid for video types (and not for overlay,
  vbi, touch, meta, etc).
- Refactor struct v4l2_ext_buffer and struct v4l2_ext_planes as
  discussed with Tomasz:
	- add bytesused back
	- remove lenght field
	- move memory field from planes to buffer object
- Fix order in documentation of struct v4l2_ext_buffer (Tomasz)
- Fix flags documentation of struct v4l2_ext_buffer, don't say when flags are ignored (Tomasz)
- v4l_print_ext_buffer(): print request_fd and offset/userptr (Tomasz)

Changes in v5:
- migrate memory from v4l2_ext_buffer to v4l2_ext_plane
- return mem_offset to struct v4l2_ext_plane
- change sizes and reorder fields to avoid holes in the struct and make
  it the same for 32 and 64 bits

Changes in v4:
- Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
- Drop VIDIOC_EXT_EXPBUF, since the only difference from VIDIOC_EXPBUF
was that with VIDIOC_EXT_EXPBUF we could export multiple planes at once.
I think we can add this later, so I removed it from this RFC to simplify it.
- Remove num_planes field from struct v4l2_ext_buffer
- Add flags field to struct v4l2_ext_create_buffers
- Reformulate struct v4l2_ext_plane
- Fix some bugs caught by v4l2-compliance
- Rebased on top of media/master (post 5.8-rc1)

Changes in v3:
- Rebased on top of media/master (post 5.4-rc1)

Changes in v2:
- Add reserved space to v4l2_ext_buffer so that new fields can be added
  later on

Signed-off-by: Helen Koike <helen.koike@collabora.com>
---
 drivers/media/v4l2-core/v4l2-dev.c   |   4 +
 drivers/media/v4l2-core/v4l2-ioctl.c | 184 +++++++++++++++++++++++++++
 include/media/v4l2-ioctl.h           |   8 ++
 include/uapi/linux/videodev2.h       |  55 ++++++++
 4 files changed, 251 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 5add58cb6d45..94c9f1e04704 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -664,6 +664,10 @@ static void determine_valid_ioctls(struct video_device *vdev)
 			set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
 		SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
 		SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
+		SET_VALID_IOCTL(ops, VIDIOC_EXT_QBUF, vidioc_ext_qbuf);
+		SET_VALID_IOCTL(ops, VIDIOC_EXT_QBUF, vidioc_qbuf);
+		SET_VALID_IOCTL(ops, VIDIOC_EXT_DQBUF, vidioc_ext_dqbuf);
+		SET_VALID_IOCTL(ops, VIDIOC_EXT_DQBUF, vidioc_dqbuf);
 	}
 	if (is_meta && is_rx) {
 		/* metadata capture specific ioctls */
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index a9c07c0a73ec..ba633e7efd6d 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -533,6 +533,24 @@ static void v4l_print_buffer(const void *arg, bool write_only)
 			tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
 }
 
+static void v4l_print_ext_buffer(const void *arg, bool write_only)
+{
+	const struct v4l2_ext_buffer *e = arg;
+	unsigned int i;
+
+	pr_cont("%lld index=%d, type=%s, request_fd=%d, flags=0x%08llx, field=%s, sequence=%d, memory=%s\n",
+		e->timestamp, e->index, prt_names(e->type, v4l2_type_names),
+		e->request_fd, e->flags, prt_names(e->field, v4l2_field_names),
+		e->sequence, prt_names(e->memory, v4l2_memory_names));
+
+	for (i = 0; i < VIDEO_MAX_PLANES && e->planes[i].m.userptr; i++) {
+		const struct v4l2_ext_plane *plane = &e->planes[i];
+
+		pr_cont("plane %d: bytesused=%d, offset=0x%08x, userptr=0x%llx\n",
+			i, plane->bytesused, plane->offset, plane->m.userptr);
+	}
+}
+
 static void v4l_print_exportbuffer(const void *arg, bool write_only)
 {
 	const struct v4l2_exportbuffer *p = arg;
@@ -2552,6 +2570,130 @@ static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
 	return ret ? ret : ops->vidioc_qbuf(file, fh, p);
 }
 
+static bool v4l2_ext_buffer_is_single_membuf(const struct v4l2_ext_buffer *eb)
+{
+	unsigned int i;
+
+	for (i = 1; i < VIDEO_MAX_PLANES && eb->planes[i].m.userptr; i++)
+		if (eb->planes[i].m.userptr != eb->planes[i - 1].m.userptr)
+			return false;
+	return true;
+}
+
+static int v4l2_fill_buffer_from_ext_buffer(const struct v4l2_ioctl_ops *ops,
+					    struct file *file, void *fh,
+					    const struct v4l2_ext_buffer *eb,
+					    struct v4l2_buffer *b,
+					    struct v4l2_plane *bplanes)
+{
+	const struct v4l2_ext_plane *eplanes = (struct v4l2_ext_plane *)&eb->planes;
+	struct video_device *vfd = video_devdata(file);
+	bool is_mplane = V4L2_IS_CAP_MULTIPLANAR(vfd);
+	unsigned int i;
+	u64 nsecs;
+	int ret;
+
+	b->index = eb->index;
+	if (is_mplane) {
+		b->m.planes = bplanes;
+		b->length = VIDEO_MAX_PLANES;
+		if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			b->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		else if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+			b->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+	} else {
+		b->type = eb->type;
+	}
+
+	/* Fill the rest of the v4l2_buffer */
+	ret = v4l_querybuf(ops, file, fh, b);
+	if (ret)
+		return ret;
+
+	/* Fill other fields from v4l2_ext_buffer */
+	b->flags = eb->flags;
+	b->field = eb->field;
+	b->timestamp.tv_sec = div64_u64_rem(eb->timestamp, NSEC_PER_SEC, &nsecs);
+	b->timestamp.tv_usec = (u32)nsecs / NSEC_PER_USEC;
+	b->sequence = eb->sequence;
+
+	if (!is_mplane) {
+		for (i = 0; i < VIDEO_MAX_PLANES; i++) {
+			b->bytesused += eplanes[i].bytesused;
+			WARN_ON(eplanes[i].offset);
+		}
+
+		/* MMAP info was filled by querybuf */
+		if (b->memory == V4L2_MEMORY_MMAP)
+			return 0 ;
+
+		/*
+		 * TODO: get the length of the buffer, for now, just
+		 * set to max to avoid errors in checks.
+		 */
+		b->length = U32_MAX;
+		b->m.userptr = eplanes[0].m.userptr;
+		return 0;
+	}
+
+	bplanes[0].bytesused = eplanes[0].bytesused + eplanes[0].offset;
+	bplanes[0].data_offset = eplanes[0].offset;
+	if (v4l2_ext_buffer_is_single_membuf(eb))
+		for (i = 1; i < VIDEO_MAX_PLANES && eplanes[i].bytesused; i++) {
+			bplanes[0].bytesused += eplanes[i].bytesused;
+			WARN_ON(eplanes[i].offset);
+		}
+	else
+		for (i = 1; i < VIDEO_MAX_PLANES && eplanes[i].bytesused; i++) {
+			bplanes[i].bytesused = eplanes[i].bytesused +
+					       eplanes[i].offset;
+			bplanes[i].data_offset = eplanes[i].offset;
+		}
+
+	/* MMAP info was filled by querybuf */
+	if (b->memory == V4L2_MEMORY_MMAP)
+		return 0;
+
+	for (i = 0; i < VIDEO_MAX_PLANES && eplanes[i].m.userptr; i++) {
+		bplanes[i].m.userptr = eplanes[i].m.userptr;
+		/*
+		 * TODO: get the length of the buffer, for now, just
+		 * set to max to avoid errors in checks.
+		 */
+		bplanes[i].length = U32_MAX;
+	}
+	return 0;
+}
+
+static int v4l_ext_qbuf(const struct v4l2_ioctl_ops *ops,
+			struct file *file, void *fh, void *arg)
+{
+	struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
+	struct v4l2_ext_buffer *eb = arg;
+	struct v4l2_buffer b = {0};
+
+	int ret = check_fmt(file, eb->type);
+
+	if (!ret)
+		return ret;
+
+	if (ops->vidioc_ext_qbuf)
+		return ops->vidioc_ext_qbuf(file, fh, eb);
+
+	/* Fill other fields from v4l2_ext_buffer */
+	ret = v4l2_fill_buffer_from_ext_buffer(ops, file, fh, eb, &b, planes);
+	if (ret)
+		return ret;
+
+	ret = v4l_qbuf(ops, file, fh, &b);
+	if (ret)
+		return ret;
+
+	/* TODO: check if we need to fill other fields */
+	eb->flags = b.flags;
+	return 0;
+}
+
 static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
@@ -2561,6 +2703,46 @@ static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
 	return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
 }
 
+static int v4l_ext_dqbuf(const struct v4l2_ioctl_ops *ops,
+			 struct file *file, void *fh, void *arg)
+{
+	struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
+	struct v4l2_ext_buffer *eb = arg;
+	struct v4l2_buffer b = {0};
+	unsigned int i;
+
+	int ret = check_fmt(file, eb->type);
+
+	if (!ret)
+		return ret;
+
+	if (ops->vidioc_ext_qbuf)
+		return ops->vidioc_ext_qbuf(file, fh, eb);
+
+	/* Fill other fields from v4l2_ext_buffer */
+	ret = v4l2_fill_buffer_from_ext_buffer(ops, file, fh, eb, &b, planes);
+	if (ret)
+		return ret;
+
+	ret = v4l_qbuf(ops, file, fh, &b);
+	if (ret)
+		return ret;
+
+	/* TODO: check if we need to fill other fields */
+	eb->flags = b.flags;
+
+	/*
+	 * Set buffer pointers to zero. Usecase: DMA-fd might have being
+	 * alread closed, so just request userspace to fill it again in queue
+	 * time.
+	 */
+	for (i = 0; i < VIDEO_MAX_PLANES && eb->planes[i].m.userptr; i++) {
+		eb->planes[i].m.userptr = 0;
+	}
+
+	return 0;
+}
+
 static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
@@ -3340,6 +3522,8 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
 	IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, v4l_print_ext_pix_format, 0),
 	IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, v4l_print_ext_pix_format, INFO_FL_PRIO),
 	IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, v4l_print_ext_pix_format, 0),
+	IOCTL_INFO(VIDIOC_EXT_QBUF, v4l_ext_qbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
+	IOCTL_INFO(VIDIOC_EXT_DQBUF, v4l_ext_dqbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
 };
 #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
 
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index c44708dc9355..1d0ed36e5e67 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -171,10 +171,14 @@ struct v4l2_fh;
  *	:ref:`VIDIOC_QUERYBUF <vidioc_querybuf>` ioctl
  * @vidioc_qbuf: pointer to the function that implements
  *	:ref:`VIDIOC_QBUF <vidioc_qbuf>` ioctl
+ * @vidioc_ext_qbuf: pointer to the function that implements
+ *	:ref:`VIDIOC_EXT_QBUF <vidioc_ext_qbuf>` ioctl
  * @vidioc_expbuf: pointer to the function that implements
  *	:ref:`VIDIOC_EXPBUF <vidioc_expbuf>` ioctl
  * @vidioc_dqbuf: pointer to the function that implements
  *	:ref:`VIDIOC_DQBUF <vidioc_qbuf>` ioctl
+ * @vidioc_ext_dqbuf: pointer to the function that implements
+ *	:ref:`VIDIOC_EXT_DQBUF <vidioc_ext_qbuf>` ioctl
  * @vidioc_create_bufs: pointer to the function that implements
  *	:ref:`VIDIOC_CREATE_BUFS <vidioc_create_bufs>` ioctl
  * @vidioc_prepare_buf: pointer to the function that implements
@@ -441,10 +445,14 @@ struct v4l2_ioctl_ops {
 			       struct v4l2_buffer *b);
 	int (*vidioc_qbuf)(struct file *file, void *fh,
 			   struct v4l2_buffer *b);
+	int (*vidioc_ext_qbuf)(struct file *file, void *fh,
+			       struct v4l2_ext_buffer *b);
 	int (*vidioc_expbuf)(struct file *file, void *fh,
 			     struct v4l2_exportbuffer *e);
 	int (*vidioc_dqbuf)(struct file *file, void *fh,
 			    struct v4l2_buffer *b);
+	int (*vidioc_ext_dqbuf)(struct file *file, void *fh,
+				struct v4l2_ext_buffer *b);
 
 	int (*vidioc_create_bufs)(struct file *file, void *fh,
 				  struct v4l2_create_buffers *b);
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index a2d850513708..508ac11645bc 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -1056,6 +1056,59 @@ struct v4l2_buffer {
 	};
 };
 
+/**
+ * struct v4l2_ext_plane - extended plane buffer info
+ * @offset:		offset in the memory buffer where the plane starts.
+ * @bytesused:		number of bytes occupied by data in the plane (payload).
+ * @mmap_offset:	If V4L2_MEMORY_MMAP is used, then it can be a "cookie"
+ *			that should be passed to mmap() called on the video node.
+ * @userptr:		when memory is V4L2_MEMORY_USERPTR, a userspace pointer pointing
+ *			to this plane.
+ * @dmabuf_fd:		when memory is V4L2_MEMORY_DMABUF, a userspace file descriptor
+ *			associated with this plane.
+ * @reserved:		extra space reserved for future fields, must be set to 0.
+ */
+struct v4l2_ext_plane {
+	__u32 offset;
+	__u32 bytesused;
+	union {
+		__u32 mmap_offset;
+		__u64 userptr;
+		__s32 dmabuf_fd;
+	} m;
+	__u32 reserved[6];
+};
+
+/**
+ * struct v4l2_ext_buffer - extended video buffer info
+ * @index:	id number of the buffer
+ * @type:	V4L2_BUF_TYPE_VIDEO_CAPTURE or V4L2_BUF_TYPE_VIDEO_OUTPUT
+ * @field:	enum v4l2_field; field order of the image in the buffer
+ * @sequence:	sequence count of this frame
+ * @flags:	buffer informational flags
+ * @timestamp:	frame timestamp
+ * @memory:	enum v4l2_memory; the method, in which the actual video
+ *		data is passed
+ * @request_fd:	fd of the request that this buffer should use
+ * @planes:	per-plane buffer information
+ * @reserved:	extra space reserved for future fields, must be set to 0
+ *
+ * Contains data exchanged by application and driver using one of the Streaming
+ * I/O methods.
+ */
+struct v4l2_ext_buffer {
+	__u32 index;
+	__u32 type;
+	__u32 field;
+	__u32 sequence;
+	__u64 flags;
+	__u64 timestamp;
+	__u32 memory;
+	__s32 request_fd;
+	struct v4l2_ext_plane planes[VIDEO_MAX_PLANES];
+	__u32 reserved[10];
+};
+
 #ifndef __KERNEL__
 /**
  * v4l2_timeval_to_ns - Convert timeval to nanoseconds
@@ -2623,6 +2676,8 @@ struct v4l2_create_buffers {
 #define VIDIOC_G_EXT_PIX_FMT	_IOWR('V', 104, struct v4l2_ext_pix_format)
 #define VIDIOC_S_EXT_PIX_FMT	_IOWR('V', 105, struct v4l2_ext_pix_format)
 #define VIDIOC_TRY_EXT_PIX_FMT	_IOWR('V', 106, struct v4l2_ext_pix_format)
+#define VIDIOC_EXT_QBUF		_IOWR('V', 107, struct v4l2_ext_buffer)
+#define VIDIOC_EXT_DQBUF	_IOWR('V', 118, struct v4l2_ext_buffer)
 
 /* Reminder: when adding new ioctls please add support for them to
    drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 04/11] media: videobuf2-v4l2: reorganize flags handling
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
                   ` (2 preceding siblings ...)
  2021-01-14 18:07 ` [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 05/11] media: videobuf2: Expose helpers for Ext qbuf/dqbuf Helen Koike
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Reorganize flags handling to be easily reuseble when Ext functions get
added.
No logic is changed, just moving around code.

- Two new functions:
	v4l2_clear_buffer_flags()
	vb2_fill_vb2_v4l2_buffer_flags()
- set_buffer_cache_hints() receives a pointer to flags instead of the
  v4l2_buffer object, making it undependent of this struct.

Signed-off-by: Helen Koike <helen.koike@collabora.com>

---
Changes in v6:
- New patch
---
 .../media/common/videobuf2/videobuf2-v4l2.c   | 176 ++++++++++--------
 1 file changed, 97 insertions(+), 79 deletions(-)

diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index bb642c0775d1..aa4f17ec0982 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -174,6 +174,43 @@ static void vb2_warn_zero_bytesused(struct vb2_buffer *vb)
 		pr_warn("use the actual size instead.\n");
 }
 
+static void
+vb2_fill_vb2_v4l2_buffer_flags(struct vb2_buffer *vb,
+			       u32 type, u32 field, u32 flags)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+	/* Zero flags that we handle */
+	vbuf->flags = flags & ~V4L2_BUFFER_MASK_FLAGS;
+	if (!vb->vb2_queue->copy_timestamp || V4L2_TYPE_IS_CAPTURE(type)) {
+		/*
+		 * Non-COPY timestamps and non-OUTPUT queues will get
+		 * their timestamp and timestamp source flags from the
+		 * queue.
+		 */
+		vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+	}
+
+	if (V4L2_TYPE_IS_OUTPUT(type)) {
+		/*
+		 * For output buffers mask out the timecode flag:
+		 * this will be handled later in vb2_qbuf().
+		 * The 'field' is valid metadata for this output buffer
+		 * and so that needs to be copied here.
+		 */
+		vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
+		vbuf->field = field;
+		if (!(q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
+			vbuf->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
+	} else {
+		/* Zero any output buffer flags as this is a capture buffer */
+		vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
+		/* Zero last flag, this is a signal from driver to userspace */
+		vbuf->flags &= ~V4L2_BUF_FLAG_LAST;
+	}
+}
+
 static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
 {
 	struct vb2_queue *q = vb->vb2_queue;
@@ -310,41 +347,14 @@ static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b
 
 	}
 
-	/* Zero flags that we handle */
-	vbuf->flags = b->flags & ~V4L2_BUFFER_MASK_FLAGS;
-	if (!vb->vb2_queue->copy_timestamp || V4L2_TYPE_IS_CAPTURE(b->type)) {
-		/*
-		 * Non-COPY timestamps and non-OUTPUT queues will get
-		 * their timestamp and timestamp source flags from the
-		 * queue.
-		 */
-		vbuf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
-	}
-
-	if (V4L2_TYPE_IS_OUTPUT(b->type)) {
-		/*
-		 * For output buffers mask out the timecode flag:
-		 * this will be handled later in vb2_qbuf().
-		 * The 'field' is valid metadata for this output buffer
-		 * and so that needs to be copied here.
-		 */
-		vbuf->flags &= ~V4L2_BUF_FLAG_TIMECODE;
-		vbuf->field = b->field;
-		if (!(q->subsystem_flags & VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF))
-			vbuf->flags &= ~V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
-	} else {
-		/* Zero any output buffer flags as this is a capture buffer */
-		vbuf->flags &= ~V4L2_BUFFER_OUT_FLAGS;
-		/* Zero last flag, this is a signal from driver to userspace */
-		vbuf->flags &= ~V4L2_BUF_FLAG_LAST;
-	}
+	vb2_fill_vb2_v4l2_buffer_flags(vb, b->type, b->field, b->flags);
 
 	return 0;
 }
 
 static void set_buffer_cache_hints(struct vb2_queue *q,
 				   struct vb2_buffer *vb,
-				   struct v4l2_buffer *b)
+				   u32 *flags)
 {
 	/*
 	 * DMA exporter should take care of cache syncs, so we can avoid
@@ -370,8 +380,8 @@ static void set_buffer_cache_hints(struct vb2_queue *q,
 		 * space hints. That's to indicate to userspace that these
 		 * flags won't work.
 		 */
-		b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_INVALIDATE;
-		b->flags &= ~V4L2_BUF_FLAG_NO_CACHE_CLEAN;
+		*flags &= ~V4L2_BUF_FLAG_NO_CACHE_INVALIDATE;
+		*flags &= ~V4L2_BUF_FLAG_NO_CACHE_CLEAN;
 		return;
 	}
 
@@ -382,10 +392,10 @@ static void set_buffer_cache_hints(struct vb2_queue *q,
 	if (q->dma_dir == DMA_TO_DEVICE)
 		vb->need_cache_sync_on_finish = 0;
 
-	if (b->flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE)
+	if (*flags & V4L2_BUF_FLAG_NO_CACHE_INVALIDATE)
 		vb->need_cache_sync_on_finish = 0;
 
-	if (b->flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN)
+	if (*flags & V4L2_BUF_FLAG_NO_CACHE_CLEAN)
 		vb->need_cache_sync_on_prepare = 0;
 }
 
@@ -433,7 +443,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
 	}
 
 	if (!vb->prepared) {
-		set_buffer_cache_hints(q, vb, b);
+		set_buffer_cache_hints(q, vb, &b->flags);
 		/* Copy relevant information provided by the userspace */
 		memset(vbuf->planes, 0,
 		       sizeof(vbuf->planes[0]) * vb->num_planes);
@@ -511,6 +521,58 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
 	return 0;
 }
 
+/*
+ * Clear any buffer state related flags.
+ */
+static void v4l2_clear_buffer_flags(struct vb2_buffer *vb, u32 *flags,
+				    struct vb2_queue *q,
+				    struct vb2_v4l2_buffer *vbuf,
+				    u32* request_fd)
+{
+	*flags &= ~V4L2_BUFFER_MASK_FLAGS;
+	*flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK;
+	if (!q->copy_timestamp) {
+		/*
+		 * For non-COPY timestamps, drop timestamp source bits
+		 * and obtain the timestamp source from the queue.
+		 */
+		*flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+		*flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+	}
+
+	switch (vb->state) {
+	case VB2_BUF_STATE_QUEUED:
+	case VB2_BUF_STATE_ACTIVE:
+		*flags |= V4L2_BUF_FLAG_QUEUED;
+		break;
+	case VB2_BUF_STATE_IN_REQUEST:
+		*flags |= V4L2_BUF_FLAG_IN_REQUEST;
+		break;
+	case VB2_BUF_STATE_ERROR:
+		*flags |= V4L2_BUF_FLAG_ERROR;
+		fallthrough;
+	case VB2_BUF_STATE_DONE:
+		*flags |= V4L2_BUF_FLAG_DONE;
+		break;
+	case VB2_BUF_STATE_PREPARING:
+	case VB2_BUF_STATE_DEQUEUED:
+		/* nothing */
+		break;
+	}
+
+	if ((vb->state == VB2_BUF_STATE_DEQUEUED ||
+	     vb->state == VB2_BUF_STATE_IN_REQUEST) &&
+	    vb->synced && vb->prepared)
+		*flags |= V4L2_BUF_FLAG_PREPARED;
+
+	if (vb2_buffer_in_use(q, vb))
+		*flags |= V4L2_BUF_FLAG_MAPPED;
+	if (vbuf->request_fd >= 0) {
+		*flags |= V4L2_BUF_FLAG_REQUEST_FD;
+		*request_fd = vbuf->request_fd;
+	}
+}
+
 /*
  * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
  * returned to userspace
@@ -572,51 +634,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
 			b->m.fd = vb->planes[0].m.fd;
 	}
 
-	/*
-	 * Clear any buffer state related flags.
-	 */
-	b->flags &= ~V4L2_BUFFER_MASK_FLAGS;
-	b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK;
-	if (!q->copy_timestamp) {
-		/*
-		 * For non-COPY timestamps, drop timestamp source bits
-		 * and obtain the timestamp source from the queue.
-		 */
-		b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
-		b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
-	}
-
-	switch (vb->state) {
-	case VB2_BUF_STATE_QUEUED:
-	case VB2_BUF_STATE_ACTIVE:
-		b->flags |= V4L2_BUF_FLAG_QUEUED;
-		break;
-	case VB2_BUF_STATE_IN_REQUEST:
-		b->flags |= V4L2_BUF_FLAG_IN_REQUEST;
-		break;
-	case VB2_BUF_STATE_ERROR:
-		b->flags |= V4L2_BUF_FLAG_ERROR;
-		fallthrough;
-	case VB2_BUF_STATE_DONE:
-		b->flags |= V4L2_BUF_FLAG_DONE;
-		break;
-	case VB2_BUF_STATE_PREPARING:
-	case VB2_BUF_STATE_DEQUEUED:
-		/* nothing */
-		break;
-	}
-
-	if ((vb->state == VB2_BUF_STATE_DEQUEUED ||
-	     vb->state == VB2_BUF_STATE_IN_REQUEST) &&
-	    vb->synced && vb->prepared)
-		b->flags |= V4L2_BUF_FLAG_PREPARED;
-
-	if (vb2_buffer_in_use(q, vb))
-		b->flags |= V4L2_BUF_FLAG_MAPPED;
-	if (vbuf->request_fd >= 0) {
-		b->flags |= V4L2_BUF_FLAG_REQUEST_FD;
-		b->request_fd = vbuf->request_fd;
-	}
+	v4l2_clear_buffer_flags(vb, &b->flags, q, vbuf, &b->request_fd);
 }
 
 /*
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 05/11] media: videobuf2: Expose helpers for Ext qbuf/dqbuf
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
                   ` (3 preceding siblings ...)
  2021-01-14 18:07 ` [RFC PATCH v6 04/11] media: videobuf2-v4l2: reorganize flags handling Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 06/11] media: vivid: use vb2_ioctls_ext_{d}qbuf hooks Helen Koike
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

To overcome the limitations of Ext ioctls, that is being converted to
classic hooks, add helpers to allow applications support layouts such as
using the same buffer with planes in different offsets.

To use the new hooks, drivers should:

 static const struct v4l2_ioctl_ops ioctl_ops = {
 ...
 +	.vidioc_ext_qbuf = vb2_ioctl_ext_qbuf,
 +	.vidioc_ext_dqbuf = vb2_ioctl_ext_dqbuf,
 ...
 }

 +	vb2_set_pixelformat(dev->pixelformat)

The old hooks should be kept to keep the driver compatible with classic
Api.

Add mem_offset field to struct vb2_plane, to allow tracking where the plane
starts in a buffer, as defined from userspace.
When returning the buffer to userspace, this offset can be adjusted
depending on the data_offset returned from the driver.

Add pixelformat field to struct vb2_buffer, to allow vb2 to know how to
decompose the payload set with vb2_set_plane_payload() into color planes
when a single memory buffer is used.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Helen Koike <helen.koike@collabora.com>

---
Changes in v6:
This patch is based on original "media: videobuf2: Expose helpers to implement the _ext_fmt and _ext_buf hooks"

- This patch was completly refactored
- Conversions from v4l2_buffer to v4l2_ext_buffer was removed from vb2.
  Both v4l2_buffer and v4l2_ext_buffer need to be supported, since Ext is only valid
  for video devices, v4l2_buffer needs to be supported for vbi, meta, and others.
- Zero v4l2_ext_buffer.planes.m field (Tomasz for DMA-fd)

Changes in v5:
- Update with new format and buffer structs
- Updated commit message with the uAPI prefix

Changes in v4:
- Update with new format and buffer structs
- Fix some bugs caught by v4l2-compliance
- Rebased on top of media/master (post 5.8-rc1)

Changes in v3:
- Rebased on top of media/master (post 5.4-rc1)

Changes in v2:
- New patch
---
 .../media/common/videobuf2/videobuf2-core.c   |  46 ++-
 .../media/common/videobuf2/videobuf2-v4l2.c   | 326 +++++++++++++++++-
 include/media/videobuf2-core.h                |  33 +-
 include/media/videobuf2-v4l2.h                |   8 +-
 4 files changed, 388 insertions(+), 25 deletions(-)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index 02281d13505f..b034bd525ea4 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -1569,8 +1569,10 @@ static int vb2_start_streaming(struct vb2_queue *q)
 	return ret;
 }
 
-int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
-		  struct media_request *req)
+static int
+vb2_core_qbuf_common(struct vb2_queue *q, unsigned int index,
+		     struct v4l2_buffer *pb, struct v4l2_ext_buffer *eb,
+		     struct media_request *req)
 {
 	struct vb2_buffer *vb;
 	int ret;
@@ -1642,6 +1644,9 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
 		if (pb) {
 			call_void_bufop(q, copy_timestamp, vb, pb);
 			call_void_bufop(q, fill_user_buffer, vb, pb);
+		} else if (eb) {
+			call_void_bufop(q, copy_timestamp_ext, vb, eb);
+			call_void_bufop(q, fill_user_ext_buffer, vb, eb);
 		}
 
 		dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index);
@@ -1680,6 +1685,8 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
 
 	if (pb)
 		call_void_bufop(q, copy_timestamp, vb, pb);
+	else if (eb)
+		call_void_bufop(q, copy_timestamp_ext, vb, eb);
 
 	trace_vb2_qbuf(q, vb);
 
@@ -1693,6 +1700,8 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
 	/* Fill buffer information for the userspace */
 	if (pb)
 		call_void_bufop(q, fill_user_buffer, vb, pb);
+	else if (eb)
+		call_void_bufop(q, fill_user_ext_buffer, vb, eb);
 
 	/*
 	 * If streamon has been called, and we haven't yet called
@@ -1710,8 +1719,21 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
 	dprintk(q, 2, "qbuf of buffer %d succeeded\n", vb->index);
 	return 0;
 }
+
+int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
+		  struct media_request *req)
+{
+	return vb2_core_qbuf_common(q, index, pb, NULL, req);
+}
 EXPORT_SYMBOL_GPL(vb2_core_qbuf);
 
+int vb2_core_ext_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
+		      struct media_request *req)
+{
+	return vb2_core_qbuf_common(q, index, NULL, pb, req);
+}
+EXPORT_SYMBOL_GPL(vb2_core_ext_qbuf);
+
 /*
  * __vb2_wait_for_done_vb() - wait for a buffer to become available
  * for dequeuing
@@ -1861,8 +1883,10 @@ static void __vb2_dqbuf(struct vb2_buffer *vb)
 	call_void_bufop(q, init_buffer, vb);
 }
 
-int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
-		   bool nonblocking)
+static int
+vb2_core_dqbuf_common(struct vb2_queue *q, unsigned int *pindex,
+		      struct v4l2_buffer *pb, struct v4l2_ext_buffer *eb,
+		      bool nonblocking)
 {
 	struct vb2_buffer *vb = NULL;
 	int ret;
@@ -1893,6 +1917,8 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
 	/* Fill buffer information for the userspace */
 	if (pb)
 		call_void_bufop(q, fill_user_buffer, vb, pb);
+	else if (eb)
+		call_void_bufop(q, fill_user_ext_buffer, vb, eb);
 
 	/* Remove from videobuf queue */
 	list_del(&vb->queued_entry);
@@ -1915,10 +1941,22 @@ int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
 		vb->index, vb2_state_name(vb->state));
 
 	return 0;
+}
 
+int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
+		   bool nonblocking)
+{
+	return vb2_core_dqbuf_common(q, pindex, pb, NULL, nonblocking);
 }
 EXPORT_SYMBOL_GPL(vb2_core_dqbuf);
 
+int vb2_core_ext_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
+		   bool nonblocking)
+{
+	return vb2_core_dqbuf_common(q, pindex, NULL, pb, nonblocking);
+}
+EXPORT_SYMBOL_GPL(vb2_core_ext_dqbuf);
+
 /*
  * __vb2_queue_cancel() - cancel and stop (pause) streaming
  *
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index aa4f17ec0982..0b0e4fe41f0a 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -158,6 +158,23 @@ static void __copy_timestamp(struct vb2_buffer *vb, const void *pb)
 	}
 };
 
+static void __copy_timestamp_ext(struct vb2_buffer *vb, const void *pb)
+{
+	const struct v4l2_ext_buffer *eb = pb;
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct vb2_queue *q = vb->vb2_queue;
+
+	if (q->is_output) {
+		/*
+		 * For output buffers copy the timestamp if needed,
+		 * and the timecode field and flag if needed.
+		 */
+		if (q->copy_timestamp)
+			vb->timestamp = eb->timestamp;
+		vbuf->flags |= eb->flags & V4L2_BUF_FLAG_TIMECODE;
+	}
+};
+
 static void vb2_warn_zero_bytesused(struct vb2_buffer *vb)
 {
 	static bool check_once;
@@ -211,6 +228,68 @@ vb2_fill_vb2_v4l2_buffer_flags(struct vb2_buffer *vb,
 	}
 }
 
+static int vb2_fill_vb2_v4l2_buffer_ext(struct vb2_buffer *vb,
+					const struct v4l2_ext_buffer *eb)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct vb2_plane *planes = vbuf->planes;
+	unsigned int i;
+
+	if (eb->field == V4L2_FIELD_ALTERNATE && q->is_output) {
+		/*
+		 * If the format's field is ALTERNATE, then the buffer's field
+		 * should be either TOP or BOTTOM, not ALTERNATE since that
+		 * makes no sense. The driver has to know whether the
+		 * buffer represents a top or a bottom field in order to
+		 * program any DMA correctly. Using ALTERNATE is wrong, since
+		 * that just says that it is either a top or a bottom field,
+		 * but not which of the two it is.
+		 */
+		dprintk(q, 1, "the field is incorrectly set to ALTERNATE for an output buffer\n");
+		return -EINVAL;
+	}
+	vbuf->sequence = 0;
+	vbuf->request_fd = -1;
+	vbuf->is_held = false;
+
+	for (i = 0; i < VIDEO_MAX_PLANES && eb->planes[i].m.userptr; i++) {
+		if (eb->memory == VB2_MEMORY_MMAP) {
+			planes[i].m.userptr = vb->planes[i].m.userptr;
+			planes[i].length = vb->planes[i].length;
+			planes[i].mem_offset = vb->planes[i].mem_offset;
+		} else {
+			planes[i].m.userptr = eb->planes[i].m.userptr;
+			/*
+			 * TODO: get dmabuf/mmap length, assing to max only for
+			 * userptr, that we assume userspace passed the right
+			 * size (can we assume that?)
+			 */
+			planes[i].length = UINT_MAX;
+			planes[i].mem_offset = eb->planes[i].offset;
+		}
+
+		planes[i].data_offset = 0;
+		if (V4L2_TYPE_IS_OUTPUT(eb->type)) {
+			if (eb->planes[i].bytesused == 0)
+				vb2_warn_zero_bytesused(vb);
+
+			if (vb->vb2_queue->allow_zero_bytesused)
+				planes[i].bytesused = eb->planes[i].bytesused;
+			else
+				planes[i].bytesused = eb->planes[i].bytesused ?
+						      eb->planes[i].bytesused :
+						      planes[i].length;
+		} else {
+			planes[i].bytesused = 0;
+		}
+	}
+
+	vb2_fill_vb2_v4l2_buffer_flags(vb, eb->type, eb->field, eb->flags);
+
+	return 0;
+}
+
 static int vb2_fill_vb2_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
 {
 	struct vb2_queue *q = vb->vb2_queue;
@@ -399,8 +478,35 @@ static void set_buffer_cache_hints(struct vb2_queue *q,
 		vb->need_cache_sync_on_prepare = 0;
 }
 
-static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
-				    struct v4l2_buffer *b, bool is_prepare,
+static enum v4l2_buf_type vb2_ext_to_queue_type(unsigned int type,
+						bool is_multiplanar)
+{
+	if (!is_multiplanar)
+		return type;
+
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		return V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+	return type;
+}
+
+static enum v4l2_buf_type vb2_queue_to_ext_type(unsigned int type)
+{
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+	return type;
+}
+
+static int vb2_queue_or_prepare_buf(struct vb2_queue *q,
+				    struct media_device *mdev,
+				    struct v4l2_buffer *b,
+				    struct v4l2_ext_buffer *eb,
+				    bool is_prepare,
 				    struct media_request **p_req)
 {
 	const char *opname = is_prepare ? "prepare_buf" : "qbuf";
@@ -408,46 +514,68 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
 	struct vb2_v4l2_buffer *vbuf;
 	struct vb2_buffer *vb;
 	int ret;
+	u32 index, memory, type, request_fd;
+	u32 *flags;
+
+	/* TODO: see how to improve this */
+	if (eb) {
+		index = eb->index;
+		type = vb2_ext_to_queue_type(eb->type, q->is_multiplanar);
+		memory = eb->memory;
+		flags = (u32*)&eb->flags;
+		request_fd = eb->request_fd;
+	} else {
+		index = b->index;
+		type = b->type;
+		memory = b->memory;
+		flags = &b->flags;
+		request_fd = b->request_fd;
+	}
 
-	if (b->type != q->type) {
+	if (type != q->type) {
 		dprintk(q, 1, "%s: invalid buffer type\n", opname);
 		return -EINVAL;
 	}
 
-	if (b->index >= q->num_buffers) {
+	if (index >= q->num_buffers) {
 		dprintk(q, 1, "%s: buffer index out of range\n", opname);
 		return -EINVAL;
 	}
 
-	if (q->bufs[b->index] == NULL) {
+	if (q->bufs[index] == NULL) {
 		/* Should never happen */
 		dprintk(q, 1, "%s: buffer is NULL\n", opname);
 		return -EINVAL;
 	}
 
-	if (b->memory != q->memory) {
+	if (memory != q->memory) {
 		dprintk(q, 1, "%s: invalid memory type\n", opname);
 		return -EINVAL;
 	}
 
-	vb = q->bufs[b->index];
+	vb = q->bufs[index];
 	vbuf = to_vb2_v4l2_buffer(vb);
-	ret = __verify_planes_array(vb, b);
-	if (ret)
-		return ret;
+	if (b) {
+		ret = __verify_planes_array(vb, b);
+		if (ret)
+			return ret;
+	}
 
-	if (!is_prepare && (b->flags & V4L2_BUF_FLAG_REQUEST_FD) &&
+	if (!is_prepare && (*flags & V4L2_BUF_FLAG_REQUEST_FD) &&
 	    vb->state != VB2_BUF_STATE_DEQUEUED) {
 		dprintk(q, 1, "%s: buffer is not in dequeued state\n", opname);
 		return -EINVAL;
 	}
 
 	if (!vb->prepared) {
-		set_buffer_cache_hints(q, vb, &b->flags);
+		set_buffer_cache_hints(q, vb, flags);
 		/* Copy relevant information provided by the userspace */
 		memset(vbuf->planes, 0,
 		       sizeof(vbuf->planes[0]) * vb->num_planes);
-		ret = vb2_fill_vb2_v4l2_buffer(vb, b);
+		if (b)
+			ret = vb2_fill_vb2_v4l2_buffer(vb, b);
+		else
+			ret = vb2_fill_vb2_v4l2_buffer_ext(vb, eb);
 		if (ret)
 			return ret;
 	}
@@ -455,7 +583,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
 	if (is_prepare)
 		return 0;
 
-	if (!(b->flags & V4L2_BUF_FLAG_REQUEST_FD)) {
+	if (!(*flags & V4L2_BUF_FLAG_REQUEST_FD)) {
 		if (q->requires_requests) {
 			dprintk(q, 1, "%s: queue requires requests\n", opname);
 			return -EBADR;
@@ -498,7 +626,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
 		    !q->ops->buf_out_validate))
 		return -EINVAL;
 
-	req = media_request_get_by_fd(mdev, b->request_fd);
+	req = media_request_get_by_fd(mdev, request_fd);
 	if (IS_ERR(req)) {
 		dprintk(q, 1, "%s: invalid request_fd\n", opname);
 		return PTR_ERR(req);
@@ -516,7 +644,7 @@ static int vb2_queue_or_prepare_buf(struct vb2_queue *q, struct media_device *md
 	}
 
 	*p_req = req;
-	vbuf->request_fd = b->request_fd;
+	vbuf->request_fd = request_fd;
 
 	return 0;
 }
@@ -573,6 +701,91 @@ static void v4l2_clear_buffer_flags(struct vb2_buffer *vb, u32 *flags,
 	}
 }
 
+/*
+ * v4l2_decompose_planes - decompose format into color components
+ *
+ * @eplanes: pointer to struct v4l2_ext_plane to fill.
+ * @sizeimage: size of the image when using a single plane.
+ * @pixelformat: forcc to consider when decomposing @sizeimage.
+ * @data_offset: offset of data in buffer
+ * @membuf: buffer to fill in eplanes[i].m
+ *
+ * Fill out @eplanes, dividing @sizeimage according to @pixelformat, where
+ * @sizeimage is the size of all planes in a contiguous manner.
+ */
+static int v4l2_decompose_planes(struct v4l2_ext_plane *eplanes,
+				 unsigned int sizeimage,
+				 const struct v4l2_format_info *info,
+				 unsigned int data_offset, u64 membuf)
+{
+	unsigned int i, y_plane_size, chroma_plane_size;
+
+	/*
+	 * Considering:
+	 * sizeimage = Y_plane_size + (comp_planes - 1) * chroma_plane_size
+	 * Y_plane_size = chroma_plane_size * (hdiv*vdiv)
+	 * Then:
+	 * chroma_plane_size = sizeimage / (hdiv*vdiv + comp_planes - 1)
+	 */
+	chroma_plane_size = sizeimage /
+			((info->hdiv * info->vdiv) + info->comp_planes - 1);
+	y_plane_size = chroma_plane_size * info->hdiv * info->vdiv;
+	eplanes[0].m.userptr = membuf;
+	eplanes[0].bytesused = y_plane_size;
+	eplanes[0].offset = data_offset;
+
+	for (i = 1; i < info->comp_planes; i++) {
+		eplanes[i].m.userptr = membuf;
+		eplanes[i] = eplanes[0];
+		eplanes[i].offset = data_offset + y_plane_size +
+				    chroma_plane_size * (i - 1);
+		eplanes[i].bytesused = chroma_plane_size;
+	}
+	return 0;
+}
+
+/*
+ * __fill_v4l2_ext_buffer() - fill in a struct v4l2_ext_buffer with information to be
+ * returned to userspace
+ */
+static void __fill_v4l2_ext_buffer(struct vb2_buffer *vb, void *pb)
+{
+	const struct v4l2_format_info *pix_info =
+					v4l2_format_info(vb->pixelformat);
+	struct v4l2_ext_buffer *eb = pb;
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct vb2_queue *q = vb->vb2_queue;
+	unsigned int i;
+
+	memset(eb, 0, sizeof(*eb));
+
+	/* Copy back data such as timestamp, flags, etc. */
+	eb->index = vb->index;
+	eb->type = vb2_queue_to_ext_type(vb->type);
+	eb->flags = vbuf->flags;
+	eb->field = vbuf->field;
+	eb->timestamp = vb->timestamp;
+	eb->sequence = vbuf->sequence;
+	eb->request_fd = 0;
+	eb->memory = vb->memory;
+
+	if (vb->num_planes == 1 && pix_info)
+		v4l2_decompose_planes(eb->planes,
+			vb->planes[0].bytesused - vb->planes[0].data_offset,
+			pix_info, vb->planes[i].data_offset,
+			vb->planes[0].m.userptr);
+	else
+		for (i = 0; i < vb->num_planes; i++) {
+			eb->planes[i].m.userptr = vb->planes[i].m.userptr;
+			eb->planes[i].bytesused = vb->planes[i].bytesused -
+						  vb->planes[i].data_offset;
+			eb->planes[i].offset = vb->planes[i].mem_offset +
+					       vb->planes[i].data_offset;
+		}
+
+	v4l2_clear_buffer_flags(vb, (u32*)&eb->flags, q, vbuf, &eb->request_fd);
+}
+
 /*
  * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
  * returned to userspace
@@ -665,8 +878,10 @@ static const struct vb2_buf_ops v4l2_buf_ops = {
 	.verify_planes_array	= __verify_planes_array_core,
 	.init_buffer		= __init_vb2_v4l2_buffer,
 	.fill_user_buffer	= __fill_v4l2_buffer,
+	.fill_user_ext_buffer	= __fill_v4l2_ext_buffer,
 	.fill_vb2_buffer	= __fill_vb2_buffer,
 	.copy_timestamp		= __copy_timestamp,
+	.copy_timestamp_ext	= __copy_timestamp_ext,
 };
 
 int vb2_find_timestamp(const struct vb2_queue *q, u64 timestamp,
@@ -758,7 +973,7 @@ int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
 	if (b->flags & V4L2_BUF_FLAG_REQUEST_FD)
 		return -EINVAL;
 
-	ret = vb2_queue_or_prepare_buf(q, mdev, b, true, NULL);
+	ret = vb2_queue_or_prepare_buf(q, mdev, b, NULL, true, NULL);
 
 	return ret ? ret : vb2_core_prepare_buf(q, b->index, b);
 }
@@ -833,7 +1048,7 @@ int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
 		return -EBUSY;
 	}
 
-	ret = vb2_queue_or_prepare_buf(q, mdev, b, false, &req);
+	ret = vb2_queue_or_prepare_buf(q, mdev, b, NULL, false, &req);
 	if (ret)
 		return ret;
 	ret = vb2_core_qbuf(q, b->index, b, req);
@@ -843,6 +1058,27 @@ int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
 }
 EXPORT_SYMBOL_GPL(vb2_qbuf);
 
+int vb2_ext_qbuf(struct vb2_queue *q, struct media_device *mdev,
+		 struct v4l2_ext_buffer *eb)
+{
+	struct media_request *req = NULL;
+	int ret;
+
+	if (vb2_fileio_is_active(q)) {
+		dprintk(q, 1, "file io in progress\n");
+		return -EBUSY;
+	}
+
+	ret = vb2_queue_or_prepare_buf(q, mdev, NULL, eb, false, &req);
+	if (ret)
+		return ret;
+	ret = vb2_core_ext_qbuf(q, eb->index, eb, req);
+	if (req)
+		media_request_put(req);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_ext_qbuf);
+
 int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
 {
 	int ret;
@@ -874,6 +1110,39 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
 }
 EXPORT_SYMBOL_GPL(vb2_dqbuf);
 
+int
+vb2_ext_dqbuf(struct vb2_queue *q, struct v4l2_ext_buffer *eb, bool nonblocking)
+{
+	unsigned int i;
+	int ret;
+
+	if (vb2_fileio_is_active(q)) {
+		dprintk(q, 1, "file io in progress\n");
+		return -EBUSY;
+	}
+
+	if (vb2_ext_to_queue_type(eb->type, q->is_multiplanar) != q->type) {
+		dprintk(q, 1, "invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	ret = vb2_core_ext_dqbuf(q, NULL, eb, nonblocking);
+
+	if (!q->is_output &&
+	    eb->flags & V4L2_BUF_FLAG_DONE &&
+	    eb->flags & V4L2_BUF_FLAG_LAST)
+		q->last_buffer_dequeued = true;
+
+	/*
+	 *  After calling the VIDIOC_DQBUF V4L2_BUF_FLAG_DONE must be
+	 *  cleared.
+	 */
+	eb->flags &= ~V4L2_BUF_FLAG_DONE;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_ext_dqbuf);
+
 int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
 {
 	if (vb2_fileio_is_active(q)) {
@@ -1070,6 +1339,16 @@ int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 }
 EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
 
+int vb2_ioctl_ext_qbuf(struct file *file, void *priv, struct v4l2_ext_buffer *p)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	if (vb2_queue_is_busy(vdev, file))
+		return -EBUSY;
+	return vb2_ext_qbuf(vdev->queue, vdev->v4l2_dev->mdev, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_ext_qbuf);
+
 int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
 	struct video_device *vdev = video_devdata(file);
@@ -1080,6 +1359,17 @@ int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 }
 EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf);
 
+int
+vb2_ioctl_ext_dqbuf(struct file *file, void *priv, struct v4l2_ext_buffer *p)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	if (vb2_queue_is_busy(vdev, file))
+		return -EBUSY;
+	return vb2_ext_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_ext_dqbuf);
+
 int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
 {
 	struct video_device *vdev = video_devdata(file);
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 61969402a0e3..d3bb34fe8b7d 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -165,8 +165,12 @@ struct vb2_mem_ops {
  *		pointing to this plane.
  * @m.fd:	when memory is %VB2_MEMORY_DMABUF, a userspace file
  *		descriptor associated with this plane.
+ * @mem_offset:	offset where the plane starts. Usually 0, unless the buffer
+ *		is shared by all planes of a multi-planar format.
  * @data_offset:	offset in the plane to the start of data; usually 0,
- *		unless there is a header in front of the data.
+ *		unless there is a header in front of the data. In Ext API, this
+ *		is used by the driver to update the offset value from
+ *		v4l2_ext_buffer accordinly.
  *
  * Should contain enough information to be able to cover all the fields
  * of &struct v4l2_plane at videodev2.h.
@@ -183,6 +187,7 @@ struct vb2_plane {
 		unsigned long	userptr;
 		int		fd;
 	} m;
+	unsigned int		mem_offset;
 	unsigned int		data_offset;
 };
 
@@ -235,12 +240,14 @@ struct vb2_queue;
  * @index:		id number of the buffer.
  * @type:		buffer type.
  * @memory:		the method, in which the actual data is passed.
- * @num_planes:		number of planes in the buffer
+ * @num_planes:		number of memory planes in the buffer
  *			on an internal driver queue.
  * @timestamp:		frame timestamp in ns.
  * @request:		the request this buffer is associated with.
  * @req_obj:		used to bind this buffer to a request. This
  *			request object has a refcount.
+ * @pixelformat		pixelformat so vb2 can calculate the number of color
+ *			planes.
  */
 struct vb2_buffer {
 	struct vb2_queue	*vb2_queue;
@@ -251,6 +258,7 @@ struct vb2_buffer {
 	u64			timestamp;
 	struct media_request	*request;
 	struct media_request_object	req_obj;
+	u32			pixelformat;
 
 	/* private: internal use only
 	 *
@@ -447,18 +455,24 @@ struct vb2_ops {
  *			For V4L2 this is a &struct vb2_v4l2_buffer.
  * @fill_user_buffer:	given a &vb2_buffer fill in the userspace structure.
  *			For V4L2 this is a &struct v4l2_buffer.
+ * @fill_user_ext_buffer:given a &vb2_buffer fill in the userspace structure.
+ *			For V4L2 this is a &struct v4l2_ext_buffer.
  * @fill_vb2_buffer:	given a userspace structure, fill in the &vb2_buffer.
  *			If the userspace structure is invalid, then this op
  *			will return an error.
  * @copy_timestamp:	copy the timestamp from a userspace structure to
  *			the &struct vb2_buffer.
+ * @copy_timestamp_ext:	copy the timestamp from a userspace structure to
+ *			the &struct vb2_buffer.
  */
 struct vb2_buf_ops {
 	int (*verify_planes_array)(struct vb2_buffer *vb, const void *pb);
 	void (*init_buffer)(struct vb2_buffer *vb);
 	void (*fill_user_buffer)(struct vb2_buffer *vb, void *pb);
+	void (*fill_user_ext_buffer)(struct vb2_buffer *vb, void *pb);
 	int (*fill_vb2_buffer)(struct vb2_buffer *vb, struct vb2_plane *planes);
 	void (*copy_timestamp)(struct vb2_buffer *vb, const void *pb);
+	void (*copy_timestamp_ext)(struct vb2_buffer *vb, const void *eb);
 };
 
 /**
@@ -845,6 +859,8 @@ int vb2_core_prepare_buf(struct vb2_queue *q, unsigned int index, void *pb);
  */
 int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
 		  struct media_request *req);
+int vb2_core_ext_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
+		      struct media_request *req);
 
 /**
  * vb2_core_dqbuf() - Dequeue a buffer to the userspace
@@ -871,6 +887,8 @@ int vb2_core_qbuf(struct vb2_queue *q, unsigned int index, void *pb,
  */
 int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
 		   bool nonblocking);
+int vb2_core_ext_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
+		       bool nonblocking);
 
 /**
  * vb2_core_streamon() - Implements VB2 stream ON logic
@@ -1149,6 +1167,17 @@ static inline void vb2_set_plane_payload(struct vb2_buffer *vb,
 		vb->planes[plane_no].bytesused = size;
 }
 
+/**
+ * vb2_set_pixelformat() - set pixelformat for the buffer
+ * @vb:			pointer to &struct vb2_buffer.
+ * @pixelformat:	pixelformat to associate with the buffer.
+ */
+static inline void vb2_set_pixelformat(struct vb2_buffer *vb,
+				       u32 pixelformat)
+{
+	vb->pixelformat = pixelformat;
+}
+
 /**
  * vb2_get_plane_payload() - get bytesused for the plane plane_no
  * @vb:		pointer to &struct vb2_buffer to which the plane in
diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h
index c203047eb834..96b4c67b2110 100644
--- a/include/media/videobuf2-v4l2.h
+++ b/include/media/videobuf2-v4l2.h
@@ -39,7 +39,7 @@ struct video_device;
  * @planes:	plane information (userptr/fd, length, bytesused, data_offset).
  *
  * Should contain enough information to be able to cover all the fields
- * of &struct v4l2_buffer at ``videodev2.h``.
+ * of &struct v4l2_buffer and &struct v4l2_ext_buffer at ``videodev2.h``.
  */
 struct vb2_v4l2_buffer {
 	struct vb2_buffer	vb2_buf;
@@ -150,6 +150,8 @@ int vb2_prepare_buf(struct vb2_queue *q, struct media_device *mdev,
  */
 int vb2_qbuf(struct vb2_queue *q, struct media_device *mdev,
 	     struct v4l2_buffer *b);
+int vb2_ext_qbuf(struct vb2_queue *q, struct media_device *mdev,
+		 struct v4l2_ext_buffer *b);
 
 /**
  * vb2_expbuf() - Export a buffer as a file descriptor
@@ -187,6 +189,8 @@ int vb2_expbuf(struct vb2_queue *q, struct v4l2_exportbuffer *eb);
  * from &v4l2_ioctl_ops->vidioc_dqbuf handler in driver.
  */
 int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking);
+int vb2_ext_dqbuf(struct vb2_queue *q, struct v4l2_ext_buffer *b,
+		  bool nonblocking);
 
 /**
  * vb2_streamon - start streaming
@@ -300,7 +304,9 @@ int vb2_ioctl_prepare_buf(struct file *file, void *priv,
 			  struct v4l2_buffer *p);
 int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p);
 int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p);
+int vb2_ioctl_ext_qbuf(struct file *file, void *priv, struct v4l2_ext_buffer *p);
 int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p);
+int vb2_ioctl_ext_dqbuf(struct file *file, void *priv, struct v4l2_ext_buffer *p);
 int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i);
 int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i);
 int vb2_ioctl_expbuf(struct file *file, void *priv,
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 06/11] media: vivid: use vb2_ioctls_ext_{d}qbuf hooks
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
                   ` (4 preceding siblings ...)
  2021-01-14 18:07 ` [RFC PATCH v6 05/11] media: videobuf2: Expose helpers for Ext qbuf/dqbuf Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 07/11] media: vimc: " Helen Koike
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Add vb2 ext hooks and call vb2_set_pixelformat().
This allows more flexibility with buffer handling.

Signed-off-by: Helen Koike <helen.koike@collabora.com>
---
Changes in v6:
- New patch to exemplify how drivers would easily support features from Ext Buf
---
 drivers/media/test-drivers/vivid/vivid-core.c    | 2 ++
 drivers/media/test-drivers/vivid/vivid-vid-cap.c | 1 +
 drivers/media/test-drivers/vivid/vivid-vid-out.c | 1 +
 3 files changed, 4 insertions(+)

diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c
index 0dc65ef3aa14..eacb23808c7e 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.c
+++ b/drivers/media/test-drivers/vivid/vivid-core.c
@@ -723,6 +723,8 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = {
 	.vidioc_querybuf		= vb2_ioctl_querybuf,
 	.vidioc_qbuf			= vb2_ioctl_qbuf,
 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_ext_qbuf		= vb2_ioctl_ext_qbuf,
+	.vidioc_ext_dqbuf		= vb2_ioctl_ext_dqbuf,
 	.vidioc_expbuf			= vb2_ioctl_expbuf,
 	.vidioc_streamon		= vb2_ioctl_streamon,
 	.vidioc_streamoff		= vb2_ioctl_streamoff,
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index b9caa4b26209..882b0c4a6591 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -169,6 +169,7 @@ static int vid_cap_buf_prepare(struct vb2_buffer *vb)
 		}
 
 		vb2_set_plane_payload(vb, p, size);
+		vb2_set_pixelformat(vb, dev->fmt_cap->fourcc);
 		vb->planes[p].data_offset = dev->fmt_cap->data_offset[p];
 	}
 
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c
index ac1e981e8342..6be61112065d 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-out.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c
@@ -136,6 +136,7 @@ static int vid_out_buf_prepare(struct vb2_buffer *vb)
 			return -EINVAL;
 		}
 	}
+	vb2_set_pixelformat(vb, dev->fmt_out->fourcc);
 
 	return 0;
 }
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 07/11] media: vimc: use vb2_ioctls_ext_{d}qbuf hooks
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
                   ` (5 preceding siblings ...)
  2021-01-14 18:07 ` [RFC PATCH v6 06/11] media: vivid: use vb2_ioctls_ext_{d}qbuf hooks Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 08/11] media: mediabus: Add helpers to convert a ext_pix format to/from a mbus_fmt Helen Koike
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Add vb2 ext hooks and call vb2_set_pixelformat().
This allows more flexibility with buffer handling.

Signed-off-by: Helen Koike <helen.koike@collabora.com>

---
Changes in v6:
- New patch to exemplify how drivers would easily support features from Ext Buf
---
 drivers/media/test-drivers/vimc/vimc-capture.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c
index 5e9fd902cd37..729614d19002 100644
--- a/drivers/media/test-drivers/vimc/vimc-capture.c
+++ b/drivers/media/test-drivers/vimc/vimc-capture.c
@@ -217,6 +217,8 @@ static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
 	.vidioc_querybuf = vb2_ioctl_querybuf,
 	.vidioc_qbuf = vb2_ioctl_qbuf,
 	.vidioc_dqbuf = vb2_ioctl_dqbuf,
+	.vidioc_ext_qbuf = vb2_ioctl_ext_qbuf,
+	.vidioc_ext_dqbuf = vb2_ioctl_ext_dqbuf,
 	.vidioc_expbuf = vb2_ioctl_expbuf,
 	.vidioc_streamon = vb2_ioctl_streamon,
 	.vidioc_streamoff = vb2_ioctl_streamoff,
@@ -389,6 +391,7 @@ static void *vimc_cap_process_frame(struct vimc_ent_device *ved,
 	/* Set it as ready */
 	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
 			      vcap->format.sizeimage);
+	vb2_set_pixelformat(&vimc_buf->vb2.vb2_buf, vcap->format.pixelformat);
 	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
 	return NULL;
 }
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 08/11] media: mediabus: Add helpers to convert a ext_pix format to/from a mbus_fmt
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
                   ` (6 preceding siblings ...)
  2021-01-14 18:07 ` [RFC PATCH v6 07/11] media: vimc: " Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 09/11] media: vivid: Convert to v4l2_ext_pix_format Helen Koike
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Just a new version of v4l2_fill_mbus_format() and v4l2_fill_ext_pix_format()
to deal with the new v4l2_ext_pix_format struct.
This is needed to convert the VIMC driver to the EXT_FMT/EXT_BUF iocts.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Helen Koike <helen.koike@collabora.com>
---
Changes in v6:
- Rename v4l2_fill_ext_pix_format() to v4l2_fill_ext_pix_format_from_mbus() (Tomasz)

Changes in v4:
- Add helper v4l2_fill_ext_pix_format()
- Rebased on top of media/master (post 5.8-rc1)

Changes in v3:
- Rebased on top of media/master (post 5.4-rc1)

Changes in v2:
- New patch
---
 include/media/v4l2-mediabus.h | 42 +++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h
index 841e190aedd9..055e2abbc1dd 100644
--- a/include/media/v4l2-mediabus.h
+++ b/include/media/v4l2-mediabus.h
@@ -209,4 +209,46 @@ v4l2_fill_mbus_format_mplane(struct v4l2_mbus_framefmt *mbus_fmt,
 	mbus_fmt->xfer_func = pix_mp_fmt->xfer_func;
 }
 
+/**
+ * v4l2_fill_ext_pix_format_from_mbus - Ancillary routine that fills a &struct
+ *     v4l2_ext_pix_format fields from a &struct v4l2_mbus_framefmt.
+ *
+ * @pix_fmt:   pointer to &struct v4l2_ext_pix_format to be filled
+ * @mbus_fmt:  pointer to &struct v4l2_mbus_framefmt to be used as model
+ */
+static inline void
+v4l2_fill_ext_pix_format_from_mbus(struct v4l2_ext_pix_format *pix_fmt,
+				   const struct v4l2_mbus_framefmt *mbus_fmt)
+{
+	pix_fmt->width = mbus_fmt->width;
+	pix_fmt->height = mbus_fmt->height;
+	pix_fmt->field = mbus_fmt->field;
+	pix_fmt->colorspace = mbus_fmt->colorspace;
+	pix_fmt->ycbcr_enc = mbus_fmt->ycbcr_enc;
+	pix_fmt->quantization = mbus_fmt->quantization;
+	pix_fmt->xfer_func = mbus_fmt->xfer_func;
+}
+
+/**
+ * v4l2_fill_mbus_format_ext - Ancillary routine that fills a &struct
+ *     v4l2_mbus_framefmt from a &struct v4l2_ext_pix_format.
+ *
+ * @mbus_fmt:  pointer to &struct v4l2_mbus_framefmt to be filled
+ * @pix_fmt:   pointer to &struct v4l2_ext_pix_format to be used as model
+ * @code:      data format code (from &enum v4l2_mbus_pixelcode)
+ */
+static inline void
+v4l2_fill_mbus_format_ext(struct v4l2_mbus_framefmt *mbus_fmt,
+			  const struct v4l2_ext_pix_format *pix_fmt, u32 code)
+{
+	mbus_fmt->width = pix_fmt->width;
+	mbus_fmt->height = pix_fmt->height;
+	mbus_fmt->field = pix_fmt->field;
+	mbus_fmt->colorspace = pix_fmt->colorspace;
+	mbus_fmt->ycbcr_enc = pix_fmt->ycbcr_enc;
+	mbus_fmt->quantization = pix_fmt->quantization;
+	mbus_fmt->xfer_func = pix_fmt->xfer_func;
+	mbus_fmt->code = code;
+}
+
 #endif
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 09/11] media: vivid: Convert to v4l2_ext_pix_format
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
                   ` (7 preceding siblings ...)
  2021-01-14 18:07 ` [RFC PATCH v6 08/11] media: mediabus: Add helpers to convert a ext_pix format to/from a mbus_fmt Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 10/11] media: vimc: " Helen Koike
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Simplify Multi/Single planer API handling by converting to v4l2_ext_pix_format.

Duplicate v4l2_ioctl_ops for touch devices. This is done to force the
framework to use the ext hooks when the classic Api is used from
userspace in Vid devices, and to keep touch devices with classic hook.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Helen Koike <helen.koike@collabora.com>
---
Changes in v6:
- Update with new format and buffer structs
- duplicate v4l2_ioctl_ops for touch devices.

Changes in v4:
- Update with new format and buffer structs
- Rebased on top of media/master (post 5.8-rc1)

Changes in v3:
- Rebased on top of media/master (post 5.4-rc1)

Changes in v2:
- New patch
---
---
 drivers/media/test-drivers/vivid/vivid-core.c | 207 ++++++++++--------
 .../media/test-drivers/vivid/vivid-vid-cap.c  | 202 ++++++-----------
 .../media/test-drivers/vivid/vivid-vid-cap.h  |  15 +-
 .../media/test-drivers/vivid/vivid-vid-out.c  | 197 ++++++-----------
 .../media/test-drivers/vivid/vivid-vid-out.h  |  15 +-
 5 files changed, 271 insertions(+), 365 deletions(-)

diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c
index eacb23808c7e..cebd2032e209 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.c
+++ b/drivers/media/test-drivers/vivid/vivid-core.c
@@ -477,76 +477,6 @@ static int vivid_s_input(struct file *file, void *priv, unsigned int i)
 	return vidioc_s_input(file, priv, i);
 }
 
-static int vivid_enum_fmt_cap(struct file *file, void  *priv,
-			      struct v4l2_fmtdesc *f)
-{
-	struct video_device *vdev = video_devdata(file);
-
-	if (vdev->vfl_type == VFL_TYPE_TOUCH)
-		return vivid_enum_fmt_tch(file, priv, f);
-	return vivid_enum_fmt_vid(file, priv, f);
-}
-
-static int vivid_g_fmt_cap(struct file *file, void *priv,
-			   struct v4l2_format *f)
-{
-	struct video_device *vdev = video_devdata(file);
-
-	if (vdev->vfl_type == VFL_TYPE_TOUCH)
-		return vivid_g_fmt_tch(file, priv, f);
-	return vidioc_g_fmt_vid_cap(file, priv, f);
-}
-
-static int vivid_try_fmt_cap(struct file *file, void *priv,
-			     struct v4l2_format *f)
-{
-	struct video_device *vdev = video_devdata(file);
-
-	if (vdev->vfl_type == VFL_TYPE_TOUCH)
-		return vivid_g_fmt_tch(file, priv, f);
-	return vidioc_try_fmt_vid_cap(file, priv, f);
-}
-
-static int vivid_s_fmt_cap(struct file *file, void *priv,
-			   struct v4l2_format *f)
-{
-	struct video_device *vdev = video_devdata(file);
-
-	if (vdev->vfl_type == VFL_TYPE_TOUCH)
-		return vivid_g_fmt_tch(file, priv, f);
-	return vidioc_s_fmt_vid_cap(file, priv, f);
-}
-
-static int vivid_g_fmt_cap_mplane(struct file *file, void *priv,
-				  struct v4l2_format *f)
-{
-	struct video_device *vdev = video_devdata(file);
-
-	if (vdev->vfl_type == VFL_TYPE_TOUCH)
-		return vivid_g_fmt_tch_mplane(file, priv, f);
-	return vidioc_g_fmt_vid_cap_mplane(file, priv, f);
-}
-
-static int vivid_try_fmt_cap_mplane(struct file *file, void *priv,
-				    struct v4l2_format *f)
-{
-	struct video_device *vdev = video_devdata(file);
-
-	if (vdev->vfl_type == VFL_TYPE_TOUCH)
-		return vivid_g_fmt_tch_mplane(file, priv, f);
-	return vidioc_try_fmt_vid_cap_mplane(file, priv, f);
-}
-
-static int vivid_s_fmt_cap_mplane(struct file *file, void *priv,
-				  struct v4l2_format *f)
-{
-	struct video_device *vdev = video_devdata(file);
-
-	if (vdev->vfl_type == VFL_TYPE_TOUCH)
-		return vivid_g_fmt_tch_mplane(file, priv, f);
-	return vidioc_s_fmt_vid_cap_mplane(file, priv, f);
-}
-
 static bool vivid_is_in_use(bool valid, struct video_device *vdev)
 {
 	unsigned long flags;
@@ -656,24 +586,129 @@ static const struct v4l2_file_operations vivid_radio_fops = {
 	.unlocked_ioctl = video_ioctl2,
 };
 
+static const struct v4l2_ioctl_ops vivid_ioctl_ops_tch = {
+	.vidioc_querycap		= vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = vivid_enum_fmt_tch,
+	.vidioc_g_fmt_vid_cap = vivid_g_fmt_tch,
+	.vidioc_try_fmt_vid_cap = vivid_g_fmt_tch,
+	.vidioc_s_fmt_vid_cap = vivid_g_fmt_tch,
+	.vidioc_g_fmt_vid_cap_mplane = vivid_g_fmt_tch_mplane,
+	.vidioc_try_fmt_vid_cap_mplane = vivid_g_fmt_tch_mplane,
+	.vidioc_s_fmt_vid_cap_mplane = vivid_g_fmt_tch_mplane,
+
+	.vidioc_g_selection		= vidioc_g_selection,
+	.vidioc_s_selection		= vidioc_s_selection,
+	.vidioc_g_pixelaspect		= vidioc_g_pixelaspect,
+
+	.vidioc_g_fmt_vbi_cap		= vidioc_g_fmt_vbi_cap,
+	.vidioc_try_fmt_vbi_cap		= vidioc_g_fmt_vbi_cap,
+	.vidioc_s_fmt_vbi_cap		= vidioc_s_fmt_vbi_cap,
+
+	.vidioc_g_fmt_sliced_vbi_cap    = vidioc_g_fmt_sliced_vbi_cap,
+	.vidioc_try_fmt_sliced_vbi_cap  = vidioc_try_fmt_sliced_vbi_cap,
+	.vidioc_s_fmt_sliced_vbi_cap    = vidioc_s_fmt_sliced_vbi_cap,
+	.vidioc_g_sliced_vbi_cap	= vidioc_g_sliced_vbi_cap,
+
+	.vidioc_g_fmt_vbi_out		= vidioc_g_fmt_vbi_out,
+	.vidioc_try_fmt_vbi_out		= vidioc_g_fmt_vbi_out,
+	.vidioc_s_fmt_vbi_out		= vidioc_s_fmt_vbi_out,
+
+	.vidioc_g_fmt_sliced_vbi_out    = vidioc_g_fmt_sliced_vbi_out,
+	.vidioc_try_fmt_sliced_vbi_out  = vidioc_try_fmt_sliced_vbi_out,
+	.vidioc_s_fmt_sliced_vbi_out    = vidioc_s_fmt_sliced_vbi_out,
+
+	.vidioc_enum_fmt_sdr_cap	= vidioc_enum_fmt_sdr_cap,
+	.vidioc_g_fmt_sdr_cap		= vidioc_g_fmt_sdr_cap,
+	.vidioc_try_fmt_sdr_cap		= vidioc_try_fmt_sdr_cap,
+	.vidioc_s_fmt_sdr_cap		= vidioc_s_fmt_sdr_cap,
+
+	.vidioc_overlay			= vidioc_overlay,
+	.vidioc_enum_framesizes		= vidioc_enum_framesizes,
+	.vidioc_enum_frameintervals	= vidioc_enum_frameintervals,
+	.vidioc_g_parm			= vidioc_g_parm,
+	.vidioc_s_parm			= vidioc_s_parm,
+
+	.vidioc_enum_fmt_vid_overlay	= vidioc_enum_fmt_vid_overlay,
+	.vidioc_g_fmt_vid_overlay	= vidioc_g_fmt_vid_overlay,
+	.vidioc_try_fmt_vid_overlay	= vidioc_try_fmt_vid_overlay,
+	.vidioc_s_fmt_vid_overlay	= vidioc_s_fmt_vid_overlay,
+	.vidioc_g_fmt_vid_out_overlay	= vidioc_g_fmt_vid_out_overlay,
+	.vidioc_try_fmt_vid_out_overlay	= vidioc_try_fmt_vid_out_overlay,
+	.vidioc_s_fmt_vid_out_overlay	= vidioc_s_fmt_vid_out_overlay,
+	.vidioc_g_fbuf			= vidioc_g_fbuf,
+	.vidioc_s_fbuf			= vidioc_s_fbuf,
+
+	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_expbuf			= vb2_ioctl_expbuf,
+	.vidioc_streamon		= vb2_ioctl_streamon,
+	.vidioc_streamoff		= vb2_ioctl_streamoff,
+
+	.vidioc_enum_input		= vivid_enum_input,
+	.vidioc_g_input			= vivid_g_input,
+	.vidioc_s_input			= vivid_s_input,
+	.vidioc_s_audio			= vidioc_s_audio,
+	.vidioc_g_audio			= vidioc_g_audio,
+	.vidioc_enumaudio		= vidioc_enumaudio,
+	.vidioc_s_frequency		= vidioc_s_frequency,
+	.vidioc_g_frequency		= vidioc_g_frequency,
+	.vidioc_s_tuner			= vidioc_s_tuner,
+	.vidioc_g_tuner			= vidioc_g_tuner,
+	.vidioc_s_modulator		= vidioc_s_modulator,
+	.vidioc_g_modulator		= vidioc_g_modulator,
+	.vidioc_s_hw_freq_seek		= vidioc_s_hw_freq_seek,
+	.vidioc_enum_freq_bands		= vidioc_enum_freq_bands,
+
+	.vidioc_enum_output		= vidioc_enum_output,
+	.vidioc_g_output		= vidioc_g_output,
+	.vidioc_s_output		= vidioc_s_output,
+	.vidioc_s_audout		= vidioc_s_audout,
+	.vidioc_g_audout		= vidioc_g_audout,
+	.vidioc_enumaudout		= vidioc_enumaudout,
+
+	.vidioc_querystd		= vidioc_querystd,
+	.vidioc_g_std			= vidioc_g_std,
+	.vidioc_s_std			= vidioc_s_std,
+	.vidioc_s_dv_timings		= vidioc_s_dv_timings,
+	.vidioc_g_dv_timings		= vidioc_g_dv_timings,
+	.vidioc_query_dv_timings	= vidioc_query_dv_timings,
+	.vidioc_enum_dv_timings		= vidioc_enum_dv_timings,
+	.vidioc_dv_timings_cap		= vidioc_dv_timings_cap,
+	.vidioc_g_edid			= vidioc_g_edid,
+	.vidioc_s_edid			= vidioc_s_edid,
+
+	.vidioc_log_status		= vidioc_log_status,
+	.vidioc_subscribe_event		= vidioc_subscribe_event,
+	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
+
+	.vidioc_enum_fmt_meta_cap	= vidioc_enum_fmt_meta_cap,
+	.vidioc_g_fmt_meta_cap		= vidioc_g_fmt_meta_cap,
+	.vidioc_s_fmt_meta_cap		= vidioc_g_fmt_meta_cap,
+	.vidioc_try_fmt_meta_cap	= vidioc_g_fmt_meta_cap,
+
+	.vidioc_enum_fmt_meta_out       = vidioc_enum_fmt_meta_out,
+	.vidioc_g_fmt_meta_out          = vidioc_g_fmt_meta_out,
+	.vidioc_s_fmt_meta_out          = vidioc_g_fmt_meta_out,
+	.vidioc_try_fmt_meta_out        = vidioc_g_fmt_meta_out,
+};
+
 static const struct v4l2_ioctl_ops vivid_ioctl_ops = {
 	.vidioc_querycap		= vidioc_querycap,
 
-	.vidioc_enum_fmt_vid_cap	= vivid_enum_fmt_cap,
-	.vidioc_g_fmt_vid_cap		= vivid_g_fmt_cap,
-	.vidioc_try_fmt_vid_cap		= vivid_try_fmt_cap,
-	.vidioc_s_fmt_vid_cap		= vivid_s_fmt_cap,
-	.vidioc_g_fmt_vid_cap_mplane	= vivid_g_fmt_cap_mplane,
-	.vidioc_try_fmt_vid_cap_mplane	= vivid_try_fmt_cap_mplane,
-	.vidioc_s_fmt_vid_cap_mplane	= vivid_s_fmt_cap_mplane,
-
-	.vidioc_enum_fmt_vid_out	= vivid_enum_fmt_vid,
-	.vidioc_g_fmt_vid_out		= vidioc_g_fmt_vid_out,
-	.vidioc_try_fmt_vid_out		= vidioc_try_fmt_vid_out,
-	.vidioc_s_fmt_vid_out		= vidioc_s_fmt_vid_out,
-	.vidioc_g_fmt_vid_out_mplane	= vidioc_g_fmt_vid_out_mplane,
-	.vidioc_try_fmt_vid_out_mplane	= vidioc_try_fmt_vid_out_mplane,
-	.vidioc_s_fmt_vid_out_mplane	= vidioc_s_fmt_vid_out_mplane,
+	.vidioc_enum_fmt_vid_cap = vivid_enum_fmt_vid,
+	.vidioc_g_ext_pix_fmt_vid_cap = vivid_g_fmt_vid_cap,
+	.vidioc_try_ext_pix_fmt_vid_cap = vivid_try_fmt_vid_cap,
+	.vidioc_s_ext_pix_fmt_vid_cap = vivid_s_fmt_vid_cap,
+
+	.vidioc_enum_fmt_vid_out = vivid_enum_fmt_vid,
+	.vidioc_g_ext_pix_fmt_vid_out = vivid_g_fmt_vid_out,
+	.vidioc_try_ext_pix_fmt_vid_out = vivid_try_fmt_vid_out,
+	.vidioc_s_ext_pix_fmt_vid_out = vivid_s_fmt_vid_out,
 
 	.vidioc_g_selection		= vidioc_g_selection,
 	.vidioc_s_selection		= vidioc_s_selection,
@@ -1635,7 +1670,7 @@ static int vivid_create_devnodes(struct platform_device *pdev,
 		snprintf(vfd->name, sizeof(vfd->name),
 			 "vivid-%03d-touch-cap", inst);
 		vfd->fops = &vivid_fops;
-		vfd->ioctl_ops = &vivid_ioctl_ops;
+		vfd->ioctl_ops = &vivid_ioctl_ops_tch;
 		vfd->device_caps = dev->touch_cap_caps;
 		vfd->release = video_device_release_empty;
 		vfd->v4l2_dev = &dev->v4l2_dev;
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index 882b0c4a6591..a16af0a2d98c 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -14,6 +14,7 @@
 #include <media/v4l2-common.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
 #include <media/v4l2-rect.h>
 
 #include "vivid-core.h"
@@ -522,61 +523,59 @@ static unsigned vivid_quantization_cap(struct vivid_dev *dev)
 }
 
 int vivid_g_fmt_vid_cap(struct file *file, void *priv,
-					struct v4l2_format *f)
+			struct v4l2_ext_pix_format *f)
 {
 	struct vivid_dev *dev = video_drvdata(file);
-	struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
 	unsigned p;
 
-	mp->width        = dev->fmt_cap_rect.width;
-	mp->height       = dev->fmt_cap_rect.height;
-	mp->field        = dev->field_cap;
-	mp->pixelformat  = dev->fmt_cap->fourcc;
-	mp->colorspace   = vivid_colorspace_cap(dev);
-	mp->xfer_func    = vivid_xfer_func_cap(dev);
+	f->width = dev->fmt_cap_rect.width;
+	f->height = dev->fmt_cap_rect.height;
+	f->field = dev->field_cap;
+	f->pixelformat = dev->fmt_cap->fourcc;
+	f->colorspace = vivid_colorspace_cap(dev);
+	f->xfer_func = vivid_xfer_func_cap(dev);
 	if (dev->fmt_cap->color_enc == TGP_COLOR_ENC_HSV)
-		mp->hsv_enc    = vivid_hsv_enc_cap(dev);
+		f->hsv_enc = vivid_hsv_enc_cap(dev);
 	else
-		mp->ycbcr_enc    = vivid_ycbcr_enc_cap(dev);
-	mp->quantization = vivid_quantization_cap(dev);
-	mp->num_planes = dev->fmt_cap->buffers;
-	for (p = 0; p < mp->num_planes; p++) {
-		mp->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p);
-		mp->plane_fmt[p].sizeimage =
-			(tpg_g_line_width(&dev->tpg, p) * mp->height) /
+		f->ycbcr_enc    = vivid_ycbcr_enc_cap(dev);
+	f->quantization = vivid_quantization_cap(dev);
+	memset(f->plane_fmt, 0, sizeof(f->plane_fmt));
+	for (p = 0; p < dev->fmt_cap->buffers; p++) {
+		f->plane_fmt[p].bytesperline = tpg_g_bytesperline(&dev->tpg, p);
+		f->plane_fmt[p].sizeimage =
+			(tpg_g_line_width(&dev->tpg, p) * f->height) /
 			dev->fmt_cap->vdownsampling[p] +
 			dev->fmt_cap->data_offset[p];
 	}
+
 	return 0;
 }
 
 int vivid_try_fmt_vid_cap(struct file *file, void *priv,
-			struct v4l2_format *f)
+			  struct v4l2_ext_pix_format *f)
 {
-	struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
-	struct v4l2_plane_pix_format *pfmt = mp->plane_fmt;
 	struct vivid_dev *dev = video_drvdata(file);
+	struct v4l2_plane_pix_format *pfmt = f->plane_fmt;
 	const struct vivid_fmt *fmt;
 	unsigned bytesperline, max_bpl;
 	unsigned factor = 1;
 	unsigned w, h;
 	unsigned p;
-	bool user_set_csc = !!(mp->flags & V4L2_PIX_FMT_FLAG_SET_CSC);
 
-	fmt = vivid_get_format(dev, mp->pixelformat);
+	fmt = vivid_get_format(dev, f->pixelformat);
 	if (!fmt) {
 		dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
-			mp->pixelformat);
-		mp->pixelformat = V4L2_PIX_FMT_YUYV;
-		fmt = vivid_get_format(dev, mp->pixelformat);
+			f->pixelformat);
+		f->pixelformat = V4L2_PIX_FMT_YUYV;
+		fmt = vivid_get_format(dev, f->pixelformat);
 	}
 
-	mp->field = vivid_field_cap(dev, mp->field);
+	f->field = vivid_field_cap(dev, f->field);
 	if (vivid_is_webcam(dev)) {
 		const struct v4l2_frmsize_discrete *sz =
 			v4l2_find_nearest_size(webcam_sizes,
 					       VIVID_WEBCAM_SIZES, width,
-					       height, mp->width, mp->height);
+					       height, f->width, f->height);
 
 		w = sz->width;
 		h = sz->height;
@@ -587,14 +586,14 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
 		w = dev->src_rect.width;
 		h = dev->src_rect.height;
 	}
-	if (V4L2_FIELD_HAS_T_OR_B(mp->field))
+	if (V4L2_FIELD_HAS_T_OR_B(f->field))
 		factor = 2;
 	if (vivid_is_webcam(dev) ||
 	    (!dev->has_scaler_cap && !dev->has_crop_cap && !dev->has_compose_cap)) {
-		mp->width = w;
-		mp->height = h / factor;
+		f->width = w;
+		f->height = h / factor;
 	} else {
-		struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor };
+		struct v4l2_rect r = { 0, 0, f->width, f->height * factor };
 
 		v4l2_rect_set_min_size(&r, &vivid_min_rect);
 		v4l2_rect_set_max_size(&r, &vivid_max_rect);
@@ -607,16 +606,15 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
 		} else if (!dev->has_scaler_cap && !dev->has_crop_cap) {
 			v4l2_rect_set_min_size(&r, &dev->src_rect);
 		}
-		mp->width = r.width;
-		mp->height = r.height / factor;
+		f->width = r.width;
+		f->height = r.height / factor;
 	}
 
 	/* This driver supports custom bytesperline values */
 
-	mp->num_planes = fmt->buffers;
 	for (p = 0; p < fmt->buffers; p++) {
 		/* Calculate the minimum supported bytesperline value */
-		bytesperline = (mp->width * fmt->bit_depth[p]) >> 3;
+		bytesperline = (f->width * fmt->bit_depth[p]) >> 3;
 		/* Calculate the maximum supported bytesperline value */
 		max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3;
 
@@ -625,48 +623,49 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
 		if (pfmt[p].bytesperline < bytesperline)
 			pfmt[p].bytesperline = bytesperline;
 
-		pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) /
+		pfmt[p].sizeimage = (pfmt[p].bytesperline * f->height) /
 				fmt->vdownsampling[p] + fmt->data_offset[p];
-
-		memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
 	}
+
+	if (p < VIDEO_MAX_PLANES)
+		pfmt[p].sizeimage = 0;
+
 	for (p = fmt->buffers; p < fmt->planes; p++)
-		pfmt[0].sizeimage += (pfmt[0].bytesperline * mp->height *
+		pfmt[0].sizeimage += (pfmt[0].bytesperline * f->height *
 			(fmt->bit_depth[p] / fmt->vdownsampling[p])) /
 			(fmt->bit_depth[0] / fmt->vdownsampling[0]);
 
-	if (!user_set_csc || !v4l2_is_colorspace_valid(mp->colorspace))
-		mp->colorspace = vivid_colorspace_cap(dev);
+	if (!v4l2_is_colorspace_valid(f->colorspace))
+		f->colorspace = vivid_colorspace_cap(dev);
 
-	if (!user_set_csc || !v4l2_is_xfer_func_valid(mp->xfer_func))
-		mp->xfer_func = vivid_xfer_func_cap(dev);
+	if (!v4l2_is_xfer_func_valid(f->xfer_func))
+		f->xfer_func = vivid_xfer_func_cap(dev);
 
 	if (fmt->color_enc == TGP_COLOR_ENC_HSV) {
-		if (!user_set_csc || !v4l2_is_hsv_enc_valid(mp->hsv_enc))
-			mp->hsv_enc = vivid_hsv_enc_cap(dev);
+		if (!v4l2_is_hsv_enc_valid(f->hsv_enc))
+			f->hsv_enc = vivid_hsv_enc_cap(dev);
 	} else if (fmt->color_enc == TGP_COLOR_ENC_YCBCR) {
-		if (!user_set_csc || !v4l2_is_ycbcr_enc_valid(mp->ycbcr_enc))
-			mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev);
+		if (!v4l2_is_ycbcr_enc_valid(f->ycbcr_enc))
+			f->ycbcr_enc = vivid_ycbcr_enc_cap(dev);
 	} else {
-		mp->ycbcr_enc = vivid_ycbcr_enc_cap(dev);
+		f->ycbcr_enc = vivid_ycbcr_enc_cap(dev);
 	}
 
 	if (fmt->color_enc == TGP_COLOR_ENC_YCBCR ||
 	    fmt->color_enc == TGP_COLOR_ENC_RGB) {
-		if (!user_set_csc || !v4l2_is_quant_valid(mp->quantization))
-			mp->quantization = vivid_quantization_cap(dev);
+		if (!v4l2_is_quant_valid(f->quantization))
+			f->quantization = vivid_quantization_cap(dev);
 	} else {
-		mp->quantization = vivid_quantization_cap(dev);
+		f->quantization = vivid_quantization_cap(dev);
 	}
 
-	memset(mp->reserved, 0, sizeof(mp->reserved));
+	memset(f->reserved, 0, sizeof(f->reserved));
 	return 0;
 }
 
 int vivid_s_fmt_vid_cap(struct file *file, void *priv,
-					struct v4l2_format *f)
+			struct v4l2_ext_pix_format *f)
 {
-	struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
 	struct vivid_dev *dev = video_drvdata(file);
 	struct v4l2_rect *crop = &dev->crop_cap;
 	struct v4l2_rect *compose = &dev->compose_cap;
@@ -684,20 +683,21 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv,
 		return -EBUSY;
 	}
 
-	if (dev->overlay_cap_owner && dev->fb_cap.fmt.pixelformat != mp->pixelformat) {
+	if (dev->overlay_cap_owner &&
+	    dev->fb_cap.fmt.pixelformat != f->pixelformat) {
 		dprintk(dev, 1, "overlay is active, can't change pixelformat\n");
 		return -EBUSY;
 	}
 
-	dev->fmt_cap = vivid_get_format(dev, mp->pixelformat);
-	if (V4L2_FIELD_HAS_T_OR_B(mp->field))
+	dev->fmt_cap = vivid_get_format(dev, f->pixelformat);
+	if (V4L2_FIELD_HAS_T_OR_B(f->field))
 		factor = 2;
 
 	/* Note: the webcam input doesn't support scaling, cropping or composing */
 
 	if (!vivid_is_webcam(dev) &&
 	    (dev->has_scaler_cap || dev->has_crop_cap || dev->has_compose_cap)) {
-		struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+		struct v4l2_rect r = { 0, 0, f->width, f->height };
 
 		if (dev->has_scaler_cap) {
 			if (dev->has_compose_cap)
@@ -758,107 +758,47 @@ int vivid_s_fmt_vid_cap(struct file *file, void *priv,
 	} else if (vivid_is_webcam(dev)) {
 		/* Guaranteed to be a match */
 		for (i = 0; i < ARRAY_SIZE(webcam_sizes); i++)
-			if (webcam_sizes[i].width == mp->width &&
-					webcam_sizes[i].height == mp->height)
+			if (webcam_sizes[i].width == f->width &&
+			    webcam_sizes[i].height == f->height)
 				break;
 		dev->webcam_size_idx = i;
 		if (dev->webcam_ival_idx >= 2 * (VIVID_WEBCAM_SIZES - i))
 			dev->webcam_ival_idx = 2 * (VIVID_WEBCAM_SIZES - i) - 1;
 		vivid_update_format_cap(dev, false);
 	} else {
-		struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+		struct v4l2_rect r = { 0, 0, f->width, f->height };
 
 		v4l2_rect_set_size_to(compose, &r);
 		r.height *= factor;
 		v4l2_rect_set_size_to(crop, &r);
 	}
 
-	dev->fmt_cap_rect.width = mp->width;
-	dev->fmt_cap_rect.height = mp->height;
-	tpg_s_buf_height(&dev->tpg, mp->height);
+	dev->fmt_cap_rect.width = f->width;
+	dev->fmt_cap_rect.height = f->height;
+	tpg_s_buf_height(&dev->tpg, f->height);
 	tpg_s_fourcc(&dev->tpg, dev->fmt_cap->fourcc);
 	for (p = 0; p < tpg_g_buffers(&dev->tpg); p++)
-		tpg_s_bytesperline(&dev->tpg, p, mp->plane_fmt[p].bytesperline);
-	dev->field_cap = mp->field;
+		tpg_s_bytesperline(&dev->tpg, p, f->plane_fmt[p].bytesperline);
+	dev->field_cap = f->field;
 	if (dev->field_cap == V4L2_FIELD_ALTERNATE)
 		tpg_s_field(&dev->tpg, V4L2_FIELD_TOP, true);
 	else
 		tpg_s_field(&dev->tpg, dev->field_cap, false);
 	tpg_s_crop_compose(&dev->tpg, &dev->crop_cap, &dev->compose_cap);
 	if (vivid_is_sdtv_cap(dev))
-		dev->tv_field_cap = mp->field;
+		dev->tv_field_cap = f->field;
 	tpg_update_mv_step(&dev->tpg);
-	dev->tpg.colorspace = mp->colorspace;
-	dev->tpg.xfer_func = mp->xfer_func;
+	dev->tpg.colorspace = f->colorspace;
+	dev->tpg.xfer_func = f->xfer_func;
 	if (dev->fmt_cap->color_enc == TGP_COLOR_ENC_YCBCR)
-		dev->tpg.ycbcr_enc = mp->ycbcr_enc;
+		dev->tpg.ycbcr_enc = f->ycbcr_enc;
 	else
-		dev->tpg.hsv_enc = mp->hsv_enc;
-	dev->tpg.quantization = mp->quantization;
+		dev->tpg.hsv_enc = f->hsv_enc;
+	dev->tpg.quantization = f->quantization;
 
 	return 0;
 }
 
-int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv,
-					struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (!dev->multiplanar)
-		return -ENOTTY;
-	return vivid_g_fmt_vid_cap(file, priv, f);
-}
-
-int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv,
-			struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (!dev->multiplanar)
-		return -ENOTTY;
-	return vivid_try_fmt_vid_cap(file, priv, f);
-}
-
-int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv,
-			struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (!dev->multiplanar)
-		return -ENOTTY;
-	return vivid_s_fmt_vid_cap(file, priv, f);
-}
-
-int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
-					struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (dev->multiplanar)
-		return -ENOTTY;
-	return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_cap);
-}
-
-int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
-			struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (dev->multiplanar)
-		return -ENOTTY;
-	return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_cap);
-}
-
-int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
-			struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (dev->multiplanar)
-		return -ENOTTY;
-	return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_cap);
-}
-
 int vivid_vid_cap_g_selection(struct file *file, void *priv,
 			      struct v4l2_selection *sel)
 {
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h
index 1e422a59eeab..7c9fc5c787b5 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h
@@ -17,15 +17,12 @@ extern const char * const vivid_ctrl_standard_strings[];
 
 extern const struct vb2_ops vivid_vid_cap_qops;
 
-int vivid_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_g_fmt_vid_cap(struct file *file, void *priv,
+			struct v4l2_ext_pix_format *f);
+int vivid_try_fmt_vid_cap(struct file *file, void *priv,
+			  struct v4l2_ext_pix_format *f);
+int vivid_s_fmt_vid_cap(struct file *file, void *priv,
+			struct v4l2_ext_pix_format *f);
 int vivid_vid_cap_g_selection(struct file *file, void *priv, struct v4l2_selection *sel);
 int vivid_vid_cap_s_selection(struct file *file, void *fh, struct v4l2_selection *s);
 int vivid_vid_cap_g_pixelaspect(struct file *file, void *priv, int type, struct v4l2_fract *f);
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c
index 6be61112065d..bd967cc595c9 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-out.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c
@@ -13,6 +13,7 @@
 #include <media/v4l2-common.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
 #include <media/v4l2-rect.h>
 
 #include "vivid-core.h"
@@ -316,59 +317,57 @@ static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev)
 }
 
 int vivid_g_fmt_vid_out(struct file *file, void *priv,
-					struct v4l2_format *f)
+			struct v4l2_ext_pix_format *f)
 {
 	struct vivid_dev *dev = video_drvdata(file);
-	struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
 	const struct vivid_fmt *fmt = dev->fmt_out;
 	unsigned p;
 
-	mp->width        = dev->fmt_out_rect.width;
-	mp->height       = dev->fmt_out_rect.height;
-	mp->field        = dev->field_out;
-	mp->pixelformat  = fmt->fourcc;
-	mp->colorspace   = dev->colorspace_out;
-	mp->xfer_func    = dev->xfer_func_out;
-	mp->ycbcr_enc    = dev->ycbcr_enc_out;
-	mp->quantization = dev->quantization_out;
-	mp->num_planes = fmt->buffers;
-	for (p = 0; p < mp->num_planes; p++) {
-		mp->plane_fmt[p].bytesperline = dev->bytesperline_out[p];
-		mp->plane_fmt[p].sizeimage =
-			mp->plane_fmt[p].bytesperline * mp->height +
+	f->width        = dev->fmt_out_rect.width;
+	f->height       = dev->fmt_out_rect.height;
+	f->field        = dev->field_out;
+	f->pixelformat  = fmt->fourcc;
+	f->colorspace   = dev->colorspace_out;
+	f->xfer_func    = dev->xfer_func_out;
+	f->ycbcr_enc    = dev->ycbcr_enc_out;
+	f->quantization = dev->quantization_out;
+	memset(f->plane_fmt, 0, sizeof(f->plane_fmt));
+	for (p = 0; p < fmt->buffers; p++) {
+		f->plane_fmt[p].bytesperline = dev->bytesperline_out[p];
+		f->plane_fmt[p].sizeimage =
+			f->plane_fmt[p].bytesperline * f->height +
 			fmt->data_offset[p];
 	}
 	for (p = fmt->buffers; p < fmt->planes; p++) {
 		unsigned stride = dev->bytesperline_out[p];
 
-		mp->plane_fmt[0].sizeimage +=
-			(stride * mp->height) / fmt->vdownsampling[p];
+		f->plane_fmt[0].sizeimage +=
+			(stride * f->height) / fmt->vdownsampling[p];
 	}
 	return 0;
 }
 
 int vivid_try_fmt_vid_out(struct file *file, void *priv,
-			struct v4l2_format *f)
+			  struct v4l2_ext_pix_format *f)
 {
 	struct vivid_dev *dev = video_drvdata(file);
+	struct v4l2_plane_pix_format *pfmt = f->plane_fmt;
 	struct v4l2_bt_timings *bt = &dev->dv_timings_out.bt;
-	struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
-	struct v4l2_plane_pix_format *pfmt = mp->plane_fmt;
 	const struct vivid_fmt *fmt;
 	unsigned bytesperline, max_bpl;
 	unsigned factor = 1;
 	unsigned w, h;
 	unsigned p;
 
-	fmt = vivid_get_format(dev, mp->pixelformat);
+	fmt = vivid_get_format(dev, f->pixelformat);
 	if (!fmt) {
 		dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
-			mp->pixelformat);
-		mp->pixelformat = V4L2_PIX_FMT_YUYV;
-		fmt = vivid_get_format(dev, mp->pixelformat);
+			f->pixelformat);
+		f->pixelformat = V4L2_PIX_FMT_YUYV;
+		fmt = vivid_get_format(dev, f->pixelformat);
 	}
 
-	mp->field = vivid_field_out(dev, mp->field);
+	f->field = vivid_field_out(dev, f->field);
 	if (vivid_is_svid_out(dev)) {
 		w = 720;
 		h = (dev->std_out & V4L2_STD_525_60) ? 480 : 576;
@@ -376,13 +375,13 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv,
 		w = dev->sink_rect.width;
 		h = dev->sink_rect.height;
 	}
-	if (V4L2_FIELD_HAS_T_OR_B(mp->field))
+	if (V4L2_FIELD_HAS_T_OR_B(f->field))
 		factor = 2;
 	if (!dev->has_scaler_out && !dev->has_crop_out && !dev->has_compose_out) {
-		mp->width = w;
-		mp->height = h / factor;
+		f->width = w;
+		f->height = h / factor;
 	} else {
-		struct v4l2_rect r = { 0, 0, mp->width, mp->height * factor };
+		struct v4l2_rect r = { 0, 0, f->width, f->height * factor };
 
 		v4l2_rect_set_min_size(&r, &vivid_min_rect);
 		v4l2_rect_set_max_size(&r, &vivid_max_rect);
@@ -395,16 +394,15 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv,
 		} else if (!dev->has_scaler_out && !dev->has_compose_out) {
 			v4l2_rect_set_min_size(&r, &dev->sink_rect);
 		}
-		mp->width = r.width;
-		mp->height = r.height / factor;
+		f->width = r.width;
+		f->height = r.height / factor;
 	}
 
 	/* This driver supports custom bytesperline values */
 
-	mp->num_planes = fmt->buffers;
 	for (p = 0; p < fmt->buffers; p++) {
 		/* Calculate the minimum supported bytesperline value */
-		bytesperline = (mp->width * fmt->bit_depth[p]) >> 3;
+		bytesperline = (f->width * fmt->bit_depth[p]) >> 3;
 		/* Calculate the maximum supported bytesperline value */
 		max_bpl = (MAX_ZOOM * MAX_WIDTH * fmt->bit_depth[p]) >> 3;
 
@@ -413,42 +411,41 @@ int vivid_try_fmt_vid_out(struct file *file, void *priv,
 		if (pfmt[p].bytesperline < bytesperline)
 			pfmt[p].bytesperline = bytesperline;
 
-		pfmt[p].sizeimage = (pfmt[p].bytesperline * mp->height) /
+		pfmt[p].sizeimage = (pfmt[p].bytesperline * f->height) /
 				fmt->vdownsampling[p] + fmt->data_offset[p];
 
-		memset(pfmt[p].reserved, 0, sizeof(pfmt[p].reserved));
 	}
+	if (p < VIDEO_MAX_PLANES)
+		pfmt[p].sizeimage = 0;
 	for (p = fmt->buffers; p < fmt->planes; p++)
-		pfmt[0].sizeimage += (pfmt[0].bytesperline * mp->height *
+		pfmt[0].sizeimage += (pfmt[0].bytesperline * f->height *
 			(fmt->bit_depth[p] / fmt->vdownsampling[p])) /
 			(fmt->bit_depth[0] / fmt->vdownsampling[0]);
 
-	mp->xfer_func = V4L2_XFER_FUNC_DEFAULT;
-	mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
-	mp->quantization = V4L2_QUANTIZATION_DEFAULT;
+	f->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+	f->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+	f->quantization = V4L2_QUANTIZATION_DEFAULT;
 	if (vivid_is_svid_out(dev)) {
-		mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
+		f->colorspace = V4L2_COLORSPACE_SMPTE170M;
 	} else if (dev->dvi_d_out || !(bt->flags & V4L2_DV_FL_IS_CE_VIDEO)) {
-		mp->colorspace = V4L2_COLORSPACE_SRGB;
+		f->colorspace = V4L2_COLORSPACE_SRGB;
 		if (dev->dvi_d_out)
-			mp->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+			f->quantization = V4L2_QUANTIZATION_LIM_RANGE;
 	} else if (bt->width == 720 && bt->height <= 576) {
-		mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
-	} else if (mp->colorspace != V4L2_COLORSPACE_SMPTE170M &&
-		   mp->colorspace != V4L2_COLORSPACE_REC709 &&
-		   mp->colorspace != V4L2_COLORSPACE_OPRGB &&
-		   mp->colorspace != V4L2_COLORSPACE_BT2020 &&
-		   mp->colorspace != V4L2_COLORSPACE_SRGB) {
-		mp->colorspace = V4L2_COLORSPACE_REC709;
+		f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+	} else if (f->colorspace != V4L2_COLORSPACE_SMPTE170M &&
+		   f->colorspace != V4L2_COLORSPACE_REC709 &&
+		   f->colorspace != V4L2_COLORSPACE_OPRGB &&
+		   f->colorspace != V4L2_COLORSPACE_BT2020 &&
+		   f->colorspace != V4L2_COLORSPACE_SRGB) {
+		f->colorspace = V4L2_COLORSPACE_REC709;
 	}
-	memset(mp->reserved, 0, sizeof(mp->reserved));
 	return 0;
 }
 
 int vivid_s_fmt_vid_out(struct file *file, void *priv,
-					struct v4l2_format *f)
+			struct v4l2_ext_pix_format *f)
 {
-	struct v4l2_pix_format_mplane *mp = &f->fmt.pix_mp;
 	struct vivid_dev *dev = video_drvdata(file);
 	struct v4l2_rect *crop = &dev->crop_out;
 	struct v4l2_rect *compose = &dev->compose_out;
@@ -462,10 +459,10 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv,
 
 	if (vb2_is_busy(q) &&
 	    (vivid_is_svid_out(dev) ||
-	     mp->width != dev->fmt_out_rect.width ||
-	     mp->height != dev->fmt_out_rect.height ||
-	     mp->pixelformat != dev->fmt_out->fourcc ||
-	     mp->field != dev->field_out)) {
+	     f->width != dev->fmt_out_rect.width ||
+	     f->height != dev->fmt_out_rect.height ||
+	     f->pixelformat != dev->fmt_out->fourcc ||
+	     f->field != dev->field_out)) {
 		dprintk(dev, 1, "%s device busy\n", __func__);
 		return -EBUSY;
 	}
@@ -478,12 +475,12 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv,
 	if (vb2_is_busy(q))
 		goto set_colorspace;
 
-	dev->fmt_out = vivid_get_format(dev, mp->pixelformat);
-	if (V4L2_FIELD_HAS_T_OR_B(mp->field))
+	dev->fmt_out = vivid_get_format(dev, f->pixelformat);
+	if (V4L2_FIELD_HAS_T_OR_B(f->field))
 		factor = 2;
 
 	if (dev->has_scaler_out || dev->has_crop_out || dev->has_compose_out) {
-		struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+		struct v4l2_rect r = { 0, 0, f->width, f->height };
 
 		if (dev->has_scaler_out) {
 			if (dev->has_crop_out)
@@ -542,30 +539,30 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv,
 			crop->height /= factor;
 		}
 	} else {
-		struct v4l2_rect r = { 0, 0, mp->width, mp->height };
+		struct v4l2_rect r = { 0, 0, f->width, f->height };
 
 		v4l2_rect_set_size_to(crop, &r);
 		r.height /= factor;
 		v4l2_rect_set_size_to(compose, &r);
 	}
 
-	dev->fmt_out_rect.width = mp->width;
-	dev->fmt_out_rect.height = mp->height;
-	for (p = 0; p < mp->num_planes; p++)
-		dev->bytesperline_out[p] = mp->plane_fmt[p].bytesperline;
+	dev->fmt_out_rect.width = f->width;
+	dev->fmt_out_rect.height = f->height;
+	for (p = 0; p < VIDEO_MAX_PLANES && f->plane_fmt[p].sizeimage; p++)
+		dev->bytesperline_out[p] = f->plane_fmt[p].bytesperline;
 	for (p = dev->fmt_out->buffers; p < dev->fmt_out->planes; p++)
 		dev->bytesperline_out[p] =
 			(dev->bytesperline_out[0] * dev->fmt_out->bit_depth[p]) /
 			dev->fmt_out->bit_depth[0];
-	dev->field_out = mp->field;
+	dev->field_out = f->field;
 	if (vivid_is_svid_out(dev))
-		dev->tv_field_out = mp->field;
+		dev->tv_field_out = f->field;
 
 set_colorspace:
-	dev->colorspace_out = mp->colorspace;
-	dev->xfer_func_out = mp->xfer_func;
-	dev->ycbcr_enc_out = mp->ycbcr_enc;
-	dev->quantization_out = mp->quantization;
+	dev->colorspace_out = f->colorspace;
+	dev->xfer_func_out = f->xfer_func;
+	dev->ycbcr_enc_out = f->ycbcr_enc;
+	dev->quantization_out = f->quantization;
 	if (dev->loop_video) {
 		vivid_send_source_change(dev, SVID);
 		vivid_send_source_change(dev, HDMI);
@@ -573,66 +570,6 @@ int vivid_s_fmt_vid_out(struct file *file, void *priv,
 	return 0;
 }
 
-int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv,
-					struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (!dev->multiplanar)
-		return -ENOTTY;
-	return vivid_g_fmt_vid_out(file, priv, f);
-}
-
-int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv,
-			struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (!dev->multiplanar)
-		return -ENOTTY;
-	return vivid_try_fmt_vid_out(file, priv, f);
-}
-
-int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv,
-			struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (!dev->multiplanar)
-		return -ENOTTY;
-	return vivid_s_fmt_vid_out(file, priv, f);
-}
-
-int vidioc_g_fmt_vid_out(struct file *file, void *priv,
-					struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (dev->multiplanar)
-		return -ENOTTY;
-	return fmt_sp2mp_func(file, priv, f, vivid_g_fmt_vid_out);
-}
-
-int vidioc_try_fmt_vid_out(struct file *file, void *priv,
-			struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (dev->multiplanar)
-		return -ENOTTY;
-	return fmt_sp2mp_func(file, priv, f, vivid_try_fmt_vid_out);
-}
-
-int vidioc_s_fmt_vid_out(struct file *file, void *priv,
-			struct v4l2_format *f)
-{
-	struct vivid_dev *dev = video_drvdata(file);
-
-	if (dev->multiplanar)
-		return -ENOTTY;
-	return fmt_sp2mp_func(file, priv, f, vivid_s_fmt_vid_out);
-}
-
 int vivid_vid_out_g_selection(struct file *file, void *priv,
 			      struct v4l2_selection *sel)
 {
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.h b/drivers/media/test-drivers/vivid/vivid-vid-out.h
index 8d56314f4ea1..b84dc578af36 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-out.h
+++ b/drivers/media/test-drivers/vivid/vivid-vid-out.h
@@ -12,15 +12,12 @@ extern const struct vb2_ops vivid_vid_out_qops;
 
 void vivid_update_format_out(struct vivid_dev *dev);
 
-int vivid_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vivid_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_g_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_out_mplane(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_g_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
-int vidioc_s_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f);
+int vivid_g_fmt_vid_out(struct file *file, void *priv,
+			struct v4l2_ext_pix_format *f);
+int vivid_try_fmt_vid_out(struct file *file, void *priv,
+			  struct v4l2_ext_pix_format *f);
+int vivid_s_fmt_vid_out(struct file *file, void *priv,
+			struct v4l2_ext_pix_format *f);
 int vivid_vid_out_g_selection(struct file *file, void *priv, struct v4l2_selection *sel);
 int vivid_vid_out_s_selection(struct file *file, void *fh, struct v4l2_selection *s);
 int vivid_vid_out_g_pixelaspect(struct file *file, void *priv, int type, struct v4l2_fract *f);
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 10/11] media: vimc: Convert to v4l2_ext_pix_format
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
                   ` (8 preceding siblings ...)
  2021-01-14 18:07 ` [RFC PATCH v6 09/11] media: vivid: Convert to v4l2_ext_pix_format Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-01-14 18:07 ` [RFC PATCH v6 11/11] media: docs: add documentation for the Extended API Helen Koike
  2021-02-05 18:39 ` [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
  11 siblings, 0 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Simplify Multi/Single planer API handling by converting to v4l2_ext_pix_format.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Signed-off-by: Helen Koike <helen.koike@collabora.com>
---
Changes in v6:
- Update with new format and buffer structs

Changes in v4:
- Update with new format and buffer structs
- Rebased on top of media/master (post 5.8-rc1)

Changes in v3:
- Rebased on top of media/master (post 5.4-rc1)

Changes in v2:
- New patch
---
 .../media/test-drivers/vimc/vimc-capture.c    | 54 ++++++++++---------
 drivers/media/test-drivers/vimc/vimc-common.c |  6 +--
 drivers/media/test-drivers/vimc/vimc-common.h |  2 +-
 3 files changed, 32 insertions(+), 30 deletions(-)

diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c
index 729614d19002..51191dc9661b 100644
--- a/drivers/media/test-drivers/vimc/vimc-capture.c
+++ b/drivers/media/test-drivers/vimc/vimc-capture.c
@@ -15,7 +15,7 @@
 struct vimc_cap_device {
 	struct vimc_ent_device ved;
 	struct video_device vdev;
-	struct v4l2_pix_format format;
+	struct v4l2_ext_pix_format format;
 	struct vb2_queue queue;
 	struct list_head buf_list;
 	/*
@@ -32,7 +32,8 @@ struct vimc_cap_device {
 	struct media_pad pad;
 };
 
-static const struct v4l2_pix_format fmt_default = {
+static const struct v4l2_ext_pix_format fmt_default = {
+	.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
 	.width = 640,
 	.height = 480,
 	.pixelformat = V4L2_PIX_FMT_RGB24,
@@ -63,7 +64,7 @@ static int vimc_cap_querycap(struct file *file, void *priv,
 }
 
 static void vimc_cap_get_format(struct vimc_ent_device *ved,
-				struct v4l2_pix_format *fmt)
+				struct v4l2_ext_pix_format *fmt)
 {
 	struct vimc_cap_device *vcap = container_of(ved, struct vimc_cap_device,
 						    ved);
@@ -72,19 +73,18 @@ static void vimc_cap_get_format(struct vimc_ent_device *ved,
 }
 
 static int vimc_cap_g_fmt_vid_cap(struct file *file, void *priv,
-				  struct v4l2_format *f)
+				  struct v4l2_ext_pix_format *f)
 {
 	struct vimc_cap_device *vcap = video_drvdata(file);
 
-	f->fmt.pix = vcap->format;
+	*f = vcap->format;
 
 	return 0;
 }
 
 static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv,
-				    struct v4l2_format *f)
+				    struct v4l2_ext_pix_format *format)
 {
-	struct v4l2_pix_format *format = &f->fmt.pix;
 	const struct vimc_pix_map *vpix;
 
 	format->width = clamp_t(u32, format->width, VIMC_FRAME_MIN_WIDTH,
@@ -99,8 +99,10 @@ static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv,
 		vpix = vimc_pix_map_by_pixelformat(format->pixelformat);
 	}
 	/* TODO: Add support for custom bytesperline values */
-	format->bytesperline = format->width * vpix->bpp;
-	format->sizeimage = format->bytesperline * format->height;
+	memset(format->plane_fmt, 0, sizeof(format->plane_fmt));
+	format->plane_fmt[0].bytesperline = format->width * vpix->bpp;
+	format->plane_fmt[0].sizeimage = format->plane_fmt[0].bytesperline *
+					 format->height;
 
 	if (format->field == V4L2_FIELD_ANY)
 		format->field = fmt_default.field;
@@ -114,7 +116,7 @@ static int vimc_cap_try_fmt_vid_cap(struct file *file, void *priv,
 }
 
 static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv,
-				  struct v4l2_format *f)
+				  struct v4l2_ext_pix_format *f)
 {
 	struct vimc_cap_device *vcap = video_drvdata(file);
 	int ret;
@@ -136,12 +138,10 @@ static int vimc_cap_s_fmt_vid_cap(struct file *file, void *priv,
 		vcap->format.quantization, vcap->format.xfer_func,
 		vcap->format.ycbcr_enc,
 		/* new */
-		f->fmt.pix.width, f->fmt.pix.height,
-		f->fmt.pix.pixelformat,	f->fmt.pix.colorspace,
-		f->fmt.pix.quantization, f->fmt.pix.xfer_func,
-		f->fmt.pix.ycbcr_enc);
+		f->width, f->height, f->pixelformat, f->colorspace,
+		f->quantization, f->xfer_func, f->ycbcr_enc);
 
-	vcap->format = f->fmt.pix;
+	vcap->format = *f;
 
 	return 0;
 }
@@ -205,9 +205,9 @@ static const struct v4l2_file_operations vimc_cap_fops = {
 static const struct v4l2_ioctl_ops vimc_cap_ioctl_ops = {
 	.vidioc_querycap = vimc_cap_querycap,
 
-	.vidioc_g_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
-	.vidioc_s_fmt_vid_cap = vimc_cap_s_fmt_vid_cap,
-	.vidioc_try_fmt_vid_cap = vimc_cap_try_fmt_vid_cap,
+	.vidioc_g_ext_pix_fmt_vid_cap = vimc_cap_g_fmt_vid_cap,
+	.vidioc_s_ext_pix_fmt_vid_cap = vimc_cap_s_fmt_vid_cap,
+	.vidioc_try_ext_pix_fmt_vid_cap = vimc_cap_try_fmt_vid_cap,
 	.vidioc_enum_fmt_vid_cap = vimc_cap_enum_fmt_vid_cap,
 	.vidioc_enum_framesizes = vimc_cap_enum_framesizes,
 
@@ -300,10 +300,11 @@ static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
 	struct vimc_cap_device *vcap = vb2_get_drv_priv(vq);
 
 	if (*nplanes)
-		return sizes[0] < vcap->format.sizeimage ? -EINVAL : 0;
+		return sizes[0] < vcap->format.plane_fmt[0].sizeimage ?
+		       -EINVAL : 0;
 	/* We don't support multiplanes for now */
 	*nplanes = 1;
-	sizes[0] = vcap->format.sizeimage;
+	sizes[0] = vcap->format.plane_fmt[0].sizeimage;
 
 	return 0;
 }
@@ -311,7 +312,7 @@ static int vimc_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
 static int vimc_cap_buffer_prepare(struct vb2_buffer *vb)
 {
 	struct vimc_cap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
-	unsigned long size = vcap->format.sizeimage;
+	unsigned long size = vcap->format.plane_fmt[0].sizeimage;
 
 	if (vb2_plane_size(vb, 0) < size) {
 		dev_err(vcap->ved.dev, "%s: buffer too small (%lu < %lu)\n",
@@ -386,11 +387,11 @@ static void *vimc_cap_process_frame(struct vimc_ent_device *ved,
 
 	vbuf = vb2_plane_vaddr(&vimc_buf->vb2.vb2_buf, 0);
 
-	memcpy(vbuf, frame, vcap->format.sizeimage);
+	memcpy(vbuf, frame, vcap->format.plane_fmt[0].sizeimage);
 
 	/* Set it as ready */
 	vb2_set_plane_payload(&vimc_buf->vb2.vb2_buf, 0,
-			      vcap->format.sizeimage);
+			      vcap->format.plane_fmt[0].sizeimage);
 	vb2_set_pixelformat(&vimc_buf->vb2.vb2_buf, vcap->format.pixelformat);
 	vb2_buffer_done(&vimc_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
 	return NULL;
@@ -449,9 +450,10 @@ static struct vimc_ent_device *vimc_cap_add(struct vimc_device *vimc,
 	/* Set default frame format */
 	vcap->format = fmt_default;
 	vpix = vimc_pix_map_by_pixelformat(vcap->format.pixelformat);
-	vcap->format.bytesperline = vcap->format.width * vpix->bpp;
-	vcap->format.sizeimage = vcap->format.bytesperline *
-				 vcap->format.height;
+	memset(vcap->format.plane_fmt, 0, sizeof(vcap->format.plane_fmt));
+	vcap->format.plane_fmt[0].bytesperline = vcap->format.width * vpix->bpp;
+	vcap->format.plane_fmt[0].sizeimage = vcap->format.plane_fmt[0].bytesperline *
+					      vcap->format.height;
 
 	/* Fill the vimc_ent_device struct */
 	vcap->ved.ent = &vcap->vdev.entity;
diff --git a/drivers/media/test-drivers/vimc/vimc-common.c b/drivers/media/test-drivers/vimc/vimc-common.c
index 7b27153c0728..8bbf80f2acb9 100644
--- a/drivers/media/test-drivers/vimc/vimc-common.c
+++ b/drivers/media/test-drivers/vimc/vimc-common.c
@@ -236,7 +236,7 @@ const struct vimc_pix_map *vimc_pix_map_by_pixelformat(u32 pixelformat)
 }
 
 static int vimc_get_pix_format(struct media_pad *pad,
-			       struct v4l2_pix_format *fmt)
+			       struct v4l2_ext_pix_format *fmt)
 {
 	if (is_media_entity_v4l2_subdev(pad->entity)) {
 		struct v4l2_subdev *sd =
@@ -252,7 +252,7 @@ static int vimc_get_pix_format(struct media_pad *pad,
 		if (ret)
 			return ret;
 
-		v4l2_fill_pix_format(fmt, &sd_fmt.format);
+		v4l2_fill_ext_pix_format_from_mbus(fmt, &sd_fmt.format);
 		pix_map = vimc_pix_map_by_code(sd_fmt.format.code);
 		fmt->pixelformat = pix_map->pixelformat;
 	} else if (is_media_entity_v4l2_video_device(pad->entity)) {
@@ -274,7 +274,7 @@ static int vimc_get_pix_format(struct media_pad *pad,
 
 int vimc_vdev_link_validate(struct media_link *link)
 {
-	struct v4l2_pix_format source_fmt, sink_fmt;
+	struct v4l2_ext_pix_format source_fmt, sink_fmt;
 	int ret;
 
 	ret = vimc_get_pix_format(link->source, &source_fmt);
diff --git a/drivers/media/test-drivers/vimc/vimc-common.h b/drivers/media/test-drivers/vimc/vimc-common.h
index a289434e75ba..42fa60350d87 100644
--- a/drivers/media/test-drivers/vimc/vimc-common.h
+++ b/drivers/media/test-drivers/vimc/vimc-common.h
@@ -104,7 +104,7 @@ struct vimc_ent_device {
 	void * (*process_frame)(struct vimc_ent_device *ved,
 				const void *frame);
 	void (*vdev_get_format)(struct vimc_ent_device *ved,
-			      struct v4l2_pix_format *fmt);
+				struct v4l2_ext_pix_format *fmt);
 };
 
 /**
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* [RFC PATCH v6 11/11] media: docs: add documentation for the Extended API
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
                   ` (9 preceding siblings ...)
  2021-01-14 18:07 ` [RFC PATCH v6 10/11] media: vimc: " Helen Koike
@ 2021-01-14 18:07 ` Helen Koike
  2021-02-05 18:39 ` [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
  11 siblings, 0 replies; 48+ messages in thread
From: Helen Koike @ 2021-01-14 18:07 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Add documentation and update references in current documentation for the
Extended API.

Signed-off-by: Helen Koike <helen.koike@collabora.com>
---
Changes in v6:
- Update note saying ext_api should be used for new applications on
  newer kernels (Tomasz and Hans)
- Fix typos pointed in v5 (Hand and Tomasz)
- Change order, mention Ext first in format.rst (Tomasz)
- Mention planes[i].offset should be set to zero for userptr
- Remove ext_create_buf and ext_prep_buf from the docs
- s/displayed/consumed for output (Tomasz)
- Remove references for plane length
- Drop EIO sentence mentioning signal loss (Hans)
- Removed first half of the note in EIO (Tomas and Hans)
- Update text to mention EXT_TRY_FMT is mandatory (Hans and Tomasz)
- Remove requirement to fill `memory` field for dqbuf (Tomasz)
- EXT_DQBUF sets `m` field to zero (Tomasz for DMA-fd)

Changes in v5:
- new patch
---
 .../userspace-api/media/v4l/buffer.rst        |   5 +
 .../userspace-api/media/v4l/common.rst        |   1 +
 .../userspace-api/media/v4l/dev-capture.rst   |   6 +
 .../userspace-api/media/v4l/dev-output.rst    |   6 +
 .../userspace-api/media/v4l/ext-api.rst       |  89 +++++++++
 .../userspace-api/media/v4l/format.rst        |  18 +-
 .../userspace-api/media/v4l/user-func.rst     |   5 +
 .../media/v4l/vidioc-ext-qbuf.rst             | 188 ++++++++++++++++++
 .../media/v4l/vidioc-g-ext-pix-fmt.rst        | 116 +++++++++++
 .../userspace-api/media/v4l/vidioc-qbuf.rst   |   2 +-
 10 files changed, 431 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/userspace-api/media/v4l/ext-api.rst
 create mode 100644 Documentation/userspace-api/media/v4l/vidioc-ext-qbuf.rst
 create mode 100644 Documentation/userspace-api/media/v4l/vidioc-g-ext-pix-fmt.rst

diff --git a/Documentation/userspace-api/media/v4l/buffer.rst b/Documentation/userspace-api/media/v4l/buffer.rst
index 1b0fdc160533..89652fa7a098 100644
--- a/Documentation/userspace-api/media/v4l/buffer.rst
+++ b/Documentation/userspace-api/media/v4l/buffer.rst
@@ -21,6 +21,11 @@ such as pointers and sizes for each plane, are stored in
 struct :c:type:`v4l2_plane` instead. In that case,
 struct :c:type:`v4l2_buffer` contains an array of plane structures.
 
+.. note::
+
+    For modern applications on newer kernels, these operations are replaced
+    by their :ref:`ext_api` counterparts, which should be used instead.
+
 Dequeued video buffers come with timestamps. The driver decides at which
 part of the frame and with which clock the timestamp is taken. Please
 see flags in the masks ``V4L2_BUF_FLAG_TIMESTAMP_MASK`` and
diff --git a/Documentation/userspace-api/media/v4l/common.rst b/Documentation/userspace-api/media/v4l/common.rst
index 8c263c5a85d8..61a64065f9f3 100644
--- a/Documentation/userspace-api/media/v4l/common.rst
+++ b/Documentation/userspace-api/media/v4l/common.rst
@@ -53,6 +53,7 @@ applicable to all devices.
     ext-ctrls-detect
     fourcc
     format
+    ext-api
     planar-apis
     selection-api
     crop
diff --git a/Documentation/userspace-api/media/v4l/dev-capture.rst b/Documentation/userspace-api/media/v4l/dev-capture.rst
index fe58fd450e2f..73580e16057c 100644
--- a/Documentation/userspace-api/media/v4l/dev-capture.rst
+++ b/Documentation/userspace-api/media/v4l/dev-capture.rst
@@ -93,6 +93,12 @@ and :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl, even if :ref:`VIDIOC_S_FMT <VIDIOC
 requests and always returns default parameters as :ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` does.
 :ref:`VIDIOC_TRY_FMT <VIDIOC_G_FMT>` is optional.
 
+.. note::
+
+    For modern applications on newer kernels, these operations are replaced
+    by their :ref:`ext_api` counterparts, which should be used instead.
+
+
 Reading Images
 ==============
 
diff --git a/Documentation/userspace-api/media/v4l/dev-output.rst b/Documentation/userspace-api/media/v4l/dev-output.rst
index eadcb4aa813b..676578c099b6 100644
--- a/Documentation/userspace-api/media/v4l/dev-output.rst
+++ b/Documentation/userspace-api/media/v4l/dev-output.rst
@@ -90,6 +90,12 @@ and :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl, even if :ref:`VIDIOC_S_FMT <VIDIOC
 requests and always returns default parameters as :ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` does.
 :ref:`VIDIOC_TRY_FMT <VIDIOC_G_FMT>` is optional.
 
+.. note::
+
+    For modern applications on newer kernels, these operations are replaced
+    by their :ref:`ext_api` counterparts, which should be used instead.
+
+
 Writing Images
 ==============
 
diff --git a/Documentation/userspace-api/media/v4l/ext-api.rst b/Documentation/userspace-api/media/v4l/ext-api.rst
new file mode 100644
index 000000000000..e73d5b77a550
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/ext-api.rst
@@ -0,0 +1,89 @@
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/userspace-api/media/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _ext_api:
+
+*************
+Extended API
+*************
+
+Introduction
+============
+
+The Extended Format API was conceived to extend V4L2 capabilities and
+to simplify certain mechanisms.
+It unifies single- vs multi- planar handling,
+brings the concept of pixelformat + modifiers, allows planes to be placed
+in any offset inside a buffer and let userspace to change colorspace
+attributes if supported by the driver.
+
+Data format negotiation and buffer handling works very similar to the classical
+version, thus in this document we mention only the main differences.
+
+Data Format Negotiation
+=======================
+
+The API replaces the classical ioctls:
+
+:ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>`, :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>`,
+:ref:`VIDIOC_TRY_FMT <VIDIOC_G_FMT>`
+(with :c:type:`v4l2_format` as the main parameter).
+
+By the extended versions:
+
+:ref:`VIDIOC_G_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>`,
+:ref:`VIDIOC_S_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>`,
+:ref:`VIDIOC_TRY_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>`
+(with :c:type:`v4l2_ext_pix_format` as the main parameter).
+
+For CAPTURE and OUTPUT queues only.
+
+The ``type`` field of struct :c:type:`v4l2_ext_pix_format` only accepts 
+``V4L2_BUF_TYPE_VIDEO_CAPTURE`` or ``V4L2_BUF_TYPE_VIDEO_OUTPUT``.
+
+The ``plane_fmt`` field is an array which holds information by plane using
+the :c:type:`v4l2_plane_pix_format`. When this struct is filled, its
+``sizeimage`` field should be non-zero for all valid planes for a given
+``pixelformat`` + ``modifier`` combination, and zeroed for the invalid ones.
+
+Colorspace attributes are not read-only as in the classical version, i.e, they
+can be set by application and drivers will adjust accordingly depending on what
+is supported by the underlying hardware.
+
+Buffers
+=======
+
+The API replaces the classical ioctls:
+
+:ref:`VIDIOC_QBUF <VIDIOC_QBUF>`,
+:ref:`VIDIOC_DQBUF <VIDIOC_QBUF>`,
+(with :c:type:`v4l2_buffer` as the main parameter)
+
+By the extended versions:
+
+:ref:`VIDIOC_EXT_QBUF <VIDIOC_EXT_QBUF>`,
+:ref:`VIDIOC_EXT_DQBUF <VIDIOC_EXT_QBUF>`,
+(with :c:type:`v4l2_ext_buffer` as the main parameter)
+
+Comparing :c:type:`v4l2_ext_buffer` with :c:type:`v4l2_buffer`, in the
+extended version there is a unification of the single-/multi- planar handling
+through the ``planes`` field of type :c:type:`v4l2_ext_plane`.
+
+The :c:type:`v4l2_ext_plane` also allows planes to be placed in a given offset
+inside a buffer.
+Planes can be placed in different locations inside the same buffer, or in
+different buffers.
+
+
+.. kernel-doc:: include/uapi/linux/videodev2.h
+   :functions: v4l2_ext_plane
+
+
+.. kernel-doc:: include/uapi/linux/videodev2.h
+   :functions: v4l2_ext_buffer
diff --git a/Documentation/userspace-api/media/v4l/format.rst b/Documentation/userspace-api/media/v4l/format.rst
index 35bbb2fea46e..713e1a9f699a 100644
--- a/Documentation/userspace-api/media/v4l/format.rst
+++ b/Documentation/userspace-api/media/v4l/format.rst
@@ -21,13 +21,19 @@ format and the driver selects and reports the best the hardware can do
 to satisfy the request. Of course applications can also just query the
 current selection.
 
-A single mechanism exists to negotiate all data formats using the
-aggregate struct :c:type:`v4l2_format` and the
+There are two mechanism to negotiate data formats:
+
+The first one is through the :ref:`ext_api`, please refer to its documentation
+for more information.
+
+The second one is using the aggregate struct :c:type:`v4l2_format` and the
 :ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` and
 :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctls. Additionally the
 :ref:`VIDIOC_TRY_FMT <VIDIOC_G_FMT>` ioctl can be used to examine
 what the hardware *could* do, without actually selecting a new data
-format. The data formats supported by the V4L2 API are covered in the
+format.
+
+The data formats supported by the V4L2 API are covered in the
 respective device section in :ref:`devices`. For a closer look at
 image formats see :ref:`pixfmt`.
 
@@ -64,8 +70,12 @@ earlier versions of V4L2. Switching the logical stream or returning into
 *may* support a switch using :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>`.
 
 All drivers exchanging data with applications must support the
-:ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` and :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl. Implementation of the
+:ref:`VIDIOC_G_FMT <VIDIOC_G_FMT>` and :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl
+or the ref:`Extended <VIDIOC_G_EXT_PIX_FMT>` respective versions.
+Implementation of the
 :ref:`VIDIOC_TRY_FMT <VIDIOC_G_FMT>` is highly recommended but optional.
+For the Extended API, ref:`VIDIOC_TRY_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` is
+mandatory.
 
 Image Format Enumeration
 ========================
diff --git a/Documentation/userspace-api/media/v4l/user-func.rst b/Documentation/userspace-api/media/v4l/user-func.rst
index 53e604bd7d60..3e21a780033d 100644
--- a/Documentation/userspace-api/media/v4l/user-func.rst
+++ b/Documentation/userspace-api/media/v4l/user-func.rst
@@ -13,6 +13,7 @@ Function Reference
     func-close
     func-ioctl
     vidioc-create-bufs
+    vidioc-ext-create-bufs
     vidioc-cropcap
     vidioc-dbg-g-chip-info
     vidioc-dbg-g-register
@@ -41,6 +42,7 @@ Function Reference
     vidioc-g-ext-ctrls
     vidioc-g-fbuf
     vidioc-g-fmt
+    vidioc-g-ext-pix-fmt
     vidioc-g-frequency
     vidioc-g-input
     vidioc-g-jpegcomp
@@ -55,8 +57,11 @@ Function Reference
     vidioc-log-status
     vidioc-overlay
     vidioc-prepare-buf
+    vidioc-ext-prepare-buf
     vidioc-qbuf
+    vidioc-ext-qbuf
     vidioc-querybuf
+    vidioc-ext-querybuf
     vidioc-querycap
     vidioc-queryctrl
     vidioc-query-dv-timings
diff --git a/Documentation/userspace-api/media/v4l/vidioc-ext-qbuf.rst b/Documentation/userspace-api/media/v4l/vidioc-ext-qbuf.rst
new file mode 100644
index 000000000000..d137eddc331e
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/vidioc-ext-qbuf.rst
@@ -0,0 +1,188 @@
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/userspace-api/media/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _VIDIOC_EXT_QBUF:
+
+***************************************
+ioctl VIDIOC_EXT_QBUF, VIDIOC_EXT_DQBUF
+***************************************
+
+Name
+====
+
+VIDIOC_EXT_QBUF - VIDIOC_EXT_DQBUF - Exchange a buffer with the driver
+
+
+Synopsis
+========
+
+.. c:function:: int ioctl( int fd, VIDIOC_EXT_QBUF, struct v4l2_ext_buffer *argp )
+    :name: VIDIOC_EXT_QBUF
+
+.. c:function:: int ioctl( int fd, VIDIOC_EXT_DQBUF, struct v4l2_ext_buffer *argp )
+    :name: VIDIOC_EXT_DQBUF
+
+
+Arguments
+=========
+
+``fd``
+    File descriptor returned by :ref:`open() <func-open>`.
+
+``argp``
+    Pointer to struct :c:type:`v4l2_ext_buffer`.
+
+
+Description
+===========
+
+Applications call the ``VIDIOC_EXT_QBUF`` ioctl to enqueue an empty
+(capturing) or filled (output) buffer in the driver's incoming queue.
+The semantics depend on the selected I/O method.
+
+To enqueue a buffer applications set the ``type`` field of a struct
+:c:type:`v4l2_ext_buffer` to the same buffer type as was
+previously used with struct :c:type:`v4l2_ext_pix_format` ``type``.
+Applications must also set the ``index`` field. Valid index numbers
+range from zero to the number of buffers allocated with
+:ref:`VIDIOC_REQBUFS` (struct
+:c:type:`v4l2_requestbuffers` ``count``) minus
+one.
+When the buffer is intended for output (``type`` is
+``V4L2_BUF_TYPE_VIDEO_OUTPUT``) applications must also initialize the
+``timestamp`` fields.
+see :ref:`buffer` for details. Applications must also set ``flags`` to 0. The
+``reserved`` field must be set to 0.
+
+To enqueue a :ref:`memory mapped <mmap>` buffer applications set the
+``memory`` field to ``V4L2_MEMORY_MMAP``.
+When ``VIDIOC_EXT_QBUF`` is called with a pointer to this structure
+the driver sets the ``V4L2_BUF_FLAG_MAPPED`` and ``V4L2_BUF_FLAG_QUEUED``
+flags and clears the ``V4L2_BUF_FLAG_DONE`` flag in the ``flags`` field, or
+it returns an ``EINVAL`` error code.
+
+To enqueue a :ref:`user pointer <userp>` buffer applications set the
+``memory`` field to ``V4L2_MEMORY_USERPTR``, the ``planes[i].m.userptr``
+field to the address of the buffer and ``planes[i].offset`` should be zero.
+When ``VIDIOC_EXT_QBUF`` is called with a pointer to this structure
+the driver sets the ``V4L2_BUF_FLAG_QUEUED`` flag and clears the
+``V4L2_BUF_FLAG_MAPPED`` and ``V4L2_BUF_FLAG_DONE`` flags in the
+``flags`` field, or it returns an error code. This ioctl locks the
+memory pages of the buffer in physical memory, they cannot be swapped
+out to disk.
+Buffers remain locked until dequeued, until the
+:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` or
+:ref:`VIDIOC_REQBUFS` ioctl is called, or until the
+device is closed.
+
+To enqueue a :ref:`DMABUF <dmabuf>` buffer applications set the
+``memory`` field to ``V4L2_MEMORY_DMABUF``, the ``planes[i].m.fd`` field to a
+file descriptor associated with a DMABUF buffer
+and ``planes[i].offset`` of the plane in the memory buffer.
+When ``VIDIOC_EXT_QBUF`` is called with a pointer to this structure the driver
+sets the ``V4L2_BUF_FLAG_QUEUED`` flag and clears the
+``V4L2_BUF_FLAG_MAPPED`` and ``V4L2_BUF_FLAG_DONE`` flags in the
+``flags`` field, or it returns an error code. This ioctl locks the
+buffer. Locking a buffer means passing it to a driver for a hardware
+access (usually DMA). If an application accesses (reads/writes) a locked
+buffer then the result is undefined.
+Buffers remain locked until dequeued, until the
+:ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` or
+:ref:`VIDIOC_REQBUFS` ioctl is called, or until the
+device is closed.
+
+The ``request_fd`` field can be used with the ``VIDIOC_EXT_QBUF`` ioctl to specify
+the file descriptor of a :ref:`request <media-request-api>`, if requests are
+in use. Setting it means that the buffer will not be passed to the driver
+until the request itself is queued. Also, the driver will apply any
+settings associated with the request for this buffer. This field will
+be ignored unless the ``V4L2_BUF_FLAG_REQUEST_FD`` flag is set.
+If the device does not support requests, then ``EBADR`` will be returned.
+If requests are supported but an invalid request file descriptor is given,
+then ``EINVAL`` will be returned.
+
+.. caution::
+   It is not allowed to mix queuing requests with queuing buffers directly.
+   ``EBUSY`` will be returned if the first buffer was queued directly and
+   then the application tries to queue a request, or vice versa. After
+   closing the file descriptor, calling
+   :ref:`VIDIOC_STREAMOFF <VIDIOC_STREAMON>` or calling :ref:`VIDIOC_REQBUFS`
+   the check for this will be reset.
+
+   For :ref:`memory-to-memory devices <mem2mem>` you can specify the
+   ``request_fd`` only for output buffers, not for capture buffers. Attempting
+   to specify this for a capture buffer will result in an ``EBADR`` error.
+
+Applications call the ``VIDIOC_EXT_DQBUF`` ioctl to dequeue a filled
+(capturing) or displayed (output) buffer from the driver's outgoing
+queue. They just set the ``type`` and ``reserved`` fields of
+a struct :c:type:`v4l2_ext_buffer` as above, when
+``VIDIOC_EXT_DQBUF`` is called with a pointer to this structure the driver
+fills the remaining fields or returns an error code. The driver may also
+set ``V4L2_BUF_FLAG_ERROR`` in the ``flags`` field. It indicates a
+non-critical (recoverable) streaming error. In such case the application
+may continue as normal, but should be aware that data in the dequeued
+buffer might be corrupted.
+
+Pointers in ``m`` field is set to zero. Applications must track the buffers
+in queuing time.
+
+By default ``VIDIOC_EXT_DQBUF`` blocks when no buffer is in the outgoing
+queue. When the ``O_NONBLOCK`` flag was given to the
+:ref:`open() <func-open>` function, ``VIDIOC_EXT_DQBUF`` returns
+immediately with an ``EAGAIN`` error code when no buffer is available.
+
+
+.. kernel-doc:: include/uapi/linux/videodev2.h
+   :functions: v4l2_ext_buffers
+
+
+Return Value
+============
+
+On success 0 is returned, on error -1 and the ``errno`` variable is set
+appropriately. The generic error codes are described at the
+:ref:`Generic Error Codes <gen-errors>` chapter.
+
+EAGAIN
+    Non-blocking I/O has been selected using ``O_NONBLOCK`` and no
+    buffer was in the outgoing queue.
+
+EINVAL
+    The buffer ``type`` is not supported, or the ``index`` is out of
+    bounds, or no buffers have been allocated yet, or the ``userptr``
+    are invalid, or the ``V4L2_BUF_FLAG_REQUEST_FD`` flag was
+    set but the the given ``request_fd`` was invalid, or ``m.fd`` was
+    an invalid DMABUF file descriptor.
+
+EIO
+    ``VIDIOC_EXT_DQBUF`` failed due to an internal error.
+
+    .. note::
+
+       It is recommended that drivers indicate recoverable errors by setting
+       the ``V4L2_BUF_FLAG_ERROR`` and returning 0 instead. In that case the
+       application should be able to safely reuse the buffer and continue
+       streaming.
+
+EPIPE
+    ``VIDIOC_EXT_DQBUF`` returns this on an empty capture queue for mem2mem
+    codecs if a buffer with the ``V4L2_BUF_FLAG_LAST`` was already
+    dequeued and no new buffers are expected to become available.
+
+EBADR
+    The ``V4L2_BUF_FLAG_REQUEST_FD`` flag was set but the device does not
+    support requests for the given buffer type, or
+    the ``V4L2_BUF_FLAG_REQUEST_FD`` flag was not set but the device requires
+    that the buffer is part of a request.
+
+EBUSY
+    The first buffer was queued via a request, but the application now tries
+    to queue it directly, or vice versa (it is not permitted to mix the two
+    APIs).
diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-pix-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-pix-fmt.rst
new file mode 100644
index 000000000000..6cb14390dc51
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-pix-fmt.rst
@@ -0,0 +1,116 @@
+.. Permission is granted to copy, distribute and/or modify this
+.. document under the terms of the GNU Free Documentation License,
+.. Version 1.1 or any later version published by the Free Software
+.. Foundation, with no Invariant Sections, no Front-Cover Texts
+.. and no Back-Cover Texts. A copy of the license is included at
+.. Documentation/userspace-api/media/fdl-appendix.rst.
+..
+.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections
+
+.. _VIDIOC_G_EXT_PIX_FMT:
+
+************************************************************************
+ioctl VIDIOC_G_EXT_PIX_FMT, VIDIOC_S_EXT_PIX_FMT, VIDIOC_TRY_EXT_PIX_FMT
+************************************************************************
+
+Name
+====
+
+VIDIOC_G_EXT_PIX_FMT - VIDIOC_S_EXT_PIX_FMT - VIDIOC_TRY_EXT_PIX_FMT - Get or set the data format, try a format
+
+
+Synopsis
+========
+
+.. c:function:: int ioctl( int fd, VIDIOC_G_EXT_PIX_FMT, struct v4l2_ext_pix_format *argp )
+    :name: VIDIOC_G_EXT_PIX_FMT
+
+.. c:function:: int ioctl( int fd, VIDIOC_S_EXT_PIX_FMT, struct v4l2_ext_pix_format *argp )
+    :name: VIDIOC_S_EXT_PIX_FMT
+
+.. c:function:: int ioctl( int fd, VIDIOC_TRY_EXT_PIX_FMT, struct v4l2_ext_pix_format *argp )
+    :name: VIDIOC_TRY_EXT_PIX_FMT
+
+Arguments
+=========
+
+``fd``
+    File descriptor returned by :ref:`open() <func-open>`.
+
+``argp``
+    Pointer to struct :c:type:`v4l2_ext_pix_format`.
+
+
+Description
+===========
+
+These ioctls are used to negotiate the format of data (typically image
+format) exchanged between driver and application.
+
+To query the current parameters applications set the ``type`` field of a
+struct :c:type:`v4l2_ext_pix_format` to  ``V4L2_BUF_TYPE_VIDEO_CAPTURE`` or
+``V4L2_BUF_TYPE_VIDEO_OUTPUT``, all the other types are invalid in this API,
+and multiplanar is supported through modifiers.
+
+When the application calls the
+:ref:`VIDIOC_G_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` ioctl with a pointer to this
+structure the driver fills the other members.
+When the requested buffer type is not supported drivers return
+an ``EINVAL`` error code.
+
+To change the current format parameters applications initialize all
+the fields in the struct.
+For details see the documentation of the various devices types in
+:ref:`devices`. Good practice is to query the current parameters
+first, and to modify only those parameters not suitable for the
+application. When the application calls the :ref:`VIDIOC_S_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` ioctl with
+a pointer to a struct :c:type:`v4l2_ext_pix_format` structure the driver
+checks and adjusts the parameters against hardware abilities. Drivers
+should not return an error code unless the ``type`` field is invalid,
+this is a mechanism to fathom device capabilities and to approach
+parameters acceptable for both the application and driver. On success
+the driver may program the hardware, allocate resources and generally
+prepare for data exchange. Finally the :ref:`VIDIOC_S_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` ioctl returns
+the current format parameters as :ref:`VIDIOC_G_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` does. Very simple,
+inflexible devices may even ignore all input and always return the
+default parameters. However all V4L2 devices exchanging data with the
+application must implement the :ref:`VIDIOC_G_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` and :ref:`VIDIOC_S_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>`
+ioctl. When the requested buffer type is not supported drivers return an
+EINVAL error code on a :ref:`VIDIOC_S_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` attempt. When I/O is already in
+progress or the resource is not available for other reasons drivers
+return the ``EBUSY`` error code.
+
+The :ref:`VIDIOC_TRY_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` ioctl is equivalent to :ref:`VIDIOC_S_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` with one
+exception: it does not change driver state. It can also be called at any
+time, never returning ``EBUSY``. This function is provided to negotiate
+parameters, to learn about hardware limitations, without disabling I/O
+or possibly time consuming hardware preparations.
+
+The format as returned by :ref:`VIDIOC_TRY_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` must be identical to what
+:ref:`VIDIOC_S_EXT_PIX_FMT <VIDIOC_G_EXT_PIX_FMT>` returns for the same input or output.
+
+
+.. kernel-doc:: include/uapi/linux/videodev2.h
+   :functions: v4l2_plane_pix_format
+
+
+.. kernel-doc:: include/uapi/linux/videodev2.h
+   :functions: v4l2_ext_pix_format
+
+
+Return Value
+============
+
+On success 0 is returned, on error -1 and the ``errno`` variable is set
+appropriately. The generic error codes are described at the
+:ref:`Generic Error Codes <gen-errors>` chapter.
+
+EINVAL
+    The struct :c:type:`v4l2_ext_pix_format` ``type`` field is
+    invalid or the requested buffer type not supported.
+
+EBUSY
+    The device is busy and cannot change the format. This could be
+    because or the device is streaming or buffers are allocated or
+    queued to the driver. Relevant for :ref:`VIDIOC_S_EXT_PIX_FMT
+    <VIDIOC_G_EXT_PIX_FMT>` only.
diff --git a/Documentation/userspace-api/media/v4l/vidioc-qbuf.rst b/Documentation/userspace-api/media/v4l/vidioc-qbuf.rst
index 77e0747a6d28..7f2aae992348 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-qbuf.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-qbuf.rst
@@ -121,7 +121,7 @@ then ``EINVAL`` will be returned.
    to specify this for a capture buffer will result in an ``EBADR`` error.
 
 Applications call the ``VIDIOC_DQBUF`` ioctl to dequeue a filled
-(capturing) or displayed (output) buffer from the driver's outgoing
+(capturing) or consumed (output) buffer from the driver's outgoing
 queue. They just set the ``type``, ``memory`` and ``reserved`` fields of
 a struct :c:type:`v4l2_buffer` as above, when
 ``VIDIOC_DQBUF`` is called with a pointer to this structure the driver
-- 
2.29.2


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls
  2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
                   ` (10 preceding siblings ...)
  2021-01-14 18:07 ` [RFC PATCH v6 11/11] media: docs: add documentation for the Extended API Helen Koike
@ 2021-02-05 18:39 ` Helen Koike
  11 siblings, 0 replies; 48+ messages in thread
From: Helen Koike @ 2021-02-05 18:39 UTC (permalink / raw)
  To: linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Hello,

On 1/14/21 3:07 PM, Helen Koike wrote:
> Hello,
> 
> This is v6 of the Extended API for formats and buffers (see below the new API).
> 
> The new API comes for free for old drivers through the conversion layer, which
> is independent of vb2.
> 
> I completly refactored several patches. I would like to request comments not
> only in the uAPI, but also the kAPI for drivers, and I would appreciate any
> ideas on improving the quality of the code (in short: please review everything).
> 
> NOTE: The Ext API wans't tested yet. My next step is to patch v4l2-compliance.

I implemented on libcamera to test it, please check:

    https://lists.libcamera.org/pipermail/libcamera-devel/2021-February/017169.html

Thanks,
Helen

> 
> Regression tests - v4l2-compliance with test-media script:
> 	vivid: http://ix.io/2M0G - Final Summary: 1856, Succeeded: 1856, Failed: 0, Warnings: 0)
> 	vimc: http://ix.io/2M0I - Final Summary: 488, Succeeded: 488, Failed: 0, Warnings: 0
> 
> Git: https://gitlab.collabora.com/koike/linux/-/tree/v4l2/ext-api/v6
> 
> v5: https://patchwork.linuxtv.org/project/linux-media/cover/20200804192939.2251988-1-helen.koike@collabora.com/
> v4: https://patchwork.linuxtv.org/project/linux-media/cover/20200717115435.2632623-1-helen.koike@collabora.com/
> v3: https://patchwork.linuxtv.org/cover/59345/
> v2: https://patchwork.kernel.org/project/linux-media/list/?series=101153
> v1: https://patchwork.kernel.org/project/linux-media/list/?series=93707
> 
> Conversion layer:
> =================
> 
> * Old drivers implementing only ops->vidioc_*_fmt_vid_cap supports
>   VIDIOC_*_EXT_PIX_FMT automatically with limitations[1].
> 
> * New drivers implementing only ops->vidioc_*_ext_pix_fmt_vid_cap supports
>   VIDIOC_*_FMT automatically.
> 
> * Old drivers implementing only ops->vidioc_*buf support
>   VIDIOC_EXT_*BUF automatically with limitations[2].
> 
> * New drivers should implement both ops->vidioc_*buf and ops->vidioc_*buf
>   to overcome limitations[2] and support both APIs.
>   Which is easy with vb2:
>      static const struct v4l2_ioctl_ops ioctl_ops = {
>      ...
>      +      .vidioc_ext_qbuf = vb2_ioctl_ext_qbuf,
>      +      .vidioc_ext_dqbuf = vb2_ioctl_ext_dqbuf,
>      ...
>      }
>      ...
>      +      /* Inform vb2 how to split the memory buffer in case a single one is used */
>      +      vb2_set_pixelformat(dev->pixelformat)
> 
> [1] There are some limitations in the conversion such as modifiers that are
>     ignored when converting v4l2_ext_pix_format to v4l_format
> 
> [2] Ext API allows a single buffer with planes placed in random locations,
>     which is not possible with v4l2_buffer.
> 
> 
> Major changes in v6:
> ====================
> 
> Fixed color planes vs memory planes handling.
> 
> Removed VIDIOC_EXT_PREPARE_BUF, since this is an optimization, it doesn't blocks
> the API, we can add it later (my goal was to simplify this patchset).
> 
> Removed VIDIOC_EXT_CREATE_BUFS, since this is useful only to MMAP (thus low priority)
> with the new format.
> Classic VIDIOC_CREATE_BUFS and VIDIOC_REQBUFS can still be used.
> 
> Reformulated conversion layer as per above.
> 
> Removed conversions in vb2, it is easier to add hooks to drivers.
> 
> Fixed vb2 to allow Ext API only to Video types.
> 
> API updates:
> * remove buffer and plane lengths
> * move `memory` field to v4l2_ext_buffer instead of v4l2_ext_plane
> * remove struct v4l2_plane_ext_pix_format
> * reordering
> 
> Make Ext API valid only for Video types, and not for touch, vbi, meta, etc.
> 
> Sereval code refactoring, simplification, fixes and applied suggestions from v5.
> 
> New API (for convenience):
> ==========================
> 
> int ioctl(int fd, VIDIOC_G_EXT_PIX_FMT, struct v4l2_ext_pix_format *argp)
> int ioctl(int fd, VIDIOC_S_EXT_PIX_FMT, struct v4l2_ext_pix_format *argp)
> int ioctl(int fd, VIDIOC_TRY_EXT_PIX_FMT, struct v4l2_ext_pix_format *argp)
> int ioctl(int fd, VIDIOC_EXT_QBUF, struct v4l2_ext_buffer *argp)
> int ioctl(int fd, VIDIOC_EXT_DQBUF, struct v4l2_ext_buffer *argp)
> 
> struct v4l2_ext_pix_format {
> 	__u32 type;
> 	__u32 width;
> 	__u32 height;
> 	__u32 field;
> 	struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
> 	__u32 pixelformat;
> 	__u64 modifier;
> 	__u32 colorspace;
> 	__u32 xfer_func;
> 	union {
> 		__u32 ycbcr_enc;
> 		__u32 hsv_enc;
> 	};
> 	__u32 quantization;
> 	__u32 reserved[9];
> };
> 
> struct v4l2_ext_buffer {
> 	__u32 index;
> 	__u32 type;
> 	__u32 field;
> 	__u32 sequence;
> 	__u64 flags;
> 	__u64 timestamp;
> 	__u32 memory;
> 	__s32 request_fd;
> 	struct v4l2_ext_plane planes[VIDEO_MAX_PLANES];
> 	__u32 reserved[10];
> };
> 
> struct v4l2_ext_plane {
> 	__u32 offset;
> 	__u32 bytesused;
> 	union {
> 		__u32 mmap_offset;
> 		__u64 userptr;
> 		__s32 dmabuf_fd;
> 	} m;
> 	__u32 reserved[6];
> };
> 
> Helen Koike (11):
>   media: v4l2-common: add normalized pixelformat field to struct
>     v4l2_format_info
>   media: v4l2: Extend pixel formats to unify single/multi-planar
>     handling (and more)
>   media: v4l2: Add extended buffer (de)queue operations for video types
>   media: videobuf2-v4l2: reorganize flags handling
>   media: videobuf2: Expose helpers for Ext qbuf/dqbuf
>   media: vivid: use vb2_ioctls_ext_{d}qbuf hooks
>   media: vimc: use vb2_ioctls_ext_{d}qbuf hooks
>   media: mediabus: Add helpers to convert a ext_pix format to/from a
>     mbus_fmt
>   media: vivid: Convert to v4l2_ext_pix_format
>   media: vimc: Convert to v4l2_ext_pix_format
>   media: docs: add documentation for the Extended API
> 
>  .../userspace-api/media/v4l/buffer.rst        |   5 +
>  .../userspace-api/media/v4l/common.rst        |   1 +
>  .../userspace-api/media/v4l/dev-capture.rst   |   6 +
>  .../userspace-api/media/v4l/dev-output.rst    |   6 +
>  .../userspace-api/media/v4l/ext-api.rst       |  89 +++
>  .../userspace-api/media/v4l/format.rst        |  18 +-
>  .../userspace-api/media/v4l/user-func.rst     |   5 +
>  .../media/v4l/vidioc-ext-qbuf.rst             | 188 +++++
>  .../media/v4l/vidioc-g-ext-pix-fmt.rst        | 116 +++
>  .../userspace-api/media/v4l/vidioc-qbuf.rst   |   2 +-
>  .../media/common/videobuf2/videobuf2-core.c   |  46 +-
>  .../media/common/videobuf2/videobuf2-v4l2.c   | 500 +++++++++---
>  .../media/test-drivers/vimc/vimc-capture.c    |  57 +-
>  drivers/media/test-drivers/vimc/vimc-common.c |   6 +-
>  drivers/media/test-drivers/vimc/vimc-common.h |   2 +-
>  drivers/media/test-drivers/vivid/vivid-core.c | 209 ++---
>  .../media/test-drivers/vivid/vivid-vid-cap.c  | 203 ++---
>  .../media/test-drivers/vivid/vivid-vid-cap.h  |  15 +-
>  .../media/test-drivers/vivid/vivid-vid-out.c  | 198 ++---
>  .../media/test-drivers/vivid/vivid-vid-out.h  |  15 +-
>  drivers/media/v4l2-core/v4l2-common.c         |  16 +-
>  drivers/media/v4l2-core/v4l2-dev.c            |  31 +-
>  drivers/media/v4l2-core/v4l2-ioctl.c          | 722 +++++++++++++++++-
>  include/media/v4l2-common.h                   |   3 +
>  include/media/v4l2-ioctl.h                    |  36 +
>  include/media/v4l2-mediabus.h                 |  42 +
>  include/media/videobuf2-core.h                |  33 +-
>  include/media/videobuf2-v4l2.h                |   8 +-
>  include/uapi/linux/videodev2.h                |  96 +++
>  29 files changed, 2131 insertions(+), 543 deletions(-)
>  create mode 100644 Documentation/userspace-api/media/v4l/ext-api.rst
>  create mode 100644 Documentation/userspace-api/media/v4l/vidioc-ext-qbuf.rst
>  create mode 100644 Documentation/userspace-api/media/v4l/vidioc-g-ext-pix-fmt.rst
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 01/11] media: v4l2-common: add normalized pixelformat field to struct v4l2_format_info
  2021-01-14 18:07 ` [RFC PATCH v6 01/11] media: v4l2-common: add normalized pixelformat field to struct v4l2_format_info Helen Koike
@ 2021-02-10 12:37   ` Dafna Hirschfeld
  0 siblings, 0 replies; 48+ messages in thread
From: Dafna Hirschfeld @ 2021-02-10 12:37 UTC (permalink / raw)
  To: Helen Koike, linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Hi!

Am 14.01.21 um 19:07 schrieb Helen Koike:
> Add normalization to pixelformats, so we can fallback to it when using
> Ext API, and eliminating the handling of two variantes (M and non-M
> formats).
> 
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> 
> ---
> Changes in v6:
> - New patch
> ---
>   drivers/media/v4l2-core/v4l2-common.c | 16 ++++++++--------
>   include/media/v4l2-common.h           |  3 +++
>   2 files changed, 11 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
> index 78007dba4677..002051b9dc0c 100644
> --- a/drivers/media/v4l2-core/v4l2-common.c
> +++ b/drivers/media/v4l2-core/v4l2-common.c
> @@ -276,17 +276,17 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
>   		{ .format = V4L2_PIX_FMT_GREY,    .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
>   
>   		/* YUV planar formats, non contiguous variant */
> -		{ .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
> -		{ .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2 },
> -		{ .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
> +		{ .format = V4L2_PIX_FMT_YUV420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2, .norm= V4L2_PIX_FMT_YUV420 },
's/norm= /norm =/'

Thanks,
Dafna

> +		{ .format = V4L2_PIX_FMT_YVU420M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 2, .norm= V4L2_PIX_FMT_YVU420 },
> +		{ .format = V4L2_PIX_FMT_YUV422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1, .norm= V4L2_PIX_FMT_YUV422P },
>   		{ .format = V4L2_PIX_FMT_YVU422M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 2, .vdiv = 1 },
> -		{ .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
> +		{ .format = V4L2_PIX_FMT_YUV444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1, .norm= V4L2_PIX_FMT_YUV444 },
>   		{ .format = V4L2_PIX_FMT_YVU444M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 3, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .hdiv = 1, .vdiv = 1 },
>   
> -		{ .format = V4L2_PIX_FMT_NV12M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
> -		{ .format = V4L2_PIX_FMT_NV21M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2 },
> -		{ .format = V4L2_PIX_FMT_NV16M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
> -		{ .format = V4L2_PIX_FMT_NV61M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1 },
> +		{ .format = V4L2_PIX_FMT_NV12M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2, .norm = V4L2_PIX_FMT_NV12 },
> +		{ .format = V4L2_PIX_FMT_NV21M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 2, .norm = V4L2_PIX_FMT_NV21 },
> +		{ .format = V4L2_PIX_FMT_NV16M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1, .norm = V4L2_PIX_FMT_NV16 },
> +		{ .format = V4L2_PIX_FMT_NV61M,   .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .hdiv = 2, .vdiv = 1, .norm = V4L2_PIX_FMT_NV61 },
>   
>   		/* Bayer RGB formats */
>   		{ .format = V4L2_PIX_FMT_SBGGR8,	.pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .hdiv = 1, .vdiv = 1 },
> diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
> index be36cbdcc1bd..7236af1cfa2f 100644
> --- a/include/media/v4l2-common.h
> +++ b/include/media/v4l2-common.h
> @@ -483,6 +483,8 @@ enum v4l2_pixel_encoding {
>    * @vdiv: Vertical chroma subsampling factor
>    * @block_w: Per-plane macroblock pixel width (optional)
>    * @block_h: Per-plane macroblock pixel height (optional)
> + * @norm: The normalized format that should be used in Ext API. Should be set
> + *	  to zero if @format is already the normalized version.
>    */
>   struct v4l2_format_info {
>   	u32 format;
> @@ -494,6 +496,7 @@ struct v4l2_format_info {
>   	u8 vdiv;
>   	u8 block_w[4];
>   	u8 block_h[4];
> +	u32 norm;
>   };
>   
>   static inline bool v4l2_is_format_rgb(const struct v4l2_format_info *f)
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2021-01-14 18:07 ` [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more) Helen Koike
@ 2021-02-10 15:02   ` Dafna Hirschfeld
  2021-02-23 12:35   ` Hans Verkuil
  1 sibling, 0 replies; 48+ messages in thread
From: Dafna Hirschfeld @ 2021-02-10 15:02 UTC (permalink / raw)
  To: Helen Koike, linux-media
  Cc: hverkuil, mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Hi,

Am 14.01.21 um 19:07 schrieb Helen Koike:
> This is part of the multiplanar and singleplanar unification process.
> v4l2_ext_pix_format is supposed to work for both cases.
> 
> We also add the concept of modifiers already employed in DRM to expose
> HW-specific formats (like tiled or compressed formats) and allow
> exchanging this information with the DRM subsystem in a consistent way.
> 
> Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
> v4l2_ext_format, other types will be rejected if you use the
> {G,S,TRY}_EXT_PIX_FMT ioctls.
> 
> New hooks have been added to v4l2_ioctl_ops to support those new ioctls
> in drivers, but, in the meantime, the core takes care of converting
> {S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers can
> still work if the userspace app/lib uses the new ioctls.
> 
> The conversion is also done the other around to allow userspace
> apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
> _ext_ hooks.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> ---
> 
> Changes in v6:
>   The main change here was fixing the conversion, so planes reflects color planes,
>   and to implement this properly I made major refactors compared to the previous
>   version.
> - struct v4l2_plane_ext_pix_format removed, using struct v4l2_plane_pix_format instead (Tomasz)
> - refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
> - reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
> - do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
> - refactor conversion functions, so planes are color planes (Tomasz)
> - Don't explicitly check for e->modifier != 0 in v4l2_ext_pix_format_to_format() (Tomasz)
> - Use "ef" for extended formats in the framework for consistency (Tomasz)
> - Handle xfer_func field in conversions (Tomasz)
> - Zero reserved fields in v4l_s_ext_pix_fmt() and v4l_try_ext_pix_fmt() (Tomasz)
> - Refactor format functions to use v4l_fmt_ioctl_via_ext()
> - Several fixes/refactoring/changes
> - Remove EXT API for touch devices
> 
> Changes in v5:
> - change sizes and reorder fields to avoid holes in the struct and make
>    it the same for 32 and 64 bits
> - removed __attribute__ ((packed)) from uapi structs
> - Fix doc warning from make htmldocs
> - Updated commit message with EXT_PIX prefix for the ioctls.
> 
> Changes in v4:
> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
> - Add reserved fields
> - Removed num_planes from struct v4l2_ext_pix_format
> - Removed flag field from struct v4l2_ext_pix_format, since the only
>    defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
>    where we can use modifiers, or add it back later through the reserved
>    bits.
> - In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
>    != MOD_INVALID
> - Fix type assignment in v4l_g_fmt_ext_pix()
> - Rebased on top of media/master (post 5.8-rc1)
> 
> Changes in v3:
> - Rebased on top of media/master (post 5.4-rc1)
> 
> Changes in v2:
> - Move the modifier in v4l2_ext_format (was formerly placed in
>    v4l2_ext_plane)
> - Fix a few bugs in the converters and add a strict parameter to
>    allow conversion of uninitialized/mis-initialized objects
> ---
>   drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
>   drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
>   include/media/v4l2-ioctl.h           |  28 ++
>   include/uapi/linux/videodev2.h       |  41 ++
>   4 files changed, 602 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> index f9cff033d0dc..5add58cb6d45 100644
> --- a/drivers/media/v4l2-core/v4l2-dev.c
> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> @@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct video_device *vdev)
>   			       ops->vidioc_enum_fmt_vid_overlay)) ||
>   		    (is_tx && ops->vidioc_enum_fmt_vid_out))
>   			set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
> +		if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
> +		    (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
> +			 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>   		if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
>   			       ops->vidioc_g_fmt_vid_cap_mplane ||
> -			       ops->vidioc_g_fmt_vid_overlay)) ||
> +			       ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
>   		    (is_tx && (ops->vidioc_g_fmt_vid_out ||
>   			       ops->vidioc_g_fmt_vid_out_mplane ||
> -			       ops->vidioc_g_fmt_vid_out_overlay)))
> +			       ops->vidioc_g_ext_pix_fmt_vid_out))) {
>   			 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
> +			 set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
> +		}
> +		if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
> +		    (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
> +			 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>   		if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
>   			       ops->vidioc_s_fmt_vid_cap_mplane ||
> -			       ops->vidioc_s_fmt_vid_overlay)) ||
> +			       ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
>   		    (is_tx && (ops->vidioc_s_fmt_vid_out ||
>   			       ops->vidioc_s_fmt_vid_out_mplane ||
> -			       ops->vidioc_s_fmt_vid_out_overlay)))
> +			       ops->vidioc_s_ext_pix_fmt_vid_out))) {
>   			 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
> +			 set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
> +		}
> +		if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
> +		    (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
> +			 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>   		if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
>   			       ops->vidioc_try_fmt_vid_cap_mplane ||
> -			       ops->vidioc_try_fmt_vid_overlay)) ||
> +			       ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
>   		    (is_tx && (ops->vidioc_try_fmt_vid_out ||
>   			       ops->vidioc_try_fmt_vid_out_mplane ||
> -			       ops->vidioc_try_fmt_vid_out_overlay)))
> +			       ops->vidioc_try_ext_pix_fmt_vid_out))) {
>   			 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
> +			 set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
> +		}
>   		SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
>   		SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
>   		SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 848286a284f6..a9c07c0a73ec 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -18,6 +18,8 @@
>   
>   #include <linux/videodev2.h>
>   
> +#include <drm/drm_fourcc.h>
> +
>   #include <media/v4l2-common.h>
>   #include <media/v4l2-ioctl.h>
>   #include <media/v4l2-ctrls.h>
> @@ -38,6 +40,11 @@
>   
>   #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), (vfd)->valid_ioctls)
>   
> +#define V4L2_IS_CAP_MULTIPLANAR(vdev)	(vdev->device_caps & \
> +					 (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
> +					 V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
> +					 V4L2_CAP_VIDEO_M2M_MPLANE))
> +
>   struct std_descr {
>   	v4l2_std_id std;
>   	const char *descr;
> @@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg, bool write_only)
>   	}
>   }
>   
> +static void v4l_print_ext_pix_format(const void *arg, bool write_only)
> +{
> +	const struct v4l2_ext_pix_format *ef = arg;
> +	unsigned int i;
> +
> +	pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u, xfer_func=%u\n",
> +		prt_names(ef->type, v4l2_type_names),
> +		ef->width, ef->height,
> +		(ef->pixelformat & 0xff),
> +		(ef->pixelformat >>  8) & 0xff,
> +		(ef->pixelformat >> 16) & 0xff,
> +		(ef->pixelformat >> 24) & 0xff,
> +		ef->modifier, prt_names(ef->field, v4l2_field_names),
> +		ef->colorspace, ef->ycbcr_enc,
> +		ef->quantization, ef->xfer_func);
> +	for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
> +		pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
> +			 i, ef->plane_fmt[i].bytesperline,
> +			 ef->plane_fmt[i].sizeimage);
> +}
> +
>   static void v4l_print_framebuffer(const void *arg, bool write_only)
>   {
>   	const struct v4l2_framebuffer *p = arg;
> @@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
>   	switch (type) {
>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>   		if ((is_vid || is_tch) && is_rx &&
> -		    (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane))
> +		    (ops->vidioc_g_fmt_vid_cap ||
> +		     ops->vidioc_g_ext_pix_fmt_vid_cap ||
> +		     ops->vidioc_g_fmt_vid_cap_mplane))
>   			return 0;
>   		break;
>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if ((is_vid || is_tch) && is_rx && ops->vidioc_g_fmt_vid_cap_mplane)
> +		if ((is_vid || is_tch) && is_rx &&
> +		    (ops->vidioc_g_fmt_vid_cap_mplane ||
> +		     ops->vidioc_g_ext_pix_fmt_vid_cap))
>   			return 0;
>   		break;
>   	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> @@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
>   		break;
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>   		if (is_vid && is_tx &&
> -		    (ops->vidioc_g_fmt_vid_out || ops->vidioc_g_fmt_vid_out_mplane))
> +		    (ops->vidioc_g_fmt_vid_out ||
> +		     ops->vidioc_g_ext_pix_fmt_vid_out ||
> +		     ops->vidioc_g_fmt_vid_out_mplane))
>   			return 0;
>   		break;
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
> +		if (is_vid && is_tx &&
> +		    (ops->vidioc_g_ext_pix_fmt_vid_out ||
> +		     ops->vidioc_g_fmt_vid_out_mplane))
>   			return 0;
>   		break;
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> @@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct v4l2_format *fmt)
>   	       sizeof(fmt->fmt.pix) - offset);
>   }
>   
> +static void
> +v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
> +				  struct v4l2_pix_format *pix)
> +{
> +	unsigned int i;
> +
> +	pix->width = ef->width;
> +	pix->height = ef->height;
> +	pix->field = ef->field;
> +	pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
> +	pix->colorspace = ef->colorspace;
> +	pix->ycbcr_enc = ef->ycbcr_enc;
> +	pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
> +	pix->quantization = ef->quantization;
> +	pix->pixelformat = ef->pixelformat;
> +	pix->bytesperline = ef->plane_fmt[0].bytesperline;
> +	pix->sizeimage = ef->plane_fmt[0].sizeimage;
> +	for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
> +		pix->sizeimage += ef->plane_fmt[i].sizeimage;
> +}
> +
> +static void
> +v4l2_ext_pix_format_to_pix_mp_format(const struct v4l2_ext_pix_format *ef,
> +				     struct v4l2_pix_format_mplane *pix_mp)
> +{
> +	const struct v4l2_format_info *info =
> +					v4l2_format_info(ef->pixelformat);
> +	unsigned int i;
> +
> +	pix_mp->width = ef->width;
> +	pix_mp->height = ef->height;
> +	pix_mp->field = ef->field;
> +	pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
> +	pix_mp->colorspace = ef->colorspace;
> +	pix_mp->ycbcr_enc = ef->ycbcr_enc;
> +	pix_mp->quantization = ef->quantization;
> +	pix_mp->pixelformat = ef->pixelformat;
> +
> +	/* This is true when converting to non-M-variant */
> +	if (info && info->mem_planes == 1) {
> +		pix_mp->plane_fmt[0] = ef->plane_fmt[0];
> +		pix_mp->num_planes = 1;
> +		for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
> +			pix_mp->plane_fmt[0].sizeimage += ef->plane_fmt[i].sizeimage;
> +
> +		return;
> +	}
> +
> +	for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
> +		pix_mp->plane_fmt[i] = ef->plane_fmt[i];
> +	pix_mp->num_planes = i;
> +}
> +
> +/*
> + * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to v4l2_format
> + *
> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
> + * @f: A pointer to struct v4l2_format to be filled.
> + * @is_mplane: Bool indicating if multiplanar API should be used in @f.
> + *
> + * If pixelformat should be converted to M-variant, change ef->pixelformat
> + * to the M-variant before calling this function.
> + */
> +static void v4l2_ext_pix_format_to_format(const struct v4l2_ext_pix_format *ef,
> +					  struct v4l2_format *f, bool is_mplane)
> +{
> +	memset(f, 0, sizeof(*f));
> +
> +	if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
> +	    ef->modifier != DRM_FORMAT_MOD_INVALID)
> +		pr_warn("Modifiers are not supported in v4l2_format, ignoring %llx\n",
> +			ef->modifier);
> +
> +	if (!is_mplane) {
> +		f->type = ef->type;
> +		v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
> +		return;
> +	}
> +
> +	if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +	else
> +		f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> +
> +	v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
> +}
> +
> +static void
> +v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
> +				  struct v4l2_ext_pix_format *ef)
> +{
> +	const struct v4l2_format_info *info =
> +					v4l2_format_info(pix->pixelformat);
> +	unsigned int i;
> +
> +	ef->width = pix->width;
> +	ef->height = pix->height;
> +	ef->field = pix->field;
> +	ef->colorspace = pix->colorspace;
> +	ef->ycbcr_enc = pix->ycbcr_enc;
> +	ef->quantization = pix->quantization;
> +	ef->xfer_func = pix->xfer_func;
> +	if (pix->flags)
> +		pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
> +
> +	/* We assume M-variants won't be used in this function */
> +	ef->pixelformat = pix->pixelformat;
> +
> +	ef->plane_fmt[0].bytesperline = pix->bytesperline;
> +	ef->plane_fmt[0].sizeimage = pix->sizeimage;
> +
> +	if (!info)
> +		return;

What if info is null? It looks like we have a wrong conversion in that case.
Many parts of this patch rellay on 'info' information, maybe we should extend
v4l2_format_info to support all formats?


> +
> +	for (i = 1; i < info->comp_planes; i++) {
> +		ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
> +		ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
> +					     ef->height / info->vdiv;
> +		ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
> +	}
> +}
> +
> +static void
> +v4l2_pix_mp_format_to_ext_pix_format(const struct v4l2_pix_format_mplane *pix_mp,
> +				     struct v4l2_ext_pix_format *ef)
> +{
> +	const struct v4l2_format_info *info =
> +					v4l2_format_info(pix_mp->pixelformat);
> +	unsigned int i;
> +
> +	ef->width = pix_mp->width;
> +	ef->height = pix_mp->height;
> +	ef->field = pix_mp->field;
> +	ef->colorspace = pix_mp->colorspace;
> +	ef->ycbcr_enc = pix_mp->ycbcr_enc;
> +	ef->quantization = pix_mp->quantization;
> +	ef->xfer_func = pix_mp->xfer_func;
> +	if (pix_mp->flags)
> +		pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
> +
> +	if (!info)
> +		return;
> +
> +	ef->pixelformat = info && info->norm ?

The code above this line already return if !info so you can replace s/info && info->norm ?/info->norm/

> +			  info->norm : pix_mp->pixelformat;
> +
> +	if (info->comp_planes == info->mem_planes) {
> +		for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES; i++)
> +			ef->plane_fmt[i] = pix_mp->plane_fmt[i];
> +
> +		return;
> +	}
> +
> +	/* case where mem_planes is 1 and comp_planes > 1 */
> +	ef->plane_fmt[0] = pix_mp->plane_fmt[0];
> +	for (i = 1; i < info->comp_planes; i++) {
> +		ef->plane_fmt[i].bytesperline =
> +			pix_mp->plane_fmt[0].bytesperline / info->hdiv;
> +		ef->plane_fmt[i].sizeimage =
> +			ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
> +		ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
> +	}
> +}
> +
> +/*
> + * v4l2_format_to_ext_pix_format - convert to v4l2_format to v4l2_ext_pix_format
> + *
> + * @f: A pointer to struct v4l2_format to be converted.
> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
> + *
> + * This method normalize the pixelformat to non-M variant.
> + */
> +static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
> +					  struct v4l2_ext_pix_format *ef)
> +{
> +	memset(ef, 0, sizeof(*ef));
> +
> +	switch (f->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		ef->type = f->type;
> +		v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +		ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +		v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +		v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
> +		break;
> +	default:
> +		WARN("Converting to Ext Pix Format with wrong buffer type %s\n",
> +		     prt_names(f->type, v4l2_type_names));
> +		break;
> +	}
> +}
> +
>   static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
>   				struct file *file, void *fh, void *arg)
>   {
> @@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct v4l2_pix_format *p)
>   	p->xfer_func = 0;
>   }
>   
> +static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
> +				 struct file *file, void *fh,
> +				 struct v4l2_format *f,
> +				 unsigned int ioctl)
> +{
> +	bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
> +	struct video_device *vdev = video_devdata(file);
> +	struct v4l2_ext_pix_format ef = {0};
> +	u32 original_pixfmt = 0;
> +	u32 cap_mask;
> +	int ret;
> +
> +	if (ioctl != VIDIOC_G_FMT) {
> +		/*
> +		 * If CSC attributes are read only, set them to DEFAULT
> +		 * to avoid changes by the driver.> +		 */
> +		if (is_multiplanar) {
> +			if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
> +				f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
> +				f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> +				f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
> +				f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
> +			}
> +			/* Unset the flag to avoid warning in the convertion */

which warning?

> +			f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
> +
> +			/* Save pixelformat in case M-variant is being used */
> +			original_pixfmt = f->fmt.pix_mp.pixelformat;
> +		} else {
> +			if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
> +				f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
> +				f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> +				f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
> +				f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
> +			}
> +			/* Unset the flag to avoid warning in the convertion */
> +			f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
> +		}

I don't think there is a need to do this CSC adjustment. If the CSC flag
is not set then drivers should ignore the colorspace,ycbcr_enc,quantizaion,xfer_func anyway.

> +		v4l2_format_to_ext_pix_format(f, &ef);
> +	}
> +
> +	switch (f->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +		cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> +			   V4L2_CAP_VIDEO_M2M_MPLANE;
> +		if (!!(vdev->device_caps & cap_mask) !=
> +		    (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))

This function is called if a driver supports the Ext API right?
In that case shouldn't "vdev->device_caps & cap_mask" always be non-zero here?

> +			return -EINVAL;
> +
> +		ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +		if (ioctl == VIDIOC_G_FMT)
> +			ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
> +		else if (ioctl == VIDIOC_S_FMT)
> +			ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
> +		else
> +			ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
> +								  &ef);
> +		break;
> +
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> +			   V4L2_CAP_VIDEO_M2M_MPLANE;
> +		if (!!(vdev->device_caps & cap_mask) !=

dito

> +		    (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> +			return -EINVAL;
> +
> +		ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +		if (ioctl == VIDIOC_G_FMT)
> +			ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
> +		else if (ioctl == VIDIOC_S_FMT)
> +			ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
> +		else
> +			ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
> +								  &ef);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if (ret)
> +		return ret;
> +
> +	if (original_pixfmt != ef.pixelformat &&
> +	    v4l2_format_info(original_pixfmt))
> +		ef.pixelformat = original_pixfmt;

original_pixfmt is set only for multiplaner, so it looks like this line might set ef.pixelformat to 0.


> +
> +	v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
> +	return 0;
> +}
> +
>   static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>   				struct file *file, void *fh, void *arg)
>   {
> @@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>   
>   	switch (p->type) {
>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!ops->vidioc_g_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
> +			     !ops->vidioc_g_ext_pix_fmt_vid_cap))
>   			break;
>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> -		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
> +		ret = ops->vidioc_g_fmt_vid_cap ?
> +		      ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +					    VIDIOC_G_FMT);>   		/* just in case the driver zeroed it again */
>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>   		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>   			v4l_pix_format_touch(&p->fmt.pix);
>   		return ret;
>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
> +		if (ops->vidioc_g_fmt_vid_cap_mplane)
> +			return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
> +		else if (ops->vidioc_g_ext_pix_fmt_vid_cap)
> +			return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +						     VIDIOC_G_FMT);
> +		break;
>   	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>   		return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>   	case V4L2_BUF_TYPE_VBI_CAPTURE:
> @@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>   	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>   		return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!ops->vidioc_g_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_g_fmt_vid_out &&
> +			     !ops->vidioc_g_ext_pix_fmt_vid_out))
>   			break;
>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> -		ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
> +		ret = ops->vidioc_g_fmt_vid_out ?
> +		      ops->vidioc_g_fmt_vid_out(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
>   		/* just in case the driver zeroed it again */
>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>   		return ret;
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
> +		if (ops->vidioc_g_fmt_vid_out_mplane)
> +			return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
> +		else if (ops->vidioc_g_ext_pix_fmt_vid_out)
> +			return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +						     VIDIOC_G_FMT);
> +		break;
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>   		return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>   	case V4L2_BUF_TYPE_VBI_OUTPUT:
> @@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>   	return -EINVAL;
>   }
>   
> +static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> +			     struct file *file, void *fh, void *arg)
> +{
> +	struct v4l2_ext_pix_format *ef = arg;
> +	struct v4l2_format f = {
> +		.type = ef->type,
> +	};
> +	int ret = check_fmt(file, ef->type);
> +
> +	if (ret)
> +		return ret;
> +
> +	memset(ef, 0, sizeof(*ef));
> +	ef->type = f.type;
> +
> +	switch (f.type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		if (ops->vidioc_g_ext_pix_fmt_vid_cap)
> +			return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		if (ops->vidioc_g_ext_pix_fmt_vid_out)
> +			return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = v4l_g_fmt(ops, file, fh, &f);
> +	if (ret)
> +		return ret;
> +
> +	v4l2_format_to_ext_pix_format(&f, ef);
> +	return 0;
> +}
> +
>   static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>   				struct file *file, void *fh, void *arg)
>   {
> @@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>   
>   	switch (p->type) {
>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
> +			     !ops->vidioc_s_ext_pix_fmt_vid_cap))
>   			break;
>   		CLEAR_AFTER_FIELD(p, fmt.pix);
> -		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
> +		ret = ops->vidioc_s_fmt_vid_cap ?
> +		      ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>   		/* just in case the driver zeroed it again */
>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>   		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>   			v4l_pix_format_touch(&p->fmt.pix);
>   		return ret;
>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
> +			     !ops->vidioc_s_ext_pix_fmt_vid_cap))
>   			break;
>   		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>   		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>   			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>   					  bytesperline);
> -		return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
> +		return ops->vidioc_s_fmt_vid_cap_mplane ?
> +		       ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,  VIDIOC_S_FMT);
>   	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>   		if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>   			break;
> @@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>   		CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>   		return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!ops->vidioc_s_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_out &&
> +			     !ops->vidioc_s_ext_pix_fmt_vid_out))
>   			break;
>   		CLEAR_AFTER_FIELD(p, fmt.pix);
> -		ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
> +		ret = ops->vidioc_s_fmt_vid_out ?
> +		      ops->vidioc_s_fmt_vid_out(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>   		/* just in case the driver zeroed it again */
>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>   		return ret;
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
> +			     !ops->vidioc_s_ext_pix_fmt_vid_out))
>   			break;
>   		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>   		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>   			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>   					  bytesperline);
> -		return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
> +		return ops->vidioc_s_fmt_vid_out_mplane ?
> +		       ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>   		if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>   			break;
> @@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>   	return -EINVAL;
>   }
>   
> +static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> +			     struct file *file, void *fh, void *arg)
> +{
> +	struct video_device *vfd = video_devdata(file);
> +	struct v4l2_ext_pix_format *ef = arg;
> +	struct v4l2_format f;
> +	int ret = check_fmt(file, ef->type);
> +
> +	if (ret)
> +		return ret;
> +
> +	memset(ef->reserved, 0, sizeof(ef->reserved));
> +
> +	switch (ef->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		if (ops->vidioc_s_ext_pix_fmt_vid_cap)
> +			return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		if (ops->vidioc_s_ext_pix_fmt_vid_out)
> +			return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
> +
> +	ret = v4l_s_fmt(ops, file, fh, &f);
> +	if (ret)
> +		/* TODO: retry with M-variant of ef->pixelformat? */

I think not, ef->pixelformat is a paramater that comes from userspace right?
Then userspace should set it according to what the driver supports.

> +		return ret;
> +
> +	v4l2_format_to_ext_pix_format(&f, ef);
> +	return 0;
> +}
> +
>   static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>   				struct file *file, void *fh, void *arg)
>   {
> @@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>   
>   	switch (p->type) {
>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!ops->vidioc_try_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>   			break;
>   		CLEAR_AFTER_FIELD(p, fmt.pix);
> -		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
> +		ret = ops->vidioc_try_fmt_vid_cap ?
> +		      ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
>   		/* just in case the driver zeroed it again */
>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>   		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>   			v4l_pix_format_touch(&p->fmt.pix);
>   		return ret;
>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>   			break;
>   		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>   		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>   			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>   					  bytesperline);
> -		return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
> +		return ops->vidioc_try_fmt_vid_cap_mplane ?
> +		       ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +					     VIDIOC_TRY_FMT);
>   	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>   		if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>   			break;
> @@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>   		CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>   		return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!ops->vidioc_try_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_out &&
> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>   			break;
>   		CLEAR_AFTER_FIELD(p, fmt.pix);
> -		ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
> +		ret = ops->vidioc_try_fmt_vid_out ?
> +		      ops->vidioc_try_fmt_vid_out(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
>   		/* just in case the driver zeroed it again */
>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>   		return ret;
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>   			break;
>   		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>   		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>   			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>   					  bytesperline);
> -		return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
> +		return ops->vidioc_try_fmt_vid_out_mplane ?
> +		       ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +					     VIDIOC_TRY_FMT);
>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>   		if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>   			break;
> @@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>   	return -EINVAL;
>   }
>   
> +static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> +			       struct file *file, void *fh, void *arg)
> +{
> +	struct video_device *vfd = video_devdata(file);
> +	struct v4l2_ext_pix_format *ef = arg;
> +	struct v4l2_format f;
> +	int ret = check_fmt(file, ef->type);
> +
> +	if (ret)
> +		return ret;
> +
> +	memset(ef->reserved, 0, sizeof(ef->reserved));
> +
> +	switch (ef->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		if (ops->vidioc_try_ext_pix_fmt_vid_cap)
> +			return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
> +								   ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		if (ops->vidioc_try_ext_pix_fmt_vid_out)
> +			return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
> +								   ef);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
> +
> +	ret = v4l_try_fmt(ops, file, fh, &f);
> +	if (ret)
> +		/* TODO: retry with M-variant of ef->pixelformat? */

dito

> +		return ret;
> +
> +	v4l2_format_to_ext_pix_format(&f, ef);
> +	return 0;
> +}
> +
>   static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
>   				struct file *file, void *fh, void *arg)
>   {
> @@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
>   	IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
>   	IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
>   	IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
> +	IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, v4l_print_ext_pix_format, 0),
> +	IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, v4l_print_ext_pix_format, INFO_FL_PRIO),
> +	IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, v4l_print_ext_pix_format, 0),
>   };
>   #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
>   
> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
> index edb733f21604..c44708dc9355 100644
> --- a/include/media/v4l2-ioctl.h
> +++ b/include/media/v4l2-ioctl.h
> @@ -48,11 +48,17 @@ struct v4l2_fh;
>    * @vidioc_g_fmt_vid_cap: pointer to the function that implements
>    *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
>    *	in single plane mode
> + * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that implements
> + *	:ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
> + *	capture
>    * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
>    *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>    * @vidioc_g_fmt_vid_out: pointer to the function that implements
>    *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
>    *	in single plane mode
> + * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that implements
> + *	:ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
> + *	out
>    * @vidioc_g_fmt_vid_out_overlay: pointer to the function that implements
>    *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
>    * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
> @@ -82,11 +88,16 @@ struct v4l2_fh;
>    * @vidioc_s_fmt_vid_cap: pointer to the function that implements
>    *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
>    *	in single plane mode
> + * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that implements
> + *	:ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
> + *	capture
>    * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
>    *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>    * @vidioc_s_fmt_vid_out: pointer to the function that implements
>    *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
>    *	in single plane mode
> + * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that implements
> + *	:ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out

s/vidioc_g_fmt/vidioc_g_ext_pix_fmt/ ?

>    * @vidioc_s_fmt_vid_out_overlay: pointer to the function that implements
>    *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
>    * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
> @@ -116,11 +127,16 @@ struct v4l2_fh;
>    * @vidioc_try_fmt_vid_cap: pointer to the function that implements
>    *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video capture
>    *	in single plane mode
> + * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that implements
> + *	:ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for
> +	video capture
>    * @vidioc_try_fmt_vid_overlay: pointer to the function that implements
>    *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>    * @vidioc_try_fmt_vid_out: pointer to the function that implements
>    *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
>    *	in single plane mode
> + * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that implements
> + *	:ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out

similar dito

Thanks,
Dafna

>    * @vidioc_try_fmt_vid_out_overlay: pointer to the function that implements
>    *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>    *	output
> @@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
>   	/* VIDIOC_G_FMT handlers */
>   	int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
>   				    struct v4l2_format *f);
> +	int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> +					    struct v4l2_ext_pix_format *ef);
>   	int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
>   					struct v4l2_format *f);
>   	int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
>   				    struct v4l2_format *f);
> +	int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> +					    struct v4l2_ext_pix_format *ef);
>   	int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
>   					    struct v4l2_format *f);
>   	int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
> @@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
>   	/* VIDIOC_S_FMT handlers */
>   	int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
>   				    struct v4l2_format *f);
> +	int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> +					    struct v4l2_ext_pix_format *ef);
>   	int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
>   					struct v4l2_format *f);
>   	int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
>   				    struct v4l2_format *f);
> +	int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> +					    struct v4l2_ext_pix_format *ef);
>   	int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
>   					    struct v4l2_format *f);
>   	int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
> @@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
>   	/* VIDIOC_TRY_FMT handlers */
>   	int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
>   				      struct v4l2_format *f);
> +	int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> +					      struct v4l2_ext_pix_format *ef);
>   	int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
>   					  struct v4l2_format *f);
>   	int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
>   				      struct v4l2_format *f);
> +	int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> +					      struct v4l2_ext_pix_format *ef);
>   	int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
>   					     struct v4l2_format *f);
>   	int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index d9b7c9177605..a2d850513708 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
>   	__u8				reserved[7];
>   } __attribute__ ((packed));
>   
> +/**
> + * struct v4l2_ext_pix_format - extended single/multiplanar format definition
> + * @type:		type of the data stream; V4L2_BUF_TYPE_VIDEO_CAPTURE or
> + *			V4L2_BUF_TYPE_VIDEO_OUTPUT
> + * @width:		image width in pixels
> + * @height:		image height in pixels
> + * @field:		enum v4l2_field; field order (for interlaced video)
> + * @plane_fmt:		per-plane information
> + * @pixelformat:	little endian four character code (fourcc)
> + * @modifier:		modifier applied to the format (used for tiled formats
> + *			and other kind of HW-specific formats, like compressed
> + *			formats) as defined in drm_fourcc.h
> + * @colorspace:		enum v4l2_colorspace; supplemental to pixelformat
> + * @xfer_func:		enum v4l2_xfer_func, colorspace transfer function
> + * @ycbcr_enc:		enum v4l2_ycbcr_encoding, Y'CbCr encoding
> + * @hsv_enc:		enum v4l2_hsv_encoding, HSV encoding
> + * @quantization:	enum v4l2_quantization, colorspace quantization
> + * @reserved:		extra space reserved for future fields, must be set to 0
> + */
> +struct v4l2_ext_pix_format {
> +	__u32 type;
> +	__u32 width;
> +	__u32 height;
> +	__u32 field;
> +	struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
> +	__u32 pixelformat;
> +	__u64 modifier;
> +	__u32 colorspace;
> +	__u32 xfer_func;
> +	union {
> +		__u32 ycbcr_enc;
> +		__u32 hsv_enc;
> +	};
> +	__u32 quantization;
> +	__u32 reserved[9];
> +};
> +
>   /**
>    * struct v4l2_sdr_format - SDR format definition
>    * @pixelformat:	little endian four character code (fourcc)
> @@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
>   
>   #define VIDIOC_QUERY_EXT_CTRL	_IOWR('V', 103, struct v4l2_query_ext_ctrl)
>   
> +#define VIDIOC_G_EXT_PIX_FMT	_IOWR('V', 104, struct v4l2_ext_pix_format)
> +#define VIDIOC_S_EXT_PIX_FMT	_IOWR('V', 105, struct v4l2_ext_pix_format)
> +#define VIDIOC_TRY_EXT_PIX_FMT	_IOWR('V', 106, struct v4l2_ext_pix_format)
> +
>   /* Reminder: when adding new ioctls please add support for them to
>      drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
>   
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2021-01-14 18:07 ` [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more) Helen Koike
  2021-02-10 15:02   ` Dafna Hirschfeld
@ 2021-02-23 12:35   ` Hans Verkuil
  2021-02-24 15:12     ` Helen Koike
  1 sibling, 1 reply; 48+ messages in thread
From: Hans Verkuil @ 2021-02-23 12:35 UTC (permalink / raw)
  To: Helen Koike, linux-media
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Hi Helen,

On 14/01/2021 19:07, Helen Koike wrote:
> This is part of the multiplanar and singleplanar unification process.
> v4l2_ext_pix_format is supposed to work for both cases.
> 
> We also add the concept of modifiers already employed in DRM to expose
> HW-specific formats (like tiled or compressed formats) and allow
> exchanging this information with the DRM subsystem in a consistent way.
> 
> Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
> v4l2_ext_format, other types will be rejected if you use the
> {G,S,TRY}_EXT_PIX_FMT ioctls.
> 
> New hooks have been added to v4l2_ioctl_ops to support those new ioctls
> in drivers, but, in the meantime, the core takes care of converting
> {S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers can
> still work if the userspace app/lib uses the new ioctls.
> 
> The conversion is also done the other around to allow userspace
> apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
> _ext_ hooks.

I have some small comments below, but also one high level comment:

Regarding M variants of pixelformats: this patch 'normalizes' them to
regular pixelformats in the extended API. This makes life complicated,
and I wonder if this is the right approach.

Currently there are two reasons for a driver to support e.g. NV12M:
either luma and chroma need to be in two memory banks, or the luma
and chroma planes cannot be contiguous due to alignment requirements.

The first requirement is still valid for drivers that support the extended API.
The second requirement is no longer a reason to support NV12M. But I
don't think we should just drop NV12M support if it was already supported
before the conversion to this extended API. Since NV12M allocates two buffers
instead of one, it is still different from a regular NV12.

I would prefer that such drivers support both NV12 and NV12M, so no
automatic conversion.

A related question is how to handle pixelformat enumeration: with the
extended API an NV12 format might work, but not with the old API (e.g.
due to memory alignment requirements). I wonder if a VIDIOC_ENUM_EXT_PIX_FMT
isn't needed.

VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while VIDIOC_ENUM_FMT
would just report NV12M.

> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> ---
> 
> Changes in v6:
>  The main change here was fixing the conversion, so planes reflects color planes,
>  and to implement this properly I made major refactors compared to the previous
>  version.
> - struct v4l2_plane_ext_pix_format removed, using struct v4l2_plane_pix_format instead (Tomasz)
> - refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
> - reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
> - do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
> - refactor conversion functions, so planes are color planes (Tomasz)
> - Don't explicitly check for e->modifier != 0 in v4l2_ext_pix_format_to_format() (Tomasz)
> - Use "ef" for extended formats in the framework for consistency (Tomasz)
> - Handle xfer_func field in conversions (Tomasz)
> - Zero reserved fields in v4l_s_ext_pix_fmt() and v4l_try_ext_pix_fmt() (Tomasz)
> - Refactor format functions to use v4l_fmt_ioctl_via_ext()
> - Several fixes/refactoring/changes
> - Remove EXT API for touch devices
> 
> Changes in v5:
> - change sizes and reorder fields to avoid holes in the struct and make
>   it the same for 32 and 64 bits
> - removed __attribute__ ((packed)) from uapi structs
> - Fix doc warning from make htmldocs
> - Updated commit message with EXT_PIX prefix for the ioctls.
> 
> Changes in v4:
> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
> - Add reserved fields
> - Removed num_planes from struct v4l2_ext_pix_format
> - Removed flag field from struct v4l2_ext_pix_format, since the only
>   defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
>   where we can use modifiers, or add it back later through the reserved
>   bits.
> - In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
>   != MOD_INVALID
> - Fix type assignment in v4l_g_fmt_ext_pix()
> - Rebased on top of media/master (post 5.8-rc1)
> 
> Changes in v3:
> - Rebased on top of media/master (post 5.4-rc1)
> 
> Changes in v2:
> - Move the modifier in v4l2_ext_format (was formerly placed in
>   v4l2_ext_plane)
> - Fix a few bugs in the converters and add a strict parameter to
>   allow conversion of uninitialized/mis-initialized objects
> ---
>  drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
>  drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
>  include/media/v4l2-ioctl.h           |  28 ++
>  include/uapi/linux/videodev2.h       |  41 ++
>  4 files changed, 602 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> index f9cff033d0dc..5add58cb6d45 100644
> --- a/drivers/media/v4l2-core/v4l2-dev.c
> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> @@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct video_device *vdev)
>  			       ops->vidioc_enum_fmt_vid_overlay)) ||
>  		    (is_tx && ops->vidioc_enum_fmt_vid_out))
>  			set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
> +		if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
> +		    (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
> +			 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>  		if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
>  			       ops->vidioc_g_fmt_vid_cap_mplane ||
> -			       ops->vidioc_g_fmt_vid_overlay)) ||
> +			       ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
>  		    (is_tx && (ops->vidioc_g_fmt_vid_out ||
>  			       ops->vidioc_g_fmt_vid_out_mplane ||
> -			       ops->vidioc_g_fmt_vid_out_overlay)))
> +			       ops->vidioc_g_ext_pix_fmt_vid_out))) {
>  			 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
> +			 set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
> +		}
> +		if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
> +		    (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
> +			 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>  		if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
>  			       ops->vidioc_s_fmt_vid_cap_mplane ||
> -			       ops->vidioc_s_fmt_vid_overlay)) ||
> +			       ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
>  		    (is_tx && (ops->vidioc_s_fmt_vid_out ||
>  			       ops->vidioc_s_fmt_vid_out_mplane ||
> -			       ops->vidioc_s_fmt_vid_out_overlay)))
> +			       ops->vidioc_s_ext_pix_fmt_vid_out))) {
>  			 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
> +			 set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
> +		}
> +		if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
> +		    (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
> +			 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>  		if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
>  			       ops->vidioc_try_fmt_vid_cap_mplane ||
> -			       ops->vidioc_try_fmt_vid_overlay)) ||
> +			       ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
>  		    (is_tx && (ops->vidioc_try_fmt_vid_out ||
>  			       ops->vidioc_try_fmt_vid_out_mplane ||
> -			       ops->vidioc_try_fmt_vid_out_overlay)))
> +			       ops->vidioc_try_ext_pix_fmt_vid_out))) {
>  			 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
> +			 set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
> +		}
>  		SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
>  		SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
>  		SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 848286a284f6..a9c07c0a73ec 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -18,6 +18,8 @@
>  
>  #include <linux/videodev2.h>
>  
> +#include <drm/drm_fourcc.h>
> +
>  #include <media/v4l2-common.h>
>  #include <media/v4l2-ioctl.h>
>  #include <media/v4l2-ctrls.h>
> @@ -38,6 +40,11 @@
>  
>  #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), (vfd)->valid_ioctls)
>  
> +#define V4L2_IS_CAP_MULTIPLANAR(vdev)	(vdev->device_caps & \
> +					 (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
> +					 V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
> +					 V4L2_CAP_VIDEO_M2M_MPLANE))
> +
>  struct std_descr {
>  	v4l2_std_id std;
>  	const char *descr;
> @@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg, bool write_only)
>  	}
>  }
>  
> +static void v4l_print_ext_pix_format(const void *arg, bool write_only)
> +{
> +	const struct v4l2_ext_pix_format *ef = arg;
> +	unsigned int i;
> +
> +	pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u, xfer_func=%u\n",
> +		prt_names(ef->type, v4l2_type_names),
> +		ef->width, ef->height,
> +		(ef->pixelformat & 0xff),
> +		(ef->pixelformat >>  8) & 0xff,
> +		(ef->pixelformat >> 16) & 0xff,
> +		(ef->pixelformat >> 24) & 0xff,
> +		ef->modifier, prt_names(ef->field, v4l2_field_names),
> +		ef->colorspace, ef->ycbcr_enc,
> +		ef->quantization, ef->xfer_func);
> +	for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
> +		pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
> +			 i, ef->plane_fmt[i].bytesperline,
> +			 ef->plane_fmt[i].sizeimage);
> +}
> +
>  static void v4l_print_framebuffer(const void *arg, bool write_only)
>  {
>  	const struct v4l2_framebuffer *p = arg;
> @@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
>  	switch (type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>  		if ((is_vid || is_tch) && is_rx &&
> -		    (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane))
> +		    (ops->vidioc_g_fmt_vid_cap ||
> +		     ops->vidioc_g_ext_pix_fmt_vid_cap ||
> +		     ops->vidioc_g_fmt_vid_cap_mplane))
>  			return 0;
>  		break;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if ((is_vid || is_tch) && is_rx && ops->vidioc_g_fmt_vid_cap_mplane)
> +		if ((is_vid || is_tch) && is_rx &&
> +		    (ops->vidioc_g_fmt_vid_cap_mplane ||
> +		     ops->vidioc_g_ext_pix_fmt_vid_cap))
>  			return 0;
>  		break;
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> @@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
>  		break;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>  		if (is_vid && is_tx &&
> -		    (ops->vidioc_g_fmt_vid_out || ops->vidioc_g_fmt_vid_out_mplane))
> +		    (ops->vidioc_g_fmt_vid_out ||
> +		     ops->vidioc_g_ext_pix_fmt_vid_out ||
> +		     ops->vidioc_g_fmt_vid_out_mplane))
>  			return 0;
>  		break;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
> +		if (is_vid && is_tx &&
> +		    (ops->vidioc_g_ext_pix_fmt_vid_out ||
> +		     ops->vidioc_g_fmt_vid_out_mplane))
>  			return 0;
>  		break;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> @@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct v4l2_format *fmt)
>  	       sizeof(fmt->fmt.pix) - offset);
>  }
>  
> +static void
> +v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
> +				  struct v4l2_pix_format *pix)
> +{
> +	unsigned int i;
> +
> +	pix->width = ef->width;
> +	pix->height = ef->height;
> +	pix->field = ef->field;
> +	pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
> +	pix->colorspace = ef->colorspace;
> +	pix->ycbcr_enc = ef->ycbcr_enc;
> +	pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
> +	pix->quantization = ef->quantization;
> +	pix->pixelformat = ef->pixelformat;
> +	pix->bytesperline = ef->plane_fmt[0].bytesperline;
> +	pix->sizeimage = ef->plane_fmt[0].sizeimage;
> +	for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
> +		pix->sizeimage += ef->plane_fmt[i].sizeimage;
> +}
> +
> +static void
> +v4l2_ext_pix_format_to_pix_mp_format(const struct v4l2_ext_pix_format *ef,
> +				     struct v4l2_pix_format_mplane *pix_mp)
> +{
> +	const struct v4l2_format_info *info =
> +					v4l2_format_info(ef->pixelformat);
> +	unsigned int i;
> +
> +	pix_mp->width = ef->width;
> +	pix_mp->height = ef->height;
> +	pix_mp->field = ef->field;
> +	pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
> +	pix_mp->colorspace = ef->colorspace;
> +	pix_mp->ycbcr_enc = ef->ycbcr_enc;
> +	pix_mp->quantization = ef->quantization;
> +	pix_mp->pixelformat = ef->pixelformat;
> +
> +	/* This is true when converting to non-M-variant */
> +	if (info && info->mem_planes == 1) {
> +		pix_mp->plane_fmt[0] = ef->plane_fmt[0];
> +		pix_mp->num_planes = 1;
> +		for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
> +			pix_mp->plane_fmt[0].sizeimage += ef->plane_fmt[i].sizeimage;
> +
> +		return;
> +	}
> +
> +	for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
> +		pix_mp->plane_fmt[i] = ef->plane_fmt[i];
> +	pix_mp->num_planes = i;
> +}
> +
> +/*
> + * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to v4l2_format
> + *
> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
> + * @f: A pointer to struct v4l2_format to be filled.
> + * @is_mplane: Bool indicating if multiplanar API should be used in @f.
> + *
> + * If pixelformat should be converted to M-variant, change ef->pixelformat
> + * to the M-variant before calling this function.
> + */
> +static void v4l2_ext_pix_format_to_format(const struct v4l2_ext_pix_format *ef,
> +					  struct v4l2_format *f, bool is_mplane)
> +{
> +	memset(f, 0, sizeof(*f));
> +
> +	if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
> +	    ef->modifier != DRM_FORMAT_MOD_INVALID)
> +		pr_warn("Modifiers are not supported in v4l2_format, ignoring %llx\n",
> +			ef->modifier);
> +
> +	if (!is_mplane) {
> +		f->type = ef->type;
> +		v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
> +		return;
> +	}
> +
> +	if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +	else
> +		f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> +
> +	v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
> +}
> +
> +static void
> +v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
> +				  struct v4l2_ext_pix_format *ef)
> +{
> +	const struct v4l2_format_info *info =
> +					v4l2_format_info(pix->pixelformat);
> +	unsigned int i;
> +
> +	ef->width = pix->width;
> +	ef->height = pix->height;
> +	ef->field = pix->field;
> +	ef->colorspace = pix->colorspace;
> +	ef->ycbcr_enc = pix->ycbcr_enc;
> +	ef->quantization = pix->quantization;
> +	ef->xfer_func = pix->xfer_func;
> +	if (pix->flags)
> +		pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
> +
> +	/* We assume M-variants won't be used in this function */
> +	ef->pixelformat = pix->pixelformat;
> +
> +	ef->plane_fmt[0].bytesperline = pix->bytesperline;
> +	ef->plane_fmt[0].sizeimage = pix->sizeimage;
> +
> +	if (!info)
> +		return;
> +
> +	for (i = 1; i < info->comp_planes; i++) {
> +		ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
> +		ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
> +					     ef->height / info->vdiv;
> +		ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
> +	}
> +}
> +
> +static void
> +v4l2_pix_mp_format_to_ext_pix_format(const struct v4l2_pix_format_mplane *pix_mp,
> +				     struct v4l2_ext_pix_format *ef)
> +{
> +	const struct v4l2_format_info *info =
> +					v4l2_format_info(pix_mp->pixelformat);
> +	unsigned int i;
> +
> +	ef->width = pix_mp->width;
> +	ef->height = pix_mp->height;
> +	ef->field = pix_mp->field;
> +	ef->colorspace = pix_mp->colorspace;
> +	ef->ycbcr_enc = pix_mp->ycbcr_enc;
> +	ef->quantization = pix_mp->quantization;
> +	ef->xfer_func = pix_mp->xfer_func;
> +	if (pix_mp->flags)
> +		pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
> +
> +	if (!info)
> +		return;
> +
> +	ef->pixelformat = info && info->norm ?

'info &&' can be dropped, info is always non-NULL here.

> +			  info->norm : pix_mp->pixelformat;
> +
> +	if (info->comp_planes == info->mem_planes) {
> +		for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES; i++)
> +			ef->plane_fmt[i] = pix_mp->plane_fmt[i];
> +
> +		return;
> +	}
> +
> +	/* case where mem_planes is 1 and comp_planes > 1 */
> +	ef->plane_fmt[0] = pix_mp->plane_fmt[0];
> +	for (i = 1; i < info->comp_planes; i++) {
> +		ef->plane_fmt[i].bytesperline =
> +			pix_mp->plane_fmt[0].bytesperline / info->hdiv;
> +		ef->plane_fmt[i].sizeimage =
> +			ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
> +		ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
> +	}
> +}
> +
> +/*
> + * v4l2_format_to_ext_pix_format - convert to v4l2_format to v4l2_ext_pix_format
> + *
> + * @f: A pointer to struct v4l2_format to be converted.
> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
> + *
> + * This method normalize the pixelformat to non-M variant.
> + */
> +static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
> +					  struct v4l2_ext_pix_format *ef)
> +{
> +	memset(ef, 0, sizeof(*ef));
> +
> +	switch (f->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		ef->type = f->type;
> +		v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +		ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +		v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +		v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
> +		break;
> +	default:
> +		WARN("Converting to Ext Pix Format with wrong buffer type %s\n",
> +		     prt_names(f->type, v4l2_type_names));
> +		break;
> +	}
> +}
> +
>  static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
> @@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct v4l2_pix_format *p)
>  	p->xfer_func = 0;
>  }
>  
> +static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
> +				 struct file *file, void *fh,
> +				 struct v4l2_format *f,
> +				 unsigned int ioctl)
> +{
> +	bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
> +	struct video_device *vdev = video_devdata(file);
> +	struct v4l2_ext_pix_format ef = {0};
> +	u32 original_pixfmt = 0;
> +	u32 cap_mask;
> +	int ret;
> +
> +	if (ioctl != VIDIOC_G_FMT) {
> +		/*
> +		 * If CSC attributes are read only, set them to DEFAULT
> +		 * to avoid changes by the driver.
> +		 */
> +		if (is_multiplanar) {
> +			if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
> +				f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
> +				f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> +				f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
> +				f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
> +			}
> +			/* Unset the flag to avoid warning in the convertion */
> +			f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
> +
> +			/* Save pixelformat in case M-variant is being used */
> +			original_pixfmt = f->fmt.pix_mp.pixelformat;
> +		} else {
> +			if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
> +				f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
> +				f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> +				f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
> +				f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
> +			}
> +			/* Unset the flag to avoid warning in the convertion */
> +			f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
> +		}
> +		v4l2_format_to_ext_pix_format(f, &ef);
> +	}
> +
> +	switch (f->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +		cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> +			   V4L2_CAP_VIDEO_M2M_MPLANE;
> +		if (!!(vdev->device_caps & cap_mask) !=
> +		    (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
> +			return -EINVAL;
> +
> +		ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +		if (ioctl == VIDIOC_G_FMT)
> +			ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
> +		else if (ioctl == VIDIOC_S_FMT)
> +			ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
> +		else
> +			ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
> +								  &ef);
> +		break;
> +
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> +			   V4L2_CAP_VIDEO_M2M_MPLANE;
> +		if (!!(vdev->device_caps & cap_mask) !=
> +		    (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> +			return -EINVAL;
> +
> +		ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +		if (ioctl == VIDIOC_G_FMT)
> +			ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
> +		else if (ioctl == VIDIOC_S_FMT)
> +			ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
> +		else
> +			ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
> +								  &ef);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if (ret)
> +		return ret;
> +
> +	if (original_pixfmt != ef.pixelformat &&
> +	    v4l2_format_info(original_pixfmt))

Could this test be simplified to: 'if (original_pixfmt)'?

I.e., if the original pixfmt was saved, then restore it here.

> +		ef.pixelformat = original_pixfmt;
> +
> +	v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
> +	return 0;
> +}
> +
>  static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
> @@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>  
>  	switch (p->type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!ops->vidioc_g_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
> +			     !ops->vidioc_g_ext_pix_fmt_vid_cap))
>  			break;
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> -		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
> +		ret = ops->vidioc_g_fmt_vid_cap ?
> +		      ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +					    VIDIOC_G_FMT);
>  		/* just in case the driver zeroed it again */
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>  			v4l_pix_format_touch(&p->fmt.pix);
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
> +		if (ops->vidioc_g_fmt_vid_cap_mplane)
> +			return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
> +		else if (ops->vidioc_g_ext_pix_fmt_vid_cap)

'else' can be dropped.

> +			return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +						     VIDIOC_G_FMT);
> +		break;
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>  		return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
> @@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>  		return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!ops->vidioc_g_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_g_fmt_vid_out &&
> +			     !ops->vidioc_g_ext_pix_fmt_vid_out))
>  			break;
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> -		ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
> +		ret = ops->vidioc_g_fmt_vid_out ?
> +		      ops->vidioc_g_fmt_vid_out(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
>  		/* just in case the driver zeroed it again */
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
> +		if (ops->vidioc_g_fmt_vid_out_mplane)
> +			return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
> +		else if (ops->vidioc_g_ext_pix_fmt_vid_out)

Ditto.

> +			return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +						     VIDIOC_G_FMT);
> +		break;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>  		return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> @@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>  	return -EINVAL;
>  }
>  
> +static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> +			     struct file *file, void *fh, void *arg)
> +{
> +	struct v4l2_ext_pix_format *ef = arg;
> +	struct v4l2_format f = {
> +		.type = ef->type,
> +	};
> +	int ret = check_fmt(file, ef->type);
> +
> +	if (ret)
> +		return ret;
> +
> +	memset(ef, 0, sizeof(*ef));
> +	ef->type = f.type;
> +
> +	switch (f.type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		if (ops->vidioc_g_ext_pix_fmt_vid_cap)
> +			return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		if (ops->vidioc_g_ext_pix_fmt_vid_out)
> +			return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = v4l_g_fmt(ops, file, fh, &f);
> +	if (ret)
> +		return ret;
> +
> +	v4l2_format_to_ext_pix_format(&f, ef);
> +	return 0;
> +}
> +
>  static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
> @@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>  
>  	switch (p->type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
> +			     !ops->vidioc_s_ext_pix_fmt_vid_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix);
> -		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
> +		ret = ops->vidioc_s_fmt_vid_cap ?
> +		      ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>  		/* just in case the driver zeroed it again */
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>  			v4l_pix_format_touch(&p->fmt.pix);
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
> +			     !ops->vidioc_s_ext_pix_fmt_vid_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>  		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>  			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>  					  bytesperline);
> -		return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
> +		return ops->vidioc_s_fmt_vid_cap_mplane ?
> +		       ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,  VIDIOC_S_FMT);
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>  		if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>  			break;
> @@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>  		CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>  		return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!ops->vidioc_s_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_out &&
> +			     !ops->vidioc_s_ext_pix_fmt_vid_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix);
> -		ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
> +		ret = ops->vidioc_s_fmt_vid_out ?
> +		      ops->vidioc_s_fmt_vid_out(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>  		/* just in case the driver zeroed it again */
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
> +			     !ops->vidioc_s_ext_pix_fmt_vid_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>  		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>  			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>  					  bytesperline);
> -		return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
> +		return ops->vidioc_s_fmt_vid_out_mplane ?
> +		       ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>  		if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>  			break;
> @@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>  	return -EINVAL;
>  }
>  
> +static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> +			     struct file *file, void *fh, void *arg)
> +{
> +	struct video_device *vfd = video_devdata(file);
> +	struct v4l2_ext_pix_format *ef = arg;
> +	struct v4l2_format f;
> +	int ret = check_fmt(file, ef->type);
> +
> +	if (ret)
> +		return ret;
> +
> +	memset(ef->reserved, 0, sizeof(ef->reserved));
> +
> +	switch (ef->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		if (ops->vidioc_s_ext_pix_fmt_vid_cap)
> +			return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		if (ops->vidioc_s_ext_pix_fmt_vid_out)
> +			return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
> +
> +	ret = v4l_s_fmt(ops, file, fh, &f);
> +	if (ret)
> +		/* TODO: retry with M-variant of ef->pixelformat? */

See my comments on this at the top.

> +		return ret;
> +
> +	v4l2_format_to_ext_pix_format(&f, ef);
> +	return 0;
> +}
> +
>  static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
> @@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>  
>  	switch (p->type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!ops->vidioc_try_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix);
> -		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
> +		ret = ops->vidioc_try_fmt_vid_cap ?
> +		      ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
>  		/* just in case the driver zeroed it again */
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>  			v4l_pix_format_touch(&p->fmt.pix);
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>  		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>  			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>  					  bytesperline);
> -		return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
> +		return ops->vidioc_try_fmt_vid_cap_mplane ?
> +		       ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +					     VIDIOC_TRY_FMT);
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>  		if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>  			break;
> @@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>  		CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>  		return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!ops->vidioc_try_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_out &&
> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix);
> -		ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
> +		ret = ops->vidioc_try_fmt_vid_out ?
> +		      ops->vidioc_try_fmt_vid_out(file, fh, arg) :
> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
>  		/* just in case the driver zeroed it again */
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>  		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>  			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>  					  bytesperline);
> -		return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
> +		return ops->vidioc_try_fmt_vid_out_mplane ?
> +		       ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> +					     VIDIOC_TRY_FMT);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>  		if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>  			break;
> @@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>  	return -EINVAL;
>  }
>  
> +static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> +			       struct file *file, void *fh, void *arg)
> +{
> +	struct video_device *vfd = video_devdata(file);
> +	struct v4l2_ext_pix_format *ef = arg;
> +	struct v4l2_format f;
> +	int ret = check_fmt(file, ef->type);
> +
> +	if (ret)
> +		return ret;
> +
> +	memset(ef->reserved, 0, sizeof(ef->reserved));
> +
> +	switch (ef->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +		if (ops->vidioc_try_ext_pix_fmt_vid_cap)
> +			return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
> +								   ef);
> +		break;
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		if (ops->vidioc_try_ext_pix_fmt_vid_out)
> +			return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
> +								   ef);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
> +
> +	ret = v4l_try_fmt(ops, file, fh, &f);
> +	if (ret)
> +		/* TODO: retry with M-variant of ef->pixelformat? */
> +		return ret;
> +
> +	v4l2_format_to_ext_pix_format(&f, ef);
> +	return 0;
> +}
> +
>  static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
> @@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
>  	IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
>  	IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
>  	IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
> +	IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, v4l_print_ext_pix_format, 0),
> +	IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, v4l_print_ext_pix_format, INFO_FL_PRIO),
> +	IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, v4l_print_ext_pix_format, 0),
>  };
>  #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
>  
> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
> index edb733f21604..c44708dc9355 100644
> --- a/include/media/v4l2-ioctl.h
> +++ b/include/media/v4l2-ioctl.h
> @@ -48,11 +48,17 @@ struct v4l2_fh;
>   * @vidioc_g_fmt_vid_cap: pointer to the function that implements
>   *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
>   *	in single plane mode
> + * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that implements
> + *	:ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
> + *	capture
>   * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
>   *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>   * @vidioc_g_fmt_vid_out: pointer to the function that implements
>   *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
>   *	in single plane mode
> + * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that implements
> + *	:ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
> + *	out
>   * @vidioc_g_fmt_vid_out_overlay: pointer to the function that implements
>   *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
>   * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
> @@ -82,11 +88,16 @@ struct v4l2_fh;
>   * @vidioc_s_fmt_vid_cap: pointer to the function that implements
>   *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
>   *	in single plane mode
> + * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that implements
> + *	:ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
> + *	capture
>   * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
>   *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>   * @vidioc_s_fmt_vid_out: pointer to the function that implements
>   *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
>   *	in single plane mode
> + * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that implements
> + *	:ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out
>   * @vidioc_s_fmt_vid_out_overlay: pointer to the function that implements
>   *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
>   * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
> @@ -116,11 +127,16 @@ struct v4l2_fh;
>   * @vidioc_try_fmt_vid_cap: pointer to the function that implements
>   *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video capture
>   *	in single plane mode
> + * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that implements
> + *	:ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for
> +	video capture
>   * @vidioc_try_fmt_vid_overlay: pointer to the function that implements
>   *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>   * @vidioc_try_fmt_vid_out: pointer to the function that implements
>   *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
>   *	in single plane mode
> + * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that implements
> + *	:ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out
>   * @vidioc_try_fmt_vid_out_overlay: pointer to the function that implements
>   *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>   *	output
> @@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
>  	/* VIDIOC_G_FMT handlers */
>  	int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
>  				    struct v4l2_format *f);
> +	int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> +					    struct v4l2_ext_pix_format *ef);
>  	int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
>  					struct v4l2_format *f);
>  	int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
>  				    struct v4l2_format *f);
> +	int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> +					    struct v4l2_ext_pix_format *ef);
>  	int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
>  					    struct v4l2_format *f);
>  	int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
> @@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
>  	/* VIDIOC_S_FMT handlers */
>  	int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
>  				    struct v4l2_format *f);
> +	int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> +					    struct v4l2_ext_pix_format *ef);
>  	int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
>  					struct v4l2_format *f);
>  	int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
>  				    struct v4l2_format *f);
> +	int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> +					    struct v4l2_ext_pix_format *ef);
>  	int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
>  					    struct v4l2_format *f);
>  	int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
> @@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
>  	/* VIDIOC_TRY_FMT handlers */
>  	int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
>  				      struct v4l2_format *f);
> +	int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> +					      struct v4l2_ext_pix_format *ef);
>  	int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
>  					  struct v4l2_format *f);
>  	int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
>  				      struct v4l2_format *f);
> +	int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> +					      struct v4l2_ext_pix_format *ef);
>  	int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
>  					     struct v4l2_format *f);
>  	int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index d9b7c9177605..a2d850513708 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
>  	__u8				reserved[7];
>  } __attribute__ ((packed));
>  
> +/**
> + * struct v4l2_ext_pix_format - extended single/multiplanar format definition
> + * @type:		type of the data stream; V4L2_BUF_TYPE_VIDEO_CAPTURE or
> + *			V4L2_BUF_TYPE_VIDEO_OUTPUT
> + * @width:		image width in pixels
> + * @height:		image height in pixels
> + * @field:		enum v4l2_field; field order (for interlaced video)
> + * @plane_fmt:		per-plane information
> + * @pixelformat:	little endian four character code (fourcc)
> + * @modifier:		modifier applied to the format (used for tiled formats
> + *			and other kind of HW-specific formats, like compressed
> + *			formats) as defined in drm_fourcc.h
> + * @colorspace:		enum v4l2_colorspace; supplemental to pixelformat
> + * @xfer_func:		enum v4l2_xfer_func, colorspace transfer function
> + * @ycbcr_enc:		enum v4l2_ycbcr_encoding, Y'CbCr encoding
> + * @hsv_enc:		enum v4l2_hsv_encoding, HSV encoding
> + * @quantization:	enum v4l2_quantization, colorspace quantization
> + * @reserved:		extra space reserved for future fields, must be set to 0
> + */
> +struct v4l2_ext_pix_format {
> +	__u32 type;
> +	__u32 width;
> +	__u32 height;
> +	__u32 field;
> +	struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
> +	__u32 pixelformat;
> +	__u64 modifier;
> +	__u32 colorspace;
> +	__u32 xfer_func;
> +	union {
> +		__u32 ycbcr_enc;
> +		__u32 hsv_enc;
> +	};
> +	__u32 quantization;
> +	__u32 reserved[9];
> +};
> +
>  /**
>   * struct v4l2_sdr_format - SDR format definition
>   * @pixelformat:	little endian four character code (fourcc)
> @@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
>  
>  #define VIDIOC_QUERY_EXT_CTRL	_IOWR('V', 103, struct v4l2_query_ext_ctrl)
>  
> +#define VIDIOC_G_EXT_PIX_FMT	_IOWR('V', 104, struct v4l2_ext_pix_format)
> +#define VIDIOC_S_EXT_PIX_FMT	_IOWR('V', 105, struct v4l2_ext_pix_format)
> +#define VIDIOC_TRY_EXT_PIX_FMT	_IOWR('V', 106, struct v4l2_ext_pix_format)
> +
>  /* Reminder: when adding new ioctls please add support for them to
>     drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
>  
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types
  2021-01-14 18:07 ` [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types Helen Koike
@ 2021-02-23 12:58   ` Hans Verkuil
  2023-01-26  7:07     ` ayaka
       [not found]   ` <20230125200026.16643-1-ayaka@soulik.info>
  1 sibling, 1 reply; 48+ messages in thread
From: Hans Verkuil @ 2021-02-23 12:58 UTC (permalink / raw)
  To: Helen Koike, linux-media
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

On 14/01/2021 19:07, Helen Koike wrote:
> Those extended buffer ops have several purpose:
> 1/ Fix y2038 issues by converting the timestamp into an u64 counting
>    the number of ns elapsed since 1970
> 2/ Unify single/multiplanar handling
> 3/ Add a new start offset field to each v4l2 plane buffer info struct
>    to support the case where a single buffer object is storing all
>    planes data, each one being placed at a different offset
> 
> New hooks are created in v4l2_ioctl_ops so that drivers can start using
> these new objects.
> 
> Note that the timecode field is gone, since there doesn't seem to be
> in-kernel users. We can be added back in the reserved area if needed or
> use the Request API to collect more metadata information from the
> frame.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> ---
> 
> Changes in v6:
> This patch was completely refactored, and based on previous version from
> Hans and Boris.
> - Refactor conversions v4l2_buffer <-> v4l2_ext_buffer for (d)qbuf
> - I removed EXT_CREATE_BUFS since it is basically only usefull to MMAP.
>   If this is going towards DMA-fd centric, then we can use the current
>   REQUESTBUF to switch to it, and we can think a better way to support
>   MMAP later if there are usecases.
>   I also moved memory field from v4l2_ext_plane to v4l2_ext_buffer,
>   since it is very unlikely to mix memory types, and REQUESTBUF can
>   switch the whole buffer object to a given type.
> - I removed EXT_QUERYBUF, since it is only useful to MMAP, for the
>   same reason above.
> - I removed EXT_PREPARE_BUF, since it is basically just an optimization,
>   we can add it later (my intention is to simplify this patchset).
> - These ioctls are only valid for video types (and not for overlay,
>   vbi, touch, meta, etc).
> - Refactor struct v4l2_ext_buffer and struct v4l2_ext_planes as
>   discussed with Tomasz:
> 	- add bytesused back
> 	- remove lenght field
> 	- move memory field from planes to buffer object
> - Fix order in documentation of struct v4l2_ext_buffer (Tomasz)
> - Fix flags documentation of struct v4l2_ext_buffer, don't say when flags are ignored (Tomasz)
> - v4l_print_ext_buffer(): print request_fd and offset/userptr (Tomasz)
> 
> Changes in v5:
> - migrate memory from v4l2_ext_buffer to v4l2_ext_plane
> - return mem_offset to struct v4l2_ext_plane
> - change sizes and reorder fields to avoid holes in the struct and make
>   it the same for 32 and 64 bits
> 
> Changes in v4:
> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
> - Drop VIDIOC_EXT_EXPBUF, since the only difference from VIDIOC_EXPBUF
> was that with VIDIOC_EXT_EXPBUF we could export multiple planes at once.
> I think we can add this later, so I removed it from this RFC to simplify it.
> - Remove num_planes field from struct v4l2_ext_buffer
> - Add flags field to struct v4l2_ext_create_buffers
> - Reformulate struct v4l2_ext_plane
> - Fix some bugs caught by v4l2-compliance
> - Rebased on top of media/master (post 5.8-rc1)
> 
> Changes in v3:
> - Rebased on top of media/master (post 5.4-rc1)
> 
> Changes in v2:
> - Add reserved space to v4l2_ext_buffer so that new fields can be added
>   later on
> 
> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> ---
>  drivers/media/v4l2-core/v4l2-dev.c   |   4 +
>  drivers/media/v4l2-core/v4l2-ioctl.c | 184 +++++++++++++++++++++++++++
>  include/media/v4l2-ioctl.h           |   8 ++
>  include/uapi/linux/videodev2.h       |  55 ++++++++
>  4 files changed, 251 insertions(+)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> index 5add58cb6d45..94c9f1e04704 100644
> --- a/drivers/media/v4l2-core/v4l2-dev.c
> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> @@ -664,6 +664,10 @@ static void determine_valid_ioctls(struct video_device *vdev)
>  			set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
>  		SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
>  		SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
> +		SET_VALID_IOCTL(ops, VIDIOC_EXT_QBUF, vidioc_ext_qbuf);
> +		SET_VALID_IOCTL(ops, VIDIOC_EXT_QBUF, vidioc_qbuf);
> +		SET_VALID_IOCTL(ops, VIDIOC_EXT_DQBUF, vidioc_ext_dqbuf);
> +		SET_VALID_IOCTL(ops, VIDIOC_EXT_DQBUF, vidioc_dqbuf);
>  	}
>  	if (is_meta && is_rx) {
>  		/* metadata capture specific ioctls */
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index a9c07c0a73ec..ba633e7efd6d 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -533,6 +533,24 @@ static void v4l_print_buffer(const void *arg, bool write_only)
>  			tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
>  }
>  
> +static void v4l_print_ext_buffer(const void *arg, bool write_only)
> +{
> +	const struct v4l2_ext_buffer *e = arg;
> +	unsigned int i;
> +
> +	pr_cont("%lld index=%d, type=%s, request_fd=%d, flags=0x%08llx, field=%s, sequence=%d, memory=%s\n",
> +		e->timestamp, e->index, prt_names(e->type, v4l2_type_names),
> +		e->request_fd, e->flags, prt_names(e->field, v4l2_field_names),
> +		e->sequence, prt_names(e->memory, v4l2_memory_names));
> +
> +	for (i = 0; i < VIDEO_MAX_PLANES && e->planes[i].m.userptr; i++) {
> +		const struct v4l2_ext_plane *plane = &e->planes[i];
> +
> +		pr_cont("plane %d: bytesused=%d, offset=0x%08x, userptr=0x%llx\n",
> +			i, plane->bytesused, plane->offset, plane->m.userptr);
> +	}
> +}
> +
>  static void v4l_print_exportbuffer(const void *arg, bool write_only)
>  {
>  	const struct v4l2_exportbuffer *p = arg;
> @@ -2552,6 +2570,130 @@ static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
>  	return ret ? ret : ops->vidioc_qbuf(file, fh, p);
>  }
>  
> +static bool v4l2_ext_buffer_is_single_membuf(const struct v4l2_ext_buffer *eb)
> +{
> +	unsigned int i;
> +
> +	for (i = 1; i < VIDEO_MAX_PLANES && eb->planes[i].m.userptr; i++)
> +		if (eb->planes[i].m.userptr != eb->planes[i - 1].m.userptr)

Is this safe? m.userptr is a u64, but the other two fields in the union are
32 bits. If you are actually comparing fds, how certain are you that the
32 bits following the 'fd' are zeroed instead of containing garbage?

Perhaps mem_offset and fd should be u64 and s64 respectively to avoid any
uninitialized bits?

> +			return false;
> +	return true;
> +}
> +
> +static int v4l2_fill_buffer_from_ext_buffer(const struct v4l2_ioctl_ops *ops,
> +					    struct file *file, void *fh,
> +					    const struct v4l2_ext_buffer *eb,
> +					    struct v4l2_buffer *b,
> +					    struct v4l2_plane *bplanes)
> +{
> +	const struct v4l2_ext_plane *eplanes = (struct v4l2_ext_plane *)&eb->planes;
> +	struct video_device *vfd = video_devdata(file);
> +	bool is_mplane = V4L2_IS_CAP_MULTIPLANAR(vfd);
> +	unsigned int i;
> +	u64 nsecs;
> +	int ret;
> +
> +	b->index = eb->index;
> +	if (is_mplane) {
> +		b->m.planes = bplanes;
> +		b->length = VIDEO_MAX_PLANES;
> +		if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +			b->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +		else if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
> +			b->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> +	} else {
> +		b->type = eb->type;
> +	}
> +
> +	/* Fill the rest of the v4l2_buffer */
> +	ret = v4l_querybuf(ops, file, fh, b);

This call is weird. Why is this needed?

> +	if (ret)
> +		return ret;
> +
> +	/* Fill other fields from v4l2_ext_buffer */
> +	b->flags = eb->flags;
> +	b->field = eb->field;
> +	b->timestamp.tv_sec = div64_u64_rem(eb->timestamp, NSEC_PER_SEC, &nsecs);
> +	b->timestamp.tv_usec = (u32)nsecs / NSEC_PER_USEC;

Use v4l2_buffer_set_timestamp() for this.

> +	b->sequence = eb->sequence;
> +
> +	if (!is_mplane) {
> +		for (i = 0; i < VIDEO_MAX_PLANES; i++) {
> +			b->bytesused += eplanes[i].bytesused;
> +			WARN_ON(eplanes[i].offset);
> +		}
> +
> +		/* MMAP info was filled by querybuf */
> +		if (b->memory == V4L2_MEMORY_MMAP)
> +			return 0 ;

space before ;

> +
> +		/*
> +		 * TODO: get the length of the buffer, for now, just
> +		 * set to max to avoid errors in checks.
> +		 */
> +		b->length = U32_MAX;
> +		b->m.userptr = eplanes[0].m.userptr;
> +		return 0;
> +	}
> +
> +	bplanes[0].bytesused = eplanes[0].bytesused + eplanes[0].offset;
> +	bplanes[0].data_offset = eplanes[0].offset;
> +	if (v4l2_ext_buffer_is_single_membuf(eb))
> +		for (i = 1; i < VIDEO_MAX_PLANES && eplanes[i].bytesused; i++) {
> +			bplanes[0].bytesused += eplanes[i].bytesused;
> +			WARN_ON(eplanes[i].offset);
> +		}
> +	else
> +		for (i = 1; i < VIDEO_MAX_PLANES && eplanes[i].bytesused; i++) {
> +			bplanes[i].bytesused = eplanes[i].bytesused +
> +					       eplanes[i].offset;
> +			bplanes[i].data_offset = eplanes[i].offset;
> +		}
> +
> +	/* MMAP info was filled by querybuf */
> +	if (b->memory == V4L2_MEMORY_MMAP)
> +		return 0;
> +
> +	for (i = 0; i < VIDEO_MAX_PLANES && eplanes[i].m.userptr; i++) {
> +		bplanes[i].m.userptr = eplanes[i].m.userptr;
> +		/*
> +		 * TODO: get the length of the buffer, for now, just
> +		 * set to max to avoid errors in checks.
> +		 */
> +		bplanes[i].length = U32_MAX;
> +	}
> +	return 0;
> +}
> +
> +static int v4l_ext_qbuf(const struct v4l2_ioctl_ops *ops,
> +			struct file *file, void *fh, void *arg)
> +{
> +	struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
> +	struct v4l2_ext_buffer *eb = arg;
> +	struct v4l2_buffer b = {0};
> +
> +	int ret = check_fmt(file, eb->type);
> +
> +	if (!ret)
> +		return ret;
> +
> +	if (ops->vidioc_ext_qbuf)
> +		return ops->vidioc_ext_qbuf(file, fh, eb);
> +
> +	/* Fill other fields from v4l2_ext_buffer */
> +	ret = v4l2_fill_buffer_from_ext_buffer(ops, file, fh, eb, &b, planes);
> +	if (ret)
> +		return ret;
> +
> +	ret = v4l_qbuf(ops, file, fh, &b);
> +	if (ret)
> +		return ret;
> +
> +	/* TODO: check if we need to fill other fields */
> +	eb->flags = b.flags;
> +	return 0;
> +}
> +
>  static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
> @@ -2561,6 +2703,46 @@ static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
>  	return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
>  }
>  
> +static int v4l_ext_dqbuf(const struct v4l2_ioctl_ops *ops,
> +			 struct file *file, void *fh, void *arg)
> +{
> +	struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
> +	struct v4l2_ext_buffer *eb = arg;
> +	struct v4l2_buffer b = {0};
> +	unsigned int i;
> +
> +	int ret = check_fmt(file, eb->type);
> +
> +	if (!ret)
> +		return ret;
> +
> +	if (ops->vidioc_ext_qbuf)
> +		return ops->vidioc_ext_qbuf(file, fh, eb);
> +
> +	/* Fill other fields from v4l2_ext_buffer */
> +	ret = v4l2_fill_buffer_from_ext_buffer(ops, file, fh, eb, &b, planes);
> +	if (ret)
> +		return ret;
> +
> +	ret = v4l_qbuf(ops, file, fh, &b);
> +	if (ret)
> +		return ret;
> +
> +	/* TODO: check if we need to fill other fields */
> +	eb->flags = b.flags;
> +
> +	/*
> +	 * Set buffer pointers to zero. Usecase: DMA-fd might have being

Use-case

> +	 * alread closed, so just request userspace to fill it again in queue

already
in queue -> at queue

> +	 * time.
> +	 */
> +	for (i = 0; i < VIDEO_MAX_PLANES && eb->planes[i].m.userptr; i++) {
> +		eb->planes[i].m.userptr = 0;
> +	}
> +
> +	return 0;
> +}
> +
>  static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
> @@ -3340,6 +3522,8 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
>  	IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, v4l_print_ext_pix_format, 0),
>  	IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, v4l_print_ext_pix_format, INFO_FL_PRIO),
>  	IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, v4l_print_ext_pix_format, 0),
> +	IOCTL_INFO(VIDIOC_EXT_QBUF, v4l_ext_qbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
> +	IOCTL_INFO(VIDIOC_EXT_DQBUF, v4l_ext_dqbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
>  };
>  #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
>  
> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
> index c44708dc9355..1d0ed36e5e67 100644
> --- a/include/media/v4l2-ioctl.h
> +++ b/include/media/v4l2-ioctl.h
> @@ -171,10 +171,14 @@ struct v4l2_fh;
>   *	:ref:`VIDIOC_QUERYBUF <vidioc_querybuf>` ioctl
>   * @vidioc_qbuf: pointer to the function that implements
>   *	:ref:`VIDIOC_QBUF <vidioc_qbuf>` ioctl
> + * @vidioc_ext_qbuf: pointer to the function that implements
> + *	:ref:`VIDIOC_EXT_QBUF <vidioc_ext_qbuf>` ioctl
>   * @vidioc_expbuf: pointer to the function that implements
>   *	:ref:`VIDIOC_EXPBUF <vidioc_expbuf>` ioctl
>   * @vidioc_dqbuf: pointer to the function that implements
>   *	:ref:`VIDIOC_DQBUF <vidioc_qbuf>` ioctl
> + * @vidioc_ext_dqbuf: pointer to the function that implements
> + *	:ref:`VIDIOC_EXT_DQBUF <vidioc_ext_qbuf>` ioctl
>   * @vidioc_create_bufs: pointer to the function that implements
>   *	:ref:`VIDIOC_CREATE_BUFS <vidioc_create_bufs>` ioctl
>   * @vidioc_prepare_buf: pointer to the function that implements
> @@ -441,10 +445,14 @@ struct v4l2_ioctl_ops {
>  			       struct v4l2_buffer *b);
>  	int (*vidioc_qbuf)(struct file *file, void *fh,
>  			   struct v4l2_buffer *b);
> +	int (*vidioc_ext_qbuf)(struct file *file, void *fh,
> +			       struct v4l2_ext_buffer *b);
>  	int (*vidioc_expbuf)(struct file *file, void *fh,
>  			     struct v4l2_exportbuffer *e);
>  	int (*vidioc_dqbuf)(struct file *file, void *fh,
>  			    struct v4l2_buffer *b);
> +	int (*vidioc_ext_dqbuf)(struct file *file, void *fh,
> +				struct v4l2_ext_buffer *b);
>  
>  	int (*vidioc_create_bufs)(struct file *file, void *fh,
>  				  struct v4l2_create_buffers *b);
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index a2d850513708..508ac11645bc 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -1056,6 +1056,59 @@ struct v4l2_buffer {
>  	};
>  };
>  
> +/**
> + * struct v4l2_ext_plane - extended plane buffer info
> + * @offset:		offset in the memory buffer where the plane starts.
> + * @bytesused:		number of bytes occupied by data in the plane (payload).
> + * @mmap_offset:	If V4L2_MEMORY_MMAP is used, then it can be a "cookie"
> + *			that should be passed to mmap() called on the video node.
> + * @userptr:		when memory is V4L2_MEMORY_USERPTR, a userspace pointer pointing
> + *			to this plane.
> + * @dmabuf_fd:		when memory is V4L2_MEMORY_DMABUF, a userspace file descriptor
> + *			associated with this plane.
> + * @reserved:		extra space reserved for future fields, must be set to 0.
> + */
> +struct v4l2_ext_plane {
> +	__u32 offset;
> +	__u32 bytesused;
> +	union {
> +		__u32 mmap_offset;
> +		__u64 userptr;
> +		__s32 dmabuf_fd;
> +	} m;
> +	__u32 reserved[6];
> +};
> +
> +/**
> + * struct v4l2_ext_buffer - extended video buffer info
> + * @index:	id number of the buffer
> + * @type:	V4L2_BUF_TYPE_VIDEO_CAPTURE or V4L2_BUF_TYPE_VIDEO_OUTPUT
> + * @field:	enum v4l2_field; field order of the image in the buffer
> + * @sequence:	sequence count of this frame
> + * @flags:	buffer informational flags
> + * @timestamp:	frame timestamp
> + * @memory:	enum v4l2_memory; the method, in which the actual video
> + *		data is passed
> + * @request_fd:	fd of the request that this buffer should use
> + * @planes:	per-plane buffer information
> + * @reserved:	extra space reserved for future fields, must be set to 0
> + *
> + * Contains data exchanged by application and driver using one of the Streaming
> + * I/O methods.
> + */
> +struct v4l2_ext_buffer {
> +	__u32 index;
> +	__u32 type;
> +	__u32 field;
> +	__u32 sequence;
> +	__u64 flags;
> +	__u64 timestamp;
> +	__u32 memory;
> +	__s32 request_fd;
> +	struct v4l2_ext_plane planes[VIDEO_MAX_PLANES];
> +	__u32 reserved[10];
> +};
> +
>  #ifndef __KERNEL__
>  /**
>   * v4l2_timeval_to_ns - Convert timeval to nanoseconds
> @@ -2623,6 +2676,8 @@ struct v4l2_create_buffers {
>  #define VIDIOC_G_EXT_PIX_FMT	_IOWR('V', 104, struct v4l2_ext_pix_format)
>  #define VIDIOC_S_EXT_PIX_FMT	_IOWR('V', 105, struct v4l2_ext_pix_format)
>  #define VIDIOC_TRY_EXT_PIX_FMT	_IOWR('V', 106, struct v4l2_ext_pix_format)
> +#define VIDIOC_EXT_QBUF		_IOWR('V', 107, struct v4l2_ext_buffer)
> +#define VIDIOC_EXT_DQBUF	_IOWR('V', 118, struct v4l2_ext_buffer)
>  
>  /* Reminder: when adding new ioctls please add support for them to
>     drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2021-02-23 12:35   ` Hans Verkuil
@ 2021-02-24 15:12     ` Helen Koike
  2022-11-05 15:19       ` Hsia-Jun Li
  0 siblings, 1 reply; 48+ messages in thread
From: Helen Koike @ 2021-02-24 15:12 UTC (permalink / raw)
  To: Hans Verkuil, linux-media
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga

Hi Hans,

Thank you for your comment, please see my reply below.

On 2/23/21 9:35 AM, Hans Verkuil wrote:
> Hi Helen,
> 
> On 14/01/2021 19:07, Helen Koike wrote:
>> This is part of the multiplanar and singleplanar unification process.
>> v4l2_ext_pix_format is supposed to work for both cases.
>>
>> We also add the concept of modifiers already employed in DRM to expose
>> HW-specific formats (like tiled or compressed formats) and allow
>> exchanging this information with the DRM subsystem in a consistent way.
>>
>> Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
>> v4l2_ext_format, other types will be rejected if you use the
>> {G,S,TRY}_EXT_PIX_FMT ioctls.
>>
>> New hooks have been added to v4l2_ioctl_ops to support those new ioctls
>> in drivers, but, in the meantime, the core takes care of converting
>> {S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers can
>> still work if the userspace app/lib uses the new ioctls.
>>
>> The conversion is also done the other around to allow userspace
>> apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
>> _ext_ hooks.
> 
> I have some small comments below, but also one high level comment:
> 
> Regarding M variants of pixelformats: this patch 'normalizes' them to
> regular pixelformats in the extended API. This makes life complicated,
> and I wonder if this is the right approach.
> 
> Currently there are two reasons for a driver to support e.g. NV12M:
> either luma and chroma need to be in two memory banks, or the luma
> and chroma planes cannot be contiguous due to alignment requirements.
> 
> The first requirement is still valid for drivers that support the extended API.
> The second requirement is no longer a reason to support NV12M. But I
> don't think we should just drop NV12M support if it was already supported
> before the conversion to this extended API. Since NV12M allocates two buffers
> instead of one, it is still different from a regular NV12.

I don't see what would be the difference when using NV12 and NV12M in 
Ext API.
NV12 could be used for both requirements. It would even allow the second
requirement with a single memory buffer.

> 
> I would prefer that such drivers support both NV12 and NV12M, so no
> automatic conversion.
> 
> A related question is how to handle pixelformat enumeration: with the
> extended API an NV12 format might work, but not with the old API (e.g.
> due to memory alignment requirements). I wonder if a VIDIOC_ENUM_EXT_PIX_FMT
> isn't needed.

We need VIDIOC_ENUM_EXT_PIX_FMT for modifiers, but we can add it later.
If the driver reports NV12M, userspace can use it and the framework
normilizes it.

> 
> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while VIDIOC_ENUM_FMT
> would just report NV12M.

If NV12 and NV12M are equivalent in Ext API, I don't see why we would
report both (unless I'm missing something, which is probably the case).

The idea was to deprecate the M-variants one day.

Regards,
Helen

> 
>>
>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>> ---
>>
>> Changes in v6:
>>   The main change here was fixing the conversion, so planes reflects color planes,
>>   and to implement this properly I made major refactors compared to the previous
>>   version.
>> - struct v4l2_plane_ext_pix_format removed, using struct v4l2_plane_pix_format instead (Tomasz)
>> - refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
>> - reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
>> - do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
>> - refactor conversion functions, so planes are color planes (Tomasz)
>> - Don't explicitly check for e->modifier != 0 in v4l2_ext_pix_format_to_format() (Tomasz)
>> - Use "ef" for extended formats in the framework for consistency (Tomasz)
>> - Handle xfer_func field in conversions (Tomasz)
>> - Zero reserved fields in v4l_s_ext_pix_fmt() and v4l_try_ext_pix_fmt() (Tomasz)
>> - Refactor format functions to use v4l_fmt_ioctl_via_ext()
>> - Several fixes/refactoring/changes
>> - Remove EXT API for touch devices
>>
>> Changes in v5:
>> - change sizes and reorder fields to avoid holes in the struct and make
>>    it the same for 32 and 64 bits
>> - removed __attribute__ ((packed)) from uapi structs
>> - Fix doc warning from make htmldocs
>> - Updated commit message with EXT_PIX prefix for the ioctls.
>>
>> Changes in v4:
>> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
>> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
>> - Add reserved fields
>> - Removed num_planes from struct v4l2_ext_pix_format
>> - Removed flag field from struct v4l2_ext_pix_format, since the only
>>    defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
>>    where we can use modifiers, or add it back later through the reserved
>>    bits.
>> - In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
>>    != MOD_INVALID
>> - Fix type assignment in v4l_g_fmt_ext_pix()
>> - Rebased on top of media/master (post 5.8-rc1)
>>
>> Changes in v3:
>> - Rebased on top of media/master (post 5.4-rc1)
>>
>> Changes in v2:
>> - Move the modifier in v4l2_ext_format (was formerly placed in
>>    v4l2_ext_plane)
>> - Fix a few bugs in the converters and add a strict parameter to
>>    allow conversion of uninitialized/mis-initialized objects
>> ---
>>   drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
>>   drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
>>   include/media/v4l2-ioctl.h           |  28 ++
>>   include/uapi/linux/videodev2.h       |  41 ++
>>   4 files changed, 602 insertions(+), 32 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
>> index f9cff033d0dc..5add58cb6d45 100644
>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>> @@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct video_device *vdev)
>>   			       ops->vidioc_enum_fmt_vid_overlay)) ||
>>   		    (is_tx && ops->vidioc_enum_fmt_vid_out))
>>   			set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
>> +		if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
>> +		    (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
>> +			 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>>   		if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
>>   			       ops->vidioc_g_fmt_vid_cap_mplane ||
>> -			       ops->vidioc_g_fmt_vid_overlay)) ||
>> +			       ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
>>   		    (is_tx && (ops->vidioc_g_fmt_vid_out ||
>>   			       ops->vidioc_g_fmt_vid_out_mplane ||
>> -			       ops->vidioc_g_fmt_vid_out_overlay)))
>> +			       ops->vidioc_g_ext_pix_fmt_vid_out))) {
>>   			 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>> +			 set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
>> +		}
>> +		if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
>> +		    (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
>> +			 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>>   		if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
>>   			       ops->vidioc_s_fmt_vid_cap_mplane ||
>> -			       ops->vidioc_s_fmt_vid_overlay)) ||
>> +			       ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
>>   		    (is_tx && (ops->vidioc_s_fmt_vid_out ||
>>   			       ops->vidioc_s_fmt_vid_out_mplane ||
>> -			       ops->vidioc_s_fmt_vid_out_overlay)))
>> +			       ops->vidioc_s_ext_pix_fmt_vid_out))) {
>>   			 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>> +			 set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
>> +		}
>> +		if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
>> +		    (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
>> +			 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>>   		if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
>>   			       ops->vidioc_try_fmt_vid_cap_mplane ||
>> -			       ops->vidioc_try_fmt_vid_overlay)) ||
>> +			       ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
>>   		    (is_tx && (ops->vidioc_try_fmt_vid_out ||
>>   			       ops->vidioc_try_fmt_vid_out_mplane ||
>> -			       ops->vidioc_try_fmt_vid_out_overlay)))
>> +			       ops->vidioc_try_ext_pix_fmt_vid_out))) {
>>   			 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>> +			 set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
>> +		}
>>   		SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
>>   		SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
>>   		SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index 848286a284f6..a9c07c0a73ec 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -18,6 +18,8 @@
>>   
>>   #include <linux/videodev2.h>
>>   
>> +#include <drm/drm_fourcc.h>
>> +
>>   #include <media/v4l2-common.h>
>>   #include <media/v4l2-ioctl.h>
>>   #include <media/v4l2-ctrls.h>
>> @@ -38,6 +40,11 @@
>>   
>>   #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), (vfd)->valid_ioctls)
>>   
>> +#define V4L2_IS_CAP_MULTIPLANAR(vdev)	(vdev->device_caps & \
>> +					 (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
>> +					 V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
>> +					 V4L2_CAP_VIDEO_M2M_MPLANE))
>> +
>>   struct std_descr {
>>   	v4l2_std_id std;
>>   	const char *descr;
>> @@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg, bool write_only)
>>   	}
>>   }
>>   
>> +static void v4l_print_ext_pix_format(const void *arg, bool write_only)
>> +{
>> +	const struct v4l2_ext_pix_format *ef = arg;
>> +	unsigned int i;
>> +
>> +	pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u, xfer_func=%u\n",
>> +		prt_names(ef->type, v4l2_type_names),
>> +		ef->width, ef->height,
>> +		(ef->pixelformat & 0xff),
>> +		(ef->pixelformat >>  8) & 0xff,
>> +		(ef->pixelformat >> 16) & 0xff,
>> +		(ef->pixelformat >> 24) & 0xff,
>> +		ef->modifier, prt_names(ef->field, v4l2_field_names),
>> +		ef->colorspace, ef->ycbcr_enc,
>> +		ef->quantization, ef->xfer_func);
>> +	for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
>> +		pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
>> +			 i, ef->plane_fmt[i].bytesperline,
>> +			 ef->plane_fmt[i].sizeimage);
>> +}
>> +
>>   static void v4l_print_framebuffer(const void *arg, bool write_only)
>>   {
>>   	const struct v4l2_framebuffer *p = arg;
>> @@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
>>   	switch (type) {
>>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>   		if ((is_vid || is_tch) && is_rx &&
>> -		    (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane))
>> +		    (ops->vidioc_g_fmt_vid_cap ||
>> +		     ops->vidioc_g_ext_pix_fmt_vid_cap ||
>> +		     ops->vidioc_g_fmt_vid_cap_mplane))
>>   			return 0;
>>   		break;
>>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if ((is_vid || is_tch) && is_rx && ops->vidioc_g_fmt_vid_cap_mplane)
>> +		if ((is_vid || is_tch) && is_rx &&
>> +		    (ops->vidioc_g_fmt_vid_cap_mplane ||
>> +		     ops->vidioc_g_ext_pix_fmt_vid_cap))
>>   			return 0;
>>   		break;
>>   	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>> @@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
>>   		break;
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>   		if (is_vid && is_tx &&
>> -		    (ops->vidioc_g_fmt_vid_out || ops->vidioc_g_fmt_vid_out_mplane))
>> +		    (ops->vidioc_g_fmt_vid_out ||
>> +		     ops->vidioc_g_ext_pix_fmt_vid_out ||
>> +		     ops->vidioc_g_fmt_vid_out_mplane))
>>   			return 0;
>>   		break;
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
>> +		if (is_vid && is_tx &&
>> +		    (ops->vidioc_g_ext_pix_fmt_vid_out ||
>> +		     ops->vidioc_g_fmt_vid_out_mplane))
>>   			return 0;
>>   		break;
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>> @@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct v4l2_format *fmt)
>>   	       sizeof(fmt->fmt.pix) - offset);
>>   }
>>   
>> +static void
>> +v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
>> +				  struct v4l2_pix_format *pix)
>> +{
>> +	unsigned int i;
>> +
>> +	pix->width = ef->width;
>> +	pix->height = ef->height;
>> +	pix->field = ef->field;
>> +	pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>> +	pix->colorspace = ef->colorspace;
>> +	pix->ycbcr_enc = ef->ycbcr_enc;
>> +	pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
>> +	pix->quantization = ef->quantization;
>> +	pix->pixelformat = ef->pixelformat;
>> +	pix->bytesperline = ef->plane_fmt[0].bytesperline;
>> +	pix->sizeimage = ef->plane_fmt[0].sizeimage;
>> +	for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
>> +		pix->sizeimage += ef->plane_fmt[i].sizeimage;
>> +}
>> +
>> +static void
>> +v4l2_ext_pix_format_to_pix_mp_format(const struct v4l2_ext_pix_format *ef,
>> +				     struct v4l2_pix_format_mplane *pix_mp)
>> +{
>> +	const struct v4l2_format_info *info =
>> +					v4l2_format_info(ef->pixelformat);
>> +	unsigned int i;
>> +
>> +	pix_mp->width = ef->width;
>> +	pix_mp->height = ef->height;
>> +	pix_mp->field = ef->field;
>> +	pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>> +	pix_mp->colorspace = ef->colorspace;
>> +	pix_mp->ycbcr_enc = ef->ycbcr_enc;
>> +	pix_mp->quantization = ef->quantization;
>> +	pix_mp->pixelformat = ef->pixelformat;
>> +
>> +	/* This is true when converting to non-M-variant */
>> +	if (info && info->mem_planes == 1) {
>> +		pix_mp->plane_fmt[0] = ef->plane_fmt[0];
>> +		pix_mp->num_planes = 1;
>> +		for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
>> +			pix_mp->plane_fmt[0].sizeimage += ef->plane_fmt[i].sizeimage;
>> +
>> +		return;
>> +	}
>> +
>> +	for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
>> +		pix_mp->plane_fmt[i] = ef->plane_fmt[i];
>> +	pix_mp->num_planes = i;
>> +}
>> +
>> +/*
>> + * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to v4l2_format
>> + *
>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
>> + * @f: A pointer to struct v4l2_format to be filled.
>> + * @is_mplane: Bool indicating if multiplanar API should be used in @f.
>> + *
>> + * If pixelformat should be converted to M-variant, change ef->pixelformat
>> + * to the M-variant before calling this function.
>> + */
>> +static void v4l2_ext_pix_format_to_format(const struct v4l2_ext_pix_format *ef,
>> +					  struct v4l2_format *f, bool is_mplane)
>> +{
>> +	memset(f, 0, sizeof(*f));
>> +
>> +	if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
>> +	    ef->modifier != DRM_FORMAT_MOD_INVALID)
>> +		pr_warn("Modifiers are not supported in v4l2_format, ignoring %llx\n",
>> +			ef->modifier);
>> +
>> +	if (!is_mplane) {
>> +		f->type = ef->type;
>> +		v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
>> +		return;
>> +	}
>> +
>> +	if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +		f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>> +	else
>> +		f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
>> +
>> +	v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
>> +}
>> +
>> +static void
>> +v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
>> +				  struct v4l2_ext_pix_format *ef)
>> +{
>> +	const struct v4l2_format_info *info =
>> +					v4l2_format_info(pix->pixelformat);
>> +	unsigned int i;
>> +
>> +	ef->width = pix->width;
>> +	ef->height = pix->height;
>> +	ef->field = pix->field;
>> +	ef->colorspace = pix->colorspace;
>> +	ef->ycbcr_enc = pix->ycbcr_enc;
>> +	ef->quantization = pix->quantization;
>> +	ef->xfer_func = pix->xfer_func;
>> +	if (pix->flags)
>> +		pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
>> +
>> +	/* We assume M-variants won't be used in this function */
>> +	ef->pixelformat = pix->pixelformat;
>> +
>> +	ef->plane_fmt[0].bytesperline = pix->bytesperline;
>> +	ef->plane_fmt[0].sizeimage = pix->sizeimage;
>> +
>> +	if (!info)
>> +		return;
>> +
>> +	for (i = 1; i < info->comp_planes; i++) {
>> +		ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
>> +		ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
>> +					     ef->height / info->vdiv;
>> +		ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>> +	}
>> +}
>> +
>> +static void
>> +v4l2_pix_mp_format_to_ext_pix_format(const struct v4l2_pix_format_mplane *pix_mp,
>> +				     struct v4l2_ext_pix_format *ef)
>> +{
>> +	const struct v4l2_format_info *info =
>> +					v4l2_format_info(pix_mp->pixelformat);
>> +	unsigned int i;
>> +
>> +	ef->width = pix_mp->width;
>> +	ef->height = pix_mp->height;
>> +	ef->field = pix_mp->field;
>> +	ef->colorspace = pix_mp->colorspace;
>> +	ef->ycbcr_enc = pix_mp->ycbcr_enc;
>> +	ef->quantization = pix_mp->quantization;
>> +	ef->xfer_func = pix_mp->xfer_func;
>> +	if (pix_mp->flags)
>> +		pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
>> +
>> +	if (!info)
>> +		return;
>> +
>> +	ef->pixelformat = info && info->norm ?
> 
> 'info &&' can be dropped, info is always non-NULL here.
> 
>> +			  info->norm : pix_mp->pixelformat;
>> +
>> +	if (info->comp_planes == info->mem_planes) {
>> +		for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES; i++)
>> +			ef->plane_fmt[i] = pix_mp->plane_fmt[i];
>> +
>> +		return;
>> +	}
>> +
>> +	/* case where mem_planes is 1 and comp_planes > 1 */
>> +	ef->plane_fmt[0] = pix_mp->plane_fmt[0];
>> +	for (i = 1; i < info->comp_planes; i++) {
>> +		ef->plane_fmt[i].bytesperline =
>> +			pix_mp->plane_fmt[0].bytesperline / info->hdiv;
>> +		ef->plane_fmt[i].sizeimage =
>> +			ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
>> +		ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>> +	}
>> +}
>> +
>> +/*
>> + * v4l2_format_to_ext_pix_format - convert to v4l2_format to v4l2_ext_pix_format
>> + *
>> + * @f: A pointer to struct v4l2_format to be converted.
>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
>> + *
>> + * This method normalize the pixelformat to non-M variant.
>> + */
>> +static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
>> +					  struct v4l2_ext_pix_format *ef)
>> +{
>> +	memset(ef, 0, sizeof(*ef));
>> +
>> +	switch (f->type) {
>> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> +		ef->type = f->type;
>> +		v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
>> +		break;
>> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> +		ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +		v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>> +		break;
>> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> +		ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>> +		v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>> +		break;
>> +	default:
>> +		WARN("Converting to Ext Pix Format with wrong buffer type %s\n",
>> +		     prt_names(f->type, v4l2_type_names));
>> +		break;
>> +	}
>> +}
>> +
>>   static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
>>   				struct file *file, void *fh, void *arg)
>>   {
>> @@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct v4l2_pix_format *p)
>>   	p->xfer_func = 0;
>>   }
>>   
>> +static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
>> +				 struct file *file, void *fh,
>> +				 struct v4l2_format *f,
>> +				 unsigned int ioctl)
>> +{
>> +	bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
>> +	struct video_device *vdev = video_devdata(file);
>> +	struct v4l2_ext_pix_format ef = {0};
>> +	u32 original_pixfmt = 0;
>> +	u32 cap_mask;
>> +	int ret;
>> +
>> +	if (ioctl != VIDIOC_G_FMT) {
>> +		/*
>> +		 * If CSC attributes are read only, set them to DEFAULT
>> +		 * to avoid changes by the driver.
>> +		 */
>> +		if (is_multiplanar) {
>> +			if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>> +				f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
>> +				f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>> +				f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
>> +				f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>> +			}
>> +			/* Unset the flag to avoid warning in the convertion */
>> +			f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>> +
>> +			/* Save pixelformat in case M-variant is being used */
>> +			original_pixfmt = f->fmt.pix_mp.pixelformat;
>> +		} else {
>> +			if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>> +				f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
>> +				f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>> +				f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
>> +				f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>> +			}
>> +			/* Unset the flag to avoid warning in the convertion */
>> +			f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>> +		}
>> +		v4l2_format_to_ext_pix_format(f, &ef);
>> +	}
>> +
>> +	switch (f->type) {
>> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> +		cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
>> +			   V4L2_CAP_VIDEO_M2M_MPLANE;
>> +		if (!!(vdev->device_caps & cap_mask) !=
>> +		    (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
>> +			return -EINVAL;
>> +
>> +		ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>> +		if (ioctl == VIDIOC_G_FMT)
>> +			ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
>> +		else if (ioctl == VIDIOC_S_FMT)
>> +			ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
>> +		else
>> +			ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>> +								  &ef);
>> +		break;
>> +
>> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> +		cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
>> +			   V4L2_CAP_VIDEO_M2M_MPLANE;
>> +		if (!!(vdev->device_caps & cap_mask) !=
>> +		    (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>> +			return -EINVAL;
>> +
>> +		ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>> +		if (ioctl == VIDIOC_G_FMT)
>> +			ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
>> +		else if (ioctl == VIDIOC_S_FMT)
>> +			ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
>> +		else
>> +			ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>> +								  &ef);
>> +		break;
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (original_pixfmt != ef.pixelformat &&
>> +	    v4l2_format_info(original_pixfmt))
> 
> Could this test be simplified to: 'if (original_pixfmt)'?
> 
> I.e., if the original pixfmt was saved, then restore it here.
> 
>> +		ef.pixelformat = original_pixfmt;
>> +
>> +	v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
>> +	return 0;
>> +}
>> +
>>   static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>   				struct file *file, void *fh, void *arg)
>>   {
>> @@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>   
>>   	switch (p->type) {
>>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!ops->vidioc_g_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
>> +			     !ops->vidioc_g_ext_pix_fmt_vid_cap))
>>   			break;
>>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>> -		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
>> +		ret = ops->vidioc_g_fmt_vid_cap ?
>> +		      ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
>> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>> +					    VIDIOC_G_FMT);
>>   		/* just in case the driver zeroed it again */
>>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>   		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>   			v4l_pix_format_touch(&p->fmt.pix);
>>   		return ret;
>>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>> +		if (ops->vidioc_g_fmt_vid_cap_mplane)
>> +			return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>> +		else if (ops->vidioc_g_ext_pix_fmt_vid_cap)
> 
> 'else' can be dropped.
> 
>> +			return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>> +						     VIDIOC_G_FMT);
>> +		break;
>>   	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>   		return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>>   	case V4L2_BUF_TYPE_VBI_CAPTURE:
>> @@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>   	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>>   		return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!ops->vidioc_g_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_g_fmt_vid_out &&
>> +			     !ops->vidioc_g_ext_pix_fmt_vid_out))
>>   			break;
>>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>> -		ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
>> +		ret = ops->vidioc_g_fmt_vid_out ?
>> +		      ops->vidioc_g_fmt_vid_out(file, fh, arg) :
>> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
>>   		/* just in case the driver zeroed it again */
>>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>   		return ret;
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>> +		if (ops->vidioc_g_fmt_vid_out_mplane)
>> +			return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>> +		else if (ops->vidioc_g_ext_pix_fmt_vid_out)
> 
> Ditto.
> 
>> +			return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>> +						     VIDIOC_G_FMT);
>> +		break;
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>   		return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>>   	case V4L2_BUF_TYPE_VBI_OUTPUT:
>> @@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>   	return -EINVAL;
>>   }
>>   
>> +static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>> +			     struct file *file, void *fh, void *arg)
>> +{
>> +	struct v4l2_ext_pix_format *ef = arg;
>> +	struct v4l2_format f = {
>> +		.type = ef->type,
>> +	};
>> +	int ret = check_fmt(file, ef->type);
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	memset(ef, 0, sizeof(*ef));
>> +	ef->type = f.type;
>> +
>> +	switch (f.type) {
>> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> +		if (ops->vidioc_g_ext_pix_fmt_vid_cap)
>> +			return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
>> +		break;
>> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> +		if (ops->vidioc_g_ext_pix_fmt_vid_out)
>> +			return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = v4l_g_fmt(ops, file, fh, &f);
>> +	if (ret)
>> +		return ret;
>> +
>> +	v4l2_format_to_ext_pix_format(&f, ef);
>> +	return 0;
>> +}
>> +
>>   static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>   				struct file *file, void *fh, void *arg)
>>   {
>> @@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>   
>>   	switch (p->type) {
>>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
>> +			     !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>   			break;
>>   		CLEAR_AFTER_FIELD(p, fmt.pix);
>> -		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
>> +		ret = ops->vidioc_s_fmt_vid_cap ?
>> +		      ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
>> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>   		/* just in case the driver zeroed it again */
>>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>   		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>   			v4l_pix_format_touch(&p->fmt.pix);
>>   		return ret;
>>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
>> +			     !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>   			break;
>>   		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>   		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>   			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>   					  bytesperline);
>> -		return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
>> +		return ops->vidioc_s_fmt_vid_cap_mplane ?
>> +		       ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
>> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,  VIDIOC_S_FMT);
>>   	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>   		if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>>   			break;
>> @@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>   		CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>   		return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!ops->vidioc_s_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_out &&
>> +			     !ops->vidioc_s_ext_pix_fmt_vid_out))
>>   			break;
>>   		CLEAR_AFTER_FIELD(p, fmt.pix);
>> -		ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
>> +		ret = ops->vidioc_s_fmt_vid_out ?
>> +		      ops->vidioc_s_fmt_vid_out(file, fh, arg) :
>> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>   		/* just in case the driver zeroed it again */
>>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>   		return ret;
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
>> +			     !ops->vidioc_s_ext_pix_fmt_vid_out))
>>   			break;
>>   		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>   		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>   			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>   					  bytesperline);
>> -		return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
>> +		return ops->vidioc_s_fmt_vid_out_mplane ?
>> +		       ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
>> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>   		if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>>   			break;
>> @@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>   	return -EINVAL;
>>   }
>>   
>> +static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>> +			     struct file *file, void *fh, void *arg)
>> +{
>> +	struct video_device *vfd = video_devdata(file);
>> +	struct v4l2_ext_pix_format *ef = arg;
>> +	struct v4l2_format f;
>> +	int ret = check_fmt(file, ef->type);
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	memset(ef->reserved, 0, sizeof(ef->reserved));
>> +
>> +	switch (ef->type) {
>> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> +		if (ops->vidioc_s_ext_pix_fmt_vid_cap)
>> +			return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
>> +		break;
>> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> +		if (ops->vidioc_s_ext_pix_fmt_vid_out)
>> +			return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
>> +
>> +	ret = v4l_s_fmt(ops, file, fh, &f);
>> +	if (ret)
>> +		/* TODO: retry with M-variant of ef->pixelformat? */
> 
> See my comments on this at the top.
> 
>> +		return ret;
>> +
>> +	v4l2_format_to_ext_pix_format(&f, ef);
>> +	return 0;
>> +}
>> +
>>   static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>   				struct file *file, void *fh, void *arg)
>>   {
>> @@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>   
>>   	switch (p->type) {
>>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!ops->vidioc_try_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
>> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>   			break;
>>   		CLEAR_AFTER_FIELD(p, fmt.pix);
>> -		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
>> +		ret = ops->vidioc_try_fmt_vid_cap ?
>> +		      ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
>> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
>>   		/* just in case the driver zeroed it again */
>>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>   		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>   			v4l_pix_format_touch(&p->fmt.pix);
>>   		return ret;
>>   	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
>> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>   			break;
>>   		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>   		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>   			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>   					  bytesperline);
>> -		return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
>> +		return ops->vidioc_try_fmt_vid_cap_mplane ?
>> +		       ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
>> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>> +					     VIDIOC_TRY_FMT);
>>   	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>   		if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>>   			break;
>> @@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>   		CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>   		return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!ops->vidioc_try_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_out &&
>> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>   			break;
>>   		CLEAR_AFTER_FIELD(p, fmt.pix);
>> -		ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
>> +		ret = ops->vidioc_try_fmt_vid_out ?
>> +		      ops->vidioc_try_fmt_vid_out(file, fh, arg) :
>> +		      v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
>>   		/* just in case the driver zeroed it again */
>>   		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>   		return ret;
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
>> +			     !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>   			break;
>>   		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>   		for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>   			CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>   					  bytesperline);
>> -		return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
>> +		return ops->vidioc_try_fmt_vid_out_mplane ?
>> +		       ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
>> +		       v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>> +					     VIDIOC_TRY_FMT);
>>   	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>   		if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>>   			break;
>> @@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>   	return -EINVAL;
>>   }
>>   
>> +static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>> +			       struct file *file, void *fh, void *arg)
>> +{
>> +	struct video_device *vfd = video_devdata(file);
>> +	struct v4l2_ext_pix_format *ef = arg;
>> +	struct v4l2_format f;
>> +	int ret = check_fmt(file, ef->type);
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	memset(ef->reserved, 0, sizeof(ef->reserved));
>> +
>> +	switch (ef->type) {
>> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> +		if (ops->vidioc_try_ext_pix_fmt_vid_cap)
>> +			return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>> +								   ef);
>> +		break;
>> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> +		if (ops->vidioc_try_ext_pix_fmt_vid_out)
>> +			return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>> +								   ef);
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
>> +
>> +	ret = v4l_try_fmt(ops, file, fh, &f);
>> +	if (ret)
>> +		/* TODO: retry with M-variant of ef->pixelformat? */
>> +		return ret;
>> +
>> +	v4l2_format_to_ext_pix_format(&f, ef);
>> +	return 0;
>> +}
>> +
>>   static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
>>   				struct file *file, void *fh, void *arg)
>>   {
>> @@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
>>   	IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
>>   	IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
>>   	IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
>> +	IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, v4l_print_ext_pix_format, 0),
>> +	IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, v4l_print_ext_pix_format, INFO_FL_PRIO),
>> +	IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, v4l_print_ext_pix_format, 0),
>>   };
>>   #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
>>   
>> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
>> index edb733f21604..c44708dc9355 100644
>> --- a/include/media/v4l2-ioctl.h
>> +++ b/include/media/v4l2-ioctl.h
>> @@ -48,11 +48,17 @@ struct v4l2_fh;
>>    * @vidioc_g_fmt_vid_cap: pointer to the function that implements
>>    *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>    *	in single plane mode
>> + * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that implements
>> + *	:ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
>> + *	capture
>>    * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
>>    *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>    * @vidioc_g_fmt_vid_out: pointer to the function that implements
>>    *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
>>    *	in single plane mode
>> + * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that implements
>> + *	:ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
>> + *	out
>>    * @vidioc_g_fmt_vid_out_overlay: pointer to the function that implements
>>    *	:ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
>>    * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
>> @@ -82,11 +88,16 @@ struct v4l2_fh;
>>    * @vidioc_s_fmt_vid_cap: pointer to the function that implements
>>    *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>    *	in single plane mode
>> + * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that implements
>> + *	:ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
>> + *	capture
>>    * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
>>    *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>    * @vidioc_s_fmt_vid_out: pointer to the function that implements
>>    *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
>>    *	in single plane mode
>> + * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that implements
>> + *	:ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out
>>    * @vidioc_s_fmt_vid_out_overlay: pointer to the function that implements
>>    *	:ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
>>    * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
>> @@ -116,11 +127,16 @@ struct v4l2_fh;
>>    * @vidioc_try_fmt_vid_cap: pointer to the function that implements
>>    *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>    *	in single plane mode
>> + * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that implements
>> + *	:ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for
>> +	video capture
>>    * @vidioc_try_fmt_vid_overlay: pointer to the function that implements
>>    *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>    * @vidioc_try_fmt_vid_out: pointer to the function that implements
>>    *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
>>    *	in single plane mode
>> + * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that implements
>> + *	:ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out
>>    * @vidioc_try_fmt_vid_out_overlay: pointer to the function that implements
>>    *	:ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>    *	output
>> @@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
>>   	/* VIDIOC_G_FMT handlers */
>>   	int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
>>   				    struct v4l2_format *f);
>> +	int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>> +					    struct v4l2_ext_pix_format *ef);
>>   	int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
>>   					struct v4l2_format *f);
>>   	int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
>>   				    struct v4l2_format *f);
>> +	int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>> +					    struct v4l2_ext_pix_format *ef);
>>   	int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
>>   					    struct v4l2_format *f);
>>   	int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
>> @@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
>>   	/* VIDIOC_S_FMT handlers */
>>   	int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
>>   				    struct v4l2_format *f);
>> +	int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>> +					    struct v4l2_ext_pix_format *ef);
>>   	int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
>>   					struct v4l2_format *f);
>>   	int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
>>   				    struct v4l2_format *f);
>> +	int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>> +					    struct v4l2_ext_pix_format *ef);
>>   	int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
>>   					    struct v4l2_format *f);
>>   	int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
>> @@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
>>   	/* VIDIOC_TRY_FMT handlers */
>>   	int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
>>   				      struct v4l2_format *f);
>> +	int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>> +					      struct v4l2_ext_pix_format *ef);
>>   	int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
>>   					  struct v4l2_format *f);
>>   	int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
>>   				      struct v4l2_format *f);
>> +	int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>> +					      struct v4l2_ext_pix_format *ef);
>>   	int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
>>   					     struct v4l2_format *f);
>>   	int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
>> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
>> index d9b7c9177605..a2d850513708 100644
>> --- a/include/uapi/linux/videodev2.h
>> +++ b/include/uapi/linux/videodev2.h
>> @@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
>>   	__u8				reserved[7];
>>   } __attribute__ ((packed));
>>   
>> +/**
>> + * struct v4l2_ext_pix_format - extended single/multiplanar format definition
>> + * @type:		type of the data stream; V4L2_BUF_TYPE_VIDEO_CAPTURE or
>> + *			V4L2_BUF_TYPE_VIDEO_OUTPUT
>> + * @width:		image width in pixels
>> + * @height:		image height in pixels
>> + * @field:		enum v4l2_field; field order (for interlaced video)
>> + * @plane_fmt:		per-plane information
>> + * @pixelformat:	little endian four character code (fourcc)
>> + * @modifier:		modifier applied to the format (used for tiled formats
>> + *			and other kind of HW-specific formats, like compressed
>> + *			formats) as defined in drm_fourcc.h
>> + * @colorspace:		enum v4l2_colorspace; supplemental to pixelformat
>> + * @xfer_func:		enum v4l2_xfer_func, colorspace transfer function
>> + * @ycbcr_enc:		enum v4l2_ycbcr_encoding, Y'CbCr encoding
>> + * @hsv_enc:		enum v4l2_hsv_encoding, HSV encoding
>> + * @quantization:	enum v4l2_quantization, colorspace quantization
>> + * @reserved:		extra space reserved for future fields, must be set to 0
>> + */
>> +struct v4l2_ext_pix_format {
>> +	__u32 type;
>> +	__u32 width;
>> +	__u32 height;
>> +	__u32 field;
>> +	struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
>> +	__u32 pixelformat;
>> +	__u64 modifier;
>> +	__u32 colorspace;
>> +	__u32 xfer_func;
>> +	union {
>> +		__u32 ycbcr_enc;
>> +		__u32 hsv_enc;
>> +	};
>> +	__u32 quantization;
>> +	__u32 reserved[9];
>> +};
>> +
>>   /**
>>    * struct v4l2_sdr_format - SDR format definition
>>    * @pixelformat:	little endian four character code (fourcc)
>> @@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
>>   
>>   #define VIDIOC_QUERY_EXT_CTRL	_IOWR('V', 103, struct v4l2_query_ext_ctrl)
>>   
>> +#define VIDIOC_G_EXT_PIX_FMT	_IOWR('V', 104, struct v4l2_ext_pix_format)
>> +#define VIDIOC_S_EXT_PIX_FMT	_IOWR('V', 105, struct v4l2_ext_pix_format)
>> +#define VIDIOC_TRY_EXT_PIX_FMT	_IOWR('V', 106, struct v4l2_ext_pix_format)
>> +
>>   /* Reminder: when adding new ioctls please add support for them to
>>      drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
>>   
>>
> 
> Regards,
> 
> 	Hans
> 

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2021-02-24 15:12     ` Helen Koike
@ 2022-11-05 15:19       ` Hsia-Jun Li
  2022-11-06 19:24         ` Laurent Pinchart
                           ` (3 more replies)
  0 siblings, 4 replies; 48+ messages in thread
From: Hsia-Jun Li @ 2022-11-05 15:19 UTC (permalink / raw)
  To: Helen Koike
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga,
	Hans Verkuil, linux-media

Hello Helen

I didn't see any updates from V6 and V7-WIP in your repo. That is what I 
need to for our complex tile formats in our platform.

Any future plane here?

Besides I have some ideas on these patches.

On 2/24/21 23:12, Helen Koike wrote:
> Hi Hans,
> 
> Thank you for your comment, please see my reply below.
> 
> On 2/23/21 9:35 AM, Hans Verkuil wrote:
>> Hi Helen,
>>
>> On 14/01/2021 19:07, Helen Koike wrote:
>>> This is part of the multiplanar and singleplanar unification process.
>>> v4l2_ext_pix_format is supposed to work for both cases.
>>>
>>> We also add the concept of modifiers already employed in DRM to expose
>>> HW-specific formats (like tiled or compressed formats) and allow
>>> exchanging this information with the DRM subsystem in a consistent way.
>>>
>>> Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
>>> v4l2_ext_format, other types will be rejected if you use the
>>> {G,S,TRY}_EXT_PIX_FMT ioctls.
>>>
>>> New hooks have been added to v4l2_ioctl_ops to support those new ioctls
>>> in drivers, but, in the meantime, the core takes care of converting
>>> {S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers 
>>> can
>>> still work if the userspace app/lib uses the new ioctls.
>>>
>>> The conversion is also done the other around to allow userspace
>>> apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
>>> _ext_ hooks.
>>
>> I have some small comments below, but also one high level comment:
>>
>> Regarding M variants of pixelformats: this patch 'normalizes' them to
>> regular pixelformats in the extended API. This makes life complicated,
>> and I wonder if this is the right approach.
>>
>> Currently there are two reasons for a driver to support e.g. NV12M:
>> either luma and chroma need to be in two memory banks, or the luma
>> and chroma planes cannot be contiguous due to alignment requirements.
>>
>> The first requirement is still valid for drivers that support the 
>> extended API.
>> The second requirement is no longer a reason to support NV12M. But I
>> don't think we should just drop NV12M support if it was already supported
>> before the conversion to this extended API. Since NV12M allocates two 
>> buffers
>> instead of one, it is still different from a regular NV12.
> 
> I don't see what would be the difference when using NV12 and NV12M in 
> Ext API.
> NV12 could be used for both requirements. It would even allow the second
> requirement with a single memory buffer.
> 
Although I don't have problem here to support both single and multiple 
planes in our hardware. But using the single plane format here is not 
convience for us.

The tile format in our platform is little complex, you can't calculate 
the stride and sizeimage easily with just the modifier, width and 
height. Then we don't have a good place to record the offset here.

Besides, the luma and chroma planes would always need their own 
compression meta data planes. If we use NV12 here, that would make a 
very strange pixel format, one plane for the pixel datas and two planes 
for the meta data.

>>
>> I would prefer that such drivers support both NV12 and NV12M, so no
>> automatic conversion.
>>
>> A related question is how to handle pixelformat enumeration: with the
>> extended API an NV12 format might work, but not with the old API (e.g.
>> due to memory alignment requirements). I wonder if a 
>> VIDIOC_ENUM_EXT_PIX_FMT
>> isn't needed.
> 
> We need VIDIOC_ENUM_EXT_PIX_FMT for modifiers, but we can add it later.
> If the driver reports NV12M, userspace can use it and the framework
> normilizes it.
> 
>>
>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while 
>> VIDIOC_ENUM_FMT
>> would just report NV12M.
> 
> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
> report both (unless I'm missing something, which is probably the case).
> 
> The idea was to deprecate the M-variants one day.
I was thinking the way in DRM API is better, always assuming it would 
always in a multiple planes. The only problem is we don't have a way to 
let the allocator that allocate contiguous memory for planes when we 
need to do that.
> 
> Regards,
> Helen
> 
>>
>>>
>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>>> ---
>>>
>>> Changes in v6:
>>>   The main change here was fixing the conversion, so planes reflects 
>>> color planes,
>>>   and to implement this properly I made major refactors compared to 
>>> the previous
>>>   version.
>>> - struct v4l2_plane_ext_pix_format removed, using struct 
>>> v4l2_plane_pix_format instead (Tomasz)
>>> - refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
>>> - reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
>>> - do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
>>> - refactor conversion functions, so planes are color planes (Tomasz)
>>> - Don't explicitly check for e->modifier != 0 in 
>>> v4l2_ext_pix_format_to_format() (Tomasz)
>>> - Use "ef" for extended formats in the framework for consistency 
>>> (Tomasz)
>>> - Handle xfer_func field in conversions (Tomasz)
>>> - Zero reserved fields in v4l_s_ext_pix_fmt() and 
>>> v4l_try_ext_pix_fmt() (Tomasz)
>>> - Refactor format functions to use v4l_fmt_ioctl_via_ext()
>>> - Several fixes/refactoring/changes
>>> - Remove EXT API for touch devices
>>>
>>> Changes in v5:
>>> - change sizes and reorder fields to avoid holes in the struct and make
>>>    it the same for 32 and 64 bits
>>> - removed __attribute__ ((packed)) from uapi structs
>>> - Fix doc warning from make htmldocs
>>> - Updated commit message with EXT_PIX prefix for the ioctls.
>>>
>>> Changes in v4:
>>> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
>>> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
>>> - Add reserved fields
>>> - Removed num_planes from struct v4l2_ext_pix_format
>>> - Removed flag field from struct v4l2_ext_pix_format, since the only
>>>    defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
>>>    where we can use modifiers, or add it back later through the reserved
>>>    bits.
>>> - In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
>>>    != MOD_INVALID
>>> - Fix type assignment in v4l_g_fmt_ext_pix()
>>> - Rebased on top of media/master (post 5.8-rc1)
>>>
>>> Changes in v3:
>>> - Rebased on top of media/master (post 5.4-rc1)
>>>
>>> Changes in v2:
>>> - Move the modifier in v4l2_ext_format (was formerly placed in
>>>    v4l2_ext_plane)
>>> - Fix a few bugs in the converters and add a strict parameter to
>>>    allow conversion of uninitialized/mis-initialized objects
>>> ---
>>>   drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
>>>   drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
>>>   include/media/v4l2-ioctl.h           |  28 ++
>>>   include/uapi/linux/videodev2.h       |  41 ++
>>>   4 files changed, 602 insertions(+), 32 deletions(-)
>>>
>>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c 
>>> b/drivers/media/v4l2-core/v4l2-dev.c
>>> index f9cff033d0dc..5add58cb6d45 100644
>>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>>> @@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct 
>>> video_device *vdev)
>>>                      ops->vidioc_enum_fmt_vid_overlay)) ||
>>>               (is_tx && ops->vidioc_enum_fmt_vid_out))
>>>               set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
>>> +        if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
>>> +            (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
>>> +             set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>>>           if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
>>>                      ops->vidioc_g_fmt_vid_cap_mplane ||
>>> -                   ops->vidioc_g_fmt_vid_overlay)) ||
>>> +                   ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
>>>               (is_tx && (ops->vidioc_g_fmt_vid_out ||
>>>                      ops->vidioc_g_fmt_vid_out_mplane ||
>>> -                   ops->vidioc_g_fmt_vid_out_overlay)))
>>> +                   ops->vidioc_g_ext_pix_fmt_vid_out))) {
>>>                set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>>> +             set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
>>> +        }
>>> +        if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
>>> +            (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
>>> +             set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>>>           if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
>>>                      ops->vidioc_s_fmt_vid_cap_mplane ||
>>> -                   ops->vidioc_s_fmt_vid_overlay)) ||
>>> +                   ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
>>>               (is_tx && (ops->vidioc_s_fmt_vid_out ||
>>>                      ops->vidioc_s_fmt_vid_out_mplane ||
>>> -                   ops->vidioc_s_fmt_vid_out_overlay)))
>>> +                   ops->vidioc_s_ext_pix_fmt_vid_out))) {
>>>                set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>>> +             set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
>>> +        }
>>> +        if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
>>> +            (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
>>> +             set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>>>           if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
>>>                      ops->vidioc_try_fmt_vid_cap_mplane ||
>>> -                   ops->vidioc_try_fmt_vid_overlay)) ||
>>> +                   ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
>>>               (is_tx && (ops->vidioc_try_fmt_vid_out ||
>>>                      ops->vidioc_try_fmt_vid_out_mplane ||
>>> -                   ops->vidioc_try_fmt_vid_out_overlay)))
>>> +                   ops->vidioc_try_ext_pix_fmt_vid_out))) {
>>>                set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>>> +             set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
>>> +        }
>>>           SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
>>>           SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
>>>           SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
>>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c 
>>> b/drivers/media/v4l2-core/v4l2-ioctl.c
>>> index 848286a284f6..a9c07c0a73ec 100644
>>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>>> @@ -18,6 +18,8 @@
>>>   #include <linux/videodev2.h>
>>> +#include <drm/drm_fourcc.h>
>>> +
>>>   #include <media/v4l2-common.h>
>>>   #include <media/v4l2-ioctl.h>
>>>   #include <media/v4l2-ctrls.h>
>>> @@ -38,6 +40,11 @@
>>>   #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), 
>>> (vfd)->valid_ioctls)
>>> +#define V4L2_IS_CAP_MULTIPLANAR(vdev)    (vdev->device_caps & \
>>> +                     (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
>>> +                     V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
>>> +                     V4L2_CAP_VIDEO_M2M_MPLANE))
>>> +
>>>   struct std_descr {
>>>       v4l2_std_id std;
>>>       const char *descr;
>>> @@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg, 
>>> bool write_only)
>>>       }
>>>   }
>>> +static void v4l_print_ext_pix_format(const void *arg, bool write_only)
>>> +{
>>> +    const struct v4l2_ext_pix_format *ef = arg;
>>> +    unsigned int i;
>>> +
>>> +    pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier 
>>> %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u, 
>>> xfer_func=%u\n",
>>> +        prt_names(ef->type, v4l2_type_names),
>>> +        ef->width, ef->height,
>>> +        (ef->pixelformat & 0xff),
>>> +        (ef->pixelformat >>  8) & 0xff,
>>> +        (ef->pixelformat >> 16) & 0xff,
>>> +        (ef->pixelformat >> 24) & 0xff,
>>> +        ef->modifier, prt_names(ef->field, v4l2_field_names),
>>> +        ef->colorspace, ef->ycbcr_enc,
>>> +        ef->quantization, ef->xfer_func);
>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; 
>>> i++)
>>> +        pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
>>> +             i, ef->plane_fmt[i].bytesperline,
>>> +             ef->plane_fmt[i].sizeimage);
>>> +}
>>> +
>>>   static void v4l_print_framebuffer(const void *arg, bool write_only)
>>>   {
>>>       const struct v4l2_framebuffer *p = arg;
>>> @@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum 
>>> v4l2_buf_type type)
>>>       switch (type) {
>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>           if ((is_vid || is_tch) && is_rx &&
>>> -            (ops->vidioc_g_fmt_vid_cap || 
>>> ops->vidioc_g_fmt_vid_cap_mplane))
>>> +            (ops->vidioc_g_fmt_vid_cap ||
>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap ||
>>> +             ops->vidioc_g_fmt_vid_cap_mplane))
>>>               return 0;
>>>           break;
>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>> -        if ((is_vid || is_tch) && is_rx && 
>>> ops->vidioc_g_fmt_vid_cap_mplane)
>>> +        if ((is_vid || is_tch) && is_rx &&
>>> +            (ops->vidioc_g_fmt_vid_cap_mplane ||
>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap))
>>>               return 0;
>>>           break;
>>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>> @@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum 
>>> v4l2_buf_type type)
>>>           break;
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>           if (is_vid && is_tx &&
>>> -            (ops->vidioc_g_fmt_vid_out || 
>>> ops->vidioc_g_fmt_vid_out_mplane))
>>> +            (ops->vidioc_g_fmt_vid_out ||
>>> +             ops->vidioc_g_ext_pix_fmt_vid_out ||
>>> +             ops->vidioc_g_fmt_vid_out_mplane))
>>>               return 0;
>>>           break;
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>> -        if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
>>> +        if (is_vid && is_tx &&
>>> +            (ops->vidioc_g_ext_pix_fmt_vid_out ||
>>> +             ops->vidioc_g_fmt_vid_out_mplane))
>>>               return 0;
>>>           break;
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>> @@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct 
>>> v4l2_format *fmt)
>>>              sizeof(fmt->fmt.pix) - offset);
>>>   }
>>> +static void
>>> +v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
>>> +                  struct v4l2_pix_format *pix)
>>> +{
>>> +    unsigned int i;
>>> +
>>> +    pix->width = ef->width;
>>> +    pix->height = ef->height;
>>> +    pix->field = ef->field;
>>> +    pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>>> +    pix->colorspace = ef->colorspace;
>>> +    pix->ycbcr_enc = ef->ycbcr_enc;
>>> +    pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>> +    pix->quantization = ef->quantization;
>>> +    pix->pixelformat = ef->pixelformat;
>>> +    pix->bytesperline = ef->plane_fmt[0].bytesperline;
>>> +    pix->sizeimage = ef->plane_fmt[0].sizeimage;
>>> +    for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; 
>>> i++)
>>> +        pix->sizeimage += ef->plane_fmt[i].sizeimage;
>>> +}
>>> +
>>> +static void
>>> +v4l2_ext_pix_format_to_pix_mp_format(const struct 
>>> v4l2_ext_pix_format *ef,
>>> +                     struct v4l2_pix_format_mplane *pix_mp)
>>> +{
>>> +    const struct v4l2_format_info *info =
>>> +                    v4l2_format_info(ef->pixelformat);
>>> +    unsigned int i;
>>> +
>>> +    pix_mp->width = ef->width;
>>> +    pix_mp->height = ef->height;
>>> +    pix_mp->field = ef->field;
>>> +    pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>>> +    pix_mp->colorspace = ef->colorspace;
>>> +    pix_mp->ycbcr_enc = ef->ycbcr_enc;
>>> +    pix_mp->quantization = ef->quantization;
>>> +    pix_mp->pixelformat = ef->pixelformat;
>>> +
>>> +    /* This is true when converting to non-M-variant */
>>> +    if (info && info->mem_planes == 1) {
>>> +        pix_mp->plane_fmt[0] = ef->plane_fmt[0];
>>> +        pix_mp->num_planes = 1;
>>> +        for (i = 1; i < VIDEO_MAX_PLANES && 
>>> ef->plane_fmt[i].sizeimage; i++)
>>> +            pix_mp->plane_fmt[0].sizeimage += 
>>> ef->plane_fmt[i].sizeimage;
>>> +
>>> +        return;
>>> +    }
>>> +
>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; 
>>> i++)
>>> +        pix_mp->plane_fmt[i] = ef->plane_fmt[i];
>>> +    pix_mp->num_planes = i;
>>> +}
>>> +
>>> +/*
>>> + * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to 
>>> v4l2_format
>>> + *
>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
>>> + * @f: A pointer to struct v4l2_format to be filled.
>>> + * @is_mplane: Bool indicating if multiplanar API should be used in @f.
>>> + *
>>> + * If pixelformat should be converted to M-variant, change 
>>> ef->pixelformat
>>> + * to the M-variant before calling this function.
>>> + */
>>> +static void v4l2_ext_pix_format_to_format(const struct 
>>> v4l2_ext_pix_format *ef,
>>> +                      struct v4l2_format *f, bool is_mplane)
>>> +{
>>> +    memset(f, 0, sizeof(*f));
>>> +
>>> +    if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
>>> +        ef->modifier != DRM_FORMAT_MOD_INVALID)
>>> +        pr_warn("Modifiers are not supported in v4l2_format, 
>>> ignoring %llx\n",
>>> +            ef->modifier);
>>> +
>>> +    if (!is_mplane) {
>>> +        f->type = ef->type;
>>> +        v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
>>> +        return;
>>> +    }
>>> +
>>> +    if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>> +        f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>>> +    else
>>> +        f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
>>> +
>>> +    v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
>>> +}
>>> +
>>> +static void
>>> +v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
>>> +                  struct v4l2_ext_pix_format *ef)
>>> +{
>>> +    const struct v4l2_format_info *info =
>>> +                    v4l2_format_info(pix->pixelformat);
>>> +    unsigned int i;
>>> +
>>> +    ef->width = pix->width;
>>> +    ef->height = pix->height;
>>> +    ef->field = pix->field;
>>> +    ef->colorspace = pix->colorspace;
>>> +    ef->ycbcr_enc = pix->ycbcr_enc;
>>> +    ef->quantization = pix->quantization;
>>> +    ef->xfer_func = pix->xfer_func;
>>> +    if (pix->flags)
>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
>>> +
>>> +    /* We assume M-variants won't be used in this function */
>>> +    ef->pixelformat = pix->pixelformat;
>>> +
>>> +    ef->plane_fmt[0].bytesperline = pix->bytesperline;
>>> +    ef->plane_fmt[0].sizeimage = pix->sizeimage;
>>> +
>>> +    if (!info)
>>> +        return;
>>> +
>>> +    for (i = 1; i < info->comp_planes; i++) {
>>> +        ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
>>> +        ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
>>> +                         ef->height / info->vdiv;
>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>>> +    }
>>> +}
>>> +
>>> +static void
>>> +v4l2_pix_mp_format_to_ext_pix_format(const struct 
>>> v4l2_pix_format_mplane *pix_mp,
>>> +                     struct v4l2_ext_pix_format *ef)
>>> +{
>>> +    const struct v4l2_format_info *info =
>>> +                    v4l2_format_info(pix_mp->pixelformat);
>>> +    unsigned int i;
>>> +
>>> +    ef->width = pix_mp->width;
>>> +    ef->height = pix_mp->height;
>>> +    ef->field = pix_mp->field;
>>> +    ef->colorspace = pix_mp->colorspace;
>>> +    ef->ycbcr_enc = pix_mp->ycbcr_enc;
>>> +    ef->quantization = pix_mp->quantization;
>>> +    ef->xfer_func = pix_mp->xfer_func;
>>> +    if (pix_mp->flags)
>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
>>> +
>>> +    if (!info)
>>> +        return;
>>> +
>>> +    ef->pixelformat = info && info->norm ?
>>
>> 'info &&' can be dropped, info is always non-NULL here.
>>
>>> +              info->norm : pix_mp->pixelformat;
>>> +
>>> +    if (info->comp_planes == info->mem_planes) {
>>> +        for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES; 
>>> i++)
>>> +            ef->plane_fmt[i] = pix_mp->plane_fmt[i];
>>> +
>>> +        return;
>>> +    }
>>> +
>>> +    /* case where mem_planes is 1 and comp_planes > 1 */
>>> +    ef->plane_fmt[0] = pix_mp->plane_fmt[0];
>>> +    for (i = 1; i < info->comp_planes; i++) {
>>> +        ef->plane_fmt[i].bytesperline =
>>> +            pix_mp->plane_fmt[0].bytesperline / info->hdiv;
>>> +        ef->plane_fmt[i].sizeimage =
>>> +            ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>>> +    }
>>> +}
>>> +
>>> +/*
>>> + * v4l2_format_to_ext_pix_format - convert to v4l2_format to 
>>> v4l2_ext_pix_format
>>> + *
>>> + * @f: A pointer to struct v4l2_format to be converted.
>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
>>> + *
>>> + * This method normalize the pixelformat to non-M variant.
>>> + */
>>> +static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
>>> +                      struct v4l2_ext_pix_format *ef)
>>> +{
>>> +    memset(ef, 0, sizeof(*ef));
>>> +
>>> +    switch (f->type) {
>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>> +        ef->type = f->type;
>>> +        v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
>>> +        break;
>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>>> +        break;
>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>>> +        break;
>>> +    default:
>>> +        WARN("Converting to Ext Pix Format with wrong buffer type 
>>> %s\n",
>>> +             prt_names(f->type, v4l2_type_names));
>>> +        break;
>>> +    }
>>> +}
>>> +
>>>   static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
>>>                   struct file *file, void *fh, void *arg)
>>>   {
>>> @@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct 
>>> v4l2_pix_format *p)
>>>       p->xfer_func = 0;
>>>   }
>>> +static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
>>> +                 struct file *file, void *fh,
>>> +                 struct v4l2_format *f,
>>> +                 unsigned int ioctl)
>>> +{
>>> +    bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
>>> +    struct video_device *vdev = video_devdata(file);
>>> +    struct v4l2_ext_pix_format ef = {0};
>>> +    u32 original_pixfmt = 0;
>>> +    u32 cap_mask;
>>> +    int ret;
>>> +
>>> +    if (ioctl != VIDIOC_G_FMT) {
>>> +        /*
>>> +         * If CSC attributes are read only, set them to DEFAULT
>>> +         * to avoid changes by the driver.
>>> +         */
>>> +        if (is_multiplanar) {
>>> +            if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>>> +                f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
>>> +                f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>>> +                f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
>>> +                f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>>> +            }
>>> +            /* Unset the flag to avoid warning in the convertion */
>>> +            f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>>> +
>>> +            /* Save pixelformat in case M-variant is being used */
>>> +            original_pixfmt = f->fmt.pix_mp.pixelformat;
>>> +        } else {
>>> +            if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>>> +                f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
>>> +                f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>>> +                f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
>>> +                f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>>> +            }
>>> +            /* Unset the flag to avoid warning in the convertion */
>>> +            f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>>> +        }
>>> +        v4l2_format_to_ext_pix_format(f, &ef);
>>> +    }
>>> +
>>> +    switch (f->type) {
>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>> +        cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
>>> +        if (!!(vdev->device_caps & cap_mask) !=
>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
>>> +            return -EINVAL;
>>> +
>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>> +        if (ioctl == VIDIOC_G_FMT)
>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
>>> +        else if (ioctl == VIDIOC_S_FMT)
>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
>>> +        else
>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>>> +                                  &ef);
>>> +        break;
>>> +
>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>> +        cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
>>> +        if (!!(vdev->device_caps & cap_mask) !=
>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>>> +            return -EINVAL;
>>> +
>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>> +        if (ioctl == VIDIOC_G_FMT)
>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
>>> +        else if (ioctl == VIDIOC_S_FMT)
>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
>>> +        else
>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>>> +                                  &ef);
>>> +        break;
>>> +
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    if (original_pixfmt != ef.pixelformat &&
>>> +        v4l2_format_info(original_pixfmt))
>>
>> Could this test be simplified to: 'if (original_pixfmt)'?
>>
>> I.e., if the original pixfmt was saved, then restore it here.
>>
>>> +        ef.pixelformat = original_pixfmt;
>>> +
>>> +    v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
>>> +    return 0;
>>> +}
>>> +
>>>   static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>>                   struct file *file, void *fh, void *arg)
>>>   {
>>> @@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct 
>>> v4l2_ioctl_ops *ops,
>>>       switch (p->type) {
>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_cap))
>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_cap))
>>>               break;
>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>> -        ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
>>> +        ret = ops->vidioc_g_fmt_vid_cap ?
>>> +              ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>> +                        VIDIOC_G_FMT);
>>>           /* just in case the driver zeroed it again */
>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>           if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>               v4l_pix_format_touch(&p->fmt.pix);
>>>           return ret;
>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>> -        return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>> +        if (ops->vidioc_g_fmt_vid_cap_mplane)
>>> +            return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_cap)
>>
>> 'else' can be dropped.
>>
>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>> +                             VIDIOC_G_FMT);
>>> +        break;
>>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>           return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>>>       case V4L2_BUF_TYPE_VBI_CAPTURE:
>>> @@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct 
>>> v4l2_ioctl_ops *ops,
>>>       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>>>           return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_out))
>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_out &&
>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_out))
>>>               break;
>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>> -        ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
>>> +        ret = ops->vidioc_g_fmt_vid_out ?
>>> +              ops->vidioc_g_fmt_vid_out(file, fh, arg) :
>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
>>>           /* just in case the driver zeroed it again */
>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>           return ret;
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>> -        return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>> +        if (ops->vidioc_g_fmt_vid_out_mplane)
>>> +            return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_out)
>>
>> Ditto.
>>
>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>> +                             VIDIOC_G_FMT);
>>> +        break;
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>           return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>>>       case V4L2_BUF_TYPE_VBI_OUTPUT:
>>> @@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct 
>>> v4l2_ioctl_ops *ops,
>>>       return -EINVAL;
>>>   }
>>> +static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>> +                 struct file *file, void *fh, void *arg)
>>> +{
>>> +    struct v4l2_ext_pix_format *ef = arg;
>>> +    struct v4l2_format f = {
>>> +        .type = ef->type,
>>> +    };
>>> +    int ret = check_fmt(file, ef->type);
>>> +
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    memset(ef, 0, sizeof(*ef));
>>> +    ef->type = f.type;
>>> +
>>> +    switch (f.type) {
>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_cap)
>>> +            return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
>>> +        break;
>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_out)
>>> +            return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
>>> +        break;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    ret = v4l_g_fmt(ops, file, fh, &f);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>> +    return 0;
>>> +}
>>> +
>>>   static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>>                   struct file *file, void *fh, void *arg)
>>>   {
>>> @@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct 
>>> v4l2_ioctl_ops *ops,
>>>       switch (p->type) {
>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap))
>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>>               break;
>>>           CLEAR_AFTER_FIELD(p, fmt.pix);
>>> -        ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
>>> +        ret = ops->vidioc_s_fmt_vid_cap ?
>>> +              ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>           /* just in case the driver zeroed it again */
>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>           if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>               v4l_pix_format_touch(&p->fmt.pix);
>>>           return ret;
>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>>               break;
>>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>                         bytesperline);
>>> -        return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
>>> +        return ops->vidioc_s_fmt_vid_cap_mplane ?
>>> +               ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,  
>>> VIDIOC_S_FMT);
>>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>           if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>>>               break;
>>> @@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct 
>>> v4l2_ioctl_ops *ops,
>>>           CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>>           return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out))
>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out &&
>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
>>>               break;
>>>           CLEAR_AFTER_FIELD(p, fmt.pix);
>>> -        ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
>>> +        ret = ops->vidioc_s_fmt_vid_out ?
>>> +              ops->vidioc_s_fmt_vid_out(file, fh, arg) :
>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>           /* just in case the driver zeroed it again */
>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>           return ret;
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
>>>               break;
>>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>                         bytesperline);
>>> -        return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
>>> +        return ops->vidioc_s_fmt_vid_out_mplane ?
>>> +               ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>           if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>>>               break;
>>> @@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct 
>>> v4l2_ioctl_ops *ops,
>>>       return -EINVAL;
>>>   }
>>> +static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>> +                 struct file *file, void *fh, void *arg)
>>> +{
>>> +    struct video_device *vfd = video_devdata(file);
>>> +    struct v4l2_ext_pix_format *ef = arg;
>>> +    struct v4l2_format f;
>>> +    int ret = check_fmt(file, ef->type);
>>> +
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
>>> +
>>> +    switch (ef->type) {
>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_cap)
>>> +            return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
>>> +        break;
>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_out)
>>> +            return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
>>> +        break;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    v4l2_ext_pix_format_to_format(ef, &f, 
>>> V4L2_IS_CAP_MULTIPLANAR(vfd));
>>> +
>>> +    ret = v4l_s_fmt(ops, file, fh, &f);
>>> +    if (ret)
>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
>>
>> See my comments on this at the top.
>>
>>> +        return ret;
>>> +
>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>> +    return 0;
>>> +}
>>> +
>>>   static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>>                   struct file *file, void *fh, void *arg)
>>>   {
>>> @@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct 
>>> v4l2_ioctl_ops *ops,
>>>       switch (p->type) {
>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap))
>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>               break;
>>>           CLEAR_AFTER_FIELD(p, fmt.pix);
>>> -        ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
>>> +        ret = ops->vidioc_try_fmt_vid_cap ?
>>> +              ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, 
>>> VIDIOC_TRY_FMT);
>>>           /* just in case the driver zeroed it again */
>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>           if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>               v4l_pix_format_touch(&p->fmt.pix);
>>>           return ret;
>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>               break;
>>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>                         bytesperline);
>>> -        return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
>>> +        return ops->vidioc_try_fmt_vid_cap_mplane ?
>>> +               ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>> +                         VIDIOC_TRY_FMT);
>>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>           if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>>>               break;
>>> @@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct 
>>> v4l2_ioctl_ops *ops,
>>>           CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>>           return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out))
>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out &&
>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>               break;
>>>           CLEAR_AFTER_FIELD(p, fmt.pix);
>>> -        ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
>>> +        ret = ops->vidioc_try_fmt_vid_out ?
>>> +              ops->vidioc_try_fmt_vid_out(file, fh, arg) :
>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, 
>>> VIDIOC_TRY_FMT);
>>>           /* just in case the driver zeroed it again */
>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>           return ret;
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>               break;
>>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>                         bytesperline);
>>> -        return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
>>> +        return ops->vidioc_try_fmt_vid_out_mplane ?
>>> +               ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>> +                         VIDIOC_TRY_FMT);
>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>           if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>>>               break;
>>> @@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct 
>>> v4l2_ioctl_ops *ops,
>>>       return -EINVAL;
>>>   }
>>> +static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>> +                   struct file *file, void *fh, void *arg)
>>> +{
>>> +    struct video_device *vfd = video_devdata(file);
>>> +    struct v4l2_ext_pix_format *ef = arg;
>>> +    struct v4l2_format f;
>>> +    int ret = check_fmt(file, ef->type);
>>> +
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
>>> +
>>> +    switch (ef->type) {
>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_cap)
>>> +            return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>>> +                                   ef);
>>> +        break;
>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_out)
>>> +            return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>>> +                                   ef);
>>> +        break;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    v4l2_ext_pix_format_to_format(ef, &f, 
>>> V4L2_IS_CAP_MULTIPLANAR(vfd));
>>> +
>>> +    ret = v4l_try_fmt(ops, file, fh, &f);
>>> +    if (ret)
>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
>>> +        return ret;
>>> +
>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>> +    return 0;
>>> +}
>>> +
>>>   static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
>>>                   struct file *file, void *fh, void *arg)
>>>   {
>>> @@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info 
>>> v4l2_ioctls[] = {
>>>       IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, 
>>> v4l_print_freq_band, 0),
>>>       IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, 
>>> v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
>>>       IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, 
>>> v4l_print_query_ext_ctrl, INFO_FL_CTRL | 
>>> INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
>>> +    IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, 
>>> v4l_print_ext_pix_format, 0),
>>> +    IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, 
>>> v4l_print_ext_pix_format, INFO_FL_PRIO),
>>> +    IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, 
>>> v4l_print_ext_pix_format, 0),
>>>   };
>>>   #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
>>> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
>>> index edb733f21604..c44708dc9355 100644
>>> --- a/include/media/v4l2-ioctl.h
>>> +++ b/include/media/v4l2-ioctl.h
>>> @@ -48,11 +48,17 @@ struct v4l2_fh;
>>>    * @vidioc_g_fmt_vid_cap: pointer to the function that implements
>>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>>    *    in single plane mode
>>> + * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that 
>>> implements
>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic 
>>> for video
>>> + *    capture
>>>    * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
>>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>    * @vidioc_g_fmt_vid_out: pointer to the function that implements
>>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>    *    in single plane mode
>>> + * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that 
>>> implements
>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic 
>>> for video
>>> + *    out
>>>    * @vidioc_g_fmt_vid_out_overlay: pointer to the function that 
>>> implements
>>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video 
>>> overlay output
>>>    * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
>>> @@ -82,11 +88,16 @@ struct v4l2_fh;
>>>    * @vidioc_s_fmt_vid_cap: pointer to the function that implements
>>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>>    *    in single plane mode
>>> + * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that 
>>> implements
>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic 
>>> for video
>>> + *    capture
>>>    * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
>>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>    * @vidioc_s_fmt_vid_out: pointer to the function that implements
>>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>    *    in single plane mode
>>> + * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that 
>>> implements
>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for 
>>> video out
>>>    * @vidioc_s_fmt_vid_out_overlay: pointer to the function that 
>>> implements
>>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video 
>>> overlay output
>>>    * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
>>> @@ -116,11 +127,16 @@ struct v4l2_fh;
>>>    * @vidioc_try_fmt_vid_cap: pointer to the function that implements
>>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video 
>>> capture
>>>    *    in single plane mode
>>> + * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that 
>>> implements
>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl 
>>> logic for
>>> +    video capture
>>>    * @vidioc_try_fmt_vid_overlay: pointer to the function that 
>>> implements
>>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video 
>>> overlay
>>>    * @vidioc_try_fmt_vid_out: pointer to the function that implements
>>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>    *    in single plane mode
>>> + * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that 
>>> implements
>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for 
>>> video out
>>>    * @vidioc_try_fmt_vid_out_overlay: pointer to the function that 
>>> implements
>>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video 
>>> overlay
>>>    *    output
>>> @@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
>>>       /* VIDIOC_G_FMT handlers */
>>>       int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
>>>                       struct v4l2_format *f);
>>> +    int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>> +                        struct v4l2_ext_pix_format *ef);
>>>       int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
>>>                       struct v4l2_format *f);
>>>       int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
>>>                       struct v4l2_format *f);
>>> +    int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>> +                        struct v4l2_ext_pix_format *ef);
>>>       int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>                           struct v4l2_format *f);
>>>       int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
>>> @@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
>>>       /* VIDIOC_S_FMT handlers */
>>>       int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
>>>                       struct v4l2_format *f);
>>> +    int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>> +                        struct v4l2_ext_pix_format *ef);
>>>       int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
>>>                       struct v4l2_format *f);
>>>       int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
>>>                       struct v4l2_format *f);
>>> +    int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>> +                        struct v4l2_ext_pix_format *ef);
>>>       int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>                           struct v4l2_format *f);
>>>       int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
>>> @@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
>>>       /* VIDIOC_TRY_FMT handlers */
>>>       int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
>>>                         struct v4l2_format *f);
>>> +    int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>> +                          struct v4l2_ext_pix_format *ef);
>>>       int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
>>>                         struct v4l2_format *f);
>>>       int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
>>>                         struct v4l2_format *f);
>>> +    int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>> +                          struct v4l2_ext_pix_format *ef);
>>>       int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>                            struct v4l2_format *f);
>>>       int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
>>> diff --git a/include/uapi/linux/videodev2.h 
>>> b/include/uapi/linux/videodev2.h
>>> index d9b7c9177605..a2d850513708 100644
>>> --- a/include/uapi/linux/videodev2.h
>>> +++ b/include/uapi/linux/videodev2.h
>>> @@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
>>>       __u8                reserved[7];
>>>   } __attribute__ ((packed));
>>> +/**
>>> + * struct v4l2_ext_pix_format - extended single/multiplanar format 
>>> definition
>>> + * @type:        type of the data stream; 
>>> V4L2_BUF_TYPE_VIDEO_CAPTURE or
>>> + *            V4L2_BUF_TYPE_VIDEO_OUTPUT
>>> + * @width:        image width in pixels
>>> + * @height:        image height in pixels
>>> + * @field:        enum v4l2_field; field order (for interlaced video)
>>> + * @plane_fmt:        per-plane information
>>> + * @pixelformat:    little endian four character code (fourcc)
>>> + * @modifier:        modifier applied to the format (used for tiled 
>>> formats
>>> + *            and other kind of HW-specific formats, like compressed
>>> + *            formats) as defined in drm_fourcc.h
>>> + * @colorspace:        enum v4l2_colorspace; supplemental to 
>>> pixelformat
>>> + * @xfer_func:        enum v4l2_xfer_func, colorspace transfer function
>>> + * @ycbcr_enc:        enum v4l2_ycbcr_encoding, Y'CbCr encoding
>>> + * @hsv_enc:        enum v4l2_hsv_encoding, HSV encoding
>>> + * @quantization:    enum v4l2_quantization, colorspace quantization
>>> + * @reserved:        extra space reserved for future fields, must be 
>>> set to 0
>>> + */
>>> +struct v4l2_ext_pix_format {
>>> +    __u32 type;
>>> +    __u32 width;
>>> +    __u32 height;
>>> +    __u32 field;
>>> +    struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
>>> +    __u32 pixelformat;
>>> +    __u64 modifier;
>>> +    __u32 colorspace;
>>> +    __u32 xfer_func;
>>> +    union {
>>> +        __u32 ycbcr_enc;
>>> +        __u32 hsv_enc;
>>> +    };
>>> +    __u32 quantization;
>>> +    __u32 reserved[9];
>>> +};
>>> +
>>>   /**
>>>    * struct v4l2_sdr_format - SDR format definition
>>>    * @pixelformat:    little endian four character code (fourcc)
>>> @@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
>>>   #define VIDIOC_QUERY_EXT_CTRL    _IOWR('V', 103, struct 
>>> v4l2_query_ext_ctrl)
>>> +#define VIDIOC_G_EXT_PIX_FMT    _IOWR('V', 104, struct 
>>> v4l2_ext_pix_format)
>>> +#define VIDIOC_S_EXT_PIX_FMT    _IOWR('V', 105, struct 
>>> v4l2_ext_pix_format)
>>> +#define VIDIOC_TRY_EXT_PIX_FMT    _IOWR('V', 106, struct 
>>> v4l2_ext_pix_format)
>>> +
>>>   /* Reminder: when adding new ioctls please add support for them to
>>>      drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
>>>
>>
>> Regards,
>>
>>     Hans
>>
> 

-- 
Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-05 15:19       ` Hsia-Jun Li
@ 2022-11-06 19:24         ` Laurent Pinchart
  2022-11-07  1:54           ` Hsia-Jun Li
  2022-11-06 22:11         ` Dmitry Osipenko
                           ` (2 subsequent siblings)
  3 siblings, 1 reply; 48+ messages in thread
From: Laurent Pinchart @ 2022-11-06 19:24 UTC (permalink / raw)
  To: Hsia-Jun Li
  Cc: Helen Koike, mchehab, hans.verkuil, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga,
	Hans Verkuil, linux-media

Hi Hsia-Jun,

On Sat, Nov 05, 2022 at 11:19:10PM +0800, Hsia-Jun Li wrote:
> Hello Helen
> 
> I didn't see any updates from V6 and V7-WIP in your repo. That is what I 
> need to for our complex tile formats in our platform.
> 
> Any future plane here?
> 
> Besides I have some ideas on these patches.
> 
> On 2/24/21 23:12, Helen Koike wrote:
> > Hi Hans,
> > 
> > Thank you for your comment, please see my reply below.
> > 
> > On 2/23/21 9:35 AM, Hans Verkuil wrote:
> >> Hi Helen,
> >>
> >> On 14/01/2021 19:07, Helen Koike wrote:
> >>> This is part of the multiplanar and singleplanar unification process.
> >>> v4l2_ext_pix_format is supposed to work for both cases.
> >>>
> >>> We also add the concept of modifiers already employed in DRM to expose
> >>> HW-specific formats (like tiled or compressed formats) and allow
> >>> exchanging this information with the DRM subsystem in a consistent way.
> >>>
> >>> Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
> >>> v4l2_ext_format, other types will be rejected if you use the
> >>> {G,S,TRY}_EXT_PIX_FMT ioctls.
> >>>
> >>> New hooks have been added to v4l2_ioctl_ops to support those new ioctls
> >>> in drivers, but, in the meantime, the core takes care of converting
> >>> {S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers 
> >>> can
> >>> still work if the userspace app/lib uses the new ioctls.
> >>>
> >>> The conversion is also done the other around to allow userspace
> >>> apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
> >>> _ext_ hooks.
> >>
> >> I have some small comments below, but also one high level comment:
> >>
> >> Regarding M variants of pixelformats: this patch 'normalizes' them to
> >> regular pixelformats in the extended API. This makes life complicated,
> >> and I wonder if this is the right approach.
> >>
> >> Currently there are two reasons for a driver to support e.g. NV12M:
> >> either luma and chroma need to be in two memory banks, or the luma
> >> and chroma planes cannot be contiguous due to alignment requirements.
> >>
> >> The first requirement is still valid for drivers that support the 
> >> extended API.
> >> The second requirement is no longer a reason to support NV12M. But I
> >> don't think we should just drop NV12M support if it was already supported
> >> before the conversion to this extended API. Since NV12M allocates two 
> >> buffers
> >> instead of one, it is still different from a regular NV12.
> > 
> > I don't see what would be the difference when using NV12 and NV12M in 
> > Ext API.
> > NV12 could be used for both requirements. It would even allow the second
> > requirement with a single memory buffer.
> > 
> Although I don't have problem here to support both single and multiple 
> planes in our hardware. But using the single plane format here is not 
> convience for us.
> 
> The tile format in our platform is little complex, you can't calculate 
> the stride and sizeimage easily with just the modifier, width and 
> height. Then we don't have a good place to record the offset here.

What else is needed to compute those values ? Can they be computed by
the kernel driver, or do they have computed by userspace ?

> Besides, the luma and chroma planes would always need their own 
> compression meta data planes. If we use NV12 here, that would make a 
> very strange pixel format, one plane for the pixel datas and two planes 
> for the meta data.
> 
> >> I would prefer that such drivers support both NV12 and NV12M, so no
> >> automatic conversion.
> >>
> >> A related question is how to handle pixelformat enumeration: with the
> >> extended API an NV12 format might work, but not with the old API (e.g.
> >> due to memory alignment requirements). I wonder if a 
> >> VIDIOC_ENUM_EXT_PIX_FMT
> >> isn't needed.
> > 
> > We need VIDIOC_ENUM_EXT_PIX_FMT for modifiers, but we can add it later.
> > If the driver reports NV12M, userspace can use it and the framework
> > normilizes it.
> > 
> >>
> >> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while 
> >> VIDIOC_ENUM_FMT
> >> would just report NV12M.
> > 
> > If NV12 and NV12M are equivalent in Ext API, I don't see why we would
> > report both (unless I'm missing something, which is probably the case).
> > 
> > The idea was to deprecate the M-variants one day.
> 
> I was thinking the way in DRM API is better, always assuming it would 
> always in a multiple planes. The only problem is we don't have a way to 
> let the allocator that allocate contiguous memory for planes when we 
> need to do that.
> 
> >>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> >>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> >>> ---
> >>>
> >>> Changes in v6:
> >>>   The main change here was fixing the conversion, so planes reflects 
> >>> color planes,
> >>>   and to implement this properly I made major refactors compared to 
> >>> the previous
> >>>   version.
> >>> - struct v4l2_plane_ext_pix_format removed, using struct 
> >>> v4l2_plane_pix_format instead (Tomasz)
> >>> - refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
> >>> - reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
> >>> - do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
> >>> - refactor conversion functions, so planes are color planes (Tomasz)
> >>> - Don't explicitly check for e->modifier != 0 in 
> >>> v4l2_ext_pix_format_to_format() (Tomasz)
> >>> - Use "ef" for extended formats in the framework for consistency 
> >>> (Tomasz)
> >>> - Handle xfer_func field in conversions (Tomasz)
> >>> - Zero reserved fields in v4l_s_ext_pix_fmt() and 
> >>> v4l_try_ext_pix_fmt() (Tomasz)
> >>> - Refactor format functions to use v4l_fmt_ioctl_via_ext()
> >>> - Several fixes/refactoring/changes
> >>> - Remove EXT API for touch devices
> >>>
> >>> Changes in v5:
> >>> - change sizes and reorder fields to avoid holes in the struct and make
> >>>    it the same for 32 and 64 bits
> >>> - removed __attribute__ ((packed)) from uapi structs
> >>> - Fix doc warning from make htmldocs
> >>> - Updated commit message with EXT_PIX prefix for the ioctls.
> >>>
> >>> Changes in v4:
> >>> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
> >>> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
> >>> - Add reserved fields
> >>> - Removed num_planes from struct v4l2_ext_pix_format
> >>> - Removed flag field from struct v4l2_ext_pix_format, since the only
> >>>    defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
> >>>    where we can use modifiers, or add it back later through the reserved
> >>>    bits.
> >>> - In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
> >>>    != MOD_INVALID
> >>> - Fix type assignment in v4l_g_fmt_ext_pix()
> >>> - Rebased on top of media/master (post 5.8-rc1)
> >>>
> >>> Changes in v3:
> >>> - Rebased on top of media/master (post 5.4-rc1)
> >>>
> >>> Changes in v2:
> >>> - Move the modifier in v4l2_ext_format (was formerly placed in
> >>>    v4l2_ext_plane)
> >>> - Fix a few bugs in the converters and add a strict parameter to
> >>>    allow conversion of uninitialized/mis-initialized objects
> >>> ---
> >>>   drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
> >>>   drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
> >>>   include/media/v4l2-ioctl.h           |  28 ++
> >>>   include/uapi/linux/videodev2.h       |  41 ++
> >>>   4 files changed, 602 insertions(+), 32 deletions(-)
> >>>
> >>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c 
> >>> b/drivers/media/v4l2-core/v4l2-dev.c
> >>> index f9cff033d0dc..5add58cb6d45 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-dev.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> >>> @@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct 
> >>> video_device *vdev)
> >>>                      ops->vidioc_enum_fmt_vid_overlay)) ||
> >>>               (is_tx && ops->vidioc_enum_fmt_vid_out))
> >>>               set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
> >>> +        if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
> >>> +            (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
> >>> +             set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
> >>>           if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
> >>>                      ops->vidioc_g_fmt_vid_cap_mplane ||
> >>> -                   ops->vidioc_g_fmt_vid_overlay)) ||
> >>> +                   ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
> >>>               (is_tx && (ops->vidioc_g_fmt_vid_out ||
> >>>                      ops->vidioc_g_fmt_vid_out_mplane ||
> >>> -                   ops->vidioc_g_fmt_vid_out_overlay)))
> >>> +                   ops->vidioc_g_ext_pix_fmt_vid_out))) {
> >>>                set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
> >>> +             set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
> >>> +        }
> >>> +        if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
> >>> +            (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
> >>> +             set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
> >>>           if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
> >>>                      ops->vidioc_s_fmt_vid_cap_mplane ||
> >>> -                   ops->vidioc_s_fmt_vid_overlay)) ||
> >>> +                   ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
> >>>               (is_tx && (ops->vidioc_s_fmt_vid_out ||
> >>>                      ops->vidioc_s_fmt_vid_out_mplane ||
> >>> -                   ops->vidioc_s_fmt_vid_out_overlay)))
> >>> +                   ops->vidioc_s_ext_pix_fmt_vid_out))) {
> >>>                set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
> >>> +             set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
> >>> +        }
> >>> +        if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
> >>> +            (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
> >>> +             set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
> >>>           if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
> >>>                      ops->vidioc_try_fmt_vid_cap_mplane ||
> >>> -                   ops->vidioc_try_fmt_vid_overlay)) ||
> >>> +                   ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
> >>>               (is_tx && (ops->vidioc_try_fmt_vid_out ||
> >>>                      ops->vidioc_try_fmt_vid_out_mplane ||
> >>> -                   ops->vidioc_try_fmt_vid_out_overlay)))
> >>> +                   ops->vidioc_try_ext_pix_fmt_vid_out))) {
> >>>                set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
> >>> +             set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
> >>> +        }
> >>>           SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
> >>>           SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
> >>>           SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
> >>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c 
> >>> b/drivers/media/v4l2-core/v4l2-ioctl.c
> >>> index 848286a284f6..a9c07c0a73ec 100644
> >>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> >>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> >>> @@ -18,6 +18,8 @@
> >>>   #include <linux/videodev2.h>
> >>> +#include <drm/drm_fourcc.h>
> >>> +
> >>>   #include <media/v4l2-common.h>
> >>>   #include <media/v4l2-ioctl.h>
> >>>   #include <media/v4l2-ctrls.h>
> >>> @@ -38,6 +40,11 @@
> >>>   #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), 
> >>> (vfd)->valid_ioctls)
> >>> +#define V4L2_IS_CAP_MULTIPLANAR(vdev)    (vdev->device_caps & \
> >>> +                     (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
> >>> +                     V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
> >>> +                     V4L2_CAP_VIDEO_M2M_MPLANE))
> >>> +
> >>>   struct std_descr {
> >>>       v4l2_std_id std;
> >>>       const char *descr;
> >>> @@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg, 
> >>> bool write_only)
> >>>       }
> >>>   }
> >>> +static void v4l_print_ext_pix_format(const void *arg, bool write_only)
> >>> +{
> >>> +    const struct v4l2_ext_pix_format *ef = arg;
> >>> +    unsigned int i;
> >>> +
> >>> +    pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier 
> >>> %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u, 
> >>> xfer_func=%u\n",
> >>> +        prt_names(ef->type, v4l2_type_names),
> >>> +        ef->width, ef->height,
> >>> +        (ef->pixelformat & 0xff),
> >>> +        (ef->pixelformat >>  8) & 0xff,
> >>> +        (ef->pixelformat >> 16) & 0xff,
> >>> +        (ef->pixelformat >> 24) & 0xff,
> >>> +        ef->modifier, prt_names(ef->field, v4l2_field_names),
> >>> +        ef->colorspace, ef->ycbcr_enc,
> >>> +        ef->quantization, ef->xfer_func);
> >>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; 
> >>> i++)
> >>> +        pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
> >>> +             i, ef->plane_fmt[i].bytesperline,
> >>> +             ef->plane_fmt[i].sizeimage);
> >>> +}
> >>> +
> >>>   static void v4l_print_framebuffer(const void *arg, bool write_only)
> >>>   {
> >>>       const struct v4l2_framebuffer *p = arg;
> >>> @@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum 
> >>> v4l2_buf_type type)
> >>>       switch (type) {
> >>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>           if ((is_vid || is_tch) && is_rx &&
> >>> -            (ops->vidioc_g_fmt_vid_cap || 
> >>> ops->vidioc_g_fmt_vid_cap_mplane))
> >>> +            (ops->vidioc_g_fmt_vid_cap ||
> >>> +             ops->vidioc_g_ext_pix_fmt_vid_cap ||
> >>> +             ops->vidioc_g_fmt_vid_cap_mplane))
> >>>               return 0;
> >>>           break;
> >>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>> -        if ((is_vid || is_tch) && is_rx && 
> >>> ops->vidioc_g_fmt_vid_cap_mplane)
> >>> +        if ((is_vid || is_tch) && is_rx &&
> >>> +            (ops->vidioc_g_fmt_vid_cap_mplane ||
> >>> +             ops->vidioc_g_ext_pix_fmt_vid_cap))
> >>>               return 0;
> >>>           break;
> >>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >>> @@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum 
> >>> v4l2_buf_type type)
> >>>           break;
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>           if (is_vid && is_tx &&
> >>> -            (ops->vidioc_g_fmt_vid_out || 
> >>> ops->vidioc_g_fmt_vid_out_mplane))
> >>> +            (ops->vidioc_g_fmt_vid_out ||
> >>> +             ops->vidioc_g_ext_pix_fmt_vid_out ||
> >>> +             ops->vidioc_g_fmt_vid_out_mplane))
> >>>               return 0;
> >>>           break;
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>> -        if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
> >>> +        if (is_vid && is_tx &&
> >>> +            (ops->vidioc_g_ext_pix_fmt_vid_out ||
> >>> +             ops->vidioc_g_fmt_vid_out_mplane))
> >>>               return 0;
> >>>           break;
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >>> @@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct 
> >>> v4l2_format *fmt)
> >>>              sizeof(fmt->fmt.pix) - offset);
> >>>   }
> >>> +static void
> >>> +v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
> >>> +                  struct v4l2_pix_format *pix)
> >>> +{
> >>> +    unsigned int i;
> >>> +
> >>> +    pix->width = ef->width;
> >>> +    pix->height = ef->height;
> >>> +    pix->field = ef->field;
> >>> +    pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
> >>> +    pix->colorspace = ef->colorspace;
> >>> +    pix->ycbcr_enc = ef->ycbcr_enc;
> >>> +    pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>> +    pix->quantization = ef->quantization;
> >>> +    pix->pixelformat = ef->pixelformat;
> >>> +    pix->bytesperline = ef->plane_fmt[0].bytesperline;
> >>> +    pix->sizeimage = ef->plane_fmt[0].sizeimage;
> >>> +    for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; 
> >>> i++)
> >>> +        pix->sizeimage += ef->plane_fmt[i].sizeimage;
> >>> +}
> >>> +
> >>> +static void
> >>> +v4l2_ext_pix_format_to_pix_mp_format(const struct 
> >>> v4l2_ext_pix_format *ef,
> >>> +                     struct v4l2_pix_format_mplane *pix_mp)
> >>> +{
> >>> +    const struct v4l2_format_info *info =
> >>> +                    v4l2_format_info(ef->pixelformat);
> >>> +    unsigned int i;
> >>> +
> >>> +    pix_mp->width = ef->width;
> >>> +    pix_mp->height = ef->height;
> >>> +    pix_mp->field = ef->field;
> >>> +    pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
> >>> +    pix_mp->colorspace = ef->colorspace;
> >>> +    pix_mp->ycbcr_enc = ef->ycbcr_enc;
> >>> +    pix_mp->quantization = ef->quantization;
> >>> +    pix_mp->pixelformat = ef->pixelformat;
> >>> +
> >>> +    /* This is true when converting to non-M-variant */
> >>> +    if (info && info->mem_planes == 1) {
> >>> +        pix_mp->plane_fmt[0] = ef->plane_fmt[0];
> >>> +        pix_mp->num_planes = 1;
> >>> +        for (i = 1; i < VIDEO_MAX_PLANES && 
> >>> ef->plane_fmt[i].sizeimage; i++)
> >>> +            pix_mp->plane_fmt[0].sizeimage += 
> >>> ef->plane_fmt[i].sizeimage;
> >>> +
> >>> +        return;
> >>> +    }
> >>> +
> >>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; 
> >>> i++)
> >>> +        pix_mp->plane_fmt[i] = ef->plane_fmt[i];
> >>> +    pix_mp->num_planes = i;
> >>> +}
> >>> +
> >>> +/*
> >>> + * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to 
> >>> v4l2_format
> >>> + *
> >>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
> >>> + * @f: A pointer to struct v4l2_format to be filled.
> >>> + * @is_mplane: Bool indicating if multiplanar API should be used in @f.
> >>> + *
> >>> + * If pixelformat should be converted to M-variant, change 
> >>> ef->pixelformat
> >>> + * to the M-variant before calling this function.
> >>> + */
> >>> +static void v4l2_ext_pix_format_to_format(const struct 
> >>> v4l2_ext_pix_format *ef,
> >>> +                      struct v4l2_format *f, bool is_mplane)
> >>> +{
> >>> +    memset(f, 0, sizeof(*f));
> >>> +
> >>> +    if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
> >>> +        ef->modifier != DRM_FORMAT_MOD_INVALID)
> >>> +        pr_warn("Modifiers are not supported in v4l2_format, 
> >>> ignoring %llx\n",
> >>> +            ef->modifier);
> >>> +
> >>> +    if (!is_mplane) {
> >>> +        f->type = ef->type;
> >>> +        v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
> >>> +        return;
> >>> +    }
> >>> +
> >>> +    if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> >>> +        f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> >>> +    else
> >>> +        f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> >>> +
> >>> +    v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
> >>> +}
> >>> +
> >>> +static void
> >>> +v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
> >>> +                  struct v4l2_ext_pix_format *ef)
> >>> +{
> >>> +    const struct v4l2_format_info *info =
> >>> +                    v4l2_format_info(pix->pixelformat);
> >>> +    unsigned int i;
> >>> +
> >>> +    ef->width = pix->width;
> >>> +    ef->height = pix->height;
> >>> +    ef->field = pix->field;
> >>> +    ef->colorspace = pix->colorspace;
> >>> +    ef->ycbcr_enc = pix->ycbcr_enc;
> >>> +    ef->quantization = pix->quantization;
> >>> +    ef->xfer_func = pix->xfer_func;
> >>> +    if (pix->flags)
> >>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
> >>> +
> >>> +    /* We assume M-variants won't be used in this function */
> >>> +    ef->pixelformat = pix->pixelformat;
> >>> +
> >>> +    ef->plane_fmt[0].bytesperline = pix->bytesperline;
> >>> +    ef->plane_fmt[0].sizeimage = pix->sizeimage;
> >>> +
> >>> +    if (!info)
> >>> +        return;
> >>> +
> >>> +    for (i = 1; i < info->comp_planes; i++) {
> >>> +        ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
> >>> +        ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
> >>> +                         ef->height / info->vdiv;
> >>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
> >>> +    }
> >>> +}
> >>> +
> >>> +static void
> >>> +v4l2_pix_mp_format_to_ext_pix_format(const struct 
> >>> v4l2_pix_format_mplane *pix_mp,
> >>> +                     struct v4l2_ext_pix_format *ef)
> >>> +{
> >>> +    const struct v4l2_format_info *info =
> >>> +                    v4l2_format_info(pix_mp->pixelformat);
> >>> +    unsigned int i;
> >>> +
> >>> +    ef->width = pix_mp->width;
> >>> +    ef->height = pix_mp->height;
> >>> +    ef->field = pix_mp->field;
> >>> +    ef->colorspace = pix_mp->colorspace;
> >>> +    ef->ycbcr_enc = pix_mp->ycbcr_enc;
> >>> +    ef->quantization = pix_mp->quantization;
> >>> +    ef->xfer_func = pix_mp->xfer_func;
> >>> +    if (pix_mp->flags)
> >>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
> >>> +
> >>> +    if (!info)
> >>> +        return;
> >>> +
> >>> +    ef->pixelformat = info && info->norm ?
> >>
> >> 'info &&' can be dropped, info is always non-NULL here.
> >>
> >>> +              info->norm : pix_mp->pixelformat;
> >>> +
> >>> +    if (info->comp_planes == info->mem_planes) {
> >>> +        for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES; 
> >>> i++)
> >>> +            ef->plane_fmt[i] = pix_mp->plane_fmt[i];
> >>> +
> >>> +        return;
> >>> +    }
> >>> +
> >>> +    /* case where mem_planes is 1 and comp_planes > 1 */
> >>> +    ef->plane_fmt[0] = pix_mp->plane_fmt[0];
> >>> +    for (i = 1; i < info->comp_planes; i++) {
> >>> +        ef->plane_fmt[i].bytesperline =
> >>> +            pix_mp->plane_fmt[0].bytesperline / info->hdiv;
> >>> +        ef->plane_fmt[i].sizeimage =
> >>> +            ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
> >>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
> >>> +    }
> >>> +}
> >>> +
> >>> +/*
> >>> + * v4l2_format_to_ext_pix_format - convert to v4l2_format to 
> >>> v4l2_ext_pix_format
> >>> + *
> >>> + * @f: A pointer to struct v4l2_format to be converted.
> >>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
> >>> + *
> >>> + * This method normalize the pixelformat to non-M variant.
> >>> + */
> >>> +static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
> >>> +                      struct v4l2_ext_pix_format *ef)
> >>> +{
> >>> +    memset(ef, 0, sizeof(*ef));
> >>> +
> >>> +    switch (f->type) {
> >>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>> +        ef->type = f->type;
> >>> +        v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
> >>> +        break;
> >>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>> +        ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> >>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
> >>> +        break;
> >>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>> +        ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> >>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
> >>> +        break;
> >>> +    default:
> >>> +        WARN("Converting to Ext Pix Format with wrong buffer type 
> >>> %s\n",
> >>> +             prt_names(f->type, v4l2_type_names));
> >>> +        break;
> >>> +    }
> >>> +}
> >>> +
> >>>   static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
> >>>                   struct file *file, void *fh, void *arg)
> >>>   {
> >>> @@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct 
> >>> v4l2_pix_format *p)
> >>>       p->xfer_func = 0;
> >>>   }
> >>> +static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
> >>> +                 struct file *file, void *fh,
> >>> +                 struct v4l2_format *f,
> >>> +                 unsigned int ioctl)
> >>> +{
> >>> +    bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
> >>> +    struct video_device *vdev = video_devdata(file);
> >>> +    struct v4l2_ext_pix_format ef = {0};
> >>> +    u32 original_pixfmt = 0;
> >>> +    u32 cap_mask;
> >>> +    int ret;
> >>> +
> >>> +    if (ioctl != VIDIOC_G_FMT) {
> >>> +        /*
> >>> +         * If CSC attributes are read only, set them to DEFAULT
> >>> +         * to avoid changes by the driver.
> >>> +         */
> >>> +        if (is_multiplanar) {
> >>> +            if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
> >>> +                f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
> >>> +                f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> >>> +                f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
> >>> +                f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
> >>> +            }
> >>> +            /* Unset the flag to avoid warning in the convertion */
> >>> +            f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
> >>> +
> >>> +            /* Save pixelformat in case M-variant is being used */
> >>> +            original_pixfmt = f->fmt.pix_mp.pixelformat;
> >>> +        } else {
> >>> +            if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
> >>> +                f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
> >>> +                f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> >>> +                f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
> >>> +                f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
> >>> +            }
> >>> +            /* Unset the flag to avoid warning in the convertion */
> >>> +            f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
> >>> +        }
> >>> +        v4l2_format_to_ext_pix_format(f, &ef);
> >>> +    }
> >>> +
> >>> +    switch (f->type) {
> >>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>> +        cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> >>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
> >>> +        if (!!(vdev->device_caps & cap_mask) !=
> >>> +            (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
> >>> +            return -EINVAL;
> >>> +
> >>> +        ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> >>> +        if (ioctl == VIDIOC_G_FMT)
> >>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
> >>> +        else if (ioctl == VIDIOC_S_FMT)
> >>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
> >>> +        else
> >>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
> >>> +                                  &ef);
> >>> +        break;
> >>> +
> >>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>> +        cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> >>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
> >>> +        if (!!(vdev->device_caps & cap_mask) !=
> >>> +            (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> >>> +            return -EINVAL;
> >>> +
> >>> +        ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> >>> +        if (ioctl == VIDIOC_G_FMT)
> >>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
> >>> +        else if (ioctl == VIDIOC_S_FMT)
> >>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
> >>> +        else
> >>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
> >>> +                                  &ef);
> >>> +        break;
> >>> +
> >>> +    default:
> >>> +        return -EINVAL;
> >>> +    }
> >>> +
> >>> +    if (ret)
> >>> +        return ret;
> >>> +
> >>> +    if (original_pixfmt != ef.pixelformat &&
> >>> +        v4l2_format_info(original_pixfmt))
> >>
> >> Could this test be simplified to: 'if (original_pixfmt)'?
> >>
> >> I.e., if the original pixfmt was saved, then restore it here.
> >>
> >>> +        ef.pixelformat = original_pixfmt;
> >>> +
> >>> +    v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
> >>> +    return 0;
> >>> +}
> >>> +
> >>>   static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
> >>>                   struct file *file, void *fh, void *arg)
> >>>   {
> >>> @@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct 
> >>> v4l2_ioctl_ops *ops,
> >>>       switch (p->type) {
> >>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>> -        if (unlikely(!ops->vidioc_g_fmt_vid_cap))
> >>> +        if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
> >>> +                 !ops->vidioc_g_ext_pix_fmt_vid_cap))
> >>>               break;
> >>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>> -        ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
> >>> +        ret = ops->vidioc_g_fmt_vid_cap ?
> >>> +              ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
> >>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>> +                        VIDIOC_G_FMT);
> >>>           /* just in case the driver zeroed it again */
> >>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>           if (vfd->vfl_type == VFL_TYPE_TOUCH)
> >>>               v4l_pix_format_touch(&p->fmt.pix);
> >>>           return ret;
> >>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>> -        return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
> >>> +        if (ops->vidioc_g_fmt_vid_cap_mplane)
> >>> +            return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
> >>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_cap)
> >>
> >> 'else' can be dropped.
> >>
> >>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>> +                             VIDIOC_G_FMT);
> >>> +        break;
> >>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >>>           return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
> >>>       case V4L2_BUF_TYPE_VBI_CAPTURE:
> >>> @@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct 
> >>> v4l2_ioctl_ops *ops,
> >>>       case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> >>>           return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>> -        if (unlikely(!ops->vidioc_g_fmt_vid_out))
> >>> +        if (unlikely(!ops->vidioc_g_fmt_vid_out &&
> >>> +                 !ops->vidioc_g_ext_pix_fmt_vid_out))
> >>>               break;
> >>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>> -        ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
> >>> +        ret = ops->vidioc_g_fmt_vid_out ?
> >>> +              ops->vidioc_g_fmt_vid_out(file, fh, arg) :
> >>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
> >>>           /* just in case the driver zeroed it again */
> >>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>           return ret;
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>> -        return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
> >>> +        if (ops->vidioc_g_fmt_vid_out_mplane)
> >>> +            return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
> >>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_out)
> >>
> >> Ditto.
> >>
> >>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>> +                             VIDIOC_G_FMT);
> >>> +        break;
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >>>           return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
> >>>       case V4L2_BUF_TYPE_VBI_OUTPUT:
> >>> @@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct 
> >>> v4l2_ioctl_ops *ops,
> >>>       return -EINVAL;
> >>>   }
> >>> +static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> >>> +                 struct file *file, void *fh, void *arg)
> >>> +{
> >>> +    struct v4l2_ext_pix_format *ef = arg;
> >>> +    struct v4l2_format f = {
> >>> +        .type = ef->type,
> >>> +    };
> >>> +    int ret = check_fmt(file, ef->type);
> >>> +
> >>> +    if (ret)
> >>> +        return ret;
> >>> +
> >>> +    memset(ef, 0, sizeof(*ef));
> >>> +    ef->type = f.type;
> >>> +
> >>> +    switch (f.type) {
> >>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>> +        if (ops->vidioc_g_ext_pix_fmt_vid_cap)
> >>> +            return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
> >>> +        break;
> >>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>> +        if (ops->vidioc_g_ext_pix_fmt_vid_out)
> >>> +            return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
> >>> +        break;
> >>> +    default:
> >>> +        return -EINVAL;
> >>> +    }
> >>> +
> >>> +    ret = v4l_g_fmt(ops, file, fh, &f);
> >>> +    if (ret)
> >>> +        return ret;
> >>> +
> >>> +    v4l2_format_to_ext_pix_format(&f, ef);
> >>> +    return 0;
> >>> +}
> >>> +
> >>>   static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
> >>>                   struct file *file, void *fh, void *arg)
> >>>   {
> >>> @@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct 
> >>> v4l2_ioctl_ops *ops,
> >>>       switch (p->type) {
> >>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap))
> >>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
> >>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
> >>>               break;
> >>>           CLEAR_AFTER_FIELD(p, fmt.pix);
> >>> -        ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
> >>> +        ret = ops->vidioc_s_fmt_vid_cap ?
> >>> +              ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
> >>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
> >>>           /* just in case the driver zeroed it again */
> >>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>           if (vfd->vfl_type == VFL_TYPE_TOUCH)
> >>>               v4l_pix_format_touch(&p->fmt.pix);
> >>>           return ret;
> >>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
> >>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
> >>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
> >>>               break;
> >>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
> >>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
> >>>                         bytesperline);
> >>> -        return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
> >>> +        return ops->vidioc_s_fmt_vid_cap_mplane ?
> >>> +               ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
> >>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,  
> >>> VIDIOC_S_FMT);
> >>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >>>           if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
> >>>               break;
> >>> @@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct 
> >>> v4l2_ioctl_ops *ops,
> >>>           CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
> >>>           return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out))
> >>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out &&
> >>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
> >>>               break;
> >>>           CLEAR_AFTER_FIELD(p, fmt.pix);
> >>> -        ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
> >>> +        ret = ops->vidioc_s_fmt_vid_out ?
> >>> +              ops->vidioc_s_fmt_vid_out(file, fh, arg) :
> >>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
> >>>           /* just in case the driver zeroed it again */
> >>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>           return ret;
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
> >>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
> >>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
> >>>               break;
> >>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
> >>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
> >>>                         bytesperline);
> >>> -        return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
> >>> +        return ops->vidioc_s_fmt_vid_out_mplane ?
> >>> +               ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
> >>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >>>           if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
> >>>               break;
> >>> @@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct 
> >>> v4l2_ioctl_ops *ops,
> >>>       return -EINVAL;
> >>>   }
> >>> +static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> >>> +                 struct file *file, void *fh, void *arg)
> >>> +{
> >>> +    struct video_device *vfd = video_devdata(file);
> >>> +    struct v4l2_ext_pix_format *ef = arg;
> >>> +    struct v4l2_format f;
> >>> +    int ret = check_fmt(file, ef->type);
> >>> +
> >>> +    if (ret)
> >>> +        return ret;
> >>> +
> >>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
> >>> +
> >>> +    switch (ef->type) {
> >>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>> +        if (ops->vidioc_s_ext_pix_fmt_vid_cap)
> >>> +            return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
> >>> +        break;
> >>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>> +        if (ops->vidioc_s_ext_pix_fmt_vid_out)
> >>> +            return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
> >>> +        break;
> >>> +    default:
> >>> +        return -EINVAL;
> >>> +    }
> >>> +
> >>> +    v4l2_ext_pix_format_to_format(ef, &f, 
> >>> V4L2_IS_CAP_MULTIPLANAR(vfd));
> >>> +
> >>> +    ret = v4l_s_fmt(ops, file, fh, &f);
> >>> +    if (ret)
> >>> +        /* TODO: retry with M-variant of ef->pixelformat? */
> >>
> >> See my comments on this at the top.
> >>
> >>> +        return ret;
> >>> +
> >>> +    v4l2_format_to_ext_pix_format(&f, ef);
> >>> +    return 0;
> >>> +}
> >>> +
> >>>   static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
> >>>                   struct file *file, void *fh, void *arg)
> >>>   {
> >>> @@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct 
> >>> v4l2_ioctl_ops *ops,
> >>>       switch (p->type) {
> >>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap))
> >>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
> >>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
> >>>               break;
> >>>           CLEAR_AFTER_FIELD(p, fmt.pix);
> >>> -        ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
> >>> +        ret = ops->vidioc_try_fmt_vid_cap ?
> >>> +              ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
> >>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, 
> >>> VIDIOC_TRY_FMT);
> >>>           /* just in case the driver zeroed it again */
> >>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>           if (vfd->vfl_type == VFL_TYPE_TOUCH)
> >>>               v4l_pix_format_touch(&p->fmt.pix);
> >>>           return ret;
> >>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
> >>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
> >>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
> >>>               break;
> >>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
> >>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
> >>>                         bytesperline);
> >>> -        return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
> >>> +        return ops->vidioc_try_fmt_vid_cap_mplane ?
> >>> +               ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
> >>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>> +                         VIDIOC_TRY_FMT);
> >>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >>>           if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
> >>>               break;
> >>> @@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct 
> >>> v4l2_ioctl_ops *ops,
> >>>           CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
> >>>           return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out))
> >>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out &&
> >>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
> >>>               break;
> >>>           CLEAR_AFTER_FIELD(p, fmt.pix);
> >>> -        ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
> >>> +        ret = ops->vidioc_try_fmt_vid_out ?
> >>> +              ops->vidioc_try_fmt_vid_out(file, fh, arg) :
> >>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, 
> >>> VIDIOC_TRY_FMT);
> >>>           /* just in case the driver zeroed it again */
> >>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>           return ret;
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
> >>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
> >>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
> >>>               break;
> >>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
> >>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
> >>>                         bytesperline);
> >>> -        return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
> >>> +        return ops->vidioc_try_fmt_vid_out_mplane ?
> >>> +               ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
> >>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>> +                         VIDIOC_TRY_FMT);
> >>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >>>           if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
> >>>               break;
> >>> @@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct 
> >>> v4l2_ioctl_ops *ops,
> >>>       return -EINVAL;
> >>>   }
> >>> +static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> >>> +                   struct file *file, void *fh, void *arg)
> >>> +{
> >>> +    struct video_device *vfd = video_devdata(file);
> >>> +    struct v4l2_ext_pix_format *ef = arg;
> >>> +    struct v4l2_format f;
> >>> +    int ret = check_fmt(file, ef->type);
> >>> +
> >>> +    if (ret)
> >>> +        return ret;
> >>> +
> >>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
> >>> +
> >>> +    switch (ef->type) {
> >>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>> +        if (ops->vidioc_try_ext_pix_fmt_vid_cap)
> >>> +            return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
> >>> +                                   ef);
> >>> +        break;
> >>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>> +        if (ops->vidioc_try_ext_pix_fmt_vid_out)
> >>> +            return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
> >>> +                                   ef);
> >>> +        break;
> >>> +    default:
> >>> +        return -EINVAL;
> >>> +    }
> >>> +
> >>> +    v4l2_ext_pix_format_to_format(ef, &f, 
> >>> V4L2_IS_CAP_MULTIPLANAR(vfd));
> >>> +
> >>> +    ret = v4l_try_fmt(ops, file, fh, &f);
> >>> +    if (ret)
> >>> +        /* TODO: retry with M-variant of ef->pixelformat? */
> >>> +        return ret;
> >>> +
> >>> +    v4l2_format_to_ext_pix_format(&f, ef);
> >>> +    return 0;
> >>> +}
> >>> +
> >>>   static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
> >>>                   struct file *file, void *fh, void *arg)
> >>>   {
> >>> @@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info 
> >>> v4l2_ioctls[] = {
> >>>       IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, 
> >>> v4l_print_freq_band, 0),
> >>>       IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, 
> >>> v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
> >>>       IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, 
> >>> v4l_print_query_ext_ctrl, INFO_FL_CTRL | 
> >>> INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
> >>> +    IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, 
> >>> v4l_print_ext_pix_format, 0),
> >>> +    IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, 
> >>> v4l_print_ext_pix_format, INFO_FL_PRIO),
> >>> +    IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, 
> >>> v4l_print_ext_pix_format, 0),
> >>>   };
> >>>   #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
> >>> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
> >>> index edb733f21604..c44708dc9355 100644
> >>> --- a/include/media/v4l2-ioctl.h
> >>> +++ b/include/media/v4l2-ioctl.h
> >>> @@ -48,11 +48,17 @@ struct v4l2_fh;
> >>>    * @vidioc_g_fmt_vid_cap: pointer to the function that implements
> >>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
> >>>    *    in single plane mode
> >>> + * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that 
> >>> implements
> >>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic 
> >>> for video
> >>> + *    capture
> >>>    * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
> >>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
> >>>    * @vidioc_g_fmt_vid_out: pointer to the function that implements
> >>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
> >>>    *    in single plane mode
> >>> + * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that 
> >>> implements
> >>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic 
> >>> for video
> >>> + *    out
> >>>    * @vidioc_g_fmt_vid_out_overlay: pointer to the function that 
> >>> implements
> >>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video 
> >>> overlay output
> >>>    * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
> >>> @@ -82,11 +88,16 @@ struct v4l2_fh;
> >>>    * @vidioc_s_fmt_vid_cap: pointer to the function that implements
> >>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
> >>>    *    in single plane mode
> >>> + * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that 
> >>> implements
> >>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic 
> >>> for video
> >>> + *    capture
> >>>    * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
> >>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
> >>>    * @vidioc_s_fmt_vid_out: pointer to the function that implements
> >>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
> >>>    *    in single plane mode
> >>> + * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that 
> >>> implements
> >>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for 
> >>> video out
> >>>    * @vidioc_s_fmt_vid_out_overlay: pointer to the function that 
> >>> implements
> >>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video 
> >>> overlay output
> >>>    * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
> >>> @@ -116,11 +127,16 @@ struct v4l2_fh;
> >>>    * @vidioc_try_fmt_vid_cap: pointer to the function that implements
> >>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video 
> >>> capture
> >>>    *    in single plane mode
> >>> + * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that 
> >>> implements
> >>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl 
> >>> logic for
> >>> +    video capture
> >>>    * @vidioc_try_fmt_vid_overlay: pointer to the function that 
> >>> implements
> >>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video 
> >>> overlay
> >>>    * @vidioc_try_fmt_vid_out: pointer to the function that implements
> >>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
> >>>    *    in single plane mode
> >>> + * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that 
> >>> implements
> >>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for 
> >>> video out
> >>>    * @vidioc_try_fmt_vid_out_overlay: pointer to the function that 
> >>> implements
> >>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video 
> >>> overlay
> >>>    *    output
> >>> @@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
> >>>       /* VIDIOC_G_FMT handlers */
> >>>       int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
> >>>                       struct v4l2_format *f);
> >>> +    int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> >>> +                        struct v4l2_ext_pix_format *ef);
> >>>       int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
> >>>                       struct v4l2_format *f);
> >>>       int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
> >>>                       struct v4l2_format *f);
> >>> +    int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> >>> +                        struct v4l2_ext_pix_format *ef);
> >>>       int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
> >>>                           struct v4l2_format *f);
> >>>       int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
> >>> @@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
> >>>       /* VIDIOC_S_FMT handlers */
> >>>       int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
> >>>                       struct v4l2_format *f);
> >>> +    int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> >>> +                        struct v4l2_ext_pix_format *ef);
> >>>       int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
> >>>                       struct v4l2_format *f);
> >>>       int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
> >>>                       struct v4l2_format *f);
> >>> +    int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> >>> +                        struct v4l2_ext_pix_format *ef);
> >>>       int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
> >>>                           struct v4l2_format *f);
> >>>       int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
> >>> @@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
> >>>       /* VIDIOC_TRY_FMT handlers */
> >>>       int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
> >>>                         struct v4l2_format *f);
> >>> +    int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> >>> +                          struct v4l2_ext_pix_format *ef);
> >>>       int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
> >>>                         struct v4l2_format *f);
> >>>       int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
> >>>                         struct v4l2_format *f);
> >>> +    int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> >>> +                          struct v4l2_ext_pix_format *ef);
> >>>       int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
> >>>                            struct v4l2_format *f);
> >>>       int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
> >>> diff --git a/include/uapi/linux/videodev2.h 
> >>> b/include/uapi/linux/videodev2.h
> >>> index d9b7c9177605..a2d850513708 100644
> >>> --- a/include/uapi/linux/videodev2.h
> >>> +++ b/include/uapi/linux/videodev2.h
> >>> @@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
> >>>       __u8                reserved[7];
> >>>   } __attribute__ ((packed));
> >>> +/**
> >>> + * struct v4l2_ext_pix_format - extended single/multiplanar format 
> >>> definition
> >>> + * @type:        type of the data stream; 
> >>> V4L2_BUF_TYPE_VIDEO_CAPTURE or
> >>> + *            V4L2_BUF_TYPE_VIDEO_OUTPUT
> >>> + * @width:        image width in pixels
> >>> + * @height:        image height in pixels
> >>> + * @field:        enum v4l2_field; field order (for interlaced video)
> >>> + * @plane_fmt:        per-plane information
> >>> + * @pixelformat:    little endian four character code (fourcc)
> >>> + * @modifier:        modifier applied to the format (used for tiled 
> >>> formats
> >>> + *            and other kind of HW-specific formats, like compressed
> >>> + *            formats) as defined in drm_fourcc.h
> >>> + * @colorspace:        enum v4l2_colorspace; supplemental to 
> >>> pixelformat
> >>> + * @xfer_func:        enum v4l2_xfer_func, colorspace transfer function
> >>> + * @ycbcr_enc:        enum v4l2_ycbcr_encoding, Y'CbCr encoding
> >>> + * @hsv_enc:        enum v4l2_hsv_encoding, HSV encoding
> >>> + * @quantization:    enum v4l2_quantization, colorspace quantization
> >>> + * @reserved:        extra space reserved for future fields, must be 
> >>> set to 0
> >>> + */
> >>> +struct v4l2_ext_pix_format {
> >>> +    __u32 type;
> >>> +    __u32 width;
> >>> +    __u32 height;
> >>> +    __u32 field;
> >>> +    struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
> >>> +    __u32 pixelformat;
> >>> +    __u64 modifier;
> >>> +    __u32 colorspace;
> >>> +    __u32 xfer_func;
> >>> +    union {
> >>> +        __u32 ycbcr_enc;
> >>> +        __u32 hsv_enc;
> >>> +    };
> >>> +    __u32 quantization;
> >>> +    __u32 reserved[9];
> >>> +};
> >>> +
> >>>   /**
> >>>    * struct v4l2_sdr_format - SDR format definition
> >>>    * @pixelformat:    little endian four character code (fourcc)
> >>> @@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
> >>>   #define VIDIOC_QUERY_EXT_CTRL    _IOWR('V', 103, struct 
> >>> v4l2_query_ext_ctrl)
> >>> +#define VIDIOC_G_EXT_PIX_FMT    _IOWR('V', 104, struct 
> >>> v4l2_ext_pix_format)
> >>> +#define VIDIOC_S_EXT_PIX_FMT    _IOWR('V', 105, struct 
> >>> v4l2_ext_pix_format)
> >>> +#define VIDIOC_TRY_EXT_PIX_FMT    _IOWR('V', 106, struct 
> >>> v4l2_ext_pix_format)
> >>> +
> >>>   /* Reminder: when adding new ioctls please add support for them to
> >>>      drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
> >>>

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-05 15:19       ` Hsia-Jun Li
  2022-11-06 19:24         ` Laurent Pinchart
@ 2022-11-06 22:11         ` Dmitry Osipenko
  2022-11-07  2:04           ` Hsia-Jun Li
                             ` (2 more replies)
  2022-11-07  8:42         ` Hans Verkuil
  2022-11-10 17:06         ` Nicolas Dufresne
  3 siblings, 3 replies; 48+ messages in thread
From: Dmitry Osipenko @ 2022-11-06 22:11 UTC (permalink / raw)
  To: Hsia-Jun Li, Helen Koike
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga,
	Hans Verkuil, linux-media

On 11/5/22 18:19, Hsia-Jun Li wrote:
> Hello Helen
> 
> I didn't see any updates from V6 and V7-WIP in your repo. That is what I
> need to for our complex tile formats in our platform.
> 
> Any future plane here?
> 
> Besides I have some ideas on these patches.

I was looking into updating this patchset few months ago and the biggest
blocker was the absence of immediate upstream user for this new UAPI.
What your platform is? Is the driver stack completely opensource?

-- 
Best regards,
Dmitry


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-06 19:24         ` Laurent Pinchart
@ 2022-11-07  1:54           ` Hsia-Jun Li
  2022-11-07  8:28             ` Laurent Pinchart
  0 siblings, 1 reply; 48+ messages in thread
From: Hsia-Jun Li @ 2022-11-07  1:54 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Helen Koike, mchehab, hans.verkuil, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga,
	Hans Verkuil, linux-media



On 11/7/22 03:24, Laurent Pinchart wrote:
> 
> Hi Hsia-Jun,
> 
> On Sat, Nov 05, 2022 at 11:19:10PM +0800, Hsia-Jun Li wrote:
>> Hello Helen
>>
>> I didn't see any updates from V6 and V7-WIP in your repo. That is what I
>> need to for our complex tile formats in our platform.
>>
>> Any future plane here?
>>
>> Besides I have some ideas on these patches.
>>
>> On 2/24/21 23:12, Helen Koike wrote:
>>> Hi Hans,
>>>
>>> Thank you for your comment, please see my reply below.
>>>
>>> On 2/23/21 9:35 AM, Hans Verkuil wrote:
>>>> Hi Helen,
>>>>
>>>> On 14/01/2021 19:07, Helen Koike wrote:
>>>>> This is part of the multiplanar and singleplanar unification process.
>>>>> v4l2_ext_pix_format is supposed to work for both cases.
>>>>>
>>>>> We also add the concept of modifiers already employed in DRM to expose
>>>>> HW-specific formats (like tiled or compressed formats) and allow
>>>>> exchanging this information with the DRM subsystem in a consistent way.
>>>>>
>>>>> Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
>>>>> v4l2_ext_format, other types will be rejected if you use the
>>>>> {G,S,TRY}_EXT_PIX_FMT ioctls.
>>>>>
>>>>> New hooks have been added to v4l2_ioctl_ops to support those new ioctls
>>>>> in drivers, but, in the meantime, the core takes care of converting
>>>>> {S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers
>>>>> can
>>>>> still work if the userspace app/lib uses the new ioctls.
>>>>>
>>>>> The conversion is also done the other around to allow userspace
>>>>> apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
>>>>> _ext_ hooks.
>>>>
>>>> I have some small comments below, but also one high level comment:
>>>>
>>>> Regarding M variants of pixelformats: this patch 'normalizes' them to
>>>> regular pixelformats in the extended API. This makes life complicated,
>>>> and I wonder if this is the right approach.
>>>>
>>>> Currently there are two reasons for a driver to support e.g. NV12M:
>>>> either luma and chroma need to be in two memory banks, or the luma
>>>> and chroma planes cannot be contiguous due to alignment requirements.
>>>>
>>>> The first requirement is still valid for drivers that support the
>>>> extended API.
>>>> The second requirement is no longer a reason to support NV12M. But I
>>>> don't think we should just drop NV12M support if it was already supported
>>>> before the conversion to this extended API. Since NV12M allocates two
>>>> buffers
>>>> instead of one, it is still different from a regular NV12.
>>>
>>> I don't see what would be the difference when using NV12 and NV12M in
>>> Ext API.
>>> NV12 could be used for both requirements. It would even allow the second
>>> requirement with a single memory buffer.
>>>
>> Although I don't have problem here to support both single and multiple
>> planes in our hardware. But using the single plane format here is not
>> convience for us.
>>
>> The tile format in our platform is little complex, you can't calculate
>> the stride and sizeimage easily with just the modifier, width and
>> height. Then we don't have a good place to record the offset here.
> 
> What else is needed to compute those values ? Can they be computed by
> the kernel driver, or do they have computed by userspace ?
> 
I could calculate the stride(bytesperline) and sizeimage in the kernel 
driver. But it would be better to let the firmware do that. Our driver 
would depend on the V4L2_EVENT_SOURCE_CHANGE event.

>> Besides, the luma and chroma planes would always need their own
>> compression meta data planes. If we use NV12 here, that would make a
>> very strange pixel format, one plane for the pixel datas and two planes
>> for the meta data.
>>
>>>> I would prefer that such drivers support both NV12 and NV12M, so no
>>>> automatic conversion.
>>>>
>>>> A related question is how to handle pixelformat enumeration: with the
>>>> extended API an NV12 format might work, but not with the old API (e.g.
>>>> due to memory alignment requirements). I wonder if a
>>>> VIDIOC_ENUM_EXT_PIX_FMT
>>>> isn't needed.
>>>
>>> We need VIDIOC_ENUM_EXT_PIX_FMT for modifiers, but we can add it later.
>>> If the driver reports NV12M, userspace can use it and the framework
>>> normilizes it.
>>>
>>>>
>>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
>>>> VIDIOC_ENUM_FMT
>>>> would just report NV12M.
>>>
>>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
>>> report both (unless I'm missing something, which is probably the case).
>>>
>>> The idea was to deprecate the M-variants one day.
>>
>> I was thinking the way in DRM API is better, always assuming it would
>> always in a multiple planes. The only problem is we don't have a way to
>> let the allocator that allocate contiguous memory for planes when we
>> need to do that.
>>
>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>>>>> ---
>>>>>
>>>>> Changes in v6:
>>>>>    The main change here was fixing the conversion, so planes reflects
>>>>> color planes,
>>>>>    and to implement this properly I made major refactors compared to
>>>>> the previous
>>>>>    version.
>>>>> - struct v4l2_plane_ext_pix_format removed, using struct
>>>>> v4l2_plane_pix_format instead (Tomasz)
>>>>> - refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
>>>>> - reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
>>>>> - do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
>>>>> - refactor conversion functions, so planes are color planes (Tomasz)
>>>>> - Don't explicitly check for e->modifier != 0 in
>>>>> v4l2_ext_pix_format_to_format() (Tomasz)
>>>>> - Use "ef" for extended formats in the framework for consistency
>>>>> (Tomasz)
>>>>> - Handle xfer_func field in conversions (Tomasz)
>>>>> - Zero reserved fields in v4l_s_ext_pix_fmt() and
>>>>> v4l_try_ext_pix_fmt() (Tomasz)
>>>>> - Refactor format functions to use v4l_fmt_ioctl_via_ext()
>>>>> - Several fixes/refactoring/changes
>>>>> - Remove EXT API for touch devices
>>>>>
>>>>> Changes in v5:
>>>>> - change sizes and reorder fields to avoid holes in the struct and make
>>>>>     it the same for 32 and 64 bits
>>>>> - removed __attribute__ ((packed)) from uapi structs
>>>>> - Fix doc warning from make htmldocs
>>>>> - Updated commit message with EXT_PIX prefix for the ioctls.
>>>>>
>>>>> Changes in v4:
>>>>> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
>>>>> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
>>>>> - Add reserved fields
>>>>> - Removed num_planes from struct v4l2_ext_pix_format
>>>>> - Removed flag field from struct v4l2_ext_pix_format, since the only
>>>>>     defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
>>>>>     where we can use modifiers, or add it back later through the reserved
>>>>>     bits.
>>>>> - In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
>>>>>     != MOD_INVALID
>>>>> - Fix type assignment in v4l_g_fmt_ext_pix()
>>>>> - Rebased on top of media/master (post 5.8-rc1)
>>>>>
>>>>> Changes in v3:
>>>>> - Rebased on top of media/master (post 5.4-rc1)
>>>>>
>>>>> Changes in v2:
>>>>> - Move the modifier in v4l2_ext_format (was formerly placed in
>>>>>     v4l2_ext_plane)
>>>>> - Fix a few bugs in the converters and add a strict parameter to
>>>>>     allow conversion of uninitialized/mis-initialized objects
>>>>> ---
>>>>>    drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
>>>>>    drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
>>>>>    include/media/v4l2-ioctl.h           |  28 ++
>>>>>    include/uapi/linux/videodev2.h       |  41 ++
>>>>>    4 files changed, 602 insertions(+), 32 deletions(-)
>>>>>
>>>>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c
>>>>> b/drivers/media/v4l2-core/v4l2-dev.c
>>>>> index f9cff033d0dc..5add58cb6d45 100644
>>>>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>>>>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>>>>> @@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct
>>>>> video_device *vdev)
>>>>>                       ops->vidioc_enum_fmt_vid_overlay)) ||
>>>>>                (is_tx && ops->vidioc_enum_fmt_vid_out))
>>>>>                set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
>>>>> +        if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
>>>>> +            (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
>>>>> +             set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>>>>>            if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
>>>>>                       ops->vidioc_g_fmt_vid_cap_mplane ||
>>>>> -                   ops->vidioc_g_fmt_vid_overlay)) ||
>>>>> +                   ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
>>>>>                (is_tx && (ops->vidioc_g_fmt_vid_out ||
>>>>>                       ops->vidioc_g_fmt_vid_out_mplane ||
>>>>> -                   ops->vidioc_g_fmt_vid_out_overlay)))
>>>>> +                   ops->vidioc_g_ext_pix_fmt_vid_out))) {
>>>>>                 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>>>>> +             set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
>>>>> +        }
>>>>> +        if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
>>>>> +            (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
>>>>> +             set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>>>>>            if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
>>>>>                       ops->vidioc_s_fmt_vid_cap_mplane ||
>>>>> -                   ops->vidioc_s_fmt_vid_overlay)) ||
>>>>> +                   ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
>>>>>                (is_tx && (ops->vidioc_s_fmt_vid_out ||
>>>>>                       ops->vidioc_s_fmt_vid_out_mplane ||
>>>>> -                   ops->vidioc_s_fmt_vid_out_overlay)))
>>>>> +                   ops->vidioc_s_ext_pix_fmt_vid_out))) {
>>>>>                 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>>>>> +             set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
>>>>> +        }
>>>>> +        if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
>>>>> +            (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
>>>>> +             set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>>>>>            if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
>>>>>                       ops->vidioc_try_fmt_vid_cap_mplane ||
>>>>> -                   ops->vidioc_try_fmt_vid_overlay)) ||
>>>>> +                   ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
>>>>>                (is_tx && (ops->vidioc_try_fmt_vid_out ||
>>>>>                       ops->vidioc_try_fmt_vid_out_mplane ||
>>>>> -                   ops->vidioc_try_fmt_vid_out_overlay)))
>>>>> +                   ops->vidioc_try_ext_pix_fmt_vid_out))) {
>>>>>                 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>>>>> +             set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
>>>>> +        }
>>>>>            SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
>>>>>            SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
>>>>>            SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
>>>>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
>>>>> b/drivers/media/v4l2-core/v4l2-ioctl.c
>>>>> index 848286a284f6..a9c07c0a73ec 100644
>>>>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>>>>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>>>>> @@ -18,6 +18,8 @@
>>>>>    #include <linux/videodev2.h>
>>>>> +#include <drm/drm_fourcc.h>
>>>>> +
>>>>>    #include <media/v4l2-common.h>
>>>>>    #include <media/v4l2-ioctl.h>
>>>>>    #include <media/v4l2-ctrls.h>
>>>>> @@ -38,6 +40,11 @@
>>>>>    #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd),
>>>>> (vfd)->valid_ioctls)
>>>>> +#define V4L2_IS_CAP_MULTIPLANAR(vdev)    (vdev->device_caps & \
>>>>> +                     (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
>>>>> +                     V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
>>>>> +                     V4L2_CAP_VIDEO_M2M_MPLANE))
>>>>> +
>>>>>    struct std_descr {
>>>>>        v4l2_std_id std;
>>>>>        const char *descr;
>>>>> @@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg,
>>>>> bool write_only)
>>>>>        }
>>>>>    }
>>>>> +static void v4l_print_ext_pix_format(const void *arg, bool write_only)
>>>>> +{
>>>>> +    const struct v4l2_ext_pix_format *ef = arg;
>>>>> +    unsigned int i;
>>>>> +
>>>>> +    pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier
>>>>> %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u,
>>>>> xfer_func=%u\n",
>>>>> +        prt_names(ef->type, v4l2_type_names),
>>>>> +        ef->width, ef->height,
>>>>> +        (ef->pixelformat & 0xff),
>>>>> +        (ef->pixelformat >>  8) & 0xff,
>>>>> +        (ef->pixelformat >> 16) & 0xff,
>>>>> +        (ef->pixelformat >> 24) & 0xff,
>>>>> +        ef->modifier, prt_names(ef->field, v4l2_field_names),
>>>>> +        ef->colorspace, ef->ycbcr_enc,
>>>>> +        ef->quantization, ef->xfer_func);
>>>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage;
>>>>> i++)
>>>>> +        pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
>>>>> +             i, ef->plane_fmt[i].bytesperline,
>>>>> +             ef->plane_fmt[i].sizeimage);
>>>>> +}
>>>>> +
>>>>>    static void v4l_print_framebuffer(const void *arg, bool write_only)
>>>>>    {
>>>>>        const struct v4l2_framebuffer *p = arg;
>>>>> @@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum
>>>>> v4l2_buf_type type)
>>>>>        switch (type) {
>>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>            if ((is_vid || is_tch) && is_rx &&
>>>>> -            (ops->vidioc_g_fmt_vid_cap ||
>>>>> ops->vidioc_g_fmt_vid_cap_mplane))
>>>>> +            (ops->vidioc_g_fmt_vid_cap ||
>>>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap ||
>>>>> +             ops->vidioc_g_fmt_vid_cap_mplane))
>>>>>                return 0;
>>>>>            break;
>>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>> -        if ((is_vid || is_tch) && is_rx &&
>>>>> ops->vidioc_g_fmt_vid_cap_mplane)
>>>>> +        if ((is_vid || is_tch) && is_rx &&
>>>>> +            (ops->vidioc_g_fmt_vid_cap_mplane ||
>>>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap))
>>>>>                return 0;
>>>>>            break;
>>>>>        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>> @@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum
>>>>> v4l2_buf_type type)
>>>>>            break;
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>            if (is_vid && is_tx &&
>>>>> -            (ops->vidioc_g_fmt_vid_out ||
>>>>> ops->vidioc_g_fmt_vid_out_mplane))
>>>>> +            (ops->vidioc_g_fmt_vid_out ||
>>>>> +             ops->vidioc_g_ext_pix_fmt_vid_out ||
>>>>> +             ops->vidioc_g_fmt_vid_out_mplane))
>>>>>                return 0;
>>>>>            break;
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>> -        if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
>>>>> +        if (is_vid && is_tx &&
>>>>> +            (ops->vidioc_g_ext_pix_fmt_vid_out ||
>>>>> +             ops->vidioc_g_fmt_vid_out_mplane))
>>>>>                return 0;
>>>>>            break;
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>> @@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct
>>>>> v4l2_format *fmt)
>>>>>               sizeof(fmt->fmt.pix) - offset);
>>>>>    }
>>>>> +static void
>>>>> +v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
>>>>> +                  struct v4l2_pix_format *pix)
>>>>> +{
>>>>> +    unsigned int i;
>>>>> +
>>>>> +    pix->width = ef->width;
>>>>> +    pix->height = ef->height;
>>>>> +    pix->field = ef->field;
>>>>> +    pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>>>>> +    pix->colorspace = ef->colorspace;
>>>>> +    pix->ycbcr_enc = ef->ycbcr_enc;
>>>>> +    pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>> +    pix->quantization = ef->quantization;
>>>>> +    pix->pixelformat = ef->pixelformat;
>>>>> +    pix->bytesperline = ef->plane_fmt[0].bytesperline;
>>>>> +    pix->sizeimage = ef->plane_fmt[0].sizeimage;
>>>>> +    for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage;
>>>>> i++)
>>>>> +        pix->sizeimage += ef->plane_fmt[i].sizeimage;
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +v4l2_ext_pix_format_to_pix_mp_format(const struct
>>>>> v4l2_ext_pix_format *ef,
>>>>> +                     struct v4l2_pix_format_mplane *pix_mp)
>>>>> +{
>>>>> +    const struct v4l2_format_info *info =
>>>>> +                    v4l2_format_info(ef->pixelformat);
>>>>> +    unsigned int i;
>>>>> +
>>>>> +    pix_mp->width = ef->width;
>>>>> +    pix_mp->height = ef->height;
>>>>> +    pix_mp->field = ef->field;
>>>>> +    pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>>>>> +    pix_mp->colorspace = ef->colorspace;
>>>>> +    pix_mp->ycbcr_enc = ef->ycbcr_enc;
>>>>> +    pix_mp->quantization = ef->quantization;
>>>>> +    pix_mp->pixelformat = ef->pixelformat;
>>>>> +
>>>>> +    /* This is true when converting to non-M-variant */
>>>>> +    if (info && info->mem_planes == 1) {
>>>>> +        pix_mp->plane_fmt[0] = ef->plane_fmt[0];
>>>>> +        pix_mp->num_planes = 1;
>>>>> +        for (i = 1; i < VIDEO_MAX_PLANES &&
>>>>> ef->plane_fmt[i].sizeimage; i++)
>>>>> +            pix_mp->plane_fmt[0].sizeimage +=
>>>>> ef->plane_fmt[i].sizeimage;
>>>>> +
>>>>> +        return;
>>>>> +    }
>>>>> +
>>>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage;
>>>>> i++)
>>>>> +        pix_mp->plane_fmt[i] = ef->plane_fmt[i];
>>>>> +    pix_mp->num_planes = i;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to
>>>>> v4l2_format
>>>>> + *
>>>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
>>>>> + * @f: A pointer to struct v4l2_format to be filled.
>>>>> + * @is_mplane: Bool indicating if multiplanar API should be used in @f.
>>>>> + *
>>>>> + * If pixelformat should be converted to M-variant, change
>>>>> ef->pixelformat
>>>>> + * to the M-variant before calling this function.
>>>>> + */
>>>>> +static void v4l2_ext_pix_format_to_format(const struct
>>>>> v4l2_ext_pix_format *ef,
>>>>> +                      struct v4l2_format *f, bool is_mplane)
>>>>> +{
>>>>> +    memset(f, 0, sizeof(*f));
>>>>> +
>>>>> +    if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
>>>>> +        ef->modifier != DRM_FORMAT_MOD_INVALID)
>>>>> +        pr_warn("Modifiers are not supported in v4l2_format,
>>>>> ignoring %llx\n",
>>>>> +            ef->modifier);
>>>>> +
>>>>> +    if (!is_mplane) {
>>>>> +        f->type = ef->type;
>>>>> +        v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
>>>>> +        return;
>>>>> +    }
>>>>> +
>>>>> +    if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>>>> +        f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>>>>> +    else
>>>>> +        f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
>>>>> +
>>>>> +    v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
>>>>> +                  struct v4l2_ext_pix_format *ef)
>>>>> +{
>>>>> +    const struct v4l2_format_info *info =
>>>>> +                    v4l2_format_info(pix->pixelformat);
>>>>> +    unsigned int i;
>>>>> +
>>>>> +    ef->width = pix->width;
>>>>> +    ef->height = pix->height;
>>>>> +    ef->field = pix->field;
>>>>> +    ef->colorspace = pix->colorspace;
>>>>> +    ef->ycbcr_enc = pix->ycbcr_enc;
>>>>> +    ef->quantization = pix->quantization;
>>>>> +    ef->xfer_func = pix->xfer_func;
>>>>> +    if (pix->flags)
>>>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
>>>>> +
>>>>> +    /* We assume M-variants won't be used in this function */
>>>>> +    ef->pixelformat = pix->pixelformat;
>>>>> +
>>>>> +    ef->plane_fmt[0].bytesperline = pix->bytesperline;
>>>>> +    ef->plane_fmt[0].sizeimage = pix->sizeimage;
>>>>> +
>>>>> +    if (!info)
>>>>> +        return;
>>>>> +
>>>>> +    for (i = 1; i < info->comp_planes; i++) {
>>>>> +        ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
>>>>> +        ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
>>>>> +                         ef->height / info->vdiv;
>>>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +static void
>>>>> +v4l2_pix_mp_format_to_ext_pix_format(const struct
>>>>> v4l2_pix_format_mplane *pix_mp,
>>>>> +                     struct v4l2_ext_pix_format *ef)
>>>>> +{
>>>>> +    const struct v4l2_format_info *info =
>>>>> +                    v4l2_format_info(pix_mp->pixelformat);
>>>>> +    unsigned int i;
>>>>> +
>>>>> +    ef->width = pix_mp->width;
>>>>> +    ef->height = pix_mp->height;
>>>>> +    ef->field = pix_mp->field;
>>>>> +    ef->colorspace = pix_mp->colorspace;
>>>>> +    ef->ycbcr_enc = pix_mp->ycbcr_enc;
>>>>> +    ef->quantization = pix_mp->quantization;
>>>>> +    ef->xfer_func = pix_mp->xfer_func;
>>>>> +    if (pix_mp->flags)
>>>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
>>>>> +
>>>>> +    if (!info)
>>>>> +        return;
>>>>> +
>>>>> +    ef->pixelformat = info && info->norm ?
>>>>
>>>> 'info &&' can be dropped, info is always non-NULL here.
>>>>
>>>>> +              info->norm : pix_mp->pixelformat;
>>>>> +
>>>>> +    if (info->comp_planes == info->mem_planes) {
>>>>> +        for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES;
>>>>> i++)
>>>>> +            ef->plane_fmt[i] = pix_mp->plane_fmt[i];
>>>>> +
>>>>> +        return;
>>>>> +    }
>>>>> +
>>>>> +    /* case where mem_planes is 1 and comp_planes > 1 */
>>>>> +    ef->plane_fmt[0] = pix_mp->plane_fmt[0];
>>>>> +    for (i = 1; i < info->comp_planes; i++) {
>>>>> +        ef->plane_fmt[i].bytesperline =
>>>>> +            pix_mp->plane_fmt[0].bytesperline / info->hdiv;
>>>>> +        ef->plane_fmt[i].sizeimage =
>>>>> +            ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
>>>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>>>>> +    }
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * v4l2_format_to_ext_pix_format - convert to v4l2_format to
>>>>> v4l2_ext_pix_format
>>>>> + *
>>>>> + * @f: A pointer to struct v4l2_format to be converted.
>>>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
>>>>> + *
>>>>> + * This method normalize the pixelformat to non-M variant.
>>>>> + */
>>>>> +static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
>>>>> +                      struct v4l2_ext_pix_format *ef)
>>>>> +{
>>>>> +    memset(ef, 0, sizeof(*ef));
>>>>> +
>>>>> +    switch (f->type) {
>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>> +        ef->type = f->type;
>>>>> +        v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
>>>>> +        break;
>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>>>>> +        break;
>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>>>>> +        break;
>>>>> +    default:
>>>>> +        WARN("Converting to Ext Pix Format with wrong buffer type
>>>>> %s\n",
>>>>> +             prt_names(f->type, v4l2_type_names));
>>>>> +        break;
>>>>> +    }
>>>>> +}
>>>>> +
>>>>>    static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
>>>>>                    struct file *file, void *fh, void *arg)
>>>>>    {
>>>>> @@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct
>>>>> v4l2_pix_format *p)
>>>>>        p->xfer_func = 0;
>>>>>    }
>>>>> +static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
>>>>> +                 struct file *file, void *fh,
>>>>> +                 struct v4l2_format *f,
>>>>> +                 unsigned int ioctl)
>>>>> +{
>>>>> +    bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
>>>>> +    struct video_device *vdev = video_devdata(file);
>>>>> +    struct v4l2_ext_pix_format ef = {0};
>>>>> +    u32 original_pixfmt = 0;
>>>>> +    u32 cap_mask;
>>>>> +    int ret;
>>>>> +
>>>>> +    if (ioctl != VIDIOC_G_FMT) {
>>>>> +        /*
>>>>> +         * If CSC attributes are read only, set them to DEFAULT
>>>>> +         * to avoid changes by the driver.
>>>>> +         */
>>>>> +        if (is_multiplanar) {
>>>>> +            if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>>>>> +                f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
>>>>> +                f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>>>>> +                f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
>>>>> +                f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>>>>> +            }
>>>>> +            /* Unset the flag to avoid warning in the convertion */
>>>>> +            f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>>>>> +
>>>>> +            /* Save pixelformat in case M-variant is being used */
>>>>> +            original_pixfmt = f->fmt.pix_mp.pixelformat;
>>>>> +        } else {
>>>>> +            if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>>>>> +                f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
>>>>> +                f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>>>>> +                f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
>>>>> +                f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>>>>> +            }
>>>>> +            /* Unset the flag to avoid warning in the convertion */
>>>>> +            f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>>>>> +        }
>>>>> +        v4l2_format_to_ext_pix_format(f, &ef);
>>>>> +    }
>>>>> +
>>>>> +    switch (f->type) {
>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>> +        cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
>>>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
>>>>> +        if (!!(vdev->device_caps & cap_mask) !=
>>>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
>>>>> +            return -EINVAL;
>>>>> +
>>>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>>>> +        if (ioctl == VIDIOC_G_FMT)
>>>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
>>>>> +        else if (ioctl == VIDIOC_S_FMT)
>>>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
>>>>> +        else
>>>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>>>>> +                                  &ef);
>>>>> +        break;
>>>>> +
>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>> +        cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
>>>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
>>>>> +        if (!!(vdev->device_caps & cap_mask) !=
>>>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>>>>> +            return -EINVAL;
>>>>> +
>>>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>>>> +        if (ioctl == VIDIOC_G_FMT)
>>>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
>>>>> +        else if (ioctl == VIDIOC_S_FMT)
>>>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
>>>>> +        else
>>>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>>>>> +                                  &ef);
>>>>> +        break;
>>>>> +
>>>>> +    default:
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    if (original_pixfmt != ef.pixelformat &&
>>>>> +        v4l2_format_info(original_pixfmt))
>>>>
>>>> Could this test be simplified to: 'if (original_pixfmt)'?
>>>>
>>>> I.e., if the original pixfmt was saved, then restore it here.
>>>>
>>>>> +        ef.pixelformat = original_pixfmt;
>>>>> +
>>>>> +    v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>>    static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>>>>                    struct file *file, void *fh, void *arg)
>>>>>    {
>>>>> @@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct
>>>>> v4l2_ioctl_ops *ops,
>>>>>        switch (p->type) {
>>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_cap))
>>>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
>>>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_cap))
>>>>>                break;
>>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>> -        ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
>>>>> +        ret = ops->vidioc_g_fmt_vid_cap ?
>>>>> +              ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>> +                        VIDIOC_G_FMT);
>>>>>            /* just in case the driver zeroed it again */
>>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>            if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>>>                v4l_pix_format_touch(&p->fmt.pix);
>>>>>            return ret;
>>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>> -        return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>>>> +        if (ops->vidioc_g_fmt_vid_cap_mplane)
>>>>> +            return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_cap)
>>>>
>>>> 'else' can be dropped.
>>>>
>>>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>> +                             VIDIOC_G_FMT);
>>>>> +        break;
>>>>>        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>>            return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>>>>>        case V4L2_BUF_TYPE_VBI_CAPTURE:
>>>>> @@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct
>>>>> v4l2_ioctl_ops *ops,
>>>>>        case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>>>>>            return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_out))
>>>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_out &&
>>>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_out))
>>>>>                break;
>>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>> -        ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
>>>>> +        ret = ops->vidioc_g_fmt_vid_out ?
>>>>> +              ops->vidioc_g_fmt_vid_out(file, fh, arg) :
>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
>>>>>            /* just in case the driver zeroed it again */
>>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>            return ret;
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>> -        return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>>>> +        if (ops->vidioc_g_fmt_vid_out_mplane)
>>>>> +            return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_out)
>>>>
>>>> Ditto.
>>>>
>>>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>> +                             VIDIOC_G_FMT);
>>>>> +        break;
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>>            return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>>>>>        case V4L2_BUF_TYPE_VBI_OUTPUT:
>>>>> @@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct
>>>>> v4l2_ioctl_ops *ops,
>>>>>        return -EINVAL;
>>>>>    }
>>>>> +static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>>>> +                 struct file *file, void *fh, void *arg)
>>>>> +{
>>>>> +    struct v4l2_ext_pix_format *ef = arg;
>>>>> +    struct v4l2_format f = {
>>>>> +        .type = ef->type,
>>>>> +    };
>>>>> +    int ret = check_fmt(file, ef->type);
>>>>> +
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    memset(ef, 0, sizeof(*ef));
>>>>> +    ef->type = f.type;
>>>>> +
>>>>> +    switch (f.type) {
>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_cap)
>>>>> +            return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
>>>>> +        break;
>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_out)
>>>>> +            return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
>>>>> +        break;
>>>>> +    default:
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    ret = v4l_g_fmt(ops, file, fh, &f);
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>>    static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>>>>                    struct file *file, void *fh, void *arg)
>>>>>    {
>>>>> @@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct
>>>>> v4l2_ioctl_ops *ops,
>>>>>        switch (p->type) {
>>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap))
>>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
>>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>>>>                break;
>>>>>            CLEAR_AFTER_FIELD(p, fmt.pix);
>>>>> -        ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
>>>>> +        ret = ops->vidioc_s_fmt_vid_cap ?
>>>>> +              ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>>>            /* just in case the driver zeroed it again */
>>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>            if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>>>                v4l_pix_format_touch(&p->fmt.pix);
>>>>>            return ret;
>>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
>>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
>>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>>>>                break;
>>>>>            CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>>            for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>>                CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>>                          bytesperline);
>>>>> -        return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
>>>>> +        return ops->vidioc_s_fmt_vid_cap_mplane ?
>>>>> +               ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
>>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>> VIDIOC_S_FMT);
>>>>>        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>>            if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>>>>>                break;
>>>>> @@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct
>>>>> v4l2_ioctl_ops *ops,
>>>>>            CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>>>>            return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out))
>>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out &&
>>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
>>>>>                break;
>>>>>            CLEAR_AFTER_FIELD(p, fmt.pix);
>>>>> -        ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
>>>>> +        ret = ops->vidioc_s_fmt_vid_out ?
>>>>> +              ops->vidioc_s_fmt_vid_out(file, fh, arg) :
>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>>>            /* just in case the driver zeroed it again */
>>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>            return ret;
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
>>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
>>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
>>>>>                break;
>>>>>            CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>>            for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>>                CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>>                          bytesperline);
>>>>> -        return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
>>>>> +        return ops->vidioc_s_fmt_vid_out_mplane ?
>>>>> +               ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
>>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>>            if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>>>>>                break;
>>>>> @@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct
>>>>> v4l2_ioctl_ops *ops,
>>>>>        return -EINVAL;
>>>>>    }
>>>>> +static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>>>> +                 struct file *file, void *fh, void *arg)
>>>>> +{
>>>>> +    struct video_device *vfd = video_devdata(file);
>>>>> +    struct v4l2_ext_pix_format *ef = arg;
>>>>> +    struct v4l2_format f;
>>>>> +    int ret = check_fmt(file, ef->type);
>>>>> +
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
>>>>> +
>>>>> +    switch (ef->type) {
>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_cap)
>>>>> +            return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
>>>>> +        break;
>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_out)
>>>>> +            return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
>>>>> +        break;
>>>>> +    default:
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    v4l2_ext_pix_format_to_format(ef, &f,
>>>>> V4L2_IS_CAP_MULTIPLANAR(vfd));
>>>>> +
>>>>> +    ret = v4l_s_fmt(ops, file, fh, &f);
>>>>> +    if (ret)
>>>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
>>>>
>>>> See my comments on this at the top.
>>>>
>>>>> +        return ret;
>>>>> +
>>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>>    static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>>>>                    struct file *file, void *fh, void *arg)
>>>>>    {
>>>>> @@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct
>>>>> v4l2_ioctl_ops *ops,
>>>>>        switch (p->type) {
>>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap))
>>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
>>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>>                break;
>>>>>            CLEAR_AFTER_FIELD(p, fmt.pix);
>>>>> -        ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
>>>>> +        ret = ops->vidioc_try_fmt_vid_cap ?
>>>>> +              ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>> VIDIOC_TRY_FMT);
>>>>>            /* just in case the driver zeroed it again */
>>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>            if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>>>                v4l_pix_format_touch(&p->fmt.pix);
>>>>>            return ret;
>>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
>>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
>>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>>                break;
>>>>>            CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>>            for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>>                CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>>                          bytesperline);
>>>>> -        return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
>>>>> +        return ops->vidioc_try_fmt_vid_cap_mplane ?
>>>>> +               ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
>>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>> +                         VIDIOC_TRY_FMT);
>>>>>        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>>            if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>>>>>                break;
>>>>> @@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct
>>>>> v4l2_ioctl_ops *ops,
>>>>>            CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>>>>            return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out))
>>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out &&
>>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>>                break;
>>>>>            CLEAR_AFTER_FIELD(p, fmt.pix);
>>>>> -        ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
>>>>> +        ret = ops->vidioc_try_fmt_vid_out ?
>>>>> +              ops->vidioc_try_fmt_vid_out(file, fh, arg) :
>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>> VIDIOC_TRY_FMT);
>>>>>            /* just in case the driver zeroed it again */
>>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>            return ret;
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
>>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
>>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>>                break;
>>>>>            CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>>            for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>>                CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>>                          bytesperline);
>>>>> -        return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
>>>>> +        return ops->vidioc_try_fmt_vid_out_mplane ?
>>>>> +               ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
>>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>> +                         VIDIOC_TRY_FMT);
>>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>>            if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>>>>>                break;
>>>>> @@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct
>>>>> v4l2_ioctl_ops *ops,
>>>>>        return -EINVAL;
>>>>>    }
>>>>> +static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>>>> +                   struct file *file, void *fh, void *arg)
>>>>> +{
>>>>> +    struct video_device *vfd = video_devdata(file);
>>>>> +    struct v4l2_ext_pix_format *ef = arg;
>>>>> +    struct v4l2_format f;
>>>>> +    int ret = check_fmt(file, ef->type);
>>>>> +
>>>>> +    if (ret)
>>>>> +        return ret;
>>>>> +
>>>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
>>>>> +
>>>>> +    switch (ef->type) {
>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_cap)
>>>>> +            return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>>>>> +                                   ef);
>>>>> +        break;
>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_out)
>>>>> +            return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>>>>> +                                   ef);
>>>>> +        break;
>>>>> +    default:
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    v4l2_ext_pix_format_to_format(ef, &f,
>>>>> V4L2_IS_CAP_MULTIPLANAR(vfd));
>>>>> +
>>>>> +    ret = v4l_try_fmt(ops, file, fh, &f);
>>>>> +    if (ret)
>>>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
>>>>> +        return ret;
>>>>> +
>>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>>>> +    return 0;
>>>>> +}
>>>>> +
>>>>>    static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
>>>>>                    struct file *file, void *fh, void *arg)
>>>>>    {
>>>>> @@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info
>>>>> v4l2_ioctls[] = {
>>>>>        IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands,
>>>>> v4l_print_freq_band, 0),
>>>>>        IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info,
>>>>> v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
>>>>>        IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl,
>>>>> v4l_print_query_ext_ctrl, INFO_FL_CTRL |
>>>>> INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
>>>>> +    IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt,
>>>>> v4l_print_ext_pix_format, 0),
>>>>> +    IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt,
>>>>> v4l_print_ext_pix_format, INFO_FL_PRIO),
>>>>> +    IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt,
>>>>> v4l_print_ext_pix_format, 0),
>>>>>    };
>>>>>    #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
>>>>> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
>>>>> index edb733f21604..c44708dc9355 100644
>>>>> --- a/include/media/v4l2-ioctl.h
>>>>> +++ b/include/media/v4l2-ioctl.h
>>>>> @@ -48,11 +48,17 @@ struct v4l2_fh;
>>>>>     * @vidioc_g_fmt_vid_cap: pointer to the function that implements
>>>>>     *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>>>>     *    in single plane mode
>>>>> + * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that
>>>>> implements
>>>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic
>>>>> for video
>>>>> + *    capture
>>>>>     * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
>>>>>     *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>>>     * @vidioc_g_fmt_vid_out: pointer to the function that implements
>>>>>     *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>>     *    in single plane mode
>>>>> + * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that
>>>>> implements
>>>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic
>>>>> for video
>>>>> + *    out
>>>>>     * @vidioc_g_fmt_vid_out_overlay: pointer to the function that
>>>>> implements
>>>>>     *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>> overlay output
>>>>>     * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
>>>>> @@ -82,11 +88,16 @@ struct v4l2_fh;
>>>>>     * @vidioc_s_fmt_vid_cap: pointer to the function that implements
>>>>>     *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>>>>     *    in single plane mode
>>>>> + * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that
>>>>> implements
>>>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic
>>>>> for video
>>>>> + *    capture
>>>>>     * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
>>>>>     *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>>>     * @vidioc_s_fmt_vid_out: pointer to the function that implements
>>>>>     *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>>     *    in single plane mode
>>>>> + * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that
>>>>> implements
>>>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for
>>>>> video out
>>>>>     * @vidioc_s_fmt_vid_out_overlay: pointer to the function that
>>>>> implements
>>>>>     *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>> overlay output
>>>>>     * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
>>>>> @@ -116,11 +127,16 @@ struct v4l2_fh;
>>>>>     * @vidioc_try_fmt_vid_cap: pointer to the function that implements
>>>>>     *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>> capture
>>>>>     *    in single plane mode
>>>>> + * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that
>>>>> implements
>>>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl
>>>>> logic for
>>>>> +    video capture
>>>>>     * @vidioc_try_fmt_vid_overlay: pointer to the function that
>>>>> implements
>>>>>     *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>> overlay
>>>>>     * @vidioc_try_fmt_vid_out: pointer to the function that implements
>>>>>     *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>>     *    in single plane mode
>>>>> + * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that
>>>>> implements
>>>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for
>>>>> video out
>>>>>     * @vidioc_try_fmt_vid_out_overlay: pointer to the function that
>>>>> implements
>>>>>     *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>> overlay
>>>>>     *    output
>>>>> @@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
>>>>>        /* VIDIOC_G_FMT handlers */
>>>>>        int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
>>>>>                        struct v4l2_format *f);
>>>>> +    int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>>        int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
>>>>>                        struct v4l2_format *f);
>>>>>        int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
>>>>>                        struct v4l2_format *f);
>>>>> +    int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>>        int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>>>                            struct v4l2_format *f);
>>>>>        int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
>>>>> @@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
>>>>>        /* VIDIOC_S_FMT handlers */
>>>>>        int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
>>>>>                        struct v4l2_format *f);
>>>>> +    int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>>        int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
>>>>>                        struct v4l2_format *f);
>>>>>        int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
>>>>>                        struct v4l2_format *f);
>>>>> +    int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>>        int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>>>                            struct v4l2_format *f);
>>>>>        int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
>>>>> @@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
>>>>>        /* VIDIOC_TRY_FMT handlers */
>>>>>        int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
>>>>>                          struct v4l2_format *f);
>>>>> +    int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>>>> +                          struct v4l2_ext_pix_format *ef);
>>>>>        int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
>>>>>                          struct v4l2_format *f);
>>>>>        int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
>>>>>                          struct v4l2_format *f);
>>>>> +    int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>>>> +                          struct v4l2_ext_pix_format *ef);
>>>>>        int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>>>                             struct v4l2_format *f);
>>>>>        int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
>>>>> diff --git a/include/uapi/linux/videodev2.h
>>>>> b/include/uapi/linux/videodev2.h
>>>>> index d9b7c9177605..a2d850513708 100644
>>>>> --- a/include/uapi/linux/videodev2.h
>>>>> +++ b/include/uapi/linux/videodev2.h
>>>>> @@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
>>>>>        __u8                reserved[7];
>>>>>    } __attribute__ ((packed));
>>>>> +/**
>>>>> + * struct v4l2_ext_pix_format - extended single/multiplanar format
>>>>> definition
>>>>> + * @type:        type of the data stream;
>>>>> V4L2_BUF_TYPE_VIDEO_CAPTURE or
>>>>> + *            V4L2_BUF_TYPE_VIDEO_OUTPUT
>>>>> + * @width:        image width in pixels
>>>>> + * @height:        image height in pixels
>>>>> + * @field:        enum v4l2_field; field order (for interlaced video)
>>>>> + * @plane_fmt:        per-plane information
>>>>> + * @pixelformat:    little endian four character code (fourcc)
>>>>> + * @modifier:        modifier applied to the format (used for tiled
>>>>> formats
>>>>> + *            and other kind of HW-specific formats, like compressed
>>>>> + *            formats) as defined in drm_fourcc.h
>>>>> + * @colorspace:        enum v4l2_colorspace; supplemental to
>>>>> pixelformat
>>>>> + * @xfer_func:        enum v4l2_xfer_func, colorspace transfer function
>>>>> + * @ycbcr_enc:        enum v4l2_ycbcr_encoding, Y'CbCr encoding
>>>>> + * @hsv_enc:        enum v4l2_hsv_encoding, HSV encoding
>>>>> + * @quantization:    enum v4l2_quantization, colorspace quantization
>>>>> + * @reserved:        extra space reserved for future fields, must be
>>>>> set to 0
>>>>> + */
>>>>> +struct v4l2_ext_pix_format {
>>>>> +    __u32 type;
>>>>> +    __u32 width;
>>>>> +    __u32 height;
>>>>> +    __u32 field;
>>>>> +    struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
>>>>> +    __u32 pixelformat;
>>>>> +    __u64 modifier;
>>>>> +    __u32 colorspace;
>>>>> +    __u32 xfer_func;
>>>>> +    union {
>>>>> +        __u32 ycbcr_enc;
>>>>> +        __u32 hsv_enc;
>>>>> +    };
>>>>> +    __u32 quantization;
>>>>> +    __u32 reserved[9];
>>>>> +};
>>>>> +
>>>>>    /**
>>>>>     * struct v4l2_sdr_format - SDR format definition
>>>>>     * @pixelformat:    little endian four character code (fourcc)
>>>>> @@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
>>>>>    #define VIDIOC_QUERY_EXT_CTRL    _IOWR('V', 103, struct
>>>>> v4l2_query_ext_ctrl)
>>>>> +#define VIDIOC_G_EXT_PIX_FMT    _IOWR('V', 104, struct
>>>>> v4l2_ext_pix_format)
>>>>> +#define VIDIOC_S_EXT_PIX_FMT    _IOWR('V', 105, struct
>>>>> v4l2_ext_pix_format)
>>>>> +#define VIDIOC_TRY_EXT_PIX_FMT    _IOWR('V', 106, struct
>>>>> v4l2_ext_pix_format)
>>>>> +
>>>>>    /* Reminder: when adding new ioctls please add support for them to
>>>>>       drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
>>>>>
> 
> --
> Regards,
> 
> Laurent Pinchart

-- 
Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-06 22:11         ` Dmitry Osipenko
@ 2022-11-07  2:04           ` Hsia-Jun Li
  2022-11-07  8:30           ` Laurent Pinchart
  2022-11-07 16:50           ` Fritz Koenig
  2 siblings, 0 replies; 48+ messages in thread
From: Hsia-Jun Li @ 2022-11-07  2:04 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga,
	Hans Verkuil, Helen Koike, linux-media



On 11/7/22 06:11, Dmitry Osipenko wrote:
> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> On 11/5/22 18:19, Hsia-Jun Li wrote:
>> Hello Helen
>>
>> I didn't see any updates from V6 and V7-WIP in your repo. That is what I
>> need to for our complex tile formats in our platform.
>>
>> Any future plane here?
>>
>> Besides I have some ideas on these patches.
> 
> I was looking into updating this patchset few months ago and the biggest
> blocker was the absence of immediate upstream user for this new UAPI.
> What your platform is? 
Synaptics VideoSmart
https://www.synaptics.com/products/multimedia-solutions

Is the driver stack completely opensource?
If you don't include the trusted application(firmware) then yes.
I can't post it in a short time, because the firmware is not released 
yet. While the v4l2 still lacks many features I need here. If I release 
it in a short time, you would just get a version that outputs non-tile 
linear pixel formats here, the tile formats were only used for internal 
buffers.

Besides this TEE in our platform is not optee, even I posted it I don't 
think it could be merged in a short time.
> 
> --
> Best regards,
> Dmitry
> 

-- 
Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-07  1:54           ` Hsia-Jun Li
@ 2022-11-07  8:28             ` Laurent Pinchart
  2022-11-07  8:49               ` Hsia-Jun Li
  0 siblings, 1 reply; 48+ messages in thread
From: Laurent Pinchart @ 2022-11-07  8:28 UTC (permalink / raw)
  To: Hsia-Jun Li
  Cc: Helen Koike, mchehab, hans.verkuil, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga,
	Hans Verkuil, linux-media

Hi Hsia-Jun,

On Mon, Nov 07, 2022 at 09:54:11AM +0800, Hsia-Jun Li wrote:
> On 11/7/22 03:24, Laurent Pinchart wrote:
> > On Sat, Nov 05, 2022 at 11:19:10PM +0800, Hsia-Jun Li wrote:
> >> Hello Helen
> >>
> >> I didn't see any updates from V6 and V7-WIP in your repo. That is what I
> >> need to for our complex tile formats in our platform.
> >>
> >> Any future plane here?
> >>
> >> Besides I have some ideas on these patches.
> >>
> >> On 2/24/21 23:12, Helen Koike wrote:
> >>> Hi Hans,
> >>>
> >>> Thank you for your comment, please see my reply below.
> >>>
> >>> On 2/23/21 9:35 AM, Hans Verkuil wrote:
> >>>> Hi Helen,
> >>>>
> >>>> On 14/01/2021 19:07, Helen Koike wrote:
> >>>>> This is part of the multiplanar and singleplanar unification process.
> >>>>> v4l2_ext_pix_format is supposed to work for both cases.
> >>>>>
> >>>>> We also add the concept of modifiers already employed in DRM to expose
> >>>>> HW-specific formats (like tiled or compressed formats) and allow
> >>>>> exchanging this information with the DRM subsystem in a consistent way.
> >>>>>
> >>>>> Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
> >>>>> v4l2_ext_format, other types will be rejected if you use the
> >>>>> {G,S,TRY}_EXT_PIX_FMT ioctls.
> >>>>>
> >>>>> New hooks have been added to v4l2_ioctl_ops to support those new ioctls
> >>>>> in drivers, but, in the meantime, the core takes care of converting
> >>>>> {S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers
> >>>>> can
> >>>>> still work if the userspace app/lib uses the new ioctls.
> >>>>>
> >>>>> The conversion is also done the other around to allow userspace
> >>>>> apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
> >>>>> _ext_ hooks.
> >>>>
> >>>> I have some small comments below, but also one high level comment:
> >>>>
> >>>> Regarding M variants of pixelformats: this patch 'normalizes' them to
> >>>> regular pixelformats in the extended API. This makes life complicated,
> >>>> and I wonder if this is the right approach.
> >>>>
> >>>> Currently there are two reasons for a driver to support e.g. NV12M:
> >>>> either luma and chroma need to be in two memory banks, or the luma
> >>>> and chroma planes cannot be contiguous due to alignment requirements.
> >>>>
> >>>> The first requirement is still valid for drivers that support the
> >>>> extended API.
> >>>> The second requirement is no longer a reason to support NV12M. But I
> >>>> don't think we should just drop NV12M support if it was already supported
> >>>> before the conversion to this extended API. Since NV12M allocates two
> >>>> buffers
> >>>> instead of one, it is still different from a regular NV12.
> >>>
> >>> I don't see what would be the difference when using NV12 and NV12M in
> >>> Ext API.
> >>> NV12 could be used for both requirements. It would even allow the second
> >>> requirement with a single memory buffer.
> >>>
> >> Although I don't have problem here to support both single and multiple
> >> planes in our hardware. But using the single plane format here is not
> >> convience for us.
> >>
> >> The tile format in our platform is little complex, you can't calculate
> >> the stride and sizeimage easily with just the modifier, width and
> >> height. Then we don't have a good place to record the offset here.
> > 
> > What else is needed to compute those values ? Can they be computed by
> > the kernel driver, or do they have computed by userspace ?
> 
> I could calculate the stride(bytesperline) and sizeimage in the kernel 
> driver. But it would be better to let the firmware do that. Our driver 
> would depend on the V4L2_EVENT_SOURCE_CHANGE event.

It could indeed be done in the firmware, but that would mean that the
device would need to be powered up to implement VIDIOC_S_FMT and
VIDIOC_TRY_FMT. The uvcvideo driver does that, which results in very
slow startup for applications that try lots of formats. It would be best
to avoid it if possible, and calculate the values in software in the
kernel.

> >> Besides, the luma and chroma planes would always need their own
> >> compression meta data planes. If we use NV12 here, that would make a
> >> very strange pixel format, one plane for the pixel datas and two planes
> >> for the meta data.
> >>
> >>>> I would prefer that such drivers support both NV12 and NV12M, so no
> >>>> automatic conversion.
> >>>>
> >>>> A related question is how to handle pixelformat enumeration: with the
> >>>> extended API an NV12 format might work, but not with the old API (e.g.
> >>>> due to memory alignment requirements). I wonder if a
> >>>> VIDIOC_ENUM_EXT_PIX_FMT
> >>>> isn't needed.
> >>>
> >>> We need VIDIOC_ENUM_EXT_PIX_FMT for modifiers, but we can add it later.
> >>> If the driver reports NV12M, userspace can use it and the framework
> >>> normilizes it.
> >>>
> >>>>
> >>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
> >>>> VIDIOC_ENUM_FMT
> >>>> would just report NV12M.
> >>>
> >>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
> >>> report both (unless I'm missing something, which is probably the case).
> >>>
> >>> The idea was to deprecate the M-variants one day.
> >>
> >> I was thinking the way in DRM API is better, always assuming it would
> >> always in a multiple planes. The only problem is we don't have a way to
> >> let the allocator that allocate contiguous memory for planes when we
> >> need to do that.
> >>
> >>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> >>>>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
> >>>>> ---
> >>>>>
> >>>>> Changes in v6:
> >>>>>    The main change here was fixing the conversion, so planes reflects
> >>>>> color planes,
> >>>>>    and to implement this properly I made major refactors compared to
> >>>>> the previous
> >>>>>    version.
> >>>>> - struct v4l2_plane_ext_pix_format removed, using struct
> >>>>> v4l2_plane_pix_format instead (Tomasz)
> >>>>> - refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
> >>>>> - reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
> >>>>> - do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
> >>>>> - refactor conversion functions, so planes are color planes (Tomasz)
> >>>>> - Don't explicitly check for e->modifier != 0 in
> >>>>> v4l2_ext_pix_format_to_format() (Tomasz)
> >>>>> - Use "ef" for extended formats in the framework for consistency
> >>>>> (Tomasz)
> >>>>> - Handle xfer_func field in conversions (Tomasz)
> >>>>> - Zero reserved fields in v4l_s_ext_pix_fmt() and
> >>>>> v4l_try_ext_pix_fmt() (Tomasz)
> >>>>> - Refactor format functions to use v4l_fmt_ioctl_via_ext()
> >>>>> - Several fixes/refactoring/changes
> >>>>> - Remove EXT API for touch devices
> >>>>>
> >>>>> Changes in v5:
> >>>>> - change sizes and reorder fields to avoid holes in the struct and make
> >>>>>     it the same for 32 and 64 bits
> >>>>> - removed __attribute__ ((packed)) from uapi structs
> >>>>> - Fix doc warning from make htmldocs
> >>>>> - Updated commit message with EXT_PIX prefix for the ioctls.
> >>>>>
> >>>>> Changes in v4:
> >>>>> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
> >>>>> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
> >>>>> - Add reserved fields
> >>>>> - Removed num_planes from struct v4l2_ext_pix_format
> >>>>> - Removed flag field from struct v4l2_ext_pix_format, since the only
> >>>>>     defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
> >>>>>     where we can use modifiers, or add it back later through the reserved
> >>>>>     bits.
> >>>>> - In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
> >>>>>     != MOD_INVALID
> >>>>> - Fix type assignment in v4l_g_fmt_ext_pix()
> >>>>> - Rebased on top of media/master (post 5.8-rc1)
> >>>>>
> >>>>> Changes in v3:
> >>>>> - Rebased on top of media/master (post 5.4-rc1)
> >>>>>
> >>>>> Changes in v2:
> >>>>> - Move the modifier in v4l2_ext_format (was formerly placed in
> >>>>>     v4l2_ext_plane)
> >>>>> - Fix a few bugs in the converters and add a strict parameter to
> >>>>>     allow conversion of uninitialized/mis-initialized objects
> >>>>> ---
> >>>>>    drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
> >>>>>    drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
> >>>>>    include/media/v4l2-ioctl.h           |  28 ++
> >>>>>    include/uapi/linux/videodev2.h       |  41 ++
> >>>>>    4 files changed, 602 insertions(+), 32 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c
> >>>>> b/drivers/media/v4l2-core/v4l2-dev.c
> >>>>> index f9cff033d0dc..5add58cb6d45 100644
> >>>>> --- a/drivers/media/v4l2-core/v4l2-dev.c
> >>>>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> >>>>> @@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct
> >>>>> video_device *vdev)
> >>>>>                       ops->vidioc_enum_fmt_vid_overlay)) ||
> >>>>>                (is_tx && ops->vidioc_enum_fmt_vid_out))
> >>>>>                set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
> >>>>> +        if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
> >>>>> +            (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
> >>>>> +             set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
> >>>>>            if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
> >>>>>                       ops->vidioc_g_fmt_vid_cap_mplane ||
> >>>>> -                   ops->vidioc_g_fmt_vid_overlay)) ||
> >>>>> +                   ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
> >>>>>                (is_tx && (ops->vidioc_g_fmt_vid_out ||
> >>>>>                       ops->vidioc_g_fmt_vid_out_mplane ||
> >>>>> -                   ops->vidioc_g_fmt_vid_out_overlay)))
> >>>>> +                   ops->vidioc_g_ext_pix_fmt_vid_out))) {
> >>>>>                 set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
> >>>>> +             set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
> >>>>> +        }
> >>>>> +        if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
> >>>>> +            (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
> >>>>> +             set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
> >>>>>            if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
> >>>>>                       ops->vidioc_s_fmt_vid_cap_mplane ||
> >>>>> -                   ops->vidioc_s_fmt_vid_overlay)) ||
> >>>>> +                   ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
> >>>>>                (is_tx && (ops->vidioc_s_fmt_vid_out ||
> >>>>>                       ops->vidioc_s_fmt_vid_out_mplane ||
> >>>>> -                   ops->vidioc_s_fmt_vid_out_overlay)))
> >>>>> +                   ops->vidioc_s_ext_pix_fmt_vid_out))) {
> >>>>>                 set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
> >>>>> +             set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
> >>>>> +        }
> >>>>> +        if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
> >>>>> +            (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
> >>>>> +             set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
> >>>>>            if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
> >>>>>                       ops->vidioc_try_fmt_vid_cap_mplane ||
> >>>>> -                   ops->vidioc_try_fmt_vid_overlay)) ||
> >>>>> +                   ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
> >>>>>                (is_tx && (ops->vidioc_try_fmt_vid_out ||
> >>>>>                       ops->vidioc_try_fmt_vid_out_mplane ||
> >>>>> -                   ops->vidioc_try_fmt_vid_out_overlay)))
> >>>>> +                   ops->vidioc_try_ext_pix_fmt_vid_out))) {
> >>>>>                 set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
> >>>>> +             set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
> >>>>> +        }
> >>>>>            SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
> >>>>>            SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
> >>>>>            SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
> >>>>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
> >>>>> b/drivers/media/v4l2-core/v4l2-ioctl.c
> >>>>> index 848286a284f6..a9c07c0a73ec 100644
> >>>>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> >>>>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> >>>>> @@ -18,6 +18,8 @@
> >>>>>    #include <linux/videodev2.h>
> >>>>> +#include <drm/drm_fourcc.h>
> >>>>> +
> >>>>>    #include <media/v4l2-common.h>
> >>>>>    #include <media/v4l2-ioctl.h>
> >>>>>    #include <media/v4l2-ctrls.h>
> >>>>> @@ -38,6 +40,11 @@
> >>>>>    #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd),
> >>>>> (vfd)->valid_ioctls)
> >>>>> +#define V4L2_IS_CAP_MULTIPLANAR(vdev)    (vdev->device_caps & \
> >>>>> +                     (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
> >>>>> +                     V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
> >>>>> +                     V4L2_CAP_VIDEO_M2M_MPLANE))
> >>>>> +
> >>>>>    struct std_descr {
> >>>>>        v4l2_std_id std;
> >>>>>        const char *descr;
> >>>>> @@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg,
> >>>>> bool write_only)
> >>>>>        }
> >>>>>    }
> >>>>> +static void v4l_print_ext_pix_format(const void *arg, bool write_only)
> >>>>> +{
> >>>>> +    const struct v4l2_ext_pix_format *ef = arg;
> >>>>> +    unsigned int i;
> >>>>> +
> >>>>> +    pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier
> >>>>> %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u,
> >>>>> xfer_func=%u\n",
> >>>>> +        prt_names(ef->type, v4l2_type_names),
> >>>>> +        ef->width, ef->height,
> >>>>> +        (ef->pixelformat & 0xff),
> >>>>> +        (ef->pixelformat >>  8) & 0xff,
> >>>>> +        (ef->pixelformat >> 16) & 0xff,
> >>>>> +        (ef->pixelformat >> 24) & 0xff,
> >>>>> +        ef->modifier, prt_names(ef->field, v4l2_field_names),
> >>>>> +        ef->colorspace, ef->ycbcr_enc,
> >>>>> +        ef->quantization, ef->xfer_func);
> >>>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage;
> >>>>> i++)
> >>>>> +        pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
> >>>>> +             i, ef->plane_fmt[i].bytesperline,
> >>>>> +             ef->plane_fmt[i].sizeimage);
> >>>>> +}
> >>>>> +
> >>>>>    static void v4l_print_framebuffer(const void *arg, bool write_only)
> >>>>>    {
> >>>>>        const struct v4l2_framebuffer *p = arg;
> >>>>> @@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum
> >>>>> v4l2_buf_type type)
> >>>>>        switch (type) {
> >>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>>>            if ((is_vid || is_tch) && is_rx &&
> >>>>> -            (ops->vidioc_g_fmt_vid_cap ||
> >>>>> ops->vidioc_g_fmt_vid_cap_mplane))
> >>>>> +            (ops->vidioc_g_fmt_vid_cap ||
> >>>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap ||
> >>>>> +             ops->vidioc_g_fmt_vid_cap_mplane))
> >>>>>                return 0;
> >>>>>            break;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>>>> -        if ((is_vid || is_tch) && is_rx &&
> >>>>> ops->vidioc_g_fmt_vid_cap_mplane)
> >>>>> +        if ((is_vid || is_tch) && is_rx &&
> >>>>> +            (ops->vidioc_g_fmt_vid_cap_mplane ||
> >>>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap))
> >>>>>                return 0;
> >>>>>            break;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >>>>> @@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum
> >>>>> v4l2_buf_type type)
> >>>>>            break;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>>>            if (is_vid && is_tx &&
> >>>>> -            (ops->vidioc_g_fmt_vid_out ||
> >>>>> ops->vidioc_g_fmt_vid_out_mplane))
> >>>>> +            (ops->vidioc_g_fmt_vid_out ||
> >>>>> +             ops->vidioc_g_ext_pix_fmt_vid_out ||
> >>>>> +             ops->vidioc_g_fmt_vid_out_mplane))
> >>>>>                return 0;
> >>>>>            break;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>>>> -        if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
> >>>>> +        if (is_vid && is_tx &&
> >>>>> +            (ops->vidioc_g_ext_pix_fmt_vid_out ||
> >>>>> +             ops->vidioc_g_fmt_vid_out_mplane))
> >>>>>                return 0;
> >>>>>            break;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >>>>> @@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct
> >>>>> v4l2_format *fmt)
> >>>>>               sizeof(fmt->fmt.pix) - offset);
> >>>>>    }
> >>>>> +static void
> >>>>> +v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
> >>>>> +                  struct v4l2_pix_format *pix)
> >>>>> +{
> >>>>> +    unsigned int i;
> >>>>> +
> >>>>> +    pix->width = ef->width;
> >>>>> +    pix->height = ef->height;
> >>>>> +    pix->field = ef->field;
> >>>>> +    pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
> >>>>> +    pix->colorspace = ef->colorspace;
> >>>>> +    pix->ycbcr_enc = ef->ycbcr_enc;
> >>>>> +    pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>>> +    pix->quantization = ef->quantization;
> >>>>> +    pix->pixelformat = ef->pixelformat;
> >>>>> +    pix->bytesperline = ef->plane_fmt[0].bytesperline;
> >>>>> +    pix->sizeimage = ef->plane_fmt[0].sizeimage;
> >>>>> +    for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage;
> >>>>> i++)
> >>>>> +        pix->sizeimage += ef->plane_fmt[i].sizeimage;
> >>>>> +}
> >>>>> +
> >>>>> +static void
> >>>>> +v4l2_ext_pix_format_to_pix_mp_format(const struct
> >>>>> v4l2_ext_pix_format *ef,
> >>>>> +                     struct v4l2_pix_format_mplane *pix_mp)
> >>>>> +{
> >>>>> +    const struct v4l2_format_info *info =
> >>>>> +                    v4l2_format_info(ef->pixelformat);
> >>>>> +    unsigned int i;
> >>>>> +
> >>>>> +    pix_mp->width = ef->width;
> >>>>> +    pix_mp->height = ef->height;
> >>>>> +    pix_mp->field = ef->field;
> >>>>> +    pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
> >>>>> +    pix_mp->colorspace = ef->colorspace;
> >>>>> +    pix_mp->ycbcr_enc = ef->ycbcr_enc;
> >>>>> +    pix_mp->quantization = ef->quantization;
> >>>>> +    pix_mp->pixelformat = ef->pixelformat;
> >>>>> +
> >>>>> +    /* This is true when converting to non-M-variant */
> >>>>> +    if (info && info->mem_planes == 1) {
> >>>>> +        pix_mp->plane_fmt[0] = ef->plane_fmt[0];
> >>>>> +        pix_mp->num_planes = 1;
> >>>>> +        for (i = 1; i < VIDEO_MAX_PLANES &&
> >>>>> ef->plane_fmt[i].sizeimage; i++)
> >>>>> +            pix_mp->plane_fmt[0].sizeimage +=
> >>>>> ef->plane_fmt[i].sizeimage;
> >>>>> +
> >>>>> +        return;
> >>>>> +    }
> >>>>> +
> >>>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage;
> >>>>> i++)
> >>>>> +        pix_mp->plane_fmt[i] = ef->plane_fmt[i];
> >>>>> +    pix_mp->num_planes = i;
> >>>>> +}
> >>>>> +
> >>>>> +/*
> >>>>> + * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to
> >>>>> v4l2_format
> >>>>> + *
> >>>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
> >>>>> + * @f: A pointer to struct v4l2_format to be filled.
> >>>>> + * @is_mplane: Bool indicating if multiplanar API should be used in @f.
> >>>>> + *
> >>>>> + * If pixelformat should be converted to M-variant, change
> >>>>> ef->pixelformat
> >>>>> + * to the M-variant before calling this function.
> >>>>> + */
> >>>>> +static void v4l2_ext_pix_format_to_format(const struct
> >>>>> v4l2_ext_pix_format *ef,
> >>>>> +                      struct v4l2_format *f, bool is_mplane)
> >>>>> +{
> >>>>> +    memset(f, 0, sizeof(*f));
> >>>>> +
> >>>>> +    if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
> >>>>> +        ef->modifier != DRM_FORMAT_MOD_INVALID)
> >>>>> +        pr_warn("Modifiers are not supported in v4l2_format,
> >>>>> ignoring %llx\n",
> >>>>> +            ef->modifier);
> >>>>> +
> >>>>> +    if (!is_mplane) {
> >>>>> +        f->type = ef->type;
> >>>>> +        v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
> >>>>> +        return;
> >>>>> +    }
> >>>>> +
> >>>>> +    if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> >>>>> +        f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> >>>>> +    else
> >>>>> +        f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> >>>>> +
> >>>>> +    v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
> >>>>> +}
> >>>>> +
> >>>>> +static void
> >>>>> +v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
> >>>>> +                  struct v4l2_ext_pix_format *ef)
> >>>>> +{
> >>>>> +    const struct v4l2_format_info *info =
> >>>>> +                    v4l2_format_info(pix->pixelformat);
> >>>>> +    unsigned int i;
> >>>>> +
> >>>>> +    ef->width = pix->width;
> >>>>> +    ef->height = pix->height;
> >>>>> +    ef->field = pix->field;
> >>>>> +    ef->colorspace = pix->colorspace;
> >>>>> +    ef->ycbcr_enc = pix->ycbcr_enc;
> >>>>> +    ef->quantization = pix->quantization;
> >>>>> +    ef->xfer_func = pix->xfer_func;
> >>>>> +    if (pix->flags)
> >>>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
> >>>>> +
> >>>>> +    /* We assume M-variants won't be used in this function */
> >>>>> +    ef->pixelformat = pix->pixelformat;
> >>>>> +
> >>>>> +    ef->plane_fmt[0].bytesperline = pix->bytesperline;
> >>>>> +    ef->plane_fmt[0].sizeimage = pix->sizeimage;
> >>>>> +
> >>>>> +    if (!info)
> >>>>> +        return;
> >>>>> +
> >>>>> +    for (i = 1; i < info->comp_planes; i++) {
> >>>>> +        ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
> >>>>> +        ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
> >>>>> +                         ef->height / info->vdiv;
> >>>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
> >>>>> +    }
> >>>>> +}
> >>>>> +
> >>>>> +static void
> >>>>> +v4l2_pix_mp_format_to_ext_pix_format(const struct
> >>>>> v4l2_pix_format_mplane *pix_mp,
> >>>>> +                     struct v4l2_ext_pix_format *ef)
> >>>>> +{
> >>>>> +    const struct v4l2_format_info *info =
> >>>>> +                    v4l2_format_info(pix_mp->pixelformat);
> >>>>> +    unsigned int i;
> >>>>> +
> >>>>> +    ef->width = pix_mp->width;
> >>>>> +    ef->height = pix_mp->height;
> >>>>> +    ef->field = pix_mp->field;
> >>>>> +    ef->colorspace = pix_mp->colorspace;
> >>>>> +    ef->ycbcr_enc = pix_mp->ycbcr_enc;
> >>>>> +    ef->quantization = pix_mp->quantization;
> >>>>> +    ef->xfer_func = pix_mp->xfer_func;
> >>>>> +    if (pix_mp->flags)
> >>>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
> >>>>> +
> >>>>> +    if (!info)
> >>>>> +        return;
> >>>>> +
> >>>>> +    ef->pixelformat = info && info->norm ?
> >>>>
> >>>> 'info &&' can be dropped, info is always non-NULL here.
> >>>>
> >>>>> +              info->norm : pix_mp->pixelformat;
> >>>>> +
> >>>>> +    if (info->comp_planes == info->mem_planes) {
> >>>>> +        for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES;
> >>>>> i++)
> >>>>> +            ef->plane_fmt[i] = pix_mp->plane_fmt[i];
> >>>>> +
> >>>>> +        return;
> >>>>> +    }
> >>>>> +
> >>>>> +    /* case where mem_planes is 1 and comp_planes > 1 */
> >>>>> +    ef->plane_fmt[0] = pix_mp->plane_fmt[0];
> >>>>> +    for (i = 1; i < info->comp_planes; i++) {
> >>>>> +        ef->plane_fmt[i].bytesperline =
> >>>>> +            pix_mp->plane_fmt[0].bytesperline / info->hdiv;
> >>>>> +        ef->plane_fmt[i].sizeimage =
> >>>>> +            ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
> >>>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
> >>>>> +    }
> >>>>> +}
> >>>>> +
> >>>>> +/*
> >>>>> + * v4l2_format_to_ext_pix_format - convert to v4l2_format to
> >>>>> v4l2_ext_pix_format
> >>>>> + *
> >>>>> + * @f: A pointer to struct v4l2_format to be converted.
> >>>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
> >>>>> + *
> >>>>> + * This method normalize the pixelformat to non-M variant.
> >>>>> + */
> >>>>> +static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
> >>>>> +                      struct v4l2_ext_pix_format *ef)
> >>>>> +{
> >>>>> +    memset(ef, 0, sizeof(*ef));
> >>>>> +
> >>>>> +    switch (f->type) {
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>>> +        ef->type = f->type;
> >>>>> +        v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
> >>>>> +        break;
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> >>>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
> >>>>> +        break;
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> >>>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
> >>>>> +        break;
> >>>>> +    default:
> >>>>> +        WARN("Converting to Ext Pix Format with wrong buffer type
> >>>>> %s\n",
> >>>>> +             prt_names(f->type, v4l2_type_names));
> >>>>> +        break;
> >>>>> +    }
> >>>>> +}
> >>>>> +
> >>>>>    static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
> >>>>>                    struct file *file, void *fh, void *arg)
> >>>>>    {
> >>>>> @@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct
> >>>>> v4l2_pix_format *p)
> >>>>>        p->xfer_func = 0;
> >>>>>    }
> >>>>> +static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
> >>>>> +                 struct file *file, void *fh,
> >>>>> +                 struct v4l2_format *f,
> >>>>> +                 unsigned int ioctl)
> >>>>> +{
> >>>>> +    bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
> >>>>> +    struct video_device *vdev = video_devdata(file);
> >>>>> +    struct v4l2_ext_pix_format ef = {0};
> >>>>> +    u32 original_pixfmt = 0;
> >>>>> +    u32 cap_mask;
> >>>>> +    int ret;
> >>>>> +
> >>>>> +    if (ioctl != VIDIOC_G_FMT) {
> >>>>> +        /*
> >>>>> +         * If CSC attributes are read only, set them to DEFAULT
> >>>>> +         * to avoid changes by the driver.
> >>>>> +         */
> >>>>> +        if (is_multiplanar) {
> >>>>> +            if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
> >>>>> +                f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
> >>>>> +                f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> >>>>> +                f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
> >>>>> +                f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
> >>>>> +            }
> >>>>> +            /* Unset the flag to avoid warning in the convertion */
> >>>>> +            f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
> >>>>> +
> >>>>> +            /* Save pixelformat in case M-variant is being used */
> >>>>> +            original_pixfmt = f->fmt.pix_mp.pixelformat;
> >>>>> +        } else {
> >>>>> +            if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
> >>>>> +                f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
> >>>>> +                f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> >>>>> +                f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
> >>>>> +                f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
> >>>>> +            }
> >>>>> +            /* Unset the flag to avoid warning in the convertion */
> >>>>> +            f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
> >>>>> +        }
> >>>>> +        v4l2_format_to_ext_pix_format(f, &ef);
> >>>>> +    }
> >>>>> +
> >>>>> +    switch (f->type) {
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>>>> +        cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> >>>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
> >>>>> +        if (!!(vdev->device_caps & cap_mask) !=
> >>>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
> >>>>> +            return -EINVAL;
> >>>>> +
> >>>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> >>>>> +        if (ioctl == VIDIOC_G_FMT)
> >>>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
> >>>>> +        else if (ioctl == VIDIOC_S_FMT)
> >>>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
> >>>>> +        else
> >>>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
> >>>>> +                                  &ef);
> >>>>> +        break;
> >>>>> +
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>>>> +        cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> >>>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
> >>>>> +        if (!!(vdev->device_caps & cap_mask) !=
> >>>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
> >>>>> +            return -EINVAL;
> >>>>> +
> >>>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> >>>>> +        if (ioctl == VIDIOC_G_FMT)
> >>>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
> >>>>> +        else if (ioctl == VIDIOC_S_FMT)
> >>>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
> >>>>> +        else
> >>>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
> >>>>> +                                  &ef);
> >>>>> +        break;
> >>>>> +
> >>>>> +    default:
> >>>>> +        return -EINVAL;
> >>>>> +    }
> >>>>> +
> >>>>> +    if (ret)
> >>>>> +        return ret;
> >>>>> +
> >>>>> +    if (original_pixfmt != ef.pixelformat &&
> >>>>> +        v4l2_format_info(original_pixfmt))
> >>>>
> >>>> Could this test be simplified to: 'if (original_pixfmt)'?
> >>>>
> >>>> I.e., if the original pixfmt was saved, then restore it here.
> >>>>
> >>>>> +        ef.pixelformat = original_pixfmt;
> >>>>> +
> >>>>> +    v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
> >>>>> +    return 0;
> >>>>> +}
> >>>>> +
> >>>>>    static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
> >>>>>                    struct file *file, void *fh, void *arg)
> >>>>>    {
> >>>>> @@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct
> >>>>> v4l2_ioctl_ops *ops,
> >>>>>        switch (p->type) {
> >>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_cap))
> >>>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
> >>>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_cap))
> >>>>>                break;
> >>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>>> -        ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
> >>>>> +        ret = ops->vidioc_g_fmt_vid_cap ?
> >>>>> +              ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
> >>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>>>> +                        VIDIOC_G_FMT);
> >>>>>            /* just in case the driver zeroed it again */
> >>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>>>            if (vfd->vfl_type == VFL_TYPE_TOUCH)
> >>>>>                v4l_pix_format_touch(&p->fmt.pix);
> >>>>>            return ret;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>>>> -        return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
> >>>>> +        if (ops->vidioc_g_fmt_vid_cap_mplane)
> >>>>> +            return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
> >>>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_cap)
> >>>>
> >>>> 'else' can be dropped.
> >>>>
> >>>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>>>> +                             VIDIOC_G_FMT);
> >>>>> +        break;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >>>>>            return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
> >>>>>        case V4L2_BUF_TYPE_VBI_CAPTURE:
> >>>>> @@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct
> >>>>> v4l2_ioctl_ops *ops,
> >>>>>        case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> >>>>>            return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_out))
> >>>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_out &&
> >>>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_out))
> >>>>>                break;
> >>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>>> -        ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
> >>>>> +        ret = ops->vidioc_g_fmt_vid_out ?
> >>>>> +              ops->vidioc_g_fmt_vid_out(file, fh, arg) :
> >>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
> >>>>>            /* just in case the driver zeroed it again */
> >>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>>>            return ret;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>>>> -        return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
> >>>>> +        if (ops->vidioc_g_fmt_vid_out_mplane)
> >>>>> +            return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
> >>>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_out)
> >>>>
> >>>> Ditto.
> >>>>
> >>>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>>>> +                             VIDIOC_G_FMT);
> >>>>> +        break;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >>>>>            return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
> >>>>>        case V4L2_BUF_TYPE_VBI_OUTPUT:
> >>>>> @@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct
> >>>>> v4l2_ioctl_ops *ops,
> >>>>>        return -EINVAL;
> >>>>>    }
> >>>>> +static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> >>>>> +                 struct file *file, void *fh, void *arg)
> >>>>> +{
> >>>>> +    struct v4l2_ext_pix_format *ef = arg;
> >>>>> +    struct v4l2_format f = {
> >>>>> +        .type = ef->type,
> >>>>> +    };
> >>>>> +    int ret = check_fmt(file, ef->type);
> >>>>> +
> >>>>> +    if (ret)
> >>>>> +        return ret;
> >>>>> +
> >>>>> +    memset(ef, 0, sizeof(*ef));
> >>>>> +    ef->type = f.type;
> >>>>> +
> >>>>> +    switch (f.type) {
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_cap)
> >>>>> +            return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
> >>>>> +        break;
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_out)
> >>>>> +            return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
> >>>>> +        break;
> >>>>> +    default:
> >>>>> +        return -EINVAL;
> >>>>> +    }
> >>>>> +
> >>>>> +    ret = v4l_g_fmt(ops, file, fh, &f);
> >>>>> +    if (ret)
> >>>>> +        return ret;
> >>>>> +
> >>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
> >>>>> +    return 0;
> >>>>> +}
> >>>>> +
> >>>>>    static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
> >>>>>                    struct file *file, void *fh, void *arg)
> >>>>>    {
> >>>>> @@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct
> >>>>> v4l2_ioctl_ops *ops,
> >>>>>        switch (p->type) {
> >>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap))
> >>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
> >>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
> >>>>>                break;
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.pix);
> >>>>> -        ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
> >>>>> +        ret = ops->vidioc_s_fmt_vid_cap ?
> >>>>> +              ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
> >>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
> >>>>>            /* just in case the driver zeroed it again */
> >>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>>>            if (vfd->vfl_type == VFL_TYPE_TOUCH)
> >>>>>                v4l_pix_format_touch(&p->fmt.pix);
> >>>>>            return ret;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
> >>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
> >>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
> >>>>>                break;
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>>>>            for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
> >>>>>                CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
> >>>>>                          bytesperline);
> >>>>> -        return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
> >>>>> +        return ops->vidioc_s_fmt_vid_cap_mplane ?
> >>>>> +               ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
> >>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>>>> VIDIOC_S_FMT);
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >>>>>            if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
> >>>>>                break;
> >>>>> @@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct
> >>>>> v4l2_ioctl_ops *ops,
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
> >>>>>            return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out))
> >>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out &&
> >>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
> >>>>>                break;
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.pix);
> >>>>> -        ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
> >>>>> +        ret = ops->vidioc_s_fmt_vid_out ?
> >>>>> +              ops->vidioc_s_fmt_vid_out(file, fh, arg) :
> >>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
> >>>>>            /* just in case the driver zeroed it again */
> >>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>>>            return ret;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
> >>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
> >>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
> >>>>>                break;
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>>>>            for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
> >>>>>                CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
> >>>>>                          bytesperline);
> >>>>> -        return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
> >>>>> +        return ops->vidioc_s_fmt_vid_out_mplane ?
> >>>>> +               ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
> >>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >>>>>            if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
> >>>>>                break;
> >>>>> @@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct
> >>>>> v4l2_ioctl_ops *ops,
> >>>>>        return -EINVAL;
> >>>>>    }
> >>>>> +static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> >>>>> +                 struct file *file, void *fh, void *arg)
> >>>>> +{
> >>>>> +    struct video_device *vfd = video_devdata(file);
> >>>>> +    struct v4l2_ext_pix_format *ef = arg;
> >>>>> +    struct v4l2_format f;
> >>>>> +    int ret = check_fmt(file, ef->type);
> >>>>> +
> >>>>> +    if (ret)
> >>>>> +        return ret;
> >>>>> +
> >>>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
> >>>>> +
> >>>>> +    switch (ef->type) {
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_cap)
> >>>>> +            return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
> >>>>> +        break;
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_out)
> >>>>> +            return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
> >>>>> +        break;
> >>>>> +    default:
> >>>>> +        return -EINVAL;
> >>>>> +    }
> >>>>> +
> >>>>> +    v4l2_ext_pix_format_to_format(ef, &f,
> >>>>> V4L2_IS_CAP_MULTIPLANAR(vfd));
> >>>>> +
> >>>>> +    ret = v4l_s_fmt(ops, file, fh, &f);
> >>>>> +    if (ret)
> >>>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
> >>>>
> >>>> See my comments on this at the top.
> >>>>
> >>>>> +        return ret;
> >>>>> +
> >>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
> >>>>> +    return 0;
> >>>>> +}
> >>>>> +
> >>>>>    static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
> >>>>>                    struct file *file, void *fh, void *arg)
> >>>>>    {
> >>>>> @@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct
> >>>>> v4l2_ioctl_ops *ops,
> >>>>>        switch (p->type) {
> >>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap))
> >>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
> >>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
> >>>>>                break;
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.pix);
> >>>>> -        ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
> >>>>> +        ret = ops->vidioc_try_fmt_vid_cap ?
> >>>>> +              ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
> >>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>>>> VIDIOC_TRY_FMT);
> >>>>>            /* just in case the driver zeroed it again */
> >>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>>>            if (vfd->vfl_type == VFL_TYPE_TOUCH)
> >>>>>                v4l_pix_format_touch(&p->fmt.pix);
> >>>>>            return ret;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
> >>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
> >>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
> >>>>>                break;
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>>>>            for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
> >>>>>                CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
> >>>>>                          bytesperline);
> >>>>> -        return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
> >>>>> +        return ops->vidioc_try_fmt_vid_cap_mplane ?
> >>>>> +               ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
> >>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>>>> +                         VIDIOC_TRY_FMT);
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >>>>>            if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
> >>>>>                break;
> >>>>> @@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct
> >>>>> v4l2_ioctl_ops *ops,
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
> >>>>>            return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out))
> >>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out &&
> >>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
> >>>>>                break;
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.pix);
> >>>>> -        ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
> >>>>> +        ret = ops->vidioc_try_fmt_vid_out ?
> >>>>> +              ops->vidioc_try_fmt_vid_out(file, fh, arg) :
> >>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>>>> VIDIOC_TRY_FMT);
> >>>>>            /* just in case the driver zeroed it again */
> >>>>>            p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>>>>            return ret;
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
> >>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
> >>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
> >>>>>                break;
> >>>>>            CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>>>>            for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
> >>>>>                CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
> >>>>>                          bytesperline);
> >>>>> -        return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
> >>>>> +        return ops->vidioc_try_fmt_vid_out_mplane ?
> >>>>> +               ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
> >>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
> >>>>> +                         VIDIOC_TRY_FMT);
> >>>>>        case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >>>>>            if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
> >>>>>                break;
> >>>>> @@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct
> >>>>> v4l2_ioctl_ops *ops,
> >>>>>        return -EINVAL;
> >>>>>    }
> >>>>> +static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
> >>>>> +                   struct file *file, void *fh, void *arg)
> >>>>> +{
> >>>>> +    struct video_device *vfd = video_devdata(file);
> >>>>> +    struct v4l2_ext_pix_format *ef = arg;
> >>>>> +    struct v4l2_format f;
> >>>>> +    int ret = check_fmt(file, ef->type);
> >>>>> +
> >>>>> +    if (ret)
> >>>>> +        return ret;
> >>>>> +
> >>>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
> >>>>> +
> >>>>> +    switch (ef->type) {
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_cap)
> >>>>> +            return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
> >>>>> +                                   ef);
> >>>>> +        break;
> >>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >>>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_out)
> >>>>> +            return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
> >>>>> +                                   ef);
> >>>>> +        break;
> >>>>> +    default:
> >>>>> +        return -EINVAL;
> >>>>> +    }
> >>>>> +
> >>>>> +    v4l2_ext_pix_format_to_format(ef, &f,
> >>>>> V4L2_IS_CAP_MULTIPLANAR(vfd));
> >>>>> +
> >>>>> +    ret = v4l_try_fmt(ops, file, fh, &f);
> >>>>> +    if (ret)
> >>>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
> >>>>> +        return ret;
> >>>>> +
> >>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
> >>>>> +    return 0;
> >>>>> +}
> >>>>> +
> >>>>>    static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
> >>>>>                    struct file *file, void *fh, void *arg)
> >>>>>    {
> >>>>> @@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info
> >>>>> v4l2_ioctls[] = {
> >>>>>        IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands,
> >>>>> v4l_print_freq_band, 0),
> >>>>>        IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info,
> >>>>> v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
> >>>>>        IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl,
> >>>>> v4l_print_query_ext_ctrl, INFO_FL_CTRL |
> >>>>> INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
> >>>>> +    IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt,
> >>>>> v4l_print_ext_pix_format, 0),
> >>>>> +    IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt,
> >>>>> v4l_print_ext_pix_format, INFO_FL_PRIO),
> >>>>> +    IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt,
> >>>>> v4l_print_ext_pix_format, 0),
> >>>>>    };
> >>>>>    #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
> >>>>> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
> >>>>> index edb733f21604..c44708dc9355 100644
> >>>>> --- a/include/media/v4l2-ioctl.h
> >>>>> +++ b/include/media/v4l2-ioctl.h
> >>>>> @@ -48,11 +48,17 @@ struct v4l2_fh;
> >>>>>     * @vidioc_g_fmt_vid_cap: pointer to the function that implements
> >>>>>     *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
> >>>>>     *    in single plane mode
> >>>>> + * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that
> >>>>> implements
> >>>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic
> >>>>> for video
> >>>>> + *    capture
> >>>>>     * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
> >>>>>     *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
> >>>>>     * @vidioc_g_fmt_vid_out: pointer to the function that implements
> >>>>>     *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
> >>>>>     *    in single plane mode
> >>>>> + * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that
> >>>>> implements
> >>>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic
> >>>>> for video
> >>>>> + *    out
> >>>>>     * @vidioc_g_fmt_vid_out_overlay: pointer to the function that
> >>>>> implements
> >>>>>     *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video
> >>>>> overlay output
> >>>>>     * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
> >>>>> @@ -82,11 +88,16 @@ struct v4l2_fh;
> >>>>>     * @vidioc_s_fmt_vid_cap: pointer to the function that implements
> >>>>>     *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
> >>>>>     *    in single plane mode
> >>>>> + * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that
> >>>>> implements
> >>>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic
> >>>>> for video
> >>>>> + *    capture
> >>>>>     * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
> >>>>>     *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
> >>>>>     * @vidioc_s_fmt_vid_out: pointer to the function that implements
> >>>>>     *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
> >>>>>     *    in single plane mode
> >>>>> + * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that
> >>>>> implements
> >>>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for
> >>>>> video out
> >>>>>     * @vidioc_s_fmt_vid_out_overlay: pointer to the function that
> >>>>> implements
> >>>>>     *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video
> >>>>> overlay output
> >>>>>     * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
> >>>>> @@ -116,11 +127,16 @@ struct v4l2_fh;
> >>>>>     * @vidioc_try_fmt_vid_cap: pointer to the function that implements
> >>>>>     *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video
> >>>>> capture
> >>>>>     *    in single plane mode
> >>>>> + * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that
> >>>>> implements
> >>>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl
> >>>>> logic for
> >>>>> +    video capture
> >>>>>     * @vidioc_try_fmt_vid_overlay: pointer to the function that
> >>>>> implements
> >>>>>     *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video
> >>>>> overlay
> >>>>>     * @vidioc_try_fmt_vid_out: pointer to the function that implements
> >>>>>     *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
> >>>>>     *    in single plane mode
> >>>>> + * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that
> >>>>> implements
> >>>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for
> >>>>> video out
> >>>>>     * @vidioc_try_fmt_vid_out_overlay: pointer to the function that
> >>>>> implements
> >>>>>     *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video
> >>>>> overlay
> >>>>>     *    output
> >>>>> @@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
> >>>>>        /* VIDIOC_G_FMT handlers */
> >>>>>        int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
> >>>>>                        struct v4l2_format *f);
> >>>>> +    int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> >>>>> +                        struct v4l2_ext_pix_format *ef);
> >>>>>        int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
> >>>>>                        struct v4l2_format *f);
> >>>>>        int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
> >>>>>                        struct v4l2_format *f);
> >>>>> +    int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> >>>>> +                        struct v4l2_ext_pix_format *ef);
> >>>>>        int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
> >>>>>                            struct v4l2_format *f);
> >>>>>        int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
> >>>>> @@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
> >>>>>        /* VIDIOC_S_FMT handlers */
> >>>>>        int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
> >>>>>                        struct v4l2_format *f);
> >>>>> +    int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> >>>>> +                        struct v4l2_ext_pix_format *ef);
> >>>>>        int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
> >>>>>                        struct v4l2_format *f);
> >>>>>        int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
> >>>>>                        struct v4l2_format *f);
> >>>>> +    int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> >>>>> +                        struct v4l2_ext_pix_format *ef);
> >>>>>        int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
> >>>>>                            struct v4l2_format *f);
> >>>>>        int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
> >>>>> @@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
> >>>>>        /* VIDIOC_TRY_FMT handlers */
> >>>>>        int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
> >>>>>                          struct v4l2_format *f);
> >>>>> +    int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
> >>>>> +                          struct v4l2_ext_pix_format *ef);
> >>>>>        int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
> >>>>>                          struct v4l2_format *f);
> >>>>>        int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
> >>>>>                          struct v4l2_format *f);
> >>>>> +    int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
> >>>>> +                          struct v4l2_ext_pix_format *ef);
> >>>>>        int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
> >>>>>                             struct v4l2_format *f);
> >>>>>        int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
> >>>>> diff --git a/include/uapi/linux/videodev2.h
> >>>>> b/include/uapi/linux/videodev2.h
> >>>>> index d9b7c9177605..a2d850513708 100644
> >>>>> --- a/include/uapi/linux/videodev2.h
> >>>>> +++ b/include/uapi/linux/videodev2.h
> >>>>> @@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
> >>>>>        __u8                reserved[7];
> >>>>>    } __attribute__ ((packed));
> >>>>> +/**
> >>>>> + * struct v4l2_ext_pix_format - extended single/multiplanar format
> >>>>> definition
> >>>>> + * @type:        type of the data stream;
> >>>>> V4L2_BUF_TYPE_VIDEO_CAPTURE or
> >>>>> + *            V4L2_BUF_TYPE_VIDEO_OUTPUT
> >>>>> + * @width:        image width in pixels
> >>>>> + * @height:        image height in pixels
> >>>>> + * @field:        enum v4l2_field; field order (for interlaced video)
> >>>>> + * @plane_fmt:        per-plane information
> >>>>> + * @pixelformat:    little endian four character code (fourcc)
> >>>>> + * @modifier:        modifier applied to the format (used for tiled
> >>>>> formats
> >>>>> + *            and other kind of HW-specific formats, like compressed
> >>>>> + *            formats) as defined in drm_fourcc.h
> >>>>> + * @colorspace:        enum v4l2_colorspace; supplemental to
> >>>>> pixelformat
> >>>>> + * @xfer_func:        enum v4l2_xfer_func, colorspace transfer function
> >>>>> + * @ycbcr_enc:        enum v4l2_ycbcr_encoding, Y'CbCr encoding
> >>>>> + * @hsv_enc:        enum v4l2_hsv_encoding, HSV encoding
> >>>>> + * @quantization:    enum v4l2_quantization, colorspace quantization
> >>>>> + * @reserved:        extra space reserved for future fields, must be
> >>>>> set to 0
> >>>>> + */
> >>>>> +struct v4l2_ext_pix_format {
> >>>>> +    __u32 type;
> >>>>> +    __u32 width;
> >>>>> +    __u32 height;
> >>>>> +    __u32 field;
> >>>>> +    struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
> >>>>> +    __u32 pixelformat;
> >>>>> +    __u64 modifier;
> >>>>> +    __u32 colorspace;
> >>>>> +    __u32 xfer_func;
> >>>>> +    union {
> >>>>> +        __u32 ycbcr_enc;
> >>>>> +        __u32 hsv_enc;
> >>>>> +    };
> >>>>> +    __u32 quantization;
> >>>>> +    __u32 reserved[9];
> >>>>> +};
> >>>>> +
> >>>>>    /**
> >>>>>     * struct v4l2_sdr_format - SDR format definition
> >>>>>     * @pixelformat:    little endian four character code (fourcc)
> >>>>> @@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
> >>>>>    #define VIDIOC_QUERY_EXT_CTRL    _IOWR('V', 103, struct
> >>>>> v4l2_query_ext_ctrl)
> >>>>> +#define VIDIOC_G_EXT_PIX_FMT    _IOWR('V', 104, struct
> >>>>> v4l2_ext_pix_format)
> >>>>> +#define VIDIOC_S_EXT_PIX_FMT    _IOWR('V', 105, struct
> >>>>> v4l2_ext_pix_format)
> >>>>> +#define VIDIOC_TRY_EXT_PIX_FMT    _IOWR('V', 106, struct
> >>>>> v4l2_ext_pix_format)
> >>>>> +
> >>>>>    /* Reminder: when adding new ioctls please add support for them to
> >>>>>       drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
> >>>>>
> > 
> > --
> > Regards,
> > 
> > Laurent Pinchart
> 
> -- 
> Hsia-Jun(Randy) Li

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-06 22:11         ` Dmitry Osipenko
  2022-11-07  2:04           ` Hsia-Jun Li
@ 2022-11-07  8:30           ` Laurent Pinchart
  2022-11-08 14:58             ` Dmitry Osipenko
  2022-11-07 16:50           ` Fritz Koenig
  2 siblings, 1 reply; 48+ messages in thread
From: Laurent Pinchart @ 2022-11-07  8:30 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Hsia-Jun Li, Helen Koike, mchehab, hans.verkuil, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga,
	Hans Verkuil, linux-media

On Mon, Nov 07, 2022 at 01:11:32AM +0300, Dmitry Osipenko wrote:
> On 11/5/22 18:19, Hsia-Jun Li wrote:
> > Hello Helen
> > 
> > I didn't see any updates from V6 and V7-WIP in your repo. That is what I
> > need to for our complex tile formats in our platform.
> > 
> > Any future plane here?
> > 
> > Besides I have some ideas on these patches.
> 
> I was looking into updating this patchset few months ago and the biggest
> blocker was the absence of immediate upstream user for this new UAPI.
> What your platform is? Is the driver stack completely opensource?

libcamera could be a good place to test (part of) this API in userspace.
We could really do with the data offset feature for instance.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-05 15:19       ` Hsia-Jun Li
  2022-11-06 19:24         ` Laurent Pinchart
  2022-11-06 22:11         ` Dmitry Osipenko
@ 2022-11-07  8:42         ` Hans Verkuil
  2022-11-10 17:06         ` Nicolas Dufresne
  3 siblings, 0 replies; 48+ messages in thread
From: Hans Verkuil @ 2022-11-07  8:42 UTC (permalink / raw)
  To: Hsia-Jun Li, Helen Koike
  Cc: mchehab, laurent.pinchart, sakari.ailus, boris.brezillon, hiroh,
	nicolas, Brian.Starkey, kernel, narmstrong, linux-kernel,
	frkoenig, stanimir.varbanov, tfiga, linux-media

Hi Hsia-Jun,

Helen no longer works for Collabora and left the community. I've added her latest email.

Helen, are you still interested in hearing about this?

Regards,

	Hans

On 05/11/2022 16:19, Hsia-Jun Li wrote:
> Hello Helen
> 
> I didn't see any updates from V6 and V7-WIP in your repo. That is what I need to for our complex tile formats in our platform.
> 
> Any future plane here?
> 
> Besides I have some ideas on these patches.
> 
> On 2/24/21 23:12, Helen Koike wrote:
>> Hi Hans,
>>
>> Thank you for your comment, please see my reply below.
>>
>> On 2/23/21 9:35 AM, Hans Verkuil wrote:
>>> Hi Helen,
>>>
>>> On 14/01/2021 19:07, Helen Koike wrote:
>>>> This is part of the multiplanar and singleplanar unification process.
>>>> v4l2_ext_pix_format is supposed to work for both cases.
>>>>
>>>> We also add the concept of modifiers already employed in DRM to expose
>>>> HW-specific formats (like tiled or compressed formats) and allow
>>>> exchanging this information with the DRM subsystem in a consistent way.
>>>>
>>>> Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
>>>> v4l2_ext_format, other types will be rejected if you use the
>>>> {G,S,TRY}_EXT_PIX_FMT ioctls.
>>>>
>>>> New hooks have been added to v4l2_ioctl_ops to support those new ioctls
>>>> in drivers, but, in the meantime, the core takes care of converting
>>>> {S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers can
>>>> still work if the userspace app/lib uses the new ioctls.
>>>>
>>>> The conversion is also done the other around to allow userspace
>>>> apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
>>>> _ext_ hooks.
>>>
>>> I have some small comments below, but also one high level comment:
>>>
>>> Regarding M variants of pixelformats: this patch 'normalizes' them to
>>> regular pixelformats in the extended API. This makes life complicated,
>>> and I wonder if this is the right approach.
>>>
>>> Currently there are two reasons for a driver to support e.g. NV12M:
>>> either luma and chroma need to be in two memory banks, or the luma
>>> and chroma planes cannot be contiguous due to alignment requirements.
>>>
>>> The first requirement is still valid for drivers that support the extended API.
>>> The second requirement is no longer a reason to support NV12M. But I
>>> don't think we should just drop NV12M support if it was already supported
>>> before the conversion to this extended API. Since NV12M allocates two buffers
>>> instead of one, it is still different from a regular NV12.
>>
>> I don't see what would be the difference when using NV12 and NV12M in Ext API.
>> NV12 could be used for both requirements. It would even allow the second
>> requirement with a single memory buffer.
>>
> Although I don't have problem here to support both single and multiple planes in our hardware. But using the single plane format here is not convience for us.
> 
> The tile format in our platform is little complex, you can't calculate the stride and sizeimage easily with just the modifier, width and height. Then we don't have a good place to record the offset here.
> 
> Besides, the luma and chroma planes would always need their own compression meta data planes. If we use NV12 here, that would make a very strange pixel format, one plane for the pixel datas and two
> planes for the meta data.
> 
>>>
>>> I would prefer that such drivers support both NV12 and NV12M, so no
>>> automatic conversion.
>>>
>>> A related question is how to handle pixelformat enumeration: with the
>>> extended API an NV12 format might work, but not with the old API (e.g.
>>> due to memory alignment requirements). I wonder if a VIDIOC_ENUM_EXT_PIX_FMT
>>> isn't needed.
>>
>> We need VIDIOC_ENUM_EXT_PIX_FMT for modifiers, but we can add it later.
>> If the driver reports NV12M, userspace can use it and the framework
>> normilizes it.
>>
>>>
>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while VIDIOC_ENUM_FMT
>>> would just report NV12M.
>>
>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
>> report both (unless I'm missing something, which is probably the case).
>>
>> The idea was to deprecate the M-variants one day.
> I was thinking the way in DRM API is better, always assuming it would always in a multiple planes. The only problem is we don't have a way to let the allocator that allocate contiguous memory for
> planes when we need to do that.
>>
>> Regards,
>> Helen
>>
>>>
>>>>
>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>>>> ---
>>>>
>>>> Changes in v6:
>>>>   The main change here was fixing the conversion, so planes reflects color planes,
>>>>   and to implement this properly I made major refactors compared to the previous
>>>>   version.
>>>> - struct v4l2_plane_ext_pix_format removed, using struct v4l2_plane_pix_format instead (Tomasz)
>>>> - refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
>>>> - reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
>>>> - do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
>>>> - refactor conversion functions, so planes are color planes (Tomasz)
>>>> - Don't explicitly check for e->modifier != 0 in v4l2_ext_pix_format_to_format() (Tomasz)
>>>> - Use "ef" for extended formats in the framework for consistency (Tomasz)
>>>> - Handle xfer_func field in conversions (Tomasz)
>>>> - Zero reserved fields in v4l_s_ext_pix_fmt() and v4l_try_ext_pix_fmt() (Tomasz)
>>>> - Refactor format functions to use v4l_fmt_ioctl_via_ext()
>>>> - Several fixes/refactoring/changes
>>>> - Remove EXT API for touch devices
>>>>
>>>> Changes in v5:
>>>> - change sizes and reorder fields to avoid holes in the struct and make
>>>>    it the same for 32 and 64 bits
>>>> - removed __attribute__ ((packed)) from uapi structs
>>>> - Fix doc warning from make htmldocs
>>>> - Updated commit message with EXT_PIX prefix for the ioctls.
>>>>
>>>> Changes in v4:
>>>> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
>>>> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
>>>> - Add reserved fields
>>>> - Removed num_planes from struct v4l2_ext_pix_format
>>>> - Removed flag field from struct v4l2_ext_pix_format, since the only
>>>>    defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
>>>>    where we can use modifiers, or add it back later through the reserved
>>>>    bits.
>>>> - In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
>>>>    != MOD_INVALID
>>>> - Fix type assignment in v4l_g_fmt_ext_pix()
>>>> - Rebased on top of media/master (post 5.8-rc1)
>>>>
>>>> Changes in v3:
>>>> - Rebased on top of media/master (post 5.4-rc1)
>>>>
>>>> Changes in v2:
>>>> - Move the modifier in v4l2_ext_format (was formerly placed in
>>>>    v4l2_ext_plane)
>>>> - Fix a few bugs in the converters and add a strict parameter to
>>>>    allow conversion of uninitialized/mis-initialized objects
>>>> ---
>>>>   drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
>>>>   drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
>>>>   include/media/v4l2-ioctl.h           |  28 ++
>>>>   include/uapi/linux/videodev2.h       |  41 ++
>>>>   4 files changed, 602 insertions(+), 32 deletions(-)
>>>>
>>>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
>>>> index f9cff033d0dc..5add58cb6d45 100644
>>>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>>>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>>>> @@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct video_device *vdev)
>>>>                      ops->vidioc_enum_fmt_vid_overlay)) ||
>>>>               (is_tx && ops->vidioc_enum_fmt_vid_out))
>>>>               set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
>>>> +        if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
>>>> +            (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
>>>> +             set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>>>>           if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
>>>>                      ops->vidioc_g_fmt_vid_cap_mplane ||
>>>> -                   ops->vidioc_g_fmt_vid_overlay)) ||
>>>> +                   ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
>>>>               (is_tx && (ops->vidioc_g_fmt_vid_out ||
>>>>                      ops->vidioc_g_fmt_vid_out_mplane ||
>>>> -                   ops->vidioc_g_fmt_vid_out_overlay)))
>>>> +                   ops->vidioc_g_ext_pix_fmt_vid_out))) {
>>>>                set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>>>> +             set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
>>>> +        }
>>>> +        if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
>>>> +            (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
>>>> +             set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>>>>           if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
>>>>                      ops->vidioc_s_fmt_vid_cap_mplane ||
>>>> -                   ops->vidioc_s_fmt_vid_overlay)) ||
>>>> +                   ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
>>>>               (is_tx && (ops->vidioc_s_fmt_vid_out ||
>>>>                      ops->vidioc_s_fmt_vid_out_mplane ||
>>>> -                   ops->vidioc_s_fmt_vid_out_overlay)))
>>>> +                   ops->vidioc_s_ext_pix_fmt_vid_out))) {
>>>>                set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>>>> +             set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
>>>> +        }
>>>> +        if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
>>>> +            (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
>>>> +             set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>>>>           if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
>>>>                      ops->vidioc_try_fmt_vid_cap_mplane ||
>>>> -                   ops->vidioc_try_fmt_vid_overlay)) ||
>>>> +                   ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
>>>>               (is_tx && (ops->vidioc_try_fmt_vid_out ||
>>>>                      ops->vidioc_try_fmt_vid_out_mplane ||
>>>> -                   ops->vidioc_try_fmt_vid_out_overlay)))
>>>> +                   ops->vidioc_try_ext_pix_fmt_vid_out))) {
>>>>                set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>>>> +             set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
>>>> +        }
>>>>           SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
>>>>           SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
>>>>           SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
>>>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
>>>> index 848286a284f6..a9c07c0a73ec 100644
>>>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>>>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>>>> @@ -18,6 +18,8 @@
>>>>   #include <linux/videodev2.h>
>>>> +#include <drm/drm_fourcc.h>
>>>> +
>>>>   #include <media/v4l2-common.h>
>>>>   #include <media/v4l2-ioctl.h>
>>>>   #include <media/v4l2-ctrls.h>
>>>> @@ -38,6 +40,11 @@
>>>>   #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd), (vfd)->valid_ioctls)
>>>> +#define V4L2_IS_CAP_MULTIPLANAR(vdev)    (vdev->device_caps & \
>>>> +                     (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
>>>> +                     V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
>>>> +                     V4L2_CAP_VIDEO_M2M_MPLANE))
>>>> +
>>>>   struct std_descr {
>>>>       v4l2_std_id std;
>>>>       const char *descr;
>>>> @@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg, bool write_only)
>>>>       }
>>>>   }
>>>> +static void v4l_print_ext_pix_format(const void *arg, bool write_only)
>>>> +{
>>>> +    const struct v4l2_ext_pix_format *ef = arg;
>>>> +    unsigned int i;
>>>> +
>>>> +    pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u, xfer_func=%u\n",
>>>> +        prt_names(ef->type, v4l2_type_names),
>>>> +        ef->width, ef->height,
>>>> +        (ef->pixelformat & 0xff),
>>>> +        (ef->pixelformat >>  8) & 0xff,
>>>> +        (ef->pixelformat >> 16) & 0xff,
>>>> +        (ef->pixelformat >> 24) & 0xff,
>>>> +        ef->modifier, prt_names(ef->field, v4l2_field_names),
>>>> +        ef->colorspace, ef->ycbcr_enc,
>>>> +        ef->quantization, ef->xfer_func);
>>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
>>>> +        pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
>>>> +             i, ef->plane_fmt[i].bytesperline,
>>>> +             ef->plane_fmt[i].sizeimage);
>>>> +}
>>>> +
>>>>   static void v4l_print_framebuffer(const void *arg, bool write_only)
>>>>   {
>>>>       const struct v4l2_framebuffer *p = arg;
>>>> @@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
>>>>       switch (type) {
>>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>           if ((is_vid || is_tch) && is_rx &&
>>>> -            (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane))
>>>> +            (ops->vidioc_g_fmt_vid_cap ||
>>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap ||
>>>> +             ops->vidioc_g_fmt_vid_cap_mplane))
>>>>               return 0;
>>>>           break;
>>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>> -        if ((is_vid || is_tch) && is_rx && ops->vidioc_g_fmt_vid_cap_mplane)
>>>> +        if ((is_vid || is_tch) && is_rx &&
>>>> +            (ops->vidioc_g_fmt_vid_cap_mplane ||
>>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap))
>>>>               return 0;
>>>>           break;
>>>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>> @@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
>>>>           break;
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>           if (is_vid && is_tx &&
>>>> -            (ops->vidioc_g_fmt_vid_out || ops->vidioc_g_fmt_vid_out_mplane))
>>>> +            (ops->vidioc_g_fmt_vid_out ||
>>>> +             ops->vidioc_g_ext_pix_fmt_vid_out ||
>>>> +             ops->vidioc_g_fmt_vid_out_mplane))
>>>>               return 0;
>>>>           break;
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>> -        if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
>>>> +        if (is_vid && is_tx &&
>>>> +            (ops->vidioc_g_ext_pix_fmt_vid_out ||
>>>> +             ops->vidioc_g_fmt_vid_out_mplane))
>>>>               return 0;
>>>>           break;
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>> @@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct v4l2_format *fmt)
>>>>              sizeof(fmt->fmt.pix) - offset);
>>>>   }
>>>> +static void
>>>> +v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
>>>> +                  struct v4l2_pix_format *pix)
>>>> +{
>>>> +    unsigned int i;
>>>> +
>>>> +    pix->width = ef->width;
>>>> +    pix->height = ef->height;
>>>> +    pix->field = ef->field;
>>>> +    pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>>>> +    pix->colorspace = ef->colorspace;
>>>> +    pix->ycbcr_enc = ef->ycbcr_enc;
>>>> +    pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>> +    pix->quantization = ef->quantization;
>>>> +    pix->pixelformat = ef->pixelformat;
>>>> +    pix->bytesperline = ef->plane_fmt[0].bytesperline;
>>>> +    pix->sizeimage = ef->plane_fmt[0].sizeimage;
>>>> +    for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
>>>> +        pix->sizeimage += ef->plane_fmt[i].sizeimage;
>>>> +}
>>>> +
>>>> +static void
>>>> +v4l2_ext_pix_format_to_pix_mp_format(const struct v4l2_ext_pix_format *ef,
>>>> +                     struct v4l2_pix_format_mplane *pix_mp)
>>>> +{
>>>> +    const struct v4l2_format_info *info =
>>>> +                    v4l2_format_info(ef->pixelformat);
>>>> +    unsigned int i;
>>>> +
>>>> +    pix_mp->width = ef->width;
>>>> +    pix_mp->height = ef->height;
>>>> +    pix_mp->field = ef->field;
>>>> +    pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>>>> +    pix_mp->colorspace = ef->colorspace;
>>>> +    pix_mp->ycbcr_enc = ef->ycbcr_enc;
>>>> +    pix_mp->quantization = ef->quantization;
>>>> +    pix_mp->pixelformat = ef->pixelformat;
>>>> +
>>>> +    /* This is true when converting to non-M-variant */
>>>> +    if (info && info->mem_planes == 1) {
>>>> +        pix_mp->plane_fmt[0] = ef->plane_fmt[0];
>>>> +        pix_mp->num_planes = 1;
>>>> +        for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
>>>> +            pix_mp->plane_fmt[0].sizeimage += ef->plane_fmt[i].sizeimage;
>>>> +
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage; i++)
>>>> +        pix_mp->plane_fmt[i] = ef->plane_fmt[i];
>>>> +    pix_mp->num_planes = i;
>>>> +}
>>>> +
>>>> +/*
>>>> + * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to v4l2_format
>>>> + *
>>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
>>>> + * @f: A pointer to struct v4l2_format to be filled.
>>>> + * @is_mplane: Bool indicating if multiplanar API should be used in @f.
>>>> + *
>>>> + * If pixelformat should be converted to M-variant, change ef->pixelformat
>>>> + * to the M-variant before calling this function.
>>>> + */
>>>> +static void v4l2_ext_pix_format_to_format(const struct v4l2_ext_pix_format *ef,
>>>> +                      struct v4l2_format *f, bool is_mplane)
>>>> +{
>>>> +    memset(f, 0, sizeof(*f));
>>>> +
>>>> +    if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
>>>> +        ef->modifier != DRM_FORMAT_MOD_INVALID)
>>>> +        pr_warn("Modifiers are not supported in v4l2_format, ignoring %llx\n",
>>>> +            ef->modifier);
>>>> +
>>>> +    if (!is_mplane) {
>>>> +        f->type = ef->type;
>>>> +        v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>>> +        f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>>>> +    else
>>>> +        f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
>>>> +
>>>> +    v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
>>>> +}
>>>> +
>>>> +static void
>>>> +v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
>>>> +                  struct v4l2_ext_pix_format *ef)
>>>> +{
>>>> +    const struct v4l2_format_info *info =
>>>> +                    v4l2_format_info(pix->pixelformat);
>>>> +    unsigned int i;
>>>> +
>>>> +    ef->width = pix->width;
>>>> +    ef->height = pix->height;
>>>> +    ef->field = pix->field;
>>>> +    ef->colorspace = pix->colorspace;
>>>> +    ef->ycbcr_enc = pix->ycbcr_enc;
>>>> +    ef->quantization = pix->quantization;
>>>> +    ef->xfer_func = pix->xfer_func;
>>>> +    if (pix->flags)
>>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
>>>> +
>>>> +    /* We assume M-variants won't be used in this function */
>>>> +    ef->pixelformat = pix->pixelformat;
>>>> +
>>>> +    ef->plane_fmt[0].bytesperline = pix->bytesperline;
>>>> +    ef->plane_fmt[0].sizeimage = pix->sizeimage;
>>>> +
>>>> +    if (!info)
>>>> +        return;
>>>> +
>>>> +    for (i = 1; i < info->comp_planes; i++) {
>>>> +        ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
>>>> +        ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
>>>> +                         ef->height / info->vdiv;
>>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>>>> +    }
>>>> +}
>>>> +
>>>> +static void
>>>> +v4l2_pix_mp_format_to_ext_pix_format(const struct v4l2_pix_format_mplane *pix_mp,
>>>> +                     struct v4l2_ext_pix_format *ef)
>>>> +{
>>>> +    const struct v4l2_format_info *info =
>>>> +                    v4l2_format_info(pix_mp->pixelformat);
>>>> +    unsigned int i;
>>>> +
>>>> +    ef->width = pix_mp->width;
>>>> +    ef->height = pix_mp->height;
>>>> +    ef->field = pix_mp->field;
>>>> +    ef->colorspace = pix_mp->colorspace;
>>>> +    ef->ycbcr_enc = pix_mp->ycbcr_enc;
>>>> +    ef->quantization = pix_mp->quantization;
>>>> +    ef->xfer_func = pix_mp->xfer_func;
>>>> +    if (pix_mp->flags)
>>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
>>>> +
>>>> +    if (!info)
>>>> +        return;
>>>> +
>>>> +    ef->pixelformat = info && info->norm ?
>>>
>>> 'info &&' can be dropped, info is always non-NULL here.
>>>
>>>> +              info->norm : pix_mp->pixelformat;
>>>> +
>>>> +    if (info->comp_planes == info->mem_planes) {
>>>> +        for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES; i++)
>>>> +            ef->plane_fmt[i] = pix_mp->plane_fmt[i];
>>>> +
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    /* case where mem_planes is 1 and comp_planes > 1 */
>>>> +    ef->plane_fmt[0] = pix_mp->plane_fmt[0];
>>>> +    for (i = 1; i < info->comp_planes; i++) {
>>>> +        ef->plane_fmt[i].bytesperline =
>>>> +            pix_mp->plane_fmt[0].bytesperline / info->hdiv;
>>>> +        ef->plane_fmt[i].sizeimage =
>>>> +            ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
>>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>>>> +    }
>>>> +}
>>>> +
>>>> +/*
>>>> + * v4l2_format_to_ext_pix_format - convert to v4l2_format to v4l2_ext_pix_format
>>>> + *
>>>> + * @f: A pointer to struct v4l2_format to be converted.
>>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
>>>> + *
>>>> + * This method normalize the pixelformat to non-M variant.
>>>> + */
>>>> +static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
>>>> +                      struct v4l2_ext_pix_format *ef)
>>>> +{
>>>> +    memset(ef, 0, sizeof(*ef));
>>>> +
>>>> +    switch (f->type) {
>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>> +        ef->type = f->type;
>>>> +        v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
>>>> +        break;
>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>>>> +        break;
>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>>>> +        break;
>>>> +    default:
>>>> +        WARN("Converting to Ext Pix Format with wrong buffer type %s\n",
>>>> +             prt_names(f->type, v4l2_type_names));
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>>   static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
>>>>                   struct file *file, void *fh, void *arg)
>>>>   {
>>>> @@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct v4l2_pix_format *p)
>>>>       p->xfer_func = 0;
>>>>   }
>>>> +static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
>>>> +                 struct file *file, void *fh,
>>>> +                 struct v4l2_format *f,
>>>> +                 unsigned int ioctl)
>>>> +{
>>>> +    bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
>>>> +    struct video_device *vdev = video_devdata(file);
>>>> +    struct v4l2_ext_pix_format ef = {0};
>>>> +    u32 original_pixfmt = 0;
>>>> +    u32 cap_mask;
>>>> +    int ret;
>>>> +
>>>> +    if (ioctl != VIDIOC_G_FMT) {
>>>> +        /*
>>>> +         * If CSC attributes are read only, set them to DEFAULT
>>>> +         * to avoid changes by the driver.
>>>> +         */
>>>> +        if (is_multiplanar) {
>>>> +            if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>>>> +                f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
>>>> +                f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>>>> +                f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
>>>> +                f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>>>> +            }
>>>> +            /* Unset the flag to avoid warning in the convertion */
>>>> +            f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>>>> +
>>>> +            /* Save pixelformat in case M-variant is being used */
>>>> +            original_pixfmt = f->fmt.pix_mp.pixelformat;
>>>> +        } else {
>>>> +            if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>>>> +                f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
>>>> +                f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>>>> +                f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
>>>> +                f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>>>> +            }
>>>> +            /* Unset the flag to avoid warning in the convertion */
>>>> +            f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>>>> +        }
>>>> +        v4l2_format_to_ext_pix_format(f, &ef);
>>>> +    }
>>>> +
>>>> +    switch (f->type) {
>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>> +        cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
>>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
>>>> +        if (!!(vdev->device_caps & cap_mask) !=
>>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
>>>> +            return -EINVAL;
>>>> +
>>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>>> +        if (ioctl == VIDIOC_G_FMT)
>>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
>>>> +        else if (ioctl == VIDIOC_S_FMT)
>>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
>>>> +        else
>>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>>>> +                                  &ef);
>>>> +        break;
>>>> +
>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>> +        cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
>>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
>>>> +        if (!!(vdev->device_caps & cap_mask) !=
>>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>>>> +            return -EINVAL;
>>>> +
>>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>>> +        if (ioctl == VIDIOC_G_FMT)
>>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
>>>> +        else if (ioctl == VIDIOC_S_FMT)
>>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
>>>> +        else
>>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>>>> +                                  &ef);
>>>> +        break;
>>>> +
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    if (original_pixfmt != ef.pixelformat &&
>>>> +        v4l2_format_info(original_pixfmt))
>>>
>>> Could this test be simplified to: 'if (original_pixfmt)'?
>>>
>>> I.e., if the original pixfmt was saved, then restore it here.
>>>
>>>> +        ef.pixelformat = original_pixfmt;
>>>> +
>>>> +    v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
>>>> +    return 0;
>>>> +}
>>>> +
>>>>   static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>>>                   struct file *file, void *fh, void *arg)
>>>>   {
>>>> @@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>>>       switch (p->type) {
>>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_cap))
>>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
>>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_cap))
>>>>               break;
>>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>> -        ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
>>>> +        ret = ops->vidioc_g_fmt_vid_cap ?
>>>> +              ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>> +                        VIDIOC_G_FMT);
>>>>           /* just in case the driver zeroed it again */
>>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>           if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>>               v4l_pix_format_touch(&p->fmt.pix);
>>>>           return ret;
>>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>> -        return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>>> +        if (ops->vidioc_g_fmt_vid_cap_mplane)
>>>> +            return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_cap)
>>>
>>> 'else' can be dropped.
>>>
>>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>> +                             VIDIOC_G_FMT);
>>>> +        break;
>>>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>           return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>>>>       case V4L2_BUF_TYPE_VBI_CAPTURE:
>>>> @@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>>> ��     case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>>>>           return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_out))
>>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_out &&
>>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_out))
>>>>               break;
>>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>> -        ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
>>>> +        ret = ops->vidioc_g_fmt_vid_out ?
>>>> +              ops->vidioc_g_fmt_vid_out(file, fh, arg) :
>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
>>>>           /* just in case the driver zeroed it again */
>>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>           return ret;
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>> -        return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>>> +        if (ops->vidioc_g_fmt_vid_out_mplane)
>>>> +            return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_out)
>>>
>>> Ditto.
>>>
>>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>> +                             VIDIOC_G_FMT);
>>>> +        break;
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>           return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>>>>       case V4L2_BUF_TYPE_VBI_OUTPUT:
>>>> @@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>>>       return -EINVAL;
>>>>   }
>>>> +static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>>> +                 struct file *file, void *fh, void *arg)
>>>> +{
>>>> +    struct v4l2_ext_pix_format *ef = arg;
>>>> +    struct v4l2_format f = {
>>>> +        .type = ef->type,
>>>> +    };
>>>> +    int ret = check_fmt(file, ef->type);
>>>> +
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    memset(ef, 0, sizeof(*ef));
>>>> +    ef->type = f.type;
>>>> +
>>>> +    switch (f.type) {
>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_cap)
>>>> +            return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
>>>> +        break;
>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_out)
>>>> +            return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
>>>> +        break;
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    ret = v4l_g_fmt(ops, file, fh, &f);
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>>> +    return 0;
>>>> +}
>>>> +
>>>>   static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>>>                   struct file *file, void *fh, void *arg)
>>>>   {
>>>> @@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>>>       switch (p->type) {
>>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap))
>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>>>               break;
>>>>           CLEAR_AFTER_FIELD(p, fmt.pix);
>>>> -        ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
>>>> +        ret = ops->vidioc_s_fmt_vid_cap ?
>>>> +              ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>>           /* just in case the driver zeroed it again */
>>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>           if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>>               v4l_pix_format_touch(&p->fmt.pix);
>>>>           return ret;
>>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>>>               break;
>>>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>                         bytesperline);
>>>> -        return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
>>>> +        return ops->vidioc_s_fmt_vid_cap_mplane ?
>>>> +               ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,  VIDIOC_S_FMT);
>>>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>           if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>>>>               break;
>>>> @@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>>>           CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>>>           return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out))
>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out &&
>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
>>>>               break;
>>>>           CLEAR_AFTER_FIELD(p, fmt.pix);
>>>> -        ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
>>>> +        ret = ops->vidioc_s_fmt_vid_out ?
>>>> +              ops->vidioc_s_fmt_vid_out(file, fh, arg) :
>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>>           /* just in case the driver zeroed it again */
>>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>           return ret;
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
>>>>               break;
>>>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>                         bytesperline);
>>>> -        return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
>>>> +        return ops->vidioc_s_fmt_vid_out_mplane ?
>>>> +               ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>           if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>>>>               break;
>>>> @@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>>>       return -EINVAL;
>>>>   }
>>>> +static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>>> +                 struct file *file, void *fh, void *arg)
>>>> +{
>>>> +    struct video_device *vfd = video_devdata(file);
>>>> +    struct v4l2_ext_pix_format *ef = arg;
>>>> +    struct v4l2_format f;
>>>> +    int ret = check_fmt(file, ef->type);
>>>> +
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
>>>> +
>>>> +    switch (ef->type) {
>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_cap)
>>>> +            return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
>>>> +        break;
>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_out)
>>>> +            return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
>>>> +        break;
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
>>>> +
>>>> +    ret = v4l_s_fmt(ops, file, fh, &f);
>>>> +    if (ret)
>>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
>>>
>>> See my comments on this at the top.
>>>
>>>> +        return ret;
>>>> +
>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>>> +    return 0;
>>>> +}
>>>> +
>>>>   static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>>>                   struct file *file, void *fh, void *arg)
>>>>   {
>>>> @@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>>>       switch (p->type) {
>>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap))
>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>               break;
>>>>           CLEAR_AFTER_FIELD(p, fmt.pix);
>>>> -        ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
>>>> +        ret = ops->vidioc_try_fmt_vid_cap ?
>>>> +              ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
>>>>           /* just in case the driver zeroed it again */
>>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>           if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>>               v4l_pix_format_touch(&p->fmt.pix);
>>>>           return ret;
>>>>       case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>               break;
>>>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>                         bytesperline);
>>>> -        return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
>>>> +        return ops->vidioc_try_fmt_vid_cap_mplane ?
>>>> +               ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>> +                         VIDIOC_TRY_FMT);
>>>>       case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>           if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>>>>               break;
>>>> @@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>>>           CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>>>           return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out))
>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out &&
>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>               break;
>>>>           CLEAR_AFTER_FIELD(p, fmt.pix);
>>>> -        ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
>>>> +        ret = ops->vidioc_try_fmt_vid_out ?
>>>> +              ops->vidioc_try_fmt_vid_out(file, fh, arg) :
>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_TRY_FMT);
>>>>           /* just in case the driver zeroed it again */
>>>>           p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>           return ret;
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>               break;
>>>>           CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>           for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>               CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>                         bytesperline);
>>>> -        return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
>>>> +        return ops->vidioc_try_fmt_vid_out_mplane ?
>>>> +               ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>> +                         VIDIOC_TRY_FMT);
>>>>       case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>           if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>>>>               break;
>>>> @@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>>>       return -EINVAL;
>>>>   }
>>>> +static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>>> +                   struct file *file, void *fh, void *arg)
>>>> +{
>>>> +    struct video_device *vfd = video_devdata(file);
>>>> +    struct v4l2_ext_pix_format *ef = arg;
>>>> +    struct v4l2_format f;
>>>> +    int ret = check_fmt(file, ef->type);
>>>> +
>>>> +    if (ret)
>>>> +        return ret;
>>>> +
>>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
>>>> +
>>>> +    switch (ef->type) {
>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_cap)
>>>> +            return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>>>> +                                   ef);
>>>> +        break;
>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_out)
>>>> +            return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>>>> +                                   ef);
>>>> +        break;
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    v4l2_ext_pix_format_to_format(ef, &f, V4L2_IS_CAP_MULTIPLANAR(vfd));
>>>> +
>>>> +    ret = v4l_try_fmt(ops, file, fh, &f);
>>>> +    if (ret)
>>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
>>>> +        return ret;
>>>> +
>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>>> +    return 0;
>>>> +}
>>>> +
>>>>   static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
>>>>                   struct file *file, void *fh, void *arg)
>>>>   {
>>>> @@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
>>>>       IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
>>>>       IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
>>>>       IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
>>>> +    IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, v4l_print_ext_pix_format, 0),
>>>> +    IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, v4l_print_ext_pix_format, INFO_FL_PRIO),
>>>> +    IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, v4l_print_ext_pix_format, 0),
>>>>   };
>>>>   #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
>>>> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
>>>> index edb733f21604..c44708dc9355 100644
>>>> --- a/include/media/v4l2-ioctl.h
>>>> +++ b/include/media/v4l2-ioctl.h
>>>> @@ -48,11 +48,17 @@ struct v4l2_fh;
>>>>    * @vidioc_g_fmt_vid_cap: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>>>    *    in single plane mode
>>>> + * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that implements
>>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
>>>> + *    capture
>>>>    * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>>    * @vidioc_g_fmt_vid_out: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>    *    in single plane mode
>>>> + * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that implements
>>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
>>>> + *    out
>>>>    * @vidioc_g_fmt_vid_out_overlay: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
>>>>    * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
>>>> @@ -82,11 +88,16 @@ struct v4l2_fh;
>>>>    * @vidioc_s_fmt_vid_cap: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>>>    *    in single plane mode
>>>> + * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that implements
>>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for video
>>>> + *    capture
>>>>    * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>>    * @vidioc_s_fmt_vid_out: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>    *    in single plane mode
>>>> + * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that implements
>>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>    * @vidioc_s_fmt_vid_out_overlay: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay output
>>>>    * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
>>>> @@ -116,11 +127,16 @@ struct v4l2_fh;
>>>>    * @vidioc_try_fmt_vid_cap: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>>>    *    in single plane mode
>>>> + * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that implements
>>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic for
>>>> +    video capture
>>>>    * @vidioc_try_fmt_vid_overlay: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>>    * @vidioc_try_fmt_vid_out: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>    *    in single plane mode
>>>> + * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that implements
>>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>    * @vidioc_try_fmt_vid_out_overlay: pointer to the function that implements
>>>>    *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>>    *    output
>>>> @@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
>>>>       /* VIDIOC_G_FMT handlers */
>>>>       int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
>>>>                       struct v4l2_format *f);
>>>> +    int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>       int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
>>>>                       struct v4l2_format *f);
>>>>       int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
>>>>                       struct v4l2_format *f);
>>>> +    int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>       int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>>                           struct v4l2_format *f);
>>>>       int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
>>>> @@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
>>>>       /* VIDIOC_S_FMT handlers */
>>>>       int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
>>>>                       struct v4l2_format *f);
>>>> +    int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>       int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
>>>>                       struct v4l2_format *f);
>>>>       int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
>>>>                       struct v4l2_format *f);
>>>> +    int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>       int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>>                           struct v4l2_format *f);
>>>>       int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
>>>> @@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
>>>>       /* VIDIOC_TRY_FMT handlers */
>>>>       int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
>>>>                         struct v4l2_format *f);
>>>> +    int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>>> +                          struct v4l2_ext_pix_format *ef);
>>>>       int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
>>>>                         struct v4l2_format *f);
>>>>       int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
>>>>                         struct v4l2_format *f);
>>>> +    int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>>> +                          struct v4l2_ext_pix_format *ef);
>>>>       int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>>                            struct v4l2_format *f);
>>>>       int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
>>>> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
>>>> index d9b7c9177605..a2d850513708 100644
>>>> --- a/include/uapi/linux/videodev2.h
>>>> +++ b/include/uapi/linux/videodev2.h
>>>> @@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
>>>>       __u8                reserved[7];
>>>>   } __attribute__ ((packed));
>>>> +/**
>>>> + * struct v4l2_ext_pix_format - extended single/multiplanar format definition
>>>> + * @type:        type of the data stream; V4L2_BUF_TYPE_VIDEO_CAPTURE or
>>>> + *            V4L2_BUF_TYPE_VIDEO_OUTPUT
>>>> + * @width:        image width in pixels
>>>> + * @height:        image height in pixels
>>>> + * @field:        enum v4l2_field; field order (for interlaced video)
>>>> + * @plane_fmt:        per-plane information
>>>> + * @pixelformat:    little endian four character code (fourcc)
>>>> + * @modifier:        modifier applied to the format (used for tiled formats
>>>> + *            and other kind of HW-specific formats, like compressed
>>>> + *            formats) as defined in drm_fourcc.h
>>>> + * @colorspace:        enum v4l2_colorspace; supplemental to pixelformat
>>>> + * @xfer_func:        enum v4l2_xfer_func, colorspace transfer function
>>>> + * @ycbcr_enc:        enum v4l2_ycbcr_encoding, Y'CbCr encoding
>>>> + * @hsv_enc:        enum v4l2_hsv_encoding, HSV encoding
>>>> + * @quantization:    enum v4l2_quantization, colorspace quantization
>>>> + * @reserved:        extra space reserved for future fields, must be set to 0
>>>> + */
>>>> +struct v4l2_ext_pix_format {
>>>> +    __u32 type;
>>>> +    __u32 width;
>>>> +    __u32 height;
>>>> +    __u32 field;
>>>> +    struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
>>>> +    __u32 pixelformat;
>>>> +    __u64 modifier;
>>>> +    __u32 colorspace;
>>>> +    __u32 xfer_func;
>>>> +    union {
>>>> +        __u32 ycbcr_enc;
>>>> +        __u32 hsv_enc;
>>>> +    };
>>>> +    __u32 quantization;
>>>> +    __u32 reserved[9];
>>>> +};
>>>> +
>>>>   /**
>>>>    * struct v4l2_sdr_format - SDR format definition
>>>>    * @pixelformat:    little endian four character code (fourcc)
>>>> @@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
>>>>   #define VIDIOC_QUERY_EXT_CTRL    _IOWR('V', 103, struct v4l2_query_ext_ctrl)
>>>> +#define VIDIOC_G_EXT_PIX_FMT    _IOWR('V', 104, struct v4l2_ext_pix_format)
>>>> +#define VIDIOC_S_EXT_PIX_FMT    _IOWR('V', 105, struct v4l2_ext_pix_format)
>>>> +#define VIDIOC_TRY_EXT_PIX_FMT    _IOWR('V', 106, struct v4l2_ext_pix_format)
>>>> +
>>>>   /* Reminder: when adding new ioctls please add support for them to
>>>>      drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
>>>>
>>>
>>> Regards,
>>>
>>>     Hans
>>>
>>
> 


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-07  8:28             ` Laurent Pinchart
@ 2022-11-07  8:49               ` Hsia-Jun Li
  0 siblings, 0 replies; 48+ messages in thread
From: Hsia-Jun Li @ 2022-11-07  8:49 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Helen Koike, mchehab, hans.verkuil, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga,
	Hans Verkuil, linux-media



On 11/7/22 16:28, Laurent Pinchart wrote:
> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> Hi Hsia-Jun,
> 
> On Mon, Nov 07, 2022 at 09:54:11AM +0800, Hsia-Jun Li wrote:
>> On 11/7/22 03:24, Laurent Pinchart wrote:
>>> On Sat, Nov 05, 2022 at 11:19:10PM +0800, Hsia-Jun Li wrote:
>>>> Hello Helen
>>>>
>>>> I didn't see any updates from V6 and V7-WIP in your repo. That is what I
>>>> need to for our complex tile formats in our platform.
>>>>
>>>> Any future plane here?
>>>>
>>>> Besides I have some ideas on these patches.
>>>>
>>>> On 2/24/21 23:12, Helen Koike wrote:
>>>>> Hi Hans,
>>>>>
>>>>> Thank you for your comment, please see my reply below.
>>>>>
>>>>> On 2/23/21 9:35 AM, Hans Verkuil wrote:
>>>>>> Hi Helen,
>>>>>>
>>>>>> On 14/01/2021 19:07, Helen Koike wrote:
>>>>>>> This is part of the multiplanar and singleplanar unification process.
>>>>>>> v4l2_ext_pix_format is supposed to work for both cases.
>>>>>>>
>>>>>>> We also add the concept of modifiers already employed in DRM to expose
>>>>>>> HW-specific formats (like tiled or compressed formats) and allow
>>>>>>> exchanging this information with the DRM subsystem in a consistent way.
>>>>>>>
>>>>>>> Note that only V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] are accepted in
>>>>>>> v4l2_ext_format, other types will be rejected if you use the
>>>>>>> {G,S,TRY}_EXT_PIX_FMT ioctls.
>>>>>>>
>>>>>>> New hooks have been added to v4l2_ioctl_ops to support those new ioctls
>>>>>>> in drivers, but, in the meantime, the core takes care of converting
>>>>>>> {S,G,TRY}_EXT_PIX_FMT requests into {S,G,TRY}_FMT so that old drivers
>>>>>>> can
>>>>>>> still work if the userspace app/lib uses the new ioctls.
>>>>>>>
>>>>>>> The conversion is also done the other around to allow userspace
>>>>>>> apps/libs using {S,G,TRY}_FMT to work with drivers implementing the
>>>>>>> _ext_ hooks.
>>>>>>
>>>>>> I have some small comments below, but also one high level comment:
>>>>>>
>>>>>> Regarding M variants of pixelformats: this patch 'normalizes' them to
>>>>>> regular pixelformats in the extended API. This makes life complicated,
>>>>>> and I wonder if this is the right approach.
>>>>>>
>>>>>> Currently there are two reasons for a driver to support e.g. NV12M:
>>>>>> either luma and chroma need to be in two memory banks, or the luma
>>>>>> and chroma planes cannot be contiguous due to alignment requirements.
>>>>>>
>>>>>> The first requirement is still valid for drivers that support the
>>>>>> extended API.
>>>>>> The second requirement is no longer a reason to support NV12M. But I
>>>>>> don't think we should just drop NV12M support if it was already supported
>>>>>> before the conversion to this extended API. Since NV12M allocates two
>>>>>> buffers
>>>>>> instead of one, it is still different from a regular NV12.
>>>>>
>>>>> I don't see what would be the difference when using NV12 and NV12M in
>>>>> Ext API.
>>>>> NV12 could be used for both requirements. It would even allow the second
>>>>> requirement with a single memory buffer.
>>>>>
>>>> Although I don't have problem here to support both single and multiple
>>>> planes in our hardware. But using the single plane format here is not
>>>> convience for us.
>>>>
>>>> The tile format in our platform is little complex, you can't calculate
>>>> the stride and sizeimage easily with just the modifier, width and
>>>> height. Then we don't have a good place to record the offset here.
>>>
>>> What else is needed to compute those values ? Can they be computed by
>>> the kernel driver, or do they have computed by userspace ?
>>
>> I could calculate the stride(bytesperline) and sizeimage in the kernel
>> driver. But it would be better to let the firmware do that. Our driver
>> would depend on the V4L2_EVENT_SOURCE_CHANGE event.
> 
> It could indeed be done in the firmware, but that would mean that the
> device would need to be powered up to implement VIDIOC_S_FMT and
> VIDIOC_TRY_FMT. 
Although I would power on the device here but that is not necessary. The 
firmware is running in TEE(CPU) and a remote processor. Parsing the 
bitstream could be done in TEE part of firmware.

The only reason that I need to power on the device is that I can't do it 
in the TEE.

Besides, there would be encrypted video streams(DRM video), the only 
possible to know their resolutions and other buffer informations should 
only be done in TEE.

The uvcvideo driver does that, which results in very
> slow startup for applications that try lots of formats. It would be best
> to avoid it if possible, and calculate the values in software in the
> kernel.
We have had this problem here. If I allocate the motion vector buffer by 
driver internally. I have to do either in buf_init() or 
start_streaming(). When I did it in the STREAMON of the capture queue, 
it would spend lots of time on allocating.

I think I need a stage that the user won't allocate more CAPTURE buffer.
> 
>>>> Besides, the luma and chroma planes would always need their own
>>>> compression meta data planes. If we use NV12 here, that would make a
>>>> very strange pixel format, one plane for the pixel datas and two planes
>>>> for the meta data.
>>>>
>>>>>> I would prefer that such drivers support both NV12 and NV12M, so no
>>>>>> automatic conversion.
>>>>>>
>>>>>> A related question is how to handle pixelformat enumeration: with the
>>>>>> extended API an NV12 format might work, but not with the old API (e.g.
>>>>>> due to memory alignment requirements). I wonder if a
>>>>>> VIDIOC_ENUM_EXT_PIX_FMT
>>>>>> isn't needed.
>>>>>
>>>>> We need VIDIOC_ENUM_EXT_PIX_FMT for modifiers, but we can add it later.
>>>>> If the driver reports NV12M, userspace can use it and the framework
>>>>> normilizes it.
>>>>>
>>>>>>
>>>>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
>>>>>> VIDIOC_ENUM_FMT
>>>>>> would just report NV12M.
>>>>>
>>>>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
>>>>> report both (unless I'm missing something, which is probably the case).
>>>>>
>>>>> The idea was to deprecate the M-variants one day.
>>>>
>>>> I was thinking the way in DRM API is better, always assuming it would
>>>> always in a multiple planes. The only problem is we don't have a way to
>>>> let the allocator that allocate contiguous memory for planes when we
>>>> need to do that.
>>>>
>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>>>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>>>>>>> ---
>>>>>>>
>>>>>>> Changes in v6:
>>>>>>>     The main change here was fixing the conversion, so planes reflects
>>>>>>> color planes,
>>>>>>>     and to implement this properly I made major refactors compared to
>>>>>>> the previous
>>>>>>>     version.
>>>>>>> - struct v4l2_plane_ext_pix_format removed, using struct
>>>>>>> v4l2_plane_pix_format instead (Tomasz)
>>>>>>> - refer to drm_fourcc.h in struct v4l2_ext_pix_format docs (Hans)
>>>>>>> - reorder colorimetry fields in struct v4l2_ext_pix_format (Hans)
>>>>>>> - do not set Ext ioctls as valid for vid_out_overlay (Tomasz)
>>>>>>> - refactor conversion functions, so planes are color planes (Tomasz)
>>>>>>> - Don't explicitly check for e->modifier != 0 in
>>>>>>> v4l2_ext_pix_format_to_format() (Tomasz)
>>>>>>> - Use "ef" for extended formats in the framework for consistency
>>>>>>> (Tomasz)
>>>>>>> - Handle xfer_func field in conversions (Tomasz)
>>>>>>> - Zero reserved fields in v4l_s_ext_pix_fmt() and
>>>>>>> v4l_try_ext_pix_fmt() (Tomasz)
>>>>>>> - Refactor format functions to use v4l_fmt_ioctl_via_ext()
>>>>>>> - Several fixes/refactoring/changes
>>>>>>> - Remove EXT API for touch devices
>>>>>>>
>>>>>>> Changes in v5:
>>>>>>> - change sizes and reorder fields to avoid holes in the struct and make
>>>>>>>      it the same for 32 and 64 bits
>>>>>>> - removed __attribute__ ((packed)) from uapi structs
>>>>>>> - Fix doc warning from make htmldocs
>>>>>>> - Updated commit message with EXT_PIX prefix for the ioctls.
>>>>>>>
>>>>>>> Changes in v4:
>>>>>>> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
>>>>>>> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
>>>>>>> - Add reserved fields
>>>>>>> - Removed num_planes from struct v4l2_ext_pix_format
>>>>>>> - Removed flag field from struct v4l2_ext_pix_format, since the only
>>>>>>>      defined value is V4L2_PIX_FMT_FLAG_PREMUL_ALPHA only used by vsp1,
>>>>>>>      where we can use modifiers, or add it back later through the reserved
>>>>>>>      bits.
>>>>>>> - In v4l2_ext_format_to_format(), check if modifier is != MOD_LINEAR &&
>>>>>>>      != MOD_INVALID
>>>>>>> - Fix type assignment in v4l_g_fmt_ext_pix()
>>>>>>> - Rebased on top of media/master (post 5.8-rc1)
>>>>>>>
>>>>>>> Changes in v3:
>>>>>>> - Rebased on top of media/master (post 5.4-rc1)
>>>>>>>
>>>>>>> Changes in v2:
>>>>>>> - Move the modifier in v4l2_ext_format (was formerly placed in
>>>>>>>      v4l2_ext_plane)
>>>>>>> - Fix a few bugs in the converters and add a strict parameter to
>>>>>>>      allow conversion of uninitialized/mis-initialized objects
>>>>>>> ---
>>>>>>>     drivers/media/v4l2-core/v4l2-dev.c   |  27 +-
>>>>>>>     drivers/media/v4l2-core/v4l2-ioctl.c | 538 +++++++++++++++++++++++++--
>>>>>>>     include/media/v4l2-ioctl.h           |  28 ++
>>>>>>>     include/uapi/linux/videodev2.h       |  41 ++
>>>>>>>     4 files changed, 602 insertions(+), 32 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c
>>>>>>> b/drivers/media/v4l2-core/v4l2-dev.c
>>>>>>> index f9cff033d0dc..5add58cb6d45 100644
>>>>>>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>>>>>>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>>>>>>> @@ -608,27 +608,42 @@ static void determine_valid_ioctls(struct
>>>>>>> video_device *vdev)
>>>>>>>                        ops->vidioc_enum_fmt_vid_overlay)) ||
>>>>>>>                 (is_tx && ops->vidioc_enum_fmt_vid_out))
>>>>>>>                 set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
>>>>>>> +        if ((is_rx && ops->vidioc_g_fmt_vid_overlay) ||
>>>>>>> +            (is_tx && ops->vidioc_g_fmt_vid_out_overlay))
>>>>>>> +             set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>>>>>>>             if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
>>>>>>>                        ops->vidioc_g_fmt_vid_cap_mplane ||
>>>>>>> -                   ops->vidioc_g_fmt_vid_overlay)) ||
>>>>>>> +                   ops->vidioc_g_ext_pix_fmt_vid_cap)) ||
>>>>>>>                 (is_tx && (ops->vidioc_g_fmt_vid_out ||
>>>>>>>                        ops->vidioc_g_fmt_vid_out_mplane ||
>>>>>>> -                   ops->vidioc_g_fmt_vid_out_overlay)))
>>>>>>> +                   ops->vidioc_g_ext_pix_fmt_vid_out))) {
>>>>>>>                  set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
>>>>>>> +             set_bit(_IOC_NR(VIDIOC_G_EXT_PIX_FMT), valid_ioctls);
>>>>>>> +        }
>>>>>>> +        if ((is_rx && ops->vidioc_s_fmt_vid_overlay) ||
>>>>>>> +            (is_tx && ops->vidioc_s_fmt_vid_out_overlay))
>>>>>>> +             set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>>>>>>>             if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
>>>>>>>                        ops->vidioc_s_fmt_vid_cap_mplane ||
>>>>>>> -                   ops->vidioc_s_fmt_vid_overlay)) ||
>>>>>>> +                   ops->vidioc_s_ext_pix_fmt_vid_cap)) ||
>>>>>>>                 (is_tx && (ops->vidioc_s_fmt_vid_out ||
>>>>>>>                        ops->vidioc_s_fmt_vid_out_mplane ||
>>>>>>> -                   ops->vidioc_s_fmt_vid_out_overlay)))
>>>>>>> +                   ops->vidioc_s_ext_pix_fmt_vid_out))) {
>>>>>>>                  set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
>>>>>>> +             set_bit(_IOC_NR(VIDIOC_S_EXT_PIX_FMT), valid_ioctls);
>>>>>>> +        }
>>>>>>> +        if ((is_rx && ops->vidioc_try_fmt_vid_overlay) ||
>>>>>>> +            (is_tx && ops->vidioc_try_fmt_vid_out_overlay))
>>>>>>> +             set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>>>>>>>             if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
>>>>>>>                        ops->vidioc_try_fmt_vid_cap_mplane ||
>>>>>>> -                   ops->vidioc_try_fmt_vid_overlay)) ||
>>>>>>> +                   ops->vidioc_try_ext_pix_fmt_vid_cap)) ||
>>>>>>>                 (is_tx && (ops->vidioc_try_fmt_vid_out ||
>>>>>>>                        ops->vidioc_try_fmt_vid_out_mplane ||
>>>>>>> -                   ops->vidioc_try_fmt_vid_out_overlay)))
>>>>>>> +                   ops->vidioc_try_ext_pix_fmt_vid_out))) {
>>>>>>>                  set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
>>>>>>> +             set_bit(_IOC_NR(VIDIOC_TRY_EXT_PIX_FMT), valid_ioctls);
>>>>>>> +        }
>>>>>>>             SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
>>>>>>>             SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
>>>>>>>             SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
>>>>>>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c
>>>>>>> b/drivers/media/v4l2-core/v4l2-ioctl.c
>>>>>>> index 848286a284f6..a9c07c0a73ec 100644
>>>>>>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>>>>>>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>>>>>>> @@ -18,6 +18,8 @@
>>>>>>>     #include <linux/videodev2.h>
>>>>>>> +#include <drm/drm_fourcc.h>
>>>>>>> +
>>>>>>>     #include <media/v4l2-common.h>
>>>>>>>     #include <media/v4l2-ioctl.h>
>>>>>>>     #include <media/v4l2-ctrls.h>
>>>>>>> @@ -38,6 +40,11 @@
>>>>>>>     #define is_valid_ioctl(vfd, cmd) test_bit(_IOC_NR(cmd),
>>>>>>> (vfd)->valid_ioctls)
>>>>>>> +#define V4L2_IS_CAP_MULTIPLANAR(vdev)    (vdev->device_caps & \
>>>>>>> +                     (V4L2_CAP_VIDEO_CAPTURE_MPLANE | \
>>>>>>> +                     V4L2_CAP_VIDEO_OUTPUT_MPLANE | \
>>>>>>> +                     V4L2_CAP_VIDEO_M2M_MPLANE))
>>>>>>> +
>>>>>>>     struct std_descr {
>>>>>>>         v4l2_std_id std;
>>>>>>>         const char *descr;
>>>>>>> @@ -379,6 +386,27 @@ static void v4l_print_format(const void *arg,
>>>>>>> bool write_only)
>>>>>>>         }
>>>>>>>     }
>>>>>>> +static void v4l_print_ext_pix_format(const void *arg, bool write_only)
>>>>>>> +{
>>>>>>> +    const struct v4l2_ext_pix_format *ef = arg;
>>>>>>> +    unsigned int i;
>>>>>>> +
>>>>>>> +    pr_cont("type=%s, width=%u, height=%u, format=%c%c%c%c, modifier
>>>>>>> %llx, field=%s, colorspace=%d, ycbcr_enc=%u, quantization=%u,
>>>>>>> xfer_func=%u\n",
>>>>>>> +        prt_names(ef->type, v4l2_type_names),
>>>>>>> +        ef->width, ef->height,
>>>>>>> +        (ef->pixelformat & 0xff),
>>>>>>> +        (ef->pixelformat >>  8) & 0xff,
>>>>>>> +        (ef->pixelformat >> 16) & 0xff,
>>>>>>> +        (ef->pixelformat >> 24) & 0xff,
>>>>>>> +        ef->modifier, prt_names(ef->field, v4l2_field_names),
>>>>>>> +        ef->colorspace, ef->ycbcr_enc,
>>>>>>> +        ef->quantization, ef->xfer_func);
>>>>>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage;
>>>>>>> i++)
>>>>>>> +        pr_debug("plane %u: bytesperline=%u sizeimage=%u\n",
>>>>>>> +             i, ef->plane_fmt[i].bytesperline,
>>>>>>> +             ef->plane_fmt[i].sizeimage);
>>>>>>> +}
>>>>>>> +
>>>>>>>     static void v4l_print_framebuffer(const void *arg, bool write_only)
>>>>>>>     {
>>>>>>>         const struct v4l2_framebuffer *p = arg;
>>>>>>> @@ -963,11 +991,15 @@ static int check_fmt(struct file *file, enum
>>>>>>> v4l2_buf_type type)
>>>>>>>         switch (type) {
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>>>             if ((is_vid || is_tch) && is_rx &&
>>>>>>> -            (ops->vidioc_g_fmt_vid_cap ||
>>>>>>> ops->vidioc_g_fmt_vid_cap_mplane))
>>>>>>> +            (ops->vidioc_g_fmt_vid_cap ||
>>>>>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap ||
>>>>>>> +             ops->vidioc_g_fmt_vid_cap_mplane))
>>>>>>>                 return 0;
>>>>>>>             break;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>>>> -        if ((is_vid || is_tch) && is_rx &&
>>>>>>> ops->vidioc_g_fmt_vid_cap_mplane)
>>>>>>> +        if ((is_vid || is_tch) && is_rx &&
>>>>>>> +            (ops->vidioc_g_fmt_vid_cap_mplane ||
>>>>>>> +             ops->vidioc_g_ext_pix_fmt_vid_cap))
>>>>>>>                 return 0;
>>>>>>>             break;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>>>> @@ -976,11 +1008,15 @@ static int check_fmt(struct file *file, enum
>>>>>>> v4l2_buf_type type)
>>>>>>>             break;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>>>             if (is_vid && is_tx &&
>>>>>>> -            (ops->vidioc_g_fmt_vid_out ||
>>>>>>> ops->vidioc_g_fmt_vid_out_mplane))
>>>>>>> +            (ops->vidioc_g_fmt_vid_out ||
>>>>>>> +             ops->vidioc_g_ext_pix_fmt_vid_out ||
>>>>>>> +             ops->vidioc_g_fmt_vid_out_mplane))
>>>>>>>                 return 0;
>>>>>>>             break;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>>>> -        if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
>>>>>>> +        if (is_vid && is_tx &&
>>>>>>> +            (ops->vidioc_g_ext_pix_fmt_vid_out ||
>>>>>>> +             ops->vidioc_g_fmt_vid_out_mplane))
>>>>>>>                 return 0;
>>>>>>>             break;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>>>> @@ -1060,6 +1096,204 @@ static void v4l_sanitize_format(struct
>>>>>>> v4l2_format *fmt)
>>>>>>>                sizeof(fmt->fmt.pix) - offset);
>>>>>>>     }
>>>>>>> +static void
>>>>>>> +v4l2_ext_pix_format_to_pix_format(const struct v4l2_ext_pix_format *ef,
>>>>>>> +                  struct v4l2_pix_format *pix)
>>>>>>> +{
>>>>>>> +    unsigned int i;
>>>>>>> +
>>>>>>> +    pix->width = ef->width;
>>>>>>> +    pix->height = ef->height;
>>>>>>> +    pix->field = ef->field;
>>>>>>> +    pix->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>>>>>>> +    pix->colorspace = ef->colorspace;
>>>>>>> +    pix->ycbcr_enc = ef->ycbcr_enc;
>>>>>>> +    pix->priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>>> +    pix->quantization = ef->quantization;
>>>>>>> +    pix->pixelformat = ef->pixelformat;
>>>>>>> +    pix->bytesperline = ef->plane_fmt[0].bytesperline;
>>>>>>> +    pix->sizeimage = ef->plane_fmt[0].sizeimage;
>>>>>>> +    for (i = 1; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage;
>>>>>>> i++)
>>>>>>> +        pix->sizeimage += ef->plane_fmt[i].sizeimage;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void
>>>>>>> +v4l2_ext_pix_format_to_pix_mp_format(const struct
>>>>>>> v4l2_ext_pix_format *ef,
>>>>>>> +                     struct v4l2_pix_format_mplane *pix_mp)
>>>>>>> +{
>>>>>>> +    const struct v4l2_format_info *info =
>>>>>>> +                    v4l2_format_info(ef->pixelformat);
>>>>>>> +    unsigned int i;
>>>>>>> +
>>>>>>> +    pix_mp->width = ef->width;
>>>>>>> +    pix_mp->height = ef->height;
>>>>>>> +    pix_mp->field = ef->field;
>>>>>>> +    pix_mp->flags = V4L2_PIX_FMT_FLAG_SET_CSC;
>>>>>>> +    pix_mp->colorspace = ef->colorspace;
>>>>>>> +    pix_mp->ycbcr_enc = ef->ycbcr_enc;
>>>>>>> +    pix_mp->quantization = ef->quantization;
>>>>>>> +    pix_mp->pixelformat = ef->pixelformat;
>>>>>>> +
>>>>>>> +    /* This is true when converting to non-M-variant */
>>>>>>> +    if (info && info->mem_planes == 1) {
>>>>>>> +        pix_mp->plane_fmt[0] = ef->plane_fmt[0];
>>>>>>> +        pix_mp->num_planes = 1;
>>>>>>> +        for (i = 1; i < VIDEO_MAX_PLANES &&
>>>>>>> ef->plane_fmt[i].sizeimage; i++)
>>>>>>> +            pix_mp->plane_fmt[0].sizeimage +=
>>>>>>> ef->plane_fmt[i].sizeimage;
>>>>>>> +
>>>>>>> +        return;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    for (i = 0; i < VIDEO_MAX_PLANES && ef->plane_fmt[i].sizeimage;
>>>>>>> i++)
>>>>>>> +        pix_mp->plane_fmt[i] = ef->plane_fmt[i];
>>>>>>> +    pix_mp->num_planes = i;
>>>>>>> +}
>>>>>>> +
>>>>>>> +/*
>>>>>>> + * v4l2_ext_pix_format_to_format - convert to v4l2_ext_pix_format to
>>>>>>> v4l2_format
>>>>>>> + *
>>>>>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be converted.
>>>>>>> + * @f: A pointer to struct v4l2_format to be filled.
>>>>>>> + * @is_mplane: Bool indicating if multiplanar API should be used in @f.
>>>>>>> + *
>>>>>>> + * If pixelformat should be converted to M-variant, change
>>>>>>> ef->pixelformat
>>>>>>> + * to the M-variant before calling this function.
>>>>>>> + */
>>>>>>> +static void v4l2_ext_pix_format_to_format(const struct
>>>>>>> v4l2_ext_pix_format *ef,
>>>>>>> +                      struct v4l2_format *f, bool is_mplane)
>>>>>>> +{
>>>>>>> +    memset(f, 0, sizeof(*f));
>>>>>>> +
>>>>>>> +    if (ef->modifier != DRM_FORMAT_MOD_LINEAR &&
>>>>>>> +        ef->modifier != DRM_FORMAT_MOD_INVALID)
>>>>>>> +        pr_warn("Modifiers are not supported in v4l2_format,
>>>>>>> ignoring %llx\n",
>>>>>>> +            ef->modifier);
>>>>>>> +
>>>>>>> +    if (!is_mplane) {
>>>>>>> +        f->type = ef->type;
>>>>>>> +        v4l2_ext_pix_format_to_pix_format(ef, &f->fmt.pix);
>>>>>>> +        return;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    if (ef->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>>>>>>> +        f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>>>>>>> +    else
>>>>>>> +        f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
>>>>>>> +
>>>>>>> +    v4l2_ext_pix_format_to_pix_mp_format(ef, &f->fmt.pix_mp);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void
>>>>>>> +v4l2_pix_format_to_ext_pix_format(const struct v4l2_pix_format *pix,
>>>>>>> +                  struct v4l2_ext_pix_format *ef)
>>>>>>> +{
>>>>>>> +    const struct v4l2_format_info *info =
>>>>>>> +                    v4l2_format_info(pix->pixelformat);
>>>>>>> +    unsigned int i;
>>>>>>> +
>>>>>>> +    ef->width = pix->width;
>>>>>>> +    ef->height = pix->height;
>>>>>>> +    ef->field = pix->field;
>>>>>>> +    ef->colorspace = pix->colorspace;
>>>>>>> +    ef->ycbcr_enc = pix->ycbcr_enc;
>>>>>>> +    ef->quantization = pix->quantization;
>>>>>>> +    ef->xfer_func = pix->xfer_func;
>>>>>>> +    if (pix->flags)
>>>>>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix->flags);
>>>>>>> +
>>>>>>> +    /* We assume M-variants won't be used in this function */
>>>>>>> +    ef->pixelformat = pix->pixelformat;
>>>>>>> +
>>>>>>> +    ef->plane_fmt[0].bytesperline = pix->bytesperline;
>>>>>>> +    ef->plane_fmt[0].sizeimage = pix->sizeimage;
>>>>>>> +
>>>>>>> +    if (!info)
>>>>>>> +        return;
>>>>>>> +
>>>>>>> +    for (i = 1; i < info->comp_planes; i++) {
>>>>>>> +        ef->plane_fmt[i].bytesperline = pix->bytesperline / info->hdiv;
>>>>>>> +        ef->plane_fmt[i].sizeimage = ef->plane_fmt[i].bytesperline *
>>>>>>> +                         ef->height / info->vdiv;
>>>>>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>>>>>>> +    }
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void
>>>>>>> +v4l2_pix_mp_format_to_ext_pix_format(const struct
>>>>>>> v4l2_pix_format_mplane *pix_mp,
>>>>>>> +                     struct v4l2_ext_pix_format *ef)
>>>>>>> +{
>>>>>>> +    const struct v4l2_format_info *info =
>>>>>>> +                    v4l2_format_info(pix_mp->pixelformat);
>>>>>>> +    unsigned int i;
>>>>>>> +
>>>>>>> +    ef->width = pix_mp->width;
>>>>>>> +    ef->height = pix_mp->height;
>>>>>>> +    ef->field = pix_mp->field;
>>>>>>> +    ef->colorspace = pix_mp->colorspace;
>>>>>>> +    ef->ycbcr_enc = pix_mp->ycbcr_enc;
>>>>>>> +    ef->quantization = pix_mp->quantization;
>>>>>>> +    ef->xfer_func = pix_mp->xfer_func;
>>>>>>> +    if (pix_mp->flags)
>>>>>>> +        pr_warn("Ignoring pixelformat flags 0x%x\n", pix_mp->flags);
>>>>>>> +
>>>>>>> +    if (!info)
>>>>>>> +        return;
>>>>>>> +
>>>>>>> +    ef->pixelformat = info && info->norm ?
>>>>>>
>>>>>> 'info &&' can be dropped, info is always non-NULL here.
>>>>>>
>>>>>>> +              info->norm : pix_mp->pixelformat;
>>>>>>> +
>>>>>>> +    if (info->comp_planes == info->mem_planes) {
>>>>>>> +        for (i = 0; i < pix_mp->num_planes && i < VIDEO_MAX_PLANES;
>>>>>>> i++)
>>>>>>> +            ef->plane_fmt[i] = pix_mp->plane_fmt[i];
>>>>>>> +
>>>>>>> +        return;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    /* case where mem_planes is 1 and comp_planes > 1 */
>>>>>>> +    ef->plane_fmt[0] = pix_mp->plane_fmt[0];
>>>>>>> +    for (i = 1; i < info->comp_planes; i++) {
>>>>>>> +        ef->plane_fmt[i].bytesperline =
>>>>>>> +            pix_mp->plane_fmt[0].bytesperline / info->hdiv;
>>>>>>> +        ef->plane_fmt[i].sizeimage =
>>>>>>> +            ef->plane_fmt[i].bytesperline * ef->height / info->vdiv;
>>>>>>> +        ef->plane_fmt[0].sizeimage -= ef->plane_fmt[i].sizeimage;
>>>>>>> +    }
>>>>>>> +}
>>>>>>> +
>>>>>>> +/*
>>>>>>> + * v4l2_format_to_ext_pix_format - convert to v4l2_format to
>>>>>>> v4l2_ext_pix_format
>>>>>>> + *
>>>>>>> + * @f: A pointer to struct v4l2_format to be converted.
>>>>>>> + * @ef: A pointer to struct struct v4l2_ext_pix_format to be filled.
>>>>>>> + *
>>>>>>> + * This method normalize the pixelformat to non-M variant.
>>>>>>> + */
>>>>>>> +static void v4l2_format_to_ext_pix_format(const struct v4l2_format *f,
>>>>>>> +                      struct v4l2_ext_pix_format *ef)
>>>>>>> +{
>>>>>>> +    memset(ef, 0, sizeof(*ef));
>>>>>>> +
>>>>>>> +    switch (f->type) {
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>>> +        ef->type = f->type;
>>>>>>> +        v4l2_pix_format_to_ext_pix_format(&f->fmt.pix, ef);
>>>>>>> +        break;
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>>>>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>>>>>>> +        break;
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>>>> +        ef->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>>>>>> +        v4l2_pix_mp_format_to_ext_pix_format(&f->fmt.pix_mp, ef);
>>>>>>> +        break;
>>>>>>> +    default:
>>>>>>> +        WARN("Converting to Ext Pix Format with wrong buffer type
>>>>>>> %s\n",
>>>>>>> +             prt_names(f->type, v4l2_type_names));
>>>>>>> +        break;
>>>>>>> +    }
>>>>>>> +}
>>>>>>> +
>>>>>>>     static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
>>>>>>>                     struct file *file, void *fh, void *arg)
>>>>>>>     {
>>>>>>> @@ -1565,6 +1799,100 @@ static void v4l_pix_format_touch(struct
>>>>>>> v4l2_pix_format *p)
>>>>>>>         p->xfer_func = 0;
>>>>>>>     }
>>>>>>> +static int v4l_fmt_ioctl_via_ext(const struct v4l2_ioctl_ops *ops,
>>>>>>> +                 struct file *file, void *fh,
>>>>>>> +                 struct v4l2_format *f,
>>>>>>> +                 unsigned int ioctl)
>>>>>>> +{
>>>>>>> +    bool is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(f->type);
>>>>>>> +    struct video_device *vdev = video_devdata(file);
>>>>>>> +    struct v4l2_ext_pix_format ef = {0};
>>>>>>> +    u32 original_pixfmt = 0;
>>>>>>> +    u32 cap_mask;
>>>>>>> +    int ret;
>>>>>>> +
>>>>>>> +    if (ioctl != VIDIOC_G_FMT) {
>>>>>>> +        /*
>>>>>>> +         * If CSC attributes are read only, set them to DEFAULT
>>>>>>> +         * to avoid changes by the driver.
>>>>>>> +         */
>>>>>>> +        if (is_multiplanar) {
>>>>>>> +            if (!(f->fmt.pix_mp.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>>>>>>> +                f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
>>>>>>> +                f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>>>>>>> +                f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
>>>>>>> +                f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>>>>>>> +            }
>>>>>>> +            /* Unset the flag to avoid warning in the convertion */
>>>>>>> +            f->fmt.pix_mp.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>>>>>>> +
>>>>>>> +            /* Save pixelformat in case M-variant is being used */
>>>>>>> +            original_pixfmt = f->fmt.pix_mp.pixelformat;
>>>>>>> +        } else {
>>>>>>> +            if (!(f->fmt.pix.flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
>>>>>>> +                f->fmt.pix.colorspace = V4L2_COLORSPACE_DEFAULT;
>>>>>>> +                f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
>>>>>>> +                f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
>>>>>>> +                f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
>>>>>>> +            }
>>>>>>> +            /* Unset the flag to avoid warning in the convertion */
>>>>>>> +            f->fmt.pix.flags &= ~V4L2_PIX_FMT_FLAG_SET_CSC;
>>>>>>> +        }
>>>>>>> +        v4l2_format_to_ext_pix_format(f, &ef);
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    switch (f->type) {
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>>>> +        cap_mask = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
>>>>>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
>>>>>>> +        if (!!(vdev->device_caps & cap_mask) !=
>>>>>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
>>>>>>> +            return -EINVAL;
>>>>>>> +
>>>>>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>>>>>> +        if (ioctl == VIDIOC_G_FMT)
>>>>>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, &ef);
>>>>>>> +        else if (ioctl == VIDIOC_S_FMT)
>>>>>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, &ef);
>>>>>>> +        else
>>>>>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>>>>>>> +                                  &ef);
>>>>>>> +        break;
>>>>>>> +
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>>>> +        cap_mask = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
>>>>>>> +               V4L2_CAP_VIDEO_M2M_MPLANE;
>>>>>>> +        if (!!(vdev->device_caps & cap_mask) !=
>>>>>>> +            (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
>>>>>>> +            return -EINVAL;
>>>>>>> +
>>>>>>> +        ef.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>>>>>> +        if (ioctl == VIDIOC_G_FMT)
>>>>>>> +            ret = ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, &ef);
>>>>>>> +        else if (ioctl == VIDIOC_S_FMT)
>>>>>>> +            ret = ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, &ef);
>>>>>>> +        else
>>>>>>> +            ret = ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>>>>>>> +                                  &ef);
>>>>>>> +        break;
>>>>>>> +
>>>>>>> +    default:
>>>>>>> +        return -EINVAL;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>> +    if (original_pixfmt != ef.pixelformat &&
>>>>>>> +        v4l2_format_info(original_pixfmt))
>>>>>>
>>>>>> Could this test be simplified to: 'if (original_pixfmt)'?
>>>>>>
>>>>>> I.e., if the original pixfmt was saved, then restore it here.
>>>>>>
>>>>>>> +        ef.pixelformat = original_pixfmt;
>>>>>>> +
>>>>>>> +    v4l2_ext_pix_format_to_format(&ef, f, is_multiplanar);
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>>     static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>>>>>>                     struct file *file, void *fh, void *arg)
>>>>>>>     {
>>>>>>> @@ -1601,17 +1929,26 @@ static int v4l_g_fmt(const struct
>>>>>>> v4l2_ioctl_ops *ops,
>>>>>>>         switch (p->type) {
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_cap))
>>>>>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_cap &&
>>>>>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_cap))
>>>>>>>                 break;
>>>>>>>             p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>>> -        ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
>>>>>>> +        ret = ops->vidioc_g_fmt_vid_cap ?
>>>>>>> +              ops->vidioc_g_fmt_vid_cap(file, fh, arg) :
>>>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>>>> +                        VIDIOC_G_FMT);
>>>>>>>             /* just in case the driver zeroed it again */
>>>>>>>             p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>>>             if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>>>>>                 v4l_pix_format_touch(&p->fmt.pix);
>>>>>>>             return ret;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>>>> -        return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>>>>>> +        if (ops->vidioc_g_fmt_vid_cap_mplane)
>>>>>>> +            return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>>>>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_cap)
>>>>>>
>>>>>> 'else' can be dropped.
>>>>>>
>>>>>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>>>> +                             VIDIOC_G_FMT);
>>>>>>> +        break;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>>>>             return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>>>>>>>         case V4L2_BUF_TYPE_VBI_CAPTURE:
>>>>>>> @@ -1619,15 +1956,23 @@ static int v4l_g_fmt(const struct
>>>>>>> v4l2_ioctl_ops *ops,
>>>>>>>         case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>>>>>>>             return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>>> -        if (unlikely(!ops->vidioc_g_fmt_vid_out))
>>>>>>> +        if (unlikely(!ops->vidioc_g_fmt_vid_out &&
>>>>>>> +                 !ops->vidioc_g_ext_pix_fmt_vid_out))
>>>>>>>                 break;
>>>>>>>             p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>>> -        ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
>>>>>>> +        ret = ops->vidioc_g_fmt_vid_out ?
>>>>>>> +              ops->vidioc_g_fmt_vid_out(file, fh, arg) :
>>>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_G_FMT);
>>>>>>>             /* just in case the driver zeroed it again */
>>>>>>>             p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>>>             return ret;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>>>> -        return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>>>>>> +        if (ops->vidioc_g_fmt_vid_out_mplane)
>>>>>>> +            return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>>>>>> +        else if (ops->vidioc_g_ext_pix_fmt_vid_out)
>>>>>>
>>>>>> Ditto.
>>>>>>
>>>>>>> +            return v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>>>> +                             VIDIOC_G_FMT);
>>>>>>> +        break;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>>>>             return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>>>>>>>         case V4L2_BUF_TYPE_VBI_OUTPUT:
>>>>>>> @@ -1646,6 +1991,42 @@ static int v4l_g_fmt(const struct
>>>>>>> v4l2_ioctl_ops *ops,
>>>>>>>         return -EINVAL;
>>>>>>>     }
>>>>>>> +static int v4l_g_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>>>>>> +                 struct file *file, void *fh, void *arg)
>>>>>>> +{
>>>>>>> +    struct v4l2_ext_pix_format *ef = arg;
>>>>>>> +    struct v4l2_format f = {
>>>>>>> +        .type = ef->type,
>>>>>>> +    };
>>>>>>> +    int ret = check_fmt(file, ef->type);
>>>>>>> +
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>> +    memset(ef, 0, sizeof(*ef));
>>>>>>> +    ef->type = f.type;
>>>>>>> +
>>>>>>> +    switch (f.type) {
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_cap)
>>>>>>> +            return ops->vidioc_g_ext_pix_fmt_vid_cap(file, fh, ef);
>>>>>>> +        break;
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>>> +        if (ops->vidioc_g_ext_pix_fmt_vid_out)
>>>>>>> +            return ops->vidioc_g_ext_pix_fmt_vid_out(file, fh, ef);
>>>>>>> +        break;
>>>>>>> +    default:
>>>>>>> +        return -EINVAL;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    ret = v4l_g_fmt(ops, file, fh, &f);
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>>     static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>>>>>>                     struct file *file, void *fh, void *arg)
>>>>>>>     {
>>>>>>> @@ -1664,23 +2045,29 @@ static int v4l_s_fmt(const struct
>>>>>>> v4l2_ioctl_ops *ops,
>>>>>>>         switch (p->type) {
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap))
>>>>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap &&
>>>>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>>>>>>                 break;
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.pix);
>>>>>>> -        ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
>>>>>>> +        ret = ops->vidioc_s_fmt_vid_cap ?
>>>>>>> +              ops->vidioc_s_fmt_vid_cap(file, fh, arg) :
>>>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>>>>>             /* just in case the driver zeroed it again */
>>>>>>>             p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>>>             if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>>>>>                 v4l_pix_format_touch(&p->fmt.pix);
>>>>>>>             return ret;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
>>>>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane &&
>>>>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_cap))
>>>>>>>                 break;
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>>>>             for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>>>>                 CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>>>>                           bytesperline);
>>>>>>> -        return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
>>>>>>> +        return ops->vidioc_s_fmt_vid_cap_mplane ?
>>>>>>> +               ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg) :
>>>>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>>>> VIDIOC_S_FMT);
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>>>>             if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>>>>>>>                 break;
>>>>>>> @@ -1697,21 +2084,27 @@ static int v4l_s_fmt(const struct
>>>>>>> v4l2_ioctl_ops *ops,
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>>>>>>             return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out))
>>>>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out &&
>>>>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
>>>>>>>                 break;
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.pix);
>>>>>>> -        ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
>>>>>>> +        ret = ops->vidioc_s_fmt_vid_out ?
>>>>>>> +              ops->vidioc_s_fmt_vid_out(file, fh, arg) :
>>>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>>>>>             /* just in case the driver zeroed it again */
>>>>>>>             p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>>>             return ret;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>>>> -        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
>>>>>>> +        if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane &&
>>>>>>> +                 !ops->vidioc_s_ext_pix_fmt_vid_out))
>>>>>>>                 break;
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>>>>             for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>>>>                 CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>>>>                           bytesperline);
>>>>>>> -        return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
>>>>>>> +        return ops->vidioc_s_fmt_vid_out_mplane ?
>>>>>>> +               ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg) :
>>>>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg, VIDIOC_S_FMT);
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>>>>             if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>>>>>>>                 break;
>>>>>>> @@ -1751,6 +2144,43 @@ static int v4l_s_fmt(const struct
>>>>>>> v4l2_ioctl_ops *ops,
>>>>>>>         return -EINVAL;
>>>>>>>     }
>>>>>>> +static int v4l_s_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>>>>>> +                 struct file *file, void *fh, void *arg)
>>>>>>> +{
>>>>>>> +    struct video_device *vfd = video_devdata(file);
>>>>>>> +    struct v4l2_ext_pix_format *ef = arg;
>>>>>>> +    struct v4l2_format f;
>>>>>>> +    int ret = check_fmt(file, ef->type);
>>>>>>> +
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
>>>>>>> +
>>>>>>> +    switch (ef->type) {
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_cap)
>>>>>>> +            return ops->vidioc_s_ext_pix_fmt_vid_cap(file, fh, ef);
>>>>>>> +        break;
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>>> +        if (ops->vidioc_s_ext_pix_fmt_vid_out)
>>>>>>> +            return ops->vidioc_s_ext_pix_fmt_vid_out(file, fh, ef);
>>>>>>> +        break;
>>>>>>> +    default:
>>>>>>> +        return -EINVAL;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    v4l2_ext_pix_format_to_format(ef, &f,
>>>>>>> V4L2_IS_CAP_MULTIPLANAR(vfd));
>>>>>>> +
>>>>>>> +    ret = v4l_s_fmt(ops, file, fh, &f);
>>>>>>> +    if (ret)
>>>>>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
>>>>>>
>>>>>> See my comments on this at the top.
>>>>>>
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>>     static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>>>>>>                     struct file *file, void *fh, void *arg)
>>>>>>>     {
>>>>>>> @@ -1766,23 +2196,30 @@ static int v4l_try_fmt(const struct
>>>>>>> v4l2_ioctl_ops *ops,
>>>>>>>         switch (p->type) {
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap))
>>>>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap &&
>>>>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>>>>                 break;
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.pix);
>>>>>>> -        ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
>>>>>>> +        ret = ops->vidioc_try_fmt_vid_cap ?
>>>>>>> +              ops->vidioc_try_fmt_vid_cap(file, fh, arg) :
>>>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>>>> VIDIOC_TRY_FMT);
>>>>>>>             /* just in case the driver zeroed it again */
>>>>>>>             p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>>>             if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>>>>>>                 v4l_pix_format_touch(&p->fmt.pix);
>>>>>>>             return ret;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>>>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
>>>>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane &&
>>>>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>>>>                 break;
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>>>>             for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>>>>                 CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>>>>                           bytesperline);
>>>>>>> -        return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
>>>>>>> +        return ops->vidioc_try_fmt_vid_cap_mplane ?
>>>>>>> +               ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg) :
>>>>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>>>> +                         VIDIOC_TRY_FMT);
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>>>>>>             if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>>>>>>>                 break;
>>>>>>> @@ -1799,21 +2236,28 @@ static int v4l_try_fmt(const struct
>>>>>>> v4l2_ioctl_ops *ops,
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.sliced.io_size);
>>>>>>>             return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out))
>>>>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out &&
>>>>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>>>>                 break;
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.pix);
>>>>>>> -        ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
>>>>>>> +        ret = ops->vidioc_try_fmt_vid_out ?
>>>>>>> +              ops->vidioc_try_fmt_vid_out(file, fh, arg) :
>>>>>>> +              v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>>>> VIDIOC_TRY_FMT);
>>>>>>>             /* just in case the driver zeroed it again */
>>>>>>>             p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>>>>>>             return ret;
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>>>>>>> -        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
>>>>>>> +        if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane &&
>>>>>>> +                 !ops->vidioc_try_ext_pix_fmt_vid_cap))
>>>>>>>                 break;
>>>>>>>             CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>>>>>>             for (i = 0; i < p->fmt.pix_mp.num_planes; i++)
>>>>>>>                 CLEAR_AFTER_FIELD(&p->fmt.pix_mp.plane_fmt[i],
>>>>>>>                           bytesperline);
>>>>>>> -        return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
>>>>>>> +        return ops->vidioc_try_fmt_vid_out_mplane ?
>>>>>>> +               ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg) :
>>>>>>> +               v4l_fmt_ioctl_via_ext(ops, file, fh, arg,
>>>>>>> +                         VIDIOC_TRY_FMT);
>>>>>>>         case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>>>>>>             if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>>>>>>>                 break;
>>>>>>> @@ -1853,6 +2297,45 @@ static int v4l_try_fmt(const struct
>>>>>>> v4l2_ioctl_ops *ops,
>>>>>>>         return -EINVAL;
>>>>>>>     }
>>>>>>> +static int v4l_try_ext_pix_fmt(const struct v4l2_ioctl_ops *ops,
>>>>>>> +                   struct file *file, void *fh, void *arg)
>>>>>>> +{
>>>>>>> +    struct video_device *vfd = video_devdata(file);
>>>>>>> +    struct v4l2_ext_pix_format *ef = arg;
>>>>>>> +    struct v4l2_format f;
>>>>>>> +    int ret = check_fmt(file, ef->type);
>>>>>>> +
>>>>>>> +    if (ret)
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>> +    memset(ef->reserved, 0, sizeof(ef->reserved));
>>>>>>> +
>>>>>>> +    switch (ef->type) {
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>>>>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_cap)
>>>>>>> +            return ops->vidioc_try_ext_pix_fmt_vid_cap(file, fh,
>>>>>>> +                                   ef);
>>>>>>> +        break;
>>>>>>> +    case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>>>>>>> +        if (ops->vidioc_try_ext_pix_fmt_vid_out)
>>>>>>> +            return ops->vidioc_try_ext_pix_fmt_vid_out(file, fh,
>>>>>>> +                                   ef);
>>>>>>> +        break;
>>>>>>> +    default:
>>>>>>> +        return -EINVAL;
>>>>>>> +    }
>>>>>>> +
>>>>>>> +    v4l2_ext_pix_format_to_format(ef, &f,
>>>>>>> V4L2_IS_CAP_MULTIPLANAR(vfd));
>>>>>>> +
>>>>>>> +    ret = v4l_try_fmt(ops, file, fh, &f);
>>>>>>> +    if (ret)
>>>>>>> +        /* TODO: retry with M-variant of ef->pixelformat? */
>>>>>>> +        return ret;
>>>>>>> +
>>>>>>> +    v4l2_format_to_ext_pix_format(&f, ef);
>>>>>>> +    return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>>     static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
>>>>>>>                     struct file *file, void *fh, void *arg)
>>>>>>>     {
>>>>>>> @@ -2854,6 +3337,9 @@ static const struct v4l2_ioctl_info
>>>>>>> v4l2_ioctls[] = {
>>>>>>>         IOCTL_INFO(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands,
>>>>>>> v4l_print_freq_band, 0),
>>>>>>>         IOCTL_INFO(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info,
>>>>>>> v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)),
>>>>>>>         IOCTL_INFO(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl,
>>>>>>> v4l_print_query_ext_ctrl, INFO_FL_CTRL |
>>>>>>> INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)),
>>>>>>> +    IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt,
>>>>>>> v4l_print_ext_pix_format, 0),
>>>>>>> +    IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt,
>>>>>>> v4l_print_ext_pix_format, INFO_FL_PRIO),
>>>>>>> +    IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt,
>>>>>>> v4l_print_ext_pix_format, 0),
>>>>>>>     };
>>>>>>>     #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
>>>>>>> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
>>>>>>> index edb733f21604..c44708dc9355 100644
>>>>>>> --- a/include/media/v4l2-ioctl.h
>>>>>>> +++ b/include/media/v4l2-ioctl.h
>>>>>>> @@ -48,11 +48,17 @@ struct v4l2_fh;
>>>>>>>      * @vidioc_g_fmt_vid_cap: pointer to the function that implements
>>>>>>>      *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>>>>>>      *    in single plane mode
>>>>>>> + * @vidioc_g_ext_pix_fmt_vid_cap: pointer to the function that
>>>>>>> implements
>>>>>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic
>>>>>>> for video
>>>>>>> + *    capture
>>>>>>>      * @vidioc_g_fmt_vid_overlay: pointer to the function that implements
>>>>>>>      *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>>>>>      * @vidioc_g_fmt_vid_out: pointer to the function that implements
>>>>>>>      *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>>>>      *    in single plane mode
>>>>>>> + * @vidioc_g_ext_pix_fmt_vid_out: pointer to the function that
>>>>>>> implements
>>>>>>> + *    :ref:`VIDIOC_G_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic
>>>>>>> for video
>>>>>>> + *    out
>>>>>>>      * @vidioc_g_fmt_vid_out_overlay: pointer to the function that
>>>>>>> implements
>>>>>>>      *    :ref:`VIDIOC_G_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>>>> overlay output
>>>>>>>      * @vidioc_g_fmt_vbi_cap: pointer to the function that implements
>>>>>>> @@ -82,11 +88,16 @@ struct v4l2_fh;
>>>>>>>      * @vidioc_s_fmt_vid_cap: pointer to the function that implements
>>>>>>>      *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video capture
>>>>>>>      *    in single plane mode
>>>>>>> + * @vidioc_s_ext_pix_fmt_vid_cap: pointer to the function that
>>>>>>> implements
>>>>>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl logic
>>>>>>> for video
>>>>>>> + *    capture
>>>>>>>      * @vidioc_s_fmt_vid_overlay: pointer to the function that implements
>>>>>>>      *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video overlay
>>>>>>>      * @vidioc_s_fmt_vid_out: pointer to the function that implements
>>>>>>>      *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>>>>      *    in single plane mode
>>>>>>> + * @vidioc_s_ext_pix_fmt_vid_out: pointer to the function that
>>>>>>> implements
>>>>>>> + *    :ref:`VIDIOC_S_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for
>>>>>>> video out
>>>>>>>      * @vidioc_s_fmt_vid_out_overlay: pointer to the function that
>>>>>>> implements
>>>>>>>      *    :ref:`VIDIOC_S_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>>>> overlay output
>>>>>>>      * @vidioc_s_fmt_vbi_cap: pointer to the function that implements
>>>>>>> @@ -116,11 +127,16 @@ struct v4l2_fh;
>>>>>>>      * @vidioc_try_fmt_vid_cap: pointer to the function that implements
>>>>>>>      *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>>>> capture
>>>>>>>      *    in single plane mode
>>>>>>> + * @vidioc_try_ext_pix_fmt_vid_cap: pointer to the function that
>>>>>>> implements
>>>>>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_ext_pix_fmt>` ioctl
>>>>>>> logic for
>>>>>>> +    video capture
>>>>>>>      * @vidioc_try_fmt_vid_overlay: pointer to the function that
>>>>>>> implements
>>>>>>>      *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>>>> overlay
>>>>>>>      * @vidioc_try_fmt_vid_out: pointer to the function that implements
>>>>>>>      *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video out
>>>>>>>      *    in single plane mode
>>>>>>> + * @vidioc_try_ext_pix_fmt_vid_out: pointer to the function that
>>>>>>> implements
>>>>>>> + *    :ref:`VIDIOC_TRY_EXT_PIX_FMT <vidioc_g_fmt>` ioctl logic for
>>>>>>> video out
>>>>>>>      * @vidioc_try_fmt_vid_out_overlay: pointer to the function that
>>>>>>> implements
>>>>>>>      *    :ref:`VIDIOC_TRY_FMT <vidioc_g_fmt>` ioctl logic for video
>>>>>>> overlay
>>>>>>>      *    output
>>>>>>> @@ -319,10 +335,14 @@ struct v4l2_ioctl_ops {
>>>>>>>         /* VIDIOC_G_FMT handlers */
>>>>>>>         int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
>>>>>>>                         struct v4l2_format *f);
>>>>>>> +    int (*vidioc_g_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>>>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>>>>         int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
>>>>>>>                         struct v4l2_format *f);
>>>>>>>         int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
>>>>>>>                         struct v4l2_format *f);
>>>>>>> +    int (*vidioc_g_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>>>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>>>>         int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>>>>>                             struct v4l2_format *f);
>>>>>>>         int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
>>>>>>> @@ -349,10 +369,14 @@ struct v4l2_ioctl_ops {
>>>>>>>         /* VIDIOC_S_FMT handlers */
>>>>>>>         int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
>>>>>>>                         struct v4l2_format *f);
>>>>>>> +    int (*vidioc_s_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>>>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>>>>         int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
>>>>>>>                         struct v4l2_format *f);
>>>>>>>         int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
>>>>>>>                         struct v4l2_format *f);
>>>>>>> +    int (*vidioc_s_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>>>>>> +                        struct v4l2_ext_pix_format *ef);
>>>>>>>         int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>>>>>                             struct v4l2_format *f);
>>>>>>>         int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
>>>>>>> @@ -379,10 +403,14 @@ struct v4l2_ioctl_ops {
>>>>>>>         /* VIDIOC_TRY_FMT handlers */
>>>>>>>         int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
>>>>>>>                           struct v4l2_format *f);
>>>>>>> +    int (*vidioc_try_ext_pix_fmt_vid_cap)(struct file *file, void *fh,
>>>>>>> +                          struct v4l2_ext_pix_format *ef);
>>>>>>>         int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
>>>>>>>                           struct v4l2_format *f);
>>>>>>>         int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
>>>>>>>                           struct v4l2_format *f);
>>>>>>> +    int (*vidioc_try_ext_pix_fmt_vid_out)(struct file *file, void *fh,
>>>>>>> +                          struct v4l2_ext_pix_format *ef);
>>>>>>>         int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
>>>>>>>                              struct v4l2_format *f);
>>>>>>>         int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
>>>>>>> diff --git a/include/uapi/linux/videodev2.h
>>>>>>> b/include/uapi/linux/videodev2.h
>>>>>>> index d9b7c9177605..a2d850513708 100644
>>>>>>> --- a/include/uapi/linux/videodev2.h
>>>>>>> +++ b/include/uapi/linux/videodev2.h
>>>>>>> @@ -2270,6 +2270,43 @@ struct v4l2_pix_format_mplane {
>>>>>>>         __u8                reserved[7];
>>>>>>>     } __attribute__ ((packed));
>>>>>>> +/**
>>>>>>> + * struct v4l2_ext_pix_format - extended single/multiplanar format
>>>>>>> definition
>>>>>>> + * @type:        type of the data stream;
>>>>>>> V4L2_BUF_TYPE_VIDEO_CAPTURE or
>>>>>>> + *            V4L2_BUF_TYPE_VIDEO_OUTPUT
>>>>>>> + * @width:        image width in pixels
>>>>>>> + * @height:        image height in pixels
>>>>>>> + * @field:        enum v4l2_field; field order (for interlaced video)
>>>>>>> + * @plane_fmt:        per-plane information
>>>>>>> + * @pixelformat:    little endian four character code (fourcc)
>>>>>>> + * @modifier:        modifier applied to the format (used for tiled
>>>>>>> formats
>>>>>>> + *            and other kind of HW-specific formats, like compressed
>>>>>>> + *            formats) as defined in drm_fourcc.h
>>>>>>> + * @colorspace:        enum v4l2_colorspace; supplemental to
>>>>>>> pixelformat
>>>>>>> + * @xfer_func:        enum v4l2_xfer_func, colorspace transfer function
>>>>>>> + * @ycbcr_enc:        enum v4l2_ycbcr_encoding, Y'CbCr encoding
>>>>>>> + * @hsv_enc:        enum v4l2_hsv_encoding, HSV encoding
>>>>>>> + * @quantization:    enum v4l2_quantization, colorspace quantization
>>>>>>> + * @reserved:        extra space reserved for future fields, must be
>>>>>>> set to 0
>>>>>>> + */
>>>>>>> +struct v4l2_ext_pix_format {
>>>>>>> +    __u32 type;
>>>>>>> +    __u32 width;
>>>>>>> +    __u32 height;
>>>>>>> +    __u32 field;
>>>>>>> +    struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES];
>>>>>>> +    __u32 pixelformat;
>>>>>>> +    __u64 modifier;
>>>>>>> +    __u32 colorspace;
>>>>>>> +    __u32 xfer_func;
>>>>>>> +    union {
>>>>>>> +        __u32 ycbcr_enc;
>>>>>>> +        __u32 hsv_enc;
>>>>>>> +    };
>>>>>>> +    __u32 quantization;
>>>>>>> +    __u32 reserved[9];
>>>>>>> +};
>>>>>>> +
>>>>>>>     /**
>>>>>>>      * struct v4l2_sdr_format - SDR format definition
>>>>>>>      * @pixelformat:    little endian four character code (fourcc)
>>>>>>> @@ -2583,6 +2620,10 @@ struct v4l2_create_buffers {
>>>>>>>     #define VIDIOC_QUERY_EXT_CTRL    _IOWR('V', 103, struct
>>>>>>> v4l2_query_ext_ctrl)
>>>>>>> +#define VIDIOC_G_EXT_PIX_FMT    _IOWR('V', 104, struct
>>>>>>> v4l2_ext_pix_format)
>>>>>>> +#define VIDIOC_S_EXT_PIX_FMT    _IOWR('V', 105, struct
>>>>>>> v4l2_ext_pix_format)
>>>>>>> +#define VIDIOC_TRY_EXT_PIX_FMT    _IOWR('V', 106, struct
>>>>>>> v4l2_ext_pix_format)
>>>>>>> +
>>>>>>>     /* Reminder: when adding new ioctls please add support for them to
>>>>>>>        drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
>>>>>>>
>>>
>>> --
>>> Regards,
>>>
>>> Laurent Pinchart
>>
>> --
>> Hsia-Jun(Randy) Li
> 
> --
> Regards,
> 
> Laurent Pinchart

-- 
Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-06 22:11         ` Dmitry Osipenko
  2022-11-07  2:04           ` Hsia-Jun Li
  2022-11-07  8:30           ` Laurent Pinchart
@ 2022-11-07 16:50           ` Fritz Koenig
  2 siblings, 0 replies; 48+ messages in thread
From: Fritz Koenig @ 2022-11-07 16:50 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Hsia-Jun Li, Helen Koike, mchehab, hans.verkuil,
	laurent.pinchart, sakari.ailus, boris.brezillon, hiroh, nicolas,
	Brian.Starkey, kernel, narmstrong, linux-kernel, frkoenig,
	stanimir.varbanov, tfiga, Hans Verkuil, linux-media

On Sun, Nov 6, 2022 at 2:11 PM Dmitry Osipenko
<dmitry.osipenko@collabora.com> wrote:
>
> On 11/5/22 18:19, Hsia-Jun Li wrote:
> > Hello Helen
> >
> > I didn't see any updates from V6 and V7-WIP in your repo. That is what I
> > need to for our complex tile formats in our platform.
> >
> > Any future plane here?
> >
> > Besides I have some ideas on these patches.
>
> I was looking into updating this patchset few months ago and the biggest
> blocker was the absence of immediate upstream user for this new UAPI.
> What your platform is? Is the driver stack completely opensource?
>
ChromeOS had interest in this for enabling UBWC for the venus driver.
We have a workaround at the moment, but would be interested.  So not
immediate need, but would hopefully be a user at some point.

-Fritz
> --
> Best regards,
> Dmitry
>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-07  8:30           ` Laurent Pinchart
@ 2022-11-08 14:58             ` Dmitry Osipenko
  0 siblings, 0 replies; 48+ messages in thread
From: Dmitry Osipenko @ 2022-11-08 14:58 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hsia-Jun Li, Helen Koike, mchehab, hans.verkuil, sakari.ailus,
	boris.brezillon, hiroh, nicolas, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov, tfiga,
	Hans Verkuil, linux-media, frkoenig

On 11/7/22 11:30, Laurent Pinchart wrote:
> On Mon, Nov 07, 2022 at 01:11:32AM +0300, Dmitry Osipenko wrote:
>> On 11/5/22 18:19, Hsia-Jun Li wrote:
>>> Hello Helen
>>>
>>> I didn't see any updates from V6 and V7-WIP in your repo. That is what I
>>> need to for our complex tile formats in our platform.
>>>
>>> Any future plane here?
>>>
>>> Besides I have some ideas on these patches.
>>
>> I was looking into updating this patchset few months ago and the biggest
>> blocker was the absence of immediate upstream user for this new UAPI.
>> What your platform is? Is the driver stack completely opensource?
> 
> libcamera could be a good place to test (part of) this API in userspace.
> We could really do with the data offset feature for instance.

Since we don't have a user for the new UAPI right now, perhaps we can
put the new UAPI behind the staging Kconfig until it will get a
real/production user.

On the other hand, I had unpleasant experience with having UAPI gated by
staging Kconfig for other kernel subsystem in a sense that it
permanently stays a staging UAPI because nobody has time for unstaging it.

I see a common demand in this new UAPI which only becomes stronger over
time. Won't hurt to refresh this patchset. I had a rebased version
locally, but likely it needs to be rebased again. Will try to allocate
time for this.

-- 
Best regards,
Dmitry


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-05 15:19       ` Hsia-Jun Li
                           ` (2 preceding siblings ...)
  2022-11-07  8:42         ` Hans Verkuil
@ 2022-11-10 17:06         ` Nicolas Dufresne
  2022-11-11  3:03           ` Hsia-Jun Li
  3 siblings, 1 reply; 48+ messages in thread
From: Nicolas Dufresne @ 2022-11-10 17:06 UTC (permalink / raw)
  To: Hsia-Jun Li, Helen Koike
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, Brian.Starkey, kernel, narmstrong,
	linux-kernel, frkoenig, stanimir.varbanov, tfiga, Hans Verkuil,
	linux-media

Le samedi 05 novembre 2022 à 23:19 +0800, Hsia-Jun Li a écrit :
> > > VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while 
> > > VIDIOC_ENUM_FMT
> > > would just report NV12M.
> > 
> > If NV12 and NV12M are equivalent in Ext API, I don't see why we would
> > report both (unless I'm missing something, which is probably the case).
> > 
> > The idea was to deprecate the M-variants one day.
> I was thinking the way in DRM API is better, always assuming it would 
> always in a multiple planes. The only problem is we don't have a way to 
> let the allocator that allocate contiguous memory for planes when we 
> need to do that.

Its not too late to allow this to be negotiated, but I would move this out of
the pixel format definition to stop the explosion of duplicate pixel formats,
which is a nightmare to deal with. If I simplify the discussion, we want to
negotiate contiguity with the driver. The new FMT structure should have a
CONTIGUOUS flag. So if userpace sets:

  S_FMT(NV12, CONTIGUOUS)

The driver can accepts, and return the unmodified structure, or may drop the
CONTIGUOUS flag, which would mean its not supported. Could be the other way
around too. As for allocation, if you have CONTIGUOUS flag set, userspace does
not have to export or map memory for each planes, as they are the same. We
simply need to define the offset as relative to their allocation, which I think
is the most sensible thing.

Nicolas


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-10 17:06         ` Nicolas Dufresne
@ 2022-11-11  3:03           ` Hsia-Jun Li
  2022-11-11  5:48             ` Tomasz Figa
  2022-11-15 15:57             ` Nicolas Dufresne
  0 siblings, 2 replies; 48+ messages in thread
From: Hsia-Jun Li @ 2022-11-11  3:03 UTC (permalink / raw)
  To: Nicolas Dufresne
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, Brian.Starkey, kernel, narmstrong,
	linux-kernel, frkoenig, stanimir.varbanov, tfiga, Hans Verkuil,
	linux-media



On 11/11/22 01:06, Nicolas Dufresne wrote:
> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> Le samedi 05 novembre 2022 à 23:19 +0800, Hsia-Jun Li a écrit :
>>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
>>>> VIDIOC_ENUM_FMT
>>>> would just report NV12M.
>>>
>>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
>>> report both (unless I'm missing something, which is probably the case).
>>>
>>> The idea was to deprecate the M-variants one day.
>> I was thinking the way in DRM API is better, always assuming it would
>> always in a multiple planes. The only problem is we don't have a way to
>> let the allocator that allocate contiguous memory for planes when we
>> need to do that.
> 
> Its not too late to allow this to be negotiated, but I would move this out of
> the pixel format definition to stop the explosion of duplicate pixel formats,
> which is a nightmare to deal with. 
I wonder whether we need to keep the pixel formats in videodev2.h 
anymore. If we would like to use the modifiers from drm_fourcc.h, why 
don't we use their pixel formats, they should be the same values of 
non-M variant pixel formats of v4l2.

Let videodev2.h only maintain the those codecs or motion based 
compressed (pixel) formats.

If I simplify the discussion, we want to
> negotiate contiguity with the driver. The new FMT structure should have a
> CONTIGUOUS flag. So if userpace sets:
> 
>    S_FMT(NV12, CONTIGUOUS)
I wonder whether we would allow some planes being contiguous while some 
would not. For example, the graphics planes could be in a contiguous 
memory address while its compression metadata are not.
Although that is not the case of our platform. I believe it sounds like 
reasonable case for improving the performance, two meta planes could 
resident in a different memory bank.

That lead to another question which I forgot whether I mention it before.

There are four modifiers in DRM while we would only one in these patches.
 From the EGL
https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt

The modifier for echo plane could be different. I wish it would be 
better to create a framebuffer being aware of which planes are graphics 
or metadata.

I wonder whether it would be better that convincing the DRM maintainer 
adding a non vendor flag for contiguous memory allocation here(DRM 
itself don't need it).
While whether the memory could be contiguous for these vendor pixel 
formats, it is complex vendor defined.

> 
> The driver can accepts, and return the unmodified structure, or may drop the
> CONTIGUOUS flag, which would mean its not supported. Could be the other way
> around too. As for allocation, if you have CONTIGUOUS flag set, userspace does
> not have to export or map memory for each planes, as they are the same. We
> simply need to define the offset as relative to their allocation, which I think
> is the most sensible thing.
> 
> Nicolas
> 

-- 
Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-11  3:03           ` Hsia-Jun Li
@ 2022-11-11  5:48             ` Tomasz Figa
  2022-11-11  6:30               ` Hsia-Jun Li
  2022-11-11  8:42               ` Laurent Pinchart
  2022-11-15 15:57             ` Nicolas Dufresne
  1 sibling, 2 replies; 48+ messages in thread
From: Tomasz Figa @ 2022-11-11  5:48 UTC (permalink / raw)
  To: Hsia-Jun Li
  Cc: Nicolas Dufresne, mchehab, hans.verkuil, laurent.pinchart,
	sakari.ailus, boris.brezillon, hiroh, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov,
	Hans Verkuil, linux-media

On Fri, Nov 11, 2022 at 12:04 PM Hsia-Jun Li <Randy.Li@synaptics.com> wrote:
>
>
>
> On 11/11/22 01:06, Nicolas Dufresne wrote:
> > CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> >
> >
> > Le samedi 05 novembre 2022 à 23:19 +0800, Hsia-Jun Li a écrit :
> >>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
> >>>> VIDIOC_ENUM_FMT
> >>>> would just report NV12M.
> >>>
> >>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
> >>> report both (unless I'm missing something, which is probably the case).
> >>>
> >>> The idea was to deprecate the M-variants one day.
> >> I was thinking the way in DRM API is better, always assuming it would
> >> always in a multiple planes. The only problem is we don't have a way to
> >> let the allocator that allocate contiguous memory for planes when we
> >> need to do that.
> >
> > Its not too late to allow this to be negotiated, but I would move this out of
> > the pixel format definition to stop the explosion of duplicate pixel formats,
> > which is a nightmare to deal with.
> I wonder whether we need to keep the pixel formats in videodev2.h
> anymore. If we would like to use the modifiers from drm_fourcc.h, why
> don't we use their pixel formats, they should be the same values of
> non-M variant pixel formats of v4l2.
>
> Let videodev2.h only maintain the those codecs or motion based
> compressed (pixel) formats.
>
> If I simplify the discussion, we want to
> > negotiate contiguity with the driver. The new FMT structure should have a
> > CONTIGUOUS flag. So if userpace sets:
> >
> >    S_FMT(NV12, CONTIGUOUS)
> I wonder whether we would allow some planes being contiguous while some
> would not. For example, the graphics planes could be in a contiguous
> memory address while its compression metadata are not.
> Although that is not the case of our platform. I believe it sounds like
> reasonable case for improving the performance, two meta planes could
> resident in a different memory bank.

I feel like this would be only useful in the MMAP mode. Looking at how
the other UAPIs are evolving, things are going towards
userspace-managed allocations, using, for example, DMA-buf heaps. I
think we should follow the trend and keep the MMAP mode just at the
same level of functionality as is today and focus on improvements and
new functionality for the DMABUF mode.

>
> That lead to another question which I forgot whether I mention it before.
>
> There are four modifiers in DRM while we would only one in these patches.
>  From the EGL
> https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
>
> The modifier for echo plane could be different. I wish it would be
> better to create a framebuffer being aware of which planes are graphics
> or metadata.

What's an echo plane?

That said, it indeed looks like we may want to be consistent with DRM
here and allow per-plane modifiers.

>
> I wonder whether it would be better that convincing the DRM maintainer
> adding a non vendor flag for contiguous memory allocation here(DRM
> itself don't need it).
> While whether the memory could be contiguous for these vendor pixel
> formats, it is complex vendor defined.

Memory allocation doesn't sound to me like it is related to formats or
modifiers in any way. I agree with Nicolas that if we want to allow
the userspace to specify if the memory should be contiguous or not,
that should be a separate flag and actually I'd probably see it in
REQBUF_EXT and CREATE_BUFS_EXT, rather than as a part of the format.

>
> >
> > The driver can accepts, and return the unmodified structure, or may drop the
> > CONTIGUOUS flag, which would mean its not supported. Could be the other way
> > around too. As for allocation, if you have CONTIGUOUS flag set, userspace does
> > not have to export or map memory for each planes, as they are the same. We
> > simply need to define the offset as relative to their allocation, which I think
> > is the most sensible thing.
> >
> > Nicolas
> >
>
> --
> Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-11  5:48             ` Tomasz Figa
@ 2022-11-11  6:30               ` Hsia-Jun Li
  2022-11-11  8:52                 ` Tomasz Figa
  2022-11-11  8:42               ` Laurent Pinchart
  1 sibling, 1 reply; 48+ messages in thread
From: Hsia-Jun Li @ 2022-11-11  6:30 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Nicolas Dufresne, mchehab, hans.verkuil, laurent.pinchart,
	sakari.ailus, boris.brezillon, hiroh, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov,
	Hans Verkuil, linux-media



On 11/11/22 13:48, Tomasz Figa wrote:
> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> On Fri, Nov 11, 2022 at 12:04 PM Hsia-Jun Li <Randy.Li@synaptics.com> wrote:
>>
>>
>>
>> On 11/11/22 01:06, Nicolas Dufresne wrote:
>>> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
>>>
>>>
>>> Le samedi 05 novembre 2022 à 23:19 +0800, Hsia-Jun Li a écrit :
>>>>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
>>>>>> VIDIOC_ENUM_FMT
>>>>>> would just report NV12M.
>>>>>
>>>>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
>>>>> report both (unless I'm missing something, which is probably the case).
>>>>>
>>>>> The idea was to deprecate the M-variants one day.
>>>> I was thinking the way in DRM API is better, always assuming it would
>>>> always in a multiple planes. The only problem is we don't have a way to
>>>> let the allocator that allocate contiguous memory for planes when we
>>>> need to do that.
>>>
>>> Its not too late to allow this to be negotiated, but I would move this out of
>>> the pixel format definition to stop the explosion of duplicate pixel formats,
>>> which is a nightmare to deal with.
>> I wonder whether we need to keep the pixel formats in videodev2.h
>> anymore. If we would like to use the modifiers from drm_fourcc.h, why
>> don't we use their pixel formats, they should be the same values of
>> non-M variant pixel formats of v4l2.
>>
>> Let videodev2.h only maintain the those codecs or motion based
>> compressed (pixel) formats.
>>
>> If I simplify the discussion, we want to
>>> negotiate contiguity with the driver. The new FMT structure should have a
>>> CONTIGUOUS flag. So if userpace sets:
>>>
>>>     S_FMT(NV12, CONTIGUOUS)
>> I wonder whether we would allow some planes being contiguous while some
>> would not. For example, the graphics planes could be in a contiguous
>> memory address while its compression metadata are not.
>> Although that is not the case of our platform. I believe it sounds like
>> reasonable case for improving the performance, two meta planes could
>> resident in a different memory bank.
> 
> I feel like this would be only useful in the MMAP mode. Looking at how
> the other UAPIs are evolving, things are going towards
> userspace-managed allocations, using, for example, DMA-buf heaps. I
> think we should follow the trend and keep the MMAP mode just at the
> same level of functionality as is today and focus on improvements and
> new functionality for the DMABUF mode.
> 
I know there are still some devices(encoder) which only have one 
register for storing the address of a graphics buffer.
>>
>> That lead to another question which I forgot whether I mention it before.
>>
>> There are four modifiers in DRM while we would only one in these patches.
>>   From the EGL
>> https://urldefense.proofpoint.com/v2/url?u=https-3A__registry.khronos.org_EGL_extensions_EXT_EGL-5FEXT-5Fimage-5Fdma-5Fbuf-5Fimport-5Fmodifiers.txt&d=DwIFaQ&c=7dfBJ8cXbWjhc0BhImu8wVIoUFmBzj1s88r8EGyM0UY&r=P4xb2_7biqBxD4LGGPrSV6j-jf3C3xlR7PXU-mLTeZE&m=mCebYOAiZK6pbpH1MrZGq-ZkDW-OqORCSwsCEX9ScgdXk_yfWZFJPC5aC93CUg5F&s=rtmW_t2LYoJ6g3Y5wgyICmABu-2Npw3JCOlvUVIYH2o&e=
>>
>> The modifier for echo plane could be different. I wish it would be
>> better to create a framebuffer being aware of which planes are graphics
>> or metadata.
> 
> What's an echo plane?
> 
They could be
DRM_FORMAT_MOD_SYNA_V4H1_128L128_COMPRESSED
DRM_FORMAT_MOD_SYNA_V4H1_128L128_COMPRESSED
DRM_FORMAT_MOD_SYNA_MTR
DRM_FORMAT_MOD_SYNA_MTR
Or
DRM_FORMAT_MOD_SYNA_V4H3P8_64L4
DRM_FORMAT_MOD_SYNA_V4H3P8_64L4

in our platform. It could give a better idea on what is stored in a plane.
> That said, it indeed looks like we may want to be consistent with DRM
> here and allow per-plane modifiers.
> 
>>
>> I wonder whether it would be better that convincing the DRM maintainer
>> adding a non vendor flag for contiguous memory allocation here(DRM
>> itself don't need it).
>> While whether the memory could be contiguous for these vendor pixel
>> formats, it is complex vendor defined.
> 
> Memory allocation doesn't sound to me like it is related to formats or
> modifiers in any way. I agree with Nicolas that if we want to allow
> the userspace to specify if the memory should be contiguous or not,
> that should be a separate flag and actually I'd probably see it in
> REQBUF_EXT and CREATE_BUFS_EXT, rather than as a part of the format.
> 
I agree with that. But here is a problem, if there was a display 
device(DRM) that only supports contiguous planes in a frame buffer.
How do we be aware of that?
>>
>>>
>>> The driver can accepts, and return the unmodified structure, or may drop the
>>> CONTIGUOUS flag, which would mean its not supported. Could be the other way
>>> around too. As for allocation, if you have CONTIGUOUS flag set, userspace does
>>> not have to export or map memory for each planes, as they are the same. We
>>> simply need to define the offset as relative to their allocation, which I think
>>> is the most sensible thing.
>>>
>>> Nicolas
>>>
>>
>> --
>> Hsia-Jun(Randy) Li

-- 
Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-11  5:48             ` Tomasz Figa
  2022-11-11  6:30               ` Hsia-Jun Li
@ 2022-11-11  8:42               ` Laurent Pinchart
  2022-11-11  8:54                 ` Tomasz Figa
  1 sibling, 1 reply; 48+ messages in thread
From: Laurent Pinchart @ 2022-11-11  8:42 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Hsia-Jun Li, Nicolas Dufresne, mchehab, hans.verkuil,
	sakari.ailus, boris.brezillon, hiroh, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov,
	Hans Verkuil, linux-media

Hi Tomasz,

On Fri, Nov 11, 2022 at 02:48:48PM +0900, Tomasz Figa wrote:
> On Fri, Nov 11, 2022 at 12:04 PM Hsia-Jun Li wrote:
> > On 11/11/22 01:06, Nicolas Dufresne wrote:
> > > Le samedi 05 novembre 2022 à 23:19 +0800, Hsia-Jun Li a écrit :
> > >>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
> > >>>> VIDIOC_ENUM_FMT
> > >>>> would just report NV12M.
> > >>>
> > >>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
> > >>> report both (unless I'm missing something, which is probably the case).
> > >>>
> > >>> The idea was to deprecate the M-variants one day.
> > >> I was thinking the way in DRM API is better, always assuming it would
> > >> always in a multiple planes. The only problem is we don't have a way to
> > >> let the allocator that allocate contiguous memory for planes when we
> > >> need to do that.
> > >
> > > Its not too late to allow this to be negotiated, but I would move this out of
> > > the pixel format definition to stop the explosion of duplicate pixel formats,
> > > which is a nightmare to deal with.
> > 
> > I wonder whether we need to keep the pixel formats in videodev2.h
> > anymore. If we would like to use the modifiers from drm_fourcc.h, why
> > don't we use their pixel formats, they should be the same values of
> > non-M variant pixel formats of v4l2.
> >
> > Let videodev2.h only maintain the those codecs or motion based
> > compressed (pixel) formats.
> >
> > If I simplify the discussion, we want to
> > 
> > > negotiate contiguity with the driver. The new FMT structure should have a
> > > CONTIGUOUS flag. So if userpace sets:
> > >
> > >    S_FMT(NV12, CONTIGUOUS)
> > 
> > I wonder whether we would allow some planes being contiguous while some
> > would not. For example, the graphics planes could be in a contiguous
> > memory address while its compression metadata are not.
> > Although that is not the case of our platform. I believe it sounds like
> > reasonable case for improving the performance, two meta planes could
> > resident in a different memory bank.
> 
> I feel like this would be only useful in the MMAP mode. Looking at how
> the other UAPIs are evolving, things are going towards
> userspace-managed allocations, using, for example, DMA-buf heaps. I
> think we should follow the trend and keep the MMAP mode just at the
> same level of functionality as is today and focus on improvements and
> new functionality for the DMABUF mode.

I agree, but we will need an API to expose the memory constraints of the
device, or userspace won't be able to allocate memory compatible with
the hardware or driver requirements.

> > That lead to another question which I forgot whether I mention it before.
> >
> > There are four modifiers in DRM while we would only one in these patches.
> >  From the EGL
> > https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
> >
> > The modifier for echo plane could be different. I wish it would be
> > better to create a framebuffer being aware of which planes are graphics
> > or metadata.
> 
> What's an echo plane?
> 
> That said, it indeed looks like we may want to be consistent with DRM
> here and allow per-plane modifiers.
> 
> > I wonder whether it would be better that convincing the DRM maintainer
> > adding a non vendor flag for contiguous memory allocation here(DRM
> > itself don't need it).
> > While whether the memory could be contiguous for these vendor pixel
> > formats, it is complex vendor defined.
> 
> Memory allocation doesn't sound to me like it is related to formats or
> modifiers in any way. I agree with Nicolas that if we want to allow
> the userspace to specify if the memory should be contiguous or not,
> that should be a separate flag and actually I'd probably see it in
> REQBUF_EXT and CREATE_BUFS_EXT, rather than as a part of the format.

I like how DRM decouples allocation of buffer objects and creation of
frame buffers.

> > > The driver can accepts, and return the unmodified structure, or may drop the
> > > CONTIGUOUS flag, which would mean its not supported. Could be the other way
> > > around too. As for allocation, if you have CONTIGUOUS flag set, userspace does
> > > not have to export or map memory for each planes, as they are the same. We
> > > simply need to define the offset as relative to their allocation, which I think
> > > is the most sensible thing.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-11  6:30               ` Hsia-Jun Li
@ 2022-11-11  8:52                 ` Tomasz Figa
  2022-11-11  9:13                   ` Hsia-Jun Li
  2022-11-15 16:03                   ` Nicolas Dufresne
  0 siblings, 2 replies; 48+ messages in thread
From: Tomasz Figa @ 2022-11-11  8:52 UTC (permalink / raw)
  To: Hsia-Jun Li
  Cc: Nicolas Dufresne, mchehab, hans.verkuil, laurent.pinchart,
	sakari.ailus, boris.brezillon, hiroh, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov,
	Hans Verkuil, linux-media

On Fri, Nov 11, 2022 at 3:31 PM Hsia-Jun Li <Randy.Li@synaptics.com> wrote:
>
>
>
> On 11/11/22 13:48, Tomasz Figa wrote:
> > CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> >
> >
> > On Fri, Nov 11, 2022 at 12:04 PM Hsia-Jun Li <Randy.Li@synaptics.com> wrote:
> >>
> >>
> >>
> >> On 11/11/22 01:06, Nicolas Dufresne wrote:
> >>> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> >>>
> >>>
> >>> Le samedi 05 novembre 2022 à 23:19 +0800, Hsia-Jun Li a écrit :
> >>>>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
> >>>>>> VIDIOC_ENUM_FMT
> >>>>>> would just report NV12M.
> >>>>>
> >>>>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
> >>>>> report both (unless I'm missing something, which is probably the case).
> >>>>>
> >>>>> The idea was to deprecate the M-variants one day.
> >>>> I was thinking the way in DRM API is better, always assuming it would
> >>>> always in a multiple planes. The only problem is we don't have a way to
> >>>> let the allocator that allocate contiguous memory for planes when we
> >>>> need to do that.
> >>>
> >>> Its not too late to allow this to be negotiated, but I would move this out of
> >>> the pixel format definition to stop the explosion of duplicate pixel formats,
> >>> which is a nightmare to deal with.
> >> I wonder whether we need to keep the pixel formats in videodev2.h
> >> anymore. If we would like to use the modifiers from drm_fourcc.h, why
> >> don't we use their pixel formats, they should be the same values of
> >> non-M variant pixel formats of v4l2.
> >>
> >> Let videodev2.h only maintain the those codecs or motion based
> >> compressed (pixel) formats.
> >>
> >> If I simplify the discussion, we want to
> >>> negotiate contiguity with the driver. The new FMT structure should have a
> >>> CONTIGUOUS flag. So if userpace sets:
> >>>
> >>>     S_FMT(NV12, CONTIGUOUS)
> >> I wonder whether we would allow some planes being contiguous while some
> >> would not. For example, the graphics planes could be in a contiguous
> >> memory address while its compression metadata are not.
> >> Although that is not the case of our platform. I believe it sounds like
> >> reasonable case for improving the performance, two meta planes could
> >> resident in a different memory bank.
> >
> > I feel like this would be only useful in the MMAP mode. Looking at how
> > the other UAPIs are evolving, things are going towards
> > userspace-managed allocations, using, for example, DMA-buf heaps. I
> > think we should follow the trend and keep the MMAP mode just at the
> > same level of functionality as is today and focus on improvements and
> > new functionality for the DMABUF mode.
> >
> I know there are still some devices(encoder) which only have one
> register for storing the address of a graphics buffer.

For those, the legacy MMAP mode (with existing functionality) can be
successfully used, we wouldn't be removing it any time soon. Just
don't want to design new functionality specifically for the legacy
mode.

> >>
> >> That lead to another question which I forgot whether I mention it before.
> >>
> >> There are four modifiers in DRM while we would only one in these patches.
> >>   From the EGL
> >> https://urldefense.proofpoint.com/v2/url?u=https-3A__registry.khronos.org_EGL_extensions_EXT_EGL-5FEXT-5Fimage-5Fdma-5Fbuf-5Fimport-5Fmodifiers.txt&d=DwIFaQ&c=7dfBJ8cXbWjhc0BhImu8wVIoUFmBzj1s88r8EGyM0UY&r=P4xb2_7biqBxD4LGGPrSV6j-jf3C3xlR7PXU-mLTeZE&m=mCebYOAiZK6pbpH1MrZGq-ZkDW-OqORCSwsCEX9ScgdXk_yfWZFJPC5aC93CUg5F&s=rtmW_t2LYoJ6g3Y5wgyICmABu-2Npw3JCOlvUVIYH2o&e=
> >>
> >> The modifier for echo plane could be different. I wish it would be
> >> better to create a framebuffer being aware of which planes are graphics
> >> or metadata.
> >
> > What's an echo plane?
> >
> They could be
> DRM_FORMAT_MOD_SYNA_V4H1_128L128_COMPRESSED
> DRM_FORMAT_MOD_SYNA_V4H1_128L128_COMPRESSED
> DRM_FORMAT_MOD_SYNA_MTR
> DRM_FORMAT_MOD_SYNA_MTR
> Or
> DRM_FORMAT_MOD_SYNA_V4H3P8_64L4
> DRM_FORMAT_MOD_SYNA_V4H3P8_64L4
>
> in our platform. It could give a better idea on what is stored in a plane.

Yes, that's what I was thinking, but my question is more about what
those planes hold. Are you sure that they should be planes of the same
buffer rather than separate buffers?

> > That said, it indeed looks like we may want to be consistent with DRM
> > here and allow per-plane modifiers.
> >
> >>
> >> I wonder whether it would be better that convincing the DRM maintainer
> >> adding a non vendor flag for contiguous memory allocation here(DRM
> >> itself don't need it).
> >> While whether the memory could be contiguous for these vendor pixel
> >> formats, it is complex vendor defined.
> >
> > Memory allocation doesn't sound to me like it is related to formats or
> > modifiers in any way. I agree with Nicolas that if we want to allow
> > the userspace to specify if the memory should be contiguous or not,
> > that should be a separate flag and actually I'd probably see it in
> > REQBUF_EXT and CREATE_BUFS_EXT, rather than as a part of the format.
> >
> I agree with that. But here is a problem, if there was a display
> device(DRM) that only supports contiguous planes in a frame buffer.
> How do we be aware of that?

That's why I think the MMAP mode is not scalable and shouldn't be
expanded anymore. Both V4L2 and DRM devices should describe their
constraints to the userspace and then the userspace should allocate
accordingly from the right DMA-buf heap. (Or as Android and ChromeOS
do, just have a central allocator library that understands the
constraints, so there is no need to query the drivers.)

> >>
> >>>
> >>> The driver can accepts, and return the unmodified structure, or may drop the
> >>> CONTIGUOUS flag, which would mean its not supported. Could be the other way
> >>> around too. As for allocation, if you have CONTIGUOUS flag set, userspace does
> >>> not have to export or map memory for each planes, as they are the same. We
> >>> simply need to define the offset as relative to their allocation, which I think
> >>> is the most sensible thing.
> >>>
> >>> Nicolas
> >>>
> >>
> >> --
> >> Hsia-Jun(Randy) Li
>
> --
> Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-11  8:42               ` Laurent Pinchart
@ 2022-11-11  8:54                 ` Tomasz Figa
  2022-11-15 16:19                   ` Nicolas Dufresne
  0 siblings, 1 reply; 48+ messages in thread
From: Tomasz Figa @ 2022-11-11  8:54 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Hsia-Jun Li, Nicolas Dufresne, mchehab, hans.verkuil,
	sakari.ailus, boris.brezillon, hiroh, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov,
	Hans Verkuil, linux-media

On Fri, Nov 11, 2022 at 5:43 PM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Tomasz,
>
> On Fri, Nov 11, 2022 at 02:48:48PM +0900, Tomasz Figa wrote:
> > On Fri, Nov 11, 2022 at 12:04 PM Hsia-Jun Li wrote:
> > > On 11/11/22 01:06, Nicolas Dufresne wrote:
> > > > Le samedi 05 novembre 2022 à 23:19 +0800, Hsia-Jun Li a écrit :
> > > >>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
> > > >>>> VIDIOC_ENUM_FMT
> > > >>>> would just report NV12M.
> > > >>>
> > > >>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
> > > >>> report both (unless I'm missing something, which is probably the case).
> > > >>>
> > > >>> The idea was to deprecate the M-variants one day.
> > > >> I was thinking the way in DRM API is better, always assuming it would
> > > >> always in a multiple planes. The only problem is we don't have a way to
> > > >> let the allocator that allocate contiguous memory for planes when we
> > > >> need to do that.
> > > >
> > > > Its not too late to allow this to be negotiated, but I would move this out of
> > > > the pixel format definition to stop the explosion of duplicate pixel formats,
> > > > which is a nightmare to deal with.
> > >
> > > I wonder whether we need to keep the pixel formats in videodev2.h
> > > anymore. If we would like to use the modifiers from drm_fourcc.h, why
> > > don't we use their pixel formats, they should be the same values of
> > > non-M variant pixel formats of v4l2.
> > >
> > > Let videodev2.h only maintain the those codecs or motion based
> > > compressed (pixel) formats.
> > >
> > > If I simplify the discussion, we want to
> > >
> > > > negotiate contiguity with the driver. The new FMT structure should have a
> > > > CONTIGUOUS flag. So if userpace sets:
> > > >
> > > >    S_FMT(NV12, CONTIGUOUS)
> > >
> > > I wonder whether we would allow some planes being contiguous while some
> > > would not. For example, the graphics planes could be in a contiguous
> > > memory address while its compression metadata are not.
> > > Although that is not the case of our platform. I believe it sounds like
> > > reasonable case for improving the performance, two meta planes could
> > > resident in a different memory bank.
> >
> > I feel like this would be only useful in the MMAP mode. Looking at how
> > the other UAPIs are evolving, things are going towards
> > userspace-managed allocations, using, for example, DMA-buf heaps. I
> > think we should follow the trend and keep the MMAP mode just at the
> > same level of functionality as is today and focus on improvements and
> > new functionality for the DMABUF mode.
>
> I agree, but we will need an API to expose the memory constraints of the
> device, or userspace won't be able to allocate memory compatible with
> the hardware or driver requirements.

Yes, I fully agree and that's why I think we should rather focus our
efforts in that direction rather than expanding the existing MMAP
capabilities.

>
> > > That lead to another question which I forgot whether I mention it before.
> > >
> > > There are four modifiers in DRM while we would only one in these patches.
> > >  From the EGL
> > > https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
> > >
> > > The modifier for echo plane could be different. I wish it would be
> > > better to create a framebuffer being aware of which planes are graphics
> > > or metadata.
> >
> > What's an echo plane?
> >
> > That said, it indeed looks like we may want to be consistent with DRM
> > here and allow per-plane modifiers.
> >
> > > I wonder whether it would be better that convincing the DRM maintainer
> > > adding a non vendor flag for contiguous memory allocation here(DRM
> > > itself don't need it).
> > > While whether the memory could be contiguous for these vendor pixel
> > > formats, it is complex vendor defined.
> >
> > Memory allocation doesn't sound to me like it is related to formats or
> > modifiers in any way. I agree with Nicolas that if we want to allow
> > the userspace to specify if the memory should be contiguous or not,
> > that should be a separate flag and actually I'd probably see it in
> > REQBUF_EXT and CREATE_BUFS_EXT, rather than as a part of the format.
>
> I like how DRM decouples allocation of buffer objects and creation of
> frame buffers.

Exactly why I proposed so rather than coupling it with S_FMT. (But
then it's moot if we decide to focus on DMABUF mode.)

>
> > > > The driver can accepts, and return the unmodified structure, or may drop the
> > > > CONTIGUOUS flag, which would mean its not supported. Could be the other way
> > > > around too. As for allocation, if you have CONTIGUOUS flag set, userspace does
> > > > not have to export or map memory for each planes, as they are the same. We
> > > > simply need to define the offset as relative to their allocation, which I think
> > > > is the most sensible thing.
>
> --
> Regards,
>
> Laurent Pinchart

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-11  8:52                 ` Tomasz Figa
@ 2022-11-11  9:13                   ` Hsia-Jun Li
  2022-11-15 16:03                   ` Nicolas Dufresne
  1 sibling, 0 replies; 48+ messages in thread
From: Hsia-Jun Li @ 2022-11-11  9:13 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Nicolas Dufresne, mchehab, hans.verkuil, laurent.pinchart,
	sakari.ailus, boris.brezillon, hiroh, Brian.Starkey, kernel,
	narmstrong, linux-kernel, frkoenig, stanimir.varbanov,
	Hans Verkuil, linux-media



On 11/11/22 16:52, Tomasz Figa wrote:
> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> On Fri, Nov 11, 2022 at 3:31 PM Hsia-Jun Li <Randy.Li@synaptics.com> wrote:
>>
>>
>>
>> On 11/11/22 13:48, Tomasz Figa wrote:
>>> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
>>>
>>>
>>> On Fri, Nov 11, 2022 at 12:04 PM Hsia-Jun Li <Randy.Li@synaptics.com> wrote:
>>>>
>>>>
>>>>
>>>> On 11/11/22 01:06, Nicolas Dufresne wrote:
>>>>> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
>>>>>
>>>>>
>>>>> Le samedi 05 novembre 2022 à 23:19 +0800, Hsia-Jun Li a écrit :
>>>>>>>> VIDIOC_ENUM_EXT_PIX_FMT would report NV12 and NV12M, while
>>>>>>>> VIDIOC_ENUM_FMT
>>>>>>>> would just report NV12M.
>>>>>>>
>>>>>>> If NV12 and NV12M are equivalent in Ext API, I don't see why we would
>>>>>>> report both (unless I'm missing something, which is probably the case).
>>>>>>>
>>>>>>> The idea was to deprecate the M-variants one day.
>>>>>> I was thinking the way in DRM API is better, always assuming it would
>>>>>> always in a multiple planes. The only problem is we don't have a way to
>>>>>> let the allocator that allocate contiguous memory for planes when we
>>>>>> need to do that.
>>>>>
>>>>> Its not too late to allow this to be negotiated, but I would move this out of
>>>>> the pixel format definition to stop the explosion of duplicate pixel formats,
>>>>> which is a nightmare to deal with.
>>>> I wonder whether we need to keep the pixel formats in videodev2.h
>>>> anymore. If we would like to use the modifiers from drm_fourcc.h, why
>>>> don't we use their pixel formats, they should be the same values of
>>>> non-M variant pixel formats of v4l2.
>>>>
>>>> Let videodev2.h only maintain the those codecs or motion based
>>>> compressed (pixel) formats.
>>>>
>>>> If I simplify the discussion, we want to
>>>>> negotiate contiguity with the driver. The new FMT structure should have a
>>>>> CONTIGUOUS flag. So if userpace sets:
>>>>>
>>>>>      S_FMT(NV12, CONTIGUOUS)
>>>> I wonder whether we would allow some planes being contiguous while some
>>>> would not. For example, the graphics planes could be in a contiguous
>>>> memory address while its compression metadata are not.
>>>> Although that is not the case of our platform. I believe it sounds like
>>>> reasonable case for improving the performance, two meta planes could
>>>> resident in a different memory bank.
>>>
>>> I feel like this would be only useful in the MMAP mode. Looking at how
>>> the other UAPIs are evolving, things are going towards
>>> userspace-managed allocations, using, for example, DMA-buf heaps. I
>>> think we should follow the trend and keep the MMAP mode just at the
>>> same level of functionality as is today and focus on improvements and
>>> new functionality for the DMABUF mode.
>>>
>> I know there are still some devices(encoder) which only have one
>> register for storing the address of a graphics buffer.
> 
> For those, the legacy MMAP mode (with existing functionality) can be
> successfully used, we wouldn't be removing it any time soon. Just
> don't want to design new functionality specifically for the legacy
> mode.
> 
But it prevents the encoder using the buffer from the outside.
For example, there was an PCI-e interface camera which would write to 
the system memory where is configured to its register, then we would 
like to encode those buffers.
>>>>
>>>> That lead to another question which I forgot whether I mention it before.
>>>>
>>>> There are four modifiers in DRM while we would only one in these patches.
>>>>    From the EGL
>>>> https://urldefense.proofpoint.com/v2/url?u=https-3A__registry.khronos.org_EGL_extensions_EXT_EGL-5FEXT-5Fimage-5Fdma-5Fbuf-5Fimport-5Fmodifiers.txt&d=DwIFaQ&c=7dfBJ8cXbWjhc0BhImu8wVIoUFmBzj1s88r8EGyM0UY&r=P4xb2_7biqBxD4LGGPrSV6j-jf3C3xlR7PXU-mLTeZE&m=mCebYOAiZK6pbpH1MrZGq-ZkDW-OqORCSwsCEX9ScgdXk_yfWZFJPC5aC93CUg5F&s=rtmW_t2LYoJ6g3Y5wgyICmABu-2Npw3JCOlvUVIYH2o&e=
>>>>
>>>> The modifier for echo plane could be different. I wish it would be
>>>> better to create a framebuffer being aware of which planes are graphics
>>>> or metadata.
>>>
>>> What's an echo plane?
>>>
>> They could be
>> DRM_FORMAT_MOD_SYNA_V4H1_128L128_COMPRESSED
>> DRM_FORMAT_MOD_SYNA_V4H1_128L128_COMPRESSED
>> DRM_FORMAT_MOD_SYNA_MTR
>> DRM_FORMAT_MOD_SYNA_MTR
>> Or
>> DRM_FORMAT_MOD_SYNA_V4H3P8_64L4
>> DRM_FORMAT_MOD_SYNA_V4H3P8_64L4
>>
>> in our platform. It could give a better idea on what is stored in a plane.
> 
> Yes, that's what I was thinking, but my question is more about what
> those planes hold.
DRM_FORMAT_MOD_SYNA_V4H1* or DRM_FORMAT_MOD_SYNA_V4H3P8*
would be the luma and chroma (un)compressed data here. They are 
modifiers to NV12 and NV15.
  Are you sure that they should be planes of the same
> buffer rather than separate buffers?
I am not sure about your question here. I prefer they are in a different 
memory plane. But not all Android APIs support that. If I just think 
about our platform and GNU Linux, I won't care about those limitations.
> 
>>> That said, it indeed looks like we may want to be consistent with DRM
>>> here and allow per-plane modifiers.
>>>
>>>>
>>>> I wonder whether it would be better that convincing the DRM maintainer
>>>> adding a non vendor flag for contiguous memory allocation here(DRM
>>>> itself don't need it).
>>>> While whether the memory could be contiguous for these vendor pixel
>>>> formats, it is complex vendor defined.
>>>
>>> Memory allocation doesn't sound to me like it is related to formats or
>>> modifiers in any way. I agree with Nicolas that if we want to allow
>>> the userspace to specify if the memory should be contiguous or not,
>>> that should be a separate flag and actually I'd probably see it in
>>> REQBUF_EXT and CREATE_BUFS_EXT, rather than as a part of the format.
>>>
>> I agree with that. But here is a problem, if there was a display
>> device(DRM) that only supports contiguous planes in a frame buffer.
>> How do we be aware of that?
> 
> That's why I think the MMAP mode is not scalable and shouldn't be
> expanded anymore. Both V4L2 and DRM devices should describe their
> constraints to the userspace and then the userspace should allocate
> accordingly from the right DMA-buf heap. (Or as Android and ChromeOS
> do, just have a central allocator library that understands the
> constraints, so there is no need to query the drivers.)
> 
Because we are working for embedded platforms which don't have memory 
beyond the system memory. I believe those GPU vendors would hate idea of 
DMAheap only.
>>>>
>>>>>
>>>>> The driver can accepts, and return the unmodified structure, or may drop the
>>>>> CONTIGUOUS flag, which would mean its not supported. Could be the other way
>>>>> around too. As for allocation, if you have CONTIGUOUS flag set, userspace does
>>>>> not have to export or map memory for each planes, as they are the same. We
>>>>> simply need to define the offset as relative to their allocation, which I think
>>>>> is the most sensible thing.
>>>>>
>>>>> Nicolas
>>>>>
>>>>
>>>> --
>>>> Hsia-Jun(Randy) Li
>>
>> --
>> Hsia-Jun(Randy) Li

-- 
Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-11  3:03           ` Hsia-Jun Li
  2022-11-11  5:48             ` Tomasz Figa
@ 2022-11-15 15:57             ` Nicolas Dufresne
  1 sibling, 0 replies; 48+ messages in thread
From: Nicolas Dufresne @ 2022-11-15 15:57 UTC (permalink / raw)
  To: Hsia-Jun Li
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, Brian.Starkey, kernel, narmstrong,
	linux-kernel, frkoenig, stanimir.varbanov, tfiga, Hans Verkuil,
	linux-media

Le vendredi 11 novembre 2022 à 11:03 +0800, Hsia-Jun Li a écrit :
> There are four modifiers in DRM while we would only one in these patches.
>  From the EGL
> https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt
> 
> The modifier for echo plane could be different. I wish it would be 
> better to create a framebuffer being aware of which planes are graphics 
> or metadata.

I was told that having different modifiers per plane is not supported and just
an historical artifact of an over-engineered API design.

Nicolas

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-11  8:52                 ` Tomasz Figa
  2022-11-11  9:13                   ` Hsia-Jun Li
@ 2022-11-15 16:03                   ` Nicolas Dufresne
  2022-11-16 12:38                     ` ayaka
  1 sibling, 1 reply; 48+ messages in thread
From: Nicolas Dufresne @ 2022-11-15 16:03 UTC (permalink / raw)
  To: Tomasz Figa, Hsia-Jun Li
  Cc: mchehab, hans.verkuil, laurent.pinchart, sakari.ailus,
	boris.brezillon, hiroh, Brian.Starkey, kernel, narmstrong,
	linux-kernel, frkoenig, stanimir.varbanov, Hans Verkuil,
	linux-media

Le vendredi 11 novembre 2022 à 17:52 +0900, Tomasz Figa a écrit :
> > > Memory allocation doesn't sound to me like it is related to formats or
> > > modifiers in any way. I agree with Nicolas that if we want to allow
> > > the userspace to specify if the memory should be contiguous or not,
> > > that should be a separate flag and actually I'd probably see it in
> > > REQBUF_EXT and CREATE_BUFS_EXT, rather than as a part of the format.
> > > 
> > I agree with that. But here is a problem, if there was a display
> > device(DRM) that only supports contiguous planes in a frame buffer.
> > How do we be aware of that?
> 
> That's why I think the MMAP mode is not scalable and shouldn't be
> expanded anymore. Both V4L2 and DRM devices should describe their
> constraints to the userspace and then the userspace should allocate
> accordingly from the right DMA-buf heap. (Or as Android and ChromeOS
> do, just have a central allocator library that understands the
> constraints, so there is no need to query the drivers.)

Just pointing out, they "hardcode" the constraints, they don't truly understand
them. Also, the Android/ChromeOS implementation is not a great playground, as it
completely ignores the constrait already exposed by V4L2 API (the sizeimage and
bytesperline found in the FMT structure). You would not have to implement Hantro
and Rockchip motion vector size calculation there if you'd simply use the
sizeimage.

Nicolas


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-11  8:54                 ` Tomasz Figa
@ 2022-11-15 16:19                   ` Nicolas Dufresne
  0 siblings, 0 replies; 48+ messages in thread
From: Nicolas Dufresne @ 2022-11-15 16:19 UTC (permalink / raw)
  To: Tomasz Figa, Laurent Pinchart
  Cc: Hsia-Jun Li, mchehab, hans.verkuil, sakari.ailus,
	boris.brezillon, hiroh, Brian.Starkey, kernel, narmstrong,
	linux-kernel, frkoenig, stanimir.varbanov, Hans Verkuil,
	linux-media

Le vendredi 11 novembre 2022 à 17:54 +0900, Tomasz Figa a écrit :
> > > I feel like this would be only useful in the MMAP mode. Looking at how
> > > the other UAPIs are evolving, things are going towards
> > > userspace-managed allocations, using, for example, DMA-buf heaps. I
> > > think we should follow the trend and keep the MMAP mode just at the
> > > same level of functionality as is today and focus on improvements and
> > > new functionality for the DMABUF mode.
> > 
> > I agree, but we will need an API to expose the memory constraints of the
> > device, or userspace won't be able to allocate memory compatible with
> > the hardware or driver requirements.
> 
> Yes, I fully agree and that's why I think we should rather focus our
> efforts in that direction rather than expanding the existing MMAP
> capabilities.

I was once told that MMAP was mandatory to support in v4l2 drivers. I'd like to
get some clarification on the subject for sure. We can't break compat, unless we
spin v4l3 here.

One thing that come to mind, is that its not true that a V4L2 driver can always
be importer only. For cards like Xplorer X1600P PCIe Accelerator [1] from Blaize
(they are using a modified, but still generic, Hantro Driver), the CODECs memory
should be allocated on the card for best performance. Only the driver is aware
that there is memory on that card, and so it must export the buffers.

In a DMABuf import only future, that basically means the driver must implement a
DMABuf HEAP driver, and to make this usable by generic software, the constraints
API need to support telling userspace that this Heap is to be used. This gap
easily extend to DRM, which have per driver API to allocate memory, and in some
cases these API must be used (they don't have heaps for on-card memory
allocation). When this is happening inside the GFX stack, it works very well,
but when you need to integrate this with some V4L2 driver, its not really
practical and requires something like minigbm/gralloc, which is non-generic.

[1] https://www.blaize.com/products/ai-edge-computing-platforms/

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more)
  2022-11-15 16:03                   ` Nicolas Dufresne
@ 2022-11-16 12:38                     ` ayaka
  0 siblings, 0 replies; 48+ messages in thread
From: ayaka @ 2022-11-16 12:38 UTC (permalink / raw)
  To: Nicolas Dufresne
  Cc: Tomasz Figa, Hsia-Jun Li, mchehab, hans.verkuil,
	laurent.pinchart, sakari.ailus, boris.brezillon, hiroh,
	Brian.Starkey, kernel, narmstrong, linux-kernel, frkoenig,
	stanimir.varbanov, Hans Verkuil, linux-media


> On Nov 16, 2022, at 12:03 AM, Nicolas Dufresne <nicolas@ndufresne.ca> wrote:
> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> Le vendredi 11 novembre 2022 à 17:52 +0900, Tomasz Figa a écrit :
>>>> Memory allocation doesn't sound to me like it is related to formats or
>>>> modifiers in any way. I agree with Nicolas that if we want to allow
>>>> the userspace to specify if the memory should be contiguous or not,
>>>> that should be a separate flag and actually I'd probably see it in
>>>> REQBUF_EXT and CREATE_BUFS_EXT, rather than as a part of the format.
>>> I agree with that. But here is a problem, if there was a display
>>> device(DRM) that only supports contiguous planes in a frame buffer.
>>> How do we be aware of that?
>> 
>> That's why I think the MMAP mode is not scalable and shouldn't be
>> expanded anymore. Both V4L2 and DRM devices should describe their
>> constraints to the userspace and then the userspace should allocate
>> accordingly from the right DMA-buf heap. (Or as Android and ChromeOS
>> do, just have a central allocator library that understands the
>> constraints, so there is no need to query the drivers.)
> 
> Just pointing out, they "hardcode" the constraints, they don't truly understand
> them. Also, the Android/ChromeOS implementation is not a great playground, as it
> completely ignores the constrait already exposed by V4L2 API (the sizeimage and
> bytesperline found in the FMT structure). You would not have to implement Hantro
> and Rockchip motion vector size calculation there if you'd simply use the
> sizeimage.
> 
About the allocation, I have my own plan here. Beyond the draft of DMAHeap allocator I threw last time.
I would like to modify the v4l2_queue API:
1. allowing different allocators for each memory plane(that matches the demand of synaptics video smart platform, which graphic planes support IOMMU while the compression metadata planes don’t).
2. New API that let driver allocate all memory planes for a buffer in a call

And DMAHeap V4l2 allocator would become a template or currying.

> Nicolas


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types
  2021-02-23 12:58   ` Hans Verkuil
@ 2023-01-26  7:07     ` ayaka
  0 siblings, 0 replies; 48+ messages in thread
From: ayaka @ 2023-01-26  7:07 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media, mchehab, hans.verkuil, boris.brezillon, hiroh,
	nicolas, Brian.Starkey, kernel, narmstrong, linux-kernel,
	frkoenig, stanimir.varbanov, tfiga, sakari.ailus,
	laurent.pinchart, randy.li

I am currently refresh this patchset, but I didn't see the need beyond 
v4l2_ext_pix_fmt, which I had done.

On 2/23/21 20:58, Hans Verkuil wrote:
> On 14/01/2021 19:07, Helen Koike wrote:
>> Those extended buffer ops have several purpose:
>> 1/ Fix y2038 issues by converting the timestamp into an u64 counting
>>     the number of ns elapsed since 1970
I think application just use the timestamp field for tracking the 
buffer. It would be just a sequence number.
At least for the most widely cases, the video encoder and decoder and 
ISP, this field is not a wall time.
>> 2/ Unify single/multiplanar handling
>> 3/ Add a new start offset field to each v4l2 plane buffer info struct
>>     to support the case where a single buffer object is storing all
>>     planes data, each one being placed at a different offset

This mention a long time issue, but I don't think we need to expand API 
here.

May the data_offset field in struct v4l2_plane is enough? The rest is 
the problem of the kernel internal API and allocator.

I am thinking just add a field recording the offset input from the user.
When we return the buffer back to the user, the value of the offset 
should be same as the it is queued.

Meanwhile, the API compatible that I want to keep is user using the new 
ext_pix API could access those drivers support old API.
But I don't want the user would expect they could get correct pixel 
format using the old ioctl(). It could create many duplicated pixel 
formats. If we want to keep the compatible here, that is the job of libv4l.

Besides, I think make the driver using the new API be compatible with 
the old ioctl() would lead a huge problem. User won't like to update its 
code if it could work even in a less performance mode because this code 
are for all the other hardware vendors/models.
Unless we make this a feature, they could make a new branch in their 
code(don't count them would update the kernel of the other products).
>> New hooks are created in v4l2_ioctl_ops so that drivers can start using
>> these new objects.
>>
>> Note that the timecode field is gone, since there doesn't seem to be
>> in-kernel users. We can be added back in the reserved area if needed or
>> use the Request API to collect more metadata information from the
>> frame.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>> ---
>>
>> Changes in v6:
>> This patch was completely refactored, and based on previous version from
>> Hans and Boris.
>> - Refactor conversions v4l2_buffer <-> v4l2_ext_buffer for (d)qbuf
>> - I removed EXT_CREATE_BUFS since it is basically only usefull to MMAP.
>>    If this is going towards DMA-fd centric, then we can use the current
>>    REQUESTBUF to switch to it, and we can think a better way to support
>>    MMAP later if there are usecases.
>>    I also moved memory field from v4l2_ext_plane to v4l2_ext_buffer,
>>    since it is very unlikely to mix memory types, and REQUESTBUF can
>>    switch the whole buffer object to a given type.
>> - I removed EXT_QUERYBUF, since it is only useful to MMAP, for the
>>    same reason above.
>> - I removed EXT_PREPARE_BUF, since it is basically just an optimization,
>>    we can add it later (my intention is to simplify this patchset).
>> - These ioctls are only valid for video types (and not for overlay,
>>    vbi, touch, meta, etc).
>> - Refactor struct v4l2_ext_buffer and struct v4l2_ext_planes as
>>    discussed with Tomasz:
>> 	- add bytesused back
>> 	- remove lenght field
>> 	- move memory field from planes to buffer object
>> - Fix order in documentation of struct v4l2_ext_buffer (Tomasz)
>> - Fix flags documentation of struct v4l2_ext_buffer, don't say when flags are ignored (Tomasz)
>> - v4l_print_ext_buffer(): print request_fd and offset/userptr (Tomasz)
>>
>> Changes in v5:
>> - migrate memory from v4l2_ext_buffer to v4l2_ext_plane
>> - return mem_offset to struct v4l2_ext_plane
>> - change sizes and reorder fields to avoid holes in the struct and make
>>    it the same for 32 and 64 bits
>>
>> Changes in v4:
>> - Use v4l2_ext_pix_format directly in the ioctl, drop v4l2_ext_format,
>> making V4L2_BUF_TYPE_VIDEO_[OUTPUT,CAPTURE] the only valid types.
>> - Drop VIDIOC_EXT_EXPBUF, since the only difference from VIDIOC_EXPBUF
>> was that with VIDIOC_EXT_EXPBUF we could export multiple planes at once.
>> I think we can add this later, so I removed it from this RFC to simplify it.
>> - Remove num_planes field from struct v4l2_ext_buffer
>> - Add flags field to struct v4l2_ext_create_buffers
>> - Reformulate struct v4l2_ext_plane
>> - Fix some bugs caught by v4l2-compliance
>> - Rebased on top of media/master (post 5.8-rc1)
>>
>> Changes in v3:
>> - Rebased on top of media/master (post 5.4-rc1)
>>
>> Changes in v2:
>> - Add reserved space to v4l2_ext_buffer so that new fields can be added
>>    later on
>>
>> Signed-off-by: Helen Koike <helen.koike@collabora.com>
>> ---
>>   drivers/media/v4l2-core/v4l2-dev.c   |   4 +
>>   drivers/media/v4l2-core/v4l2-ioctl.c | 184 +++++++++++++++++++++++++++
>>   include/media/v4l2-ioctl.h           |   8 ++
>>   include/uapi/linux/videodev2.h       |  55 ++++++++
>>   4 files changed, 251 insertions(+)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
>> index 5add58cb6d45..94c9f1e04704 100644
>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>> @@ -664,6 +664,10 @@ static void determine_valid_ioctls(struct video_device *vdev)
>>   			set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
>>   		SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
>>   		SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
>> +		SET_VALID_IOCTL(ops, VIDIOC_EXT_QBUF, vidioc_ext_qbuf);
>> +		SET_VALID_IOCTL(ops, VIDIOC_EXT_QBUF, vidioc_qbuf);
>> +		SET_VALID_IOCTL(ops, VIDIOC_EXT_DQBUF, vidioc_ext_dqbuf);
>> +		SET_VALID_IOCTL(ops, VIDIOC_EXT_DQBUF, vidioc_dqbuf);
>>   	}
>>   	if (is_meta && is_rx) {
>>   		/* metadata capture specific ioctls */
>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index a9c07c0a73ec..ba633e7efd6d 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -533,6 +533,24 @@ static void v4l_print_buffer(const void *arg, bool write_only)
>>   			tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
>>   }
>>   
>> +static void v4l_print_ext_buffer(const void *arg, bool write_only)
>> +{
>> +	const struct v4l2_ext_buffer *e = arg;
>> +	unsigned int i;
>> +
>> +	pr_cont("%lld index=%d, type=%s, request_fd=%d, flags=0x%08llx, field=%s, sequence=%d, memory=%s\n",
>> +		e->timestamp, e->index, prt_names(e->type, v4l2_type_names),
>> +		e->request_fd, e->flags, prt_names(e->field, v4l2_field_names),
>> +		e->sequence, prt_names(e->memory, v4l2_memory_names));
>> +
>> +	for (i = 0; i < VIDEO_MAX_PLANES && e->planes[i].m.userptr; i++) {
>> +		const struct v4l2_ext_plane *plane = &e->planes[i];
>> +
>> +		pr_cont("plane %d: bytesused=%d, offset=0x%08x, userptr=0x%llx\n",
>> +			i, plane->bytesused, plane->offset, plane->m.userptr);
>> +	}
>> +}
>> +
>>   static void v4l_print_exportbuffer(const void *arg, bool write_only)
>>   {
>>   	const struct v4l2_exportbuffer *p = arg;
>> @@ -2552,6 +2570,130 @@ static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
>>   	return ret ? ret : ops->vidioc_qbuf(file, fh, p);
>>   }
>>   
>> +static bool v4l2_ext_buffer_is_single_membuf(const struct v4l2_ext_buffer *eb)
>> +{
>> +	unsigned int i;
>> +
>> +	for (i = 1; i < VIDEO_MAX_PLANES && eb->planes[i].m.userptr; i++)
>> +		if (eb->planes[i].m.userptr != eb->planes[i - 1].m.userptr)
> Is this safe? m.userptr is a u64, but the other two fields in the union are
> 32 bits. If you are actually comparing fds, how certain are you that the
> 32 bits following the 'fd' are zeroed instead of containing garbage?
>
> Perhaps mem_offset and fd should be u64 and s64 respectively to avoid any
> uninitialized bits?
>
>> +			return false;
>> +	return true;
>> +}
>> +
>> +static int v4l2_fill_buffer_from_ext_buffer(const struct v4l2_ioctl_ops *ops,
>> +					    struct file *file, void *fh,
>> +					    const struct v4l2_ext_buffer *eb,
>> +					    struct v4l2_buffer *b,
>> +					    struct v4l2_plane *bplanes)
>> +{
>> +	const struct v4l2_ext_plane *eplanes = (struct v4l2_ext_plane *)&eb->planes;
>> +	struct video_device *vfd = video_devdata(file);
>> +	bool is_mplane = V4L2_IS_CAP_MULTIPLANAR(vfd);
>> +	unsigned int i;
>> +	u64 nsecs;
>> +	int ret;
>> +
>> +	b->index = eb->index;
>> +	if (is_mplane) {
>> +		b->m.planes = bplanes;
>> +		b->length = VIDEO_MAX_PLANES;
>> +		if (eb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
>> +			b->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
>> +		else if (eb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
>> +			b->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
>> +	} else {
>> +		b->type = eb->type;
>> +	}
>> +
>> +	/* Fill the rest of the v4l2_buffer */
>> +	ret = v4l_querybuf(ops, file, fh, b);
> This call is weird. Why is this needed?
>
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Fill other fields from v4l2_ext_buffer */
>> +	b->flags = eb->flags;
>> +	b->field = eb->field;
>> +	b->timestamp.tv_sec = div64_u64_rem(eb->timestamp, NSEC_PER_SEC, &nsecs);
>> +	b->timestamp.tv_usec = (u32)nsecs / NSEC_PER_USEC;
> Use v4l2_buffer_set_timestamp() for this.
>
>> +	b->sequence = eb->sequence;
>> +
>> +	if (!is_mplane) {
>> +		for (i = 0; i < VIDEO_MAX_PLANES; i++) {
>> +			b->bytesused += eplanes[i].bytesused;
>> +			WARN_ON(eplanes[i].offset);
>> +		}
>> +
>> +		/* MMAP info was filled by querybuf */
>> +		if (b->memory == V4L2_MEMORY_MMAP)
>> +			return 0 ;
> space before ;
>
>> +
>> +		/*
>> +		 * TODO: get the length of the buffer, for now, just
>> +		 * set to max to avoid errors in checks.
>> +		 */
>> +		b->length = U32_MAX;
>> +		b->m.userptr = eplanes[0].m.userptr;
>> +		return 0;
>> +	}
>> +
>> +	bplanes[0].bytesused = eplanes[0].bytesused + eplanes[0].offset;
>> +	bplanes[0].data_offset = eplanes[0].offset;
>> +	if (v4l2_ext_buffer_is_single_membuf(eb))
>> +		for (i = 1; i < VIDEO_MAX_PLANES && eplanes[i].bytesused; i++) {
>> +			bplanes[0].bytesused += eplanes[i].bytesused;
>> +			WARN_ON(eplanes[i].offset);
>> +		}
>> +	else
>> +		for (i = 1; i < VIDEO_MAX_PLANES && eplanes[i].bytesused; i++) {
>> +			bplanes[i].bytesused = eplanes[i].bytesused +
>> +					       eplanes[i].offset;
>> +			bplanes[i].data_offset = eplanes[i].offset;
>> +		}
>> +
>> +	/* MMAP info was filled by querybuf */
>> +	if (b->memory == V4L2_MEMORY_MMAP)
>> +		return 0;
>> +
>> +	for (i = 0; i < VIDEO_MAX_PLANES && eplanes[i].m.userptr; i++) {
>> +		bplanes[i].m.userptr = eplanes[i].m.userptr;
>> +		/*
>> +		 * TODO: get the length of the buffer, for now, just
>> +		 * set to max to avoid errors in checks.
>> +		 */
>> +		bplanes[i].length = U32_MAX;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int v4l_ext_qbuf(const struct v4l2_ioctl_ops *ops,
>> +			struct file *file, void *fh, void *arg)
>> +{
>> +	struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
>> +	struct v4l2_ext_buffer *eb = arg;
>> +	struct v4l2_buffer b = {0};
>> +
>> +	int ret = check_fmt(file, eb->type);
>> +
>> +	if (!ret)
>> +		return ret;
>> +
>> +	if (ops->vidioc_ext_qbuf)
>> +		return ops->vidioc_ext_qbuf(file, fh, eb);
>> +
>> +	/* Fill other fields from v4l2_ext_buffer */
>> +	ret = v4l2_fill_buffer_from_ext_buffer(ops, file, fh, eb, &b, planes);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = v4l_qbuf(ops, file, fh, &b);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* TODO: check if we need to fill other fields */
>> +	eb->flags = b.flags;
>> +	return 0;
>> +}
>> +
>>   static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
>>   				struct file *file, void *fh, void *arg)
>>   {
>> @@ -2561,6 +2703,46 @@ static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
>>   	return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
>>   }
>>   
>> +static int v4l_ext_dqbuf(const struct v4l2_ioctl_ops *ops,
>> +			 struct file *file, void *fh, void *arg)
>> +{
>> +	struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
>> +	struct v4l2_ext_buffer *eb = arg;
>> +	struct v4l2_buffer b = {0};
>> +	unsigned int i;
>> +
>> +	int ret = check_fmt(file, eb->type);
>> +
>> +	if (!ret)
>> +		return ret;
>> +
>> +	if (ops->vidioc_ext_qbuf)
>> +		return ops->vidioc_ext_qbuf(file, fh, eb);
>> +
>> +	/* Fill other fields from v4l2_ext_buffer */
>> +	ret = v4l2_fill_buffer_from_ext_buffer(ops, file, fh, eb, &b, planes);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = v4l_qbuf(ops, file, fh, &b);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* TODO: check if we need to fill other fields */
>> +	eb->flags = b.flags;
>> +
>> +	/*
>> +	 * Set buffer pointers to zero. Usecase: DMA-fd might have being
> Use-case
>
>> +	 * alread closed, so just request userspace to fill it again in queue
> already
> in queue -> at queue
>
>> +	 * time.
>> +	 */
>> +	for (i = 0; i < VIDEO_MAX_PLANES && eb->planes[i].m.userptr; i++) {
>> +		eb->planes[i].m.userptr = 0;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>>   static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
>>   				struct file *file, void *fh, void *arg)
>>   {
>> @@ -3340,6 +3522,8 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
>>   	IOCTL_INFO(VIDIOC_G_EXT_PIX_FMT, v4l_g_ext_pix_fmt, v4l_print_ext_pix_format, 0),
>>   	IOCTL_INFO(VIDIOC_S_EXT_PIX_FMT, v4l_s_ext_pix_fmt, v4l_print_ext_pix_format, INFO_FL_PRIO),
>>   	IOCTL_INFO(VIDIOC_TRY_EXT_PIX_FMT, v4l_try_ext_pix_fmt, v4l_print_ext_pix_format, 0),
>> +	IOCTL_INFO(VIDIOC_EXT_QBUF, v4l_ext_qbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
>> +	IOCTL_INFO(VIDIOC_EXT_DQBUF, v4l_ext_dqbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
>>   };
>>   #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
>>   
>> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
>> index c44708dc9355..1d0ed36e5e67 100644
>> --- a/include/media/v4l2-ioctl.h
>> +++ b/include/media/v4l2-ioctl.h
>> @@ -171,10 +171,14 @@ struct v4l2_fh;
>>    *	:ref:`VIDIOC_QUERYBUF <vidioc_querybuf>` ioctl
>>    * @vidioc_qbuf: pointer to the function that implements
>>    *	:ref:`VIDIOC_QBUF <vidioc_qbuf>` ioctl
>> + * @vidioc_ext_qbuf: pointer to the function that implements
>> + *	:ref:`VIDIOC_EXT_QBUF <vidioc_ext_qbuf>` ioctl
>>    * @vidioc_expbuf: pointer to the function that implements
>>    *	:ref:`VIDIOC_EXPBUF <vidioc_expbuf>` ioctl
>>    * @vidioc_dqbuf: pointer to the function that implements
>>    *	:ref:`VIDIOC_DQBUF <vidioc_qbuf>` ioctl
>> + * @vidioc_ext_dqbuf: pointer to the function that implements
>> + *	:ref:`VIDIOC_EXT_DQBUF <vidioc_ext_qbuf>` ioctl
>>    * @vidioc_create_bufs: pointer to the function that implements
>>    *	:ref:`VIDIOC_CREATE_BUFS <vidioc_create_bufs>` ioctl
>>    * @vidioc_prepare_buf: pointer to the function that implements
>> @@ -441,10 +445,14 @@ struct v4l2_ioctl_ops {
>>   			       struct v4l2_buffer *b);
>>   	int (*vidioc_qbuf)(struct file *file, void *fh,
>>   			   struct v4l2_buffer *b);
>> +	int (*vidioc_ext_qbuf)(struct file *file, void *fh,
>> +			       struct v4l2_ext_buffer *b);
>>   	int (*vidioc_expbuf)(struct file *file, void *fh,
>>   			     struct v4l2_exportbuffer *e);
>>   	int (*vidioc_dqbuf)(struct file *file, void *fh,
>>   			    struct v4l2_buffer *b);
>> +	int (*vidioc_ext_dqbuf)(struct file *file, void *fh,
>> +				struct v4l2_ext_buffer *b);
>>   
>>   	int (*vidioc_create_bufs)(struct file *file, void *fh,
>>   				  struct v4l2_create_buffers *b);
>> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
>> index a2d850513708..508ac11645bc 100644
>> --- a/include/uapi/linux/videodev2.h
>> +++ b/include/uapi/linux/videodev2.h
>> @@ -1056,6 +1056,59 @@ struct v4l2_buffer {
>>   	};
>>   };
>>   
>> +/**
>> + * struct v4l2_ext_plane - extended plane buffer info
>> + * @offset:		offset in the memory buffer where the plane starts.
>> + * @bytesused:		number of bytes occupied by data in the plane (payload).
>> + * @mmap_offset:	If V4L2_MEMORY_MMAP is used, then it can be a "cookie"
>> + *			that should be passed to mmap() called on the video node.
>> + * @userptr:		when memory is V4L2_MEMORY_USERPTR, a userspace pointer pointing
>> + *			to this plane.
>> + * @dmabuf_fd:		when memory is V4L2_MEMORY_DMABUF, a userspace file descriptor
>> + *			associated with this plane.
>> + * @reserved:		extra space reserved for future fields, must be set to 0.
>> + */
>> +struct v4l2_ext_plane {
>> +	__u32 offset;
>> +	__u32 bytesused;
>> +	union {
>> +		__u32 mmap_offset;
>> +		__u64 userptr;
>> +		__s32 dmabuf_fd;
>> +	} m;
>> +	__u32 reserved[6];
>> +};
>> +
>> +/**
>> + * struct v4l2_ext_buffer - extended video buffer info
>> + * @index:	id number of the buffer
>> + * @type:	V4L2_BUF_TYPE_VIDEO_CAPTURE or V4L2_BUF_TYPE_VIDEO_OUTPUT
>> + * @field:	enum v4l2_field; field order of the image in the buffer
>> + * @sequence:	sequence count of this frame
>> + * @flags:	buffer informational flags
>> + * @timestamp:	frame timestamp
>> + * @memory:	enum v4l2_memory; the method, in which the actual video
>> + *		data is passed
>> + * @request_fd:	fd of the request that this buffer should use
>> + * @planes:	per-plane buffer information
>> + * @reserved:	extra space reserved for future fields, must be set to 0
>> + *
>> + * Contains data exchanged by application and driver using one of the Streaming
>> + * I/O methods.
>> + */
>> +struct v4l2_ext_buffer {
>> +	__u32 index;
>> +	__u32 type;
>> +	__u32 field;
>> +	__u32 sequence;
>> +	__u64 flags;
>> +	__u64 timestamp;
>> +	__u32 memory;
>> +	__s32 request_fd;
>> +	struct v4l2_ext_plane planes[VIDEO_MAX_PLANES];
>> +	__u32 reserved[10];
>> +};
>> +
>>   #ifndef __KERNEL__
>>   /**
>>    * v4l2_timeval_to_ns - Convert timeval to nanoseconds
>> @@ -2623,6 +2676,8 @@ struct v4l2_create_buffers {
>>   #define VIDIOC_G_EXT_PIX_FMT	_IOWR('V', 104, struct v4l2_ext_pix_format)
>>   #define VIDIOC_S_EXT_PIX_FMT	_IOWR('V', 105, struct v4l2_ext_pix_format)
>>   #define VIDIOC_TRY_EXT_PIX_FMT	_IOWR('V', 106, struct v4l2_ext_pix_format)
>> +#define VIDIOC_EXT_QBUF		_IOWR('V', 107, struct v4l2_ext_buffer)
>> +#define VIDIOC_EXT_DQBUF	_IOWR('V', 118, struct v4l2_ext_buffer)
>>   
>>   /* Reminder: when adding new ioctls please add support for them to
>>      drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
>>
> Regards,
>
> 	Hans
>

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types
       [not found]   ` <20230125200026.16643-1-ayaka@soulik.info>
@ 2023-01-26  8:57     ` Hans Verkuil
  2023-01-26 11:02       ` Laurent Pinchart
  0 siblings, 1 reply; 48+ messages in thread
From: Hans Verkuil @ 2023-01-26  8:57 UTC (permalink / raw)
  To: ayaka
  Cc: randy.li, Brian.Starkey, frkoenig, hans.verkuil, helen.koike,
	hiroh, kernel, laurent.pinchart, linux-kernel, linux-media,
	mchehab, narmstrong, nicolas, sakari.ailus, stanimir.varbanov,
	tfiga

On 25/01/2023 21:00, ayaka wrote:
> I am currently refresh this patchset, but I didn't see the need beyond v4l2_ext_pix_fmt, which I had done.
> On 2/23/21 20:58, Hans Verkuil wrote:
>> On 14/01/2021 19:07, Helen Koike wrote:
>>> Those extended buffer ops have several purpose:
>>> 1/ Fix y2038 issues by converting the timestamp into an u64 counting
>>>     the number of ns elapsed since 1970
> 
> I think application just use the timestamp field for tracking the buffer. It would be just a sequence buffer.
> At least for the most widely cases, the video encoder and decoder and ISP, this field is not a wall time.

For video capture and video output this is typically the monotonic clock value.

For memory-to-memory devices it is something that is just copied from output to capture.

So ISPs definitely use this as a proper timestamp.

>>> 2/ Unify single/multiplanar handling
>>> 3/ Add a new start offset field to each v4l2 plane buffer info struct
>>>     to support the case where a single buffer object is storing all
>>>     planes data, each one being placed at a different offset
>>>
> I really care about this. But I think the data_offset field in struct v4l2_plane is enough. The rest is the problem of the kernel internal API and allocator.

data_offset has proven to be very confusing and is rarely used because of that.

We do need some sort of an offset field as proposed here, but it shouldn't be
named data_offset.

> I am thinking just add a field recording the offset input from the user.
> When we return the buffer back to the user, the value of the offset should be same as the it is queued.
> 
> Meanwhile, the API compatible that I want to keep is user using the ext_pix API could access those drivers support old API.
> But I don't want the user would expect they could get correct pixel format using the old ioctl(). It could create many duplicated pixel formats. If we want to keep the compatible here, that is the job of libv4l.
> 
> Besides, I think make the driver using the new API be compatible with the old ioctl() would lead a huge problem. User won't like to update its code if it could work even in a less performance mode because this code are for all the other hardware vendors/models.
> Unless we make this a feature, they could make a new branch in their code(don't count them would upate the kernel of the other products).

New drivers that require the additional information that these new ioctls give can
decide to just support these new ioctls only. But for existing drivers you want
to automatically support the new ioctls.

Regards,

	Hans


^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types
  2023-01-26  8:57     ` Hans Verkuil
@ 2023-01-26 11:02       ` Laurent Pinchart
  2023-01-26 18:36         ` ayaka
  0 siblings, 1 reply; 48+ messages in thread
From: Laurent Pinchart @ 2023-01-26 11:02 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: ayaka, randy.li, Brian.Starkey, frkoenig, hans.verkuil,
	helen.koike, hiroh, kernel, linux-kernel, linux-media, mchehab,
	narmstrong, nicolas, sakari.ailus, stanimir.varbanov, tfiga

On Thu, Jan 26, 2023 at 09:57:51AM +0100, Hans Verkuil wrote:
> On 25/01/2023 21:00, ayaka wrote:
> > I am currently refresh this patchset, but I didn't see the need beyond v4l2_ext_pix_fmt, which I had done.
> > On 2/23/21 20:58, Hans Verkuil wrote:
> >> On 14/01/2021 19:07, Helen Koike wrote:
> >>> Those extended buffer ops have several purpose:
> >>> 1/ Fix y2038 issues by converting the timestamp into an u64 counting
> >>>     the number of ns elapsed since 1970
> > 
> > I think application just use the timestamp field for tracking the
> > buffer. It would be just a sequence buffer.
> > At least for the most widely cases, the video encoder and decoder
> > and ISP, this field is not a wall time.
> 
> For video capture and video output this is typically the monotonic
> clock value.
> 
> For memory-to-memory devices it is something that is just copied from
> output to capture.
> 
> So ISPs definitely use this as a proper timestamp.

There are both inline (live-to-memory) and offline (memory-to-memory)
ISPs. The former certainly need a proper timestamp.

> >>> 2/ Unify single/multiplanar handling
> >>> 3/ Add a new start offset field to each v4l2 plane buffer info struct
> >>>     to support the case where a single buffer object is storing all
> >>>     planes data, each one being placed at a different offset
> >
> > I really care about this. But I think the data_offset field in
> > struct v4l2_plane is enough. The rest is the problem of the kernel
> > internal API and allocator.
> 
> data_offset has proven to be very confusing and is rarely used because
> of that.
> 
> We do need some sort of an offset field as proposed here, but it
> shouldn't be named data_offset.

The existing data_offset field was indeed added for other purposes, to
let drivers report where the actual image data starts for devices that
prepend some sort of header. That's indeed not what we want here, we
instead need something similar to the offsets field of struct
drm_mode_fb_cmd2.

> > I am thinking just add a field recording the offset input from the user.
> > When we return the buffer back to the user, the value of the offset
> > should be same as the it is queued.
> > 
> > Meanwhile, the API compatible that I want to keep is user using the
> > ext_pix API could access those drivers support old API.
> > But I don't want the user would expect they could get correct pixel
> > format using the old ioctl(). It could create many duplicated pixel
> > formats. If we want to keep the compatible here, that is the job of
> > libv4l.
> > 
> > Besides, I think make the driver using the new API be compatible
> > with the old ioctl() would lead a huge problem. User won't like to
> > update its code if it could work even in a less performance mode
> > because this code are for all the other hardware vendors/models.
> > Unless we make this a feature, they could make a new branch in their
> > code(don't count them would upate the kernel of the other products).
> 
> New drivers that require the additional information that these new ioctls give can
> decide to just support these new ioctls only. But for existing drivers you want
> to automatically support the new ioctls.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types
  2023-01-26 11:02       ` Laurent Pinchart
@ 2023-01-26 18:36         ` ayaka
  2023-01-27  8:11           ` Hans Verkuil
  0 siblings, 1 reply; 48+ messages in thread
From: ayaka @ 2023-01-26 18:36 UTC (permalink / raw)
  To: Laurent Pinchart, Hans Verkuil
  Cc: randy.li, Brian.Starkey, frkoenig, hans.verkuil, helen.koike,
	hiroh, kernel, linux-kernel, linux-media, mchehab, narmstrong,
	nicolas, sakari.ailus, stanimir.varbanov, tfiga


On 1/26/23 19:02, Laurent Pinchart wrote:
> On Thu, Jan 26, 2023 at 09:57:51AM +0100, Hans Verkuil wrote:
>> On 25/01/2023 21:00, ayaka wrote:
>>> I am currently refresh this patchset, but I didn't see the need beyond v4l2_ext_pix_fmt, which I had done.
>>> On 2/23/21 20:58, Hans Verkuil wrote:
>>>> On 14/01/2021 19:07, Helen Koike wrote:
>>>>> Those extended buffer ops have several purpose:
>>>>> 1/ Fix y2038 issues by converting the timestamp into an u64 counting
>>>>>      the number of ns elapsed since 1970
>>> I think application just use the timestamp field for tracking the
>>> buffer. It would be just a sequence buffer.
>>> At least for the most widely cases, the video encoder and decoder
>>> and ISP, this field is not a wall time.
>> For video capture and video output this is typically the monotonic
>> clock value.
>>
>> For memory-to-memory devices it is something that is just copied from
>> output to capture.
>>
>> So ISPs definitely use this as a proper timestamp.
> There are both inline (live-to-memory) and offline (memory-to-memory)
> ISPs. The former certainly need a proper timestamp.
>
I really have not seen a device that has timer starting with the epoch.

I rarely know the ISP has a wall clock timer.

Timestamp is not my first concern here. Offset is.

>>>>> 2/ Unify single/multiplanar handling
>>>>> 3/ Add a new start offset field to each v4l2 plane buffer info struct
>>>>>      to support the case where a single buffer object is storing all
>>>>>      planes data, each one being placed at a different offset
>>> I really care about this. But I think the data_offset field in
>>> struct v4l2_plane is enough. The rest is the problem of the kernel
>>> internal API and allocator.
>> data_offset has proven to be very confusing and is rarely used because
>> of that.
Yes, I didn't know any stateful codec driver support this.
>> We do need some sort of an offset field as proposed here, but it
>> shouldn't be named data_offset.
Maybe we could just rename it or make a union in the existing struct.
> The existing data_offset field was indeed added for other purposes, to
> let drivers report where the actual image data starts for devices that
> prepend some sort of header.

For the compressed image, it makes sense. But the most of usage I knew 
is the upstream would just allocate a large buffer for compression video 
bitstream,

Then it could tell where the decoder should start.

>   That's indeed not what we want here, we
> instead need something similar to the offsets field of struct
> drm_mode_fb_cmd2.

That leads to another question. Should the offset be fixed from the 
first enqueued?

For the dmabuf, the v4l2 core framework would detatch then attach the 
buffer when it found the private of a plane is not same. Although it 
sounds unnecessary, some devices would a different cache line for the 
chroma channel, it should be updated.

For the drm_mode_fb_cmd2, unless you remove that fb_id, there is no way 
to modify the offset. But this would break the existing usage I 
mentioned before.

We need to consider whether we need to keep the previous offset and a 
hook for update it.

>>> I am thinking just add a field recording the offset input from the user.
>>> When we return the buffer back to the user, the value of the offset
>>> should be same as the it is queued.
>>>
>>> Meanwhile, the API compatible that I want to keep is user using the
>>> ext_pix API could access those drivers support old API.
>>> But I don't want the user would expect they could get correct pixel
>>> format using the old ioctl(). It could create many duplicated pixel
>>> formats. If we want to keep the compatible here, that is the job of
>>> libv4l.
>>>
>>> Besides, I think make the driver using the new API be compatible
>>> with the old ioctl() would lead a huge problem. User won't like to
>>> update its code if it could work even in a less performance mode
>>> because this code are for all the other hardware vendors/models.
>>> Unless we make this a feature, they could make a new branch in their
>>> code(don't count them would upate the kernel of the other products).
>> New drivers that require the additional information that these new ioctls give can
>> decide to just support these new ioctls only. But for existing drivers you want
>> to automatically support the new ioctls.

What I said didn't break that. Application would use the new ioctl() to 
contact with the existing driver.

What I want to remove is that Application use the old ioctl() to contact 
with the driver support new ioctl().

I would omit this related patches in the refresh set. We could always 
add it back. But what I want is a way  to enqueue and dequeue different 
formats(or usage) of buffers in both OUTPUT and CAPTURE. I may add a 
more complex API later.

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types
  2023-01-26 18:36         ` ayaka
@ 2023-01-27  8:11           ` Hans Verkuil
  2023-01-30 10:07             ` Hsia-Jun Li
  0 siblings, 1 reply; 48+ messages in thread
From: Hans Verkuil @ 2023-01-27  8:11 UTC (permalink / raw)
  To: ayaka, Laurent Pinchart
  Cc: randy.li, Brian.Starkey, frkoenig, hans.verkuil, helen.koike,
	hiroh, kernel, linux-kernel, linux-media, mchehab, narmstrong,
	nicolas, sakari.ailus, stanimir.varbanov, tfiga

On 26/01/2023 19:36, ayaka wrote:
> 
> On 1/26/23 19:02, Laurent Pinchart wrote:
>> On Thu, Jan 26, 2023 at 09:57:51AM +0100, Hans Verkuil wrote:
>>> On 25/01/2023 21:00, ayaka wrote:
>>>> I am currently refresh this patchset, but I didn't see the need beyond v4l2_ext_pix_fmt, which I had done.
>>>> On 2/23/21 20:58, Hans Verkuil wrote:
>>>>> On 14/01/2021 19:07, Helen Koike wrote:
>>>>>> Those extended buffer ops have several purpose:
>>>>>> 1/ Fix y2038 issues by converting the timestamp into an u64 counting
>>>>>>      the number of ns elapsed since 1970
>>>> I think application just use the timestamp field for tracking the
>>>> buffer. It would be just a sequence buffer.
>>>> At least for the most widely cases, the video encoder and decoder
>>>> and ISP, this field is not a wall time.
>>> For video capture and video output this is typically the monotonic
>>> clock value.
>>>
>>> For memory-to-memory devices it is something that is just copied from
>>> output to capture.
>>>
>>> So ISPs definitely use this as a proper timestamp.
>> There are both inline (live-to-memory) and offline (memory-to-memory)
>> ISPs. The former certainly need a proper timestamp.
>>
> I really have not seen a device that has timer starting with the epoch.
> 
> I rarely know the ISP has a wall clock timer.
> 
> Timestamp is not my first concern here. Offset is.

You are working in the V4L2 core framework here, something that is used
by all V4L2 drivers. So everything is important. You can't just focus on
your own use-case.

> 
>>>>>> 2/ Unify single/multiplanar handling
>>>>>> 3/ Add a new start offset field to each v4l2 plane buffer info struct
>>>>>>      to support the case where a single buffer object is storing all
>>>>>>      planes data, each one being placed at a different offset
>>>> I really care about this. But I think the data_offset field in
>>>> struct v4l2_plane is enough. The rest is the problem of the kernel
>>>> internal API and allocator.
>>> data_offset has proven to be very confusing and is rarely used because
>>> of that.
> Yes, I didn't know any stateful codec driver support this.
>>> We do need some sort of an offset field as proposed here, but it
>>> shouldn't be named data_offset.
> Maybe we could just rename it or make a union in the existing struct.
>> The existing data_offset field was indeed added for other purposes, to
>> let drivers report where the actual image data starts for devices that
>> prepend some sort of header.
> 
> For the compressed image, it makes sense. But the most of usage I knew is the upstream would just allocate a large buffer for compression video bitstream,
> 
> Then it could tell where the decoder should start.

It's not codec specific, it's meant to be used with raw video frames.

The key problem in today's API is that if the buffer for the video frame
contains multiple planes, typically Y and UV (2 planes) or Y, U and V (3 planes).

The offset at which each plane begins is currently a property of the
pixelformat. That doesn't scale since there are often HW requirements
that influence this.

One of the main confusing issues is that data_offset is included in
the bytesused value, which was a design mistake in hindsight.

For the new APIs just ignore the existing data_offset and design
this from scratch.

> 
>>   That's indeed not what we want here, we
>> instead need something similar to the offsets field of struct
>> drm_mode_fb_cmd2.
> 
> That leads to another question. Should the offset be fixed from the first enqueued?

It's always been fixed in the hardware I have seen, but I'm sure someone will
make it dynamic at some point in the future :-(

So I would say that the public API has to support this as a future enhancement,
but it is OK to write the initial code with the assumption that it will remain
fixed.

> 
> For the dmabuf, the v4l2 core framework would detatch then attach the buffer when it found the private of a plane is not same. Although it sounds unnecessary, some devices would a different cache line
> for the chroma channel, it should be updated.
> 
> For the drm_mode_fb_cmd2, unless you remove that fb_id, there is no way to modify the offset. But this would break the existing usage I mentioned before.
> 
> We need to consider whether we need to keep the previous offset and a hook for update it.
> 
>>>> I am thinking just add a field recording the offset input from the user.
>>>> When we return the buffer back to the user, the value of the offset
>>>> should be same as the it is queued.
>>>>
>>>> Meanwhile, the API compatible that I want to keep is user using the
>>>> ext_pix API could access those drivers support old API.
>>>> But I don't want the user would expect they could get correct pixel
>>>> format using the old ioctl(). It could create many duplicated pixel
>>>> formats. If we want to keep the compatible here, that is the job of
>>>> libv4l.
>>>>
>>>> Besides, I think make the driver using the new API be compatible
>>>> with the old ioctl() would lead a huge problem. User won't like to
>>>> update its code if it could work even in a less performance mode
>>>> because this code are for all the other hardware vendors/models.
>>>> Unless we make this a feature, they could make a new branch in their
>>>> code(don't count them would upate the kernel of the other products).
>>> New drivers that require the additional information that these new ioctls give can
>>> decide to just support these new ioctls only. But for existing drivers you want
>>> to automatically support the new ioctls.
> 
> What I said didn't break that. Application would use the new ioctl() to contact with the existing driver.
> 
> What I want to remove is that Application use the old ioctl() to contact with the driver support new ioctl().

No, you can't do that. Not unless the driver uses features that only work with the new API.

I.e. if I make a new driver whose properties are completely compatible with the existing
APIs (so no weird offsets etc.), then I want to write the driver using the new ioctls,
and leave it to the V4L2 framework to provide support for the old ioctls.

There is absolutely no reason to block old ioctls in that case. Applications will not
just be able to support a new API overnight, that takes years.

> 
> I would omit this related patches in the refresh set. We could always add it back. But what I want is a way  to enqueue and dequeue different formats(or usage) of buffers in both OUTPUT and CAPTURE. I
> may add a more complex API later.

For discussion it is OK to drop the old ioctl support, but once you go beyond the RFC stage
it has to be put back.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types
  2023-01-27  8:11           ` Hans Verkuil
@ 2023-01-30 10:07             ` Hsia-Jun Li
  2023-01-30 12:17               ` Hans Verkuil
  0 siblings, 1 reply; 48+ messages in thread
From: Hsia-Jun Li @ 2023-01-30 10:07 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Brian.Starkey, frkoenig, hans.verkuil, helen.koike, hiroh,
	kernel, linux-kernel, linux-media, mchehab, narmstrong, nicolas,
	sakari.ailus, stanimir.varbanov, tfiga, Laurent Pinchart, ayaka



On 1/27/23 16:11, Hans Verkuil wrote:
> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
> 
> 
> On 26/01/2023 19:36, ayaka wrote:
>>
>> On 1/26/23 19:02, Laurent Pinchart wrote:
>>> On Thu, Jan 26, 2023 at 09:57:51AM +0100, Hans Verkuil wrote:
>>>> On 25/01/2023 21:00, ayaka wrote:
>>>>> I am currently refresh this patchset, but I didn't see the need beyond v4l2_ext_pix_fmt, which I had done.
>>>>> On 2/23/21 20:58, Hans Verkuil wrote:
>>>>>> On 14/01/2021 19:07, Helen Koike wrote:
>>>>>>> Those extended buffer ops have several purpose:
>>>>>>> 1/ Fix y2038 issues by converting the timestamp into an u64 counting
>>>>>>>       the number of ns elapsed since 1970
>>>>> I think application just use the timestamp field for tracking the
>>>>> buffer. It would be just a sequence buffer.
>>>>> At least for the most widely cases, the video encoder and decoder
>>>>> and ISP, this field is not a wall time.
>>>> For video capture and video output this is typically the monotonic
>>>> clock value.
>>>>
>>>> For memory-to-memory devices it is something that is just copied from
>>>> output to capture.
>>>>
>>>> So ISPs definitely use this as a proper timestamp.
>>> There are both inline (live-to-memory) and offline (memory-to-memory)
>>> ISPs. The former certainly need a proper timestamp.
>>>
>> I really have not seen a device that has timer starting with the epoch.
>>
>> I rarely know the ISP has a wall clock timer.
>>
>> Timestamp is not my first concern here. Offset is.
> 
> You are working in the V4L2 core framework here, something that is used
> by all V4L2 drivers. So everything is important. You can't just focus on
> your own use-case.
> 
I was trying to say we could focus on a more important item: offset.
>>
>>>>>>> 2/ Unify single/multiplanar handling
>>>>>>> 3/ Add a new start offset field to each v4l2 plane buffer info struct
>>>>>>>       to support the case where a single buffer object is storing all
>>>>>>>       planes data, each one being placed at a different offset
>>>>> I really care about this. But I think the data_offset field in
>>>>> struct v4l2_plane is enough. The rest is the problem of the kernel
>>>>> internal API and allocator.
>>>> data_offset has proven to be very confusing and is rarely used because
>>>> of that.
>> Yes, I didn't know any stateful codec driver support this.
>>>> We do need some sort of an offset field as proposed here, but it
>>>> shouldn't be named data_offset.
>> Maybe we could just rename it or make a union in the existing struct.
>>> The existing data_offset field was indeed added for other purposes, to
>>> let drivers report where the actual image data starts for devices that
>>> prepend some sort of header.
>>
>> For the compressed image, it makes sense. But the most of usage I knew is the upstream would just allocate a large buffer for compression video bitstream,
>>
>> Then it could tell where the decoder should start.
> 
> It's not codec specific, it's meant to be used with raw video frames.
> 
> The key problem in today's API is that if the buffer for the video frame
> contains multiple planes, typically Y and UV (2 planes) or Y, U and V (3 planes).
> 
> The offset at which each plane begins is currently a property of the
> pixelformat. That doesn't scale since there are often HW requirements
> that influence this.
> 
> One of the main confusing issues is that data_offset is included in
> the bytesused value, which was a design mistake in hindsight.
> 
I didn't see any problem here.
And v6 does NOT have the length property. There are many reasons that we 
want to limit the hardware access region for a plane.
> For the new APIs just ignore the existing data_offset and design
> this from scratch.
> 
All I am worrying is the effort we spent didn't have much change to the 
previous. Beyond breaking the userspace compatibility, it needs huge 
work on updating existing drivers included those could be replaced with 
the new framework.

I highly recommend just rename a property which was not used by any 
drivers in the upstream and make a new description for it.

>>
>>>    That's indeed not what we want here, we
>>> instead need something similar to the offsets field of struct
>>> drm_mode_fb_cmd2.
>>
>> That leads to another question. Should the offset be fixed from the first enqueued?
> 
> It's always been fixed in the hardware I have seen, but I'm sure someone will
> make it dynamic at some point in the future :-(
> 
There have been two cases the offset won't be fixed.
1. video codec bitstream input in a circular buffer as mentioned in the 
previous mail
2. decoder resolution change(down) while reusing the existing 
buffer(re-allocation costs lots of time)
> So I would say that the public API has to support this as a future enhancement,
> but it is OK to write the initial code with the assumption that it will remain
> fixed.
> 
>>
>> For the dmabuf, the v4l2 core framework would detatch then attach the buffer when it found the private of a plane is not same. Although it sounds unnecessary, some devices would a different cache line
>> for the chroma channel, it should be updated.
>>
>> For the drm_mode_fb_cmd2, unless you remove that fb_id, there is no way to modify the offset. But this would break the existing usage I mentioned before.
>>
>> We need to consider whether we need to keep the previous offset and a hook for update it.
>>
>>>>> I am thinking just add a field recording the offset input from the user.
>>>>> When we return the buffer back to the user, the value of the offset
>>>>> should be same as the it is queued.
>>>>>
>>>>> Meanwhile, the API compatible that I want to keep is user using the
>>>>> ext_pix API could access those drivers support old API.
>>>>> But I don't want the user would expect they could get correct pixel
>>>>> format using the old ioctl(). It could create many duplicated pixel
>>>>> formats. If we want to keep the compatible here, that is the job of
>>>>> libv4l.
>>>>>
>>>>> Besides, I think make the driver using the new API be compatible
>>>>> with the old ioctl() would lead a huge problem. User won't like to
>>>>> update its code if it could work even in a less performance mode
>>>>> because this code are for all the other hardware vendors/models.
>>>>> Unless we make this a feature, they could make a new branch in their
>>>>> code(don't count them would upate the kernel of the other products).
>>>> New drivers that require the additional information that these new ioctls give can
>>>> decide to just support these new ioctls only. But for existing drivers you want
>>>> to automatically support the new ioctls.
>>
>> What I said didn't break that. Application would use the new ioctl() to contact with the existing driver.
>>
>> What I want to remove is that Application use the old ioctl() to contact with the driver support new ioctl().
> 
> No, you can't do that. Not unless the driver uses features that only work with the new API.
> 
> I.e. if I make a new driver whose properties are completely compatible with the existing
> APIs (so no weird offsets etc.), then I want to write the driver using the new ioctls,
> and leave it to the V4L2 framework to provide support for the old ioctls.
> 
> There is absolutely no reason to block old ioctls in that case. Applications will not
> just be able to support a new API overnight, that takes years.
> 
>>
>> I would omit this related patches in the refresh set. We could always add it back. But what I want is a way  to enqueue and dequeue different formats(or usage) of buffers in both OUTPUT and CAPTURE. I
>> may add a more complex API later.
> 
> For discussion it is OK to drop the old ioctl support, but once you go beyond the RFC stage
> it has to be put back.
> 
> Regards,
> 
>          Hans

-- 
Hsia-Jun(Randy) Li

^ permalink raw reply	[flat|nested] 48+ messages in thread

* Re: [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types
  2023-01-30 10:07             ` Hsia-Jun Li
@ 2023-01-30 12:17               ` Hans Verkuil
  0 siblings, 0 replies; 48+ messages in thread
From: Hans Verkuil @ 2023-01-30 12:17 UTC (permalink / raw)
  To: Hsia-Jun Li
  Cc: Brian.Starkey, frkoenig, hans.verkuil, helen.koike, hiroh,
	kernel, linux-kernel, linux-media, mchehab, narmstrong, nicolas,
	sakari.ailus, stanimir.varbanov, tfiga, Laurent Pinchart, ayaka

On 30/01/2023 11:07, Hsia-Jun Li wrote:
> 
> 
> On 1/27/23 16:11, Hans Verkuil wrote:
>> CAUTION: Email originated externally, do not click links or open attachments unless you recognize the sender and know the content is safe.
>>
>>
>> On 26/01/2023 19:36, ayaka wrote:
>>>
>>> On 1/26/23 19:02, Laurent Pinchart wrote:
>>>> On Thu, Jan 26, 2023 at 09:57:51AM +0100, Hans Verkuil wrote:
>>>>> On 25/01/2023 21:00, ayaka wrote:
>>>>>> I am currently refresh this patchset, but I didn't see the need beyond v4l2_ext_pix_fmt, which I had done.
>>>>>> On 2/23/21 20:58, Hans Verkuil wrote:
>>>>>>> On 14/01/2021 19:07, Helen Koike wrote:
>>>>>>>> Those extended buffer ops have several purpose:
>>>>>>>> 1/ Fix y2038 issues by converting the timestamp into an u64 counting
>>>>>>>>       the number of ns elapsed since 1970
>>>>>> I think application just use the timestamp field for tracking the
>>>>>> buffer. It would be just a sequence buffer.
>>>>>> At least for the most widely cases, the video encoder and decoder
>>>>>> and ISP, this field is not a wall time.
>>>>> For video capture and video output this is typically the monotonic
>>>>> clock value.
>>>>>
>>>>> For memory-to-memory devices it is something that is just copied from
>>>>> output to capture.
>>>>>
>>>>> So ISPs definitely use this as a proper timestamp.
>>>> There are both inline (live-to-memory) and offline (memory-to-memory)
>>>> ISPs. The former certainly need a proper timestamp.
>>>>
>>> I really have not seen a device that has timer starting with the epoch.
>>>
>>> I rarely know the ISP has a wall clock timer.
>>>
>>> Timestamp is not my first concern here. Offset is.
>>
>> You are working in the V4L2 core framework here, something that is used
>> by all V4L2 drivers. So everything is important. You can't just focus on
>> your own use-case.
>>
> I was trying to say we could focus on a more important item: offset.
>>>
>>>>>>>> 2/ Unify single/multiplanar handling
>>>>>>>> 3/ Add a new start offset field to each v4l2 plane buffer info struct
>>>>>>>>       to support the case where a single buffer object is storing all
>>>>>>>>       planes data, each one being placed at a different offset
>>>>>> I really care about this. But I think the data_offset field in
>>>>>> struct v4l2_plane is enough. The rest is the problem of the kernel
>>>>>> internal API and allocator.
>>>>> data_offset has proven to be very confusing and is rarely used because
>>>>> of that.
>>> Yes, I didn't know any stateful codec driver support this.
>>>>> We do need some sort of an offset field as proposed here, but it
>>>>> shouldn't be named data_offset.
>>> Maybe we could just rename it or make a union in the existing struct.
>>>> The existing data_offset field was indeed added for other purposes, to
>>>> let drivers report where the actual image data starts for devices that
>>>> prepend some sort of header.
>>>
>>> For the compressed image, it makes sense. But the most of usage I knew is the upstream would just allocate a large buffer for compression video bitstream,
>>>
>>> Then it could tell where the decoder should start.
>>
>> It's not codec specific, it's meant to be used with raw video frames.
>>
>> The key problem in today's API is that if the buffer for the video frame
>> contains multiple planes, typically Y and UV (2 planes) or Y, U and V (3 planes).
>>
>> The offset at which each plane begins is currently a property of the
>> pixelformat. That doesn't scale since there are often HW requirements
>> that influence this.
>>
>> One of the main confusing issues is that data_offset is included in
>> the bytesused value, which was a design mistake in hindsight.
>>
> I didn't see any problem here.
> And v6 does NOT have the length property. There are many reasons that we want to limit the hardware access region for a plane.
>> For the new APIs just ignore the existing data_offset and design
>> this from scratch.
>>
> All I am worrying is the effort we spent didn't have much change to the previous. Beyond breaking the userspace compatibility, it needs huge work on updating existing drivers included those could be
> replaced with the new framework.

What would you need to update existing drivers?

Perhaps the only drivers that would need work (I'm not at all certain about
that!) would be the drivers that use data_offset today. And frankly, it
wouldn't hurt if those are checked carefully anyway.

> 
> I highly recommend just rename a property which was not used by any drivers in the upstream and make a new description for it.
> 
>>>
>>>>    That's indeed not what we want here, we
>>>> instead need something similar to the offsets field of struct
>>>> drm_mode_fb_cmd2.
>>>
>>> That leads to another question. Should the offset be fixed from the first enqueued?
>>
>> It's always been fixed in the hardware I have seen, but I'm sure someone will
>> make it dynamic at some point in the future :-(
>>
> There have been two cases the offset won't be fixed.
> 1. video codec bitstream input in a circular buffer as mentioned in the previous mail
> 2. decoder resolution change(down) while reusing the existing buffer(re-allocation costs lots of time)
>> So I would say that the public API has to support this as a future enhancement,
>> but it is OK to write the initial code with the assumption that it will remain
>> fixed.
>>
>>>
>>> For the dmabuf, the v4l2 core framework would detatch then attach the buffer when it found the private of a plane is not same. Although it sounds unnecessary, some devices would a different cache line
>>> for the chroma channel, it should be updated.
>>>
>>> For the drm_mode_fb_cmd2, unless you remove that fb_id, there is no way to modify the offset. But this would break the existing usage I mentioned before.
>>>
>>> We need to consider whether we need to keep the previous offset and a hook for update it.
>>>
>>>>>> I am thinking just add a field recording the offset input from the user.
>>>>>> When we return the buffer back to the user, the value of the offset
>>>>>> should be same as the it is queued.
>>>>>>
>>>>>> Meanwhile, the API compatible that I want to keep is user using the
>>>>>> ext_pix API could access those drivers support old API.
>>>>>> But I don't want the user would expect they could get correct pixel
>>>>>> format using the old ioctl(). It could create many duplicated pixel
>>>>>> formats. If we want to keep the compatible here, that is the job of
>>>>>> libv4l.
>>>>>>
>>>>>> Besides, I think make the driver using the new API be compatible
>>>>>> with the old ioctl() would lead a huge problem. User won't like to
>>>>>> update its code if it could work even in a less performance mode
>>>>>> because this code are for all the other hardware vendors/models.
>>>>>> Unless we make this a feature, they could make a new branch in their
>>>>>> code(don't count them would upate the kernel of the other products).
>>>>> New drivers that require the additional information that these new ioctls give can
>>>>> decide to just support these new ioctls only. But for existing drivers you want
>>>>> to automatically support the new ioctls.
>>>
>>> What I said didn't break that. Application would use the new ioctl() to contact with the existing driver.
>>>
>>> What I want to remove is that Application use the old ioctl() to contact with the driver support new ioctl().
>>
>> No, you can't do that. Not unless the driver uses features that only work with the new API.
>>
>> I.e. if I make a new driver whose properties are completely compatible with the existing
>> APIs (so no weird offsets etc.), then I want to write the driver using the new ioctls,
>> and leave it to the V4L2 framework to provide support for the old ioctls.
>>
>> There is absolutely no reason to block old ioctls in that case. Applications will not
>> just be able to support a new API overnight, that takes years.
>>
>>>
>>> I would omit this related patches in the refresh set. We could always add it back. But what I want is a way  to enqueue and dequeue different formats(or usage) of buffers in both OUTPUT and CAPTURE. I
>>> may add a more complex API later.
>>
>> For discussion it is OK to drop the old ioctl support, but once you go beyond the RFC stage
>> it has to be put back.
>>
>> Regards,
>>
>>          Hans
> 

General note: this email thread is getting hard to read, and it is not helped
by the fact that the original patch series is very old.

Perhaps you should rebase v6 and post it as a v7?

Regards,

	Hans

^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2023-01-30 12:17 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-14 18:07 [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike
2021-01-14 18:07 ` [RFC PATCH v6 01/11] media: v4l2-common: add normalized pixelformat field to struct v4l2_format_info Helen Koike
2021-02-10 12:37   ` Dafna Hirschfeld
2021-01-14 18:07 ` [RFC PATCH v6 02/11] media: v4l2: Extend pixel formats to unify single/multi-planar handling (and more) Helen Koike
2021-02-10 15:02   ` Dafna Hirschfeld
2021-02-23 12:35   ` Hans Verkuil
2021-02-24 15:12     ` Helen Koike
2022-11-05 15:19       ` Hsia-Jun Li
2022-11-06 19:24         ` Laurent Pinchart
2022-11-07  1:54           ` Hsia-Jun Li
2022-11-07  8:28             ` Laurent Pinchart
2022-11-07  8:49               ` Hsia-Jun Li
2022-11-06 22:11         ` Dmitry Osipenko
2022-11-07  2:04           ` Hsia-Jun Li
2022-11-07  8:30           ` Laurent Pinchart
2022-11-08 14:58             ` Dmitry Osipenko
2022-11-07 16:50           ` Fritz Koenig
2022-11-07  8:42         ` Hans Verkuil
2022-11-10 17:06         ` Nicolas Dufresne
2022-11-11  3:03           ` Hsia-Jun Li
2022-11-11  5:48             ` Tomasz Figa
2022-11-11  6:30               ` Hsia-Jun Li
2022-11-11  8:52                 ` Tomasz Figa
2022-11-11  9:13                   ` Hsia-Jun Li
2022-11-15 16:03                   ` Nicolas Dufresne
2022-11-16 12:38                     ` ayaka
2022-11-11  8:42               ` Laurent Pinchart
2022-11-11  8:54                 ` Tomasz Figa
2022-11-15 16:19                   ` Nicolas Dufresne
2022-11-15 15:57             ` Nicolas Dufresne
2021-01-14 18:07 ` [RFC PATCH v6 03/11] media: v4l2: Add extended buffer (de)queue operations for video types Helen Koike
2021-02-23 12:58   ` Hans Verkuil
2023-01-26  7:07     ` ayaka
     [not found]   ` <20230125200026.16643-1-ayaka@soulik.info>
2023-01-26  8:57     ` Hans Verkuil
2023-01-26 11:02       ` Laurent Pinchart
2023-01-26 18:36         ` ayaka
2023-01-27  8:11           ` Hans Verkuil
2023-01-30 10:07             ` Hsia-Jun Li
2023-01-30 12:17               ` Hans Verkuil
2021-01-14 18:07 ` [RFC PATCH v6 04/11] media: videobuf2-v4l2: reorganize flags handling Helen Koike
2021-01-14 18:07 ` [RFC PATCH v6 05/11] media: videobuf2: Expose helpers for Ext qbuf/dqbuf Helen Koike
2021-01-14 18:07 ` [RFC PATCH v6 06/11] media: vivid: use vb2_ioctls_ext_{d}qbuf hooks Helen Koike
2021-01-14 18:07 ` [RFC PATCH v6 07/11] media: vimc: " Helen Koike
2021-01-14 18:07 ` [RFC PATCH v6 08/11] media: mediabus: Add helpers to convert a ext_pix format to/from a mbus_fmt Helen Koike
2021-01-14 18:07 ` [RFC PATCH v6 09/11] media: vivid: Convert to v4l2_ext_pix_format Helen Koike
2021-01-14 18:07 ` [RFC PATCH v6 10/11] media: vimc: " Helen Koike
2021-01-14 18:07 ` [RFC PATCH v6 11/11] media: docs: add documentation for the Extended API Helen Koike
2021-02-05 18:39 ` [RFC PATCH v6 00/11] media: v4l2: Add extended fmt and buffer ioctls Helen Koike

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).