From: Sakari Ailus <sakari.ailus@iki.fi>
To: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: Linux Media Mailing List <linux-media@vger.kernel.org>,
Hans Verkuil <hverkuil@xs4all.nl>,
Pawel Osciak <pawel@osciak.com>,
Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>,
Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
Mauro Carvalho Chehab <mchehab@infradead.org>
Subject: Re: [PATCH 4/6 v4] V4L: vb2: add support for buffers of different sizes on a single queue
Date: Sat, 06 Aug 2011 21:56:48 +0300 [thread overview]
Message-ID: <4E3D8E70.2010407@iki.fi> (raw)
In-Reply-To: <Pine.LNX.4.64.1108050930450.26715@axis700.grange>
Hi Guennadi,
Thanks for the patch. I have a few comments below.
Guennadi Liakhovetski wrote:
> The two recently added ioctl()s VIDIOC_CREATE_BUFS and VIDIOC_PREPARE_BUF
> allow user-space applications to allocate video buffers of different
> sizes and hand them over to the driver for fast switching between
> different frame formats. This patch adds support for buffers of different
> sizes on the same buffer-queue to vb2.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
> drivers/media/video/videobuf2-core.c | 266 +++++++++++++++++++++++++++++-----
> include/media/videobuf2-core.h | 31 +++--
> 2 files changed, 246 insertions(+), 51 deletions(-)
>
> diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
> index 7045d1f..6e7f9e5 100644
> --- a/drivers/media/video/videobuf2-core.c
> +++ b/drivers/media/video/videobuf2-core.c
> @@ -44,7 +44,7 @@ module_param(debug, int, 0644);
> * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
> */
> static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
> - unsigned int *plane_sizes)
> + const unsigned int *plane_sizes)
> {
> struct vb2_queue *q = vb->vb2_queue;
> void *mem_priv;
> @@ -110,13 +110,22 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
> * __setup_offsets() - setup unique offsets ("cookies") for every plane in
> * every buffer on the queue
> */
> -static void __setup_offsets(struct vb2_queue *q)
> +static void __setup_offsets(struct vb2_queue *q, unsigned int n)
> {
> unsigned int buffer, plane;
> struct vb2_buffer *vb;
> - unsigned long off = 0;
> + unsigned long off;
>
> - for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> + if (q->num_buffers) {
> + struct v4l2_plane *p;
> + vb = q->bufs[q->num_buffers - 1];
> + p = &vb->v4l2_planes[vb->num_planes - 1];
> + off = PAGE_ALIGN(p->m.mem_offset + p->length);
> + } else {
> + off = 0;
> + }
> +
> + for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
> vb = q->bufs[buffer];
> if (!vb)
> continue;
> @@ -142,7 +151,7 @@ static void __setup_offsets(struct vb2_queue *q)
> */
> static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
> unsigned int num_buffers, unsigned int num_planes,
> - unsigned int plane_sizes[])
> + const unsigned int *plane_sizes)
> {
> unsigned int buffer;
> struct vb2_buffer *vb;
> @@ -163,7 +172,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
> vb->state = VB2_BUF_STATE_DEQUEUED;
> vb->vb2_queue = q;
> vb->num_planes = num_planes;
> - vb->v4l2_buf.index = buffer;
> + vb->v4l2_buf.index = q->num_buffers + buffer;
> vb->v4l2_buf.type = q->type;
> vb->v4l2_buf.memory = memory;
>
> @@ -191,15 +200,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
> }
> }
>
> - q->bufs[buffer] = vb;
> + q->bufs[q->num_buffers + buffer] = vb;
> }
>
> - q->num_buffers = buffer;
> -
> - __setup_offsets(q);
> + __setup_offsets(q, buffer);
>
> dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
> - q->num_buffers, num_planes);
> + buffer, num_planes);
>
> return buffer;
> }
> @@ -207,12 +214,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
> /**
> * __vb2_free_mem() - release all video buffer memory for a given queue
> */
> -static void __vb2_free_mem(struct vb2_queue *q)
> +static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
> {
> unsigned int buffer;
> struct vb2_buffer *vb;
>
> - for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
> + ++buffer) {
> vb = q->bufs[buffer];
> if (!vb)
> continue;
> @@ -226,17 +234,18 @@ static void __vb2_free_mem(struct vb2_queue *q)
> }
>
> /**
> - * __vb2_queue_free() - free the queue - video memory and related information
> - * and return the queue to an uninitialized state. Might be called even if the
> - * queue has already been freed.
> + * __vb2_queue_free() - free buffers at the end of the queue - video memory and
> + * related information, if no buffers are left return the queue to an
> + * uninitialized state. Might be called even if the queue has already been freed.
> */
> -static void __vb2_queue_free(struct vb2_queue *q)
> +static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
> {
> unsigned int buffer;
>
> /* Call driver-provided cleanup function for each buffer, if provided */
> if (q->ops->buf_cleanup) {
> - for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
> + ++buffer) {
> if (NULL == q->bufs[buffer])
> continue;
> q->ops->buf_cleanup(q->bufs[buffer]);
> @@ -244,16 +253,18 @@ static void __vb2_queue_free(struct vb2_queue *q)
> }
>
> /* Release video buffer memory */
> - __vb2_free_mem(q);
> + __vb2_free_mem(q, buffers);
>
> /* Free videobuf buffers */
> - for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
> + ++buffer) {
> kfree(q->bufs[buffer]);
> q->bufs[buffer] = NULL;
> }
>
> - q->num_buffers = 0;
> - q->memory = 0;
> + q->num_buffers -= buffers;
> + if (!q->num_buffers)
> + q->memory = 0;
> }
>
> /**
> @@ -454,7 +465,7 @@ static bool __buffers_in_use(struct vb2_queue *q)
> */
> int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> {
> - unsigned int num_buffers, num_planes;
> + unsigned int num_buffers, allocated_buffers, num_planes = 0;
> unsigned int plane_sizes[VIDEO_MAX_PLANES];
> int ret = 0;
>
> @@ -503,7 +514,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> return -EBUSY;
> }
>
> - __vb2_queue_free(q);
> + __vb2_queue_free(q, q->num_buffers);
>
> /*
> * In case of REQBUFS(0) return immediately without calling
> @@ -538,44 +549,166 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> return -ENOMEM;
> }
>
> + allocated_buffers = ret;
> +
> /*
> * Check if driver can handle the allocated number of buffers.
> */
> if (ret < num_buffers) {
> - unsigned int orig_num_buffers;
> + num_buffers = ret;
>
> - orig_num_buffers = num_buffers = ret;
> ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
> plane_sizes, q->alloc_ctx);
> - if (ret)
> - goto free_mem;
>
> - if (orig_num_buffers < num_buffers) {
> + if (!ret && allocated_buffers < num_buffers)
> ret = -ENOMEM;
> - goto free_mem;
> - }
>
> /*
> - * Ok, driver accepted smaller number of buffers.
> + * Either the driver has accepted a smaller number of buffers,
> + * or .queue_setup() returned an error
> */
> - ret = num_buffers;
> + }
> +
> + q->num_buffers = allocated_buffers;
> +
> + if (ret < 0) {
> + __vb2_queue_free(q, allocated_buffers);
> + return ret;
> }
>
> /*
> * Return the number of successfully allocated buffers
> * to the userspace.
> */
> - req->count = ret;
> + req->count = allocated_buffers;
>
> return 0;
> -
> -free_mem:
> - __vb2_queue_free(q);
> - return ret;
> }
> EXPORT_SYMBOL_GPL(vb2_reqbufs);
>
> /**
> + * vb2_create_bufs() - Allocate buffers and any required auxiliary structs
> + * @q: videobuf2 queue
> + * @create: creation parameters, passed from userspace to vidioc_create_bufs
> + * handler in driver
> + *
> + * Should be called from vidioc_create_bufs ioctl handler of a driver.
> + * This function:
> + * 1) verifies parameter sanity
> + * 2) calls the .queue_setup() queue operation
> + * 3) performs any necessary memory allocations
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_create_bufs handler in driver.
> + */
> +int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
> +{
> + unsigned int num_planes = create->num_planes,
> + num_buffers = create->count, allocated_buffers;
> + int ret = 0;
> +
> + if (q->fileio) {
> + dprintk(1, "%s(): file io in progress\n", __func__);
> + return -EBUSY;
> + }
> +
> + if (create->memory != V4L2_MEMORY_MMAP
> + && create->memory != V4L2_MEMORY_USERPTR) {
> + dprintk(1, "%s(): unsupported memory type\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (create->type != q->type) {
> + dprintk(1, "%s(): requested type is incorrect\n", __func__);
> + return -EINVAL;
> + }
> +
> + /*
> + * Make sure all the required memory ops for given memory type
> + * are available.
> + */
> + if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
> + dprintk(1, "%s(): MMAP for current setup unsupported\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (create->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
> + dprintk(1, "%s(): USERPTR for current setup unsupported\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (q->num_buffers == VIDEO_MAX_FRAME) {
> + dprintk(1, "%s(): maximum number of buffers already allocated\n",
> + __func__);
> + return -ENOBUFS;
> + }
> +
> + create->index = q->num_buffers;
> +
> + if (!q->num_buffers) {
> + memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
> + q->memory = create->memory;
> + }
> +
> + /*
> + * Ask the driver, whether the requested number of buffers, planes per
> + * buffer and their sizes are acceptable
> + */
> + ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
> + create->sizes, q->alloc_ctx);
> + if (ret)
> + return ret;
> +
> + /* Finally, allocate buffers and video memory */
> + ret = __vb2_queue_alloc(q, create->memory, num_buffers,
> + num_planes, create->sizes);
> + if (ret < 0) {
> + dprintk(1, "Memory allocation failed with error: %d\n", ret);
> + return ret;
> + }
> +
> + allocated_buffers = ret;
> +
> + /*
> + * Check if driver can handle the so far allocated number of buffers.
> + */
> + if (ret < num_buffers) {
> + num_buffers = ret;
s/ret/allocated_buffers/ might make the above easier to understand.
> +
> + /*
> + * q->num_buffers contains the total number of buffers, that the
> + * queue driver has set up
> + */
> + ret = call_qop(q, queue_setup, q, &num_buffers,
> + &num_planes, create->sizes, q->alloc_ctx);
> +
> + if (!ret && allocated_buffers < num_buffers)
> + ret = -ENOMEM;
Is this really an error? How is the queue_setup op expected to change
num_buffers, and why?
> +
> + /*
> + * Either the driver has accepted a smaller number of buffers,
> + * or .queue_setup() returned an error
> + */
> + }
> +
> + q->num_buffers += allocated_buffers;
> +
> + if (ret < 0) {
> + __vb2_queue_free(q, allocated_buffers);
> + return ret;
> + }
> +
> + /*
> + * Return the number of successfully allocated buffers
> + * to the userspace.
> + */
> + create->count = allocated_buffers;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(vb2_create_bufs);
> +
> +/**
> * vb2_plane_vaddr() - Return a kernel virtual address of a given plane
> * @vb: vb2_buffer to which the plane in question belongs to
> * @plane_no: plane number for which the address is to be returned
> @@ -844,6 +977,61 @@ static int __buf_prepare(struct vb2_buffer *vb, struct v4l2_buffer *b)
> }
>
> /**
> + * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
> + * @q: videobuf2 queue
> + * @b: buffer structure passed from userspace to vidioc_prepare_buf
> + * handler in driver
> + *
> + * Should be called from vidioc_prepare_buf ioctl handler of a driver.
> + * This function:
> + * 1) verifies the passed buffer,
> + * 2) calls buf_prepare callback in the driver (if provided), in which
> + * driver-specific buffer initialization can be performed,
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_prepare_buf handler in driver.
> + */
> +int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
> +{
> + struct vb2_buffer *vb;
> +
> + if (q->fileio) {
> + dprintk(1, "%s(): file io in progress\n", __func__);
> + return -EBUSY;
> + }
> +
> + if (b->type != q->type) {
> + dprintk(1, "%s(): invalid buffer type\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (b->index >= q->num_buffers) {
> + dprintk(1, "%s(): buffer index out of range\n", __func__);
> + return -EINVAL;
> + }
> +
> + vb = q->bufs[b->index];
> + if (NULL == vb) {
> + /* Should never happen */
> + dprintk(1, "%s(): buffer is NULL\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (b->memory != q->memory) {
> + dprintk(1, "%s(): invalid memory type\n", __func__);
> + return -EINVAL;
> + }
> +
> + if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> + dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state);
> + return -EINVAL;
> + }
> +
> + return __buf_prepare(vb, b);
> +}
> +EXPORT_SYMBOL_GPL(vb2_prepare_buf);
> +
> +/**
> * vb2_qbuf() - Queue a buffer from userspace
> * @q: videobuf2 queue
> * @b: buffer structure passed from userspace to vidioc_qbuf handler
> @@ -1476,7 +1664,7 @@ void vb2_queue_release(struct vb2_queue *q)
> {
> __vb2_cleanup_fileio(q);
> __vb2_queue_cancel(q);
> - __vb2_queue_free(q);
> + __vb2_queue_free(q, q->num_buffers);
> }
> EXPORT_SYMBOL_GPL(vb2_queue_release);
>
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index 7dd6bca..8900e25 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -172,13 +172,17 @@ struct vb2_buffer {
> /**
> * struct vb2_ops - driver-specific callbacks
> *
> - * @queue_setup: called from a VIDIOC_REQBUFS handler, before
> - * memory allocation; driver should return the required
> - * number of buffers in num_buffers, the required number
> - * of planes per buffer in num_planes; the size of each
> - * plane should be set in the sizes[] array and optional
> - * per-plane allocator specific context in alloc_ctxs[]
> - * array
> + * @queue_setup: called from VIDIOC_REQBUFS and VIDIOC_CREATE_BUFS
> + * handlers, before memory allocation. When called with
> + * zeroed num_planes or plane sizes, the driver should
> + * return the required number of buffers in num_buffers,
> + * the required number of planes per buffer in num_planes;
> + * the size of each plane should be set in the sizes[]
> + * array and optional per-plane allocator specific context
> + * in alloc_ctxs[] array. If num_planes and sizes[] are
> + * both non-zero, the driver should use them. Otherwise the
> + * driver must make no assumptions about the buffers, that
> + * will be made available to it.
> * @wait_prepare: release any locks taken while calling vb2 functions;
> * it is called before an ioctl needs to wait for a new
> * buffer to arrive; required to avoid a deadlock in
> @@ -191,11 +195,11 @@ struct vb2_buffer {
> * perform additional buffer-related initialization;
> * initialization failure (return != 0) will prevent
> * queue setup from completing successfully; optional
> - * @buf_prepare: called every time the buffer is queued from userspace;
> - * drivers may perform any initialization required before
> - * each hardware operation in this callback;
> - * if an error is returned, the buffer will not be queued
> - * in driver; optional
> + * @buf_prepare: called every time the buffer is queued from userspace
> + * and from the VIDIOC_PREPARE_BUF ioctl; drivers may
> + * perform any initialization required before each hardware
> + * operation in this callback; if an error is returned, the
> + * buffer will not be queued in driver; optional
> * @buf_finish: called before every dequeue of the buffer back to
> * userspace; drivers may perform any operations required
> * before userspace accesses the buffer; optional
> @@ -293,6 +297,9 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q);
> int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b);
> int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req);
>
> +int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create);
> +int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b);
> +
> int vb2_queue_init(struct vb2_queue *q);
>
> void vb2_queue_release(struct vb2_queue *q);
--
Sakari Ailus
sakari.ailus@iki.fi
next prev parent reply other threads:[~2011-08-08 8:22 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-08-05 7:47 [PATCH 0/6 v4] new ioctl()s and soc-camera implementation Guennadi Liakhovetski
2011-08-05 7:47 ` [PATCH 1/6 v4] V4L: add two new ioctl()s for multi-size videobuffer management Guennadi Liakhovetski
2011-08-06 18:42 ` Sakari Ailus
2011-08-17 8:41 ` Guennadi Liakhovetski
2011-08-17 12:13 ` Sakari Ailus
2011-08-06 21:51 ` Pawel Osciak
2011-08-08 9:16 ` Hans Verkuil
2011-08-08 11:40 ` Laurent Pinchart
2011-08-08 12:40 ` Hans Verkuil
2011-08-08 22:06 ` Laurent Pinchart
2011-08-08 22:46 ` Sakari Ailus
2011-08-09 7:26 ` Hans Verkuil
2011-08-09 23:37 ` Sakari Ailus
2011-08-10 6:25 ` Hans Verkuil
2011-08-11 11:09 ` Sakari Ailus
2011-08-15 11:28 ` Guennadi Liakhovetski
2011-08-15 11:36 ` Hans Verkuil
2011-08-15 13:45 ` Guennadi Liakhovetski
2011-08-16 13:13 ` Guennadi Liakhovetski
2011-08-16 16:14 ` Pawel Osciak
2011-08-17 9:11 ` Guennadi Liakhovetski
2011-08-17 9:14 ` Laurent Pinchart
2011-08-17 15:29 ` Pawel Osciak
2011-08-22 10:06 ` Hans Verkuil
2011-08-22 10:40 ` Guennadi Liakhovetski
2011-08-22 11:16 ` Hans Verkuil
2011-08-22 13:54 ` Guennadi Liakhovetski
2011-08-22 14:01 ` Hans Verkuil
2011-08-22 15:42 ` Laurent Pinchart
2011-08-22 15:52 ` Hans Verkuil
2011-08-22 17:21 ` Laurent Pinchart
2011-08-23 6:31 ` Hans Verkuil
2011-08-24 4:05 ` Pawel Osciak
2011-08-24 6:44 ` Hans Verkuil
2011-08-17 13:22 ` Marek Szyprowski
2011-08-17 14:57 ` Pawel Osciak
2011-08-18 5:49 ` Marek Szyprowski
2011-08-05 7:47 ` [PATCH 2/6 v4] V4L: add a new videobuf2 buffer state VB2_BUF_STATE_PREPARED Guennadi Liakhovetski
2011-08-05 7:47 ` [PATCH 3/6 v4] V4L: vb2: change .queue_setup() argument to unsigned int Guennadi Liakhovetski
2011-08-05 21:36 ` [PATCH 3/6 v5] V4L: vb2: prepare to support multi-size buffers Guennadi Liakhovetski
2011-08-05 7:47 ` [PATCH 4/6 v4] V4L: vb2: add support for buffers of different sizes on a single queue Guennadi Liakhovetski
2011-08-06 18:56 ` Sakari Ailus [this message]
2011-08-17 8:44 ` Guennadi Liakhovetski
2011-08-05 7:47 ` [PATCH 5/6 v4] V4L: sh-mobile-ceu-camera: prepare to support multi-size buffers Guennadi Liakhovetski
2011-08-05 21:39 ` [PATCH 5/6 v5] " Guennadi Liakhovetski
2011-08-05 7:47 ` [PATCH 6/6 v4] V4L: soc-camera: add 2 new ioctl() handlers Guennadi Liakhovetski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4E3D8E70.2010407@iki.fi \
--to=sakari.ailus@iki.fi \
--cc=g.liakhovetski@gmx.de \
--cc=hverkuil@xs4all.nl \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-media@vger.kernel.org \
--cc=mchehab@infradead.org \
--cc=pawel@osciak.com \
--cc=sakari.ailus@maxwell.research.nokia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox