diff mbox series

[RFC,v3,2/6] media: v4l2: Add extended buffer operations

Message ID 20191008091119.7294-3-boris.brezillon@collabora.com (mailing list archive)
State New, archived
Headers show
Series media: v4l2: Add extended fmt and buffer ioctls | expand

Commit Message

Boris Brezillon Oct. 8, 2019, 9:11 a.m. UTC
From: Hans Verkuil <hans.verkuil@cisco.com>

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.

The core takes care of converting new ioctls requests to old ones
if the driver does not support the new hooks, and vice versa.

Note that the timecode field is gone, since there doesn't seem to be
in-kernel users, but can be added back in the reserved area if needed.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
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
---
 drivers/media/v4l2-core/v4l2-dev.c   |  30 +-
 drivers/media/v4l2-core/v4l2-ioctl.c | 428 +++++++++++++++++++++++++--
 include/media/v4l2-ioctl.h           |  30 ++
 include/uapi/linux/videodev2.h       | 130 ++++++++
 4 files changed, 591 insertions(+), 27 deletions(-)

Comments

Fritz Koenig March 4, 2020, 5:54 p.m. UTC | #1
Thanks for putting this patch set together.  I'm currently trying to get
modifiers working for UBWC.  I've applied these patches and with a little
tweaking can get video decode working without modifiers.  I haven't tried the
new ioctls yet.
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> 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.
> 
> The core takes care of converting new ioctls requests to old ones
> if the driver does not support the new hooks, and vice versa.
> 
> Note that the timecode field is gone, since there doesn't seem to be
> in-kernel users, but can be added back in the reserved area if needed.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> 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
> ---
>  drivers/media/v4l2-core/v4l2-dev.c   |  30 +-
>  drivers/media/v4l2-core/v4l2-ioctl.c | 428 +++++++++++++++++++++++++--
>  include/media/v4l2-ioctl.h           |  30 ++
>  include/uapi/linux/videodev2.h       | 130 ++++++++
>  4 files changed, 591 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> index 9aad715537b3..35c8caccd025 100644
> --- a/drivers/media/v4l2-core/v4l2-dev.c
> +++ b/drivers/media/v4l2-core/v4l2-dev.c
> @@ -696,12 +696,30 @@ static void determine_valid_ioctls(struct video_device *vdev)
>  	if (is_vid || is_vbi || is_sdr || is_tch) {
>  		/* ioctls valid for video, metadata, vbi or sdr */
>  		SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
> -		SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
> -		SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
> -		SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf);
> -		SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
> -		SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
> -		SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
> +		if (ops->vidioc_querybuf || ops->vidioc_ext_querybuf) {
> +			set_bit(_IOC_NR(VIDIOC_QUERYBUF), valid_ioctls);
> +			set_bit(_IOC_NR(VIDIOC_EXT_QUERYBUF), valid_ioctls);
> +		}
> +		if (ops->vidioc_qbuf || ops->vidioc_ext_qbuf) {
> +			set_bit(_IOC_NR(VIDIOC_QBUF), valid_ioctls);
> +			set_bit(_IOC_NR(VIDIOC_EXT_QBUF), valid_ioctls);
> +		}
> +		if (ops->vidioc_expbuf || ops->vidioc_ext_expbuf) {
> +			set_bit(_IOC_NR(VIDIOC_EXPBUF), valid_ioctls);
> +			set_bit(_IOC_NR(VIDIOC_EXT_EXPBUF), valid_ioctls);
> +		}
> +		if (ops->vidioc_dqbuf || ops->vidioc_ext_dqbuf) {
> +			set_bit(_IOC_NR(VIDIOC_DQBUF), valid_ioctls);
> +			set_bit(_IOC_NR(VIDIOC_EXT_DQBUF), valid_ioctls);
> +		}
> +		if (ops->vidioc_create_bufs || ops->vidioc_ext_create_bufs) {
> +			set_bit(_IOC_NR(VIDIOC_CREATE_BUFS), valid_ioctls);
> +			set_bit(_IOC_NR(VIDIOC_EXT_CREATE_BUFS), valid_ioctls);
> +		}
> +		if (ops->vidioc_prepare_buf || ops->vidioc_ext_prepare_buf) {
> +			set_bit(_IOC_NR(VIDIOC_PREPARE_BUF), valid_ioctls);
> +			set_bit(_IOC_NR(VIDIOC_EXT_PREPARE_BUF), valid_ioctls);
> +		}
>  		SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
>  		SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
>  	}
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 78e14c1dc76f..356218e44ccb 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -579,6 +579,25 @@ 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 *p = arg;
> +	const struct v4l2_ext_plane *plane;
> +	int i;
> +
> +	pr_cont("%lld index=%d, type=%s, flags=0x%08x, field=%s, sequence=%d, memory=%s\n",
> +		p->timestamp, p->index, prt_names(p->type, v4l2_type_names),
> +		p->flags, prt_names(p->field, v4l2_field_names),
> +		p->sequence, prt_names(p->memory, v4l2_memory_names));
> +
> +	for (i = 0; i < p->num_planes; ++i) {
> +		plane = &p->planes[i];
> +		pr_debug("plane %d: bytesused=%d, data_offset=0x%08x, offset/userptr=0x%llx, length=%d\n",
> +			 i, plane->bytesused, plane->data_offset,
> +			 plane->m.userptr, plane->length);
> +	}
> +}
> +
>  static void v4l_print_exportbuffer(const void *arg, bool write_only)
>  {
>  	const struct v4l2_exportbuffer *p = arg;
> @@ -588,6 +607,18 @@ static void v4l_print_exportbuffer(const void *arg, bool write_only)
>  		p->index, p->plane, p->flags);
>  }
>  
> +static void v4l_print_ext_exportbuffer(const void *arg, bool write_only)
> +{
> +	const struct v4l2_ext_exportbuffer *p = arg;
> +	unsigned int i;
> +
> +	pr_cont("type=%s, index=%u, first_plane=%u num_planes=%u, flags=%08x\n",
> +		prt_names(p->type, v4l2_type_names), p->index, p->first_plane,
> +		p->num_planes, p->flags);
> +	for (i = p->first_plane; i < p->first_plane + p->num_planes; ++i)
> +		pr_debug("plane %u: fd=%d\n", i, p->fds[i]);
> +}
> +
>  static void v4l_print_create_buffers(const void *arg, bool write_only)
>  {
>  	const struct v4l2_create_buffers *p = arg;
> @@ -598,6 +629,15 @@ static void v4l_print_create_buffers(const void *arg, bool write_only)
>  	v4l_print_format(&p->format, write_only);
>  }
>  
> +static void v4l_print_ext_create_buffers(const void *arg, bool write_only)
> +{
> +	const struct v4l2_ext_create_buffers *p = arg;
> +
> +	pr_cont("index=%d, count=%d, memory=%s, ", p->index, p->count,
> +		prt_names(p->memory, v4l2_memory_names));
> +	v4l_print_ext_format(&p->format, write_only);
> +}
> +
>  static void v4l_print_streamparm(const void *arg, bool write_only)
>  {
>  	const struct v4l2_streamparm *p = arg;
> @@ -1319,6 +1359,123 @@ int v4l2_format_to_ext_format(const struct v4l2_format *f,
>  }
>  EXPORT_SYMBOL_GPL(v4l2_format_to_ext_format);
>  
> +int v4l2_ext_buffer_to_buffer(const struct v4l2_ext_buffer *e,
> +			      struct v4l2_buffer *b, bool mplane_cap)
> +{
> +	u64 nsecs;
> +
> +	if (!mplane_cap && e->num_planes > 1)
> +		return -EINVAL;
> +
> +	memset(b, 0, sizeof(*b));

b->planes needs to be preserved.

I was not able to find a tree that these patches applied to cleanly.  In v5.4
the struct v4l2_buffer member planes is a raw pointer, so this memset will
clear it out.

> +
> +	b->index = e->index;
> +	b->flags = e->flags;
> +	b->field = e->field;
> +	b->sequence = e->sequence;
> +	b->memory = e->memory;
> +	b->request_fd = e->request_fd;
> +	b->timestamp.tv_sec = div64_u64_rem(e->timestamp, NSEC_PER_SEC, &nsecs);
> +	b->timestamp.tv_usec = (u32)nsecs / NSEC_PER_USEC;
> +	if (mplane_cap) {
> +		unsigned int i;
> +
> +		if (e->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +			b->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> +		else
> +			b->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> +
> +		b->length = e->num_planes;
> +		for (i = 0; i < e->num_planes; i++) {
> +			if (b->memory == V4L2_MEMORY_DMABUF) {
> +				if (e->planes[i].m.dmabuf.offset)
> +					return -EINVAL;
> +
> +				b->m.planes[i].m.fd = e->planes[i].m.dmabuf.fd;
> +			} else {
> +				b->m.planes[i].m.userptr = e->planes[i].m.userptr;
> +			}
> +			b->m.planes[i].length = e->planes[i].length;
> +			b->m.planes[i].bytesused = e->planes[i].bytesused;
> +			b->m.planes[i].data_offset = e->planes[i].data_offset;
> +			memset(b->m.planes[i].reserved, 0,
> +			       sizeof(b->m.planes[i].reserved));
> +		}
> +	} else {
> +		b->type = e->type;
> +		b->bytesused = e->planes[0].bytesused;
> +		b->length = e->planes[0].length;
> +		if (b->memory == V4L2_MEMORY_DMABUF) {
> +			if (e->planes[0].m.dmabuf.offset)
> +				return -EINVAL;
> +
> +			b->m.fd = e->planes[0].m.dmabuf.fd;
> +		} else {
> +			b->m.userptr = e->planes[0].m.userptr;
> +		}
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_ext_buffer_to_buffer);
> +
> +int v4l2_buffer_to_ext_buffer(const struct v4l2_buffer *b,
> +			      struct v4l2_ext_buffer *e)
> +{
> +	memset(e, 0, sizeof(*e));
> +
> +	e->index = b->index;
> +	e->flags = b->flags;
> +	e->field = b->field;
> +	e->sequence = b->sequence;
> +	e->memory = b->memory;
> +	e->request_fd = b->request_fd;
> +	e->timestamp = b->timestamp.tv_sec * NSEC_PER_SEC +
> +		b->timestamp.tv_usec * NSEC_PER_USEC;
> +	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
> +		unsigned int i;
> +
> +		if (!b->m.planes)
> +			return -EINVAL;
> +
> +		if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> +			e->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +		else
> +			e->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +
> +		e->num_planes = b->length;
> +		for (i = 0; i < e->num_planes; i++) {
> +			if (b->memory == V4L2_MEMORY_DMABUF) {
> +				e->planes[i].m.dmabuf.fd = b->m.planes[i].m.fd;
> +				e->planes[i].m.dmabuf.offset = 0;
> +			} else {
> +				e->planes[i].m.userptr = b->m.planes[i].m.userptr;
> +			}
> +			e->planes[i].length = b->m.planes[i].length;
> +			e->planes[i].bytesused = b->m.planes[i].bytesused;
> +			e->planes[i].data_offset = b->m.planes[i].data_offset;
> +			memset(e->planes[i].reserved, 0,
> +			       sizeof(e->planes[i].reserved));
> +		}
> +	} else {
> +		e->type = b->type;
> +		e->num_planes = 1;
> +		e->planes[0].bytesused = b->bytesused;
> +		e->planes[0].length = b->length;
> +		if (b->memory == V4L2_MEMORY_DMABUF) {
> +			e->planes[0].m.dmabuf.fd = b->m.fd;
> +			e->planes[0].m.dmabuf.offset = 0;
> +		} else {
> +			e->planes[0].m.userptr = b->m.userptr;
> +		}
> +		e->planes[0].m.userptr = b->m.userptr;
> +		e->planes[0].data_offset = 0;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_buffer_to_ext_buffer);
> +
>  static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
> @@ -2506,31 +2663,109 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
>  	return ops->vidioc_reqbufs(file, fh, p);
>  }
>  
> -static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
> -				struct file *file, void *fh, void *arg)
> +static int v4l_do_buf_op(int (*op)(struct file *, void *,
> +				   struct v4l2_buffer *),
> +			 int (*ext_op)(struct file *, void *,
> +				       struct v4l2_ext_buffer *),
> +			 struct file *file, void *fh, struct v4l2_buffer *b)
>  {
> -	struct v4l2_buffer *p = arg;
> -	int ret = check_fmt(file, p->type);
> +	struct v4l2_ext_buffer eb;
> +	int ret;
>  
> -	return ret ? ret : ops->vidioc_querybuf(file, fh, p);
> +	ret = check_fmt(file, b->type);
> +	if (ret)
> +		return ret;
> +
> +	if (op)
> +		return op(file, fh, b);
> +
> +	ret = v4l2_buffer_to_ext_buffer(b, &eb);
> +	if (ret)
> +		return ret;
> +
> +	ret = ext_op(file, fh, &eb);
> +	if (ret)
> +		return ret;
> +
> +	v4l2_ext_buffer_to_buffer(&eb, b, V4L2_TYPE_IS_MULTIPLANAR(b->type));
> +	return 0;
> +}
> +
> +static int v4l_do_ext_buf_op(int (*op)(struct file *, void *,
> +				       struct v4l2_buffer *),
> +			     int (*ext_op)(struct file *, void *,
> +					   struct v4l2_ext_buffer *),
> +			     struct file *file, void *fh,
> +			     struct v4l2_ext_buffer *eb)
> +{
> +	struct video_device *vdev = video_devdata(file);
> +	struct v4l2_buffer b;
> +	bool mplane_cap;
> +	int ret;
> +
> +	ret = check_fmt(file, eb->type);
> +	if (ret)
> +		return ret;
> +
> +	if (ext_op)
> +		return ext_op(file, fh, eb);
> +
> +	mplane_cap = !!(vdev->device_caps &
> +			(V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> +			 V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> +			 V4L2_CAP_VIDEO_M2M_MPLANE));
> +	ret = v4l2_ext_buffer_to_buffer(eb, &b, mplane_cap);
> +	if (ret)
> +		return ret;
> +
> +	ret = op(file, fh, &b);
> +	if (ret)
> +		return ret;
> +
> +	v4l2_buffer_to_ext_buffer(&b, eb);
> +	return 0;
> +}
> +
> +static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
> +			struct file *file, void *fh, void *arg)
> +{
> +	return v4l_do_buf_op(ops->vidioc_querybuf, ops->vidioc_ext_querybuf,
> +			     file, fh, arg);
> +}
> +
> +static int v4l_ext_querybuf(const struct v4l2_ioctl_ops *ops,
> +			    struct file *file, void *fh, void *arg)
> +{
> +	return v4l_do_ext_buf_op(ops->vidioc_querybuf,
> +				 ops->vidioc_ext_querybuf, file, fh, arg);
>  }
>  
>  static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
> -				struct file *file, void *fh, void *arg)
> +		    struct file *file, void *fh, void *arg)
>  {
> -	struct v4l2_buffer *p = arg;
> -	int ret = check_fmt(file, p->type);
> +	return v4l_do_buf_op(ops->vidioc_qbuf, ops->vidioc_ext_qbuf,
> +			     file, fh, arg);
> +}
>  
> -	return ret ? ret : ops->vidioc_qbuf(file, fh, p);
> +static int v4l_ext_qbuf(const struct v4l2_ioctl_ops *ops,
> +			struct file *file, void *fh, void *arg)
> +{
> +	return v4l_do_ext_buf_op(ops->vidioc_qbuf, ops->vidioc_ext_qbuf,
> +				 file, fh, arg);
>  }
>  
>  static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
> -				struct file *file, void *fh, void *arg)
> +		     struct file *file, void *fh, void *arg)
>  {
> -	struct v4l2_buffer *p = arg;
> -	int ret = check_fmt(file, p->type);
> +	return v4l_do_buf_op(ops->vidioc_dqbuf, ops->vidioc_ext_dqbuf,
> +			     file, fh, arg);
> +}
>  
> -	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)
> +{
> +	return v4l_do_ext_buf_op(ops->vidioc_dqbuf, ops->vidioc_ext_dqbuf,
> +				 file, fh, arg);
>  }
>  
>  static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
> @@ -2546,7 +2781,27 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
>  
>  	v4l_sanitize_format(&create->format);
>  
> -	ret = ops->vidioc_create_bufs(file, fh, create);
> +	if (ops->vidioc_create_bufs) {
> +		ret = ops->vidioc_create_bufs(file, fh, create);
> +	} else {
> +		struct v4l2_ext_create_buffers ecreate = {
> +			.count = create->count,
> +			.memory = create->memory,
> +		};
> +
> +		ret = v4l2_format_to_ext_format(&create->format,
> +						&ecreate.format, true);
> +		if (ret)
> +			return ret;
> +
> +		ret = ops->vidioc_ext_create_bufs(file, fh, &ecreate);
> +		if (ret)
> +			return ret;
> +
> +		create->index = ecreate.index;
> +		create->count = ecreate.count;
> +		create->capabilities = ecreate.capabilities;
> +	}
>  
>  	if (create->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
>  	    create->format.type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
> @@ -2555,13 +2810,59 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
>  	return ret;
>  }
>  
> -static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
> -				struct file *file, void *fh, void *arg)
> +static int v4l_ext_create_bufs(const struct v4l2_ioctl_ops *ops,
> +			       struct file *file, void *fh, void *arg)
>  {
> -	struct v4l2_buffer *b = arg;
> -	int ret = check_fmt(file, b->type);
> +	struct v4l2_ext_create_buffers *ecreate = arg;
> +	struct video_device *vdev = video_devdata(file);
> +	struct v4l2_create_buffers create = {
> +		.count = ecreate->count,
> +		.memory = ecreate->memory,
> +	};
> +	bool mplane_cap;
> +	int ret;
>  
> -	return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
> +	ret = check_fmt(file, ecreate->format.type);
> +	if (ret)
> +		return ret;
> +
> +	if (ops->vidioc_ext_create_bufs)
> +		return ops->vidioc_ext_create_bufs(file, fh, ecreate);
> +
> +	mplane_cap = !!(vdev->device_caps &
> +			(V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> +			 V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> +			 V4L2_CAP_VIDEO_M2M_MPLANE));
> +	ret = v4l2_ext_format_to_format(&ecreate->format,
> +					&create.format, mplane_cap, true);
> +	if (ret)
> +		return ret;
> +
> +	ret = v4l_create_bufs(ops, file, fh, &create);
> +	if (ret)
> +		return ret;
> +
> +	ecreate->index = create.index;
> +	ecreate->count = create.count;
> +	ecreate->capabilities = create.capabilities;
> +
> +	return 0;
> +}
> +
> +static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
> +			   struct file *file, void *fh, void *arg)
> +{
> +	return v4l_do_buf_op(ops->vidioc_prepare_buf,
> +			     ops->vidioc_ext_prepare_buf,
> +			     file, fh, arg);
> +}
> +
> +static int v4l_ext_prepare_buf(const struct v4l2_ioctl_ops *ops,
> +			       struct file *file, void *fh, void *arg)
> +{
> +	return v4l_do_ext_buf_op(ops->vidioc_prepare_buf,
> +				 ops->vidioc_ext_prepare_buf,
> +				 file, fh, arg);
>  }
>  
>  static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
> @@ -3159,6 +3460,86 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
>  	return -ENOTTY;
>  }
>  
> +static int v4l_expbuf(const struct v4l2_ioctl_ops *ops, struct file *file,
> +		      void *fh, void *arg)
> +{
> +	struct v4l2_exportbuffer *b = arg;
> +	struct v4l2_ext_exportbuffer eb = {
> +		.type = b->type,
> +		.index = b->index,
> +		.first_plane = b->plane,
> +		.num_planes = 1,
> +		.flags = b->flags,
> +	};
> +	int ret;
> +
> +	if (ops->vidioc_expbuf)
> +		return ops->vidioc_expbuf(file, fh, b);
> +
> +	if (b->plane >= VIDEO_MAX_PLANES)
> +		return -EINVAL;
> +
> +	ret = ops->vidioc_ext_expbuf(file, fh, &eb);
> +	if (ret)
> +		return ret;
> +
> +	b->fd = eb.fds[b->plane];
> +	return 0;
> +}
> +
> +static int v4l_ext_expbuf(const struct v4l2_ioctl_ops *ops,
> +			  struct file *file, void *fh, void *arg)
> +{
> +	struct v4l2_ext_exportbuffer *eb = arg;
> +	unsigned int i;
> +	int ret;
> +
> +	if (eb->first_plane >= VIDEO_MAX_PLANES ||
> +	    eb->num_planes > VIDEO_MAX_PLANES ||
> +	    eb->first_plane + eb->num_planes > VIDEO_MAX_PLANES)
> +		return -EINVAL;
> +
> +	if (ops->vidioc_ext_expbuf)
> +		return ops->vidioc_ext_expbuf(file, fh, eb);
> +
> +	for (i = eb->first_plane; i < eb->first_plane + eb->num_planes; i++) {
> +		struct v4l2_exportbuffer b = {
> +			.type = eb->type,
> +			.index = eb->index,
> +			.plane = i,
> +			.flags = eb->flags,
> +		};
> +
> +		ret = ops->vidioc_expbuf(file, fh, &b);
> +		if (ret)
> +			goto err_put_dmabufs;
> +
> +		eb->fds[i] = b.fd;
> +	}
> +
> +	return 0;
> +
> +err_put_dmabufs:
> +	for (i = eb->first_plane; i < eb->first_plane + eb->num_planes; i++) {
> +		struct dma_buf *dmabuf;
> +
> +		if (eb->fds[i] <= 0)
> +			break;
> +
> +		/*
> +		 * We must call dma_buf_put() twice because we got one
> +		 * reference taken at dmabuf creation time one taken when
> +		 * calling dma_buf_get().
> +		 * FIXME: not entirely sure this works correctly.
> +		 */
> +		dmabuf = dma_buf_get(eb->fds[i]);
> +		dma_buf_put(dmabuf);
> +		dma_buf_put(dmabuf);
> +	}
> +
> +	return ret;
> +}
> +
>  struct v4l2_ioctl_info {
>  	unsigned int ioctl;
>  	u32 flags;
> @@ -3201,7 +3582,6 @@ struct v4l2_ioctl_info {
>  
>  DEFINE_V4L_STUB_FUNC(g_fbuf)
>  DEFINE_V4L_STUB_FUNC(s_fbuf)
> -DEFINE_V4L_STUB_FUNC(expbuf)
>  DEFINE_V4L_STUB_FUNC(g_std)
>  DEFINE_V4L_STUB_FUNC(g_audio)
>  DEFINE_V4L_STUB_FUNC(s_audio)
> @@ -3237,12 +3617,16 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
>  	IOCTL_INFO(VIDIOC_S_EXT_FMT, v4l_s_ext_fmt, v4l_print_ext_format, INFO_FL_PRIO),
>  	IOCTL_INFO(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
>  	IOCTL_INFO(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
> +	IOCTL_INFO(VIDIOC_EXT_QUERYBUF, v4l_ext_querybuf, v4l_print_ext_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_ext_buffer, num_planes)),
>  	IOCTL_INFO(VIDIOC_G_FBUF, v4l_stub_g_fbuf, v4l_print_framebuffer, 0),
>  	IOCTL_INFO(VIDIOC_S_FBUF, v4l_stub_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
>  	IOCTL_INFO(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
>  	IOCTL_INFO(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
> -	IOCTL_INFO(VIDIOC_EXPBUF, v4l_stub_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
> +	IOCTL_INFO(VIDIOC_EXT_QBUF, v4l_ext_qbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
> +	IOCTL_INFO(VIDIOC_EXPBUF, v4l_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
> +	IOCTL_INFO(VIDIOC_EXT_EXPBUF, v4l_ext_expbuf, v4l_print_ext_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
>  	IOCTL_INFO(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
> +	IOCTL_INFO(VIDIOC_EXT_DQBUF, v4l_ext_dqbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
>  	IOCTL_INFO(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
>  	IOCTL_INFO(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
>  	IOCTL_INFO(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
> @@ -3307,7 +3691,9 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
>  	IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
>  	IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
>  	IOCTL_INFO(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
> +	IOCTL_INFO(VIDIOC_EXT_CREATE_BUFS, v4l_ext_create_bufs, v4l_print_ext_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
>  	IOCTL_INFO(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
> +	IOCTL_INFO(VIDIOC_EXT_PREPARE_BUF, v4l_ext_prepare_buf, v4l_print_ext_buffer, INFO_FL_QUEUE),
>  	IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, v4l_stub_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)),
>  	IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, v4l_stub_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY),
>  	IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, v4l_stub_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, pad)),
> diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
> index 39ac07fbc7b7..f7e375d38602 100644
> --- a/include/media/v4l2-ioctl.h
> +++ b/include/media/v4l2-ioctl.h
> @@ -168,16 +168,28 @@ struct v4l2_fh;
>   *	:ref:`VIDIOC_REQBUFS <vidioc_reqbufs>` ioctl
>   * @vidioc_querybuf: pointer to the function that implements
>   *	:ref:`VIDIOC_QUERYBUF <vidioc_querybuf>` ioctl
> + * @vidioc_ext_querybuf: pointer to the function that implements
> + *	:ref:`VIDIOC_EXT_QUERYBUF <vidioc_ext_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_ext_expbuf: pointer to the function that implements
> + *	:ref:`VIDIOC_EXT_EXPBUF <vidioc_ext_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_ext_create_bufs: pointer to the function that implements
> + *	:ref:`VIDIOC_EXT_CREATE_BUFS <vidioc_ext_create_bufs>` ioctl
>   * @vidioc_prepare_buf: pointer to the function that implements
>   *	:ref:`VIDIOC_PREPARE_BUF <vidioc_prepare_buf>` ioctl
> + * @vidioc_ext_prepare_buf: pointer to the function that implements
> + *	:ref:`VIDIOC_EXT_PREPARE_BUF <vidioc_ext_prepare_buf>` ioctl
>   * @vidioc_overlay: pointer to the function that implements
>   *	:ref:`VIDIOC_OVERLAY <vidioc_overlay>` ioctl
>   * @vidioc_g_fbuf: pointer to the function that implements
> @@ -438,17 +450,29 @@ struct v4l2_ioctl_ops {
>  			      struct v4l2_requestbuffers *b);
>  	int (*vidioc_querybuf)(struct file *file, void *fh,
>  			       struct v4l2_buffer *b);
> +	int (*vidioc_ext_querybuf)(struct file *file, void *fh,
> +				   struct v4l2_ext_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_ext_expbuf)(struct file *file, void *fh,
> +				 struct v4l2_ext_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);
> +	int (*vidioc_ext_create_bufs)(struct file *file, void *fh,
> +				      struct v4l2_ext_create_buffers *b);
>  	int (*vidioc_prepare_buf)(struct file *file, void *fh,
>  				  struct v4l2_buffer *b);
> +	int (*vidioc_ext_prepare_buf)(struct file *file, void *fh,
> +				      struct v4l2_ext_buffer *b);
>  
>  	int (*vidioc_overlay)(struct file *file, void *fh, unsigned int i);
>  	int (*vidioc_g_fbuf)(struct file *file, void *fh,
> @@ -757,4 +781,10 @@ int v4l2_ext_format_to_format(const struct v4l2_ext_format *e,
>  			      struct v4l2_format *f,
>  			      bool mplane_cap, bool strict);
>  
> +int v4l2_ext_buffer_to_buffer(const struct v4l2_ext_buffer *e,
> +			      struct v4l2_buffer *b,
> +			      bool mplane_cap);
> +int v4l2_buffer_to_ext_buffer(const struct v4l2_buffer *b,
> +			      struct v4l2_ext_buffer *e);
> +
>  #endif /* _V4L2_IOCTL_H */
> diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> index c7b169de1c7b..33c8348df13f 100644
> --- a/include/uapi/linux/videodev2.h
> +++ b/include/uapi/linux/videodev2.h
> @@ -953,6 +953,49 @@ struct v4l2_plane {
>  	__u32			reserved[11];
>  };
>  
> +/**
> + * struct v4l2_ext_plane - extended plane buffer info
> + * @bytesused: number of bytes occupied by data in the plane (payload)
> + * @length: size of this plane (NOT the payload) in bytes
> + * @mem_offset: when memory in the associated struct v4l2_ext_buffer is
> + *		V4L2_MEMORY_MMAP, equals the offset from the start of the
> + *		device memory for this plane (or is 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
> + * @dmabuf.offset: where the plane starts inside the DMABUF buffer. All planes
> + *		   might share the same buffer object. In this case we need to
> + *		   know where the plane start inside this buffer.
> + * @data_offset: offset in the plane to the start of data; usually 0, unless
> + *		 there is a header in front of the data. data_offset is
> + *		 relative to start_offset, so absolute data_offset is actually
> + *		 start_offset + data_offset
> + *
> + *
> + * Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer
> + * with two planes can have one plane for Y, and another for interleaved CbCr
> + * components. Each plane can reside in a separate memory buffer, or even in
> + * a completely separate memory node (e.g. in embedded devices).
> + * Note that this struct is also used for uni-planar buffers, but in that case
> + * you'll only have one plane defined.
> + */
> +struct v4l2_ext_plane {
> +	__u32 bytesused;
> +	__u32 length;
> +	union {
> +		__u32 mem_offset;
> +		__u64 userptr;
> +		struct {
> +			__s32 fd;
> +			__u32 offset;
> +		} dmabuf;
> +	} m;
> +	__u32 data_offset;
> +	__u32 reserved[10];
> +};
> +
>  /**
>   * struct v4l2_buffer - video buffer info
>   * @index:	id number of the buffer
> @@ -1010,6 +1053,40 @@ struct v4l2_buffer {
>  	};
>  };
>  
> +/**
> + * struct v4l2_ext_buffer - extended video buffer info
> + * @index: id number of the buffer
> + * @type: enum v4l2_buf_type; buffer type. _MPLANE and _OVERLAY formats are
> + *	  invalid
> + * @flags: buffer informational flags
> + * @field: enum v4l2_field; field order of the image in the buffer
> + * @timestamp: frame timestamp
> + * @sequence: sequence count of this frame
> + * @memory: enum v4l2_memory; the method, in which the actual video data is
> + *          passed
> + * @planes: per-plane buffer information
> + * @num_planes: number of plane buffers
> + * @request_fd: fd of the request that this buffer should use
> + * @reserved: some extra space reserved to add future fields (like timecode).
> + *	      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 flags;
> +	__u32 field;
> +	__u64 timestamp;
> +	__u32 sequence;
> +	__u32 memory;
> +	struct v4l2_ext_plane planes[VIDEO_MAX_PLANES];
> +	__u32 num_planes;
> +	__u32 request_fd;
> +	__u32 reserved[10];
> +};
> +
>  /**
>   * v4l2_timeval_to_ns - Convert timeval to nanoseconds
>   * @ts:		pointer to the timeval variable to be converted
> @@ -1087,6 +1164,35 @@ struct v4l2_exportbuffer {
>  	__u32		reserved[11];
>  };
>  
> +/**
> + * struct v4l2_ext_exportbuffer - export of video buffer as DMABUF file
> + *				  descriptor using extended format
> + *
> + * @index: id number of the buffer
> + * @type: enum v4l2_buf_type; buffer type
> + * @flags: flags for newly created file(s), currently only O_CLOEXEC is
> + *	   supported, refer to manual of open syscall for more details
> + * @first_plane: first plane to export. Most likely set to 0
> + * @num_planes: number of planes to export. Most set to the number of planes
> + *		attached to the buffer
> + * @fds: file descriptors associated with DMABUF (set by driver). Note that all
> + *	 planes might share the same buffer and then be returned the same FD
> + *
> + * Contains data used for exporting a video buffer as DMABUF file descriptor.
> + * The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF
> + * (identical to the cookie used to mmap() the buffer to userspace). All
> + * reserved fields must be set to zero.
> + */
> +struct v4l2_ext_exportbuffer {
> +	__u32 type; /* enum v4l2_buf_type */
> +	__u32 index;
> +	__u32 flags;
> +	__u32 first_plane;
> +	__u32 num_planes;
> +	__s32 fds[VIDEO_MAX_PLANES];
> +	__u32 reserved;
> +};
> +
>  /*
>   *	O V E R L A Y   P R E V I E W
>   */
> @@ -2483,6 +2589,23 @@ struct v4l2_create_buffers {
>  	__u32			reserved[7];
>  };
>  
> +/**
> + * struct v4l2_ext_create_buffers - VIDIOC_EXT_CREATE_BUFS argument
> + * @index:	on return, index of the first created buffer
> + * @count:	entry: number of requested buffers,
> + *		return: number of created buffers
> + * @memory:	enum v4l2_memory; buffer memory type
> + * @capabilities: capabilities of this buffer type.
> + * @format:	frame format, for which buffers are requested
> + */
> +struct v4l2_ext_create_buffers {
> +	__u32			index;
> +	__u32			count;
> +	__u32			memory;
> +	__u32			capabilities;
> +	struct v4l2_ext_format	format;
> +};
> +
>  /*
>   *	I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
>   *
> @@ -2586,6 +2709,13 @@ struct v4l2_create_buffers {
>  #define VIDIOC_G_EXT_FMT	_IOWR('V', 104, struct v4l2_ext_format)
>  #define VIDIOC_S_EXT_FMT	_IOWR('V', 105, struct v4l2_ext_format)
>  #define VIDIOC_TRY_EXT_FMT	_IOWR('V', 106, struct v4l2_ext_format)
> +#define VIDIOC_EXT_CREATE_BUFS	_IOWR('V', 107, struct v4l2_ext_create_buffers)
> +#define VIDIOC_EXT_QUERYBUF	_IOWR('V', 108, struct v4l2_ext_buffer)
> +#define VIDIOC_EXT_QBUF		_IOWR('V', 109, struct v4l2_ext_buffer)
> +#define VIDIOC_EXT_DQBUF	_IOWR('V', 110, struct v4l2_ext_buffer)
> +#define VIDIOC_EXT_PREPARE_BUF	_IOWR('V', 111, struct v4l2_ext_buffer)
> +#define VIDIOC_EXT_EXPBUF	_IOWR('V', 112, struct v4l2_ext_exportbuffer)
> +
>  /* Reminder: when adding new ioctls please add support for them to
>     drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
>  
> -- 
> 2.21.0
> 
>
Fritz Koenig March 4, 2020, 6:25 p.m. UTC | #2
On Wed, Mar 4, 2020 at 9:54 AM Fritz Koenig <frkoenig@google.com> wrote:
>
> Thanks for putting this patch set together.  I'm currently trying to get
> modifiers working for UBWC.  I've applied these patches and with a little
> tweaking can get video decode working without modifiers.  I haven't tried the
> new ioctls yet.
> > From: Hans Verkuil <hans.verkuil@cisco.com>
> >
> > 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.
> >
> > The core takes care of converting new ioctls requests to old ones
> > if the driver does not support the new hooks, and vice versa.
> >
> > Note that the timecode field is gone, since there doesn't seem to be
> > in-kernel users, but can be added back in the reserved area if needed.
> >
> > Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > ---
> > 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
> > ---
> >  drivers/media/v4l2-core/v4l2-dev.c   |  30 +-
> >  drivers/media/v4l2-core/v4l2-ioctl.c | 428 +++++++++++++++++++++++++--
> >  include/media/v4l2-ioctl.h           |  30 ++
> >  include/uapi/linux/videodev2.h       | 130 ++++++++
> >  4 files changed, 591 insertions(+), 27 deletions(-)
> >
> > diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
> > index 9aad715537b3..35c8caccd025 100644
> > --- a/drivers/media/v4l2-core/v4l2-dev.c
> > +++ b/drivers/media/v4l2-core/v4l2-dev.c
> > @@ -696,12 +696,30 @@ static void determine_valid_ioctls(struct video_device *vdev)
> >       if (is_vid || is_vbi || is_sdr || is_tch) {
> >               /* ioctls valid for video, metadata, vbi or sdr */
> >               SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
> > -             SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
> > -             SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
> > -             SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf);
> > -             SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
> > -             SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
> > -             SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
> > +             if (ops->vidioc_querybuf || ops->vidioc_ext_querybuf) {
> > +                     set_bit(_IOC_NR(VIDIOC_QUERYBUF), valid_ioctls);
> > +                     set_bit(_IOC_NR(VIDIOC_EXT_QUERYBUF), valid_ioctls);
> > +             }
> > +             if (ops->vidioc_qbuf || ops->vidioc_ext_qbuf) {
> > +                     set_bit(_IOC_NR(VIDIOC_QBUF), valid_ioctls);
> > +                     set_bit(_IOC_NR(VIDIOC_EXT_QBUF), valid_ioctls);
> > +             }
> > +             if (ops->vidioc_expbuf || ops->vidioc_ext_expbuf) {
> > +                     set_bit(_IOC_NR(VIDIOC_EXPBUF), valid_ioctls);
> > +                     set_bit(_IOC_NR(VIDIOC_EXT_EXPBUF), valid_ioctls);
> > +             }
> > +             if (ops->vidioc_dqbuf || ops->vidioc_ext_dqbuf) {
> > +                     set_bit(_IOC_NR(VIDIOC_DQBUF), valid_ioctls);
> > +                     set_bit(_IOC_NR(VIDIOC_EXT_DQBUF), valid_ioctls);
> > +             }
> > +             if (ops->vidioc_create_bufs || ops->vidioc_ext_create_bufs) {
> > +                     set_bit(_IOC_NR(VIDIOC_CREATE_BUFS), valid_ioctls);
> > +                     set_bit(_IOC_NR(VIDIOC_EXT_CREATE_BUFS), valid_ioctls);
> > +             }
> > +             if (ops->vidioc_prepare_buf || ops->vidioc_ext_prepare_buf) {
> > +                     set_bit(_IOC_NR(VIDIOC_PREPARE_BUF), valid_ioctls);
> > +                     set_bit(_IOC_NR(VIDIOC_EXT_PREPARE_BUF), valid_ioctls);
> > +             }
> >               SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
> >               SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
> >       }
> > diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> > index 78e14c1dc76f..356218e44ccb 100644
> > --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> > @@ -579,6 +579,25 @@ 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 *p = arg;
> > +     const struct v4l2_ext_plane *plane;
> > +     int i;
> > +
> > +     pr_cont("%lld index=%d, type=%s, flags=0x%08x, field=%s, sequence=%d, memory=%s\n",
> > +             p->timestamp, p->index, prt_names(p->type, v4l2_type_names),
> > +             p->flags, prt_names(p->field, v4l2_field_names),
> > +             p->sequence, prt_names(p->memory, v4l2_memory_names));
> > +
> > +     for (i = 0; i < p->num_planes; ++i) {
> > +             plane = &p->planes[i];
> > +             pr_debug("plane %d: bytesused=%d, data_offset=0x%08x, offset/userptr=0x%llx, length=%d\n",
> > +                      i, plane->bytesused, plane->data_offset,
> > +                      plane->m.userptr, plane->length);
> > +     }
> > +}
> > +
> >  static void v4l_print_exportbuffer(const void *arg, bool write_only)
> >  {
> >       const struct v4l2_exportbuffer *p = arg;
> > @@ -588,6 +607,18 @@ static void v4l_print_exportbuffer(const void *arg, bool write_only)
> >               p->index, p->plane, p->flags);
> >  }
> >
> > +static void v4l_print_ext_exportbuffer(const void *arg, bool write_only)
> > +{
> > +     const struct v4l2_ext_exportbuffer *p = arg;
> > +     unsigned int i;
> > +
> > +     pr_cont("type=%s, index=%u, first_plane=%u num_planes=%u, flags=%08x\n",
> > +             prt_names(p->type, v4l2_type_names), p->index, p->first_plane,
> > +             p->num_planes, p->flags);
> > +     for (i = p->first_plane; i < p->first_plane + p->num_planes; ++i)
> > +             pr_debug("plane %u: fd=%d\n", i, p->fds[i]);
> > +}
> > +
> >  static void v4l_print_create_buffers(const void *arg, bool write_only)
> >  {
> >       const struct v4l2_create_buffers *p = arg;
> > @@ -598,6 +629,15 @@ static void v4l_print_create_buffers(const void *arg, bool write_only)
> >       v4l_print_format(&p->format, write_only);
> >  }
> >
> > +static void v4l_print_ext_create_buffers(const void *arg, bool write_only)
> > +{
> > +     const struct v4l2_ext_create_buffers *p = arg;
> > +
> > +     pr_cont("index=%d, count=%d, memory=%s, ", p->index, p->count,
> > +             prt_names(p->memory, v4l2_memory_names));
> > +     v4l_print_ext_format(&p->format, write_only);
> > +}
> > +
> >  static void v4l_print_streamparm(const void *arg, bool write_only)
> >  {
> >       const struct v4l2_streamparm *p = arg;
> > @@ -1319,6 +1359,123 @@ int v4l2_format_to_ext_format(const struct v4l2_format *f,
> >  }
> >  EXPORT_SYMBOL_GPL(v4l2_format_to_ext_format);
> >
> > +int v4l2_ext_buffer_to_buffer(const struct v4l2_ext_buffer *e,
> > +                           struct v4l2_buffer *b, bool mplane_cap)
> > +{
> > +     u64 nsecs;
> > +
> > +     if (!mplane_cap && e->num_planes > 1)
> > +             return -EINVAL;
> > +
> > +     memset(b, 0, sizeof(*b));
>
> b->planes needs to be preserved.
>
> I was not able to find a tree that these patches applied to cleanly.  In v5.4
> the struct v4l2_buffer member planes is a raw pointer, so this memset will
> clear it out.
>
> > +
> > +     b->index = e->index;
> > +     b->flags = e->flags;
> > +     b->field = e->field;
> > +     b->sequence = e->sequence;
> > +     b->memory = e->memory;
> > +     b->request_fd = e->request_fd;
> > +     b->timestamp.tv_sec = div64_u64_rem(e->timestamp, NSEC_PER_SEC, &nsecs);
> > +     b->timestamp.tv_usec = (u32)nsecs / NSEC_PER_USEC;
> > +     if (mplane_cap) {
> > +             unsigned int i;
> > +
> > +             if (e->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> > +                     b->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> > +             else
> > +                     b->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
> > +
> > +             b->length = e->num_planes;
> > +             for (i = 0; i < e->num_planes; i++) {
> > +                     if (b->memory == V4L2_MEMORY_DMABUF) {
> > +                             if (e->planes[i].m.dmabuf.offset)
> > +                                     return -EINVAL;
> > +
> > +                             b->m.planes[i].m.fd = e->planes[i].m.dmabuf.fd;
> > +                     } else {
> > +                             b->m.planes[i].m.userptr = e->planes[i].m.userptr;
> > +                     }
> > +                     b->m.planes[i].length = e->planes[i].length;
> > +                     b->m.planes[i].bytesused = e->planes[i].bytesused;
> > +                     b->m.planes[i].data_offset = e->planes[i].data_offset;
> > +                     memset(b->m.planes[i].reserved, 0,
> > +                            sizeof(b->m.planes[i].reserved));
> > +             }
> > +     } else {
> > +             b->type = e->type;
> > +             b->bytesused = e->planes[0].bytesused;
> > +             b->length = e->planes[0].length;
> > +             if (b->memory == V4L2_MEMORY_DMABUF) {
> > +                     if (e->planes[0].m.dmabuf.offset)
> > +                             return -EINVAL;
> > +
> > +                     b->m.fd = e->planes[0].m.dmabuf.fd;
> > +             } else {
> > +                     b->m.userptr = e->planes[0].m.userptr;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(v4l2_ext_buffer_to_buffer);
> > +
> > +int v4l2_buffer_to_ext_buffer(const struct v4l2_buffer *b,
> > +                           struct v4l2_ext_buffer *e)
> > +{
> > +     memset(e, 0, sizeof(*e));
> > +
> > +     e->index = b->index;
> > +     e->flags = b->flags;
> > +     e->field = b->field;
> > +     e->sequence = b->sequence;
> > +     e->memory = b->memory;
> > +     e->request_fd = b->request_fd;
> > +     e->timestamp = b->timestamp.tv_sec * NSEC_PER_SEC +
> > +             b->timestamp.tv_usec * NSEC_PER_USEC;
> > +     if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
> > +             unsigned int i;
> > +
> > +             if (!b->m.planes)
> > +                     return -EINVAL;
> > +
> > +             if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> > +                     e->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > +             else
> > +                     e->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
> > +
> > +             e->num_planes = b->length;
> > +             for (i = 0; i < e->num_planes; i++) {
> > +                     if (b->memory == V4L2_MEMORY_DMABUF) {
> > +                             e->planes[i].m.dmabuf.fd = b->m.planes[i].m.fd;
> > +                             e->planes[i].m.dmabuf.offset = 0;
> > +                     } else {
> > +                             e->planes[i].m.userptr = b->m.planes[i].m.userptr;
> > +                     }
> > +                     e->planes[i].length = b->m.planes[i].length;
> > +                     e->planes[i].bytesused = b->m.planes[i].bytesused;
> > +                     e->planes[i].data_offset = b->m.planes[i].data_offset;
> > +                     memset(e->planes[i].reserved, 0,
> > +                            sizeof(e->planes[i].reserved));
> > +             }
> > +     } else {
> > +             e->type = b->type;
> > +             e->num_planes = 1;
> > +             e->planes[0].bytesused = b->bytesused;
> > +             e->planes[0].length = b->length;
> > +             if (b->memory == V4L2_MEMORY_DMABUF) {
> > +                     e->planes[0].m.dmabuf.fd = b->m.fd;
> > +                     e->planes[0].m.dmabuf.offset = 0;
> > +             } else {
> > +                     e->planes[0].m.userptr = b->m.userptr;
> > +             }
> > +             e->planes[0].m.userptr = b->m.userptr;
> > +             e->planes[0].data_offset = 0;
> > +     }
> > +
> > +     return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(v4l2_buffer_to_ext_buffer);
> > +
> >  static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
> >                               struct file *file, void *fh, void *arg)
> >  {
> > @@ -2506,31 +2663,109 @@ static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
> >       return ops->vidioc_reqbufs(file, fh, p);
> >  }
> >
> > -static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
> > -                             struct file *file, void *fh, void *arg)
> > +static int v4l_do_buf_op(int (*op)(struct file *, void *,
> > +                                struct v4l2_buffer *),
> > +                      int (*ext_op)(struct file *, void *,
> > +                                    struct v4l2_ext_buffer *),
> > +                      struct file *file, void *fh, struct v4l2_buffer *b)
> >  {
> > -     struct v4l2_buffer *p = arg;
> > -     int ret = check_fmt(file, p->type);
> > +     struct v4l2_ext_buffer eb;
> > +     int ret;
> >
> > -     return ret ? ret : ops->vidioc_querybuf(file, fh, p);
> > +     ret = check_fmt(file, b->type);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (op)
> > +             return op(file, fh, b);
> > +
> > +     ret = v4l2_buffer_to_ext_buffer(b, &eb);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = ext_op(file, fh, &eb);
> > +     if (ret)
> > +             return ret;
> > +
> > +     v4l2_ext_buffer_to_buffer(&eb, b, V4L2_TYPE_IS_MULTIPLANAR(b->type));
> > +     return 0;
> > +}
> > +
> > +static int v4l_do_ext_buf_op(int (*op)(struct file *, void *,
> > +                                    struct v4l2_buffer *),
> > +                          int (*ext_op)(struct file *, void *,
> > +                                        struct v4l2_ext_buffer *),
> > +                          struct file *file, void *fh,
> > +                          struct v4l2_ext_buffer *eb)
> > +{
> > +     struct video_device *vdev = video_devdata(file);
> > +     struct v4l2_buffer b;
> > +     bool mplane_cap;
> > +     int ret;
> > +
> > +     ret = check_fmt(file, eb->type);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (ext_op)
> > +             return ext_op(file, fh, eb);
> > +
> > +     mplane_cap = !!(vdev->device_caps &
> > +                     (V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> > +                      V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> > +                      V4L2_CAP_VIDEO_M2M_MPLANE));
> > +     ret = v4l2_ext_buffer_to_buffer(eb, &b, mplane_cap);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = op(file, fh, &b);
> > +     if (ret)
> > +             return ret;
> > +
> > +     v4l2_buffer_to_ext_buffer(&b, eb);
> > +     return 0;
> > +}
> > +
> > +static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
> > +                     struct file *file, void *fh, void *arg)
> > +{
> > +     return v4l_do_buf_op(ops->vidioc_querybuf, ops->vidioc_ext_querybuf,
> > +                          file, fh, arg);
> > +}
> > +

VIDIOC_QUERYBUF has a problem now.
v4l_do_buf_op macro expands into:
v4l2_buffer_to_ext_buffer
op
v4l2_ext_buffer_to_buffer

To copy to an v4l2_ext_buffer, type is changed by
v4l2_buffer_to_ext_buffer from V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE to
V4L2_BUF_TYPE_VIDEO_CAPTURE.

When __fill_v4l2_buffer is called from vb2_core_querybuf, type is set
back to the V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE.

Finally when v4l2_ext_buffer_to_buffer is called the queue ends up
getting set to V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE.

The reason I see this is that my OUTPUT queue is mm and my CAPTURE
queue is dmabuf.  When I try to then VIDIOC_QBUF that buffer, the
memory types for the queue and the buffer do not match.


> > +static int v4l_ext_querybuf(const struct v4l2_ioctl_ops *ops,
> > +                         struct file *file, void *fh, void *arg)
> > +{
> > +     return v4l_do_ext_buf_op(ops->vidioc_querybuf,
> > +                              ops->vidioc_ext_querybuf, file, fh, arg);
> >  }
> >
> >  static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
> > -                             struct file *file, void *fh, void *arg)
> > +                 struct file *file, void *fh, void *arg)
> >  {
> > -     struct v4l2_buffer *p = arg;
> > -     int ret = check_fmt(file, p->type);
> > +     return v4l_do_buf_op(ops->vidioc_qbuf, ops->vidioc_ext_qbuf,
> > +                          file, fh, arg);
> > +}
> >
> > -     return ret ? ret : ops->vidioc_qbuf(file, fh, p);
> > +static int v4l_ext_qbuf(const struct v4l2_ioctl_ops *ops,
> > +                     struct file *file, void *fh, void *arg)
> > +{
> > +     return v4l_do_ext_buf_op(ops->vidioc_qbuf, ops->vidioc_ext_qbuf,
> > +                              file, fh, arg);
> >  }
> >
> >  static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
> > -                             struct file *file, void *fh, void *arg)
> > +                  struct file *file, void *fh, void *arg)
> >  {
> > -     struct v4l2_buffer *p = arg;
> > -     int ret = check_fmt(file, p->type);
> > +     return v4l_do_buf_op(ops->vidioc_dqbuf, ops->vidioc_ext_dqbuf,
> > +                          file, fh, arg);
> > +}
> >
> > -     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)
> > +{
> > +     return v4l_do_ext_buf_op(ops->vidioc_dqbuf, ops->vidioc_ext_dqbuf,
> > +                              file, fh, arg);
> >  }
> >
> >  static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
> > @@ -2546,7 +2781,27 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
> >
> >       v4l_sanitize_format(&create->format);
> >
> > -     ret = ops->vidioc_create_bufs(file, fh, create);
> > +     if (ops->vidioc_create_bufs) {
> > +             ret = ops->vidioc_create_bufs(file, fh, create);
> > +     } else {
> > +             struct v4l2_ext_create_buffers ecreate = {
> > +                     .count = create->count,
> > +                     .memory = create->memory,
> > +             };
> > +
> > +             ret = v4l2_format_to_ext_format(&create->format,
> > +                                             &ecreate.format, true);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             ret = ops->vidioc_ext_create_bufs(file, fh, &ecreate);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             create->index = ecreate.index;
> > +             create->count = ecreate.count;
> > +             create->capabilities = ecreate.capabilities;
> > +     }
> >
> >       if (create->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
> >           create->format.type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
> > @@ -2555,13 +2810,59 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
> >       return ret;
> >  }
> >
> > -static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
> > -                             struct file *file, void *fh, void *arg)
> > +static int v4l_ext_create_bufs(const struct v4l2_ioctl_ops *ops,
> > +                            struct file *file, void *fh, void *arg)
> >  {
> > -     struct v4l2_buffer *b = arg;
> > -     int ret = check_fmt(file, b->type);
> > +     struct v4l2_ext_create_buffers *ecreate = arg;
> > +     struct video_device *vdev = video_devdata(file);
> > +     struct v4l2_create_buffers create = {
> > +             .count = ecreate->count,
> > +             .memory = ecreate->memory,
> > +     };
> > +     bool mplane_cap;
> > +     int ret;
> >
> > -     return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
> > +     ret = check_fmt(file, ecreate->format.type);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (ops->vidioc_ext_create_bufs)
> > +             return ops->vidioc_ext_create_bufs(file, fh, ecreate);
> > +
> > +     mplane_cap = !!(vdev->device_caps &
> > +                     (V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> > +                      V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> > +                      V4L2_CAP_VIDEO_M2M_MPLANE));
> > +     ret = v4l2_ext_format_to_format(&ecreate->format,
> > +                                     &create.format, mplane_cap, true);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = v4l_create_bufs(ops, file, fh, &create);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ecreate->index = create.index;
> > +     ecreate->count = create.count;
> > +     ecreate->capabilities = create.capabilities;
> > +
> > +     return 0;
> > +}
> > +
> > +static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
> > +                        struct file *file, void *fh, void *arg)
> > +{
> > +     return v4l_do_buf_op(ops->vidioc_prepare_buf,
> > +                          ops->vidioc_ext_prepare_buf,
> > +                          file, fh, arg);
> > +}
> > +
> > +static int v4l_ext_prepare_buf(const struct v4l2_ioctl_ops *ops,
> > +                            struct file *file, void *fh, void *arg)
> > +{
> > +     return v4l_do_ext_buf_op(ops->vidioc_prepare_buf,
> > +                              ops->vidioc_ext_prepare_buf,
> > +                              file, fh, arg);
> >  }
> >
> >  static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
> > @@ -3159,6 +3460,86 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
> >       return -ENOTTY;
> >  }
> >
> > +static int v4l_expbuf(const struct v4l2_ioctl_ops *ops, struct file *file,
> > +                   void *fh, void *arg)
> > +{
> > +     struct v4l2_exportbuffer *b = arg;
> > +     struct v4l2_ext_exportbuffer eb = {
> > +             .type = b->type,
> > +             .index = b->index,
> > +             .first_plane = b->plane,
> > +             .num_planes = 1,
> > +             .flags = b->flags,
> > +     };
> > +     int ret;
> > +
> > +     if (ops->vidioc_expbuf)
> > +             return ops->vidioc_expbuf(file, fh, b);
> > +
> > +     if (b->plane >= VIDEO_MAX_PLANES)
> > +             return -EINVAL;
> > +
> > +     ret = ops->vidioc_ext_expbuf(file, fh, &eb);
> > +     if (ret)
> > +             return ret;
> > +
> > +     b->fd = eb.fds[b->plane];
> > +     return 0;
> > +}
> > +
> > +static int v4l_ext_expbuf(const struct v4l2_ioctl_ops *ops,
> > +                       struct file *file, void *fh, void *arg)
> > +{
> > +     struct v4l2_ext_exportbuffer *eb = arg;
> > +     unsigned int i;
> > +     int ret;
> > +
> > +     if (eb->first_plane >= VIDEO_MAX_PLANES ||
> > +         eb->num_planes > VIDEO_MAX_PLANES ||
> > +         eb->first_plane + eb->num_planes > VIDEO_MAX_PLANES)
> > +             return -EINVAL;
> > +
> > +     if (ops->vidioc_ext_expbuf)
> > +             return ops->vidioc_ext_expbuf(file, fh, eb);
> > +
> > +     for (i = eb->first_plane; i < eb->first_plane + eb->num_planes; i++) {
> > +             struct v4l2_exportbuffer b = {
> > +                     .type = eb->type,
> > +                     .index = eb->index,
> > +                     .plane = i,
> > +                     .flags = eb->flags,
> > +             };
> > +
> > +             ret = ops->vidioc_expbuf(file, fh, &b);
> > +             if (ret)
> > +                     goto err_put_dmabufs;
> > +
> > +             eb->fds[i] = b.fd;
> > +     }
> > +
> > +     return 0;
> > +
> > +err_put_dmabufs:
> > +     for (i = eb->first_plane; i < eb->first_plane + eb->num_planes; i++) {
> > +             struct dma_buf *dmabuf;
> > +
> > +             if (eb->fds[i] <= 0)
> > +                     break;
> > +
> > +             /*
> > +              * We must call dma_buf_put() twice because we got one
> > +              * reference taken at dmabuf creation time one taken when
> > +              * calling dma_buf_get().
> > +              * FIXME: not entirely sure this works correctly.
> > +              */
> > +             dmabuf = dma_buf_get(eb->fds[i]);
> > +             dma_buf_put(dmabuf);
> > +             dma_buf_put(dmabuf);
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> >  struct v4l2_ioctl_info {
> >       unsigned int ioctl;
> >       u32 flags;
> > @@ -3201,7 +3582,6 @@ struct v4l2_ioctl_info {
> >
> >  DEFINE_V4L_STUB_FUNC(g_fbuf)
> >  DEFINE_V4L_STUB_FUNC(s_fbuf)
> > -DEFINE_V4L_STUB_FUNC(expbuf)
> >  DEFINE_V4L_STUB_FUNC(g_std)
> >  DEFINE_V4L_STUB_FUNC(g_audio)
> >  DEFINE_V4L_STUB_FUNC(s_audio)
> > @@ -3237,12 +3617,16 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
> >       IOCTL_INFO(VIDIOC_S_EXT_FMT, v4l_s_ext_fmt, v4l_print_ext_format, INFO_FL_PRIO),
> >       IOCTL_INFO(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
> >       IOCTL_INFO(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
> > +     IOCTL_INFO(VIDIOC_EXT_QUERYBUF, v4l_ext_querybuf, v4l_print_ext_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_ext_buffer, num_planes)),
> >       IOCTL_INFO(VIDIOC_G_FBUF, v4l_stub_g_fbuf, v4l_print_framebuffer, 0),
> >       IOCTL_INFO(VIDIOC_S_FBUF, v4l_stub_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
> >       IOCTL_INFO(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
> >       IOCTL_INFO(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
> > -     IOCTL_INFO(VIDIOC_EXPBUF, v4l_stub_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
> > +     IOCTL_INFO(VIDIOC_EXT_QBUF, v4l_ext_qbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
> > +     IOCTL_INFO(VIDIOC_EXPBUF, v4l_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
> > +     IOCTL_INFO(VIDIOC_EXT_EXPBUF, v4l_ext_expbuf, v4l_print_ext_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
> >       IOCTL_INFO(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
> > +     IOCTL_INFO(VIDIOC_EXT_DQBUF, v4l_ext_dqbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
> >       IOCTL_INFO(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
> >       IOCTL_INFO(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
> >       IOCTL_INFO(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
> > @@ -3307,7 +3691,9 @@ static const struct v4l2_ioctl_info v4l2_ioctls[] = {
> >       IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
> >       IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
> >       IOCTL_INFO(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
> > +     IOCTL_INFO(VIDIOC_EXT_CREATE_BUFS, v4l_ext_create_bufs, v4l_print_ext_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
> >       IOCTL_INFO(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
> > +     IOCTL_INFO(VIDIOC_EXT_PREPARE_BUF, v4l_ext_prepare_buf, v4l_print_ext_buffer, INFO_FL_QUEUE),
> >       IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, v4l_stub_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)),
> >       IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, v4l_stub_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY),
> >       IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, v4l_stub_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, pad)),
> > diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
> > index 39ac07fbc7b7..f7e375d38602 100644
> > --- a/include/media/v4l2-ioctl.h
> > +++ b/include/media/v4l2-ioctl.h
> > @@ -168,16 +168,28 @@ struct v4l2_fh;
> >   *   :ref:`VIDIOC_REQBUFS <vidioc_reqbufs>` ioctl
> >   * @vidioc_querybuf: pointer to the function that implements
> >   *   :ref:`VIDIOC_QUERYBUF <vidioc_querybuf>` ioctl
> > + * @vidioc_ext_querybuf: pointer to the function that implements
> > + *   :ref:`VIDIOC_EXT_QUERYBUF <vidioc_ext_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_ext_expbuf: pointer to the function that implements
> > + *   :ref:`VIDIOC_EXT_EXPBUF <vidioc_ext_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_ext_create_bufs: pointer to the function that implements
> > + *   :ref:`VIDIOC_EXT_CREATE_BUFS <vidioc_ext_create_bufs>` ioctl
> >   * @vidioc_prepare_buf: pointer to the function that implements
> >   *   :ref:`VIDIOC_PREPARE_BUF <vidioc_prepare_buf>` ioctl
> > + * @vidioc_ext_prepare_buf: pointer to the function that implements
> > + *   :ref:`VIDIOC_EXT_PREPARE_BUF <vidioc_ext_prepare_buf>` ioctl
> >   * @vidioc_overlay: pointer to the function that implements
> >   *   :ref:`VIDIOC_OVERLAY <vidioc_overlay>` ioctl
> >   * @vidioc_g_fbuf: pointer to the function that implements
> > @@ -438,17 +450,29 @@ struct v4l2_ioctl_ops {
> >                             struct v4l2_requestbuffers *b);
> >       int (*vidioc_querybuf)(struct file *file, void *fh,
> >                              struct v4l2_buffer *b);
> > +     int (*vidioc_ext_querybuf)(struct file *file, void *fh,
> > +                                struct v4l2_ext_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_ext_expbuf)(struct file *file, void *fh,
> > +                              struct v4l2_ext_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);
> > +     int (*vidioc_ext_create_bufs)(struct file *file, void *fh,
> > +                                   struct v4l2_ext_create_buffers *b);
> >       int (*vidioc_prepare_buf)(struct file *file, void *fh,
> >                                 struct v4l2_buffer *b);
> > +     int (*vidioc_ext_prepare_buf)(struct file *file, void *fh,
> > +                                   struct v4l2_ext_buffer *b);
> >
> >       int (*vidioc_overlay)(struct file *file, void *fh, unsigned int i);
> >       int (*vidioc_g_fbuf)(struct file *file, void *fh,
> > @@ -757,4 +781,10 @@ int v4l2_ext_format_to_format(const struct v4l2_ext_format *e,
> >                             struct v4l2_format *f,
> >                             bool mplane_cap, bool strict);
> >
> > +int v4l2_ext_buffer_to_buffer(const struct v4l2_ext_buffer *e,
> > +                           struct v4l2_buffer *b,
> > +                           bool mplane_cap);
> > +int v4l2_buffer_to_ext_buffer(const struct v4l2_buffer *b,
> > +                           struct v4l2_ext_buffer *e);
> > +
> >  #endif /* _V4L2_IOCTL_H */
> > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> > index c7b169de1c7b..33c8348df13f 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -953,6 +953,49 @@ struct v4l2_plane {
> >       __u32                   reserved[11];
> >  };
> >
> > +/**
> > + * struct v4l2_ext_plane - extended plane buffer info
> > + * @bytesused: number of bytes occupied by data in the plane (payload)
> > + * @length: size of this plane (NOT the payload) in bytes
> > + * @mem_offset: when memory in the associated struct v4l2_ext_buffer is
> > + *           V4L2_MEMORY_MMAP, equals the offset from the start of the
> > + *           device memory for this plane (or is 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
> > + * @dmabuf.offset: where the plane starts inside the DMABUF buffer. All planes
> > + *              might share the same buffer object. In this case we need to
> > + *              know where the plane start inside this buffer.
> > + * @data_offset: offset in the plane to the start of data; usually 0, unless
> > + *            there is a header in front of the data. data_offset is
> > + *            relative to start_offset, so absolute data_offset is actually
> > + *            start_offset + data_offset
> > + *
> > + *
> > + * Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer
> > + * with two planes can have one plane for Y, and another for interleaved CbCr
> > + * components. Each plane can reside in a separate memory buffer, or even in
> > + * a completely separate memory node (e.g. in embedded devices).
> > + * Note that this struct is also used for uni-planar buffers, but in that case
> > + * you'll only have one plane defined.
> > + */
> > +struct v4l2_ext_plane {
> > +     __u32 bytesused;
> > +     __u32 length;
> > +     union {
> > +             __u32 mem_offset;
> > +             __u64 userptr;
> > +             struct {
> > +                     __s32 fd;
> > +                     __u32 offset;
> > +             } dmabuf;
> > +     } m;
> > +     __u32 data_offset;
> > +     __u32 reserved[10];
> > +};
> > +
> >  /**
> >   * struct v4l2_buffer - video buffer info
> >   * @index:   id number of the buffer
> > @@ -1010,6 +1053,40 @@ struct v4l2_buffer {
> >       };
> >  };
> >
> > +/**
> > + * struct v4l2_ext_buffer - extended video buffer info
> > + * @index: id number of the buffer
> > + * @type: enum v4l2_buf_type; buffer type. _MPLANE and _OVERLAY formats are
> > + *     invalid
> > + * @flags: buffer informational flags
> > + * @field: enum v4l2_field; field order of the image in the buffer
> > + * @timestamp: frame timestamp
> > + * @sequence: sequence count of this frame
> > + * @memory: enum v4l2_memory; the method, in which the actual video data is
> > + *          passed
> > + * @planes: per-plane buffer information
> > + * @num_planes: number of plane buffers
> > + * @request_fd: fd of the request that this buffer should use
> > + * @reserved: some extra space reserved to add future fields (like timecode).
> > + *         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 flags;
> > +     __u32 field;
> > +     __u64 timestamp;
> > +     __u32 sequence;
> > +     __u32 memory;
> > +     struct v4l2_ext_plane planes[VIDEO_MAX_PLANES];
> > +     __u32 num_planes;
> > +     __u32 request_fd;
> > +     __u32 reserved[10];
> > +};
> > +
> >  /**
> >   * v4l2_timeval_to_ns - Convert timeval to nanoseconds
> >   * @ts:              pointer to the timeval variable to be converted
> > @@ -1087,6 +1164,35 @@ struct v4l2_exportbuffer {
> >       __u32           reserved[11];
> >  };
> >
> > +/**
> > + * struct v4l2_ext_exportbuffer - export of video buffer as DMABUF file
> > + *                             descriptor using extended format
> > + *
> > + * @index: id number of the buffer
> > + * @type: enum v4l2_buf_type; buffer type
> > + * @flags: flags for newly created file(s), currently only O_CLOEXEC is
> > + *      supported, refer to manual of open syscall for more details
> > + * @first_plane: first plane to export. Most likely set to 0
> > + * @num_planes: number of planes to export. Most set to the number of planes
> > + *           attached to the buffer
> > + * @fds: file descriptors associated with DMABUF (set by driver). Note that all
> > + *    planes might share the same buffer and then be returned the same FD
> > + *
> > + * Contains data used for exporting a video buffer as DMABUF file descriptor.
> > + * The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF
> > + * (identical to the cookie used to mmap() the buffer to userspace). All
> > + * reserved fields must be set to zero.
> > + */
> > +struct v4l2_ext_exportbuffer {
> > +     __u32 type; /* enum v4l2_buf_type */
> > +     __u32 index;
> > +     __u32 flags;
> > +     __u32 first_plane;
> > +     __u32 num_planes;
> > +     __s32 fds[VIDEO_MAX_PLANES];
> > +     __u32 reserved;
> > +};
> > +
> >  /*
> >   *   O V E R L A Y   P R E V I E W
> >   */
> > @@ -2483,6 +2589,23 @@ struct v4l2_create_buffers {
> >       __u32                   reserved[7];
> >  };
> >
> > +/**
> > + * struct v4l2_ext_create_buffers - VIDIOC_EXT_CREATE_BUFS argument
> > + * @index:   on return, index of the first created buffer
> > + * @count:   entry: number of requested buffers,
> > + *           return: number of created buffers
> > + * @memory:  enum v4l2_memory; buffer memory type
> > + * @capabilities: capabilities of this buffer type.
> > + * @format:  frame format, for which buffers are requested
> > + */
> > +struct v4l2_ext_create_buffers {
> > +     __u32                   index;
> > +     __u32                   count;
> > +     __u32                   memory;
> > +     __u32                   capabilities;
> > +     struct v4l2_ext_format  format;
> > +};
> > +
> >  /*
> >   *   I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
> >   *
> > @@ -2586,6 +2709,13 @@ struct v4l2_create_buffers {
> >  #define VIDIOC_G_EXT_FMT     _IOWR('V', 104, struct v4l2_ext_format)
> >  #define VIDIOC_S_EXT_FMT     _IOWR('V', 105, struct v4l2_ext_format)
> >  #define VIDIOC_TRY_EXT_FMT   _IOWR('V', 106, struct v4l2_ext_format)
> > +#define VIDIOC_EXT_CREATE_BUFS       _IOWR('V', 107, struct v4l2_ext_create_buffers)
> > +#define VIDIOC_EXT_QUERYBUF  _IOWR('V', 108, struct v4l2_ext_buffer)
> > +#define VIDIOC_EXT_QBUF              _IOWR('V', 109, struct v4l2_ext_buffer)
> > +#define VIDIOC_EXT_DQBUF     _IOWR('V', 110, struct v4l2_ext_buffer)
> > +#define VIDIOC_EXT_PREPARE_BUF       _IOWR('V', 111, struct v4l2_ext_buffer)
> > +#define VIDIOC_EXT_EXPBUF    _IOWR('V', 112, struct v4l2_ext_exportbuffer)
> > +
> >  /* Reminder: when adding new ioctls please add support for them to
> >     drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */
> >
> > --
> > 2.21.0
> >
> >
diff mbox series

Patch

diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 9aad715537b3..35c8caccd025 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -696,12 +696,30 @@  static void determine_valid_ioctls(struct video_device *vdev)
 	if (is_vid || is_vbi || is_sdr || is_tch) {
 		/* ioctls valid for video, metadata, vbi or sdr */
 		SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
-		SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
-		SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
-		SET_VALID_IOCTL(ops, VIDIOC_EXPBUF, vidioc_expbuf);
-		SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
-		SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
-		SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
+		if (ops->vidioc_querybuf || ops->vidioc_ext_querybuf) {
+			set_bit(_IOC_NR(VIDIOC_QUERYBUF), valid_ioctls);
+			set_bit(_IOC_NR(VIDIOC_EXT_QUERYBUF), valid_ioctls);
+		}
+		if (ops->vidioc_qbuf || ops->vidioc_ext_qbuf) {
+			set_bit(_IOC_NR(VIDIOC_QBUF), valid_ioctls);
+			set_bit(_IOC_NR(VIDIOC_EXT_QBUF), valid_ioctls);
+		}
+		if (ops->vidioc_expbuf || ops->vidioc_ext_expbuf) {
+			set_bit(_IOC_NR(VIDIOC_EXPBUF), valid_ioctls);
+			set_bit(_IOC_NR(VIDIOC_EXT_EXPBUF), valid_ioctls);
+		}
+		if (ops->vidioc_dqbuf || ops->vidioc_ext_dqbuf) {
+			set_bit(_IOC_NR(VIDIOC_DQBUF), valid_ioctls);
+			set_bit(_IOC_NR(VIDIOC_EXT_DQBUF), valid_ioctls);
+		}
+		if (ops->vidioc_create_bufs || ops->vidioc_ext_create_bufs) {
+			set_bit(_IOC_NR(VIDIOC_CREATE_BUFS), valid_ioctls);
+			set_bit(_IOC_NR(VIDIOC_EXT_CREATE_BUFS), valid_ioctls);
+		}
+		if (ops->vidioc_prepare_buf || ops->vidioc_ext_prepare_buf) {
+			set_bit(_IOC_NR(VIDIOC_PREPARE_BUF), valid_ioctls);
+			set_bit(_IOC_NR(VIDIOC_EXT_PREPARE_BUF), valid_ioctls);
+		}
 		SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
 		SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
 	}
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 78e14c1dc76f..356218e44ccb 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -579,6 +579,25 @@  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 *p = arg;
+	const struct v4l2_ext_plane *plane;
+	int i;
+
+	pr_cont("%lld index=%d, type=%s, flags=0x%08x, field=%s, sequence=%d, memory=%s\n",
+		p->timestamp, p->index, prt_names(p->type, v4l2_type_names),
+		p->flags, prt_names(p->field, v4l2_field_names),
+		p->sequence, prt_names(p->memory, v4l2_memory_names));
+
+	for (i = 0; i < p->num_planes; ++i) {
+		plane = &p->planes[i];
+		pr_debug("plane %d: bytesused=%d, data_offset=0x%08x, offset/userptr=0x%llx, length=%d\n",
+			 i, plane->bytesused, plane->data_offset,
+			 plane->m.userptr, plane->length);
+	}
+}
+
 static void v4l_print_exportbuffer(const void *arg, bool write_only)
 {
 	const struct v4l2_exportbuffer *p = arg;
@@ -588,6 +607,18 @@  static void v4l_print_exportbuffer(const void *arg, bool write_only)
 		p->index, p->plane, p->flags);
 }
 
+static void v4l_print_ext_exportbuffer(const void *arg, bool write_only)
+{
+	const struct v4l2_ext_exportbuffer *p = arg;
+	unsigned int i;
+
+	pr_cont("type=%s, index=%u, first_plane=%u num_planes=%u, flags=%08x\n",
+		prt_names(p->type, v4l2_type_names), p->index, p->first_plane,
+		p->num_planes, p->flags);
+	for (i = p->first_plane; i < p->first_plane + p->num_planes; ++i)
+		pr_debug("plane %u: fd=%d\n", i, p->fds[i]);
+}
+
 static void v4l_print_create_buffers(const void *arg, bool write_only)
 {
 	const struct v4l2_create_buffers *p = arg;
@@ -598,6 +629,15 @@  static void v4l_print_create_buffers(const void *arg, bool write_only)
 	v4l_print_format(&p->format, write_only);
 }
 
+static void v4l_print_ext_create_buffers(const void *arg, bool write_only)
+{
+	const struct v4l2_ext_create_buffers *p = arg;
+
+	pr_cont("index=%d, count=%d, memory=%s, ", p->index, p->count,
+		prt_names(p->memory, v4l2_memory_names));
+	v4l_print_ext_format(&p->format, write_only);
+}
+
 static void v4l_print_streamparm(const void *arg, bool write_only)
 {
 	const struct v4l2_streamparm *p = arg;
@@ -1319,6 +1359,123 @@  int v4l2_format_to_ext_format(const struct v4l2_format *f,
 }
 EXPORT_SYMBOL_GPL(v4l2_format_to_ext_format);
 
+int v4l2_ext_buffer_to_buffer(const struct v4l2_ext_buffer *e,
+			      struct v4l2_buffer *b, bool mplane_cap)
+{
+	u64 nsecs;
+
+	if (!mplane_cap && e->num_planes > 1)
+		return -EINVAL;
+
+	memset(b, 0, sizeof(*b));
+
+	b->index = e->index;
+	b->flags = e->flags;
+	b->field = e->field;
+	b->sequence = e->sequence;
+	b->memory = e->memory;
+	b->request_fd = e->request_fd;
+	b->timestamp.tv_sec = div64_u64_rem(e->timestamp, NSEC_PER_SEC, &nsecs);
+	b->timestamp.tv_usec = (u32)nsecs / NSEC_PER_USEC;
+	if (mplane_cap) {
+		unsigned int i;
+
+		if (e->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			b->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+		else
+			b->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+		b->length = e->num_planes;
+		for (i = 0; i < e->num_planes; i++) {
+			if (b->memory == V4L2_MEMORY_DMABUF) {
+				if (e->planes[i].m.dmabuf.offset)
+					return -EINVAL;
+
+				b->m.planes[i].m.fd = e->planes[i].m.dmabuf.fd;
+			} else {
+				b->m.planes[i].m.userptr = e->planes[i].m.userptr;
+			}
+			b->m.planes[i].length = e->planes[i].length;
+			b->m.planes[i].bytesused = e->planes[i].bytesused;
+			b->m.planes[i].data_offset = e->planes[i].data_offset;
+			memset(b->m.planes[i].reserved, 0,
+			       sizeof(b->m.planes[i].reserved));
+		}
+	} else {
+		b->type = e->type;
+		b->bytesused = e->planes[0].bytesused;
+		b->length = e->planes[0].length;
+		if (b->memory == V4L2_MEMORY_DMABUF) {
+			if (e->planes[0].m.dmabuf.offset)
+				return -EINVAL;
+
+			b->m.fd = e->planes[0].m.dmabuf.fd;
+		} else {
+			b->m.userptr = e->planes[0].m.userptr;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_ext_buffer_to_buffer);
+
+int v4l2_buffer_to_ext_buffer(const struct v4l2_buffer *b,
+			      struct v4l2_ext_buffer *e)
+{
+	memset(e, 0, sizeof(*e));
+
+	e->index = b->index;
+	e->flags = b->flags;
+	e->field = b->field;
+	e->sequence = b->sequence;
+	e->memory = b->memory;
+	e->request_fd = b->request_fd;
+	e->timestamp = b->timestamp.tv_sec * NSEC_PER_SEC +
+		b->timestamp.tv_usec * NSEC_PER_USEC;
+	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+		unsigned int i;
+
+		if (!b->m.planes)
+			return -EINVAL;
+
+		if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+			e->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		else
+			e->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+		e->num_planes = b->length;
+		for (i = 0; i < e->num_planes; i++) {
+			if (b->memory == V4L2_MEMORY_DMABUF) {
+				e->planes[i].m.dmabuf.fd = b->m.planes[i].m.fd;
+				e->planes[i].m.dmabuf.offset = 0;
+			} else {
+				e->planes[i].m.userptr = b->m.planes[i].m.userptr;
+			}
+			e->planes[i].length = b->m.planes[i].length;
+			e->planes[i].bytesused = b->m.planes[i].bytesused;
+			e->planes[i].data_offset = b->m.planes[i].data_offset;
+			memset(e->planes[i].reserved, 0,
+			       sizeof(e->planes[i].reserved));
+		}
+	} else {
+		e->type = b->type;
+		e->num_planes = 1;
+		e->planes[0].bytesused = b->bytesused;
+		e->planes[0].length = b->length;
+		if (b->memory == V4L2_MEMORY_DMABUF) {
+			e->planes[0].m.dmabuf.fd = b->m.fd;
+			e->planes[0].m.dmabuf.offset = 0;
+		} else {
+			e->planes[0].m.userptr = b->m.userptr;
+		}
+		e->planes[0].m.userptr = b->m.userptr;
+		e->planes[0].data_offset = 0;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_buffer_to_ext_buffer);
+
 static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
@@ -2506,31 +2663,109 @@  static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
 	return ops->vidioc_reqbufs(file, fh, p);
 }
 
-static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
-				struct file *file, void *fh, void *arg)
+static int v4l_do_buf_op(int (*op)(struct file *, void *,
+				   struct v4l2_buffer *),
+			 int (*ext_op)(struct file *, void *,
+				       struct v4l2_ext_buffer *),
+			 struct file *file, void *fh, struct v4l2_buffer *b)
 {
-	struct v4l2_buffer *p = arg;
-	int ret = check_fmt(file, p->type);
+	struct v4l2_ext_buffer eb;
+	int ret;
 
-	return ret ? ret : ops->vidioc_querybuf(file, fh, p);
+	ret = check_fmt(file, b->type);
+	if (ret)
+		return ret;
+
+	if (op)
+		return op(file, fh, b);
+
+	ret = v4l2_buffer_to_ext_buffer(b, &eb);
+	if (ret)
+		return ret;
+
+	ret = ext_op(file, fh, &eb);
+	if (ret)
+		return ret;
+
+	v4l2_ext_buffer_to_buffer(&eb, b, V4L2_TYPE_IS_MULTIPLANAR(b->type));
+	return 0;
+}
+
+static int v4l_do_ext_buf_op(int (*op)(struct file *, void *,
+				       struct v4l2_buffer *),
+			     int (*ext_op)(struct file *, void *,
+					   struct v4l2_ext_buffer *),
+			     struct file *file, void *fh,
+			     struct v4l2_ext_buffer *eb)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct v4l2_buffer b;
+	bool mplane_cap;
+	int ret;
+
+	ret = check_fmt(file, eb->type);
+	if (ret)
+		return ret;
+
+	if (ext_op)
+		return ext_op(file, fh, eb);
+
+	mplane_cap = !!(vdev->device_caps &
+			(V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+			 V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+			 V4L2_CAP_VIDEO_M2M_MPLANE));
+	ret = v4l2_ext_buffer_to_buffer(eb, &b, mplane_cap);
+	if (ret)
+		return ret;
+
+	ret = op(file, fh, &b);
+	if (ret)
+		return ret;
+
+	v4l2_buffer_to_ext_buffer(&b, eb);
+	return 0;
+}
+
+static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
+			struct file *file, void *fh, void *arg)
+{
+	return v4l_do_buf_op(ops->vidioc_querybuf, ops->vidioc_ext_querybuf,
+			     file, fh, arg);
+}
+
+static int v4l_ext_querybuf(const struct v4l2_ioctl_ops *ops,
+			    struct file *file, void *fh, void *arg)
+{
+	return v4l_do_ext_buf_op(ops->vidioc_querybuf,
+				 ops->vidioc_ext_querybuf, file, fh, arg);
 }
 
 static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
-				struct file *file, void *fh, void *arg)
+		    struct file *file, void *fh, void *arg)
 {
-	struct v4l2_buffer *p = arg;
-	int ret = check_fmt(file, p->type);
+	return v4l_do_buf_op(ops->vidioc_qbuf, ops->vidioc_ext_qbuf,
+			     file, fh, arg);
+}
 
-	return ret ? ret : ops->vidioc_qbuf(file, fh, p);
+static int v4l_ext_qbuf(const struct v4l2_ioctl_ops *ops,
+			struct file *file, void *fh, void *arg)
+{
+	return v4l_do_ext_buf_op(ops->vidioc_qbuf, ops->vidioc_ext_qbuf,
+				 file, fh, arg);
 }
 
 static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
-				struct file *file, void *fh, void *arg)
+		     struct file *file, void *fh, void *arg)
 {
-	struct v4l2_buffer *p = arg;
-	int ret = check_fmt(file, p->type);
+	return v4l_do_buf_op(ops->vidioc_dqbuf, ops->vidioc_ext_dqbuf,
+			     file, fh, arg);
+}
 
-	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)
+{
+	return v4l_do_ext_buf_op(ops->vidioc_dqbuf, ops->vidioc_ext_dqbuf,
+				 file, fh, arg);
 }
 
 static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
@@ -2546,7 +2781,27 @@  static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
 
 	v4l_sanitize_format(&create->format);
 
-	ret = ops->vidioc_create_bufs(file, fh, create);
+	if (ops->vidioc_create_bufs) {
+		ret = ops->vidioc_create_bufs(file, fh, create);
+	} else {
+		struct v4l2_ext_create_buffers ecreate = {
+			.count = create->count,
+			.memory = create->memory,
+		};
+
+		ret = v4l2_format_to_ext_format(&create->format,
+						&ecreate.format, true);
+		if (ret)
+			return ret;
+
+		ret = ops->vidioc_ext_create_bufs(file, fh, &ecreate);
+		if (ret)
+			return ret;
+
+		create->index = ecreate.index;
+		create->count = ecreate.count;
+		create->capabilities = ecreate.capabilities;
+	}
 
 	if (create->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
 	    create->format.type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
@@ -2555,13 +2810,59 @@  static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
 	return ret;
 }
 
-static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
-				struct file *file, void *fh, void *arg)
+static int v4l_ext_create_bufs(const struct v4l2_ioctl_ops *ops,
+			       struct file *file, void *fh, void *arg)
 {
-	struct v4l2_buffer *b = arg;
-	int ret = check_fmt(file, b->type);
+	struct v4l2_ext_create_buffers *ecreate = arg;
+	struct video_device *vdev = video_devdata(file);
+	struct v4l2_create_buffers create = {
+		.count = ecreate->count,
+		.memory = ecreate->memory,
+	};
+	bool mplane_cap;
+	int ret;
 
-	return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
+	ret = check_fmt(file, ecreate->format.type);
+	if (ret)
+		return ret;
+
+	if (ops->vidioc_ext_create_bufs)
+		return ops->vidioc_ext_create_bufs(file, fh, ecreate);
+
+	mplane_cap = !!(vdev->device_caps &
+			(V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+			 V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+			 V4L2_CAP_VIDEO_M2M_MPLANE));
+	ret = v4l2_ext_format_to_format(&ecreate->format,
+					&create.format, mplane_cap, true);
+	if (ret)
+		return ret;
+
+	ret = v4l_create_bufs(ops, file, fh, &create);
+	if (ret)
+		return ret;
+
+	ecreate->index = create.index;
+	ecreate->count = create.count;
+	ecreate->capabilities = create.capabilities;
+
+	return 0;
+}
+
+static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
+			   struct file *file, void *fh, void *arg)
+{
+	return v4l_do_buf_op(ops->vidioc_prepare_buf,
+			     ops->vidioc_ext_prepare_buf,
+			     file, fh, arg);
+}
+
+static int v4l_ext_prepare_buf(const struct v4l2_ioctl_ops *ops,
+			       struct file *file, void *fh, void *arg)
+{
+	return v4l_do_ext_buf_op(ops->vidioc_prepare_buf,
+				 ops->vidioc_ext_prepare_buf,
+				 file, fh, arg);
 }
 
 static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
@@ -3159,6 +3460,86 @@  static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
 	return -ENOTTY;
 }
 
+static int v4l_expbuf(const struct v4l2_ioctl_ops *ops, struct file *file,
+		      void *fh, void *arg)
+{
+	struct v4l2_exportbuffer *b = arg;
+	struct v4l2_ext_exportbuffer eb = {
+		.type = b->type,
+		.index = b->index,
+		.first_plane = b->plane,
+		.num_planes = 1,
+		.flags = b->flags,
+	};
+	int ret;
+
+	if (ops->vidioc_expbuf)
+		return ops->vidioc_expbuf(file, fh, b);
+
+	if (b->plane >= VIDEO_MAX_PLANES)
+		return -EINVAL;
+
+	ret = ops->vidioc_ext_expbuf(file, fh, &eb);
+	if (ret)
+		return ret;
+
+	b->fd = eb.fds[b->plane];
+	return 0;
+}
+
+static int v4l_ext_expbuf(const struct v4l2_ioctl_ops *ops,
+			  struct file *file, void *fh, void *arg)
+{
+	struct v4l2_ext_exportbuffer *eb = arg;
+	unsigned int i;
+	int ret;
+
+	if (eb->first_plane >= VIDEO_MAX_PLANES ||
+	    eb->num_planes > VIDEO_MAX_PLANES ||
+	    eb->first_plane + eb->num_planes > VIDEO_MAX_PLANES)
+		return -EINVAL;
+
+	if (ops->vidioc_ext_expbuf)
+		return ops->vidioc_ext_expbuf(file, fh, eb);
+
+	for (i = eb->first_plane; i < eb->first_plane + eb->num_planes; i++) {
+		struct v4l2_exportbuffer b = {
+			.type = eb->type,
+			.index = eb->index,
+			.plane = i,
+			.flags = eb->flags,
+		};
+
+		ret = ops->vidioc_expbuf(file, fh, &b);
+		if (ret)
+			goto err_put_dmabufs;
+
+		eb->fds[i] = b.fd;
+	}
+
+	return 0;
+
+err_put_dmabufs:
+	for (i = eb->first_plane; i < eb->first_plane + eb->num_planes; i++) {
+		struct dma_buf *dmabuf;
+
+		if (eb->fds[i] <= 0)
+			break;
+
+		/*
+		 * We must call dma_buf_put() twice because we got one
+		 * reference taken at dmabuf creation time one taken when
+		 * calling dma_buf_get().
+		 * FIXME: not entirely sure this works correctly.
+		 */
+		dmabuf = dma_buf_get(eb->fds[i]);
+		dma_buf_put(dmabuf);
+		dma_buf_put(dmabuf);
+	}
+
+	return ret;
+}
+
 struct v4l2_ioctl_info {
 	unsigned int ioctl;
 	u32 flags;
@@ -3201,7 +3582,6 @@  struct v4l2_ioctl_info {
 
 DEFINE_V4L_STUB_FUNC(g_fbuf)
 DEFINE_V4L_STUB_FUNC(s_fbuf)
-DEFINE_V4L_STUB_FUNC(expbuf)
 DEFINE_V4L_STUB_FUNC(g_std)
 DEFINE_V4L_STUB_FUNC(g_audio)
 DEFINE_V4L_STUB_FUNC(s_audio)
@@ -3237,12 +3617,16 @@  static const struct v4l2_ioctl_info v4l2_ioctls[] = {
 	IOCTL_INFO(VIDIOC_S_EXT_FMT, v4l_s_ext_fmt, v4l_print_ext_format, INFO_FL_PRIO),
 	IOCTL_INFO(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
 	IOCTL_INFO(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
+	IOCTL_INFO(VIDIOC_EXT_QUERYBUF, v4l_ext_querybuf, v4l_print_ext_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_ext_buffer, num_planes)),
 	IOCTL_INFO(VIDIOC_G_FBUF, v4l_stub_g_fbuf, v4l_print_framebuffer, 0),
 	IOCTL_INFO(VIDIOC_S_FBUF, v4l_stub_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
 	IOCTL_INFO(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
 	IOCTL_INFO(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
-	IOCTL_INFO(VIDIOC_EXPBUF, v4l_stub_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
+	IOCTL_INFO(VIDIOC_EXT_QBUF, v4l_ext_qbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
+	IOCTL_INFO(VIDIOC_EXPBUF, v4l_expbuf, v4l_print_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
+	IOCTL_INFO(VIDIOC_EXT_EXPBUF, v4l_ext_expbuf, v4l_print_ext_exportbuffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_exportbuffer, flags)),
 	IOCTL_INFO(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
+	IOCTL_INFO(VIDIOC_EXT_DQBUF, v4l_ext_dqbuf, v4l_print_ext_buffer, INFO_FL_QUEUE),
 	IOCTL_INFO(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
 	IOCTL_INFO(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
 	IOCTL_INFO(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
@@ -3307,7 +3691,9 @@  static const struct v4l2_ioctl_info v4l2_ioctls[] = {
 	IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
 	IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
 	IOCTL_INFO(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+	IOCTL_INFO(VIDIOC_EXT_CREATE_BUFS, v4l_ext_create_bufs, v4l_print_ext_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
 	IOCTL_INFO(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
+	IOCTL_INFO(VIDIOC_EXT_PREPARE_BUF, v4l_ext_prepare_buf, v4l_print_ext_buffer, INFO_FL_QUEUE),
 	IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, v4l_stub_enum_dv_timings, v4l_print_enum_dv_timings, INFO_FL_CLEAR(v4l2_enum_dv_timings, pad)),
 	IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, v4l_stub_query_dv_timings, v4l_print_dv_timings, INFO_FL_ALWAYS_COPY),
 	IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, v4l_stub_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, pad)),
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index 39ac07fbc7b7..f7e375d38602 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -168,16 +168,28 @@  struct v4l2_fh;
  *	:ref:`VIDIOC_REQBUFS <vidioc_reqbufs>` ioctl
  * @vidioc_querybuf: pointer to the function that implements
  *	:ref:`VIDIOC_QUERYBUF <vidioc_querybuf>` ioctl
+ * @vidioc_ext_querybuf: pointer to the function that implements
+ *	:ref:`VIDIOC_EXT_QUERYBUF <vidioc_ext_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_ext_expbuf: pointer to the function that implements
+ *	:ref:`VIDIOC_EXT_EXPBUF <vidioc_ext_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_ext_create_bufs: pointer to the function that implements
+ *	:ref:`VIDIOC_EXT_CREATE_BUFS <vidioc_ext_create_bufs>` ioctl
  * @vidioc_prepare_buf: pointer to the function that implements
  *	:ref:`VIDIOC_PREPARE_BUF <vidioc_prepare_buf>` ioctl
+ * @vidioc_ext_prepare_buf: pointer to the function that implements
+ *	:ref:`VIDIOC_EXT_PREPARE_BUF <vidioc_ext_prepare_buf>` ioctl
  * @vidioc_overlay: pointer to the function that implements
  *	:ref:`VIDIOC_OVERLAY <vidioc_overlay>` ioctl
  * @vidioc_g_fbuf: pointer to the function that implements
@@ -438,17 +450,29 @@  struct v4l2_ioctl_ops {
 			      struct v4l2_requestbuffers *b);
 	int (*vidioc_querybuf)(struct file *file, void *fh,
 			       struct v4l2_buffer *b);
+	int (*vidioc_ext_querybuf)(struct file *file, void *fh,
+				   struct v4l2_ext_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_ext_expbuf)(struct file *file, void *fh,
+				 struct v4l2_ext_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);
+	int (*vidioc_ext_create_bufs)(struct file *file, void *fh,
+				      struct v4l2_ext_create_buffers *b);
 	int (*vidioc_prepare_buf)(struct file *file, void *fh,
 				  struct v4l2_buffer *b);
+	int (*vidioc_ext_prepare_buf)(struct file *file, void *fh,
+				      struct v4l2_ext_buffer *b);
 
 	int (*vidioc_overlay)(struct file *file, void *fh, unsigned int i);
 	int (*vidioc_g_fbuf)(struct file *file, void *fh,
@@ -757,4 +781,10 @@  int v4l2_ext_format_to_format(const struct v4l2_ext_format *e,
 			      struct v4l2_format *f,
 			      bool mplane_cap, bool strict);
 
+int v4l2_ext_buffer_to_buffer(const struct v4l2_ext_buffer *e,
+			      struct v4l2_buffer *b,
+			      bool mplane_cap);
+int v4l2_buffer_to_ext_buffer(const struct v4l2_buffer *b,
+			      struct v4l2_ext_buffer *e);
+
 #endif /* _V4L2_IOCTL_H */
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index c7b169de1c7b..33c8348df13f 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -953,6 +953,49 @@  struct v4l2_plane {
 	__u32			reserved[11];
 };
 
+/**
+ * struct v4l2_ext_plane - extended plane buffer info
+ * @bytesused: number of bytes occupied by data in the plane (payload)
+ * @length: size of this plane (NOT the payload) in bytes
+ * @mem_offset: when memory in the associated struct v4l2_ext_buffer is
+ *		V4L2_MEMORY_MMAP, equals the offset from the start of the
+ *		device memory for this plane (or is 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
+ * @dmabuf.offset: where the plane starts inside the DMABUF buffer. All planes
+ *		   might share the same buffer object. In this case we need to
+ *		   know where the plane start inside this buffer.
+ * @data_offset: offset in the plane to the start of data; usually 0, unless
+ *		 there is a header in front of the data. data_offset is
+ *		 relative to start_offset, so absolute data_offset is actually
+ *		 start_offset + data_offset
+ *
+ *
+ * Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer
+ * with two planes can have one plane for Y, and another for interleaved CbCr
+ * components. Each plane can reside in a separate memory buffer, or even in
+ * a completely separate memory node (e.g. in embedded devices).
+ * Note that this struct is also used for uni-planar buffers, but in that case
+ * you'll only have one plane defined.
+ */
+struct v4l2_ext_plane {
+	__u32 bytesused;
+	__u32 length;
+	union {
+		__u32 mem_offset;
+		__u64 userptr;
+		struct {
+			__s32 fd;
+			__u32 offset;
+		} dmabuf;
+	} m;
+	__u32 data_offset;
+	__u32 reserved[10];
+};
+
 /**
  * struct v4l2_buffer - video buffer info
  * @index:	id number of the buffer
@@ -1010,6 +1053,40 @@  struct v4l2_buffer {
 	};
 };
 
+/**
+ * struct v4l2_ext_buffer - extended video buffer info
+ * @index: id number of the buffer
+ * @type: enum v4l2_buf_type; buffer type. _MPLANE and _OVERLAY formats are
+ *	  invalid
+ * @flags: buffer informational flags
+ * @field: enum v4l2_field; field order of the image in the buffer
+ * @timestamp: frame timestamp
+ * @sequence: sequence count of this frame
+ * @memory: enum v4l2_memory; the method, in which the actual video data is
+ *          passed
+ * @planes: per-plane buffer information
+ * @num_planes: number of plane buffers
+ * @request_fd: fd of the request that this buffer should use
+ * @reserved: some extra space reserved to add future fields (like timecode).
+ *	      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 flags;
+	__u32 field;
+	__u64 timestamp;
+	__u32 sequence;
+	__u32 memory;
+	struct v4l2_ext_plane planes[VIDEO_MAX_PLANES];
+	__u32 num_planes;
+	__u32 request_fd;
+	__u32 reserved[10];
+};
+
 /**
  * v4l2_timeval_to_ns - Convert timeval to nanoseconds
  * @ts:		pointer to the timeval variable to be converted
@@ -1087,6 +1164,35 @@  struct v4l2_exportbuffer {
 	__u32		reserved[11];
 };
 
+/**
+ * struct v4l2_ext_exportbuffer - export of video buffer as DMABUF file
+ *				  descriptor using extended format
+ *
+ * @index: id number of the buffer
+ * @type: enum v4l2_buf_type; buffer type
+ * @flags: flags for newly created file(s), currently only O_CLOEXEC is
+ *	   supported, refer to manual of open syscall for more details
+ * @first_plane: first plane to export. Most likely set to 0
+ * @num_planes: number of planes to export. Most set to the number of planes
+ *		attached to the buffer
+ * @fds: file descriptors associated with DMABUF (set by driver). Note that all
+ *	 planes might share the same buffer and then be returned the same FD
+ *
+ * Contains data used for exporting a video buffer as DMABUF file descriptor.
+ * The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF
+ * (identical to the cookie used to mmap() the buffer to userspace). All
+ * reserved fields must be set to zero.
+ */
+struct v4l2_ext_exportbuffer {
+	__u32 type; /* enum v4l2_buf_type */
+	__u32 index;
+	__u32 flags;
+	__u32 first_plane;
+	__u32 num_planes;
+	__s32 fds[VIDEO_MAX_PLANES];
+	__u32 reserved;
+};
+
 /*
  *	O V E R L A Y   P R E V I E W
  */
@@ -2483,6 +2589,23 @@  struct v4l2_create_buffers {
 	__u32			reserved[7];
 };
 
+/**
+ * struct v4l2_ext_create_buffers - VIDIOC_EXT_CREATE_BUFS argument
+ * @index:	on return, index of the first created buffer
+ * @count:	entry: number of requested buffers,
+ *		return: number of created buffers
+ * @memory:	enum v4l2_memory; buffer memory type
+ * @capabilities: capabilities of this buffer type.
+ * @format:	frame format, for which buffers are requested
+ */
+struct v4l2_ext_create_buffers {
+	__u32			index;
+	__u32			count;
+	__u32			memory;
+	__u32			capabilities;
+	struct v4l2_ext_format	format;
+};
+
 /*
  *	I O C T L   C O D E S   F O R   V I D E O   D E V I C E S
  *
@@ -2586,6 +2709,13 @@  struct v4l2_create_buffers {
 #define VIDIOC_G_EXT_FMT	_IOWR('V', 104, struct v4l2_ext_format)
 #define VIDIOC_S_EXT_FMT	_IOWR('V', 105, struct v4l2_ext_format)
 #define VIDIOC_TRY_EXT_FMT	_IOWR('V', 106, struct v4l2_ext_format)
+#define VIDIOC_EXT_CREATE_BUFS	_IOWR('V', 107, struct v4l2_ext_create_buffers)
+#define VIDIOC_EXT_QUERYBUF	_IOWR('V', 108, struct v4l2_ext_buffer)
+#define VIDIOC_EXT_QBUF		_IOWR('V', 109, struct v4l2_ext_buffer)
+#define VIDIOC_EXT_DQBUF	_IOWR('V', 110, struct v4l2_ext_buffer)
+#define VIDIOC_EXT_PREPARE_BUF	_IOWR('V', 111, struct v4l2_ext_buffer)
+#define VIDIOC_EXT_EXPBUF	_IOWR('V', 112, struct v4l2_ext_exportbuffer)
+
 /* Reminder: when adding new ioctls please add support for them to
    drivers/media/v4l2-core/v4l2-compat-ioctl32.c as well! */