All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH/RFC v1 0/7] Videobuf2 framework
@ 2010-09-09  9:19 Pawel Osciak
  2010-09-09  9:19 ` [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Pawel Osciak
                   ` (8 more replies)
  0 siblings, 9 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-09  9:19 UTC (permalink / raw)
  To: linux-media; +Cc: p.osciak, kyungmin.park, m.szyprowski, t.fujak

Hello,

These patches add a new driver framework for Video for Linux 2 driver
- Videobuf2.

Videobuf2 is intended as a replacement for videobuf, the current driver
framework, which will be referred to as "videobuf1" for the remainder
of this document.

================================
What is videobuf2?
================================
Videobuf2 is a Video for Linux 2 API-compatible driver framework for
multimedia devices. It acts as an intermediate layer between userspace
applications and device drivers. It also provides low-level, modular
memory management functions for drivers.

Videobuf2 eases driver development, reduces drivers' code size and aids in
proper and consistent implementation of V4L2 API in drivers.

Videobuf2 memory management backend is fully modular. This allows custom
memory management routines for devices and platforms with non-standard
memory management requirements to be plugged in, without changing the
high-level buffer management functions and API.

The framework provides:
- implementations of streaming I/O V4L2 ioctls and file operations
- high-level video buffer, video queue and state management functions
- video buffer memory allocation and management

================================
Why a new framework?
================================
There have been many discussions in the V4L2 community about the feasibility
of writing a new framework, as opposed to fixing the existing one. It has been
agreed though that:
- videobuf1 has major flaws and an attempt to fix it would end up in rewriting
most of the code
- many drivers depend on videobuf1 and since the changes would be major,
an effort to adapt and test them all would not be realistically possible

Due to the problems with videobuf most new drivers cannot use it. This leads
to code replication and overcomplicated drivers.

================================
What is wrong with videobuf1?
================================
There are many problems with the current videobuf implementation. During a V4L2
mini-summit in Helsinki in June 2010, two presentations were delivered
on this topic:
- Laurent Pinchart "videobuf - the good, the bad and the ugly"
http://linuxtv.org/downloads/presentations/summit_jun_2010/20100614-v4l2_summit-videobuf.pdf
- Pawel Osciak "Future of the videobuf framework"
http://linuxtv.org/downloads/presentations/summit_jun_2010/Videobuf_Helsinki_June2010.pdf

These presentations highlighted many problems with videobuf. The most prominent
include:

- V4L2 API violations and wrong memory management design
  - it is impossible to pause streaming (buffers are freed on streamoff)
  - VIDIOC_REQBUFS(0) does not free memory
  - it is impossible to reallocate memory with VIDIOC_REQBUFS
  - video memory is allocated on mmap, qbuf or even on page fault,
    freed on unmap, streamoff or explicitly by drivers
  - per-buffer waitqueues
- not extensible enough and thus not ready for new platforms and uses,
  especially considering embedded multimedia devices
  - very hard to add new memory handling routines and custom memory allocators
  - no or poor support for handling cache coherency, IOMMUs, 
  - poor flexibility - only one do-it-all function for handling memory pinning,
    cache, sg-list creation, etc...
- unused fields, code duplication, vague/inconsistent naming, obscure usage in
  some places...

Many driver authors expressed their frustration with videobuf. Developers
acknowledge its merits and would like to use it, but due mostly to its
inflexible memory allocation schemes they are unable to do so.

================================
Main goals of the redesign
================================
- correct V4L2 API implementation, fixing videobuf1 problems and shortcomings
- full separation between queue management and memory management
- fully flexible, pluggable memory allocators and memory handling routines
- more specialized driver callbacks, called at different points
- support for new V4L2 API extensions, such as multi-planar video buffers

================================
Driver callbacks
================================
Driver callbacks have been redesigned for symmetry:
- buf_init - called once, after memory is allocated or after a new USERPTR
  buffer is queued; can be used e.g. to pin pages, verify contiguity, set up
  IOMMU mappings, etc.
- buf_prepare - called on each QBUF; can be used e.g. for cache sync, copying
  to bounce buffers, etc.
- buf_finish - called on each DQBUF; can be used e.g. for cache sync, copying
  back from bounce buffers, etc.
- buf_cleanup - called before freeing/releasing memory; can be used e.g. for
  unmapping memory, etc.

The remaining driver callbacks have been slightly redesigned:
- queue_negotiate - now incorporates multi-planar extensions; drivers return
  required number of buffers and planes per buffer
- plane_setup - drivers return plane sizes
Those two callbacks replace the old buf_setup.

- buf_queue - basically stays the same

================================
Memory allocators and handling
================================
Memory handling has been designed to allow more customization than in the
original videobuf. For this memory allocation ops have been slightly redesigned,
and have become fully replaceable and an allocator context struct have been
introduced.

Allocator context is intended to provide memory operations to videobuf and also
for storing allocator private data, if required, although simpler allocators
do not have to use this feature. Private data can be added by embedding the
context struct inside their own structures:

struct vb2_alloc_ctx {
        const struct vb2_mem_ops        *mem_ops;
};

struct vb2_foo_alloc_conf {
        struct vb2_alloc_ctx    alloc_ctx;                          
	/* Allocator private data here */
};

Moreover, a buffer context structure concept has been introduced. Allocators
return their own, custom, per-buffer structures on every allocation. This
structure is then used as a "cookie" and passed to other memory handling
methods called for its corresponding buffer.

Memory operations, stored in the allocator context, can be replaced if
needed by drivers on a per-function basis and functions from other allocators
or drivers can be reused as well. A full list with documentation can be found
in the videobuf2-core.h file.

It is also possible, although not required, to assign different contexts per
plane. This may be useful for drivers that need to use different memory types
for different planes. An example may be a driver that stores video data in the
first plane, which has to be allocated from a device-accessible memory area,
and metadata in the second plane, which does not have to be stored in
a device-accessible memory.

An good example of integrating a more advanced allocator, the recently discussed
on this list CMA (contiguous memory allocator), can be found in videobuf2-cma.*.

================================
Other changes
================================
The changes described above are the main changes in videobuf2. Most of the core
API has remained the same or very similar, although its implementation has been
fully rewritten. Some more visible changes include:

- Memory is now properly allocated on REQBUFS and can be freed and reallocated
  there as well.
- It is now possible to pause and resume streaming with streamon/streamoff,
  without freeing the buffers.
- V4L2 API-related, userspace-visible metadata, such as inputs, timestamps, etc.
  are no longer stored in videobuf buffer structure, but in an actual
  v4l2_buffer struct (idea borrowed from Laurent Pinchart). I felt that driver
  authors would prefer to use V4L2 API-based structures of videobuf custom
  structures where possible. It also eases copying v4l2_buffer-related data
  from/to userspace.
- Buffers do not include waitqueues anymore. One, global, per-queue waitqueue
  for done buffers has been introduced instead. Per-buffer waitqueues were not
  very useful and introduced additional complications in code.
  With this, drivers have gained the ability of dequeuing buffers out-of-order
  as well.
- Buffer states are not handled jointly by both videobuf and driver anymore,
  I felt it was not required and confusing for driver authors
- Some fields that were less useful have been removed and naming of others
  have been changed to better reflect their function.
- Other then reqbufs, ioctl implementations have remained almost the same
  and behave in the same way, 
  

Please see documentation in videobuf2-core.c and videobuf2-core.h for more
details and the patch porting vivi to videobuf2 for how to port existing
drivers to videobuf2.

This is a preliminary version intended for review, but has already been tested
for multiple drivers and different memory handling implementations. DMA-SG
implementation is not included yet.

The CMA allocator patches are attached for reference, to show how a more
complicated allocator can be integrated into the framework.

Any comments will be very much appreciated!

Contents:
[PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
[PATCH v1 2/7] v4l: videobuf2: add generic memory handling routines
[PATCH v1 3/7] v4l: mem2mem: port to videobuf2
[PATCH v1 4/7] v4l: videobuf2: add vmalloc allocator
[PATCH v1 5/7] v4l: videobuf2: add DMA coherent allocator
[PATCH v1 6/7] v4l: vivi: port to videobuf2
[PATCH v1 7/7] v4l: videobuf2: add CMA allocator

-- 
Best regards,
Pawel Osciak
Linux Platform Group
Samsung Poland R&D Center 

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

* [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
@ 2010-09-09  9:19 ` Pawel Osciak
  2010-09-09 17:29   ` Mauro Carvalho Chehab
  2010-09-25 14:27   ` Hans Verkuil
  2010-09-09  9:19 ` [PATCH v1 2/7] v4l: videobuf2: add generic memory handling routines Pawel Osciak
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-09  9:19 UTC (permalink / raw)
  To: linux-media; +Cc: p.osciak, kyungmin.park, m.szyprowski, t.fujak

Videobuf2 is a Video for Linux 2 API-compatible driver framework for
multimedia devices. It acts as an intermediate layer between userspace
applications and device drivers. It also provides low-level, modular
memory management functions for drivers.

Videobuf2 eases driver development, reduces drivers' code size and aids in
proper and consistent implementation of V4L2 API in drivers.

Videobuf2 memory management backend is fully modular. This allows custom
memory management routines for devices and platforms with non-standard
memory management requirements to be plugged in, without changing the
high-level buffer management functions and API.

The framework provides:
- implementations of streaming I/O V4L2 ioctls and file operations
- high-level video buffer, video queue and state management functions
- video buffer memory allocation and management

Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/Kconfig          |    3 +
 drivers/media/video/Makefile         |    2 +
 drivers/media/video/videobuf2-core.c | 1457 ++++++++++++++++++++++++++++++++++
 include/media/videobuf2-core.h       |  337 ++++++++
 4 files changed, 1799 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/videobuf2-core.c
 create mode 100644 include/media/videobuf2-core.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index f6e4d04..5764443 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -48,6 +48,9 @@ config VIDEO_TUNER
 config V4L2_MEM2MEM_DEV
 	tristate
 	depends on VIDEOBUF_GEN
+config VIDEOBUF2_CORE
+	tristate
+
 
 #
 # Multimedia Video device configuration
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 40f98fb..e66f53b 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -117,6 +117,8 @@ obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
 obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
 obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
 
+obj-$(CONFIG_VIDEOBUF2_CORE)		+= videobuf2-core.o
+
 obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
 
 obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
new file mode 100644
index 0000000..ed4b665
--- /dev/null
+++ b/drivers/media/video/videobuf2-core.c
@@ -0,0 +1,1457 @@
+/*
+ * videobuf2-core.c - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/poll.h>
+
+#include <media/videobuf2-core.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define dprintk(level, fmt, arg...)					\
+	do {								\
+		if (debug >= level)					\
+			printk(KERN_DEBUG "vb2: " fmt, ## arg);		\
+	} while (0)
+
+#define mem_ops(q, plane) ((q)->alloc_ctx[plane]->mem_ops)
+
+#define call_memop(q, plane, op, args...)				\
+	((q)->alloc_ctx[plane]->mem_ops->op) ?				\
+		((q)->alloc_ctx[plane]->mem_ops->op(args)) : 0
+
+
+/**
+ * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
+ */
+static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
+				unsigned long *plane_sizes)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	void *mem_priv;
+	int plane;
+
+	/* Allocate memory for all planes in this buffer */
+	for (plane = 0; plane < vb->num_planes; ++plane) {
+		mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane],
+					plane_sizes[plane]);
+		if (!mem_priv)
+			goto free;
+
+		/* Associate allocator private data with this plane */
+		vb->planes[plane].mem_priv = mem_priv;
+		vb->v4l2_planes[plane].length = plane_sizes[plane];
+	}
+
+	return 0;
+free:
+	/* Free already allocated memory if one of the allocations failed */
+	for (; plane > 0; --plane)
+		call_memop(q, plane, put, vb->planes[plane - 1].mem_priv);
+
+	return -ENOMEM;
+}
+
+/**
+ * __vb2_buf_mem_free() - free memory of the given buffer
+ */
+static void __vb2_buf_mem_free(struct vb2_buffer *vb)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	unsigned int plane;
+
+	for (plane = 0; plane < vb->num_planes; ++plane) {
+		call_memop(q, plane, put, vb->planes[plane].mem_priv);
+		dprintk(3, "Freed plane %d of buffer %d\n",
+				plane, vb->v4l2_buf.index);
+	}
+}
+
+/**
+ * __vb2_buf_userptr_put() - release userspace memory associated associated
+ * with a USERPTR buffer
+ */
+static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	unsigned int plane;
+
+	for (plane = 0; plane < vb->num_planes; ++plane) {
+		call_memop(q, plane, put_userptr, vb->planes[plane].mem_priv);
+		vb->planes[plane].mem_priv = NULL;
+	}
+}
+
+/**
+ * __setup_offsets() - setup unique offsets ("cookies") for every plane in
+ * every buffer on the queue
+ */
+static void __setup_offsets(struct vb2_queue *q)
+{
+	unsigned int buffer, plane;
+	struct vb2_buffer *vb;
+	unsigned long off = 0;
+
+	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+		vb = q->bufs[buffer];
+		if (!vb)
+			continue;
+
+		for (plane = 0; plane < vb->num_planes; ++plane) {
+			vb->v4l2_planes[plane].m.mem_offset = off;
+
+			dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
+					buffer, plane, off);
+
+			off += vb->v4l2_planes[plane].length;
+			off = PAGE_ALIGN(off);
+		}
+	}
+}
+
+/**
+ * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type)
+ * video buffer memory for all buffers/planes on the queue and initializes the
+ * queue
+ *
+ * Returns the number of buffers successfully allocated.
+ */
+static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
+			     unsigned int num_buffers, unsigned int num_planes)
+{
+	unsigned long plane_sizes[VIDEO_MAX_PLANES];
+	unsigned int buffer, plane;
+	struct vb2_buffer *vb;
+	int ret;
+
+	/* Get requested plane sizes from the driver */
+	for (plane = 0; plane < num_planes; ++plane) {
+		ret = q->ops->plane_setup(q, plane, &plane_sizes[plane]);
+		if (ret) {
+			dprintk(1, "Plane setup failed\n");
+			return ret;
+		}
+	}
+
+	for (buffer = 0; buffer < num_buffers; ++buffer) {
+		/* Allocate videobuf buffer structures */
+		vb = kzalloc(sizeof(struct vb2_buffer), GFP_KERNEL);
+		if (!vb) {
+			dprintk(1, "Memory alloc for buffer struct failed\n");
+			break;
+		}
+
+		/* Length stores number of planes for multiplanar buffers */
+		if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
+			vb->v4l2_buf.length = num_planes;
+
+		vb->state = VB2_BUF_STATE_DEQUEUED;
+		vb->vb2_queue = q;
+		vb->num_planes = num_planes;
+		vb->v4l2_buf.index = buffer;
+		vb->v4l2_buf.type = q->type;
+		vb->v4l2_buf.memory = memory;
+
+		/* Allocate video buffer memory for the MMAP type */
+		if (memory == V4L2_MEMORY_MMAP) {
+			ret = __vb2_buf_mem_alloc(vb, plane_sizes);
+			if (ret) {
+				dprintk(1, "Failed allocating memory for "
+						"buffer %d\n", buffer);
+				kfree(vb);
+				break;
+			}
+			/*
+			 * Call the driver-provided buffer initialization
+			 * callback, if given. An error in initialization
+			 * results in queue setup failure.
+			 */
+			if (q->ops->buf_init) {
+				ret = q->ops->buf_init(vb);
+				if (ret) {
+					dprintk(1, "Buffer %d %p initialization"
+						" failed\n", buffer, vb);
+					__vb2_buf_mem_free(vb);
+					kfree(vb);
+					break;
+				}
+			}
+		}
+
+		q->bufs[buffer] = vb;
+	}
+
+	q->num_buffers = buffer;
+
+	__setup_offsets(q);
+
+	dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
+			q->num_buffers, num_planes);
+
+	return buffer;
+}
+
+/**
+ * __vb2_free_mem() - release all video buffer memory for a given queue
+ */
+static void __vb2_free_mem(struct vb2_queue *q)
+{
+	unsigned int buffer;
+	struct vb2_buffer *vb;
+
+	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+		vb = q->bufs[buffer];
+		if (!vb)
+			continue;
+
+		/* Free MMAP buffers or release USERPTR buffers */
+		if (q->memory == V4L2_MEMORY_MMAP)
+			__vb2_buf_mem_free(vb);
+		else
+			__vb2_buf_userptr_put(vb);
+	}
+}
+
+/**
+ * __vb2_queue_free() - free the queue - video memory and related information
+ * and return the queue to an uninitialized state
+ */
+static int __vb2_queue_free(struct vb2_queue *q)
+{
+	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) {
+			if (NULL == q->bufs[buffer])
+				continue;
+			q->ops->buf_cleanup(q->bufs[buffer]);
+		}
+	}
+
+	/* Release video buffer memory */
+	__vb2_free_mem(q);
+
+	/* Free videobuf buffers */
+	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+		if (NULL == q->bufs[buffer])
+			continue;
+		kfree(q->bufs[buffer]);
+		q->bufs[buffer] = NULL;
+	}
+
+	q->num_buffers = 0;
+	q->memory = 0;
+
+	return 0;
+}
+
+/**
+ * __verify_planes_array() - verify that the planes array passed in struct
+ * v4l2_buffer from userspace can be safely used
+ */
+static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+	/* Is memory for copying plane information present? */
+	if (NULL == b->m.planes) {
+		dprintk(1, "Multi-planar buffer passed but "
+			   "planes array not provided\n");
+		return -EINVAL;
+	}
+
+	if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) {
+		dprintk(1, "Incorrect planes array length, "
+			   "expected %d, got %d\n", vb->num_planes, b->length);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
+ * returned to userspace
+ */
+static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	int ret = 0;
+
+	/* Copy back data such as timestamp, input, etc. */
+	memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
+	b->input = vb->v4l2_buf.input;
+	b->reserved = vb->v4l2_buf.reserved;
+
+	if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
+		ret = __verify_planes_array(vb, b);
+		if (ret)
+			return ret;
+
+		/*
+		 * Fill in plane-related data if userspace provided an array
+		 * for it. The memory and size is verified above.
+		 */
+		memcpy(b->m.planes, vb->v4l2_planes,
+			b->length * sizeof(struct v4l2_plane));
+	} else {
+		/*
+		 * We use length and offset in v4l2_planes array even for
+		 * single-planar buffers, but userspace does not.
+		 */
+		b->length = vb->v4l2_planes[0].length;
+		if (q->memory == V4L2_MEMORY_MMAP)
+			b->m.offset = vb->v4l2_planes[0].m.mem_offset;
+	}
+
+	b->flags = 0;
+
+	switch (vb->state) {
+	case VB2_BUF_STATE_QUEUED:
+	case VB2_BUF_STATE_ACTIVE:
+		b->flags |= V4L2_BUF_FLAG_QUEUED;
+		break;
+	case VB2_BUF_STATE_ERROR:
+		b->flags |= V4L2_BUF_FLAG_ERROR;
+		/* fall through */
+	case VB2_BUF_STATE_DONE:
+		b->flags |= V4L2_BUF_FLAG_DONE;
+		break;
+	case VB2_BUF_STATE_DEQUEUED:
+		/* nothing */
+		break;
+	}
+
+	if (vb->num_planes_mapped == vb->num_planes)
+		b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+	return ret;
+}
+
+/**
+ * vb2_querybuf() - query video buffer information
+ * @q:		videobuf queue
+ * @b:		buffer struct passed from userspace to vidioc_querybuf handler
+ *		in driver
+ *
+ * Should be called from vidioc_querybuf ioctl handler in driver.
+ * This function will verify the passed v4l2_buffer structure and fill the
+ * relevant information for the userspace.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_querybuf handler in driver.
+ */
+int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+	struct vb2_buffer *vb;
+	int ret = -EINVAL;
+
+	mutex_lock(&q->vb_lock);
+
+	if (b->type != q->type) {
+		dprintk(1, "querybuf: wrong buffer type\n");
+		goto done;
+	}
+
+	if (b->index >= q->num_buffers) {
+		dprintk(1, "querybuf: buffer index out of range\n");
+		goto done;
+	}
+	vb = q->bufs[b->index];
+	if (NULL == vb) {
+		/* Should never happen */
+		dprintk(1, "querybuf: no such buffer\n");
+		goto done;
+	}
+
+	ret = __fill_v4l2_buffer(vb, b);
+done:
+	mutex_unlock(&q->vb_lock);
+	return ret;
+}
+EXPORT_SYMBOL(vb2_querybuf);
+
+/**
+ * __verify_userptr_ops() - verify that all memory operations required for
+ * USERPTR queue type have been provided
+ */
+static int __verify_userptr_ops(struct vb2_queue *q, unsigned int num_planes)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_planes; ++i)
+		if (!mem_ops(q, i)->get_userptr || !mem_ops(q, i)->put_userptr)
+			return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * __verify_mmap_ops() - verify that all memory operations required for
+ * MMAP queue type have been provided
+ */
+static int __verify_mmap_ops(struct vb2_queue *q, unsigned int num_planes)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_planes; ++i)
+		if (!mem_ops(q, i)->alloc || !mem_ops(q, i)->put
+				|| !mem_ops(q, i)->mmap)
+			return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * __buffers_in_use() - return true if any buffers on the queue are in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffers_in_use(struct vb2_queue *q)
+{
+	unsigned int buffer, plane;
+	struct vb2_buffer *vb;
+
+	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+		vb = q->bufs[buffer];
+		for (plane = 0; plane < vb->num_planes; ++plane) {
+			/*
+			 * If num_users() has not been provided, apparently
+			 * nobody cares.
+			 */
+			if (!mem_ops(q, plane)->num_users)
+				continue;
+
+			/*
+			 * If num_users() returns more than 1, we are not the
+			 * only user of the plane's memory.
+			 */
+			if (call_memop(q, plane, num_users,
+					vb->planes[plane].mem_priv) > 1)
+				return true;
+		}
+	}
+
+	return false;
+}
+
+/**
+ * vb2_reqbufs() - Initiate streaming
+ * @q:		videobuf2 queue
+ * @req:	struct passed from userspace to vidioc_reqbufs handler in driver
+ *
+ * Should be called from vidioc_reqbufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies streaming parameters passed from the userspace,
+ * 2) sets up the queue,
+ * 3) negotiates number of buffers and planes per buffer with the driver
+ *    to be used during streaming,
+ * 4) allocates internal buffer structures (struct vb2_buffer), according to
+ *    the agreed parameters,
+ * 5) for MMAP memory type, allocates actual video memory, using the
+ *    memory handling/allocation routines provided during queue initialization
+ *
+ * If req->count is 0, all the memory will be freed instead.
+ * If the queue has been allocated previously (by a previous vb2_reqbufs) call
+ * and the queue is not busy, memory will be reallocated.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_reqbufs handler in driver.
+ */
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+	unsigned int num_buffers, num_planes;
+	int ret = 0;
+
+	if (req->memory != V4L2_MEMORY_MMAP
+			&& req->memory != V4L2_MEMORY_USERPTR) {
+		dprintk(1, "reqbufs: unsupported memory type\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&q->vb_lock);
+
+	if (req->type != q->type) {
+		dprintk(1, "reqbufs: queue type invalid\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if (q->streaming) {
+		dprintk(1, "reqbufs: streaming active\n");
+		ret = -EBUSY;
+		goto end;
+	}
+
+	if (req->count == 0) {
+		/* Free/release memory for count = 0, but only if unused */
+		if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
+			dprintk(1, "reqbufs: memory in use, cannot free\n");
+			ret = -EBUSY;
+			goto end;
+		}
+
+		ret = __vb2_queue_free(q);
+		goto end;
+	}
+
+	if (q->num_buffers != 0) {
+		/*
+		 * We already have buffers allocated, so a reallocation is
+		 * required, but only if the buffers are not in use.
+		 */
+		if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
+			dprintk(1, "reqbufs: memory in use, "
+					"cannot reallocate\n");
+			ret = -EBUSY;
+			goto end;
+		}
+
+		ret = __vb2_queue_free(q);
+		if (ret)
+			goto end;
+	}
+
+	num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
+
+	/* Ask the driver how many buffers and planes per buffer it requires */
+	ret = q->ops->queue_negotiate(q, &num_buffers, &num_planes);
+	if (ret)
+		goto end;
+
+	/*
+	 * Make sure all the required memory ops for given memory type
+	 * are available.
+	 */
+	if (req->memory == V4L2_MEMORY_MMAP
+			&& __verify_mmap_ops(q, num_planes)) {
+		dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
+		ret = -EINVAL;
+		goto end;
+	} else if (req->memory == V4L2_MEMORY_USERPTR
+			&& __verify_userptr_ops(q, num_planes)) {
+		dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	/* Finally, allocate buffers and video memory */
+	ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
+	if (ret < 0) {
+		dprintk(1, "Memory allocation failed with error: %d\n", ret);
+	} else {
+		/*
+		 * Return the number of successfully allocated buffers
+		 * to the userspace.
+		 */
+		req->count = ret;
+		ret = 0;
+	}
+
+	q->memory = req->memory;
+
+end:
+	mutex_unlock(&q->vb_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_reqbufs);
+
+/**
+ * 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
+ *
+ * This function returns a kernel virtual address of a given plane if
+ * such a mapping exist, NULL otherwise.
+ */
+void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+
+	if (plane_no > vb->num_planes)
+		return NULL;
+
+	return call_memop(q, plane_no, vaddr, vb->planes[plane_no].mem_priv);
+
+}
+EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
+
+/**
+ * vb2_plane_paddr() - Return the physical 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
+ *
+ * This function returns a physical address of a given plane if available,
+ * NULL otherwise.
+ */
+unsigned long vb2_plane_paddr(struct vb2_buffer *vb, unsigned int plane_no)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+
+	if (plane_no > vb->num_planes)
+		return 0UL;
+
+	return call_memop(q, plane_no, paddr, vb->planes[plane_no].mem_priv);
+}
+EXPORT_SYMBOL_GPL(vb2_plane_paddr);
+
+/**
+ * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
+ * @vb:		vb2_buffer returned from the driver
+ * @state:	either VB2_BUF_STATE_DONE if the operation finished successfully
+ *		or VB2_BUF_STATE_ERROR if the operation finished with an error
+ *
+ * This function should be called by the driver after a hardware operation on
+ * a buffer is finished and the buffer may be returned to userspace. The driver
+ * cannot use this buffer anymore until it is queued back to it by videobuf
+ * by the means of buf_queue callback. Only buffers previously queued to the
+ * driver by buf_queue can be passed to this function.
+ */
+void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	unsigned long flags;
+
+	if (vb->state != VB2_BUF_STATE_ACTIVE)
+		return;
+
+	if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
+		return;
+
+	dprintk(4, "Done processing on buffer %d, state: %d\n",
+			vb->v4l2_buf.index, vb->state);
+
+	/* Add the buffer to the done buffers list */
+	spin_lock_irqsave(&q->done_lock, flags);
+	vb->state = state;
+	list_add_tail(&vb->done_entry, &q->done_list);
+	spin_unlock_irqrestore(&q->done_lock, flags);
+
+	/* Inform any processes that may be waiting for buffers */
+	wake_up_interruptible(&q->done_wq);
+}
+EXPORT_SYMBOL_GPL(vb2_buffer_done);
+
+/**
+ * __fill_vb2_buffer() - fill a vb2_buffer with information provided in
+ * a v4l2_buffer by the userspace
+ */
+static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
+				struct v4l2_plane *v4l2_planes)
+{
+	unsigned int plane;
+	int ret;
+
+	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
+		/*
+		 * Verify that the userspace gave us a valid array for
+		 * plane information.
+		 */
+		ret = __verify_planes_array(vb, b);
+		if (ret)
+			return ret;
+
+		/* Fill in driver-provided information for OUTPUT types */
+		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
+			/*
+			 * Will have to go up to b->length when API starts
+			 * accepting variable number of planes.
+			 */
+			for (plane = 0; plane < vb->num_planes; ++plane) {
+				v4l2_planes[plane].bytesused =
+					b->m.planes[plane].bytesused;
+				v4l2_planes[plane].data_offset =
+					b->m.planes[plane].data_offset;
+			}
+		}
+
+		if (b->memory == V4L2_MEMORY_USERPTR) {
+			for (plane = 0; plane < vb->num_planes; ++plane) {
+				v4l2_planes[plane].m.userptr =
+					b->m.planes[plane].m.userptr;
+				v4l2_planes[plane].length =
+					b->m.planes[plane].length;
+			}
+		}
+	} else {
+		/*
+		 * Single-planar buffers do not use planes array,
+		 * so fill in relevant v4l2_buffer struct fields instead.
+		 * In videobuf we use our internal V4l2_planes struct for
+		 * single-planar buffers as well, for simplicity.
+		 */
+		if (V4L2_TYPE_IS_OUTPUT(b->type))
+			v4l2_planes[0].bytesused = b->bytesused;
+
+		if (b->memory == V4L2_MEMORY_USERPTR) {
+			v4l2_planes[0].m.userptr = b->m.userptr;
+			v4l2_planes[0].length = b->length;
+		}
+	}
+
+	vb->v4l2_buf.field = b->field;
+	vb->v4l2_buf.timestamp = b->timestamp;
+
+	return 0;
+}
+
+/**
+ * __qbuf_userptr() - handle qbuf of a USERPTR buffer
+ */
+static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+	struct v4l2_plane planes[VIDEO_MAX_PLANES];
+	struct vb2_queue *q = vb->vb2_queue;
+	void *mem_priv = NULL;
+	unsigned int plane;
+	int ret;
+
+	/* Verify and copy relevant information provided by the userspace */
+	ret = __fill_vb2_buffer(vb, b, planes);
+	if (ret)
+		return ret;
+
+	for (plane = 0; plane < vb->num_planes; ++plane) {
+		/* Skip the plane if already verified */
+		if (vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr
+		    && vb->v4l2_planes[plane].length == planes[plane].length)
+			continue;
+
+		dprintk(3, "qbuf: userspace address for plane %d changed, "
+				"reacquiring memory\n", plane);
+
+		/* Release previously acquired memory if present */
+		if (vb->planes[plane].mem_priv)
+			call_memop(q, plane, put_userptr,
+					vb->planes[plane].mem_priv);
+
+		vb->planes[plane].mem_priv = NULL;
+
+		/* Acquire each plane's memory */
+		if (mem_ops(q, plane)->get_userptr) {
+			mem_priv = mem_ops(q, plane)->get_userptr(
+							planes[plane].m.userptr,
+							planes[plane].length);
+			if (IS_ERR(mem_priv)) {
+				dprintk(1, "qbuf: failed acquiring userspace "
+						"memory for plane %d\n", plane);
+				goto err;
+			}
+
+			vb->planes[plane].mem_priv = mem_priv;
+		}
+	}
+
+	/*
+	 * Call driver-specific initialization on the newly acquired buffer,
+	 * if provided.
+	 */
+	if (q->ops->buf_init) {
+		ret = q->ops->buf_init(vb);
+		if (ret) {
+			dprintk(1, "qbuf: buffer initialization failed\n");
+			goto err;
+		}
+	}
+
+	/*
+	 * Now that everything is in order, copy relevant information
+	 * provided by userspace.
+	 */
+	for (plane = 0; plane < vb->num_planes; ++plane)
+		vb->v4l2_planes[plane] = planes[plane];
+
+	return 0;
+err:
+	/* In case of errors, release planes that were already acquired */
+	for (; plane > 0; --plane) {
+		call_memop(q, plane, put_userptr,
+				vb->planes[plane - 1].mem_priv);
+		vb->planes[plane - 1].mem_priv = NULL;
+	}
+
+	return ret;
+}
+
+/**
+ * __qbuf_mmap() - handle qbuf of an MMAP buffer
+ */
+static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b)
+{
+	return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
+}
+
+/**
+ * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
+ */
+static void __enqueue_in_driver(struct vb2_buffer *vb)
+{
+	struct vb2_queue *q = vb->vb2_queue;
+	unsigned long flags;
+
+	spin_lock_irqsave(q->drv_lock, flags);
+	vb->state = VB2_BUF_STATE_ACTIVE;
+	q->ops->buf_queue(vb);
+	spin_unlock_irqrestore(q->drv_lock, flags);
+}
+
+/**
+ * vb2_qbuf() - Queue a buffer from userspace
+ * @q:		videobuf2 queue
+ * @b:		buffer structure passed from userspace to vidioc_qbuf handler
+ *		in driver
+ *
+ * Should be called from vidioc_qbuf 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,
+ * 3) if streaming is on, queues the buffer in driver by the means of buf_queue
+ *    callback for processing.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_qbuf handler in driver.
+ */
+int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+	struct vb2_buffer *vb;
+	int ret;
+
+	mutex_lock(&q->vb_lock);
+
+	ret = -EINVAL;
+	if (b->type != q->type) {
+		dprintk(1, "qbuf: invalid buffer type\n");
+		goto done;
+	}
+	if (b->index >= q->num_buffers) {
+		dprintk(1, "qbuf: buffer index out of range\n");
+		goto done;
+	}
+
+	vb = q->bufs[b->index];
+	if (NULL == vb) {
+		/* Should never happen */
+		dprintk(1, "qbuf: buffer is NULL\n");
+		goto done;
+	}
+
+	if (b->memory != q->memory) {
+		dprintk(1, "qbuf: invalid memory type\n");
+		goto done;
+	}
+
+	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+		dprintk(1, "qbuf: buffer already in use\n");
+		goto done;
+	}
+
+	if (q->memory == V4L2_MEMORY_MMAP)
+		ret = __qbuf_mmap(vb, b);
+	else if (q->memory == V4L2_MEMORY_USERPTR)
+		ret = __qbuf_userptr(vb, b);
+	if (ret)
+		goto done;
+
+	if (q->ops->buf_prepare) {
+		ret = q->ops->buf_prepare(vb);
+		if (ret) {
+			dprintk(1, "qbuf: buffer preparation failed\n");
+			goto done;
+		}
+	}
+
+	/*
+	 * Add to the queued buffers list, a buffer will stay on it until
+	 * dequeued in dqbuf.
+	 */
+	list_add_tail(&vb->queued_entry, &q->queued_list);
+	vb->state = VB2_BUF_STATE_QUEUED;
+
+	/*
+	 * If already streaming, give the buffer to driver for processing.
+	 * If not, the buffer will be given to driver on next streamon.
+	 */
+	if (q->streaming)
+		__enqueue_in_driver(vb);
+
+	dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
+	ret = 0;
+done:
+	mutex_unlock(&q->vb_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_qbuf);
+
+/**
+ * __vb2_wait_for_done_vb() - wait for a buffer to become available
+ * for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
+{
+	int retval = 0;
+
+checks:
+	if (!q->streaming) {
+		dprintk(1, "Streaming off, will not wait for buffers\n");
+		retval = -EINVAL;
+		goto end;
+	}
+
+	/*
+	 * Buffers may be added to vb_done_list without holding the vb_lock,
+	 * but removal is performed only while holding both vb_lock and the
+	 * vb_done_lock spinlock. Thus we can be sure that as long as we hold
+	 * vb_lock, the list will remain not empty if this check succeeds.
+	 */
+	if (list_empty(&q->done_list)) {
+		if (nonblocking) {
+			dprintk(1, "Nonblocking and no buffers to dequeue, "
+					"will not wait\n");
+			retval = -EAGAIN;
+			goto end;
+		}
+
+		/*
+		 * We are streaming and nonblocking, wait for another buffer to
+		 * become ready or for streamoff. vb_lock is released to allow
+		 * streamoff or qbuf to be called while waiting.
+		 */
+		mutex_unlock(&q->vb_lock);
+		/*
+		 * Although the mutex is released here, we will be reevaluating
+		 * both conditions again after reacquiring it.
+		 */
+		dprintk(3, "Will sleep waiting for buffers\n");
+		retval = wait_event_interruptible(q->done_wq,
+				!list_empty(&q->done_list) || !q->streaming);
+		mutex_lock(&q->vb_lock);
+
+		if (retval)
+			goto end;
+
+		goto checks;
+	}
+
+end:
+	return retval;
+}
+
+/**
+ * __vb2_get_done_vb() - get a buffer ready for dequeuing
+ *
+ * Will sleep if required for nonblocking == false.
+ */
+static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
+				int nonblocking)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	/*
+	 * Wait for at least one buffer to become available on the done_list.
+	 */
+	ret = __vb2_wait_for_done_vb(q, nonblocking);
+	if (ret)
+		goto end;
+
+	/*
+	 * vb_lock has been held since we last verified that done_list is
+	 * not empty, so no need for another list_empty(done_list) check.
+	 */
+	spin_lock_irqsave(&q->done_lock, flags);
+	*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
+	list_del(&(*vb)->done_entry);
+	spin_unlock_irqrestore(&q->done_lock, flags);
+
+end:
+	return ret;
+}
+
+
+/**
+ * vb2_dqbuf() - Dequeue a buffer to the userspace
+ * @q:		videobuf2 queue
+ * @b:		buffer structure passed from userspace to vidioc_dqbuf handler
+ *		in driver
+ * @nonblocking: if true, this call will not sleep waiting for a buffer if no
+ *		 buffers ready for dequeuing are present. Normally the driver
+ *		 would be passing (file->f_flags & O_NONBLOCK) here
+ *
+ * Should be called from vidioc_dqbuf ioctl handler of a driver.
+ * This function:
+ * 1) verifies the passed buffer,
+ * 2) calls buf_finish callback in the driver (if provided), in which
+ *    driver can perform any additional operations that may be required before
+ *    returning the buffer to userspace, such as cache sync,
+ * 3) the buffer struct members are filled with relevant information for
+ *    the userspace.
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_dqbuf handler in driver.
+ */
+int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
+{
+	struct vb2_buffer *vb = NULL;
+	int ret;
+
+	mutex_lock(&q->vb_lock);
+
+	if (b->type != q->type) {
+		dprintk(1, "dqbuf: invalid buffer type\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = __vb2_get_done_vb(q, &vb, nonblocking);
+	if (ret < 0) {
+		dprintk(1, "dqbuf: error getting next done buffer\n");
+		goto done;
+	}
+
+	if (q->ops->buf_finish) {
+		ret = q->ops->buf_finish(vb);
+		if (ret) {
+			dprintk(1, "dqbuf: buffer finish failed\n");
+			goto done;
+		}
+	}
+
+	switch (vb->state) {
+	case VB2_BUF_STATE_DONE:
+		dprintk(3, "dqbuf: Returning done buffer\n");
+		break;
+	case VB2_BUF_STATE_ERROR:
+		dprintk(3, "dqbuf: Returning done buffer with errors\n");
+		break;
+	default:
+		dprintk(1, "dqbuf: Invalid buffer state\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Fill buffer information for the userspace */
+	__fill_v4l2_buffer(vb, b);
+	/* Remove from videobuf queue */
+	list_del(&vb->queued_entry);
+
+	dprintk(1, "dqbuf of buffer %d, with state %d\n",
+			vb->v4l2_buf.index, vb->state);
+
+	vb->state = VB2_BUF_STATE_DEQUEUED;
+
+done:
+	mutex_unlock(&q->vb_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_dqbuf);
+
+/**
+ * vb2_streamon - start streaming
+ * @q:		videobuf2 queue
+ * @type:	type argument passed from userspace to vidioc_streamon handler
+ *
+ * Should be called from vidioc_streamon handler of a driver.
+ * This function:
+ * 1) verifies current state
+ * 2) starts streaming and passes any previously queued buffers to the driver
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_streamon handler in the driver.
+ */
+int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+	struct vb2_buffer *vb;
+	int ret = 0;
+
+	mutex_lock(&q->vb_lock);
+
+	if (type != q->type) {
+		dprintk(1, "streamon: invalid stream type\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (q->streaming) {
+		dprintk(1, "streamon: already streaming\n");
+		ret = -EBUSY;
+		goto done;
+	}
+
+	/*
+	 * Cannot start streaming on an OUTPUT device if no buffers have
+	 * been queued yet.
+	 */
+	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+		if (list_empty(&q->queued_list)) {
+			dprintk(1, "streamon: no output buffers queued\n");
+			ret = -EINVAL;
+			goto done;
+		}
+	}
+
+	q->streaming = 1;
+
+	/*
+	 * If any buffers were queued before streamon,
+	 * we can now pass them to driver for processing.
+	 */
+	list_for_each_entry(vb, &q->queued_list, queued_entry)
+		__enqueue_in_driver(vb);
+
+	dprintk(3, "Streamon successful\n");
+done:
+	mutex_unlock(&q->vb_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_streamon);
+
+/**
+ * __vb2_queue_cancel() - cancel and stop (pause) streaming
+ *
+ * Removes all queued buffers from driver's queue and all buffers queued by
+ * userspace from videobuf's queue. Returns to state after reqbufs.
+ */
+static void __vb2_queue_cancel(struct vb2_queue *q)
+{
+	unsigned long flags = 0;
+	int i;
+
+	q->streaming = 0;
+
+	/*
+	 * Remove buffers from driver's queue. If a hardware operation
+	 * is currently underway, drv_lock should be claimed and we will
+	 * have to wait for it to finish before taking back buffers.
+	 */
+	spin_lock_irqsave(q->drv_lock, flags);
+	for (i = 0; i < q->num_buffers; ++i) {
+		if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE)
+			list_del(&q->bufs[i]->drv_entry);
+		q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
+	}
+	spin_unlock_irqrestore(q->drv_lock, flags);
+
+	/*
+	 * Remove all buffers from videobuf's list...
+	 */
+	INIT_LIST_HEAD(&q->queued_list);
+	/*
+	 * ...and done list; userspace will not receive any buffers it
+	 * has not already dequeued before initiating cancel.
+	 */
+	INIT_LIST_HEAD(&q->done_list);
+	wake_up_interruptible_all(&q->done_wq);
+}
+
+/**
+ * vb2_streamoff - stop streaming
+ * @q:		videobuf2 queue
+ * @type:	type argument passed from userspace to vidioc_streamoff handler
+ *
+ * Should be called from vidioc_streamoff handler of a driver.
+ * This function:
+ * 1) verifies current state,
+ * 2) stop streaming and dequeues any queued buffers, including those previously
+ *    passed to the driver (after waiting for the driver to finish).
+ *
+ * This call can be used for pausing playback.
+ * The return values from this function are intended to be directly returned
+ * from vidioc_streamoff handler in the driver
+ */
+int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+	int ret = 0;
+
+	mutex_lock(&q->vb_lock);
+
+	if (type != q->type) {
+		dprintk(1, "streamoff: invalid stream type\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if (!q->streaming) {
+		dprintk(1, "streamoff: not streaming\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	/*
+	 * Cancel will pause streaming and remove all buffers from the driver
+	 * and videobuf, effectively returning control over them to userspace.
+	 */
+	__vb2_queue_cancel(q);
+
+	dprintk(3, "Streamoff successful\n");
+end:
+	mutex_unlock(&q->vb_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_streamoff);
+
+/**
+ * __find_plane_by_off() - find plane associated with the given offset off
+ */
+int __find_plane_by_off(struct vb2_queue *q, unsigned long off,
+			unsigned int *_buffer, unsigned int *_plane)
+{
+	struct vb2_buffer *vb;
+	unsigned int buffer, plane;
+
+	/*
+	 * Go over all buffers and their planes, comparing the given offset
+	 * with an offset assigned to each plane. If a match is found,
+	 * return its buffer and plane numbers.
+	 */
+	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+		vb = q->bufs[buffer];
+
+		for (plane = 0; plane < vb->num_planes; ++plane) {
+			if (vb->v4l2_planes[plane].m.mem_offset == off) {
+				*_buffer = buffer;
+				*_plane = plane;
+				return 0;
+			}
+		}
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * vb2_mmap() - map video buffers into application address space
+ * @q:		videobuf2 queue
+ * @vma:	vma passed to the mmap file operation handler in the driver
+ *
+ * Should be called from mmap file operation handler of a driver.
+ * This function maps one plane of one of the available video buffers to
+ * userspace. To map whole video memory allocated on reqbufs, this function
+ * has to be called once per each plane per each buffer previously allocated.
+ *
+ * When the userspace application calls mmap, it passes to it an offset returned
+ * to it earlier by the means of vidioc_querybuf handler. That offset acts as
+ * a "cookie", which is then used to identify the plane to be mapped.
+ * This function finds a plane with a matching offset and a mapping is performed
+ * by the means of a provided memory operation.
+ *
+ * The return values from this function are intended to be directly returned
+ * from the mmap handler in driver.
+ */
+int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
+{
+	unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
+	struct vb2_plane *vb_plane;
+	struct vb2_buffer *vb;
+	unsigned int buffer, plane;
+	int ret = -EINVAL;
+
+	if (q->memory != V4L2_MEMORY_MMAP) {
+		dprintk(1, "Queue is not currently set up for mmap\n");
+		return ret;
+	}
+
+	if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) {
+		dprintk(1, "Invalid vma flags (need VM_WRITE | VM_SHARED)\n");
+		return ret;
+	}
+
+	mutex_lock(&q->vb_lock);
+
+	/*
+	 * Find the plane corresponding to the offset passed by userspace.
+	 */
+	ret = __find_plane_by_off(q, off, &buffer, &plane);
+	if (ret)
+		goto end;
+
+	vb = q->bufs[buffer];
+	vb_plane = &vb->planes[plane];
+
+	if (vb_plane->mapped) {
+		dprintk(1, "Plane already mapped\n");
+		goto end;
+	}
+
+	if (!mem_ops(q, plane)->mmap) {
+		dprintk(1, "mmap not supported\n");
+		goto end;
+	}
+
+	ret = mem_ops(q, plane)->mmap(vb_plane->mem_priv, vma);
+	if (ret)
+		goto end;
+
+	vb_plane->mapped = 1;
+	vb->num_planes_mapped++;
+
+	dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
+end:
+	mutex_unlock(&q->vb_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_mmap);
+
+/**
+ * vb2_has_consumers() - return true if the userspace is waiting for a buffer
+ * @q:		videobuf2 queue
+ *
+ * This function returns true if a userspace application is waiting for a buffer
+ * to be ready to dequeue (on which a hardware operation has been finished).
+ */
+bool vb2_has_consumers(struct vb2_queue *q)
+{
+	return waitqueue_active(&q->done_wq);
+}
+EXPORT_SYMBOL_GPL(vb2_has_consumers);
+
+/**
+ * vb2_poll() - implements poll userspace operation
+ * @q:		videobuf2 queue
+ * @file:	file argument passed to the poll file operation handler
+ * @wait:	wait argument passed to the poll file operation handler
+ *
+ * This function implements poll file operation handler for a driver.
+ * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will
+ * be informed that the file descriptor of a video device is available for
+ * reading.
+ * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor
+ * will be reported as available for writing.
+ *
+ * The return values from this function are intended to be directly returned
+ * from poll handler in driver.
+ */
+unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
+{
+	unsigned long flags = 0;
+	unsigned int ret = 0;
+	struct vb2_buffer *vb = NULL;
+
+	mutex_lock(&q->vb_lock);
+
+	/*
+	 * There is nothing to wait for if no buffers have already been queued.
+	 */
+	if (list_empty(&q->queued_list)) {
+		ret = POLLERR;
+		goto end;
+	}
+
+	poll_wait(file, &q->done_wq, wait);
+
+	/*
+	 * Take first buffer available for dequeuing.
+	 */
+	spin_lock_irqsave(&q->done_lock, flags);
+	if (!list_empty(&q->done_list))
+		vb = list_first_entry(&q->done_list, struct vb2_buffer,
+					done_entry);
+	spin_unlock_irqrestore(&q->done_lock, flags);
+
+	if (!vb)
+		goto end;
+
+	if (vb->state == VB2_BUF_STATE_DONE
+			|| vb->state == VB2_BUF_STATE_ERROR) {
+		if (V4L2_TYPE_IS_OUTPUT(q->type))
+			ret = POLLOUT | POLLWRNORM;
+		else
+			ret = POLLIN | POLLRDNORM;
+	}
+end:
+	mutex_unlock(&q->vb_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_poll);
+
+/**
+ * vb2_queue_init() - initialize a videobuf2 queue
+ * @q:		videobuf2 queue; this structure should be allocated in driver
+ * @ops:	driver-specific callbacks
+ * @alloc_ctx:	memory handler/allocator-specific context to be used;
+ *		the given context will be used for memory allocation on all
+ *		planes and buffers; it is possible to assign different contexts
+ *		per plane, use vb2_set_alloc_ctx() for that
+ * @drv_lock:	a lock for synchronization between the driver and videobuf,
+ *		it should be locked by driver whenever an operation is being
+ *		performed on a video buffer; this prevents videobuf from
+ *		forcefully taking back a buffer from a driver in the middle
+ *		of a hardware operation in case of an unexpected application
+ *		close or queue cancellation
+ * @type:	queue type
+ * @drv_priv:	driver private data, may be NULL; it can be used by driver in
+ *		driver-specific callbacks when issued
+ */
+int vb2_queue_init(struct vb2_queue *q, const struct vb2_ops *ops,
+			const struct vb2_alloc_ctx *alloc_ctx,
+			spinlock_t *drv_lock, enum v4l2_buf_type type,
+			void *drv_priv)
+{
+	unsigned int i;
+
+	BUG_ON(!q);
+	BUG_ON(!ops);
+	BUG_ON(!ops->queue_negotiate);
+	BUG_ON(!ops->plane_setup);
+	BUG_ON(!ops->buf_queue);
+
+	BUG_ON(!alloc_ctx);
+	BUG_ON(!alloc_ctx->mem_ops);
+
+	memset(q, 0, sizeof *q);
+	q->ops = ops;
+
+	for (i = 0; i < VIDEO_MAX_PLANES; ++i)
+		q->alloc_ctx[i] = alloc_ctx;
+
+	q->drv_lock = drv_lock;
+	q->type = type;
+	q->drv_priv = drv_priv;
+
+	mutex_init(&q->vb_lock);
+	INIT_LIST_HEAD(&q->queued_list);
+	INIT_LIST_HEAD(&q->done_list);
+	spin_lock_init(&q->done_lock);
+	init_waitqueue_head(&q->done_wq);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_queue_init);
+
+/**
+ * vb2_queue_release() - stop streaming, release the queue and free memory
+ * @q:		videobuf2 queue
+ *
+ * This function stops streaming and performs necessary clean ups, including
+ * freeing video buffer memory. The driver is responsible for freeing
+ * the vb2_queue structure itself.
+ */
+void vb2_queue_release(struct vb2_queue *q)
+{
+	mutex_lock(&q->vb_lock);
+
+	__vb2_queue_cancel(q);
+	__vb2_queue_free(q);
+
+	mutex_unlock(&q->vb_lock);
+}
+EXPORT_SYMBOL_GPL(vb2_queue_release);
+
+MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
+MODULE_AUTHOR("Pawel Osciak");
+MODULE_LICENSE("GPL");
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
new file mode 100644
index 0000000..d51c973
--- /dev/null
+++ b/include/media/videobuf2-core.h
@@ -0,0 +1,337 @@
+/*
+ * videobuf2-core.h - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+#ifndef _MEDIA_VIDEOBUF2_CORE_H
+#define _MEDIA_VIDEOBUF2_CORE_H
+
+#include <linux/mutex.h>
+#include <linux/mm_types.h>
+#include <linux/videodev2.h>
+#include <linux/poll.h>
+
+/**
+ * enum vb2_buffer_state - current video buffer state
+ * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control
+ * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver
+ * @VB2_BUF_STATE_ACTIVE:	buffer queued in driver and possibly used
+ *				in a hardware operation
+ * @VB2_BUF_STATE_DONE:		buffer returned from driver to videobuf, but
+ *				not yet dequeued to userspace
+ * @VB2_BUF_STATE_ERROR:	same as above, but the operation on the buffer
+ *				has ended with an error, which will be reported
+ *				to the userspace when it is dequeued
+ */
+enum vb2_buffer_state {
+	VB2_BUF_STATE_DEQUEUED,
+	VB2_BUF_STATE_QUEUED,
+	VB2_BUF_STATE_ACTIVE,
+	VB2_BUF_STATE_DONE,
+	VB2_BUF_STATE_ERROR,
+};
+
+/**
+ * struct vb2_plane - private videobuf per-plane info
+ * @mem_priv:	allocator-specific, per-memory buffer private structure
+ * @mapped:	set if the plane is mapped
+ */
+struct vb2_plane {
+	void			*mem_priv;
+	int			mapped:1;
+};
+
+/**
+ * struct vb2_buffer - represents a video buffer
+ * @v4l2_buf:		struct v4l2_buffer associated with this buffer; can
+ *			be read by the driver and relevant entries can be
+ *			changed by the driver in case of CAPTURE types
+ *			(such as timestamp)
+ * @v4l2_planes:	struct v4l2_planes associated with this buffer; can
+ *			be read by the driver and relevant entries can be
+ *			changed by the driver in case of CAPTURE types
+ *			(such as bytesused); NOTE that even for single-planar
+ *			types, the v4l2_planes[0] struct should be used
+ *			instead of v4l2_buf for filling bytesused - drivers
+ *			should use the vb2_set_plane_payload() function for that
+ * @vb2_queue:		the queue to which this driver belongs
+ * @drv_entry:		list entry to be used by driver for storing the buffer
+ * @num_planes:		number of planes in the buffer
+ *			on an internal driver queue
+ * @state:		current buffer state; do not change
+ * @queued_entry:	entry on the queued buffers list, which holds all
+ *			buffers queued from userspace
+ * @done_entry:		entry on the list that stores all buffers ready to
+ *			be dequeued to userspace
+ * @planes:		private per-plane information; do not change
+ * @num_planes_mapped:	number of mapped planes; do not change
+ */
+struct vb2_buffer {
+	struct v4l2_buffer	v4l2_buf;
+	struct v4l2_plane	v4l2_planes[VIDEO_MAX_PLANES];
+
+	struct vb2_queue	*vb2_queue;
+
+	struct list_head	drv_entry;
+	unsigned int		num_planes;
+
+/* Private: internal use only */
+	enum vb2_buffer_state	state;
+
+	struct list_head	queued_entry;
+	struct list_head	done_entry;
+
+	struct vb2_plane	planes[VIDEO_MAX_PLANES];
+	unsigned int		num_planes_mapped;
+};
+
+/**
+ * struct vb2_ops - driver-specific callbacks
+ * @queue_negotiate:	called from a VIDIOC_REQBUFS handler, before
+ *			memory allocation; driver should return the required
+ *			number of buffers in num_buffers and the required number
+ *			of planes per buffer in num_planes
+ * @plane_setup:	called before memory allocation num_planes times;
+ *			driver should return the required size of plane number
+ *			plane_no
+ * @buf_queue:		passes buffer vb to the driver; driver may use the
+ *			vb->drv_entry member to store the buffer on its internal
+ *			queue and start hardware operation on this buffer;
+ * @buf_init:		called once after allocating a buffer (in MMAP case)
+ *			or after acquiring a new USERPTR buffer; drivers may
+ *			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_finish:		called before every dequeue of the buffer back to
+ *			userspace; drivers may perform any operations required
+ *			before userspace accesses the buffer; optional
+ * @buf_cleanup:	called once before the buffer is freed; drivers may
+ *			perform any additional cleanup; optional
+ */
+struct vb2_ops {
+	int (*queue_negotiate)(struct vb2_queue *q, unsigned int *num_buffers,
+				unsigned int *num_planes);
+	int (*plane_setup)(struct vb2_queue *q,
+			   unsigned int plane_no, unsigned long *plane_size);
+	void (*buf_queue)(struct vb2_buffer *vb);
+
+	int (*buf_init)(struct vb2_buffer *vb);
+	int (*buf_prepare)(struct vb2_buffer *vb);
+	int (*buf_finish)(struct vb2_buffer *vb);
+	void (*buf_cleanup)(struct vb2_buffer *vb);
+};
+
+/**
+ * struct vb2_queue - a videobuf queue
+ *
+ * @type:	current queue type
+ * @memory:	current memory type used
+ * @drv_priv:	driver private data, passed on vb2_queue_init
+ * @bufs:	videobuf buffer structures
+ * @num_buffers: number of allocated/used buffers
+ * @vb_lock:	for ioctl handler and queue state changes synchronization
+ * @queued_list: list of buffers currently queued from userspace
+ * @done_list:	list of buffers ready to be dequeued to userspace
+ * @done_lock:	lock to protect done_list list
+ * @done_wq:	waitqueue for processes waiting for buffers ready to be dequeued
+ * @drv_lock:	driver lock for synchronization between driver and videobuf,
+ *		passed on vb2_queue_init
+ * @ops:	driver-specific callbacks
+ * @alloc_ctx:	memory type/allocator-specific callbacks
+ * @streaming:	current streaming state
+ * @userptr_supported: true if queue supports USERPTR types
+ * @mmap_supported: true if queue supports MMAP types
+ */
+struct vb2_queue {
+	enum v4l2_buf_type		type;
+	enum v4l2_memory		memory;
+	void				*drv_priv;
+
+/* private: internal use only */
+	struct vb2_buffer		*bufs[VIDEO_MAX_FRAME];
+	unsigned int			num_buffers;
+
+	struct mutex			vb_lock;
+	struct list_head		queued_list;
+
+	struct list_head		done_list;
+	spinlock_t			done_lock;
+	wait_queue_head_t		done_wq;
+
+	spinlock_t			*drv_lock;
+
+	const struct vb2_ops		*ops;
+	const struct vb2_alloc_ctx	*alloc_ctx[VIDEO_MAX_PLANES];
+
+	int				streaming:1;
+	int				userptr_supported:1;
+	int				mmap_supported:1;
+};
+
+/* The below functions are documented in videobuf2-core.c */
+void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
+unsigned long vb2_plane_paddr(struct vb2_buffer *vb, unsigned int plane_no);
+void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state);
+bool vb2_has_consumers(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_queue_init(struct vb2_queue *q, const struct vb2_ops *ops,
+			const struct vb2_alloc_ctx *alloc_ctx,
+			spinlock_t *drv_lock, enum v4l2_buf_type type,
+			void *drv_priv);
+void vb2_queue_release(struct vb2_queue *q);
+
+int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b);
+int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking);
+
+int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type);
+int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type);
+
+int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma);
+unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait);
+
+/**
+ * vb2_get_drv_priv() - return driver private data associated with the queue
+ * @q:		videobuf queue
+ */
+static inline void *vb2_get_drv_priv(struct vb2_queue *q)
+{
+	return q->drv_priv;
+}
+
+/**
+ * vb2_set_plane_payload() - set bytesused for the plane plane_no
+ * @vb:		buffer for which plane payload should be set
+ * @plane_no:	plane number for which payload should be set
+ * @size:	payload in bytes
+ */
+static inline void vb2_set_plane_payload(struct vb2_buffer *vb,
+				 unsigned int plane_no, unsigned long size)
+{
+	if (!V4L2_TYPE_IS_MULTIPLANAR(vb->vb2_queue->type) && plane_no == 0)
+		vb->v4l2_buf.bytesused = size;
+	else
+		vb->v4l2_planes[plane_no].bytesused = size;
+}
+
+/**
+ * vb2_plane_size() - return plane size in bytes
+ * @vb:		buffer for which plane size should be returned
+ * @plane_no:	plane number for which size should be returned
+ */
+static inline unsigned long
+vb2_plane_size(struct vb2_buffer *vb, unsigned int plane_no)
+{
+	if (plane_no < vb->num_planes)
+		return vb->v4l2_planes[plane_no].length;
+	else
+		return 0;
+}
+
+/**
+ * vb2_set_alloc_ctx() - use to assign a allocator context for a plane
+ * @q:		videobuf queue
+ * @alloc_ctx:	allocator context to be assigned
+ * @plane_no:	plane number to which the context is to be assigned
+ *
+ * This function can be used to assign additional allocator contexts
+ * on a per-plane basis, if a driver requires such feature.
+ * When a driver passes an allocator context to the vb2_queue_init call,
+ * it is initially assigned to all planes. Driver can then use this call
+ * to selectively assign additional contexts to particular planes.
+ * A context assigned to plane_no will be used for memory operations
+ * on plane number plane_no for all buffers.
+ */
+static inline void
+vb2_set_alloc_ctx(struct vb2_queue *q, struct vb2_alloc_ctx *alloc_ctx,
+			unsigned int plane_no)
+{
+	if (plane_no < VIDEO_MAX_PLANES)
+		q->alloc_ctx[plane_no] = alloc_ctx;
+}
+struct vb2_mem_ops;
+
+/**
+ * struct vb2_alloc_ctx - allocator/memory handler-specific context
+ * @mem_ops:	memory operations used by the current context
+ *
+ * This structure is passed to the alloc() call and can be used to store
+ * additional allocator private data. In such case it can be embedded in
+ * a allocator private structure as its first member.
+ * In more complicated cases, separate contexts can be assigned to each plane,
+ * if required. This would allow separate memory allocation/handling strategies
+ * for each plane, which is useful for drivers requiring different memory types
+ * and/or handling for each plane.
+ *
+ * See videobuf2-vmalloc.c and videobuf2-dma-coherent.c for example usage.
+ */
+struct vb2_alloc_ctx {
+	const struct vb2_mem_ops	*mem_ops;
+};
+
+/**
+ * struct vb2_mem_ops - memory handling/memory allocator operations
+ * @alloc:	allocate video memory and, optionally, allocator private data,
+ *		return NULL on failure or a pointer to allocator private,
+ *		per-buffer data on success, NULL on failure; the returned
+ *		private structure will then be passed as buf_priv argument
+ *		to other ops in this structure
+ * @put:	inform the allocator that the buffer will no longer be used;
+ *		usually will result in the allocator freeing the buffer (if
+ *		no other users of this buffer are present); the buf_priv
+ *		argument is the allocator private per-buffer structure
+ *		previously returned from the alloc callback
+ * @get_userptr: acquire userspace memory for a hardware operation; used for
+ *		 USERPTR memory types; vaddr is the address passed to the
+ *		 videobuf layer when queuing a video buffer of USERPTR type;
+ *		 should return an allocator private per-buffer structure
+ *		 associated with the buffer on success, NULL on failure;
+ *		 the returned private structure will then be passed as buf_priv
+ *		 argument to other ops in this structure
+ * @put_userptr: inform the allocator that a USERPTR buffer will no longer
+ *		 be used
+ * @vaddr:	return a kernel virtual address to a given memory buffer
+ *		associated with the passed private structure or NULL if no
+ *		such mapping exists
+ * @paddr:	return a physical address to a given memory buffer associated
+ *		with the passed private structure or NULL if not available
+ * @num_users:	return the current number of users of a memory buffer;
+ *		return 1 if the videobuf layer (or actually the driver using
+ *		it) is the only user
+ * @mmap:	setup a userspace mapping for a given memory buffer under
+ *		the provided virtual memory region
+ *
+ * Required ops for USERPTR types: get_userptr, put_userptr.
+ * Required ops for MMAP types: alloc, put, num_users, mmap.
+ */
+struct vb2_mem_ops {
+	void		*(*alloc)(const struct vb2_alloc_ctx *alloc_ctx,
+					unsigned long size);
+	void		(*put)(void *buf_priv);
+
+	void		*(*get_userptr)(unsigned long vaddr,
+						unsigned long size);
+	void		(*put_userptr)(void *buf_priv);
+
+	void		*(*vaddr)(void *buf_priv);
+	unsigned long	(*paddr)(void *buf_priv);
+	unsigned int	(*num_users)(void *buf_priv);
+
+	int		(*mmap)(void *buf_priv, struct vm_area_struct *vma);
+};
+
+
+#endif /* _MEDIA_VIDEOBUF2_CORE_H */
-- 
1.7.2.1.97.g3235b.dirty


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

* [PATCH v1 2/7] v4l: videobuf2: add generic memory handling routines
  2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
  2010-09-09  9:19 ` [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Pawel Osciak
@ 2010-09-09  9:19 ` Pawel Osciak
  2010-09-09 17:34   ` Mauro Carvalho Chehab
  2010-09-09  9:19 ` [PATCH v1 3/7] v4l: mem2mem: port to videobuf2 Pawel Osciak
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 24+ messages in thread
From: Pawel Osciak @ 2010-09-09  9:19 UTC (permalink / raw)
  To: linux-media; +Cc: p.osciak, kyungmin.park, m.szyprowski, t.fujak

Add generic memory handling routines for userspace pointer handling,
contiguous memory verification and mapping.

Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/Kconfig            |    3 +
 drivers/media/video/Makefile           |    2 +
 drivers/media/video/videobuf2-memops.c |  181 ++++++++++++++++++++++++++++++++
 include/media/videobuf2-memops.h       |   27 +++++
 4 files changed, 213 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/videobuf2-memops.c
 create mode 100644 include/media/videobuf2-memops.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 5764443..e4ac3e2 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -51,6 +51,9 @@ config V4L2_MEM2MEM_DEV
 config VIDEOBUF2_CORE
 	tristate
 
+config VIDEOBUF2_MEMOPS
+	tristate
+
 
 #
 # Multimedia Video device configuration
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index e66f53b..ab5b521 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -118,6 +118,8 @@ obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
 obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
 
 obj-$(CONFIG_VIDEOBUF2_CORE)		+= videobuf2-core.o
+obj-$(CONFIG_VIDEOBUF2_MEMOPS)		+= videobuf2-memops.o
+
 
 obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
 
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c
new file mode 100644
index 0000000..fa76983
--- /dev/null
+++ b/drivers/media/video/videobuf2-memops.c
@@ -0,0 +1,181 @@
+/*
+ * videobuf2-memops.c - generic memory handling routines for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/cma.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+
+#include <media/videobuf2-core.h>
+
+/**
+ * vb2_contig_verify_userptr() - verify contiguity of a userspace-mapped memory
+ * @vma:	virtual memory region which maps the physical memory
+ *		to be verified
+ * @vaddr:	starting virtual address of the area to be verified
+ * @size:	size of the area to be verified
+ * @paddr:	will return physical address for the given vaddr
+ *
+ * This function will go through memory area of size size mapped at vaddr and
+ * verify that the underlying physical pages are contiguous.
+ *
+ * Returns 0 on success and a physical address to the memory pointed
+ * to by vaddr in paddr.
+ */
+int vb2_contig_verify_userptr(struct vm_area_struct *vma,
+				unsigned long vaddr, unsigned long size,
+				unsigned long *paddr)
+{
+	struct mm_struct *mm = current->mm;
+	unsigned long offset;
+	unsigned long vma_size;
+	unsigned long curr_pfn, prev_pfn;
+	unsigned long num_pages;
+	int ret = -EINVAL;
+	unsigned int i;
+
+	offset = vaddr & ~PAGE_MASK;
+
+	down_read(&mm->mmap_sem);
+
+	vma = find_vma(mm, vaddr);
+	if (!vma) {
+		printk(KERN_ERR "Invalid userspace address\n");
+		goto done;
+	}
+
+	vma_size = vma->vm_end - vma->vm_start;
+
+	if (size > vma_size - offset) {
+		printk(KERN_ERR "Region too small\n");
+		goto done;
+	}
+	num_pages = (size + offset) >> PAGE_SHIFT;
+
+	ret = follow_pfn(vma, vaddr, &curr_pfn);
+	if (ret) {
+		printk(KERN_ERR "Invalid userspace address\n");
+		goto done;
+	}
+
+	*paddr = (curr_pfn << PAGE_SHIFT) + offset;
+
+	for (i = 1; i < num_pages; ++i) {
+		prev_pfn = curr_pfn;
+		vaddr += PAGE_SIZE;
+
+		ret = follow_pfn(vma, vaddr, &curr_pfn);
+		if (ret || curr_pfn != prev_pfn + 1) {
+			printk(KERN_ERR "Invalid userspace address\n");
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+done:
+	up_read(&mm->mmap_sem);
+	return ret;
+}
+
+/**
+ * vb2_mmap_pfn_range() - map physical pages to userspace
+ * @vma:	virtual memory region for the mapping
+ * @paddr:	starting physical address of the memory to be mapped
+ * @size:	size of the memory to be mapped
+ * @vm_ops:	vm operations to be assigned to the created area
+ * @priv:	private data to be associated with the area
+ *
+ * Returns 0 on success.
+ */
+int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
+				unsigned long size,
+				const struct vm_operations_struct *vm_ops,
+				void *priv)
+{
+	int ret;
+
+	size = min_t(unsigned long, vma->vm_end - vma->vm_start, size);
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT,
+				size, vma->vm_page_prot);
+	if (ret) {
+		printk(KERN_ERR "Remapping memory failed, error: %d\n", ret);
+		return ret;
+	}
+
+	vma->vm_flags		|= VM_DONTEXPAND | VM_RESERVED;
+	vma->vm_private_data	= priv;
+	vma->vm_ops		= vm_ops;
+
+	vm_ops->open(vma);
+
+	printk(KERN_DEBUG "%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
+			__func__, paddr, vma->vm_start, size);
+
+	return 0;
+}
+
+/**
+ * vb2_get_userptr() - acquire an area pointed to by userspace addres vaddr
+ * @vaddr:	virtual userspace address to the given area
+ *
+ * This function attempts to acquire an area mapped in the userspace for
+ * the duration of a hardware operation.
+ *
+ * Returns a virtual memory region associated with the given vaddr on success
+ * or NULL.
+ */
+struct vm_area_struct *vb2_get_userptr(unsigned long vaddr)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	down_read(&mm->mmap_sem);
+
+	vma = find_vma(mm, vaddr);
+	if (!vma)
+		goto done;
+
+	if (vma->vm_ops && vma->vm_ops->open)
+		vma->vm_ops->open(vma);
+
+	if (vma->vm_file)
+		get_file(vma->vm_file);
+
+done:
+	up_read(&mm->mmap_sem);
+	return vma;
+}
+
+/**
+ * vb2_put_userptr() - release a userspace memory area
+ * @vma:	virtual memory region associated with the area to be released
+ *
+ * This function releases the previously acquired memory area after a hardware
+ * operation.
+ */
+void vb2_put_userptr(struct vm_area_struct *vma)
+{
+	if (!vma)
+		return;
+
+	if (vma->vm_file)
+		fput(vma->vm_file);
+
+	if (vma->vm_ops && vma->vm_ops->close)
+		vma->vm_ops->close(vma);
+}
diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h
new file mode 100644
index 0000000..23db249
--- /dev/null
+++ b/include/media/videobuf2-memops.h
@@ -0,0 +1,27 @@
+/*
+ * videobuf2-memops.h - generic memory handling routines for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <media/videobuf2-core.h>
+
+int vb2_contig_verify_userptr(struct vm_area_struct *vma,
+				unsigned long vaddr, unsigned long size,
+				unsigned long *paddr);
+
+int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
+				unsigned long size,
+				const struct vm_operations_struct *vm_ops,
+				void *priv);
+
+struct vm_area_struct *vb2_get_userptr(unsigned long vaddr);
+
+void vb2_put_userptr(struct vm_area_struct *vma);
+
-- 
1.7.2.1.97.g3235b.dirty


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

* [PATCH v1 3/7] v4l: mem2mem: port to videobuf2
  2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
  2010-09-09  9:19 ` [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Pawel Osciak
  2010-09-09  9:19 ` [PATCH v1 2/7] v4l: videobuf2: add generic memory handling routines Pawel Osciak
@ 2010-09-09  9:19 ` Pawel Osciak
  2010-09-09  9:19 ` [PATCH v1 4/7] v4l: videobuf2: add vmalloc allocator Pawel Osciak
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-09  9:19 UTC (permalink / raw)
  To: linux-media; +Cc: p.osciak, kyungmin.park, m.szyprowski, t.fujak

Port memory-to-memory framework to videobuf2 framework.

Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/Kconfig        |    3 +-
 drivers/media/video/v4l2-mem2mem.c |  185 ++++++++++++++++++------------------
 include/media/v4l2-mem2mem.h       |   49 ++++++----
 3 files changed, 124 insertions(+), 113 deletions(-)

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index e4ac3e2..c2f99d8 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -47,7 +47,8 @@ config VIDEO_TUNER
 
 config V4L2_MEM2MEM_DEV
 	tristate
-	depends on VIDEOBUF_GEN
+	depends on VIDEOBUF2_CORE
+
 config VIDEOBUF2_CORE
 	tristate
 
diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c
index f45f940..fa50dd0 100644
--- a/drivers/media/video/v4l2-mem2mem.c
+++ b/drivers/media/video/v4l2-mem2mem.c
@@ -17,7 +17,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 
-#include <media/videobuf-core.h>
+#include <media/videobuf2-core.h>
 #include <media/v4l2-mem2mem.h>
 
 MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
@@ -77,9 +77,9 @@ static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
 }
 
 /**
- * v4l2_m2m_get_vq() - return videobuf_queue for the given type
+ * v4l2_m2m_get_vq() - return vb2_queue for the given type
  */
-struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
+struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
 				       enum v4l2_buf_type type)
 {
 	struct v4l2_m2m_queue_ctx *q_ctx;
@@ -95,26 +95,21 @@ EXPORT_SYMBOL(v4l2_m2m_get_vq);
 /**
  * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers
  */
-void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type)
+void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx)
 {
-	struct v4l2_m2m_queue_ctx *q_ctx;
-	struct videobuf_buffer *vb = NULL;
+	struct vb2_buffer *vb = NULL;
 	unsigned long flags;
 
-	q_ctx = get_queue_ctx(m2m_ctx, type);
-	if (!q_ctx)
-		return NULL;
-
-	spin_lock_irqsave(q_ctx->q.irqlock, flags);
+	spin_lock_irqsave(q_ctx->q.drv_lock, flags);
 
 	if (list_empty(&q_ctx->rdy_queue))
 		goto end;
 
-	vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer, queue);
-	vb->state = VIDEOBUF_ACTIVE;
+	vb = list_entry(q_ctx->rdy_queue.next, struct vb2_buffer, drv_entry);
+	vb->state = VB2_BUF_STATE_ACTIVE;
 
 end:
-	spin_unlock_irqrestore(q_ctx->q.irqlock, flags);
+	spin_unlock_irqrestore(q_ctx->q.drv_lock, flags);
 	return vb;
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
@@ -123,24 +118,19 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
  * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and
  * return it
  */
-void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type)
+void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx)
 {
-	struct v4l2_m2m_queue_ctx *q_ctx;
-	struct videobuf_buffer *vb = NULL;
+	struct vb2_buffer *vb = NULL;
 	unsigned long flags;
 
-	q_ctx = get_queue_ctx(m2m_ctx, type);
-	if (!q_ctx)
-		return NULL;
-
-	spin_lock_irqsave(q_ctx->q.irqlock, flags);
+	spin_lock_irqsave(q_ctx->q.drv_lock, flags);
 	if (!list_empty(&q_ctx->rdy_queue)) {
-		vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer,
-				queue);
-		list_del(&vb->queue);
+		vb = list_entry(q_ctx->rdy_queue.next, struct vb2_buffer,
+				drv_entry);
+		list_del(&vb->drv_entry);
 		q_ctx->num_rdy--;
 	}
-	spin_unlock_irqrestore(q_ctx->q.irqlock, flags);
+	spin_unlock_irqrestore(q_ctx->q.drv_lock, flags);
 
 	return vb;
 }
@@ -235,20 +225,20 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
 		return;
 	}
 
-	spin_lock_irqsave(m2m_ctx->out_q_ctx.q.irqlock, flags);
+	spin_lock_irqsave(m2m_ctx->out_q_ctx.q.drv_lock, flags);
 	if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) {
-		spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags);
+		spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.drv_lock, flags);
 		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
 		dprintk("No input buffers available\n");
 		return;
 	}
 	if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) {
-		spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags);
+		spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.drv_lock, flags);
 		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
 		dprintk("No output buffers available\n");
 		return;
 	}
-	spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags);
+	spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.drv_lock, flags);
 
 	if (m2m_dev->m2m_ops->job_ready
 		&& (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
@@ -309,10 +299,10 @@ EXPORT_SYMBOL(v4l2_m2m_job_finish);
 int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 		     struct v4l2_requestbuffers *reqbufs)
 {
-	struct videobuf_queue *vq;
+	struct vb2_queue *vq;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type);
-	return videobuf_reqbufs(vq, reqbufs);
+	return vb2_reqbufs(vq, reqbufs);
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
 
@@ -324,12 +314,12 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
 int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 		      struct v4l2_buffer *buf)
 {
-	struct videobuf_queue *vq;
-	int ret;
+	struct vb2_queue *vq;
+	int ret = 0;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
-	ret = videobuf_querybuf(vq, buf);
 
+	ret = vb2_querybuf(vq, buf);
 	if (buf->memory == V4L2_MEMORY_MMAP
 	    && vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
 		buf->m.offset += DST_QUEUE_OFF_BASE;
@@ -346,11 +336,11 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
 int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 		  struct v4l2_buffer *buf)
 {
-	struct videobuf_queue *vq;
+	struct vb2_queue *vq;
 	int ret;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
-	ret = videobuf_qbuf(vq, buf);
+	ret = vb2_qbuf(vq, buf);
 	if (!ret)
 		v4l2_m2m_try_schedule(m2m_ctx);
 
@@ -365,10 +355,10 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
 int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 		   struct v4l2_buffer *buf)
 {
-	struct videobuf_queue *vq;
+	struct vb2_queue *vq;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
-	return videobuf_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
+	return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
 
@@ -378,11 +368,11 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
 int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 		      enum v4l2_buf_type type)
 {
-	struct videobuf_queue *vq;
+	struct vb2_queue *vq;
 	int ret;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, type);
-	ret = videobuf_streamon(vq);
+	ret = vb2_streamon(vq, type);
 	if (!ret)
 		v4l2_m2m_try_schedule(m2m_ctx);
 
@@ -396,10 +386,10 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamon);
 int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 		       enum v4l2_buf_type type)
 {
-	struct videobuf_queue *vq;
+	struct vb2_queue *vq;
 
 	vq = v4l2_m2m_get_vq(m2m_ctx, type);
-	return videobuf_streamoff(vq);
+	return vb2_streamoff(vq, type);
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
 
@@ -414,9 +404,10 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
 unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 			   struct poll_table_struct *wait)
 {
-	struct videobuf_queue *src_q, *dst_q;
-	struct videobuf_buffer *src_vb = NULL, *dst_vb = NULL;
+	struct vb2_queue *src_q, *dst_q;
+	struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
 	unsigned int rc = 0;
+	unsigned long flags;
 
 	src_q = v4l2_m2m_get_src_vq(m2m_ctx);
 	dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
@@ -424,30 +415,39 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 	mutex_lock(&src_q->vb_lock);
 	mutex_lock(&dst_q->vb_lock);
 
-	if (src_q->streaming && !list_empty(&src_q->stream))
-		src_vb = list_first_entry(&src_q->stream,
-					  struct videobuf_buffer, stream);
-	if (dst_q->streaming && !list_empty(&dst_q->stream))
-		dst_vb = list_first_entry(&dst_q->stream,
-					  struct videobuf_buffer, stream);
-
-	if (!src_vb && !dst_vb) {
+	/*
+	 * There has to be at least one buffer queued on each queued_list, which
+	 * means either in driver already or waiting for driver to claim it
+	 * and start processing.
+	 */
+	if ((!src_q->streaming || list_empty(&src_q->queued_list))
+		&& (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
 		rc = POLLERR;
 		goto end;
 	}
 
-	if (src_vb) {
-		poll_wait(file, &src_vb->done, wait);
-		if (src_vb->state == VIDEOBUF_DONE
-		    || src_vb->state == VIDEOBUF_ERROR)
-			rc |= POLLOUT | POLLWRNORM;
-	}
-	if (dst_vb) {
-		poll_wait(file, &dst_vb->done, wait);
-		if (dst_vb->state == VIDEOBUF_DONE
-		    || dst_vb->state == VIDEOBUF_ERROR)
-			rc |= POLLIN | POLLRDNORM;
-	}
+	poll_wait(file, &src_q->done_wq, wait);
+	poll_wait(file, &dst_q->done_wq, wait);
+
+	spin_lock_irqsave(&src_q->done_lock, flags);
+	if (!list_empty(&src_q->done_list))
+		src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
+						done_entry);
+	spin_unlock_irqrestore(&src_q->done_lock, flags);
+
+	spin_lock_irqsave(&dst_q->done_lock, flags);
+	if (!list_empty(&dst_q->done_list))
+		src_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
+						done_entry);
+	spin_unlock_irqrestore(&dst_q->done_lock, flags);
+
+	if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
+			|| src_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLOUT | POLLWRNORM;
+
+	if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
+			|| dst_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLIN | POLLRDNORM;
 
 end:
 	mutex_unlock(&dst_q->vb_lock);
@@ -470,7 +470,7 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 			 struct vm_area_struct *vma)
 {
 	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-	struct videobuf_queue *vq;
+	struct vb2_queue *vq;
 
 	if (offset < DST_QUEUE_OFF_BASE) {
 		vq = v4l2_m2m_get_src_vq(m2m_ctx);
@@ -479,7 +479,7 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 		vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
 	}
 
-	return videobuf_mmap_mapper(vq, vma);
+	return vb2_mmap(vq, vma);
 }
 EXPORT_SYMBOL(v4l2_m2m_mmap);
 
@@ -531,21 +531,22 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_release);
  *
  * Usually called from driver's open() function.
  */
-struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev *m2m_dev,
-			void (*vq_init)(void *priv, struct videobuf_queue *,
-					enum v4l2_buf_type))
+struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev,
+				void *drv_priv, spinlock_t *drv_lock,
+				const struct vb2_ops *src_q_ops,
+				const struct vb2_alloc_ctx *src_q_alloc_ctx,
+				const struct vb2_ops *dst_q_ops,
+				const struct vb2_alloc_ctx *dst_q_alloc_ctx)
 {
 	struct v4l2_m2m_ctx *m2m_ctx;
 	struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx;
-
-	if (!vq_init)
-		return ERR_PTR(-EINVAL);
+	int ret;
 
 	m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL);
 	if (!m2m_ctx)
 		return ERR_PTR(-ENOMEM);
 
-	m2m_ctx->priv = priv;
+	m2m_ctx->priv = drv_priv;
 	m2m_ctx->m2m_dev = m2m_dev;
 
 	out_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
@@ -556,11 +557,20 @@ struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev *m2m_dev,
 
 	INIT_LIST_HEAD(&m2m_ctx->queue);
 
-	vq_init(priv, &out_q_ctx->q, V4L2_BUF_TYPE_VIDEO_OUTPUT);
-	vq_init(priv, &cap_q_ctx->q, V4L2_BUF_TYPE_VIDEO_CAPTURE);
-	out_q_ctx->q.priv_data = cap_q_ctx->q.priv_data = priv;
+	ret = vb2_queue_init(&out_q_ctx->q, src_q_ops, src_q_alloc_ctx,
+			drv_lock, V4L2_BUF_TYPE_VIDEO_OUTPUT, drv_priv);
+	if (ret)
+		goto err;
+
+	ret = vb2_queue_init(&cap_q_ctx->q, dst_q_ops, dst_q_alloc_ctx,
+			drv_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, drv_priv);
+	if (ret)
+		goto err;
 
 	return m2m_ctx;
+err:
+	kfree(m2m_ctx);
+	return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init);
 
@@ -572,7 +582,7 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init);
 void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
 {
 	struct v4l2_m2m_dev *m2m_dev;
-	struct videobuf_buffer *vb;
+	struct vb2_buffer *vb;
 	unsigned long flags;
 
 	m2m_dev = m2m_ctx->m2m_dev;
@@ -584,8 +594,6 @@ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
 		dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx);
 		vb = v4l2_m2m_next_dst_buf(m2m_ctx);
 		BUG_ON(NULL == vb);
-		wait_event(vb->done, vb->state != VIDEOBUF_ACTIVE
-				     && vb->state != VIDEOBUF_QUEUED);
 	} else if (m2m_ctx->job_flags & TRANS_QUEUED) {
 		list_del(&m2m_ctx->queue);
 		m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
@@ -597,11 +605,8 @@ void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
 		spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
 	}
 
-	videobuf_stop(&m2m_ctx->cap_q_ctx.q);
-	videobuf_stop(&m2m_ctx->out_q_ctx.q);
-
-	videobuf_mmap_free(&m2m_ctx->cap_q_ctx.q);
-	videobuf_mmap_free(&m2m_ctx->out_q_ctx.q);
+	vb2_queue_release(&m2m_ctx->cap_q_ctx.q);
+	vb2_queue_release(&m2m_ctx->out_q_ctx.q);
 
 	kfree(m2m_ctx);
 }
@@ -615,19 +620,17 @@ EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release);
  * Locking: Caller holds q->irqlock (taken by videobuf before calling buf_queue
  * callback in the driver).
  */
-void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue *vq,
-			struct videobuf_buffer *vb)
+void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb)
 {
 	struct v4l2_m2m_queue_ctx *q_ctx;
 
-	q_ctx = get_queue_ctx(m2m_ctx, vq->type);
+	q_ctx = get_queue_ctx(m2m_ctx, vb->vb2_queue->type);
 	if (!q_ctx)
 		return;
 
-	list_add_tail(&vb->queue, &q_ctx->rdy_queue);
+	list_add_tail(&vb->drv_entry, &q_ctx->rdy_queue);
 	q_ctx->num_rdy++;
-
-	vb->state = VIDEOBUF_QUEUED;
+	vb->state = VB2_BUF_STATE_QUEUED;
 }
 EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
 
diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
index 8d149f1..1b49997 100644
--- a/include/media/v4l2-mem2mem.h
+++ b/include/media/v4l2-mem2mem.h
@@ -17,7 +17,7 @@
 #ifndef _MEDIA_V4L2_MEM2MEM_H
 #define _MEDIA_V4L2_MEM2MEM_H
 
-#include <media/videobuf-core.h>
+#include <media/videobuf2-core.h>
 
 /**
  * struct v4l2_m2m_ops - mem-to-mem device driver callbacks
@@ -51,7 +51,7 @@ struct v4l2_m2m_dev;
 
 struct v4l2_m2m_queue_ctx {
 /* private: internal use only */
-	struct videobuf_queue	q;
+	struct vb2_queue	q;
 
 	/* Queue for buffers ready to be processed as soon as this
 	 * instance receives access to the device */
@@ -79,12 +79,18 @@ struct v4l2_m2m_ctx {
 
 void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev);
 
-struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
+struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
 				       enum v4l2_buf_type type);
 
 void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
 			 struct v4l2_m2m_ctx *m2m_ctx);
 
+static inline void
+v4l2_m2m_buf_done(struct vb2_buffer *buf, enum vb2_buffer_state state)
+{
+	vb2_buffer_done(buf, state);
+}
+
 int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 		     struct v4l2_requestbuffers *reqbufs);
 
@@ -110,13 +116,15 @@ int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
 struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops);
 void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev);
 
-struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev *m2m_dev,
-			void (*vq_init)(void *priv, struct videobuf_queue *,
-					enum v4l2_buf_type));
+struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev,
+				void *drv_priv, spinlock_t *drv_lock,
+				const struct vb2_ops *src_q_ops,
+				const struct vb2_alloc_ctx *src_q_alloc_ctx,
+				const struct vb2_ops *dst_q_ops,
+				const struct vb2_alloc_ctx *dst_q_alloc_ctx);
 void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx);
 
-void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue *vq,
-			struct videobuf_buffer *vb);
+void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb);
 
 /**
  * v4l2_m2m_num_src_bufs_ready() - return the number of source buffers ready for
@@ -138,7 +146,7 @@ unsigned int v4l2_m2m_num_dst_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx)
 	return m2m_ctx->out_q_ctx.num_rdy;
 }
 
-void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type);
+void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx);
 
 /**
  * v4l2_m2m_next_src_buf() - return next source buffer from the list of ready
@@ -146,7 +154,7 @@ void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type);
  */
 static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx)
 {
-	return v4l2_m2m_next_buf(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	return v4l2_m2m_next_buf(&m2m_ctx->out_q_ctx);
 }
 
 /**
@@ -155,29 +163,28 @@ static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx)
  */
 static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx)
 {
-	return v4l2_m2m_next_buf(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	return v4l2_m2m_next_buf(&m2m_ctx->cap_q_ctx);
 }
 
 /**
- * v4l2_m2m_get_src_vq() - return videobuf_queue for source buffers
+ * v4l2_m2m_get_src_vq() - return vb2_queue for source buffers
  */
 static inline
-struct videobuf_queue *v4l2_m2m_get_src_vq(struct v4l2_m2m_ctx *m2m_ctx)
+struct vb2_queue *v4l2_m2m_get_src_vq(struct v4l2_m2m_ctx *m2m_ctx)
 {
-	return v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	return &m2m_ctx->out_q_ctx.q;
 }
 
 /**
- * v4l2_m2m_get_dst_vq() - return videobuf_queue for destination buffers
+ * v4l2_m2m_get_dst_vq() - return vb2_queue for destination buffers
  */
 static inline
-struct videobuf_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx)
+struct vb2_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx)
 {
-	return v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	return &m2m_ctx->cap_q_ctx.q;
 }
 
-void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx,
-			  enum v4l2_buf_type type);
+void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx);
 
 /**
  * v4l2_m2m_src_buf_remove() - take off a source buffer from the list of ready
@@ -185,7 +192,7 @@ void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx,
  */
 static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx)
 {
-	return v4l2_m2m_buf_remove(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+	return v4l2_m2m_buf_remove(&m2m_ctx->out_q_ctx);
 }
 
 /**
@@ -194,7 +201,7 @@ static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx)
  */
 static inline void *v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx)
 {
-	return v4l2_m2m_buf_remove(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+	return v4l2_m2m_buf_remove(&m2m_ctx->cap_q_ctx);
 }
 
 #endif /* _MEDIA_V4L2_MEM2MEM_H */
-- 
1.7.2.1.97.g3235b.dirty


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

* [PATCH v1 4/7] v4l: videobuf2: add vmalloc allocator
  2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
                   ` (2 preceding siblings ...)
  2010-09-09  9:19 ` [PATCH v1 3/7] v4l: mem2mem: port to videobuf2 Pawel Osciak
@ 2010-09-09  9:19 ` Pawel Osciak
  2010-09-09  9:19 ` [PATCH v1 5/7] v4l: videobuf2: add DMA coherent allocator Pawel Osciak
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-09  9:19 UTC (permalink / raw)
  To: linux-media; +Cc: p.osciak, kyungmin.park, m.szyprowski, t.fujak

Add an implementation of contiguous virtual memory allocator and handling
routines for videobuf2, implemented on top of vmalloc()/vfree() calls.

Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/Kconfig             |    5 +
 drivers/media/video/Makefile            |    4 +
 drivers/media/video/videobuf2-vmalloc.c |  177 +++++++++++++++++++++++++++++++
 include/media/videobuf2-vmalloc.h       |   16 +++
 4 files changed, 202 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/videobuf2-vmalloc.c
 create mode 100644 include/media/videobuf2-vmalloc.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index c2f99d8..aae7a30 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -55,6 +55,11 @@ config VIDEOBUF2_CORE
 config VIDEOBUF2_MEMOPS
 	tristate
 
+config VIDEOBUF2_VMALLOC
+	select VIDEOBUF2_CORE
+	select VIDEOBUF2_GEN_MEMOPS
+	tristate
+
 
 #
 # Multimedia Video device configuration
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index ab5b521..b7c15ae 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -120,6 +120,10 @@ obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
 obj-$(CONFIG_VIDEOBUF2_CORE)		+= videobuf2-core.o
 obj-$(CONFIG_VIDEOBUF2_MEMOPS)		+= videobuf2-memops.o
 
+obj-$(CONFIG_VIDEOBUF2_VMALLOC)		+= videobuf2_vmalloc.o
+videobuf2_vmalloc-y			:= videobuf2-vmalloc.o \
+					   videobuf2-memops.o
+
 
 obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
 
diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c
new file mode 100644
index 0000000..3310900
--- /dev/null
+++ b/drivers/media/video/videobuf2-vmalloc.c
@@ -0,0 +1,177 @@
+/*
+ * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_vmalloc_conf {
+	struct vb2_alloc_ctx	alloc_ctx;
+};
+
+struct vb2_vmalloc_buf {
+	void			*vaddr;
+	unsigned long		size;
+	unsigned int		refcount;
+};
+
+static void *vb2_vmalloc_alloc(const struct vb2_alloc_ctx *alloc_ctx,
+				unsigned long size)
+{
+	struct vb2_vmalloc_buf *buf;
+
+	buf = kzalloc(sizeof *buf, GFP_KERNEL);
+	if (!buf)
+		return NULL;
+
+	buf->size = size;
+	buf->vaddr = vmalloc_user(buf->size);
+	if (!buf->vaddr) {
+		printk(KERN_ERR "vmalloc of size %ld failed\n", buf->size);
+		kfree(buf);
+		return NULL;
+	}
+
+	buf->refcount++;
+	printk(KERN_DEBUG "Allocated vmalloc buffer of size %ld at vaddr=%p\n",
+			buf->size, buf->vaddr);
+
+	return buf;
+}
+
+static void vb2_vmalloc_put(void *buf_priv)
+{
+	struct vb2_vmalloc_buf *buf = buf_priv;
+
+	buf->refcount--;
+
+	if (0 == buf->refcount) {
+		printk(KERN_DEBUG "%s: Freeing vmalloc mem at vaddr=%p\n",
+			__func__, buf->vaddr);
+		vfree(buf->vaddr);
+		kfree(buf);
+	}
+}
+
+static void *vb2_vmalloc_vaddr(void *buf_priv)
+{
+	struct vb2_vmalloc_buf *buf = buf_priv;
+
+	BUG_ON(!buf);
+
+	if (!buf->vaddr) {
+		printk(KERN_ERR "Address of an unallocated "
+				"plane requested\n");
+		return NULL;
+	}
+
+	return buf->vaddr;
+}
+
+static unsigned int vb2_vmalloc_num_users(void *buf_priv)
+{
+	struct vb2_vmalloc_buf *buf = buf_priv;
+
+	return buf->refcount;
+}
+
+/* TODO generalize and extract to core as much as possible */
+static void vb2_vmalloc_vm_open(struct vm_area_struct *vma)
+{
+	struct vb2_vmalloc_buf *buf = vma->vm_private_data;
+
+	printk(KERN_DEBUG "%s vmalloc_priv: %p, refcount: %d, "
+			"vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
+			vma->vm_start, vma->vm_end);
+
+	buf->refcount++;
+}
+
+static void vb2_vmalloc_vm_close(struct vm_area_struct *vma)
+{
+	struct vb2_vmalloc_buf *buf = vma->vm_private_data;
+
+	printk(KERN_DEBUG "%s vmalloc_priv: %p, refcount: %d, "
+			"vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
+			vma->vm_start, vma->vm_end);
+
+	vb2_vmalloc_put(buf);
+}
+
+static const struct vm_operations_struct vb2_vmalloc_vm_ops = {
+	.open = vb2_vmalloc_vm_open,
+	.close = vb2_vmalloc_vm_close,
+};
+
+static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+	struct vb2_vmalloc_buf *buf = buf_priv;
+	int ret;
+
+	if (!buf) {
+		printk(KERN_ERR "No memory to map\n");
+		return -EINVAL;
+	}
+
+	ret = remap_vmalloc_range(vma, buf->vaddr, 0);
+	if (ret) {
+		printk(KERN_ERR "Remapping vmalloc memory, error: %d\n", ret);
+		return ret;
+	}
+
+	vma->vm_flags		|= VM_DONTEXPAND | VM_RESERVED;
+	vma->vm_private_data	= buf;
+	vma->vm_ops		= &vb2_vmalloc_vm_ops;
+
+	vb2_vmalloc_vm_open(vma);
+
+	return 0;
+}
+
+static const struct vb2_mem_ops vb2_vmalloc_ops = {
+	.alloc		= vb2_vmalloc_alloc,
+	.put		= vb2_vmalloc_put,
+	.vaddr		= vb2_vmalloc_vaddr,
+	.mmap		= vb2_vmalloc_mmap,
+	.num_users	= vb2_vmalloc_num_users,
+};
+
+struct vb2_alloc_ctx *vb2_vmalloc_init(void)
+{
+	struct vb2_vmalloc_conf *conf;
+
+	conf = kzalloc(sizeof *conf, GFP_KERNEL);
+	if (!conf)
+		return ERR_PTR(-ENOMEM);
+
+	conf->alloc_ctx.mem_ops = &vb2_vmalloc_ops;
+
+	return &conf->alloc_ctx;
+}
+EXPORT_SYMBOL_GPL(vb2_vmalloc_init);
+
+void vb2_vmalloc_cleanup(struct vb2_alloc_ctx *alloc_ctx)
+{
+	struct vb2_vmalloc_conf *conf =
+		container_of(alloc_ctx, struct vb2_vmalloc_conf, alloc_ctx);
+
+	kfree(conf);
+}
+EXPORT_SYMBOL_GPL(vb2_vmalloc_cleanup);
+
+MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak");
+MODULE_LICENSE("GPL");
diff --git a/include/media/videobuf2-vmalloc.h b/include/media/videobuf2-vmalloc.h
new file mode 100644
index 0000000..7612f9f
--- /dev/null
+++ b/include/media/videobuf2-vmalloc.h
@@ -0,0 +1,16 @@
+/*
+ * videobuf2-vmalloc.h - vmalloc memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <media/videobuf2-core.h>
+
+struct vb2_alloc_ctx *vb2_vmalloc_init(void);
+void vb2_vmalloc_cleanup(struct vb2_alloc_ctx *alloc_ctx);
-- 
1.7.2.1.97.g3235b.dirty


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

* [PATCH v1 5/7] v4l: videobuf2: add DMA coherent allocator
  2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
                   ` (3 preceding siblings ...)
  2010-09-09  9:19 ` [PATCH v1 4/7] v4l: videobuf2: add vmalloc allocator Pawel Osciak
@ 2010-09-09  9:19 ` Pawel Osciak
  2010-09-09  9:19 ` [PATCH v1 6/7] v4l: vivi: port to videobuf2 Pawel Osciak
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-09  9:19 UTC (permalink / raw)
  To: linux-media; +Cc: p.osciak, kyungmin.park, m.szyprowski, t.fujak

Add an implementation of DMA coherent memory allocator and handling
routines for videobuf2, implemented on top of dma_alloc_coherent() call.

Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/Kconfig                  |    5 +
 drivers/media/video/Makefile                 |    4 +
 drivers/media/video/videobuf2-dma-coherent.c |  208 ++++++++++++++++++++++++++
 include/media/videobuf2-dma-coherent.h       |   16 ++
 4 files changed, 233 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/videobuf2-dma-coherent.c
 create mode 100644 include/media/videobuf2-dma-coherent.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index aae7a30..5286b2f 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -55,6 +55,11 @@ config VIDEOBUF2_CORE
 config VIDEOBUF2_MEMOPS
 	tristate
 
+config VIDEOBUF2_DMA_COHERENT
+	select VIDEOBUF2_CORE
+	select VIDEOBUF2_GEN_MEMOPS
+	tristate
+
 config VIDEOBUF2_VMALLOC
 	select VIDEOBUF2_CORE
 	select VIDEOBUF2_GEN_MEMOPS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index b7c15ae..20d359d 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -120,6 +120,10 @@ obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
 obj-$(CONFIG_VIDEOBUF2_CORE)		+= videobuf2-core.o
 obj-$(CONFIG_VIDEOBUF2_MEMOPS)		+= videobuf2-memops.o
 
+obj-$(CONFIG_VIDEOBUF2_DMA_COHERENT)	+= videobuf2_dma_coherent.o
+videobuf2_dma_coherent-y		:= videobuf2-dma-coherent.o \
+					   videobuf2-memops.o
+
 obj-$(CONFIG_VIDEOBUF2_VMALLOC)		+= videobuf2_vmalloc.o
 videobuf2_vmalloc-y			:= videobuf2-vmalloc.o \
 					   videobuf2-memops.o
diff --git a/drivers/media/video/videobuf2-dma-coherent.c b/drivers/media/video/videobuf2-dma-coherent.c
new file mode 100644
index 0000000..8c77f9d
--- /dev/null
+++ b/drivers/media/video/videobuf2-dma-coherent.c
@@ -0,0 +1,208 @@
+/*
+ * videobuf2-dma-coherent.c - DMA coherent memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_dc_conf {
+	struct vb2_alloc_ctx	alloc_ctx;
+	struct device		*dev;
+};
+
+struct vb2_dc_buf {
+	struct vb2_dc_conf	*conf;
+	void			*vaddr;
+	dma_addr_t		paddr;
+	unsigned long		size;
+	unsigned int		refcount;
+	struct vm_area_struct	*vma;
+};
+
+struct vb2_alloc_ctx *vb2_dma_coherent_init(struct device *dev)
+{
+	struct vb2_dc_conf *conf;
+
+	conf = kzalloc(sizeof *conf, GFP_KERNEL);
+	if (!conf)
+		return ERR_PTR(-ENOMEM);
+
+	conf->dev = dev;
+
+	return &conf->alloc_ctx;
+}
+EXPORT_SYMBOL_GPL(vb2_dma_coherent_init);
+
+void vb2_dma_coherent_cleanup(struct vb2_alloc_ctx *alloc_ctx)
+{
+	struct vb2_dc_conf *conf =
+		container_of(alloc_ctx, struct vb2_dc_conf, alloc_ctx);
+
+	kfree(conf);
+}
+EXPORT_SYMBOL_GPL(vb2_dma_coherent_cleanup);
+
+static void *vb2_dma_coherent_alloc(const struct vb2_alloc_ctx *alloc_ctx,
+					unsigned long size)
+{
+	struct vb2_dc_conf *conf =
+		container_of(alloc_ctx, struct vb2_dc_conf, alloc_ctx);
+	struct vb2_dc_buf *buf;
+
+	buf = kzalloc(sizeof *buf, GFP_KERNEL);
+	if (!buf)
+		return ERR_PTR(-ENOMEM);;
+
+	buf->vaddr = dma_alloc_coherent(conf->dev, buf->size,
+					&buf->paddr, GFP_KERNEL);
+	if (!buf->vaddr) {
+		dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n",
+			buf->size);
+		kfree(buf);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	buf->conf = conf;
+	buf->size = size;
+
+	return buf;
+}
+
+static void vb2_dma_coherent_put(void *buf_priv)
+{
+	struct vb2_dc_buf *buf = buf_priv;
+
+	buf->refcount--;
+
+	if (0 == buf->refcount) {
+		dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr,
+					buf->paddr);
+		kfree(buf);
+	}
+}
+
+static unsigned long vb2_dma_coherent_paddr(void *buf_priv)
+{
+	struct vb2_dc_buf *buf = buf_priv;
+
+	return buf->paddr;
+}
+
+static unsigned int vb2_dma_coherent_num_users(void *buf_priv)
+{
+	struct vb2_dc_buf *buf = buf_priv;
+
+	return buf->refcount;
+}
+
+static void vb2_dma_coherent_vm_open(struct vm_area_struct *vma)
+{
+	struct vb2_dc_buf *buf = vma->vm_private_data;
+
+	printk(KERN_DEBUG "%s cma_priv: %p, refcount: %d, "
+			"vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
+			vma->vm_start, vma->vm_end);
+
+	buf->refcount++;
+}
+
+static void vb2_dma_coherent_vm_close(struct vm_area_struct *vma)
+{
+	struct vb2_dc_buf *buf = vma->vm_private_data;
+
+	printk(KERN_DEBUG "%s cma_priv: %p, refcount: %d, "
+			"vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
+			vma->vm_start, vma->vm_end);
+
+	vb2_dma_coherent_put(buf);
+}
+
+static const struct vm_operations_struct vb2_dma_coherent_vm_ops = {
+	.open = vb2_dma_coherent_vm_open,
+	.close = vb2_dma_coherent_vm_close,
+};
+
+static int vb2_dma_coherent_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+	struct vb2_dc_buf *buf = buf_priv;
+
+	if (!buf) {
+		printk(KERN_ERR "No memory to map\n");
+		return -EINVAL;
+	}
+
+	return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
+					&vb2_dma_coherent_vm_ops, buf);
+}
+
+static void *vb2_dma_coherent_get_userptr(unsigned long vaddr,
+						unsigned long size)
+{
+	struct vb2_dc_buf *buf;
+	unsigned long paddr = 0;
+	int ret;
+
+	buf = kzalloc(sizeof *buf, GFP_KERNEL);
+	if (!buf)
+		return ERR_PTR(-ENOMEM);
+
+	buf->vma = vb2_get_userptr(vaddr);
+	if (!buf->vma) {
+		printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
+				vaddr);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = vb2_contig_verify_userptr(buf->vma, vaddr, size, &paddr);
+	if (ret) {
+		vb2_put_userptr(buf->vma);
+		goto done;
+	}
+
+	buf->size = size;
+	buf->paddr = paddr;
+
+	return buf;
+
+done:
+	kfree(buf);
+	return ERR_PTR(ret);
+}
+
+static void vb2_dma_coherent_put_userptr(void *mem_priv)
+{
+	struct vb2_dc_buf *buf = mem_priv;
+
+	if (!buf)
+		return;
+
+	vb2_put_userptr(buf->vma);
+	kfree(buf);
+}
+
+const struct vb2_mem_ops vb2_dma_coherent_ops = {
+	.alloc		= vb2_dma_coherent_alloc,
+	.put		= vb2_dma_coherent_put,
+	.paddr		= vb2_dma_coherent_paddr,
+	.mmap		= vb2_dma_coherent_mmap,
+	.get_userptr	= vb2_dma_coherent_get_userptr,
+	.put_userptr	= vb2_dma_coherent_put_userptr,
+	.num_users	= vb2_dma_coherent_num_users,
+};
+
+MODULE_DESCRIPTION("DMA-coherent memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak");
+MODULE_LICENSE("GPL");
+
diff --git a/include/media/videobuf2-dma-coherent.h b/include/media/videobuf2-dma-coherent.h
new file mode 100644
index 0000000..c7fce51
--- /dev/null
+++ b/include/media/videobuf2-dma-coherent.h
@@ -0,0 +1,16 @@
+/*
+ * videobuf2-dma-coherent.h - DMA coherent memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <media/videobuf2-core.h>
+
+void *vb2_dma_coherent_init(struct device *dev);
+void vb2_dma_coherent_cleanup(struct vb2_alloc_ctx *alloc_ctx);
-- 
1.7.2.1.97.g3235b.dirty


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

* [PATCH v1 6/7] v4l: vivi: port to videobuf2
  2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
                   ` (4 preceding siblings ...)
  2010-09-09  9:19 ` [PATCH v1 5/7] v4l: videobuf2: add DMA coherent allocator Pawel Osciak
@ 2010-09-09  9:19 ` Pawel Osciak
  2010-09-09  9:19 ` [PATCH v1 7/7] v4l: videobuf2: add CMA allocator Pawel Osciak
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-09  9:19 UTC (permalink / raw)
  To: linux-media; +Cc: p.osciak, kyungmin.park, m.szyprowski, t.fujak

Make vivi use videobuf2 in place of videobuf.

Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/Kconfig |    2 +-
 drivers/media/video/vivi.c  |  248 +++++++++++++++++++++----------------------
 2 files changed, 124 insertions(+), 126 deletions(-)

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 5286b2f..c2ea549 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -558,7 +558,7 @@ config VIDEO_VIVI
 	depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
 	depends on (FRAMEBUFFER_CONSOLE || STI_CONSOLE) && FONTS
 	select FONT_8x16
-	select VIDEOBUF_VMALLOC
+	select VIDEOBUF2_VMALLOC
 	default n
 	---help---
 	  Enables a virtual video driver. This device shows a color bar
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index e17b6fe..b2dea81 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -7,6 +7,9 @@
  *      John Sokol <sokol--a.t--videotechnology.com>
  *      http://v4l.videotechnology.com/
  *
+ *      Conversion to videobuf2 by Pawel Osciak <p.osciak@samsung.com>
+ *      Copyright (c) 2010 Samsung Electronics
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the BSD Licence, GNU General Public License
  * as published by the Free Software Foundation; either version 2 of the
@@ -26,7 +29,7 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
 #include <linux/freezer.h>
 #endif
-#include <media/videobuf-vmalloc.h>
+#include <media/videobuf2-vmalloc.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-common.h>
@@ -42,7 +45,7 @@
 #define MAX_HEIGHT 1200
 
 #define VIVI_MAJOR_VERSION 0
-#define VIVI_MINOR_VERSION 7
+#define VIVI_MINOR_VERSION 8
 #define VIVI_RELEASE 0
 #define VIVI_VERSION \
 	KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
@@ -70,6 +73,9 @@ MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
 /* Global font descriptor */
 static const u8 *font8x16;
 
+/* Videobuf2 allocator/memory handler context */
+static struct vb2_alloc_ctx *alloc_ctx;
+
 #define dprintk(dev, level, fmt, arg...) \
 	v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
 
@@ -141,7 +147,7 @@ struct sg_to_addr {
 /* buffer for one video frame */
 struct vivi_buffer {
 	/* common v4l buffer stuff -- must be first */
-	struct videobuf_buffer vb;
+	struct vb2_buffer	vb;
 
 	struct vivi_fmt        *fmt;
 };
@@ -190,7 +196,9 @@ struct vivi_dev {
 	/* video capture */
 	struct vivi_fmt            *fmt;
 	unsigned int               width, height;
-	struct videobuf_queue      vb_vidq;
+	struct vb2_queue	   vb_vidq;
+	enum v4l2_field		   field;
+	unsigned int		   field_count;
 
 	unsigned long 		   generating;
 	u8 			   bars[9][3];
@@ -443,10 +451,10 @@ static void gen_text(struct vivi_dev *dev, char *basep,
 
 static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
 {
-	int hmax = buf->vb.height;
-	int wmax = buf->vb.width;
+	int hmax = dev->width;
+	int wmax = dev->height;
 	struct timeval ts;
-	void *vbuf = videobuf_to_vmalloc(&buf->vb);
+	void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
 	unsigned ms;
 	char str[100];
 	int h, line = 1;
@@ -483,11 +491,11 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
 
 	dev->mv_count += 2;
 
-	/* Advice that buffer was filled */
-	buf->vb.field_count++;
+	buf->vb.v4l2_buf.field = dev->field;
+	dev->field_count++;
+	buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
 	do_gettimeofday(&ts);
-	buf->vb.ts = ts;
-	buf->vb.state = VIDEOBUF_DONE;
+	buf->vb.v4l2_buf.timestamp = ts;
 }
 
 static void vivi_thread_tick(struct vivi_dev *dev)
@@ -504,23 +512,21 @@ static void vivi_thread_tick(struct vivi_dev *dev)
 		goto unlock;
 	}
 
-	buf = list_entry(dma_q->active.next,
-			 struct vivi_buffer, vb.queue);
-
-	/* Nobody is waiting on this buffer, return */
-	if (!waitqueue_active(&buf->vb.done))
+	/* If nobody is waiting for a buffer, return */
+	if (!vb2_has_consumers(&dev->vb_vidq))
 		goto unlock;
 
-	list_del(&buf->vb.queue);
+	buf = list_entry(dma_q->active.next, struct vivi_buffer, vb.drv_entry);
+	list_del(&buf->vb.drv_entry);
 
-	do_gettimeofday(&buf->vb.ts);
+	do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
 
 	/* Fill buffer */
 	vivi_fillbuff(dev, buf);
 	dprintk(dev, 1, "filled buffer %p\n", buf);
 
-	wake_up(&buf->vb.done);
-	dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i);
+	vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+	dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
 unlock:
 	spin_unlock_irqrestore(&dev->slock, flags);
 }
@@ -619,8 +625,6 @@ static void vivi_stop_generating(struct file *file)
 		kthread_stop(dma_q->kthread);
 		dma_q->kthread = NULL;
 	}
-	videobuf_stop(&dev->vb_vidq);
-	videobuf_mmap_free(&dev->vb_vidq);
 }
 
 static int vivi_is_generating(struct vivi_dev *dev)
@@ -631,108 +635,119 @@ static int vivi_is_generating(struct vivi_dev *dev)
 /* ------------------------------------------------------------------
 	Videobuf operations
    ------------------------------------------------------------------*/
-static int
-buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
+static int queue_negotiate(struct vb2_queue *vq, unsigned int *num_buffers,
+				unsigned int *num_planes)
 {
-	struct vivi_dev *dev = vq->priv_data;
+	struct vivi_dev *dev = vb2_get_drv_priv(vq);
+	unsigned long size;
+
+	size = dev->width * dev->height * 2;
+
+	if (0 == *num_buffers)
+		*num_buffers = 32;
+
+	while (size * *num_buffers > vid_limit * 1024 * 1024)
+		(*num_buffers)--;
 
-	*size = dev->width * dev->height * 2;
+	*num_planes = 1;
+
+	dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
+		*num_buffers, size);
+
+	return 0;
+}
+
+static int plane_setup(struct vb2_queue *vq, unsigned int plane_no,
+			unsigned long *plane_size)
+{
+	struct vivi_dev *dev = vb2_get_drv_priv(vq);
 
-	if (0 == *count)
-		*count = 32;
+	if (plane_no > 0)
+		return -EINVAL;
 
-	while (*size * *count > vid_limit * 1024 * 1024)
-		(*count)--;
+	*plane_size = dev->width * dev->height * 2;
 
-	dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__,
-		*count, *size);
+	dprintk(dev, 1, "%s: plane: %d, size: %ld\n",
+			__func__, plane_no, *plane_size);
 
 	return 0;
 }
 
-static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf)
+static int buffer_init(struct vb2_buffer *vb)
 {
-	struct vivi_dev *dev = vq->priv_data;
+	struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+	BUG_ON(NULL == dev->fmt);
 
-	dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state);
+	/*
+	 * This callback is called once per buffer, after its allocation.
+	 *
+	 * Vivi does not allow changing format during streaming, but it is
+	 * possible to do so when streaming is paused (i.e. in streamoff state).
+	 * Buffers however are not freed when going into streamoff and so
+	 * buffer size verification has to be done in buffer_prepare, on each
+	 * qbuf.
+	 * It would be best to move verification code here to buf_init and
+	 * s_fmt though.
+	 */
 
-	videobuf_vmalloc_free(&buf->vb);
-	dprintk(dev, 1, "free_buffer: freed\n");
-	buf->vb.state = VIDEOBUF_NEEDS_INIT;
+	return 0;
 }
 
-static int
-buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
-						enum v4l2_field field)
+static int buffer_prepare(struct vb2_buffer *vb)
 {
-	struct vivi_dev *dev = vq->priv_data;
+	struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
 	struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
-	int rc;
+	unsigned long size;
 
-	dprintk(dev, 1, "%s, field=%d\n", __func__, field);
+	dprintk(dev, 1, "%s, field=%d\n", __func__, vb->v4l2_buf.field);
 
 	BUG_ON(NULL == dev->fmt);
 
+	/*
+	 * Theses properties only change when queue is idle, see s_fmt.
+	 * The below checks should not be performed here, on each
+	 * buffer_prepare (i.e. on each qbuf). Most of the code in this function
+	 * should thus be moved to buffer_init and s_fmt.
+	 */
 	if (dev->width  < 48 || dev->width  > MAX_WIDTH ||
 	    dev->height < 32 || dev->height > MAX_HEIGHT)
 		return -EINVAL;
 
-	buf->vb.size = dev->width * dev->height * 2;
-	if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+	size = dev->width * dev->height * 2;
+	if (vb2_plane_size(vb, 0) < size) {
+		dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
+				__func__, vb2_plane_size(vb, 0), size);
 		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(&buf->vb, 0, size);
 
-	/* These properties only change when queue is idle, see s_fmt */
-	buf->fmt       = dev->fmt;
-	buf->vb.width  = dev->width;
-	buf->vb.height = dev->height;
-	buf->vb.field  = field;
+	buf->fmt = dev->fmt;
 
 	precalculate_bars(dev);
 	precalculate_line(dev);
 
-	if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
-		rc = videobuf_iolock(vq, &buf->vb, NULL);
-		if (rc < 0)
-			goto fail;
-	}
-
-	buf->vb.state = VIDEOBUF_PREPARED;
 	return 0;
-
-fail:
-	free_buffer(vq, buf);
-	return rc;
 }
 
-static void
-buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+static void buffer_queue(struct vb2_buffer *vb)
 {
-	struct vivi_dev *dev = vq->priv_data;
+	struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
 	struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
 	struct vivi_dmaqueue *vidq = &dev->vidq;
 
 	dprintk(dev, 1, "%s\n", __func__);
 
-	buf->vb.state = VIDEOBUF_QUEUED;
-	list_add_tail(&buf->vb.queue, &vidq->active);
+	list_add_tail(&buf->vb.drv_entry, &vidq->active);
 }
 
-static void buffer_release(struct videobuf_queue *vq,
-			   struct videobuf_buffer *vb)
-{
-	struct vivi_dev *dev = vq->priv_data;
-	struct vivi_buffer *buf  = container_of(vb, struct vivi_buffer, vb);
-
-	dprintk(dev, 1, "%s\n", __func__);
-
-	free_buffer(vq, buf);
-}
-
-static struct videobuf_queue_ops vivi_video_qops = {
-	.buf_setup      = buffer_setup,
-	.buf_prepare    = buffer_prepare,
-	.buf_queue      = buffer_queue,
-	.buf_release    = buffer_release,
+static struct vb2_ops vivi_video_qops = {
+	.queue_negotiate	= queue_negotiate,
+	.plane_setup		= plane_setup,
+	.buf_init		= buffer_init,
+	.buf_prepare		= buffer_prepare,
+	.buf_queue		= buffer_queue,
 };
 
 /* ------------------------------------------------------------------
@@ -774,7 +789,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 
 	f->fmt.pix.width        = dev->width;
 	f->fmt.pix.height       = dev->height;
-	f->fmt.pix.field        = dev->vb_vidq.field;
+	f->fmt.pix.field        = dev->field;
 	f->fmt.pix.pixelformat  = dev->fmt->fourcc;
 	f->fmt.pix.bytesperline =
 		(f->fmt.pix.width * dev->fmt->depth) >> 3;
@@ -820,7 +835,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 					struct v4l2_format *f)
 {
 	struct vivi_dev *dev = video_drvdata(file);
-	struct videobuf_queue *q = &dev->vb_vidq;
+	struct vb2_queue *q = &dev->vb_vidq;
 
 	int ret = vidioc_try_fmt_vid_cap(file, priv, f);
 	if (ret < 0)
@@ -837,7 +852,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 	dev->fmt = get_format(f);
 	dev->width = f->fmt.pix.width;
 	dev->height = f->fmt.pix.height;
-	dev->vb_vidq.field = f->fmt.pix.field;
+	dev->field = f->fmt.pix.field;
 	ret = 0;
 out:
 	mutex_unlock(&q->vb_lock);
@@ -849,53 +864,41 @@ static int vidioc_reqbufs(struct file *file, void *priv,
 {
 	struct vivi_dev *dev = video_drvdata(file);
 
-	return videobuf_reqbufs(&dev->vb_vidq, p);
+	return vb2_reqbufs(&dev->vb_vidq, p);
 }
 
 static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
 	struct vivi_dev *dev = video_drvdata(file);
 
-	return videobuf_querybuf(&dev->vb_vidq, p);
+	return vb2_querybuf(&dev->vb_vidq, p);
 }
 
 static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
 	struct vivi_dev *dev = video_drvdata(file);
 
-	return videobuf_qbuf(&dev->vb_vidq, p);
+	return vb2_qbuf(&dev->vb_vidq, p);
 }
 
 static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
 {
 	struct vivi_dev *dev = video_drvdata(file);
 
-	return videobuf_dqbuf(&dev->vb_vidq, p,
-				file->f_flags & O_NONBLOCK);
-}
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
-{
-	struct vivi_dev *dev = video_drvdata(file);
-
-	return videobuf_cgmbuf(&dev->vb_vidq, mbuf, 8);
+	return vb2_dqbuf(&dev->vb_vidq, p, file->f_flags & O_NONBLOCK);
 }
-#endif
 
 static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
 {
 	struct vivi_dev *dev = video_drvdata(file);
 	int ret;
 
-	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		return -EINVAL;
-	ret = videobuf_streamon(&dev->vb_vidq);
+	ret = vb2_streamon(&dev->vb_vidq, i);
 	if (ret)
 		return ret;
 
 	vivi_start_generating(file);
-	return 0;
+	return ret;
 }
 
 static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
@@ -903,9 +906,7 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
 	struct vivi_dev *dev = video_drvdata(file);
 	int ret;
 
-	if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
-		return -EINVAL;
-	ret = videobuf_streamoff(&dev->vb_vidq);
+	ret = vb2_streamoff(&dev->vb_vidq, i);
 	if (!ret)
 		vivi_stop_generating(file);
 	return ret;
@@ -1031,26 +1032,16 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 	File operations for the device
    ------------------------------------------------------------------*/
 
-static ssize_t
-vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
-	struct vivi_dev *dev = video_drvdata(file);
-
-	vivi_start_generating(file);
-	return videobuf_read_stream(&dev->vb_vidq, data, count, ppos, 0,
-					file->f_flags & O_NONBLOCK);
-}
-
 static unsigned int
 vivi_poll(struct file *file, struct poll_table_struct *wait)
 {
 	struct vivi_dev *dev = video_drvdata(file);
-	struct videobuf_queue *q = &dev->vb_vidq;
+	struct vb2_queue *q = &dev->vb_vidq;
 
 	dprintk(dev, 1, "%s\n", __func__);
 
 	vivi_start_generating(file);
-	return videobuf_poll_stream(file, q, wait);
+	return vb2_poll(q, file, wait);
 }
 
 static int vivi_close(struct file *file)
@@ -1059,6 +1050,7 @@ static int vivi_close(struct file *file)
 	struct vivi_dev *dev = video_drvdata(file);
 
 	vivi_stop_generating(file);
+	vb2_queue_release(&dev->vb_vidq);
 
 	dprintk(dev, 1, "close called (dev=%s)\n",
 		video_device_node_name(vdev));
@@ -1072,7 +1064,7 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
 
 	dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
 
-	ret = videobuf_mmap_mapper(&dev->vb_vidq, vma);
+	ret = vb2_mmap(&dev->vb_vidq, vma);
 
 	dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
 		(unsigned long)vma->vm_start,
@@ -1084,7 +1076,6 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
 static const struct v4l2_file_operations vivi_fops = {
 	.owner		= THIS_MODULE,
 	.release        = vivi_close,
-	.read           = vivi_read,
 	.poll		= vivi_poll,
 	.ioctl          = video_ioctl2, /* V4L2 ioctl handler */
 	.mmap           = vivi_mmap,
@@ -1145,6 +1136,8 @@ static int vivi_release(void)
 		kfree(dev);
 	}
 
+	vb2_vmalloc_cleanup(alloc_ctx);
+
 	return 0;
 }
 
@@ -1173,10 +1166,8 @@ static int __init vivi_create_instance(int inst)
 	dev->saturation = 127;
 	dev->hue = 0;
 
-	videobuf_queue_vmalloc_init(&dev->vb_vidq, &vivi_video_qops,
-			NULL, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
-			V4L2_FIELD_INTERLACED,
-			sizeof(struct vivi_buffer), dev);
+	vb2_queue_init(&dev->vb_vidq, &vivi_video_qops, alloc_ctx,
+			&dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, dev);
 
 	/* init video dma queues */
 	INIT_LIST_HEAD(&dev->vidq.active);
@@ -1238,6 +1229,13 @@ static int __init vivi_init(void)
 	}
 	font8x16 = font->data;
 
+	/* Initialize vmalloc allocator */
+	alloc_ctx = vb2_vmalloc_init();
+	if (IS_ERR(alloc_ctx)) {
+		printk(KERN_ERR "vivi: allocator init failed\n");
+		return PTR_ERR(alloc_ctx);
+	}
+
 	if (n_devs <= 0)
 		n_devs = 1;
 
-- 
1.7.2.1.97.g3235b.dirty


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

* [PATCH v1 7/7] v4l: videobuf2: add CMA allocator
  2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
                   ` (5 preceding siblings ...)
  2010-09-09  9:19 ` [PATCH v1 6/7] v4l: vivi: port to videobuf2 Pawel Osciak
@ 2010-09-09  9:19 ` Pawel Osciak
  2010-09-15  8:55   ` han jonghun
  2010-09-09  9:26 ` [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
  2010-09-09 17:53 ` Mauro Carvalho Chehab
  8 siblings, 1 reply; 24+ messages in thread
From: Pawel Osciak @ 2010-09-09  9:19 UTC (permalink / raw)
  To: linux-media; +Cc: p.osciak, kyungmin.park, m.szyprowski, t.fujak

Add support for the CMA contiguous memory allocator to videobuf2.

Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/media/video/Kconfig         |    5 +
 drivers/media/video/Makefile        |    2 +
 drivers/media/video/videobuf2-cma.c |  250 +++++++++++++++++++++++++++++++++++
 include/media/videobuf2-cma.h       |   25 ++++
 4 files changed, 282 insertions(+), 0 deletions(-)
 create mode 100644 drivers/media/video/videobuf2-cma.c
 create mode 100644 include/media/videobuf2-cma.h

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index c2ea549..b63f377 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -65,6 +65,11 @@ config VIDEOBUF2_VMALLOC
 	select VIDEOBUF2_GEN_MEMOPS
 	tristate
 
+config VIDEOBUF2_CMA
+	depends on CMA
+	select VIDEOBUF2_CORE
+	select VIDEOBUF2_GEN_MEMOPS
+	tristate
 
 #
 # Multimedia Video device configuration
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 20d359d..4146700 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -128,6 +128,8 @@ obj-$(CONFIG_VIDEOBUF2_VMALLOC)		+= videobuf2_vmalloc.o
 videobuf2_vmalloc-y			:= videobuf2-vmalloc.o \
 					   videobuf2-memops.o
 
+obj-$(CONFIG_VIDEOBUF2_CMA)		+= videobuf2_cma.o
+videobuf2_cma-y				:= videobuf2-cma.o videobuf2-memops.o
 
 obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
 
diff --git a/drivers/media/video/videobuf2-cma.c b/drivers/media/video/videobuf2-cma.c
new file mode 100644
index 0000000..c51e5a8
--- /dev/null
+++ b/drivers/media/video/videobuf2-cma.c
@@ -0,0 +1,250 @@
+/*
+ * videobuf2-cma.c - CMA memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/cma.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_cma_conf {
+	struct vb2_alloc_ctx	alloc_ctx;
+	struct device		*dev;
+	const char		*type;
+	unsigned long		alignment;
+};
+
+struct vb2_cma_buf {
+	struct vb2_cma_conf	*conf;
+	dma_addr_t		paddr;
+	unsigned long		size;
+	unsigned int		refcount;
+	struct vm_area_struct	*vma;
+};
+
+static void *vb2_cma_alloc(const struct vb2_alloc_ctx *alloc_ctx,
+				unsigned long size)
+{
+	struct vb2_cma_conf *conf =
+		container_of(alloc_ctx, struct vb2_cma_conf, alloc_ctx);
+	struct vb2_cma_buf *buf;
+
+	buf = kzalloc(sizeof *buf, GFP_KERNEL);
+	if (!buf)
+		return ERR_PTR(-ENOMEM);
+
+	buf->paddr = cma_alloc(conf->dev, conf->type, size, conf->alignment);
+	if (!buf->paddr) {
+		printk(KERN_ERR "cma_alloc of size %ld failed\n", size);
+		kfree(buf);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	buf->conf = conf;
+	buf->size = size;
+	buf->refcount++;
+
+	printk(KERN_DEBUG "Allocated cma mem of size %ld at paddr=0x%08x\n",
+			buf->size, buf->paddr);
+
+	return buf;
+}
+
+static void vb2_cma_put(void *buf_priv)
+{
+	struct vb2_cma_buf *buf = buf_priv;
+
+	buf->refcount--;
+
+	if (0 == buf->refcount) {
+		cma_free(buf->paddr);
+		kfree(buf);
+	}
+}
+
+static unsigned long vb2_cma_paddr(void *buf_priv)
+{
+	struct vb2_cma_buf *buf = buf_priv;
+
+	return buf->paddr;
+}
+
+static unsigned int vb2_cma_num_users(void *buf_priv)
+{
+	struct vb2_cma_buf *buf = buf_priv;
+
+	return buf->refcount;
+}
+
+static void vb2_cma_vm_open(struct vm_area_struct *vma)
+{
+	struct vb2_cma_buf *buf = vma->vm_private_data;
+
+	printk(KERN_DEBUG "%s cma_priv: %p, refcount: %d, "
+			"vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
+			vma->vm_start, vma->vm_end);
+
+	buf->refcount++;
+}
+
+static void vb2_cma_vm_close(struct vm_area_struct *vma)
+{
+	struct vb2_cma_buf *buf = vma->vm_private_data;
+
+	printk(KERN_DEBUG "%s cma_priv: %p, refcount: %d, "
+			"vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
+			vma->vm_start, vma->vm_end);
+
+	vb2_cma_put(buf);
+}
+
+static const struct vm_operations_struct vb2_cma_vm_ops = {
+	.open = vb2_cma_vm_open,
+	.close = vb2_cma_vm_close,
+};
+
+static int vb2_cma_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+	struct vb2_cma_buf *buf = buf_priv;
+
+	if (!buf) {
+		printk(KERN_ERR "No memory to map\n");
+		return -EINVAL;
+	}
+
+	return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
+					&vb2_cma_vm_ops, buf);
+}
+
+static void *vb2_cma_get_userptr(unsigned long vaddr, unsigned long size)
+{
+	struct vb2_cma_buf *buf;
+	unsigned long paddr = 0;
+	int ret;
+
+	buf = kzalloc(sizeof *buf, GFP_KERNEL);
+	if (!buf)
+		return ERR_PTR(-ENOMEM);
+
+	buf->vma = vb2_get_userptr(vaddr);
+	if (!buf->vma) {
+		printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
+				vaddr);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = vb2_contig_verify_userptr(buf->vma, vaddr, size, &paddr);
+	if (ret) {
+		vb2_put_userptr(buf->vma);
+		goto done;
+	}
+
+	buf->size = size;
+	buf->paddr = paddr;
+
+	return buf;
+
+done:
+	kfree(buf);
+	return ERR_PTR(ret);
+}
+
+static void vb2_cma_put_userptr(void *mem_priv)
+{
+	struct vb2_cma_buf *buf = mem_priv;
+
+	if (!buf)
+		return;
+
+	vb2_put_userptr(buf->vma);
+	kfree(buf);
+}
+
+static const struct vb2_mem_ops vb2_cma_ops = {
+	.alloc		= vb2_cma_alloc,
+	.put		= vb2_cma_put,
+	.paddr		= vb2_cma_paddr,
+	.mmap		= vb2_cma_mmap,
+	.get_userptr	= vb2_cma_get_userptr,
+	.put_userptr	= vb2_cma_put_userptr,
+	.num_users	= vb2_cma_num_users,
+};
+
+struct vb2_alloc_ctx *vb2_cma_init(struct device *dev, const char *type,
+					unsigned long alignment)
+{
+	struct vb2_cma_conf *conf;
+
+	conf = kzalloc(sizeof *conf, GFP_KERNEL);
+	if (!conf)
+		return ERR_PTR(-ENOMEM);
+
+	conf->dev = dev;
+	conf->type = type;
+	conf->alignment = alignment;
+	conf->alloc_ctx.mem_ops = &vb2_cma_ops;
+
+	return &conf->alloc_ctx;
+}
+EXPORT_SYMBOL_GPL(vb2_cma_init);
+
+void vb2_cma_cleanup(struct vb2_alloc_ctx *alloc_ctx)
+{
+	struct vb2_cma_conf *conf =
+		container_of(alloc_ctx, struct vb2_cma_conf, alloc_ctx);
+
+	kfree(conf);
+}
+EXPORT_SYMBOL_GPL(vb2_cma_cleanup);
+
+struct vb2_alloc_ctx **vb2_cma_init_multi(struct device *dev,
+					  unsigned int num_planes,
+					  const char *types[],
+					  unsigned long alignments[])
+{
+	struct vb2_alloc_ctx	**alloc_ctxes;
+	struct vb2_cma_conf	*cma_conf;
+	unsigned int i;
+
+	alloc_ctxes = kzalloc((sizeof *alloc_ctxes + sizeof *cma_conf)
+				* num_planes, GFP_KERNEL);
+	if (!alloc_ctxes)
+		return ERR_PTR(-ENOMEM);
+
+	cma_conf = (void *)(alloc_ctxes + num_planes);
+
+	for (i = 0; i < num_planes; ++i, ++cma_conf) {
+		alloc_ctxes[i] = &cma_conf->alloc_ctx;
+		cma_conf->dev = dev;
+		cma_conf->type = types[i];
+		cma_conf->alignment = alignments[i];
+		cma_conf->alloc_ctx.mem_ops = &vb2_cma_ops;
+	}
+
+	return alloc_ctxes;
+}
+EXPORT_SYMBOL_GPL(vb2_cma_init_multi);
+
+void vb2_cma_cleanup_multi(struct vb2_alloc_ctx **alloc_ctxes)
+{
+	kfree(alloc_ctxes);
+}
+EXPORT_SYMBOL_GPL(vb2_cma_cleanup_multi);
+
+MODULE_DESCRIPTION("CMA allocator handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak");
+MODULE_LICENSE("GPL");
diff --git a/include/media/videobuf2-cma.h b/include/media/videobuf2-cma.h
new file mode 100644
index 0000000..557eeb0
--- /dev/null
+++ b/include/media/videobuf2-cma.h
@@ -0,0 +1,25 @@
+/*
+ * videobuf2-cma.h - CMA memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+struct vb2_alloc_ctx *vb2_cma_init(struct device *dev, const char *type,
+					unsigned long alignment);
+void vb2_cma_cleanup(struct vb2_alloc_ctx *alloc_ctx);
+
+struct vb2_alloc_ctx **vb2_cma_init_multi(struct device *dev,
+				  unsigned int num_planes, const char *types[],
+				  unsigned long alignments[]);
+void vb2_cma_cleanup_multi(struct vb2_alloc_ctx **alloc_ctxes);
+
+struct vb2_alloc_ctx *vb2_cma_init(struct device *dev, const char *type,
+					unsigned long alignment);
+void vb2_cma_cleanup(struct vb2_alloc_ctx *alloc_ctx);
+
-- 
1.7.2.1.97.g3235b.dirty


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

* Re: [PATCH/RFC v1 0/7] Videobuf2 framework
  2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
                   ` (6 preceding siblings ...)
  2010-09-09  9:19 ` [PATCH v1 7/7] v4l: videobuf2: add CMA allocator Pawel Osciak
@ 2010-09-09  9:26 ` Pawel Osciak
  2010-09-09 17:53 ` Mauro Carvalho Chehab
  8 siblings, 0 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-09  9:26 UTC (permalink / raw)
  To: Pawel Osciak; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

On 09/09/2010 06:19 PM, Pawel Osciak wrote:
> Hello,
>
> These patches add a new driver framework for Video for Linux 2 driver
> - Videobuf2.

Sorry, I failed to mention that these patches depend on the multi-planar
API extensions, but do not require multi-planar support in drivers and 
in vivi.

Videobuf2 supports multi-planes, but can be used normally with current,
single-planar API and drivers.

Multi-planar API is available on this list and can also be pulled from here:

The following changes since commit 67ac062a5138ed446a821051fddd798a01478f85:

    V4L/DVB: Fix regression for BeholdTV Columbus (2010-08-24 10:39:32 
-0300)

are available in the git repository at:
    git://git.infradead.org/users/kmpark/linux-2.6-samsung 
v4l/multiplane-api

Pawel Osciak (4):
        v4l: Add multi-planar API definitions to the V4L2 API
        v4l: Add multi-planar ioctl handling code
        v4l: Add compat functions for the multi-planar API
        v4l: fix copy sizes in compat32 for ext controls

   drivers/media/video/v4l2-compat-ioctl32.c |  229 +++++++++++++---
   drivers/media/video/v4l2-ioctl.c          |  420 
++++++++++++++++++++++++++---
   include/linux/videodev2.h                 |  124 +++++++++-
   include/media/v4l2-ioctl.h                |   16 ++
   4 files changed, 709 insertions(+), 80 deletions(-)

-- 
Best regards,
Pawel Osciak
Linux Platform Group
Samsung Poland R&D Center

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

* Re: [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-09-09  9:19 ` [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Pawel Osciak
@ 2010-09-09 17:29   ` Mauro Carvalho Chehab
  2010-09-15 20:16     ` Pawel Osciak
  2010-09-25 14:27   ` Hans Verkuil
  1 sibling, 1 reply; 24+ messages in thread
From: Mauro Carvalho Chehab @ 2010-09-09 17:29 UTC (permalink / raw)
  To: Pawel Osciak; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Em 09-09-2010 06:19, Pawel Osciak escreveu:
> Videobuf2 is a Video for Linux 2 API-compatible driver framework for
> multimedia devices. It acts as an intermediate layer between userspace
> applications and device drivers. It also provides low-level, modular
> memory management functions for drivers.
> 
> Videobuf2 eases driver development, reduces drivers' code size and aids in
> proper and consistent implementation of V4L2 API in drivers.
> 
> Videobuf2 memory management backend is fully modular. This allows custom
> memory management routines for devices and platforms with non-standard
> memory management requirements to be plugged in, without changing the
> high-level buffer management functions and API.
> 
> The framework provides:
> - implementations of streaming I/O V4L2 ioctls and file operations
> - high-level video buffer, video queue and state management functions
> - video buffer memory allocation and management
> 
> Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/Kconfig          |    3 +
>  drivers/media/video/Makefile         |    2 +
>  drivers/media/video/videobuf2-core.c | 1457 ++++++++++++++++++++++++++++++++++
>  include/media/videobuf2-core.h       |  337 ++++++++
>  4 files changed, 1799 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/videobuf2-core.c
>  create mode 100644 include/media/videobuf2-core.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index f6e4d04..5764443 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -48,6 +48,9 @@ config VIDEO_TUNER
>  config V4L2_MEM2MEM_DEV
>  	tristate
>  	depends on VIDEOBUF_GEN
> +config VIDEOBUF2_CORE
> +	tristate
> +
>  
>  #
>  # Multimedia Video device configuration
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 40f98fb..e66f53b 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -117,6 +117,8 @@ obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
>  obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
>  obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
>  
> +obj-$(CONFIG_VIDEOBUF2_CORE)		+= videobuf2-core.o
> +
>  obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
>  
>  obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
> diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
> new file mode 100644
> index 0000000..ed4b665
> --- /dev/null
> +++ b/drivers/media/video/videobuf2-core.c
> @@ -0,0 +1,1457 @@
> +/*
> + * videobuf2-core.c - V4L2 driver helper framework
> + *
> + * Copyright (C) 2010 Samsung Electronics
> + *
> + * Author: Pawel Osciak <p.osciak@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/mm.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/sched.h>
> +#include <linux/err.h>
> +#include <linux/poll.h>
> +
> +#include <media/videobuf2-core.h>
> +
> +static int debug;
> +module_param(debug, int, 0644);
> +
> +#define dprintk(level, fmt, arg...)					\
> +	do {								\
> +		if (debug >= level)					\
> +			printk(KERN_DEBUG "vb2: " fmt, ## arg);		\
> +	} while (0)
> +
> +#define mem_ops(q, plane) ((q)->alloc_ctx[plane]->mem_ops)
> +
> +#define call_memop(q, plane, op, args...)				\
> +	((q)->alloc_ctx[plane]->mem_ops->op) ?				\
> +		((q)->alloc_ctx[plane]->mem_ops->op(args)) : 0
> +
> +
> +/**
> + * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
> + */
> +static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
> +				unsigned long *plane_sizes)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +	void *mem_priv;
> +	int plane;
> +
> +	/* Allocate memory for all planes in this buffer */
> +	for (plane = 0; plane < vb->num_planes; ++plane) {
> +		mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane],
> +					plane_sizes[plane]);
> +		if (!mem_priv)
> +			goto free;
> +
> +		/* Associate allocator private data with this plane */
> +		vb->planes[plane].mem_priv = mem_priv;
> +		vb->v4l2_planes[plane].length = plane_sizes[plane];
> +	}
> +
> +	return 0;
> +free:
> +	/* Free already allocated memory if one of the allocations failed */
> +	for (; plane > 0; --plane)
> +		call_memop(q, plane, put, vb->planes[plane - 1].mem_priv);
> +
> +	return -ENOMEM;
> +}
> +
> +/**
> + * __vb2_buf_mem_free() - free memory of the given buffer
> + */
> +static void __vb2_buf_mem_free(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +	unsigned int plane;
> +
> +	for (plane = 0; plane < vb->num_planes; ++plane) {
> +		call_memop(q, plane, put, vb->planes[plane].mem_priv);
> +		dprintk(3, "Freed plane %d of buffer %d\n",
> +				plane, vb->v4l2_buf.index);
> +	}
> +}
> +
> +/**
> + * __vb2_buf_userptr_put() - release userspace memory associated associated
> + * with a USERPTR buffer
> + */
> +static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +	unsigned int plane;
> +
> +	for (plane = 0; plane < vb->num_planes; ++plane) {
> +		call_memop(q, plane, put_userptr, vb->planes[plane].mem_priv);
> +		vb->planes[plane].mem_priv = NULL;
> +	}
> +}
> +
> +/**
> + * __setup_offsets() - setup unique offsets ("cookies") for every plane in
> + * every buffer on the queue
> + */
> +static void __setup_offsets(struct vb2_queue *q)
> +{
> +	unsigned int buffer, plane;
> +	struct vb2_buffer *vb;
> +	unsigned long off = 0;
> +
> +	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> +		vb = q->bufs[buffer];
> +		if (!vb)
> +			continue;
> +
> +		for (plane = 0; plane < vb->num_planes; ++plane) {
> +			vb->v4l2_planes[plane].m.mem_offset = off;
> +
> +			dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
> +					buffer, plane, off);
> +
> +			off += vb->v4l2_planes[plane].length;
> +			off = PAGE_ALIGN(off);
> +		}
> +	}
> +}
> +
> +/**
> + * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type)
> + * video buffer memory for all buffers/planes on the queue and initializes the
> + * queue
> + *
> + * Returns the number of buffers successfully allocated.
> + */
> +static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
> +			     unsigned int num_buffers, unsigned int num_planes)
> +{
> +	unsigned long plane_sizes[VIDEO_MAX_PLANES];
> +	unsigned int buffer, plane;
> +	struct vb2_buffer *vb;
> +	int ret;
> +
> +	/* Get requested plane sizes from the driver */
> +	for (plane = 0; plane < num_planes; ++plane) {
> +		ret = q->ops->plane_setup(q, plane, &plane_sizes[plane]);
> +		if (ret) {
> +			dprintk(1, "Plane setup failed\n");
> +			return ret;
> +		}
> +	}
> +
> +	for (buffer = 0; buffer < num_buffers; ++buffer) {
> +		/* Allocate videobuf buffer structures */
> +		vb = kzalloc(sizeof(struct vb2_buffer), GFP_KERNEL);
> +		if (!vb) {
> +			dprintk(1, "Memory alloc for buffer struct failed\n");
> +			break;
> +		}
> +
> +		/* Length stores number of planes for multiplanar buffers */
> +		if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
> +			vb->v4l2_buf.length = num_planes;
> +
> +		vb->state = VB2_BUF_STATE_DEQUEUED;
> +		vb->vb2_queue = q;
> +		vb->num_planes = num_planes;
> +		vb->v4l2_buf.index = buffer;
> +		vb->v4l2_buf.type = q->type;
> +		vb->v4l2_buf.memory = memory;
> +
> +		/* Allocate video buffer memory for the MMAP type */
> +		if (memory == V4L2_MEMORY_MMAP) {
> +			ret = __vb2_buf_mem_alloc(vb, plane_sizes);
> +			if (ret) {
> +				dprintk(1, "Failed allocating memory for "
> +						"buffer %d\n", buffer);
> +				kfree(vb);
> +				break;
> +			}
> +			/*
> +			 * Call the driver-provided buffer initialization
> +			 * callback, if given. An error in initialization
> +			 * results in queue setup failure.
> +			 */
> +			if (q->ops->buf_init) {
> +				ret = q->ops->buf_init(vb);
> +				if (ret) {
> +					dprintk(1, "Buffer %d %p initialization"
> +						" failed\n", buffer, vb);
> +					__vb2_buf_mem_free(vb);
> +					kfree(vb);
> +					break;
> +				}
> +			}
> +		}
> +
> +		q->bufs[buffer] = vb;
> +	}
> +
> +	q->num_buffers = buffer;
> +
> +	__setup_offsets(q);
> +
> +	dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
> +			q->num_buffers, num_planes);
> +
> +	return buffer;
> +}
> +
> +/**
> + * __vb2_free_mem() - release all video buffer memory for a given queue
> + */
> +static void __vb2_free_mem(struct vb2_queue *q)
> +{
> +	unsigned int buffer;
> +	struct vb2_buffer *vb;
> +
> +	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> +		vb = q->bufs[buffer];
> +		if (!vb)
> +			continue;
> +
> +		/* Free MMAP buffers or release USERPTR buffers */
> +		if (q->memory == V4L2_MEMORY_MMAP)
> +			__vb2_buf_mem_free(vb);
> +		else
> +			__vb2_buf_userptr_put(vb);
> +	}
> +}
> +
> +/**
> + * __vb2_queue_free() - free the queue - video memory and related information
> + * and return the queue to an uninitialized state
> + */
> +static int __vb2_queue_free(struct vb2_queue *q)
> +{
> +	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) {
> +			if (NULL == q->bufs[buffer])
> +				continue;
> +			q->ops->buf_cleanup(q->bufs[buffer]);
> +		}
> +	}
> +
> +	/* Release video buffer memory */
> +	__vb2_free_mem(q);
> +
> +	/* Free videobuf buffers */
> +	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> +		if (NULL == q->bufs[buffer])
> +			continue;
> +		kfree(q->bufs[buffer]);
> +		q->bufs[buffer] = NULL;
> +	}
> +
> +	q->num_buffers = 0;
> +	q->memory = 0;
> +
> +	return 0;
> +}
> +
> +/**
> + * __verify_planes_array() - verify that the planes array passed in struct
> + * v4l2_buffer from userspace can be safely used
> + */
> +static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b)
> +{
> +	/* Is memory for copying plane information present? */
> +	if (NULL == b->m.planes) {
> +		dprintk(1, "Multi-planar buffer passed but "
> +			   "planes array not provided\n");
> +		return -EINVAL;
> +	}
> +
> +	if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) {
> +		dprintk(1, "Incorrect planes array length, "
> +			   "expected %d, got %d\n", vb->num_planes, b->length);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be
> + * returned to userspace
> + */
> +static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +	int ret = 0;
> +
> +	/* Copy back data such as timestamp, input, etc. */
> +	memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
> +	b->input = vb->v4l2_buf.input;
> +	b->reserved = vb->v4l2_buf.reserved;
> +
> +	if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
> +		ret = __verify_planes_array(vb, b);
> +		if (ret)
> +			return ret;
> +
> +		/*
> +		 * Fill in plane-related data if userspace provided an array
> +		 * for it. The memory and size is verified above.
> +		 */
> +		memcpy(b->m.planes, vb->v4l2_planes,
> +			b->length * sizeof(struct v4l2_plane));
> +	} else {
> +		/*
> +		 * We use length and offset in v4l2_planes array even for
> +		 * single-planar buffers, but userspace does not.
> +		 */
> +		b->length = vb->v4l2_planes[0].length;
> +		if (q->memory == V4L2_MEMORY_MMAP)
> +			b->m.offset = vb->v4l2_planes[0].m.mem_offset;
> +	}
> +
> +	b->flags = 0;
> +
> +	switch (vb->state) {
> +	case VB2_BUF_STATE_QUEUED:
> +	case VB2_BUF_STATE_ACTIVE:
> +		b->flags |= V4L2_BUF_FLAG_QUEUED;
> +		break;
> +	case VB2_BUF_STATE_ERROR:
> +		b->flags |= V4L2_BUF_FLAG_ERROR;
> +		/* fall through */
> +	case VB2_BUF_STATE_DONE:
> +		b->flags |= V4L2_BUF_FLAG_DONE;
> +		break;
> +	case VB2_BUF_STATE_DEQUEUED:
> +		/* nothing */
> +		break;
> +	}
> +
> +	if (vb->num_planes_mapped == vb->num_planes)
> +		b->flags |= V4L2_BUF_FLAG_MAPPED;
> +
> +	return ret;
> +}
> +
> +/**
> + * vb2_querybuf() - query video buffer information
> + * @q:		videobuf queue
> + * @b:		buffer struct passed from userspace to vidioc_querybuf handler
> + *		in driver
> + *
> + * Should be called from vidioc_querybuf ioctl handler in driver.
> + * This function will verify the passed v4l2_buffer structure and fill the
> + * relevant information for the userspace.
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_querybuf handler in driver.
> + */
> +int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
> +{
> +	struct vb2_buffer *vb;
> +	int ret = -EINVAL;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	if (b->type != q->type) {
> +		dprintk(1, "querybuf: wrong buffer type\n");
> +		goto done;
> +	}
> +
> +	if (b->index >= q->num_buffers) {
> +		dprintk(1, "querybuf: buffer index out of range\n");
> +		goto done;
> +	}
> +	vb = q->bufs[b->index];
> +	if (NULL == vb) {
> +		/* Should never happen */
> +		dprintk(1, "querybuf: no such buffer\n");
> +		goto done;
> +	}
> +
> +	ret = __fill_v4l2_buffer(vb, b);
> +done:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL(vb2_querybuf);
> +
> +/**
> + * __verify_userptr_ops() - verify that all memory operations required for
> + * USERPTR queue type have been provided
> + */
> +static int __verify_userptr_ops(struct vb2_queue *q, unsigned int num_planes)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < num_planes; ++i)
> +		if (!mem_ops(q, i)->get_userptr || !mem_ops(q, i)->put_userptr)
> +			return -EINVAL;
> +
> +	return 0;
> +}
> +
> +/**
> + * __verify_mmap_ops() - verify that all memory operations required for
> + * MMAP queue type have been provided
> + */
> +static int __verify_mmap_ops(struct vb2_queue *q, unsigned int num_planes)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < num_planes; ++i)
> +		if (!mem_ops(q, i)->alloc || !mem_ops(q, i)->put
> +				|| !mem_ops(q, i)->mmap)
> +			return -EINVAL;
> +
> +	return 0;
> +}
> +
> +/**
> + * __buffers_in_use() - return true if any buffers on the queue are in use and
> + * the queue cannot be freed (by the means of REQBUFS(0)) call
> + */
> +static bool __buffers_in_use(struct vb2_queue *q)
> +{
> +	unsigned int buffer, plane;
> +	struct vb2_buffer *vb;
> +
> +	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> +		vb = q->bufs[buffer];
> +		for (plane = 0; plane < vb->num_planes; ++plane) {
> +			/*
> +			 * If num_users() has not been provided, apparently
> +			 * nobody cares.
> +			 */
> +			if (!mem_ops(q, plane)->num_users)
> +				continue;
> +
> +			/*
> +			 * If num_users() returns more than 1, we are not the
> +			 * only user of the plane's memory.
> +			 */
> +			if (call_memop(q, plane, num_users,
> +					vb->planes[plane].mem_priv) > 1)
> +				return true;
> +		}
> +	}
> +
> +	return false;
> +}
> +
> +/**
> + * vb2_reqbufs() - Initiate streaming
> + * @q:		videobuf2 queue
> + * @req:	struct passed from userspace to vidioc_reqbufs handler in driver
> + *
> + * Should be called from vidioc_reqbufs ioctl handler of a driver.
> + * This function:
> + * 1) verifies streaming parameters passed from the userspace,
> + * 2) sets up the queue,
> + * 3) negotiates number of buffers and planes per buffer with the driver
> + *    to be used during streaming,
> + * 4) allocates internal buffer structures (struct vb2_buffer), according to
> + *    the agreed parameters,
> + * 5) for MMAP memory type, allocates actual video memory, using the
> + *    memory handling/allocation routines provided during queue initialization
> + *
> + * If req->count is 0, all the memory will be freed instead.
> + * If the queue has been allocated previously (by a previous vb2_reqbufs) call
> + * and the queue is not busy, memory will be reallocated.
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_reqbufs handler in driver.
> + */
> +int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> +{
> +	unsigned int num_buffers, num_planes;
> +	int ret = 0;
> +
> +	if (req->memory != V4L2_MEMORY_MMAP
> +			&& req->memory != V4L2_MEMORY_USERPTR) {
> +		dprintk(1, "reqbufs: unsupported memory type\n");
> +		return -EINVAL;

Hmm... V4L2_MEMORY_OVERLAY is not supported... this means that videobuf2 cannot replace
videobuf1 for some drivers...

> +	}
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	if (req->type != q->type) {
> +		dprintk(1, "reqbufs: queue type invalid\n");
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	if (q->streaming) {
> +		dprintk(1, "reqbufs: streaming active\n");
> +		ret = -EBUSY;
> +		goto end;
> +	}
> +
> +	if (req->count == 0) {
> +		/* Free/release memory for count = 0, but only if unused */
> +		if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
> +			dprintk(1, "reqbufs: memory in use, cannot free\n");
> +			ret = -EBUSY;
> +			goto end;
> +		}
> +
> +		ret = __vb2_queue_free(q);
> +		goto end;
> +	}
> +
> +	if (q->num_buffers != 0) {
> +		/*
> +		 * We already have buffers allocated, so a reallocation is
> +		 * required, but only if the buffers are not in use.
> +		 */
> +		if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
> +			dprintk(1, "reqbufs: memory in use, "
> +					"cannot reallocate\n");
> +			ret = -EBUSY;
> +			goto end;
> +		}
> +
> +		ret = __vb2_queue_free(q);
> +		if (ret)
> +			goto end;
> +	}
> +
> +	num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
> +
> +	/* Ask the driver how many buffers and planes per buffer it requires */
> +	ret = q->ops->queue_negotiate(q, &num_buffers, &num_planes);
> +	if (ret)
> +		goto end;
> +
> +	/*
> +	 * Make sure all the required memory ops for given memory type
> +	 * are available.
> +	 */
> +	if (req->memory == V4L2_MEMORY_MMAP
> +			&& __verify_mmap_ops(q, num_planes)) {
> +		dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
> +		ret = -EINVAL;
> +		goto end;
> +	} else if (req->memory == V4L2_MEMORY_USERPTR
> +			&& __verify_userptr_ops(q, num_planes)) {
> +		dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	/* Finally, allocate buffers and video memory */
> +	ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
> +	if (ret < 0) {
> +		dprintk(1, "Memory allocation failed with error: %d\n", ret);
> +	} else {
> +		/*
> +		 * Return the number of successfully allocated buffers
> +		 * to the userspace.
> +		 */
> +		req->count = ret;
> +		ret = 0;
> +	}
> +
> +	q->memory = req->memory;
> +
> +end:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_reqbufs);
> +
> +/**
> + * 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
> + *
> + * This function returns a kernel virtual address of a given plane if
> + * such a mapping exist, NULL otherwise.
> + */
> +void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +
> +	if (plane_no > vb->num_planes)
> +		return NULL;
> +
> +	return call_memop(q, plane_no, vaddr, vb->planes[plane_no].mem_priv);
> +
> +}
> +EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
> +
> +/**
> + * vb2_plane_paddr() - Return the physical 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
> + *
> + * This function returns a physical address of a given plane if available,
> + * NULL otherwise.
> + */
> +unsigned long vb2_plane_paddr(struct vb2_buffer *vb, unsigned int plane_no)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +
> +	if (plane_no > vb->num_planes)
> +		return 0UL;
> +
> +	return call_memop(q, plane_no, paddr, vb->planes[plane_no].mem_priv);
> +}
> +EXPORT_SYMBOL_GPL(vb2_plane_paddr);
> +
> +/**
> + * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
> + * @vb:		vb2_buffer returned from the driver
> + * @state:	either VB2_BUF_STATE_DONE if the operation finished successfully
> + *		or VB2_BUF_STATE_ERROR if the operation finished with an error
> + *
> + * This function should be called by the driver after a hardware operation on
> + * a buffer is finished and the buffer may be returned to userspace. The driver
> + * cannot use this buffer anymore until it is queued back to it by videobuf
> + * by the means of buf_queue callback. Only buffers previously queued to the
> + * driver by buf_queue can be passed to this function.
> + */
> +void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +	unsigned long flags;
> +
> +	if (vb->state != VB2_BUF_STATE_ACTIVE)
> +		return;
> +
> +	if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
> +		return;
> +
> +	dprintk(4, "Done processing on buffer %d, state: %d\n",
> +			vb->v4l2_buf.index, vb->state);
> +
> +	/* Add the buffer to the done buffers list */
> +	spin_lock_irqsave(&q->done_lock, flags);
> +	vb->state = state;
> +	list_add_tail(&vb->done_entry, &q->done_list);
> +	spin_unlock_irqrestore(&q->done_lock, flags);
> +
> +	/* Inform any processes that may be waiting for buffers */
> +	wake_up_interruptible(&q->done_wq);
> +}
> +EXPORT_SYMBOL_GPL(vb2_buffer_done);
> +
> +/**
> + * __fill_vb2_buffer() - fill a vb2_buffer with information provided in
> + * a v4l2_buffer by the userspace
> + */
> +static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
> +				struct v4l2_plane *v4l2_planes)
> +{
> +	unsigned int plane;
> +	int ret;
> +
> +	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
> +		/*
> +		 * Verify that the userspace gave us a valid array for
> +		 * plane information.
> +		 */
> +		ret = __verify_planes_array(vb, b);
> +		if (ret)
> +			return ret;
> +
> +		/* Fill in driver-provided information for OUTPUT types */
> +		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
> +			/*
> +			 * Will have to go up to b->length when API starts
> +			 * accepting variable number of planes.
> +			 */
> +			for (plane = 0; plane < vb->num_planes; ++plane) {
> +				v4l2_planes[plane].bytesused =
> +					b->m.planes[plane].bytesused;
> +				v4l2_planes[plane].data_offset =
> +					b->m.planes[plane].data_offset;
> +			}
> +		}
> +
> +		if (b->memory == V4L2_MEMORY_USERPTR) {
> +			for (plane = 0; plane < vb->num_planes; ++plane) {
> +				v4l2_planes[plane].m.userptr =
> +					b->m.planes[plane].m.userptr;
> +				v4l2_planes[plane].length =
> +					b->m.planes[plane].length;
> +			}
> +		}
> +	} else {
> +		/*
> +		 * Single-planar buffers do not use planes array,
> +		 * so fill in relevant v4l2_buffer struct fields instead.
> +		 * In videobuf we use our internal V4l2_planes struct for
> +		 * single-planar buffers as well, for simplicity.
> +		 */
> +		if (V4L2_TYPE_IS_OUTPUT(b->type))
> +			v4l2_planes[0].bytesused = b->bytesused;
> +
> +		if (b->memory == V4L2_MEMORY_USERPTR) {
> +			v4l2_planes[0].m.userptr = b->m.userptr;
> +			v4l2_planes[0].length = b->length;
> +		}
> +	}
> +
> +	vb->v4l2_buf.field = b->field;
> +	vb->v4l2_buf.timestamp = b->timestamp;
> +
> +	return 0;
> +}
> +
> +/**
> + * __qbuf_userptr() - handle qbuf of a USERPTR buffer
> + */
> +static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
> +{
> +	struct v4l2_plane planes[VIDEO_MAX_PLANES];
> +	struct vb2_queue *q = vb->vb2_queue;
> +	void *mem_priv = NULL;
> +	unsigned int plane;
> +	int ret;
> +
> +	/* Verify and copy relevant information provided by the userspace */
> +	ret = __fill_vb2_buffer(vb, b, planes);
> +	if (ret)
> +		return ret;
> +
> +	for (plane = 0; plane < vb->num_planes; ++plane) {
> +		/* Skip the plane if already verified */
> +		if (vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr
> +		    && vb->v4l2_planes[plane].length == planes[plane].length)
> +			continue;
> +
> +		dprintk(3, "qbuf: userspace address for plane %d changed, "
> +				"reacquiring memory\n", plane);
> +
> +		/* Release previously acquired memory if present */
> +		if (vb->planes[plane].mem_priv)
> +			call_memop(q, plane, put_userptr,
> +					vb->planes[plane].mem_priv);
> +
> +		vb->planes[plane].mem_priv = NULL;
> +
> +		/* Acquire each plane's memory */
> +		if (mem_ops(q, plane)->get_userptr) {
> +			mem_priv = mem_ops(q, plane)->get_userptr(
> +							planes[plane].m.userptr,
> +							planes[plane].length);
> +			if (IS_ERR(mem_priv)) {
> +				dprintk(1, "qbuf: failed acquiring userspace "
> +						"memory for plane %d\n", plane);
> +				goto err;
> +			}
> +
> +			vb->planes[plane].mem_priv = mem_priv;
> +		}
> +	}
> +
> +	/*
> +	 * Call driver-specific initialization on the newly acquired buffer,
> +	 * if provided.
> +	 */
> +	if (q->ops->buf_init) {
> +		ret = q->ops->buf_init(vb);
> +		if (ret) {
> +			dprintk(1, "qbuf: buffer initialization failed\n");
> +			goto err;
> +		}
> +	}
> +
> +	/*
> +	 * Now that everything is in order, copy relevant information
> +	 * provided by userspace.
> +	 */
> +	for (plane = 0; plane < vb->num_planes; ++plane)
> +		vb->v4l2_planes[plane] = planes[plane];
> +
> +	return 0;
> +err:
> +	/* In case of errors, release planes that were already acquired */
> +	for (; plane > 0; --plane) {
> +		call_memop(q, plane, put_userptr,
> +				vb->planes[plane - 1].mem_priv);
> +		vb->planes[plane - 1].mem_priv = NULL;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * __qbuf_mmap() - handle qbuf of an MMAP buffer
> + */
> +static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b)
> +{
> +	return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
> +}
> +
> +/**
> + * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
> + */
> +static void __enqueue_in_driver(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(q->drv_lock, flags);
> +	vb->state = VB2_BUF_STATE_ACTIVE;
> +	q->ops->buf_queue(vb);
> +	spin_unlock_irqrestore(q->drv_lock, flags);
> +}
> +
> +/**
> + * vb2_qbuf() - Queue a buffer from userspace
> + * @q:		videobuf2 queue
> + * @b:		buffer structure passed from userspace to vidioc_qbuf handler
> + *		in driver
> + *
> + * Should be called from vidioc_qbuf 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,
> + * 3) if streaming is on, queues the buffer in driver by the means of buf_queue
> + *    callback for processing.
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_qbuf handler in driver.
> + */
> +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
> +{
> +	struct vb2_buffer *vb;
> +	int ret;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	ret = -EINVAL;
> +	if (b->type != q->type) {
> +		dprintk(1, "qbuf: invalid buffer type\n");
> +		goto done;
> +	}
> +	if (b->index >= q->num_buffers) {
> +		dprintk(1, "qbuf: buffer index out of range\n");
> +		goto done;
> +	}
> +
> +	vb = q->bufs[b->index];
> +	if (NULL == vb) {
> +		/* Should never happen */
> +		dprintk(1, "qbuf: buffer is NULL\n");
> +		goto done;
> +	}
> +
> +	if (b->memory != q->memory) {
> +		dprintk(1, "qbuf: invalid memory type\n");
> +		goto done;
> +	}
> +
> +	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> +		dprintk(1, "qbuf: buffer already in use\n");
> +		goto done;
> +	}
> +
> +	if (q->memory == V4L2_MEMORY_MMAP)
> +		ret = __qbuf_mmap(vb, b);
> +	else if (q->memory == V4L2_MEMORY_USERPTR)
> +		ret = __qbuf_userptr(vb, b);
> +	if (ret)
> +		goto done;
> +
> +	if (q->ops->buf_prepare) {
> +		ret = q->ops->buf_prepare(vb);
> +		if (ret) {
> +			dprintk(1, "qbuf: buffer preparation failed\n");
> +			goto done;
> +		}
> +	}
> +
> +	/*
> +	 * Add to the queued buffers list, a buffer will stay on it until
> +	 * dequeued in dqbuf.
> +	 */
> +	list_add_tail(&vb->queued_entry, &q->queued_list);
> +	vb->state = VB2_BUF_STATE_QUEUED;
> +
> +	/*
> +	 * If already streaming, give the buffer to driver for processing.
> +	 * If not, the buffer will be given to driver on next streamon.
> +	 */
> +	if (q->streaming)
> +		__enqueue_in_driver(vb);
> +
> +	dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
> +	ret = 0;
> +done:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_qbuf);
> +
> +/**
> + * __vb2_wait_for_done_vb() - wait for a buffer to become available
> + * for dequeuing
> + *
> + * Will sleep if required for nonblocking == false.
> + */
> +static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
> +{
> +	int retval = 0;
> +
> +checks:
> +	if (!q->streaming) {
> +		dprintk(1, "Streaming off, will not wait for buffers\n");
> +		retval = -EINVAL;
> +		goto end;
> +	}
> +
> +	/*
> +	 * Buffers may be added to vb_done_list without holding the vb_lock,
> +	 * but removal is performed only while holding both vb_lock and the
> +	 * vb_done_lock spinlock. Thus we can be sure that as long as we hold
> +	 * vb_lock, the list will remain not empty if this check succeeds.
> +	 */
> +	if (list_empty(&q->done_list)) {
> +		if (nonblocking) {
> +			dprintk(1, "Nonblocking and no buffers to dequeue, "
> +					"will not wait\n");
> +			retval = -EAGAIN;
> +			goto end;
> +		}
> +
> +		/*
> +		 * We are streaming and nonblocking, wait for another buffer to
> +		 * become ready or for streamoff. vb_lock is released to allow
> +		 * streamoff or qbuf to be called while waiting.
> +		 */
> +		mutex_unlock(&q->vb_lock);

There's no mutex_lock before this call inside this function... It doesn't
seem to be a good idea to call it with a mutex locked, and having a unlock/lock
inside the fuction. The better would be to call it with mutex unlocked and let it
lock/unlock where needed.

> +		/*
> +		 * Although the mutex is released here, we will be reevaluating
> +		 * both conditions again after reacquiring it.
> +		 */
> +		dprintk(3, "Will sleep waiting for buffers\n");
> +		retval = wait_event_interruptible(q->done_wq,
> +				!list_empty(&q->done_list) || !q->streaming);

I think you could have a race condition here, as you're checking for list_empty
without a lock. The better approach would be to do something like:

static int vb2_is_videobuf_empty(struct vb2_queue *q)
{
	int is_empty;
		
	mutex_lock(&q->vb_lock);

	is_empty = list_empty(&q->done_list);

	mutex_unlock(&q->vb_lock);

	return is_empty;
}

static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
{
	...
	retval = wait_event_interruptible(q->done_wq, vb2_is_videobuf_empty(q) || !q->streaming);
	...
}

This way, you'll always have the mutex locked when checking for list empty.

Btw, shouldn't it be using, instead a spinlock?

To avoid needing to have a lock also for q->streaming, the better would be to define
it as atomic_t.

> +		mutex_lock(&q->vb_lock);
> +
> +		if (retval)
> +			goto end;
> +
> +		goto checks;
> +	}
> +
> +end:
> +	return retval;
> +}
> +
> +/**
> + * __vb2_get_done_vb() - get a buffer ready for dequeuing
> + *
> + * Will sleep if required for nonblocking == false.
> + */
> +static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
> +				int nonblocking)
> +{
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	/*
> +	 * Wait for at least one buffer to become available on the done_list.
> +	 */
> +	ret = __vb2_wait_for_done_vb(q, nonblocking);
> +	if (ret)
> +		goto end;
> +
> +	/*
> +	 * vb_lock has been held since we last verified that done_list is
> +	 * not empty, so no need for another list_empty(done_list) check.
> +	 */
> +	spin_lock_irqsave(&q->done_lock, flags);
> +	*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
> +	list_del(&(*vb)->done_entry);
> +	spin_unlock_irqrestore(&q->done_lock, flags);
> +
> +end:
> +	return ret;
> +}
> +
> +
> +/**
> + * vb2_dqbuf() - Dequeue a buffer to the userspace
> + * @q:		videobuf2 queue
> + * @b:		buffer structure passed from userspace to vidioc_dqbuf handler
> + *		in driver
> + * @nonblocking: if true, this call will not sleep waiting for a buffer if no
> + *		 buffers ready for dequeuing are present. Normally the driver
> + *		 would be passing (file->f_flags & O_NONBLOCK) here
> + *
> + * Should be called from vidioc_dqbuf ioctl handler of a driver.
> + * This function:
> + * 1) verifies the passed buffer,
> + * 2) calls buf_finish callback in the driver (if provided), in which
> + *    driver can perform any additional operations that may be required before
> + *    returning the buffer to userspace, such as cache sync,
> + * 3) the buffer struct members are filled with relevant information for
> + *    the userspace.
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_dqbuf handler in driver.
> + */
> +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
> +{
> +	struct vb2_buffer *vb = NULL;
> +	int ret;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	if (b->type != q->type) {
> +		dprintk(1, "dqbuf: invalid buffer type\n");
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	ret = __vb2_get_done_vb(q, &vb, nonblocking);
> +	if (ret < 0) {
> +		dprintk(1, "dqbuf: error getting next done buffer\n");
> +		goto done;
> +	}
> +
> +	if (q->ops->buf_finish) {
> +		ret = q->ops->buf_finish(vb);
> +		if (ret) {
> +			dprintk(1, "dqbuf: buffer finish failed\n");
> +			goto done;
> +		}
> +	}
> +
> +	switch (vb->state) {
> +	case VB2_BUF_STATE_DONE:
> +		dprintk(3, "dqbuf: Returning done buffer\n");
> +		break;
> +	case VB2_BUF_STATE_ERROR:
> +		dprintk(3, "dqbuf: Returning done buffer with errors\n");
> +		break;
> +	default:
> +		dprintk(1, "dqbuf: Invalid buffer state\n");
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	/* Fill buffer information for the userspace */
> +	__fill_v4l2_buffer(vb, b);
> +	/* Remove from videobuf queue */
> +	list_del(&vb->queued_entry);
> +
> +	dprintk(1, "dqbuf of buffer %d, with state %d\n",
> +			vb->v4l2_buf.index, vb->state);
> +
> +	vb->state = VB2_BUF_STATE_DEQUEUED;
> +
> +done:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_dqbuf);
> +
> +/**
> + * vb2_streamon - start streaming
> + * @q:		videobuf2 queue
> + * @type:	type argument passed from userspace to vidioc_streamon handler
> + *
> + * Should be called from vidioc_streamon handler of a driver.
> + * This function:
> + * 1) verifies current state
> + * 2) starts streaming and passes any previously queued buffers to the driver
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_streamon handler in the driver.
> + */
> +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
> +{
> +	struct vb2_buffer *vb;
> +	int ret = 0;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	if (type != q->type) {
> +		dprintk(1, "streamon: invalid stream type\n");
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	if (q->streaming) {
> +		dprintk(1, "streamon: already streaming\n");
> +		ret = -EBUSY;
> +		goto done;
> +	}
> +
> +	/*
> +	 * Cannot start streaming on an OUTPUT device if no buffers have
> +	 * been queued yet.
> +	 */
> +	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
> +		if (list_empty(&q->queued_list)) {
> +			dprintk(1, "streamon: no output buffers queued\n");
> +			ret = -EINVAL;
> +			goto done;
> +		}
> +	}
> +
> +	q->streaming = 1;
> +
> +	/*
> +	 * If any buffers were queued before streamon,
> +	 * we can now pass them to driver for processing.
> +	 */
> +	list_for_each_entry(vb, &q->queued_list, queued_entry)
> +		__enqueue_in_driver(vb);
> +
> +	dprintk(3, "Streamon successful\n");
> +done:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_streamon);
> +
> +/**
> + * __vb2_queue_cancel() - cancel and stop (pause) streaming
> + *
> + * Removes all queued buffers from driver's queue and all buffers queued by
> + * userspace from videobuf's queue. Returns to state after reqbufs.
> + */
> +static void __vb2_queue_cancel(struct vb2_queue *q)
> +{
> +	unsigned long flags = 0;
> +	int i;
> +
> +	q->streaming = 0;
> +
> +	/*
> +	 * Remove buffers from driver's queue. If a hardware operation
> +	 * is currently underway, drv_lock should be claimed and we will
> +	 * have to wait for it to finish before taking back buffers.
> +	 */
> +	spin_lock_irqsave(q->drv_lock, flags);
> +	for (i = 0; i < q->num_buffers; ++i) {
> +		if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE)
> +			list_del(&q->bufs[i]->drv_entry);
> +		q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
> +	}
> +	spin_unlock_irqrestore(q->drv_lock, flags);
> +
> +	/*
> +	 * Remove all buffers from videobuf's list...
> +	 */
> +	INIT_LIST_HEAD(&q->queued_list);
> +	/*
> +	 * ...and done list; userspace will not receive any buffers it
> +	 * has not already dequeued before initiating cancel.
> +	 */
> +	INIT_LIST_HEAD(&q->done_list);
> +	wake_up_interruptible_all(&q->done_wq);
> +}
> +
> +/**
> + * vb2_streamoff - stop streaming
> + * @q:		videobuf2 queue
> + * @type:	type argument passed from userspace to vidioc_streamoff handler
> + *
> + * Should be called from vidioc_streamoff handler of a driver.
> + * This function:
> + * 1) verifies current state,
> + * 2) stop streaming and dequeues any queued buffers, including those previously
> + *    passed to the driver (after waiting for the driver to finish).
> + *
> + * This call can be used for pausing playback.
> + * The return values from this function are intended to be directly returned
> + * from vidioc_streamoff handler in the driver
> + */
> +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	if (type != q->type) {
> +		dprintk(1, "streamoff: invalid stream type\n");
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	if (!q->streaming) {
> +		dprintk(1, "streamoff: not streaming\n");
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	/*
> +	 * Cancel will pause streaming and remove all buffers from the driver
> +	 * and videobuf, effectively returning control over them to userspace.
> +	 */
> +	__vb2_queue_cancel(q);
> +
> +	dprintk(3, "Streamoff successful\n");
> +end:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_streamoff);
> +
> +/**
> + * __find_plane_by_off() - find plane associated with the given offset off
> + */
> +int __find_plane_by_off(struct vb2_queue *q, unsigned long off,
> +			unsigned int *_buffer, unsigned int *_plane)
> +{
> +	struct vb2_buffer *vb;
> +	unsigned int buffer, plane;
> +
> +	/*
> +	 * Go over all buffers and their planes, comparing the given offset
> +	 * with an offset assigned to each plane. If a match is found,
> +	 * return its buffer and plane numbers.
> +	 */
> +	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
> +		vb = q->bufs[buffer];
> +
> +		for (plane = 0; plane < vb->num_planes; ++plane) {
> +			if (vb->v4l2_planes[plane].m.mem_offset == off) {
> +				*_buffer = buffer;
> +				*_plane = plane;
> +				return 0;
> +			}
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * vb2_mmap() - map video buffers into application address space
> + * @q:		videobuf2 queue
> + * @vma:	vma passed to the mmap file operation handler in the driver
> + *
> + * Should be called from mmap file operation handler of a driver.
> + * This function maps one plane of one of the available video buffers to
> + * userspace. To map whole video memory allocated on reqbufs, this function
> + * has to be called once per each plane per each buffer previously allocated.
> + *
> + * When the userspace application calls mmap, it passes to it an offset returned
> + * to it earlier by the means of vidioc_querybuf handler. That offset acts as
> + * a "cookie", which is then used to identify the plane to be mapped.
> + * This function finds a plane with a matching offset and a mapping is performed
> + * by the means of a provided memory operation.
> + *
> + * The return values from this function are intended to be directly returned
> + * from the mmap handler in driver.
> + */
> +int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
> +{
> +	unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
> +	struct vb2_plane *vb_plane;
> +	struct vb2_buffer *vb;
> +	unsigned int buffer, plane;
> +	int ret = -EINVAL;
> +
> +	if (q->memory != V4L2_MEMORY_MMAP) {
> +		dprintk(1, "Queue is not currently set up for mmap\n");
> +		return ret;
> +	}
> +
> +	if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) {
> +		dprintk(1, "Invalid vma flags (need VM_WRITE | VM_SHARED)\n");
> +		return ret;
> +	}
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	/*
> +	 * Find the plane corresponding to the offset passed by userspace.
> +	 */
> +	ret = __find_plane_by_off(q, off, &buffer, &plane);
> +	if (ret)
> +		goto end;
> +
> +	vb = q->bufs[buffer];
> +	vb_plane = &vb->planes[plane];
> +
> +	if (vb_plane->mapped) {
> +		dprintk(1, "Plane already mapped\n");
> +		goto end;
> +	}
> +
> +	if (!mem_ops(q, plane)->mmap) {
> +		dprintk(1, "mmap not supported\n");
> +		goto end;
> +	}
> +
> +	ret = mem_ops(q, plane)->mmap(vb_plane->mem_priv, vma);
> +	if (ret)
> +		goto end;
> +
> +	vb_plane->mapped = 1;
> +	vb->num_planes_mapped++;
> +
> +	dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
> +end:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_mmap);
> +
> +/**
> + * vb2_has_consumers() - return true if the userspace is waiting for a buffer
> + * @q:		videobuf2 queue
> + *
> + * This function returns true if a userspace application is waiting for a buffer
> + * to be ready to dequeue (on which a hardware operation has been finished).
> + */
> +bool vb2_has_consumers(struct vb2_queue *q)
> +{
> +	return waitqueue_active(&q->done_wq);
> +}
> +EXPORT_SYMBOL_GPL(vb2_has_consumers);
> +
> +/**
> + * vb2_poll() - implements poll userspace operation
> + * @q:		videobuf2 queue
> + * @file:	file argument passed to the poll file operation handler
> + * @wait:	wait argument passed to the poll file operation handler
> + *
> + * This function implements poll file operation handler for a driver.
> + * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will
> + * be informed that the file descriptor of a video device is available for
> + * reading.
> + * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor
> + * will be reported as available for writing.
> + *
> + * The return values from this function are intended to be directly returned
> + * from poll handler in driver.
> + */
> +unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
> +{
> +	unsigned long flags = 0;
> +	unsigned int ret = 0;
> +	struct vb2_buffer *vb = NULL;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	/*
> +	 * There is nothing to wait for if no buffers have already been queued.
> +	 */
> +	if (list_empty(&q->queued_list)) {
> +		ret = POLLERR;
> +		goto end;
> +	}
> +
> +	poll_wait(file, &q->done_wq, wait);
> +
> +	/*
> +	 * Take first buffer available for dequeuing.
> +	 */
> +	spin_lock_irqsave(&q->done_lock, flags);
> +	if (!list_empty(&q->done_list))
> +		vb = list_first_entry(&q->done_list, struct vb2_buffer,
> +					done_entry);
> +	spin_unlock_irqrestore(&q->done_lock, flags);
> +
> +	if (!vb)
> +		goto end;
> +
> +	if (vb->state == VB2_BUF_STATE_DONE
> +			|| vb->state == VB2_BUF_STATE_ERROR) {
> +		if (V4L2_TYPE_IS_OUTPUT(q->type))
> +			ret = POLLOUT | POLLWRNORM;
> +		else
> +			ret = POLLIN | POLLRDNORM;
> +	}
> +end:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_poll);

Hmm... what about the read() method?


> +
> +/**
> + * vb2_queue_init() - initialize a videobuf2 queue
> + * @q:		videobuf2 queue; this structure should be allocated in driver
> + * @ops:	driver-specific callbacks
> + * @alloc_ctx:	memory handler/allocator-specific context to be used;
> + *		the given context will be used for memory allocation on all
> + *		planes and buffers; it is possible to assign different contexts
> + *		per plane, use vb2_set_alloc_ctx() for that
> + * @drv_lock:	a lock for synchronization between the driver and videobuf,
> + *		it should be locked by driver whenever an operation is being
> + *		performed on a video buffer; this prevents videobuf from
> + *		forcefully taking back a buffer from a driver in the middle
> + *		of a hardware operation in case of an unexpected application
> + *		close or queue cancellation
> + * @type:	queue type
> + * @drv_priv:	driver private data, may be NULL; it can be used by driver in
> + *		driver-specific callbacks when issued
> + */
> +int vb2_queue_init(struct vb2_queue *q, const struct vb2_ops *ops,
> +			const struct vb2_alloc_ctx *alloc_ctx,
> +			spinlock_t *drv_lock, enum v4l2_buf_type type,
> +			void *drv_priv)
> +{
> +	unsigned int i;
> +
> +	BUG_ON(!q);
> +	BUG_ON(!ops);
> +	BUG_ON(!ops->queue_negotiate);
> +	BUG_ON(!ops->plane_setup);
> +	BUG_ON(!ops->buf_queue);
> +
> +	BUG_ON(!alloc_ctx);
> +	BUG_ON(!alloc_ctx->mem_ops);
> +
> +	memset(q, 0, sizeof *q);
> +	q->ops = ops;
> +
> +	for (i = 0; i < VIDEO_MAX_PLANES; ++i)
> +		q->alloc_ctx[i] = alloc_ctx;
> +
> +	q->drv_lock = drv_lock;
> +	q->type = type;
> +	q->drv_priv = drv_priv;
> +
> +	mutex_init(&q->vb_lock);
> +	INIT_LIST_HEAD(&q->queued_list);
> +	INIT_LIST_HEAD(&q->done_list);
> +	spin_lock_init(&q->done_lock);
> +	init_waitqueue_head(&q->done_wq);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(vb2_queue_init);
> +
> +/**
> + * vb2_queue_release() - stop streaming, release the queue and free memory
> + * @q:		videobuf2 queue
> + *
> + * This function stops streaming and performs necessary clean ups, including
> + * freeing video buffer memory. The driver is responsible for freeing
> + * the vb2_queue structure itself.
> + */
> +void vb2_queue_release(struct vb2_queue *q)
> +{
> +	mutex_lock(&q->vb_lock);
> +
> +	__vb2_queue_cancel(q);
> +	__vb2_queue_free(q);
> +
> +	mutex_unlock(&q->vb_lock);
> +}
> +EXPORT_SYMBOL_GPL(vb2_queue_release);
> +
> +MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
> +MODULE_AUTHOR("Pawel Osciak");
> +MODULE_LICENSE("GPL");
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> new file mode 100644
> index 0000000..d51c973
> --- /dev/null
> +++ b/include/media/videobuf2-core.h
> @@ -0,0 +1,337 @@
> +/*
> + * videobuf2-core.h - V4L2 driver helper framework
> + *
> + * Copyright (C) 2010 Samsung Electronics
> + *
> + * Author: Pawel Osciak <p.osciak@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation.
> + */
> +#ifndef _MEDIA_VIDEOBUF2_CORE_H
> +#define _MEDIA_VIDEOBUF2_CORE_H
> +
> +#include <linux/mutex.h>
> +#include <linux/mm_types.h>
> +#include <linux/videodev2.h>
> +#include <linux/poll.h>
> +
> +/**
> + * enum vb2_buffer_state - current video buffer state
> + * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control
> + * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver
> + * @VB2_BUF_STATE_ACTIVE:	buffer queued in driver and possibly used
> + *				in a hardware operation
> + * @VB2_BUF_STATE_DONE:		buffer returned from driver to videobuf, but
> + *				not yet dequeued to userspace
> + * @VB2_BUF_STATE_ERROR:	same as above, but the operation on the buffer
> + *				has ended with an error, which will be reported
> + *				to the userspace when it is dequeued
> + */
> +enum vb2_buffer_state {
> +	VB2_BUF_STATE_DEQUEUED,
> +	VB2_BUF_STATE_QUEUED,
> +	VB2_BUF_STATE_ACTIVE,
> +	VB2_BUF_STATE_DONE,
> +	VB2_BUF_STATE_ERROR,
> +};
> +
> +/**
> + * struct vb2_plane - private videobuf per-plane info
> + * @mem_priv:	allocator-specific, per-memory buffer private structure
> + * @mapped:	set if the plane is mapped
> + */
> +struct vb2_plane {
> +	void			*mem_priv;
> +	int			mapped:1;
> +};
> +
> +/**
> + * struct vb2_buffer - represents a video buffer
> + * @v4l2_buf:		struct v4l2_buffer associated with this buffer; can
> + *			be read by the driver and relevant entries can be
> + *			changed by the driver in case of CAPTURE types
> + *			(such as timestamp)
> + * @v4l2_planes:	struct v4l2_planes associated with this buffer; can
> + *			be read by the driver and relevant entries can be
> + *			changed by the driver in case of CAPTURE types
> + *			(such as bytesused); NOTE that even for single-planar
> + *			types, the v4l2_planes[0] struct should be used
> + *			instead of v4l2_buf for filling bytesused - drivers
> + *			should use the vb2_set_plane_payload() function for that
> + * @vb2_queue:		the queue to which this driver belongs
> + * @drv_entry:		list entry to be used by driver for storing the buffer
> + * @num_planes:		number of planes in the buffer
> + *			on an internal driver queue
> + * @state:		current buffer state; do not change
> + * @queued_entry:	entry on the queued buffers list, which holds all
> + *			buffers queued from userspace
> + * @done_entry:		entry on the list that stores all buffers ready to
> + *			be dequeued to userspace
> + * @planes:		private per-plane information; do not change
> + * @num_planes_mapped:	number of mapped planes; do not change
> + */
> +struct vb2_buffer {
> +	struct v4l2_buffer	v4l2_buf;
> +	struct v4l2_plane	v4l2_planes[VIDEO_MAX_PLANES];
> +
> +	struct vb2_queue	*vb2_queue;
> +
> +	struct list_head	drv_entry;
> +	unsigned int		num_planes;
> +
> +/* Private: internal use only */
> +	enum vb2_buffer_state	state;
> +
> +	struct list_head	queued_entry;
> +	struct list_head	done_entry;
> +
> +	struct vb2_plane	planes[VIDEO_MAX_PLANES];
> +	unsigned int		num_planes_mapped;
> +};
> +
> +/**
> + * struct vb2_ops - driver-specific callbacks
> + * @queue_negotiate:	called from a VIDIOC_REQBUFS handler, before
> + *			memory allocation; driver should return the required
> + *			number of buffers in num_buffers and the required number
> + *			of planes per buffer in num_planes
> + * @plane_setup:	called before memory allocation num_planes times;
> + *			driver should return the required size of plane number
> + *			plane_no
> + * @buf_queue:		passes buffer vb to the driver; driver may use the
> + *			vb->drv_entry member to store the buffer on its internal
> + *			queue and start hardware operation on this buffer;
> + * @buf_init:		called once after allocating a buffer (in MMAP case)
> + *			or after acquiring a new USERPTR buffer; drivers may
> + *			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_finish:		called before every dequeue of the buffer back to
> + *			userspace; drivers may perform any operations required
> + *			before userspace accesses the buffer; optional
> + * @buf_cleanup:	called once before the buffer is freed; drivers may
> + *			perform any additional cleanup; optional
> + */
> +struct vb2_ops {
> +	int (*queue_negotiate)(struct vb2_queue *q, unsigned int *num_buffers,
> +				unsigned int *num_planes);
> +	int (*plane_setup)(struct vb2_queue *q,
> +			   unsigned int plane_no, unsigned long *plane_size);
> +	void (*buf_queue)(struct vb2_buffer *vb);
> +
> +	int (*buf_init)(struct vb2_buffer *vb);
> +	int (*buf_prepare)(struct vb2_buffer *vb);
> +	int (*buf_finish)(struct vb2_buffer *vb);
> +	void (*buf_cleanup)(struct vb2_buffer *vb);
> +};
> +
> +/**
> + * struct vb2_queue - a videobuf queue
> + *
> + * @type:	current queue type
> + * @memory:	current memory type used
> + * @drv_priv:	driver private data, passed on vb2_queue_init
> + * @bufs:	videobuf buffer structures
> + * @num_buffers: number of allocated/used buffers
> + * @vb_lock:	for ioctl handler and queue state changes synchronization
> + * @queued_list: list of buffers currently queued from userspace
> + * @done_list:	list of buffers ready to be dequeued to userspace
> + * @done_lock:	lock to protect done_list list
> + * @done_wq:	waitqueue for processes waiting for buffers ready to be dequeued
> + * @drv_lock:	driver lock for synchronization between driver and videobuf,
> + *		passed on vb2_queue_init
> + * @ops:	driver-specific callbacks
> + * @alloc_ctx:	memory type/allocator-specific callbacks
> + * @streaming:	current streaming state
> + * @userptr_supported: true if queue supports USERPTR types
> + * @mmap_supported: true if queue supports MMAP types
> + */
> +struct vb2_queue {
> +	enum v4l2_buf_type		type;
> +	enum v4l2_memory		memory;
> +	void				*drv_priv;
> +
> +/* private: internal use only */
> +	struct vb2_buffer		*bufs[VIDEO_MAX_FRAME];
> +	unsigned int			num_buffers;
> +
> +	struct mutex			vb_lock;
> +	struct list_head		queued_list;
> +
> +	struct list_head		done_list;
> +	spinlock_t			done_lock;
> +	wait_queue_head_t		done_wq;
> +
> +	spinlock_t			*drv_lock;
> +
> +	const struct vb2_ops		*ops;
> +	const struct vb2_alloc_ctx	*alloc_ctx[VIDEO_MAX_PLANES];
> +
> +	int				streaming:1;
> +	int				userptr_supported:1;
> +	int				mmap_supported:1;
> +};
> +
> +/* The below functions are documented in videobuf2-core.c */
> +void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
> +unsigned long vb2_plane_paddr(struct vb2_buffer *vb, unsigned int plane_no);
> +void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state);
> +bool vb2_has_consumers(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_queue_init(struct vb2_queue *q, const struct vb2_ops *ops,
> +			const struct vb2_alloc_ctx *alloc_ctx,
> +			spinlock_t *drv_lock, enum v4l2_buf_type type,
> +			void *drv_priv);
> +void vb2_queue_release(struct vb2_queue *q);
> +
> +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b);
> +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking);
> +
> +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type);
> +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type);
> +
> +int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma);
> +unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait);
> +
> +/**
> + * vb2_get_drv_priv() - return driver private data associated with the queue
> + * @q:		videobuf queue
> + */
> +static inline void *vb2_get_drv_priv(struct vb2_queue *q)
> +{
> +	return q->drv_priv;
> +}
> +
> +/**
> + * vb2_set_plane_payload() - set bytesused for the plane plane_no
> + * @vb:		buffer for which plane payload should be set
> + * @plane_no:	plane number for which payload should be set
> + * @size:	payload in bytes
> + */
> +static inline void vb2_set_plane_payload(struct vb2_buffer *vb,
> +				 unsigned int plane_no, unsigned long size)
> +{
> +	if (!V4L2_TYPE_IS_MULTIPLANAR(vb->vb2_queue->type) && plane_no == 0)
> +		vb->v4l2_buf.bytesused = size;
> +	else
> +		vb->v4l2_planes[plane_no].bytesused = size;
> +}
> +
> +/**
> + * vb2_plane_size() - return plane size in bytes
> + * @vb:		buffer for which plane size should be returned
> + * @plane_no:	plane number for which size should be returned
> + */
> +static inline unsigned long
> +vb2_plane_size(struct vb2_buffer *vb, unsigned int plane_no)
> +{
> +	if (plane_no < vb->num_planes)
> +		return vb->v4l2_planes[plane_no].length;
> +	else
> +		return 0;
> +}
> +
> +/**
> + * vb2_set_alloc_ctx() - use to assign a allocator context for a plane
> + * @q:		videobuf queue
> + * @alloc_ctx:	allocator context to be assigned
> + * @plane_no:	plane number to which the context is to be assigned
> + *
> + * This function can be used to assign additional allocator contexts
> + * on a per-plane basis, if a driver requires such feature.
> + * When a driver passes an allocator context to the vb2_queue_init call,
> + * it is initially assigned to all planes. Driver can then use this call
> + * to selectively assign additional contexts to particular planes.
> + * A context assigned to plane_no will be used for memory operations
> + * on plane number plane_no for all buffers.
> + */
> +static inline void
> +vb2_set_alloc_ctx(struct vb2_queue *q, struct vb2_alloc_ctx *alloc_ctx,
> +			unsigned int plane_no)
> +{
> +	if (plane_no < VIDEO_MAX_PLANES)
> +		q->alloc_ctx[plane_no] = alloc_ctx;
> +}
> +struct vb2_mem_ops;
> +
> +/**
> + * struct vb2_alloc_ctx - allocator/memory handler-specific context
> + * @mem_ops:	memory operations used by the current context
> + *
> + * This structure is passed to the alloc() call and can be used to store
> + * additional allocator private data. In such case it can be embedded in
> + * a allocator private structure as its first member.
> + * In more complicated cases, separate contexts can be assigned to each plane,
> + * if required. This would allow separate memory allocation/handling strategies
> + * for each plane, which is useful for drivers requiring different memory types
> + * and/or handling for each plane.
> + *
> + * See videobuf2-vmalloc.c and videobuf2-dma-coherent.c for example usage.
> + */
> +struct vb2_alloc_ctx {
> +	const struct vb2_mem_ops	*mem_ops;
> +};
> +
> +/**
> + * struct vb2_mem_ops - memory handling/memory allocator operations
> + * @alloc:	allocate video memory and, optionally, allocator private data,
> + *		return NULL on failure or a pointer to allocator private,
> + *		per-buffer data on success, NULL on failure; the returned
> + *		private structure will then be passed as buf_priv argument
> + *		to other ops in this structure
> + * @put:	inform the allocator that the buffer will no longer be used;
> + *		usually will result in the allocator freeing the buffer (if
> + *		no other users of this buffer are present); the buf_priv
> + *		argument is the allocator private per-buffer structure
> + *		previously returned from the alloc callback
> + * @get_userptr: acquire userspace memory for a hardware operation; used for
> + *		 USERPTR memory types; vaddr is the address passed to the
> + *		 videobuf layer when queuing a video buffer of USERPTR type;
> + *		 should return an allocator private per-buffer structure
> + *		 associated with the buffer on success, NULL on failure;
> + *		 the returned private structure will then be passed as buf_priv
> + *		 argument to other ops in this structure
> + * @put_userptr: inform the allocator that a USERPTR buffer will no longer
> + *		 be used
> + * @vaddr:	return a kernel virtual address to a given memory buffer
> + *		associated with the passed private structure or NULL if no
> + *		such mapping exists
> + * @paddr:	return a physical address to a given memory buffer associated
> + *		with the passed private structure or NULL if not available
> + * @num_users:	return the current number of users of a memory buffer;
> + *		return 1 if the videobuf layer (or actually the driver using
> + *		it) is the only user
> + * @mmap:	setup a userspace mapping for a given memory buffer under
> + *		the provided virtual memory region
> + *
> + * Required ops for USERPTR types: get_userptr, put_userptr.
> + * Required ops for MMAP types: alloc, put, num_users, mmap.
> + */
> +struct vb2_mem_ops {
> +	void		*(*alloc)(const struct vb2_alloc_ctx *alloc_ctx,
> +					unsigned long size);
> +	void		(*put)(void *buf_priv);
> +
> +	void		*(*get_userptr)(unsigned long vaddr,
> +						unsigned long size);
> +	void		(*put_userptr)(void *buf_priv);
> +
> +	void		*(*vaddr)(void *buf_priv);
> +	unsigned long	(*paddr)(void *buf_priv);
> +	unsigned int	(*num_users)(void *buf_priv);
> +
> +	int		(*mmap)(void *buf_priv, struct vm_area_struct *vma);
> +};
> +
> +
> +#endif /* _MEDIA_VIDEOBUF2_CORE_H */


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

* Re: [PATCH v1 2/7] v4l: videobuf2: add generic memory handling routines
  2010-09-09  9:19 ` [PATCH v1 2/7] v4l: videobuf2: add generic memory handling routines Pawel Osciak
@ 2010-09-09 17:34   ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 24+ messages in thread
From: Mauro Carvalho Chehab @ 2010-09-09 17:34 UTC (permalink / raw)
  To: Pawel Osciak; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Em 09-09-2010 06:19, Pawel Osciak escreveu:
> Add generic memory handling routines for userspace pointer handling,
> contiguous memory verification and mapping.
> 
> Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/Kconfig            |    3 +
>  drivers/media/video/Makefile           |    2 +
>  drivers/media/video/videobuf2-memops.c |  181 ++++++++++++++++++++++++++++++++
>  include/media/videobuf2-memops.h       |   27 +++++
>  4 files changed, 213 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/videobuf2-memops.c
>  create mode 100644 include/media/videobuf2-memops.h
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index 5764443..e4ac3e2 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -51,6 +51,9 @@ config V4L2_MEM2MEM_DEV
>  config VIDEOBUF2_CORE
>  	tristate
>  
> +config VIDEOBUF2_MEMOPS
> +	tristate
> +
>  
>  #
>  # Multimedia Video device configuration
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index e66f53b..ab5b521 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -118,6 +118,8 @@ obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o
>  obj-$(CONFIG_VIDEO_BTCX)  += btcx-risc.o
>  
>  obj-$(CONFIG_VIDEOBUF2_CORE)		+= videobuf2-core.o
> +obj-$(CONFIG_VIDEOBUF2_MEMOPS)		+= videobuf2-memops.o
> +
>  
>  obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
>  
> diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c
> new file mode 100644
> index 0000000..fa76983
> --- /dev/null
> +++ b/drivers/media/video/videobuf2-memops.c
> @@ -0,0 +1,181 @@
> +/*
> + * videobuf2-memops.c - generic memory handling routines for videobuf2
> + *
> + * Copyright (C) 2010 Samsung Electronics
> + *
> + * Author: Pawel Osciak <p.osciak@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation.

Hmm... it should be saying GPL version 2 (or v2+). Please fix on all files 
for your next set of RFC patches.

> + */
> +
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/vmalloc.h>
> +#include <linux/cma.h>
> +#include <linux/mm.h>
> +#include <linux/sched.h>
> +#include <linux/file.h>
> +
> +#include <media/videobuf2-core.h>
> +
> +/**
> + * vb2_contig_verify_userptr() - verify contiguity of a userspace-mapped memory
> + * @vma:	virtual memory region which maps the physical memory
> + *		to be verified
> + * @vaddr:	starting virtual address of the area to be verified
> + * @size:	size of the area to be verified
> + * @paddr:	will return physical address for the given vaddr
> + *
> + * This function will go through memory area of size size mapped at vaddr and
> + * verify that the underlying physical pages are contiguous.
> + *
> + * Returns 0 on success and a physical address to the memory pointed
> + * to by vaddr in paddr.
> + */
> +int vb2_contig_verify_userptr(struct vm_area_struct *vma,
> +				unsigned long vaddr, unsigned long size,
> +				unsigned long *paddr)
> +{
> +	struct mm_struct *mm = current->mm;
> +	unsigned long offset;
> +	unsigned long vma_size;
> +	unsigned long curr_pfn, prev_pfn;
> +	unsigned long num_pages;
> +	int ret = -EINVAL;
> +	unsigned int i;
> +
> +	offset = vaddr & ~PAGE_MASK;
> +
> +	down_read(&mm->mmap_sem);
> +
> +	vma = find_vma(mm, vaddr);
> +	if (!vma) {
> +		printk(KERN_ERR "Invalid userspace address\n");
> +		goto done;
> +	}
> +
> +	vma_size = vma->vm_end - vma->vm_start;
> +
> +	if (size > vma_size - offset) {
> +		printk(KERN_ERR "Region too small\n");
> +		goto done;
> +	}
> +	num_pages = (size + offset) >> PAGE_SHIFT;
> +
> +	ret = follow_pfn(vma, vaddr, &curr_pfn);
> +	if (ret) {
> +		printk(KERN_ERR "Invalid userspace address\n");
> +		goto done;
> +	}
> +
> +	*paddr = (curr_pfn << PAGE_SHIFT) + offset;
> +
> +	for (i = 1; i < num_pages; ++i) {
> +		prev_pfn = curr_pfn;
> +		vaddr += PAGE_SIZE;
> +
> +		ret = follow_pfn(vma, vaddr, &curr_pfn);
> +		if (ret || curr_pfn != prev_pfn + 1) {
> +			printk(KERN_ERR "Invalid userspace address\n");
> +			ret = -EINVAL;
> +			break;
> +		}
> +	}
> +
> +done:
> +	up_read(&mm->mmap_sem);
> +	return ret;
> +}
> +
> +/**
> + * vb2_mmap_pfn_range() - map physical pages to userspace
> + * @vma:	virtual memory region for the mapping
> + * @paddr:	starting physical address of the memory to be mapped
> + * @size:	size of the memory to be mapped
> + * @vm_ops:	vm operations to be assigned to the created area
> + * @priv:	private data to be associated with the area
> + *
> + * Returns 0 on success.
> + */
> +int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
> +				unsigned long size,
> +				const struct vm_operations_struct *vm_ops,
> +				void *priv)
> +{
> +	int ret;
> +
> +	size = min_t(unsigned long, vma->vm_end - vma->vm_start, size);
> +
> +	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> +	ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT,
> +				size, vma->vm_page_prot);
> +	if (ret) {
> +		printk(KERN_ERR "Remapping memory failed, error: %d\n", ret);
> +		return ret;
> +	}
> +
> +	vma->vm_flags		|= VM_DONTEXPAND | VM_RESERVED;
> +	vma->vm_private_data	= priv;
> +	vma->vm_ops		= vm_ops;
> +
> +	vm_ops->open(vma);
> +
> +	printk(KERN_DEBUG "%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n",
> +			__func__, paddr, vma->vm_start, size);
> +
> +	return 0;
> +}
> +
> +/**
> + * vb2_get_userptr() - acquire an area pointed to by userspace addres vaddr
> + * @vaddr:	virtual userspace address to the given area
> + *
> + * This function attempts to acquire an area mapped in the userspace for
> + * the duration of a hardware operation.
> + *
> + * Returns a virtual memory region associated with the given vaddr on success
> + * or NULL.
> + */
> +struct vm_area_struct *vb2_get_userptr(unsigned long vaddr)
> +{
> +	struct mm_struct *mm = current->mm;
> +	struct vm_area_struct *vma;
> +
> +	down_read(&mm->mmap_sem);
> +
> +	vma = find_vma(mm, vaddr);
> +	if (!vma)
> +		goto done;
> +
> +	if (vma->vm_ops && vma->vm_ops->open)
> +		vma->vm_ops->open(vma);
> +
> +	if (vma->vm_file)
> +		get_file(vma->vm_file);
> +
> +done:
> +	up_read(&mm->mmap_sem);
> +	return vma;
> +}
> +
> +/**
> + * vb2_put_userptr() - release a userspace memory area
> + * @vma:	virtual memory region associated with the area to be released
> + *
> + * This function releases the previously acquired memory area after a hardware
> + * operation.
> + */
> +void vb2_put_userptr(struct vm_area_struct *vma)
> +{
> +	if (!vma)
> +		return;
> +
> +	if (vma->vm_file)
> +		fput(vma->vm_file);
> +
> +	if (vma->vm_ops && vma->vm_ops->close)
> +		vma->vm_ops->close(vma);
> +}
> diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h
> new file mode 100644
> index 0000000..23db249
> --- /dev/null
> +++ b/include/media/videobuf2-memops.h
> @@ -0,0 +1,27 @@
> +/*
> + * videobuf2-memops.h - generic memory handling routines for videobuf2
> + *
> + * Copyright (C) 2010 Samsung Electronics
> + *
> + * Author: Pawel Osciak <p.osciak@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <media/videobuf2-core.h>
> +
> +int vb2_contig_verify_userptr(struct vm_area_struct *vma,
> +				unsigned long vaddr, unsigned long size,
> +				unsigned long *paddr);
> +
> +int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr,
> +				unsigned long size,
> +				const struct vm_operations_struct *vm_ops,
> +				void *priv);
> +
> +struct vm_area_struct *vb2_get_userptr(unsigned long vaddr);
> +
> +void vb2_put_userptr(struct vm_area_struct *vma);
> +


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

* Re: [PATCH/RFC v1 0/7] Videobuf2 framework
  2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
                   ` (7 preceding siblings ...)
  2010-09-09  9:26 ` [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
@ 2010-09-09 17:53 ` Mauro Carvalho Chehab
  2010-09-10  3:20   ` Pawel Osciak
  8 siblings, 1 reply; 24+ messages in thread
From: Mauro Carvalho Chehab @ 2010-09-09 17:53 UTC (permalink / raw)
  To: Pawel Osciak; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Em 09-09-2010 06:19, Pawel Osciak escreveu:
> Hello,
> 
> These patches add a new driver framework for Video for Linux 2 driver
> - Videobuf2.

I didn't test the patches, but, from a source code review, they seem
on a good shape. I did a few comments on some patches. There are a few
missing features for them to be used with real drivers:

1) it lacks implementation of read() method. This means that vivi driver
has a regression, as it currently supports it.

2) it lacks OVERLAY mode. We can probably mark this feature as deprecated,
avoiding the need of implementing it on videobuf2, but we need a patch
for Documentation/feature-removal-schedule.txt,  in order to allow the
migration of the existing drivers like bttv and saa7134, where this feature
is implemented, of course if people agree that this is the better way;

3) it lacks the implementation of videobuf-dvb;

4) it lacks an implementation for DMA S/G.

We need to address all the above issues, in order to use it, otherwise the
migration of existing drivers would cause regressions, as features will be
missing.

Cheers,
Mauro.

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

* Re: [PATCH/RFC v1 0/7] Videobuf2 framework
  2010-09-09 17:53 ` Mauro Carvalho Chehab
@ 2010-09-10  3:20   ` Pawel Osciak
  2010-09-10  4:27     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 24+ messages in thread
From: Pawel Osciak @ 2010-09-10  3:20 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Hello Mauro,

On 09/10/2010 02:53 AM, Mauro Carvalho Chehab wrote:
> Em 09-09-2010 06:19, Pawel Osciak escreveu:
> > Hello,
> >
> > These patches add a new driver framework for Video for Linux 2 driver
> > - Videobuf2.
>
> I didn't test the patches, but, from a source code review, they seem
> on a good shape. I did a few comments on some patches. There are a few
> missing features for them to be used with real drivers:
>

Thank you for review. I will address your in-code comments in a bit, now
for general comments.

> 1) it lacks implementation of read() method. This means that vivi driver
> has a regression, as it currently supports it.

Yes, read() is not yet implemented. I guess it is not a feature that would
be deprecated, right? There are some problems with read that are 
problematic:
- not every device / memory type will be able to use it, as we need a way
to allocate kernel memory for an operation and not every memory type may
support it
- there is no way to use it with multi-planar API, as it is not possible
to read from multiple memory buffers (having multiple pointers) with read...

But I guess it it could be added to work as before with single-planar 
buffers
only, for compatibility.

Also I am thinking about where read() should be implemented. It is something
that will have to be emulated on top of normal streaming, as it is now done
in videobuf1. So maybe a more generic read implementation would be a good
idea? A separate layer that would be implementing read on top of streaming?
It would not have to be limited to videobuf only then, drivers that support
streaming but not using videobuf would be able to use it too.

> 2) it lacks OVERLAY mode. We can probably mark this feature as deprecated,
> avoiding the need of implementing it on videobuf2, but we need a patch
> for Documentation/feature-removal-schedule.txt,  in order to allow the
> migration of the existing drivers like bttv and saa7134, where this 
> feature
> is implemented, of course if people agree that this is the better way;

If you think this feature can be deprecated, it might be a good idea. If 
not,
I guess I will be able to take a stab at it when working with saa7134 and
bttv, right? Should not require too much work to add. I just did not have
a device to test it on, so I did not add it.

It would be great if you could make the decision about what to do with this.
I will try to follow your opinion and suggestions.

> 3) it lacks the implementation of videobuf-dvb;

Yes... I have no experience with DVB, but I guess I can just get a DVB card
and start hacking. Could you recommend a card? Technisat SkyStar 2?

> 4) it lacks an implementation for DMA S/G.

Yes, I am planning to work on this next, I will be testing on saa7134 
and maybe
something else as well, probably a bttv. Please let me know if you have any
suggestions or recommendations on which drivers I should be focusing.

I am also hoping for Laurent's comments on this, since he was working on SG
much more than I did.

> We need to address all the above issues, in order to use it, otherwise the
> migration of existing drivers would cause regressions, as features will be
> missing.

Right, of course. Videobuf2 is a long time effort and I hope it will be 
evolving
gradually. What I would like to assure you about is that I will not 
abandon it
and will not limit myself to working only on embedded or Samsung device 
support.

Videobuf2 is not only my in-Samsung project and I intend to be working on it
"outside my work responsibilities" as well, supporting desktop and other
platforms, as my time permits.

I would like to address whatever you and others think is necessary and make
videobuf2 useful for as many devices as possible. This is also why I am 
posting
it earlier rather than later. I hope for people to point out features 
they would
require and give their opinions early. I think that working together on 
this we
will be able to gradually come up with a solution that would be useful and
satisfying for most people.


I would like to ask about your idea for the strategy of its development and
inclusion. We will be posting a few drivers based on videobuf2 soon. Do 
you think
it could be merged gradually, as features are added?

It would be great if we could work on it and merge it gradually. Some 
people could
already start using it in new drivers, others could start adding 
features to it,
the API would be stabilizing and we would be having a solid base to work 
on. This is
a long-term effort for many months or maybe years. It goes without 
saying that it
will be harder to add everything in one go.

Others might not be eager to adopt it if it is not an official effort 
either.
And I cannot deny it, it would also make my job easier. As I said, I do 
not intend
to leave it as it is now nor work on embedded support only.

Also, we do not have to migrate all old drivers in one go, but that also 
goes without
saying. As we discussed, videobuf1 will not go away anytime soon.

I think we should consider new drivers as well here. It is troublesome 
when they
provide their custom code instead of using a framework, as is the case 
with some
drivers that have been posted in the previous months and some ongoing 
ones, but this
is also obvious.

Please let me know your opinion. It would be good if we could come up 
with a list
of features that have to be added before merging and others that could 
be added
gradually and a general plan for development of videbuf2.

Thank you!

-- 
Best regards,
Pawel Osciak
Linux Platform Group
Samsung Poland R&D Center

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

* Re: [PATCH/RFC v1 0/7] Videobuf2 framework
  2010-09-10  3:20   ` Pawel Osciak
@ 2010-09-10  4:27     ` Mauro Carvalho Chehab
  2010-09-10  7:38       ` Marek Szyprowski
  0 siblings, 1 reply; 24+ messages in thread
From: Mauro Carvalho Chehab @ 2010-09-10  4:27 UTC (permalink / raw)
  To: Pawel Osciak; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Em 10-09-2010 00:20, Pawel Osciak escreveu:
> Hello Mauro,
> 
> On 09/10/2010 02:53 AM, Mauro Carvalho Chehab wrote:
>> Em 09-09-2010 06:19, Pawel Osciak escreveu:
>> > Hello,
>> >
>> > These patches add a new driver framework for Video for Linux 2 driver
>> > - Videobuf2.
>>
>> I didn't test the patches, but, from a source code review, they seem
>> on a good shape. I did a few comments on some patches. There are a few
>> missing features for them to be used with real drivers:
>>
> 
> Thank you for review. I will address your in-code comments in a bit, now
> for general comments.
> 
>> 1) it lacks implementation of read() method. This means that vivi driver
>> has a regression, as it currently supports it.
> 
> Yes, read() is not yet implemented. I guess it is not a feature that would
> be deprecated, right? 

Yes, there are no plans to deprecate it. Also, some devices like cx88 and bttv
allows receiving simultaneous streams, one via mmap, and another via read().
This is used by some applications to allow recording video via ffmpeg/mencoder
using read(), while the main application is displaying video using mmap.

> There are some problems with read that are problematic:
> - not every device / memory type will be able to use it, as we need a way
> to allocate kernel memory for an operation and not every memory type may
> support it
> - there is no way to use it with multi-planar API, as it is not possible
> to read from multiple memory buffers (having multiple pointers) with read...
> 
> But I guess it it could be added to work as before with single-planar buffers
> only, for compatibility.

Yeah, a multi-planar read() doesn't seem to make sense.

> Also I am thinking about where read() should be implemented. It is something
> that will have to be emulated on top of normal streaming, as it is now done
> in videobuf1. So maybe a more generic read implementation would be a good
> idea? A separate layer that would be implementing read on top of streaming?
> It would not have to be limited to videobuf only then, drivers that support
> streaming but not using videobuf would be able to use it too.

I don't think we should try to cover drivers that don't use videobuf.

>> 2) it lacks OVERLAY mode. We can probably mark this feature as deprecated,
>> avoiding the need of implementing it on videobuf2, but we need a patch
>> for Documentation/feature-removal-schedule.txt,  in order to allow the
>> migration of the existing drivers like bttv and saa7134, where this feature
>> is implemented, of course if people agree that this is the better way;
> 
> If you think this feature can be deprecated, it might be a good idea. If not,
> I guess I will be able to take a stab at it when working with saa7134 and
> bttv, right? Should not require too much work to add. I just did not have
> a device to test it on, so I did not add it.
> 
> It would be great if you could make the decision about what to do with this.
> I will try to follow your opinion and suggestions.

>From Helsinki's comments, it seems that most people believe that we can deprecate it,
as a feature similar to OVERLAY can be done by using USERPTR. Also, there's currently
a problem with userspace applications and OVERLAY, and none is working to fix it.

So, if nobody objects (and fixes the userspace applications), then I think that the
better is to deprecate it.

>> 3) it lacks the implementation of videobuf-dvb;
> 
> Yes... I have no experience with DVB, but I guess I can just get a DVB card
> and start hacking. Could you recommend a card? Technisat SkyStar 2?

You should get one that uses videobuf-dvb. I would seek for one using bttv, saa7134, cx88
or em28xx. It is hard to me to point you to an specific device, especially since I am
at ISDB-T area. There are lots of supported devices, so the better is to just take a look
at *-cards.c and seek for one DVB device that you could find near you. I'm sure you'll
find lots of saa7134 cards and em28xx-based sticks with DVB support.

>> 4) it lacks an implementation for DMA S/G.
> 
> Yes, I am planning to work on this next, I will be testing on saa7134 and maybe
> something else as well, probably a bttv. Please let me know if you have any
> suggestions or recommendations on which drivers I should be focusing.
> 
> I am also hoping for Laurent's comments on this, since he was working on SG
> much more than I did.

bttv and saa7134 are the more complete/complex implementations. If you can make
them work, porting videobuf2 to the other drivers will be trivial.

>> We need to address all the above issues, in order to use it, otherwise the
>> migration of existing drivers would cause regressions, as features will be
>> missing.
> 
> Right, of course. Videobuf2 is a long time effort and I hope it will be evolving
> gradually. What I would like to assure you about is that I will not abandon it
> and will not limit myself to working only on embedded or Samsung device support.
> 
> Videobuf2 is not only my in-Samsung project and I intend to be working on it
> "outside my work responsibilities" as well, supporting desktop and other
> platforms, as my time permits.

Ok, good to know.

> I would like to address whatever you and others think is necessary and make
> videobuf2 useful for as many devices as possible. This is also why I am posting
> it earlier rather than later. I hope for people to point out features they would
> require and give their opinions early. I think that working together on this we
> will be able to gradually come up with a solution that would be useful and
> satisfying for most people.
> 

After having all features implemented, I can test and help to port it to other hardware
that uses videobuf1. While the strategy of "forking" videobuf is the proper way, we
need to make sure that we'll have a clean way to deprecate the legacy videobuf, as
maintaining both makes support harder, as time goes by.

> I would like to ask about your idea for the strategy of its development and
> inclusion. We will be posting a few drivers based on videobuf2 soon. Do you think
> it could be merged gradually, as features are added?
> 
> It would be great if we could work on it and merge it gradually. Some people could
> already start using it in new drivers, others could start adding features to it,
> the API would be stabilizing and we would be having a solid base to work on. This is
> a long-term effort for many months or maybe years. It goes without saying that it
> will be harder to add everything in one go.
> 
> Others might not be eager to adopt it if it is not an official effort either.
> And I cannot deny it, it would also make my job easier. As I said, I do not intend
> to leave it as it is now nor work on embedded support only.

In order for me to accept vivi patches, you need to implement read(), to avoid a
regression. IMHO, merging vivi earlier helps other developers to better understand
what's needed.

The next needed feature, IMHO, is the videobuf-dvb. With both read() and videobuf-dvb,
we can port an existing real case driver like em28xx and tm6000. This will allow checking
its usage, and advantages/disadvantages when compared to videobuf1. For example, it is
expected a performance increase with videobuf2, as the code is now simpler, but only
doing a perf against the same driver using vb1 and vb2 will actually show if performance
is equivalent or better, or if is there any drawback at vb2 implementation.

Implementing videobuf DMA S/G won't help if you don't have read() and dvb supported, as
no real driver could be migrated to vb2 without causing regressions.

So, for me, you should address those technical questions, on this order:
	- read()
	- dvb
	- dma s/g

With respect to OVERLAY, just send me a RFC patch deprecating it. Let's wait for one or two
weeks for people to complain. If nobody complains, let's remove it for 2.6.37 or 2.6.38.

> Also, we do not have to migrate all old drivers in one go, but that also goes without
> saying. As we discussed, videobuf1 will not go away anytime soon.

Agreed.

> I think we should consider new drivers as well here. It is troublesome when they
> provide their custom code instead of using a framework, as is the case with some
> drivers that have been posted in the previous months and some ongoing ones, but this
> is also obvious.

New drivers are easier, as they can be conceived using vb2.

> Please let me know your opinion. It would be good if we could come up with a list
> of features that have to be added before merging and others that could be added
> gradually and a general plan for development of videbuf2.

Cheers,
Mauro

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

* Re: [PATCH/RFC v1 0/7] Videobuf2 framework
  2010-09-10  4:27     ` Mauro Carvalho Chehab
@ 2010-09-10  7:38       ` Marek Szyprowski
  2010-09-10  8:22         ` Hans Verkuil
  2010-09-10 12:15         ` Mauro Carvalho Chehab
  0 siblings, 2 replies; 24+ messages in thread
From: Marek Szyprowski @ 2010-09-10  7:38 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Pawel Osciak, linux-media, kyungmin.park, t.fujak

Hello,

On 2010-09-10 13:27, Mauro Carvalho Chehab wrote:

>>> 1) it lacks implementation of read() method. This means that vivi driver
>>> has a regression, as it currently supports it.
>>
>> Yes, read() is not yet implemented. I guess it is not a feature that would
>> be deprecated, right?
>
> Yes, there are no plans to deprecate it. Also, some devices like cx88 and bttv
> allows receiving simultaneous streams, one via mmap, and another via read().
> This is used by some applications to allow recording video via ffmpeg/mencoder
> using read(), while the main application is displaying video using mmap.

Well, in my opinion such devices should provide two separate /dev/videoX 
nodes rather than hacking with mmap and read access types.

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center

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

* Re: [PATCH/RFC v1 0/7] Videobuf2 framework
  2010-09-10  7:38       ` Marek Szyprowski
@ 2010-09-10  8:22         ` Hans Verkuil
  2010-09-10  8:26           ` Devin Heitmueller
  2010-09-10 13:50           ` Andy Walls
  2010-09-10 12:15         ` Mauro Carvalho Chehab
  1 sibling, 2 replies; 24+ messages in thread
From: Hans Verkuil @ 2010-09-10  8:22 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: Mauro Carvalho Chehab, Pawel Osciak, linux-media, kyungmin.park, t.fujak

On Friday, September 10, 2010 09:38:44 Marek Szyprowski wrote:
> Hello,
> 
> On 2010-09-10 13:27, Mauro Carvalho Chehab wrote:
> 
> >>> 1) it lacks implementation of read() method. This means that vivi driver
> >>> has a regression, as it currently supports it.
> >>
> >> Yes, read() is not yet implemented. I guess it is not a feature that would
> >> be deprecated, right?
> >
> > Yes, there are no plans to deprecate it. Also, some devices like cx88 and bttv
> > allows receiving simultaneous streams, one via mmap, and another via read().
> > This is used by some applications to allow recording video via ffmpeg/mencoder
> > using read(), while the main application is displaying video using mmap.
> 
> Well, in my opinion such devices should provide two separate /dev/videoX 
> nodes rather than hacking with mmap and read access types.

1) It is in use so you can't just drop it.
2) The read() API is actually very useful for video devices that deal with
   compressed video streams. E.g. you can do things like 'cat /dev/video0 >foo.mpg'

It's been a long standing wish to convert the ivtv and cx18 drivers to videobuf,
but it's always been too complex. With a new vb2 implementation it may become
actually possible.

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco

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

* Re: [PATCH/RFC v1 0/7] Videobuf2 framework
  2010-09-10  8:22         ` Hans Verkuil
@ 2010-09-10  8:26           ` Devin Heitmueller
  2010-09-10 13:50           ` Andy Walls
  1 sibling, 0 replies; 24+ messages in thread
From: Devin Heitmueller @ 2010-09-10  8:26 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Marek Szyprowski, Mauro Carvalho Chehab, Pawel Osciak,
	linux-media, kyungmin.park, t.fujak

On Fri, Sep 10, 2010 at 4:22 AM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> It's been a long standing wish to convert the ivtv and cx18 drivers to videobuf,
> but it's always been too complex. With a new vb2 implementation it may become
> actually possible.

FYI:  KernelLabs has done a port of cx18 to videobuf.  We just haven't
submitted it upstream yet.  I'm just mentioning this so nobody else
feels the urge to take a crack at it.

Devin

-- 
Devin J. Heitmueller - Kernel Labs
http://www.kernellabs.com

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

* Re: [PATCH/RFC v1 0/7] Videobuf2 framework
  2010-09-10  7:38       ` Marek Szyprowski
  2010-09-10  8:22         ` Hans Verkuil
@ 2010-09-10 12:15         ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 24+ messages in thread
From: Mauro Carvalho Chehab @ 2010-09-10 12:15 UTC (permalink / raw)
  To: Marek Szyprowski; +Cc: Pawel Osciak, linux-media, kyungmin.park, t.fujak

Em 10-09-2010 04:38, Marek Szyprowski escreveu:
> Hello,
> 
> On 2010-09-10 13:27, Mauro Carvalho Chehab wrote:
> 
>>>> 1) it lacks implementation of read() method. This means that vivi driver
>>>> has a regression, as it currently supports it.
>>>
>>> Yes, read() is not yet implemented. I guess it is not a feature that would
>>> be deprecated, right?
>>
>> Yes, there are no plans to deprecate it. Also, some devices like cx88 and bttv
>> allows receiving simultaneous streams, one via mmap, and another via read().
>> This is used by some applications to allow recording video via ffmpeg/mencoder
>> using read(), while the main application is displaying video using mmap.
> 
> Well, in my opinion such devices should provide two separate /dev/videoX nodes rather than hacking with mmap and read access types.

Why? V4L2 API allows to have multiple applications opening and streaming. There's nothing
wrong with that, since it is a common practice in Unix to allow multiple opens for the same
device.

Cheers,
Mauro

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

* Re: [PATCH/RFC v1 0/7] Videobuf2 framework
  2010-09-10  8:22         ` Hans Verkuil
  2010-09-10  8:26           ` Devin Heitmueller
@ 2010-09-10 13:50           ` Andy Walls
  1 sibling, 0 replies; 24+ messages in thread
From: Andy Walls @ 2010-09-10 13:50 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: Marek Szyprowski, Mauro Carvalho Chehab, Pawel Osciak,
	linux-media, kyungmin.park, t.fujak

On Fri, 2010-09-10 at 10:22 +0200, Hans Verkuil wrote:
> On Friday, September 10, 2010 09:38:44 Marek Szyprowski wrote:
> > Hello,
> > 
> > On 2010-09-10 13:27, Mauro Carvalho Chehab wrote:
> > 
> > >>> 1) it lacks implementation of read() method. This means that vivi driver
> > >>> has a regression, as it currently supports it.
> > >>
> > >> Yes, read() is not yet implemented. I guess it is not a feature that would
> > >> be deprecated, right?
> > >
> > > Yes, there are no plans to deprecate it. Also, some devices like cx88 and bttv
> > > allows receiving simultaneous streams, one via mmap, and another via read().
> > > This is used by some applications to allow recording video via ffmpeg/mencoder
> > > using read(), while the main application is displaying video using mmap.
> > 
> > Well, in my opinion such devices should provide two separate /dev/videoX 
> > nodes rather than hacking with mmap and read access types.
> 
> 1) It is in use so you can't just drop it.
> 2) The read() API is actually very useful for video devices that deal with
>    compressed video streams. E.g. you can do things like 'cat /dev/video0 >foo.mpg'
> 
> It's been a long standing wish to convert the ivtv and cx18 drivers to videobuf,
> but it's always been too complex. With a new vb2 implementation it may become
> actually possible.

Steven has mmap() mostly done for the cx18 YUV stream:

http://www.kernellabs.com/hg/~stoth/cx18-videobuf/

I provided him a slew of comments on the patchset,  The comments were
mostly just grunt work to move things around and clean it up than any
major flaws.  I only saw one problem that must be fixed before it is
usable for the masses, IIRC.

Maybe if there's a test case for trying out videobuf2, it's the cx18
driver where we want to use mmap() for YUV and read() for MPEG.  Note
Steven's changes allow one to tell the CX23418 to send YUV data in YUYV
format vs. HM12.

Regards,
Andy




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

* Re: [PATCH v1 7/7] v4l: videobuf2: add CMA allocator
  2010-09-09  9:19 ` [PATCH v1 7/7] v4l: videobuf2: add CMA allocator Pawel Osciak
@ 2010-09-15  8:55   ` han jonghun
  2010-09-15 20:25     ` Pawel Osciak
  0 siblings, 1 reply; 24+ messages in thread
From: han jonghun @ 2010-09-15  8:55 UTC (permalink / raw)
  To: Pawel Osciak; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Hello,

In vb2_cma_put if buf->refcount is 0, cma_free is called.
But vb2_cma_put is usually called from munmap.
In my opinion cma_free should be called from VIDIOC_REQBUFS(0) not munmap.

BRs,

2010/9/9 Pawel Osciak <p.osciak@samsung.com>:
> Add support for the CMA contiguous memory allocator to videobuf2.
>
> Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/Kconfig         |    5 +
>  drivers/media/video/Makefile        |    2 +
>  drivers/media/video/videobuf2-cma.c |  250 +++++++++++++++++++++++++++++++++++
>  include/media/videobuf2-cma.h       |   25 ++++
>  4 files changed, 282 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/videobuf2-cma.c
>  create mode 100644 include/media/videobuf2-cma.h
>
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index c2ea549..b63f377 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -65,6 +65,11 @@ config VIDEOBUF2_VMALLOC
>        select VIDEOBUF2_GEN_MEMOPS
>        tristate
>
> +config VIDEOBUF2_CMA
> +       depends on CMA
> +       select VIDEOBUF2_CORE
> +       select VIDEOBUF2_GEN_MEMOPS
> +       tristate
>
>  #
>  # Multimedia Video device configuration
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 20d359d..4146700 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -128,6 +128,8 @@ obj-$(CONFIG_VIDEOBUF2_VMALLOC)             += videobuf2_vmalloc.o
>  videobuf2_vmalloc-y                    := videobuf2-vmalloc.o \
>                                           videobuf2-memops.o
>
> +obj-$(CONFIG_VIDEOBUF2_CMA)            += videobuf2_cma.o
> +videobuf2_cma-y                                := videobuf2-cma.o videobuf2-memops.o
>
>  obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
>
> diff --git a/drivers/media/video/videobuf2-cma.c b/drivers/media/video/videobuf2-cma.c
> new file mode 100644
> index 0000000..c51e5a8
> --- /dev/null
> +++ b/drivers/media/video/videobuf2-cma.c
> @@ -0,0 +1,250 @@
> +/*
> + * videobuf2-cma.c - CMA memory allocator for videobuf2
> + *
> + * Copyright (C) 2010 Samsung Electronics
> + *
> + * Author: Pawel Osciak <p.osciak@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/cma.h>
> +#include <linux/mm.h>
> +#include <linux/sched.h>
> +#include <linux/file.h>
> +
> +#include <media/videobuf2-core.h>
> +#include <media/videobuf2-memops.h>
> +
> +struct vb2_cma_conf {
> +       struct vb2_alloc_ctx    alloc_ctx;
> +       struct device           *dev;
> +       const char              *type;
> +       unsigned long           alignment;
> +};
> +
> +struct vb2_cma_buf {
> +       struct vb2_cma_conf     *conf;
> +       dma_addr_t              paddr;
> +       unsigned long           size;
> +       unsigned int            refcount;
> +       struct vm_area_struct   *vma;
> +};
> +
> +static void *vb2_cma_alloc(const struct vb2_alloc_ctx *alloc_ctx,
> +                               unsigned long size)
> +{
> +       struct vb2_cma_conf *conf =
> +               container_of(alloc_ctx, struct vb2_cma_conf, alloc_ctx);
> +       struct vb2_cma_buf *buf;
> +
> +       buf = kzalloc(sizeof *buf, GFP_KERNEL);
> +       if (!buf)
> +               return ERR_PTR(-ENOMEM);
> +
> +       buf->paddr = cma_alloc(conf->dev, conf->type, size, conf->alignment);
> +       if (!buf->paddr) {
> +               printk(KERN_ERR "cma_alloc of size %ld failed\n", size);
> +               kfree(buf);
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       buf->conf = conf;
> +       buf->size = size;
> +       buf->refcount++;
> +
> +       printk(KERN_DEBUG "Allocated cma mem of size %ld at paddr=0x%08x\n",
> +                       buf->size, buf->paddr);
> +
> +       return buf;
> +}
> +
> +static void vb2_cma_put(void *buf_priv)
> +{
> +       struct vb2_cma_buf *buf = buf_priv;
> +
> +       buf->refcount--;
> +
> +       if (0 == buf->refcount) {
> +               cma_free(buf->paddr);
> +               kfree(buf);
> +       }
> +}
> +
> +static unsigned long vb2_cma_paddr(void *buf_priv)
> +{
> +       struct vb2_cma_buf *buf = buf_priv;
> +
> +       return buf->paddr;
> +}
> +
> +static unsigned int vb2_cma_num_users(void *buf_priv)
> +{
> +       struct vb2_cma_buf *buf = buf_priv;
> +
> +       return buf->refcount;
> +}
> +
> +static void vb2_cma_vm_open(struct vm_area_struct *vma)
> +{
> +       struct vb2_cma_buf *buf = vma->vm_private_data;
> +
> +       printk(KERN_DEBUG "%s cma_priv: %p, refcount: %d, "
> +                       "vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
> +                       vma->vm_start, vma->vm_end);
> +
> +       buf->refcount++;
> +}
> +
> +static void vb2_cma_vm_close(struct vm_area_struct *vma)
> +{
> +       struct vb2_cma_buf *buf = vma->vm_private_data;
> +
> +       printk(KERN_DEBUG "%s cma_priv: %p, refcount: %d, "
> +                       "vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
> +                       vma->vm_start, vma->vm_end);
> +
> +       vb2_cma_put(buf);
> +}
> +
> +static const struct vm_operations_struct vb2_cma_vm_ops = {
> +       .open = vb2_cma_vm_open,
> +       .close = vb2_cma_vm_close,
> +};
> +
> +static int vb2_cma_mmap(void *buf_priv, struct vm_area_struct *vma)
> +{
> +       struct vb2_cma_buf *buf = buf_priv;
> +
> +       if (!buf) {
> +               printk(KERN_ERR "No memory to map\n");
> +               return -EINVAL;
> +       }
> +
> +       return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
> +                                       &vb2_cma_vm_ops, buf);
> +}
> +
> +static void *vb2_cma_get_userptr(unsigned long vaddr, unsigned long size)
> +{
> +       struct vb2_cma_buf *buf;
> +       unsigned long paddr = 0;
> +       int ret;
> +
> +       buf = kzalloc(sizeof *buf, GFP_KERNEL);
> +       if (!buf)
> +               return ERR_PTR(-ENOMEM);
> +
> +       buf->vma = vb2_get_userptr(vaddr);
> +       if (!buf->vma) {
> +               printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
> +                               vaddr);
> +               ret = -EINVAL;
> +               goto done;
> +       }
> +
> +       ret = vb2_contig_verify_userptr(buf->vma, vaddr, size, &paddr);
> +       if (ret) {
> +               vb2_put_userptr(buf->vma);
> +               goto done;
> +       }
> +
> +       buf->size = size;
> +       buf->paddr = paddr;
> +
> +       return buf;
> +
> +done:
> +       kfree(buf);
> +       return ERR_PTR(ret);
> +}
> +
> +static void vb2_cma_put_userptr(void *mem_priv)
> +{
> +       struct vb2_cma_buf *buf = mem_priv;
> +
> +       if (!buf)
> +               return;
> +
> +       vb2_put_userptr(buf->vma);
> +       kfree(buf);
> +}
> +
> +static const struct vb2_mem_ops vb2_cma_ops = {
> +       .alloc          = vb2_cma_alloc,
> +       .put            = vb2_cma_put,
> +       .paddr          = vb2_cma_paddr,
> +       .mmap           = vb2_cma_mmap,
> +       .get_userptr    = vb2_cma_get_userptr,
> +       .put_userptr    = vb2_cma_put_userptr,
> +       .num_users      = vb2_cma_num_users,
> +};
> +
> +struct vb2_alloc_ctx *vb2_cma_init(struct device *dev, const char *type,
> +                                       unsigned long alignment)
> +{
> +       struct vb2_cma_conf *conf;
> +
> +       conf = kzalloc(sizeof *conf, GFP_KERNEL);
> +       if (!conf)
> +               return ERR_PTR(-ENOMEM);
> +
> +       conf->dev = dev;
> +       conf->type = type;
> +       conf->alignment = alignment;
> +       conf->alloc_ctx.mem_ops = &vb2_cma_ops;
> +
> +       return &conf->alloc_ctx;
> +}
> +EXPORT_SYMBOL_GPL(vb2_cma_init);
> +
> +void vb2_cma_cleanup(struct vb2_alloc_ctx *alloc_ctx)
> +{
> +       struct vb2_cma_conf *conf =
> +               container_of(alloc_ctx, struct vb2_cma_conf, alloc_ctx);
> +
> +       kfree(conf);
> +}
> +EXPORT_SYMBOL_GPL(vb2_cma_cleanup);
> +
> +struct vb2_alloc_ctx **vb2_cma_init_multi(struct device *dev,
> +                                         unsigned int num_planes,
> +                                         const char *types[],
> +                                         unsigned long alignments[])
> +{
> +       struct vb2_alloc_ctx    **alloc_ctxes;
> +       struct vb2_cma_conf     *cma_conf;
> +       unsigned int i;
> +
> +       alloc_ctxes = kzalloc((sizeof *alloc_ctxes + sizeof *cma_conf)
> +                               * num_planes, GFP_KERNEL);
> +       if (!alloc_ctxes)
> +               return ERR_PTR(-ENOMEM);
> +
> +       cma_conf = (void *)(alloc_ctxes + num_planes);
> +
> +       for (i = 0; i < num_planes; ++i, ++cma_conf) {
> +               alloc_ctxes[i] = &cma_conf->alloc_ctx;
> +               cma_conf->dev = dev;
> +               cma_conf->type = types[i];
> +               cma_conf->alignment = alignments[i];
> +               cma_conf->alloc_ctx.mem_ops = &vb2_cma_ops;
> +       }
> +
> +       return alloc_ctxes;
> +}
> +EXPORT_SYMBOL_GPL(vb2_cma_init_multi);
> +
> +void vb2_cma_cleanup_multi(struct vb2_alloc_ctx **alloc_ctxes)
> +{
> +       kfree(alloc_ctxes);
> +}
> +EXPORT_SYMBOL_GPL(vb2_cma_cleanup_multi);
> +
> +MODULE_DESCRIPTION("CMA allocator handling routines for videobuf2");
> +MODULE_AUTHOR("Pawel Osciak");
> +MODULE_LICENSE("GPL");
> diff --git a/include/media/videobuf2-cma.h b/include/media/videobuf2-cma.h
> new file mode 100644
> index 0000000..557eeb0
> --- /dev/null
> +++ b/include/media/videobuf2-cma.h
> @@ -0,0 +1,25 @@
> +/*
> + * videobuf2-cma.h - CMA memory allocator for videobuf2
> + *
> + * Copyright (C) 2010 Samsung Electronics
> + *
> + * Author: Pawel Osciak <p.osciak@samsung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation.
> + */
> +
> +struct vb2_alloc_ctx *vb2_cma_init(struct device *dev, const char *type,
> +                                       unsigned long alignment);
> +void vb2_cma_cleanup(struct vb2_alloc_ctx *alloc_ctx);
> +
> +struct vb2_alloc_ctx **vb2_cma_init_multi(struct device *dev,
> +                                 unsigned int num_planes, const char *types[],
> +                                 unsigned long alignments[]);
> +void vb2_cma_cleanup_multi(struct vb2_alloc_ctx **alloc_ctxes);
> +
> +struct vb2_alloc_ctx *vb2_cma_init(struct device *dev, const char *type,
> +                                       unsigned long alignment);
> +void vb2_cma_cleanup(struct vb2_alloc_ctx *alloc_ctx);
> +
> --
> 1.7.2.1.97.g3235b.dirty
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

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

* Re: [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-09-09 17:29   ` Mauro Carvalho Chehab
@ 2010-09-15 20:16     ` Pawel Osciak
  0 siblings, 0 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-15 20:16 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Hello Mauro,

Thank you for the review. Please see my responses inline.
Please also note that my e-mail address has changed.

On 09/09/2010 07:29 PM, Mauro Carvalho Chehab wrote:
> Em 09-09-2010 06:19, Pawel Osciak escreveu:
>> Videobuf2 is a Video for Linux 2 API-compatible driver framework for
>> multimedia devices. It acts as an intermediate layer between userspace
>> applications and device drivers. It also provides low-level, modular
>> memory management functions for drivers.
>>
>> Videobuf2 eases driver development, reduces drivers' code size and
aids in
>> proper and consistent implementation of V4L2 API in drivers.
>>
>> Videobuf2 memory management backend is fully modular. This allows custom
>> memory management routines for devices and platforms with non-standard
>> memory management requirements to be plugged in, without changing the
>> high-level buffer management functions and API.
>>
>> The framework provides:
>> - implementations of streaming I/O V4L2 ioctls and file operations
>> - high-level video buffer, video queue and state management functions
>> - video buffer memory allocation and management
>>
>> Signed-off-by: Pawel Osciak<p.osciak@samsung.com>
>> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com>
>> ---
>>   drivers/media/video/Kconfig          |    3 +
>>   drivers/media/video/Makefile         |    2 +
>>   drivers/media/video/videobuf2-core.c | 1457++++++++++++++++++++++++++++++++++
>>   include/media/videobuf2-core.h       |  337 ++++++++
>>   4 files changed, 1799 insertions(+), 0 deletions(-)
>>   create mode 100644 drivers/media/video/videobuf2-core.c
>>   create mode 100644 include/media/videobuf2-core.h
>>

(snip)

>> +/**
>> + * __vb2_wait_for_done_vb() - wait for a buffer to become available
>> + * for dequeuing
>> + *
>> + * Will sleep if required for nonblocking == false.
>> + */
>> +static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
>> +{
>> +	int retval = 0;
>> +
>> +checks:
>> +	if (!q->streaming) {
>> +		dprintk(1, "Streaming off, will not wait for buffers\n");
>> +		retval = -EINVAL;
>> +		goto end;
>> +	}
>> +
>> +	/*
>> +	 * Buffers may be added to vb_done_list without holding the vb_lock,
>> +	 * but removal is performed only while holding both vb_lock and the
>> +	 * vb_done_lock spinlock. Thus we can be sure that as long as we hold
>> +	 * vb_lock, the list will remain not empty if this check succeeds.
>> +	 */
>> +	if (list_empty(&q->done_list)) {
>> +		if (nonblocking) {
>> +			dprintk(1, "Nonblocking and no buffers to dequeue, "
>> +					"will not wait\n");
>> +			retval = -EAGAIN;
>> +			goto end;
>> +		}
>> +
>> +		/*
>> +		 * We are streaming and nonblocking, wait for another buffer to
>> +		 * become ready or for streamoff. vb_lock is released to allow
>> +		 * streamoff or qbuf to be called while waiting.
>> +		 */
>> +		mutex_unlock(&q->vb_lock);
>
> There's no mutex_lock before this call inside this function... It doesn't
> seem to be a good idea to call it with a mutex locked, and having aunlock/lock
> inside the fuction. The better would be to call it with mutexunlocked and let it
> lock/unlock where needed.
>

Hm, this might be tricky... I am pretty sure we have to hold the vb_lock for the
duration of dqbuf, so I cannot call it without holding the vb_lock. Would you
prefer to put that whole code into dqbuf? Sorry, I am not sure I understood you
correctly here...

>> +		/*
>> +		 * Although the mutex is released here, we will be reevaluating
>> +		 * both conditions again after reacquiring it.
>> +		 */
>> +		dprintk(3, "Will sleep waiting for buffers\n");
>> +		retval = wait_event_interruptible(q->done_wq,
>> +				!list_empty(&q->done_list) || !q->streaming);
>
> I think you could have a race condition here, as you're checking forlist_empty
> without a lock. The better approach would be to do something like:
>
> static int vb2_is_videobuf_empty(struct vb2_queue *q)
> {
> 	int is_empty;
> 		
> 	mutex_lock(&q->vb_lock);
>
> 	is_empty = list_empty(&q->done_list);
>
> 	mutex_unlock(&q->vb_lock);
>
> 	return is_empty;
> }
>
> static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
> {
> 	...
> 	retval = wait_event_interruptible(q->done_wq,vb2_is_videobuf_empty(q) || !q->streaming);
> 	...
> }
>
> This way, you'll always have the mutex locked when checking for listempty.
>
> Btw, shouldn't it be using, instead a spinlock?
>

There are two locks to be considered here:
- vb_lock - main mutex protecting most of the calls
- done_lock - a spinlock protecting done_list

Both vb_lock and done_lock have to be held to remove items from the
done_list, but only done_lock has to be held to add items to the
done_list.

After we check the done_list and find it non-empty, it will
stay that way as long as we hold vb_lock. It is possible that
new buffers will be added to it during that time, but this is not a
problem, since we will acquire the done_lock spinlock before removing
anything from the list.

I am indeed checking for list_empty without holding vb_lock, but this is
only a preliminary check. After wait_event_interruptible returns 0, I
jump back to checks:, acquire vb_lock and recheck for list_empty while
holding it. If the list is still non-empty by this second check, I
can return from the function still holding the vb_lock (so nothing can
be taken off the done_list in the meantime).

I am not sure if you solution would change things here... Since
vb2_is_videobuf_empty() you proposed releases vb_lock before returning
(and it is of course how it should be), we still have to jump back,
reacquire vb_lock and recheck. We cannot release vb_lock after verifying
that the list is nonempty until we remove the buffer from the done_list,
to make sure it stays nonempty.

Or maybe I am missing something here?

> To avoid needing to have a lock also for q->streaming, the betterwould be to define
> it as atomic_t.
>

Right, this is a good idea.

>> +		mutex_lock(&q->vb_lock);
>> +
>> +		if (retval)
>> +			goto end;
>> +
>> +		goto checks;
>> +	}
>> +
>> +end:
>> +	return retval;
>> +}
>> +
>> +/**
>> + * __vb2_get_done_vb() - get a buffer ready for dequeuing
>> + *
>> + * Will sleep if required for nonblocking == false.
>> + */
>> +static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer**vb,
>> +				int nonblocking)
>> +{
>> +	unsigned long flags;
>> +	int ret = 0;
>> +
>> +	/*
>> +	 * Wait for at least one buffer to become available on the done_list.
>> +	 */
>> +	ret = __vb2_wait_for_done_vb(q, nonblocking);
>> +	if (ret)
>> +		goto end;
>> +
>> +	/*
>> +	 * vb_lock has been held since we last verified that done_list is
>> +	 * not empty, so no need for another list_empty(done_list) check.
>> +	 */
>> +	spin_lock_irqsave(&q->done_lock, flags);
>> +	*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
>> +	list_del(&(*vb)->done_entry);
>> +	spin_unlock_irqrestore(&q->done_lock, flags);
>> +
>> +end:
>> +	return ret;
>> +}
>> +
>> +
>> +/**
>> + * vb2_dqbuf() - Dequeue a buffer to the userspace
>> + * @q:		videobuf2 queue
>> + * @b:		buffer structure passed from userspace to vidioc_dqbuf handler
>> + *		in driver
>> + * @nonblocking: if true, this call will not sleep waiting for abuffer if no
>> + *		 buffers ready for dequeuing are present. Normally the driver
>> + *		 would be passing (file->f_flags&  O_NONBLOCK) here
>> + *
>> + * Should be called from vidioc_dqbuf ioctl handler of a driver.
>> + * This function:
>> + * 1) verifies the passed buffer,
>> + * 2) calls buf_finish callback in the driver (if provided), in which
>> + *    driver can perform any additional operations that may berequired before
>> + *    returning the buffer to userspace, such as cache sync,
>> + * 3) the buffer struct members are filled with relevantinformation for
>> + *    the userspace.
>> + *
>> + * The return values from this function are intended to be directlyreturned
>> + * from vidioc_dqbuf handler in driver.
>> + */
>> +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, boolnonblocking)
>> +{
>> +	struct vb2_buffer *vb = NULL;
>> +	int ret;
>> +
>> +	mutex_lock(&q->vb_lock);
>> +
>> +	if (b->type != q->type) {
>> +		dprintk(1, "dqbuf: invalid buffer type\n");
>> +		ret = -EINVAL;
>> +		goto done;
>> +	}
>> +
>> +	ret = __vb2_get_done_vb(q,&vb, nonblocking);
>> +	if (ret<  0) {
>> +		dprintk(1, "dqbuf: error getting next done buffer\n");
>> +		goto done;
>> +	}
>> +
>> +	if (q->ops->buf_finish) {
>> +		ret = q->ops->buf_finish(vb);
>> +		if (ret) {
>> +			dprintk(1, "dqbuf: buffer finish failed\n");
>> +			goto done;
>> +		}
>> +	}
>> +
>> +	switch (vb->state) {
>> +	case VB2_BUF_STATE_DONE:
>> +		dprintk(3, "dqbuf: Returning done buffer\n");
>> +		break;
>> +	case VB2_BUF_STATE_ERROR:
>> +		dprintk(3, "dqbuf: Returning done buffer with errors\n");
>> +		break;
>> +	default:
>> +		dprintk(1, "dqbuf: Invalid buffer state\n");
>> +		ret = -EINVAL;
>> +		goto done;
>> +	}
>> +
>> +	/* Fill buffer information for the userspace */
>> +	__fill_v4l2_buffer(vb, b);
>> +	/* Remove from videobuf queue */
>> +	list_del(&vb->queued_entry);
>> +
>> +	dprintk(1, "dqbuf of buffer %d, with state %d\n",
>> +			vb->v4l2_buf.index, vb->state);
>> +
>> +	vb->state = VB2_BUF_STATE_DEQUEUED;
>> +
>> +done:
>> +	mutex_unlock(&q->vb_lock);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(vb2_dqbuf);
>> +


-- 
Best regards,
Pawel Osciak

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

* Re: [PATCH v1 7/7] v4l: videobuf2: add CMA allocator
  2010-09-15  8:55   ` han jonghun
@ 2010-09-15 20:25     ` Pawel Osciak
  0 siblings, 0 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-15 20:25 UTC (permalink / raw)
  To: han jonghun; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Hello,

On 09/15/2010 10:55 AM, han jonghun wrote:
> Hello,
>
> In vb2_cma_put if buf->refcount is 0, cma_free is called.
> But vb2_cma_put is usually called from munmap.
> In my opinion cma_free should be called from VIDIOC_REQBUFS(0) not munmap.
>

cma_free has to be called from both, since we do not always call
VIDIOC_REQBUFS(0) after finishing. If an application just closes
the file descriptor (or even dies), we need a way to clean up the
memory.

-- 
Best regards,
Pawel Osciak

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

* Re: [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-09-09  9:19 ` [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Pawel Osciak
  2010-09-09 17:29   ` Mauro Carvalho Chehab
@ 2010-09-25 14:27   ` Hans Verkuil
  2010-09-29 23:40     ` Pawel Osciak
  1 sibling, 1 reply; 24+ messages in thread
From: Hans Verkuil @ 2010-09-25 14:27 UTC (permalink / raw)
  To: Pawel Osciak; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Hi Pawel,

I finally had some time for a code review:

On Thursday, September 09, 2010 11:19:42 Pawel Osciak wrote:
> Videobuf2 is a Video for Linux 2 API-compatible driver framework for
> multimedia devices. It acts as an intermediate layer between userspace
> applications and device drivers. It also provides low-level, modular
> memory management functions for drivers.
> 
> Videobuf2 eases driver development, reduces drivers' code size and aids in
> proper and consistent implementation of V4L2 API in drivers.
> 
> Videobuf2 memory management backend is fully modular. This allows custom
> memory management routines for devices and platforms with non-standard
> memory management requirements to be plugged in, without changing the
> high-level buffer management functions and API.
> 
> The framework provides:
> - implementations of streaming I/O V4L2 ioctls and file operations
> - high-level video buffer, video queue and state management functions
> - video buffer memory allocation and management
> 
> Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/media/video/Kconfig          |    3 +
>  drivers/media/video/Makefile         |    2 +
>  drivers/media/video/videobuf2-core.c | 1457 ++++++++++++++++++++++++++++++++++
>  include/media/videobuf2-core.h       |  337 ++++++++
>  4 files changed, 1799 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/videobuf2-core.c
>  create mode 100644 include/media/videobuf2-core.h
> 

<snip>

> +/**
> + * vb2_reqbufs() - Initiate streaming
> + * @q:		videobuf2 queue
> + * @req:	struct passed from userspace to vidioc_reqbufs handler in driver
> + *
> + * Should be called from vidioc_reqbufs ioctl handler of a driver.
> + * This function:
> + * 1) verifies streaming parameters passed from the userspace,
> + * 2) sets up the queue,
> + * 3) negotiates number of buffers and planes per buffer with the driver
> + *    to be used during streaming,
> + * 4) allocates internal buffer structures (struct vb2_buffer), according to
> + *    the agreed parameters,
> + * 5) for MMAP memory type, allocates actual video memory, using the
> + *    memory handling/allocation routines provided during queue initialization
> + *
> + * If req->count is 0, all the memory will be freed instead.
> + * If the queue has been allocated previously (by a previous vb2_reqbufs) call
> + * and the queue is not busy, memory will be reallocated.
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_reqbufs handler in driver.
> + */
> +int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> +{
> +	unsigned int num_buffers, num_planes;
> +	int ret = 0;
> +
> +	if (req->memory != V4L2_MEMORY_MMAP
> +			&& req->memory != V4L2_MEMORY_USERPTR) {
> +		dprintk(1, "reqbufs: unsupported memory type\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	if (req->type != q->type) {
> +		dprintk(1, "reqbufs: queue type invalid\n");
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	if (q->streaming) {
> +		dprintk(1, "reqbufs: streaming active\n");
> +		ret = -EBUSY;
> +		goto end;
> +	}
> +
> +	if (req->count == 0) {
> +		/* Free/release memory for count = 0, but only if unused */
> +		if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
> +			dprintk(1, "reqbufs: memory in use, cannot free\n");
> +			ret = -EBUSY;
> +			goto end;
> +		}
> +
> +		ret = __vb2_queue_free(q);
> +		goto end;
> +	}
> +
> +	if (q->num_buffers != 0) {
> +		/*
> +		 * We already have buffers allocated, so a reallocation is
> +		 * required, but only if the buffers are not in use.
> +		 */
> +		if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
> +			dprintk(1, "reqbufs: memory in use, "
> +					"cannot reallocate\n");
> +			ret = -EBUSY;
> +			goto end;
> +		}
> +
> +		ret = __vb2_queue_free(q);

Hmmm, __vb2_queue_free can't fail: it always returns 0. If you make __vb2_queue_free
a void function, then there is no need to test for any error code.

> +		if (ret)
> +			goto end;
> +	}
> +
> +	num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
> +
> +	/* Ask the driver how many buffers and planes per buffer it requires */
> +	ret = q->ops->queue_negotiate(q, &num_buffers, &num_planes);
> +	if (ret)
> +		goto end;
> +
> +	/*
> +	 * Make sure all the required memory ops for given memory type
> +	 * are available.
> +	 */
> +	if (req->memory == V4L2_MEMORY_MMAP
> +			&& __verify_mmap_ops(q, num_planes)) {
> +		dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
> +		ret = -EINVAL;
> +		goto end;
> +	} else if (req->memory == V4L2_MEMORY_USERPTR
> +			&& __verify_userptr_ops(q, num_planes)) {
> +		dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	/* Finally, allocate buffers and video memory */
> +	ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
> +	if (ret < 0) {
> +		dprintk(1, "Memory allocation failed with error: %d\n", ret);
> +	} else {
> +		/*
> +		 * Return the number of successfully allocated buffers
> +		 * to the userspace.
> +		 */
> +		req->count = ret;
> +		ret = 0;
> +	}
> +
> +	q->memory = req->memory;
> +
> +end:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_reqbufs);
> +
> +/**
> + * 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
> + *
> + * This function returns a kernel virtual address of a given plane if
> + * such a mapping exist, NULL otherwise.
> + */
> +void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +
> +	if (plane_no > vb->num_planes)
> +		return NULL;
> +
> +	return call_memop(q, plane_no, vaddr, vb->planes[plane_no].mem_priv);
> +
> +}
> +EXPORT_SYMBOL_GPL(vb2_plane_vaddr);
> +
> +/**
> + * vb2_plane_paddr() - Return the physical 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
> + *
> + * This function returns a physical address of a given plane if available,
> + * NULL otherwise.
> + */
> +unsigned long vb2_plane_paddr(struct vb2_buffer *vb, unsigned int plane_no)

Shouldn't this return phys_addr_t? That seems more appropriate.

> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +
> +	if (plane_no > vb->num_planes)
> +		return 0UL;
> +
> +	return call_memop(q, plane_no, paddr, vb->planes[plane_no].mem_priv);
> +}
> +EXPORT_SYMBOL_GPL(vb2_plane_paddr);
> +
> +/**
> + * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished
> + * @vb:		vb2_buffer returned from the driver
> + * @state:	either VB2_BUF_STATE_DONE if the operation finished successfully
> + *		or VB2_BUF_STATE_ERROR if the operation finished with an error
> + *
> + * This function should be called by the driver after a hardware operation on
> + * a buffer is finished and the buffer may be returned to userspace. The driver
> + * cannot use this buffer anymore until it is queued back to it by videobuf
> + * by the means of buf_queue callback. Only buffers previously queued to the
> + * driver by buf_queue can be passed to this function.
> + */
> +void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +	unsigned long flags;
> +
> +	if (vb->state != VB2_BUF_STATE_ACTIVE)
> +		return;
> +
> +	if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
> +		return;

These two checks should call WARN() to clearly signal a driver bug.

> +
> +	dprintk(4, "Done processing on buffer %d, state: %d\n",
> +			vb->v4l2_buf.index, vb->state);
> +
> +	/* Add the buffer to the done buffers list */
> +	spin_lock_irqsave(&q->done_lock, flags);
> +	vb->state = state;
> +	list_add_tail(&vb->done_entry, &q->done_list);
> +	spin_unlock_irqrestore(&q->done_lock, flags);
> +
> +	/* Inform any processes that may be waiting for buffers */
> +	wake_up_interruptible(&q->done_wq);
> +}
> +EXPORT_SYMBOL_GPL(vb2_buffer_done);
> +
> +/**
> + * __fill_vb2_buffer() - fill a vb2_buffer with information provided in
> + * a v4l2_buffer by the userspace
> + */
> +static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
> +				struct v4l2_plane *v4l2_planes)
> +{
> +	unsigned int plane;
> +	int ret;
> +
> +	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
> +		/*
> +		 * Verify that the userspace gave us a valid array for
> +		 * plane information.
> +		 */
> +		ret = __verify_planes_array(vb, b);
> +		if (ret)
> +			return ret;
> +
> +		/* Fill in driver-provided information for OUTPUT types */
> +		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
> +			/*
> +			 * Will have to go up to b->length when API starts
> +			 * accepting variable number of planes.
> +			 */
> +			for (plane = 0; plane < vb->num_planes; ++plane) {
> +				v4l2_planes[plane].bytesused =
> +					b->m.planes[plane].bytesused;
> +				v4l2_planes[plane].data_offset =
> +					b->m.planes[plane].data_offset;
> +			}
> +		}
> +
> +		if (b->memory == V4L2_MEMORY_USERPTR) {
> +			for (plane = 0; plane < vb->num_planes; ++plane) {
> +				v4l2_planes[plane].m.userptr =
> +					b->m.planes[plane].m.userptr;
> +				v4l2_planes[plane].length =
> +					b->m.planes[plane].length;
> +			}
> +		}
> +	} else {
> +		/*
> +		 * Single-planar buffers do not use planes array,
> +		 * so fill in relevant v4l2_buffer struct fields instead.
> +		 * In videobuf we use our internal V4l2_planes struct for
> +		 * single-planar buffers as well, for simplicity.
> +		 */
> +		if (V4L2_TYPE_IS_OUTPUT(b->type))
> +			v4l2_planes[0].bytesused = b->bytesused;
> +
> +		if (b->memory == V4L2_MEMORY_USERPTR) {
> +			v4l2_planes[0].m.userptr = b->m.userptr;
> +			v4l2_planes[0].length = b->length;
> +		}
> +	}
> +
> +	vb->v4l2_buf.field = b->field;
> +	vb->v4l2_buf.timestamp = b->timestamp;
> +
> +	return 0;
> +}
> +
> +/**
> + * __qbuf_userptr() - handle qbuf of a USERPTR buffer
> + */
> +static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
> +{
> +	struct v4l2_plane planes[VIDEO_MAX_PLANES];
> +	struct vb2_queue *q = vb->vb2_queue;
> +	void *mem_priv = NULL;
> +	unsigned int plane;
> +	int ret;
> +
> +	/* Verify and copy relevant information provided by the userspace */
> +	ret = __fill_vb2_buffer(vb, b, planes);
> +	if (ret)
> +		return ret;
> +
> +	for (plane = 0; plane < vb->num_planes; ++plane) {
> +		/* Skip the plane if already verified */
> +		if (vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr
> +		    && vb->v4l2_planes[plane].length == planes[plane].length)
> +			continue;
> +
> +		dprintk(3, "qbuf: userspace address for plane %d changed, "
> +				"reacquiring memory\n", plane);
> +
> +		/* Release previously acquired memory if present */
> +		if (vb->planes[plane].mem_priv)
> +			call_memop(q, plane, put_userptr,
> +					vb->planes[plane].mem_priv);
> +
> +		vb->planes[plane].mem_priv = NULL;
> +
> +		/* Acquire each plane's memory */
> +		if (mem_ops(q, plane)->get_userptr) {
> +			mem_priv = mem_ops(q, plane)->get_userptr(
> +							planes[plane].m.userptr,
> +							planes[plane].length);
> +			if (IS_ERR(mem_priv)) {
> +				dprintk(1, "qbuf: failed acquiring userspace "
> +						"memory for plane %d\n", plane);
> +				goto err;
> +			}

Just to verify my understanding: get_userptr is responsible for locking the
pages into memory and to increase a use count on that memory. So if the app
would do a QBUF, then free the memory and allocate a new buffer, then the
original buffer memory is not released until put_userptr is called, and the
pointer of the new buffer that the app allocated will always be different from
the original buffer (since it was still in use when the app allocated the new
one).

I've always wondered if something nasty like this was handled correctly. I never
had the time to dive into the mm core, though.

> +
> +			vb->planes[plane].mem_priv = mem_priv;
> +		}
> +	}
> +
> +	/*
> +	 * Call driver-specific initialization on the newly acquired buffer,
> +	 * if provided.
> +	 */
> +	if (q->ops->buf_init) {
> +		ret = q->ops->buf_init(vb);
> +		if (ret) {
> +			dprintk(1, "qbuf: buffer initialization failed\n");
> +			goto err;
> +		}
> +	}
> +
> +	/*
> +	 * Now that everything is in order, copy relevant information
> +	 * provided by userspace.
> +	 */
> +	for (plane = 0; plane < vb->num_planes; ++plane)
> +		vb->v4l2_planes[plane] = planes[plane];
> +
> +	return 0;
> +err:
> +	/* In case of errors, release planes that were already acquired */
> +	for (; plane > 0; --plane) {
> +		call_memop(q, plane, put_userptr,
> +				vb->planes[plane - 1].mem_priv);
> +		vb->planes[plane - 1].mem_priv = NULL;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * __qbuf_mmap() - handle qbuf of an MMAP buffer
> + */
> +static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b)
> +{
> +	return __fill_vb2_buffer(vb, b, vb->v4l2_planes);
> +}
> +
> +/**
> + * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
> + */
> +static void __enqueue_in_driver(struct vb2_buffer *vb)
> +{
> +	struct vb2_queue *q = vb->vb2_queue;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(q->drv_lock, flags);
> +	vb->state = VB2_BUF_STATE_ACTIVE;
> +	q->ops->buf_queue(vb);
> +	spin_unlock_irqrestore(q->drv_lock, flags);
> +}
> +
> +/**
> + * vb2_qbuf() - Queue a buffer from userspace
> + * @q:		videobuf2 queue
> + * @b:		buffer structure passed from userspace to vidioc_qbuf handler
> + *		in driver
> + *
> + * Should be called from vidioc_qbuf 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,
> + * 3) if streaming is on, queues the buffer in driver by the means of buf_queue
> + *    callback for processing.
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_qbuf handler in driver.
> + */
> +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
> +{
> +	struct vb2_buffer *vb;
> +	int ret;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	ret = -EINVAL;
> +	if (b->type != q->type) {
> +		dprintk(1, "qbuf: invalid buffer type\n");
> +		goto done;
> +	}
> +	if (b->index >= q->num_buffers) {
> +		dprintk(1, "qbuf: buffer index out of range\n");
> +		goto done;
> +	}
> +
> +	vb = q->bufs[b->index];
> +	if (NULL == vb) {
> +		/* Should never happen */
> +		dprintk(1, "qbuf: buffer is NULL\n");
> +		goto done;
> +	}
> +
> +	if (b->memory != q->memory) {
> +		dprintk(1, "qbuf: invalid memory type\n");
> +		goto done;
> +	}
> +
> +	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> +		dprintk(1, "qbuf: buffer already in use\n");
> +		goto done;
> +	}
> +
> +	if (q->memory == V4L2_MEMORY_MMAP)
> +		ret = __qbuf_mmap(vb, b);
> +	else if (q->memory == V4L2_MEMORY_USERPTR)
> +		ret = __qbuf_userptr(vb, b);
> +	if (ret)
> +		goto done;
> +
> +	if (q->ops->buf_prepare) {
> +		ret = q->ops->buf_prepare(vb);
> +		if (ret) {
> +			dprintk(1, "qbuf: buffer preparation failed\n");
> +			goto done;
> +		}
> +	}
> +
> +	/*
> +	 * Add to the queued buffers list, a buffer will stay on it until
> +	 * dequeued in dqbuf.
> +	 */
> +	list_add_tail(&vb->queued_entry, &q->queued_list);
> +	vb->state = VB2_BUF_STATE_QUEUED;
> +
> +	/*
> +	 * If already streaming, give the buffer to driver for processing.
> +	 * If not, the buffer will be given to driver on next streamon.
> +	 */
> +	if (q->streaming)
> +		__enqueue_in_driver(vb);
> +
> +	dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
> +	ret = 0;
> +done:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_qbuf);
> +
> +/**
> + * __vb2_wait_for_done_vb() - wait for a buffer to become available
> + * for dequeuing
> + *
> + * Will sleep if required for nonblocking == false.
> + */
> +static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
> +{
> +	int retval = 0;
> +
> +checks:
> +	if (!q->streaming) {
> +		dprintk(1, "Streaming off, will not wait for buffers\n");
> +		retval = -EINVAL;
> +		goto end;
> +	}
> +
> +	/*
> +	 * Buffers may be added to vb_done_list without holding the vb_lock,
> +	 * but removal is performed only while holding both vb_lock and the
> +	 * vb_done_lock spinlock. Thus we can be sure that as long as we hold
> +	 * vb_lock, the list will remain not empty if this check succeeds.
> +	 */
> +	if (list_empty(&q->done_list)) {
> +		if (nonblocking) {
> +			dprintk(1, "Nonblocking and no buffers to dequeue, "
> +					"will not wait\n");
> +			retval = -EAGAIN;
> +			goto end;
> +		}
> +
> +		/*
> +		 * We are streaming and nonblocking, wait for another buffer to
> +		 * become ready or for streamoff. vb_lock is released to allow
> +		 * streamoff or qbuf to be called while waiting.
> +		 */
> +		mutex_unlock(&q->vb_lock);
> +		/*
> +		 * Although the mutex is released here, we will be reevaluating
> +		 * both conditions again after reacquiring it.
> +		 */
> +		dprintk(3, "Will sleep waiting for buffers\n");
> +		retval = wait_event_interruptible(q->done_wq,
> +				!list_empty(&q->done_list) || !q->streaming);
> +		mutex_lock(&q->vb_lock);
> +
> +		if (retval)
> +			goto end;
> +
> +		goto checks;
> +	}
> +
> +end:
> +	return retval;
> +}
> +
> +/**
> + * __vb2_get_done_vb() - get a buffer ready for dequeuing
> + *
> + * Will sleep if required for nonblocking == false.
> + */
> +static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
> +				int nonblocking)
> +{
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	/*
> +	 * Wait for at least one buffer to become available on the done_list.
> +	 */
> +	ret = __vb2_wait_for_done_vb(q, nonblocking);
> +	if (ret)
> +		goto end;
> +
> +	/*
> +	 * vb_lock has been held since we last verified that done_list is
> +	 * not empty, so no need for another list_empty(done_list) check.
> +	 */
> +	spin_lock_irqsave(&q->done_lock, flags);
> +	*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
> +	list_del(&(*vb)->done_entry);
> +	spin_unlock_irqrestore(&q->done_lock, flags);
> +
> +end:
> +	return ret;
> +}
> +
> +
> +/**
> + * vb2_dqbuf() - Dequeue a buffer to the userspace
> + * @q:		videobuf2 queue
> + * @b:		buffer structure passed from userspace to vidioc_dqbuf handler
> + *		in driver
> + * @nonblocking: if true, this call will not sleep waiting for a buffer if no
> + *		 buffers ready for dequeuing are present. Normally the driver
> + *		 would be passing (file->f_flags & O_NONBLOCK) here
> + *
> + * Should be called from vidioc_dqbuf ioctl handler of a driver.
> + * This function:
> + * 1) verifies the passed buffer,
> + * 2) calls buf_finish callback in the driver (if provided), in which
> + *    driver can perform any additional operations that may be required before
> + *    returning the buffer to userspace, such as cache sync,
> + * 3) the buffer struct members are filled with relevant information for
> + *    the userspace.
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_dqbuf handler in driver.
> + */
> +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
> +{
> +	struct vb2_buffer *vb = NULL;
> +	int ret;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	if (b->type != q->type) {
> +		dprintk(1, "dqbuf: invalid buffer type\n");
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	ret = __vb2_get_done_vb(q, &vb, nonblocking);
> +	if (ret < 0) {
> +		dprintk(1, "dqbuf: error getting next done buffer\n");
> +		goto done;
> +	}
> +
> +	if (q->ops->buf_finish) {
> +		ret = q->ops->buf_finish(vb);
> +		if (ret) {
> +			dprintk(1, "dqbuf: buffer finish failed\n");
> +			goto done;
> +		}
> +	}
> +
> +	switch (vb->state) {
> +	case VB2_BUF_STATE_DONE:
> +		dprintk(3, "dqbuf: Returning done buffer\n");
> +		break;
> +	case VB2_BUF_STATE_ERROR:
> +		dprintk(3, "dqbuf: Returning done buffer with errors\n");
> +		break;
> +	default:
> +		dprintk(1, "dqbuf: Invalid buffer state\n");

Isn't it a driver bug if we get here? In that case we need a WARN.

> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	/* Fill buffer information for the userspace */
> +	__fill_v4l2_buffer(vb, b);
> +	/* Remove from videobuf queue */
> +	list_del(&vb->queued_entry);
> +
> +	dprintk(1, "dqbuf of buffer %d, with state %d\n",
> +			vb->v4l2_buf.index, vb->state);
> +
> +	vb->state = VB2_BUF_STATE_DEQUEUED;
> +
> +done:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_dqbuf);
> +
> +/**
> + * vb2_streamon - start streaming
> + * @q:		videobuf2 queue
> + * @type:	type argument passed from userspace to vidioc_streamon handler
> + *
> + * Should be called from vidioc_streamon handler of a driver.
> + * This function:
> + * 1) verifies current state
> + * 2) starts streaming and passes any previously queued buffers to the driver
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_streamon handler in the driver.
> + */
> +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
> +{
> +	struct vb2_buffer *vb;
> +	int ret = 0;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	if (type != q->type) {
> +		dprintk(1, "streamon: invalid stream type\n");
> +		ret = -EINVAL;
> +		goto done;
> +	}
> +
> +	if (q->streaming) {
> +		dprintk(1, "streamon: already streaming\n");
> +		ret = -EBUSY;
> +		goto done;
> +	}
> +
> +	/*
> +	 * Cannot start streaming on an OUTPUT device if no buffers have
> +	 * been queued yet.
> +	 */
> +	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
> +		if (list_empty(&q->queued_list)) {
> +			dprintk(1, "streamon: no output buffers queued\n");
> +			ret = -EINVAL;
> +			goto done;
> +		}
> +	}
> +
> +	q->streaming = 1;
> +
> +	/*
> +	 * If any buffers were queued before streamon,
> +	 * we can now pass them to driver for processing.
> +	 */
> +	list_for_each_entry(vb, &q->queued_list, queued_entry)
> +		__enqueue_in_driver(vb);
> +
> +	dprintk(3, "Streamon successful\n");
> +done:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_streamon);
> +
> +/**
> + * __vb2_queue_cancel() - cancel and stop (pause) streaming
> + *
> + * Removes all queued buffers from driver's queue and all buffers queued by
> + * userspace from videobuf's queue. Returns to state after reqbufs.
> + */
> +static void __vb2_queue_cancel(struct vb2_queue *q)
> +{
> +	unsigned long flags = 0;
> +	int i;
> +
> +	q->streaming = 0;
> +
> +	/*
> +	 * Remove buffers from driver's queue. If a hardware operation
> +	 * is currently underway, drv_lock should be claimed and we will
> +	 * have to wait for it to finish before taking back buffers.
> +	 */
> +	spin_lock_irqsave(q->drv_lock, flags);
> +	for (i = 0; i < q->num_buffers; ++i) {
> +		if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE)
> +			list_del(&q->bufs[i]->drv_entry);
> +		q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
> +	}
> +	spin_unlock_irqrestore(q->drv_lock, flags);

I feel that this spinlock is too simplistic. I think we need a new op:
stop_streaming() or something like that. The driver will stop any streaming
and dequeue any active buffers. And if necessary it has to wait for any DMA
in progress to finish first.

That will also make it possible to remove the drv_lock altogether and make the
driver responsible for proper locking in buf_queue and stop_streaming.

Actually, it might also be an idea to implement a start_streaming op for symmetry.

> +
> +	/*
> +	 * Remove all buffers from videobuf's list...
> +	 */
> +	INIT_LIST_HEAD(&q->queued_list);
> +	/*
> +	 * ...and done list; userspace will not receive any buffers it
> +	 * has not already dequeued before initiating cancel.
> +	 */
> +	INIT_LIST_HEAD(&q->done_list);

Is this correct? Shouldn't put_userptr be called for all queued and done buffers?

> +	wake_up_interruptible_all(&q->done_wq);
> +}
> +
> +/**
> + * vb2_streamoff - stop streaming
> + * @q:		videobuf2 queue
> + * @type:	type argument passed from userspace to vidioc_streamoff handler
> + *
> + * Should be called from vidioc_streamoff handler of a driver.
> + * This function:
> + * 1) verifies current state,
> + * 2) stop streaming and dequeues any queued buffers, including those previously
> + *    passed to the driver (after waiting for the driver to finish).
> + *
> + * This call can be used for pausing playback.
> + * The return values from this function are intended to be directly returned
> + * from vidioc_streamoff handler in the driver
> + */
> +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
> +{
> +	int ret = 0;
> +
> +	mutex_lock(&q->vb_lock);
> +
> +	if (type != q->type) {
> +		dprintk(1, "streamoff: invalid stream type\n");
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	if (!q->streaming) {
> +		dprintk(1, "streamoff: not streaming\n");
> +		ret = -EINVAL;
> +		goto end;
> +	}
> +
> +	/*
> +	 * Cancel will pause streaming and remove all buffers from the driver
> +	 * and videobuf, effectively returning control over them to userspace.
> +	 */
> +	__vb2_queue_cancel(q);
> +
> +	dprintk(3, "Streamoff successful\n");
> +end:
> +	mutex_unlock(&q->vb_lock);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_streamoff);

<snip>

Some general remarks:

1) It is probably useful to add a simply inline function like this:

static inline bool vb2_is_streaming(struct vb2_queue *q)
{
       return q->streaming;
}

2) We need very clear documentation detailing:

   - where the struct vb2_queue has to be stored (it should be associated with the
     video_device struct or the v4l2_device struct if there is only one queue).
   - when to call vb2_queue_init (before registering the device node, I think).
   - when to call vb2_queue_release (after unregistering the device node? Does that
     also work well with USB devices after a disconnect? I think it is OK, but I'm
     not 100% certain.)
   - how does it work if multiple file handles are open? If fh A calls REQBUFS, is fh
     B allowed to call it again? My feeling is that once a fh calls REQBUFS, the
     queue is associated with that fh until REQBUFS with count == 0 is called, or
     until the fh is closed. All the other streaming ioctls should be called from
     that fh. To implement this reqbufs should be passed a struct file (or a v4l2_fh?).
     And for the others we either need this as well or we add a simple inline function
     checking this that drivers can call.

3) Read/write will have its own issues: if the driver supports read/write, then
   some internal checks are needed: once reqbufs was called on a fh, read/write
   should not be allowed (until REQBUFS(0), that is). The same is true vice versa,
   except that once you've started reading or writing the only way to go back to
   streaming I/O is by closing the fh first.

4) If poll is called without a preceding reqbufs or read, then it should initiate
   streaming and mark the queue as being in read (or write) mode. It's the way poll
   is supposed to work for read or write.
  
5) Allowing mixing of read/write and streaming I/O. I am very much opposed to this.
   First of all it will cause skipped frames since read will steal from dqbuf (or
   vice versa, depending on how you look at it). Once you start to read it will also
   be impossible to use REQBUFS(0), and the internal administration will be a nightmare.
   Frankly, I don't think there is a way to implement this in a way that makes sense.
   We should probably investigate those utilities that are supposed to do this.
   I understand that they are xawtv and xdtv.

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco

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

* Re: [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-09-25 14:27   ` Hans Verkuil
@ 2010-09-29 23:40     ` Pawel Osciak
  0 siblings, 0 replies; 24+ messages in thread
From: Pawel Osciak @ 2010-09-29 23:40 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, kyungmin.park, m.szyprowski, t.fujak

Hi Hans,
Big thanks for the review!

On 09/25/2010 07:27 AM, Hans Verkuil wrote:
 > Hi Pawel,
 >
 > I finally had some time for a code review:
 >
 > On Thursday, September 09, 2010 11:19:42 Pawel Osciak wrote:
 >> Videobuf2 is a Video for Linux 2 API-compatible driver framework for
 >> multimedia devices. It acts as an intermediate layer between userspace
 >> applications and device drivers. It also provides low-level, modular
 >> memory management functions for drivers.
 >>
 >> Videobuf2 eases driver development, reduces drivers' code size and aids in
 >> proper and consistent implementation of V4L2 API in drivers.
 >>
 >> Videobuf2 memory management backend is fully modular. This allows custom
 >> memory management routines for devices and platforms with non-standard
 >> memory management requirements to be plugged in, without changing the
 >> high-level buffer management functions and API.
 >>
 >> The framework provides:
 >> - implementations of streaming I/O V4L2 ioctls and file operations
 >> - high-level video buffer, video queue and state management functions
 >> - video buffer memory allocation and management
 >>
 >> Signed-off-by: Pawel Osciak<p.osciak@samsung.com>
 >> Signed-off-by: Kyungmin Park<kyungmin.park@samsung.com>
 >> ---
 >>   drivers/media/video/Kconfig          |    3 +
 >>   drivers/media/video/Makefile         |    2 +
 >>   drivers/media/video/videobuf2-core.c | 1457 ++++++++++++++++++++++++++++++++++
 >>   include/media/videobuf2-core.h       |  337 ++++++++
 >>   4 files changed, 1799 insertions(+), 0 deletions(-)
 >>   create mode 100644 drivers/media/video/videobuf2-core.c
 >>   create mode 100644 include/media/videobuf2-core.h
 >>
 >
 > <snip>
 >

<snip>

 >> +
 >> +	dprintk(4, "Done processing on buffer %d, state: %d\n",
 >> +			vb->v4l2_buf.index, vb->state);
 >> +
 >> +	/* Add the buffer to the done buffers list */
 >> +	spin_lock_irqsave(&q->done_lock, flags);
 >> +	vb->state = state;
 >> +	list_add_tail(&vb->done_entry,&q->done_list);
 >> +	spin_unlock_irqrestore(&q->done_lock, flags);
 >> +
 >> +	/* Inform any processes that may be waiting for buffers */
 >> +	wake_up_interruptible(&q->done_wq);
 >> +}
 >> +EXPORT_SYMBOL_GPL(vb2_buffer_done);
 >> +
 >> +/**
 >> + * __fill_vb2_buffer() - fill a vb2_buffer with information provided in
 >> + * a v4l2_buffer by the userspace
 >> + */
 >> +static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b,
 >> +				struct v4l2_plane *v4l2_planes)
 >> +{
 >> +	unsigned int plane;
 >> +	int ret;
 >> +
 >> +	if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) {
 >> +		/*
 >> +		 * Verify that the userspace gave us a valid array for
 >> +		 * plane information.
 >> +		 */
 >> +		ret = __verify_planes_array(vb, b);
 >> +		if (ret)
 >> +			return ret;
 >> +
 >> +		/* Fill in driver-provided information for OUTPUT types */
 >> +		if (V4L2_TYPE_IS_OUTPUT(b->type)) {
 >> +			/*
 >> +			 * Will have to go up to b->length when API starts
 >> +			 * accepting variable number of planes.
 >> +			 */
 >> +			for (plane = 0; plane<  vb->num_planes; ++plane) {
 >> +				v4l2_planes[plane].bytesused =
 >> +					b->m.planes[plane].bytesused;
 >> +				v4l2_planes[plane].data_offset =
 >> +					b->m.planes[plane].data_offset;
 >> +			}
 >> +		}
 >> +
 >> +		if (b->memory == V4L2_MEMORY_USERPTR) {
 >> +			for (plane = 0; plane<  vb->num_planes; ++plane) {
 >> +				v4l2_planes[plane].m.userptr =
 >> +					b->m.planes[plane].m.userptr;
 >> +				v4l2_planes[plane].length =
 >> +					b->m.planes[plane].length;
 >> +			}
 >> +		}
 >> +	} else {
 >> +		/*
 >> +		 * Single-planar buffers do not use planes array,
 >> +		 * so fill in relevant v4l2_buffer struct fields instead.
 >> +		 * In videobuf we use our internal V4l2_planes struct for
 >> +		 * single-planar buffers as well, for simplicity.
 >> +		 */
 >> +		if (V4L2_TYPE_IS_OUTPUT(b->type))
 >> +			v4l2_planes[0].bytesused = b->bytesused;
 >> +
 >> +		if (b->memory == V4L2_MEMORY_USERPTR) {
 >> +			v4l2_planes[0].m.userptr = b->m.userptr;
 >> +			v4l2_planes[0].length = b->length;
 >> +		}
 >> +	}
 >> +
 >> +	vb->v4l2_buf.field = b->field;
 >> +	vb->v4l2_buf.timestamp = b->timestamp;
 >> +
 >> +	return 0;
 >> +}
 >> +
 >> +/**
 >> + * __qbuf_userptr() - handle qbuf of a USERPTR buffer
 >> + */
 >> +static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b)
 >> +{
 >> +	struct v4l2_plane planes[VIDEO_MAX_PLANES];
 >> +	struct vb2_queue *q = vb->vb2_queue;
 >> +	void *mem_priv = NULL;
 >> +	unsigned int plane;
 >> +	int ret;
 >> +
 >> +	/* Verify and copy relevant information provided by the userspace */
 >> +	ret = __fill_vb2_buffer(vb, b, planes);
 >> +	if (ret)
 >> +		return ret;
 >> +
 >> +	for (plane = 0; plane<  vb->num_planes; ++plane) {
 >> +		/* Skip the plane if already verified */
 >> +		if (vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr
 >> +		&&  vb->v4l2_planes[plane].length == planes[plane].length)
 >> +			continue;
 >> +
 >> +		dprintk(3, "qbuf: userspace address for plane %d changed, "
 >> +				"reacquiring memory\n", plane);
 >> +
 >> +		/* Release previously acquired memory if present */
 >> +		if (vb->planes[plane].mem_priv)
 >> +			call_memop(q, plane, put_userptr,
 >> +					vb->planes[plane].mem_priv);
 >> +
 >> +		vb->planes[plane].mem_priv = NULL;
 >> +
 >> +		/* Acquire each plane's memory */
 >> +		if (mem_ops(q, plane)->get_userptr) {
 >> +			mem_priv = mem_ops(q, plane)->get_userptr(
 >> +							planes[plane].m.userptr,
 >> +							planes[plane].length);
 >> +			if (IS_ERR(mem_priv)) {
 >> +				dprintk(1, "qbuf: failed acquiring userspace "
 >> +						"memory for plane %d\n", plane);
 >> +				goto err;
 >> +			}
 >
 > Just to verify my understanding: get_userptr is responsible for locking the
 > pages into memory and to increase a use count on that memory. So if the app
 > would do a QBUF, then free the memory and allocate a new buffer, then the
 > original buffer memory is not released until put_userptr is called, and the
 > pointer of the new buffer that the app allocated will always be different from
 > the original buffer (since it was still in use when the app allocated the new
 > one).
 >

Yes. If an application frees a buffer, the use count is still >0, since the driver
is still holding it. When the application queues the new buffer, userptr code
sees that the pointer has changed, releases the old memory and get()s the new one.

 > I've always wondered if something nasty like this was handled correctly. I never
 > had the time to dive into the mm core, though.
 >
<snip>
 >> +		ret = -EINVAL;
 >> +		goto done;
 >> +	}
 >> +
 >> +	/* Fill buffer information for the userspace */
 >> +	__fill_v4l2_buffer(vb, b);
 >> +	/* Remove from videobuf queue */
 >> +	list_del(&vb->queued_entry);
 >> +
 >> +	dprintk(1, "dqbuf of buffer %d, with state %d\n",
 >> +			vb->v4l2_buf.index, vb->state);
 >> +
 >> +	vb->state = VB2_BUF_STATE_DEQUEUED;
 >> +
 >> +done:
 >> +	mutex_unlock(&q->vb_lock);
 >> +	return ret;
 >> +}
 >> +EXPORT_SYMBOL_GPL(vb2_dqbuf);
 >> +
 >> +/**
 >> + * vb2_streamon - start streaming
 >> + * @q:		videobuf2 queue
 >> + * @type:	type argument passed from userspace to vidioc_streamon handler
 >> + *
 >> + * Should be called from vidioc_streamon handler of a driver.
 >> + * This function:
 >> + * 1) verifies current state
 >> + * 2) starts streaming and passes any previously queued buffers to the driver
 >> + *
 >> + * The return values from this function are intended to be directly returned
 >> + * from vidioc_streamon handler in the driver.
 >> + */
 >> +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
 >> +{
 >> +	struct vb2_buffer *vb;
 >> +	int ret = 0;
 >> +
 >> +	mutex_lock(&q->vb_lock);
 >> +
 >> +	if (type != q->type) {
 >> +		dprintk(1, "streamon: invalid stream type\n");
 >> +		ret = -EINVAL;
 >> +		goto done;
 >> +	}
 >> +
 >> +	if (q->streaming) {
 >> +		dprintk(1, "streamon: already streaming\n");
 >> +		ret = -EBUSY;
 >> +		goto done;
 >> +	}
 >> +
 >> +	/*
 >> +	 * Cannot start streaming on an OUTPUT device if no buffers have
 >> +	 * been queued yet.
 >> +	 */
 >> +	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
 >> +		if (list_empty(&q->queued_list)) {
 >> +			dprintk(1, "streamon: no output buffers queued\n");
 >> +			ret = -EINVAL;
 >> +			goto done;
 >> +		}
 >> +	}
 >> +
 >> +	q->streaming = 1;
 >> +
 >> +	/*
 >> +	 * If any buffers were queued before streamon,
 >> +	 * we can now pass them to driver for processing.
 >> +	 */
 >> +	list_for_each_entry(vb,&q->queued_list, queued_entry)
 >> +		__enqueue_in_driver(vb);
 >> +
 >> +	dprintk(3, "Streamon successful\n");
 >> +done:
 >> +	mutex_unlock(&q->vb_lock);
 >> +	return ret;
 >> +}
 >> +EXPORT_SYMBOL_GPL(vb2_streamon);
 >> +
 >> +/**
 >> + * __vb2_queue_cancel() - cancel and stop (pause) streaming
 >> + *
 >> + * Removes all queued buffers from driver's queue and all buffers queued by
 >> + * userspace from videobuf's queue. Returns to state after reqbufs.
 >> + */
 >> +static void __vb2_queue_cancel(struct vb2_queue *q)
 >> +{
 >> +	unsigned long flags = 0;
 >> +	int i;
 >> +
 >> +	q->streaming = 0;
 >> +
 >> +	/*
 >> +	 * Remove buffers from driver's queue. If a hardware operation
 >> +	 * is currently underway, drv_lock should be claimed and we will
 >> +	 * have to wait for it to finish before taking back buffers.
 >> +	 */
 >> +	spin_lock_irqsave(q->drv_lock, flags);
 >> +	for (i = 0; i<  q->num_buffers; ++i) {
 >> +		if (q->bufs[i]->state == VB2_BUF_STATE_ACTIVE)
 >> +			list_del(&q->bufs[i]->drv_entry);
 >> +		q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
 >> +	}
 >> +	spin_unlock_irqrestore(q->drv_lock, flags);
 >
 > I feel that this spinlock is too simplistic. I think we need a new op:
 > stop_streaming() or something like that. The driver will stop any streaming
 > and dequeue any active buffers. And if necessary it has to wait for any DMA
 > in progress to finish first.
 >
 > That will also make it possible to remove the drv_lock altogether and make the
 > driver responsible for proper locking in buf_queue and stop_streaming.
 >

The purpose of drv_lock is basically to properly handle cancelling. So yes, if we
add such an op, it'd become obsolete. Drivers would have to manage that spinlock
themselves. I'm trying to see all the implications, I'll try reimplementing it
to get a better picture.

 > Actually, it might also be an idea to implement a start_streaming op for symmetry.
 >

There is actually another thing to consider here. We had a thorough discussion with
Laurent about this, which I'm afraid I don't recall well enough at the moment though.
When vidioc_streamon in driver is called and buffers were previously queued, videobuf
calls buffer_queue in driver as well, so it can begin processing immediately. The
problem is though that the driver has to actually activate the device somewhere during
that time as well. We have two main scenarios:

driver_vidioc_streamon()
{
         activate_device();
         vb2_streamon();
}

or

driver_vidioc_streamon()
{
        vb2_streamon();
        activate_device();
}

Now in case (1), the buffers will be passed to the driver by vb after the device
is activated. This might not be good, since some devices may require buffers to be
ready before they can be activated. In case (2) on the other hand, streamon will
call the buffer_queue callback which now needs to know whether it has been called
from streamon or from a later qbuf. There are also issues with buffer_queue being
called with a spinlock held. I'm probably missing something right now, but maybe
introducing that start_streaming callback and at the same time getting rid of the
driver spinlock might help, somehow.

 >> +
 >> +	/*
 >> +	 * Remove all buffers from videobuf's list...
 >> +	 */
 >> +	INIT_LIST_HEAD(&q->queued_list);
 >> +	/*
 >> +	 * ...and done list; userspace will not receive any buffers it
 >> +	 * has not already dequeued before initiating cancel.
 >> +	 */
 >> +	INIT_LIST_HEAD(&q->done_list);
 >
 > Is this correct? Shouldn't put_userptr be called for all queued and done buffers?
 >

queue_cancel is used for pausing. Since we do not want to free memory on pause,
I thought that the symmetrical thing to do here in case of userptr is not put()ing
memory when pausing either. Put is called on close (release) or on reqbufs(0).

 >> +	wake_up_interruptible_all(&q->done_wq);
 >> +}
 >> +
 >> +/**
 >> + * vb2_streamoff - stop streaming
 >> + * @q:		videobuf2 queue
 >> + * @type:	type argument passed from userspace to vidioc_streamoff handler
 >> + *
 >> + * Should be called from vidioc_streamoff handler of a driver.
 >> + * This function:
 >> + * 1) verifies current state,
 >> + * 2) stop streaming and dequeues any queued buffers, including those previously
 >> + *    passed to the driver (after waiting for the driver to finish).
 >> + *
 >> + * This call can be used for pausing playback.
 >> + * The return values from this function are intended to be directly returned
 >> + * from vidioc_streamoff handler in the driver
 >> + */
 >> +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
 >> +{
 >> +	int ret = 0;
 >> +
 >> +	mutex_lock(&q->vb_lock);
 >> +
 >> +	if (type != q->type) {
 >> +		dprintk(1, "streamoff: invalid stream type\n");
 >> +		ret = -EINVAL;
 >> +		goto end;
 >> +	}
 >> +
 >> +	if (!q->streaming) {
 >> +		dprintk(1, "streamoff: not streaming\n");
 >> +		ret = -EINVAL;
 >> +		goto end;
 >> +	}
 >> +
 >> +	/*
 >> +	 * Cancel will pause streaming and remove all buffers from the driver
 >> +	 * and videobuf, effectively returning control over them to userspace.
 >> +	 */
 >> +	__vb2_queue_cancel(q);
 >> +
 >> +	dprintk(3, "Streamoff successful\n");
 >> +end:
 >> +	mutex_unlock(&q->vb_lock);
 >> +	return ret;
 >> +}
 >> +EXPORT_SYMBOL_GPL(vb2_streamoff);
 >
 > <snip>
 >
 > Some general remarks:
 >
 > 1) It is probably useful to add a simply inline function like this:
 >
 > static inline bool vb2_is_streaming(struct vb2_queue *q)
 > {
 >         return q->streaming;
 > }
 >

Yes, good point.

 > 2) We need very clear documentation detailing:
 >
 >     - where the struct vb2_queue has to be stored (it should be associated with the
 >       video_device struct or the v4l2_device struct if there is only one queue).

I don't think vb2 needs to be aware with what it is "associated" with. Does/should it
make any difference for vb2?

 >     - when to call vb2_queue_init (before registering the device node, I think).

No constraints on how early it can be called, basically yes.

 >     - when to call vb2_queue_release (after unregistering the device node? Does that
 >       also work well with USB devices after a disconnect? I think it is OK, but I'm
 >       not 100% certain.)

Not sure about how USB devices work in this matter...

 >     - how does it work if multiple file handles are open? If fh A calls REQBUFS, is fh
 >       B allowed to call it again? My feeling is that once a fh calls REQBUFS, the
 >       queue is associated with that fh until REQBUFS with count == 0 is called, or
 >       until the fh is closed. All the other streaming ioctls should be called from
 >       that fh. To implement this reqbufs should be passed a struct file (or a v4l2_fh?).
 >       And for the others we either need this as well or we add a simple inline function
 >       checking this that drivers can call.

Basically, if you call reqbufs multiple times, it will work as expected, i.e. will
free or reallocate memory.

vb2 doesn't have any notion of video devices, file handles, etc. Do you think it should?
It just does whatever driver passes to it, of course making sure everything stays in
a sane state (e.g. you can't allocate memory twice, or free memory when streaming).

I think the state machine in vb2 is complicated enough. If we need more advanced,
file handle-aware state machine, maybe a higher-level module should be introduced for that,
something that would build up on vb2, like mem2mem does?

I think you might be thinking of vb2 as of a more complicated framework that it really is.
I was trying to make it as compact as possible, so that it would provide fundamentals to
later built on if needed: e.g. if we needed a mem2mem-like capability, we'd use mem2mem
that would be adding additional constraints and features built on vb2, if we needed
something else, we'd have a different module for that, also utilizing the basic vb2
framework. What is your opinion on that?

 >
 > 3) Read/write will have its own issues: if the driver supports read/write, then
 >     some internal checks are needed: once reqbufs was called on a fh, read/write
 >     should not be allowed (until REQBUFS(0), that is). The same is true vice versa,
 >     except that once you've started reading or writing the only way to go back to
 >     streaming I/O is by closing the fh first.
 >

Yes, that is basically it, to sum up:
REQBUFS(n) - ... - REQBUFS(0) - read/write allowed
REQBUFS(n) - ... - read/write disallowed
REQBUFS(n) - ... - close - open - read/write allowed
read/write - close - open - REQBUFS(0) - allowed
read/write - REQBUFS(0) - disallowed

 > 4) If poll is called without a preceding reqbufs or read, then it should initiate
 >     streaming and mark the queue as being in read (or write) mode. It's the way poll
 >     is supposed to work for read or write.
 >

Ok.

 > 5) Allowing mixing of read/write and streaming I/O. I am very much opposed to this.
 >     First of all it will cause skipped frames since read will steal from dqbuf (or
 >     vice versa, depending on how you look at it). Once you start to read it will also
 >     be impossible to use REQBUFS(0), and the internal administration will be a nightmare.
 >     Frankly, I don't think there is a way to implement this in a way that makes sense.
 >     We should probably investigate those utilities that are supposed to do this.
 >     I understand that they are xawtv and xdtv.
 >

I know Mauro would like to see this, but I'm not really keen on it either. But I'm thinking
of a different way to do that: introduce two videobuf queues and let driver multiplex among
them, which would basically mean initializing both queues on open, setting up formats on both
at the same time, etc., and passing read calls to one of the queues and streaming calls to
the other. Of course this would result in frame dropping...

-- 
Best regards,
Pawel Osciak

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

end of thread, other threads:[~2010-09-29 23:40 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-09  9:19 [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
2010-09-09  9:19 ` [PATCH v1 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Pawel Osciak
2010-09-09 17:29   ` Mauro Carvalho Chehab
2010-09-15 20:16     ` Pawel Osciak
2010-09-25 14:27   ` Hans Verkuil
2010-09-29 23:40     ` Pawel Osciak
2010-09-09  9:19 ` [PATCH v1 2/7] v4l: videobuf2: add generic memory handling routines Pawel Osciak
2010-09-09 17:34   ` Mauro Carvalho Chehab
2010-09-09  9:19 ` [PATCH v1 3/7] v4l: mem2mem: port to videobuf2 Pawel Osciak
2010-09-09  9:19 ` [PATCH v1 4/7] v4l: videobuf2: add vmalloc allocator Pawel Osciak
2010-09-09  9:19 ` [PATCH v1 5/7] v4l: videobuf2: add DMA coherent allocator Pawel Osciak
2010-09-09  9:19 ` [PATCH v1 6/7] v4l: vivi: port to videobuf2 Pawel Osciak
2010-09-09  9:19 ` [PATCH v1 7/7] v4l: videobuf2: add CMA allocator Pawel Osciak
2010-09-15  8:55   ` han jonghun
2010-09-15 20:25     ` Pawel Osciak
2010-09-09  9:26 ` [PATCH/RFC v1 0/7] Videobuf2 framework Pawel Osciak
2010-09-09 17:53 ` Mauro Carvalho Chehab
2010-09-10  3:20   ` Pawel Osciak
2010-09-10  4:27     ` Mauro Carvalho Chehab
2010-09-10  7:38       ` Marek Szyprowski
2010-09-10  8:22         ` Hans Verkuil
2010-09-10  8:26           ` Devin Heitmueller
2010-09-10 13:50           ` Andy Walls
2010-09-10 12:15         ` Mauro Carvalho Chehab

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.