All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH/RFC v4 0/7] Videobuf2 framework
@ 2010-11-17  8:39 Marek Szyprowski
  2010-11-17  8:39 ` [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Marek Szyprowski
                   ` (6 more replies)
  0 siblings, 7 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-17  8:39 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

Hello,

I've finally managed to implement all the items that I presented on
Linux Plumbers Conference 2010 in Cambridge, MA, US.

This is a major update since the last public version. The most important
items are a new locking policy that came after a discussion with Hans
Verkuil on Embedded Linux Conference Europe and support for write()
call.

File IO emulator provides 2 modes for both read and write. By default
both read and write are implemented in 'streaming' style (like old
videobuf_read_stream() call). By setting VB2_FILEIO_READ_ONCE one can
request 'one shot' mode (like videobuf_read_one() from the original
videobuf). For write emulator one can set VB2_FILEIO_WRITE_IMMEDIATE
flag, what will make each write() call to transform directly into a
q_buf() with aproperiate bytesused entry set, without waiting until the
buffer is filled completely.

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

Changes since V3:
=================
- rebased onto 2.6.37-rc2
- new locking policy: vb2 has no internal/hidden locks, the driver takes
  all the responsibility to ensure proper locking: removed vb2_lock and
  introduced new entries in qops: lock and unlock
- added buf_alloc and buf_free callback, it was very hard to add driver
  dependent custom data to buffers without them (and erlier version of VIVI
  driver silently trashed memory with its vivi_buffer structures - now fixed)
- added a new macro 'call_qop' to the core, simplified code
- fixed bytesused entry handling in core (it is now always stored in planes[0])
- changed the paddr callback into a cookie (required for the new upcoming dma
  sg and iommu memory allocators), see include/media/videobuf2-dma-coherent.h
  for more details
- added generic write() support!

Changes since V2:
=================
- added read() emulator (see patch #5/7)
- fixed lack of parentheses in macro definitions (caused side effects
  in some places)
- added a separate check for VM_READ or VM_WRITE in vb2_mmap()
- added vb2_is_streaming(), vb2_lock and vb2_unlock inlines
- updated vivi driver with the new read() emulator 

Changes since V1:
=================
- removed drv_lock, added start_streaming and stop_streaming callbacks


Here is the original Videobuf2 introduction prepared by Pawel:
=======================================================================

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!

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

=======================================================================

Patch summary:

Marek Szyprowski (2):
  v4l: videobuf2: add read() and write() emulator
  v4l: vivi: port to videobuf2

Pawel Osciak (5):
  v4l: add videobuf2 Video for Linux 2 driver framework
  v4l: videobuf2: add generic memory handling routines
  v4l: videobuf2: add vmalloc allocator
  v4l: videobuf2: add DMA coherent allocator
  v4l: videobuf2: add CMA allocator

 drivers/media/video/Kconfig                  |   23 +-
 drivers/media/video/Makefile                 |    6 +
 drivers/media/video/videobuf2-cma.c          |  250 ++++
 drivers/media/video/videobuf2-core.c         | 1829 ++++++++++++++++++++++++++
 drivers/media/video/videobuf2-dma-coherent.c |  208 +++
 drivers/media/video/videobuf2-memops.c       |  199 +++
 drivers/media/video/videobuf2-vmalloc.c      |  177 +++
 drivers/media/video/vivi.c                   |  390 +++---
 include/media/videobuf2-cma.h                |   31 +
 include/media/videobuf2-core.h               |  403 ++++++
 include/media/videobuf2-dma-coherent.h       |   27 +
 include/media/videobuf2-memops.h             |   31 +
 include/media/videobuf2-vmalloc.h            |   16 +
 13 files changed, 3419 insertions(+), 171 deletions(-)
 create mode 100644 drivers/media/video/videobuf2-cma.c
 create mode 100644 drivers/media/video/videobuf2-core.c
 create mode 100644 drivers/media/video/videobuf2-dma-coherent.c
 create mode 100644 drivers/media/video/videobuf2-memops.c
 create mode 100644 drivers/media/video/videobuf2-vmalloc.c
 create mode 100644 include/media/videobuf2-cma.h
 create mode 100644 include/media/videobuf2-core.h
 create mode 100644 include/media/videobuf2-dma-coherent.h
 create mode 100644 include/media/videobuf2-memops.h
 create mode 100644 include/media/videobuf2-vmalloc.h

-- 
1.7.1.569.g6f426


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

* [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-11-17  8:39 [PATCH/RFC v4 0/7] Videobuf2 framework Marek Szyprowski
@ 2010-11-17  8:39 ` Marek Szyprowski
  2010-11-18  9:17   ` Hans Verkuil
  2010-11-17  8:39 ` [PATCH 2/7] v4l: videobuf2: add generic memory handling routines Marek Szyprowski
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-17  8:39 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

From: Pawel Osciak <p.osciak@samsung.com>

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: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/video/Kconfig          |    3 +
 drivers/media/video/Makefile         |    2 +
 drivers/media/video/videobuf2-core.c | 1434 ++++++++++++++++++++++++++++++++++
 include/media/videobuf2-core.h       |  373 +++++++++
 4 files changed, 1812 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 ac16e81..fef6a14 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -49,6 +49,9 @@ 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 af79d47..77c4f85 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -114,6 +114,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..5c9d3d8
--- /dev/null
+++ b/drivers/media/video/videobuf2-core.c
@@ -0,0 +1,1434 @@
+/*
+ * videobuf2-core.c - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *	   Marek Szyprowski <m.szyprowski@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)
+
+#define call_qop(q, op, args...)					\
+	(((q)->ops->op) ? ((q)->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_buf_alloc() - allocate a singe vb2_buffer structure
+ *
+ * Returns the pointer to the allocated structure.
+ */
+void *__vb2_buf_alloc(struct vb2_queue *q)
+{
+	struct vb2_buffer *vb;
+	if (q->ops->buf_alloc)
+		vb = q->ops->buf_alloc(q);
+	else
+		vb = kzalloc(sizeof(struct vb2_buffer), GFP_KERNEL);
+	return vb;
+}
+
+/**
+ * __vb2_buf_free() - free a singe vb2_buffer structure
+ *
+ * Frees memory used by the vb2_buffer structure.
+ */
+void __vb2_buf_free(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+	if (q->ops->buf_free)
+		q->ops->buf_free(q, vb);
+	else
+		kfree(vb);
+}
+
+/**
+ * __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 = call_qop(q, 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 = __vb2_buf_alloc(q);
+		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);
+				__vb2_buf_free(q, vb);
+				break;
+			}
+			/*
+			 * Call the driver-provided buffer initialization
+			 * callback, if given. An error in initialization
+			 * results in queue setup failure.
+			 */
+			ret = call_qop(q, buf_init, vb);
+			if (ret) {
+				dprintk(1, "Buffer %d %p initialization"
+					" failed\n", buffer, vb);
+				__vb2_buf_mem_free(vb);
+				__vb2_buf_free(q, 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;
+		__vb2_buf_free(q, 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;
+		b->bytesused = vb->v4l2_planes[0].bytesused;
+		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;
+
+	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:
+	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;
+	}
+
+	if (req->type != q->type) {
+		dprintk(1, "reqbufs: queue type invalid\n");
+		return -EINVAL;
+	}
+
+	if (q->streaming) {
+		dprintk(1, "reqbufs: streaming active\n");
+		return -EBUSY;
+	}
+
+	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:
+	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_cookie() - Return allocator specific cookie for the 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.
+ */
+void *vb2_plane_cookie(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, cookie, vb->planes[plane_no].mem_priv);
+}
+EXPORT_SYMBOL_GPL(vb2_plane_cookie);
+
+/**
+ * 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(&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.
+	 */
+	ret = call_qop(q, 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;
+	vb->state = VB2_BUF_STATE_ACTIVE;
+	q->ops->buf_queue(vb);
+}
+
+/**
+ * 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 = 0;
+
+
+	if (b->type != q->type) {
+		dprintk(1, "qbuf: invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	if (b->index >= q->num_buffers) {
+		dprintk(1, "qbuf: buffer index out of range\n");
+		return -EINVAL;
+	}
+
+	vb = q->bufs[b->index];
+	if (NULL == vb) {
+		/* Should never happen */
+		dprintk(1, "qbuf: buffer is NULL\n");
+		return -EINVAL;
+	}
+
+	if (b->memory != q->memory) {
+		dprintk(1, "qbuf: invalid memory type\n");
+		return -EINVAL;
+	}
+
+	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+		dprintk(1, "qbuf: buffer already in use\n");
+		return -EINVAL;
+	}
+
+	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;
+
+	ret = call_qop(q, 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:
+	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)
+{
+checks:
+	if (!q->streaming) {
+		dprintk(1, "Streaming off, will not wait for buffers\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Buffers may be added to vb_done_list without holding the driver's
+	 * lock, but removal is performed only while holding both driver's
+	 * lock and the vb_done_lock spinlock. Thus we can be sure that as
+	 * long as we hold lock, the list will remain not empty if this
+	 * check succeeds.
+	 */
+	if (list_empty(&q->done_list)) {
+		int retval;
+		if (nonblocking) {
+			dprintk(1, "Nonblocking and no buffers to dequeue, "
+					"will not wait\n");
+			return -EAGAIN;
+		}
+
+		/*
+		 * We are streaming and nonblocking, wait for another buffer to
+		 * become ready or for streamoff. Driver's lock is released to
+		 * allow streamoff or qbuf to be called while waiting.
+		 */
+		call_qop(q, unlock, q);
+
+		/*
+		 * 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);
+		call_qop(q, lock, q);
+
+		if (retval)
+			return -EINTR;
+
+		goto checks;
+	}
+	return 0;
+}
+
+/**
+ * __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;
+
+	/*
+	 * Drivers 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;
+
+
+	if (b->type != q->type) {
+		dprintk(1, "dqbuf: invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	ret = __vb2_get_done_vb(q, &vb, nonblocking);
+	if (ret < 0) {
+		dprintk(1, "dqbuf: error getting next done buffer\n");
+		goto done;
+	}
+
+	ret = call_qop(q, 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:
+	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;
+
+
+	if (type != q->type) {
+		dprintk(1, "streamon: invalid stream type\n");
+		return -EINVAL;
+	}
+
+	if (q->streaming) {
+		dprintk(1, "streamon: already streaming\n");
+		return -EBUSY;
+	}
+
+	/*
+	 * 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;
+
+	/*
+	 * Let driver notice that streaming state has been enabled.
+	 */
+	call_qop(q, start_streaming, q);
+
+	/*
+	 * 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:
+	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)
+{
+	/*
+	 * Tell driver to stop all dma transactions and release all queued
+	 * buffers
+	 */
+
+	if (q->streaming)
+		call_qop(q, stop_streaming, q);
+	q->streaming = 0;
+
+	/*
+	 * 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_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;
+
+
+	if (type != q->type) {
+		dprintk(1, "streamoff: invalid stream type\n");
+		return -EINVAL;
+	}
+
+	if (!q->streaming) {
+		dprintk(1, "streamoff: not streaming\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * 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");
+	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 (V4L2_TYPE_IS_OUTPUT(q->type) &&
+	   (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))) {
+		dprintk(1, "Invalid vma flags (need VM_WRITE | VM_SHARED)\n");
+		return ret;
+	} else if (!(vma->vm_flags & VM_READ) || !(vma->vm_flags & VM_SHARED)) {
+		dprintk(1, "Invalid vma flags (need VM_READ | VM_SHARED)\n");
+		return ret;
+	}
+
+	/*
+	 * 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:
+	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;
+
+
+	/*
+	 * 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:
+	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
+ * @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,
+			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->type = type;
+	q->drv_priv = drv_priv;
+
+	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)
+{
+	__vb2_queue_cancel(q);
+	__vb2_queue_free(q);
+}
+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..98bc743
--- /dev/null
+++ b/include/media/videobuf2-core.h
@@ -0,0 +1,373 @@
+/*
+ * 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>
+
+struct vb2_alloc_ctx;
+
+/**
+ * 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);
+	void		*(*cookie)(void *buf_priv);
+
+	unsigned int	(*num_users)(void *buf_priv);
+
+	int		(*mmap)(void *buf_priv, struct vm_area_struct *vma);
+};
+
+/**
+ * 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_plane {
+	void			*mem_priv;
+	int			mapped:1;
+};
+
+/**
+ * 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_queue;
+
+/**
+ * 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;
+
+	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
+ * @lock:		aquire all locks taken before any other calls to vb2;
+ *			required after sleeing in poll_wait function
+ * @unlock:		release any locks taken before calling vb2 function;
+ *			required for a safe to call poll_wait function
+ * @buf_alloc:		called to allocate a struct vb2_buffer; driver usually
+ *			embeds it in its own custom buffer structure; returns
+ *			a pointer to vb2_buffer or NULL if failed; if not
+ *			provided kmalloc(sizeof(struct vb2_buffer, GFP_KERNEL)
+ *			is used
+ * @buf_free:		called to free the structure allocated by @buf_alloc;
+ *			if not provided kfree(vb) is used
+ * @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
+ * @start_streaming:	called once before entering 'streaming' state; enables
+ *			driver to recieve buffers over buf_queue() callback
+ * @stop_streaming:	called when 'streaming' state must be disabled; driver
+ *			should stop any dma transactions or wait until they
+ *			finish and give back all buffers it got from buf_queue()
+ *			callback
+ * @buf_queue:		passes buffer vb to the driver; driver may start
+ *			hardware operation on this buffer; driver should give
+ *			the buffer back by calling vb2_buffer_done() function
+ */
+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 (*lock)(struct vb2_queue *q);
+	void (*unlock)(struct vb2_queue *q);
+
+	struct vb2_buffer *(*buf_alloc)(struct vb2_queue *q);
+	void (*buf_free)(struct vb2_queue *q, 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);
+
+	int (*start_streaming)(struct vb2_queue *q);
+	int (*stop_streaming)(struct vb2_queue *q);
+
+	void (*buf_queue)(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
+ * @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 list_head		queued_list;
+
+	struct list_head		done_list;
+	spinlock_t			done_lock;
+	wait_queue_head_t		done_wq;
+
+	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;
+};
+
+void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
+void *vb2_plane_cookie(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,
+			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_is_streaming() - return streaming status of the queue
+ * @q:		videobuf queue
+ */
+static inline bool vb2_is_streaming(struct vb2_queue *q)
+{
+	return q->streaming;
+}
+
+/**
+ * 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)
+{
+	vb->v4l2_planes[plane_no].bytesused = size;
+}
+
+/**
+ * vb2_get_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 unsigned long vb2_get_plane_payload(struct vb2_buffer *vb,
+				 unsigned int plane_no)
+{
+	return vb->v4l2_planes[plane_no].bytesused;
+}
+
+/**
+ * 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;
+}
+
+#endif /* _MEDIA_VIDEOBUF2_CORE_H */
-- 
1.7.1.569.g6f426


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

* [PATCH 2/7] v4l: videobuf2: add generic memory handling routines
  2010-11-17  8:39 [PATCH/RFC v4 0/7] Videobuf2 framework Marek Szyprowski
  2010-11-17  8:39 ` [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Marek Szyprowski
@ 2010-11-17  8:39 ` Marek Szyprowski
  2010-11-17  8:39 ` [PATCH 3/7] v4l: videobuf2: add vmalloc allocator Marek Szyprowski
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-17  8:39 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

From: Pawel Osciak <p.osciak@samsung.com>

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: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/video/Kconfig            |    3 +
 drivers/media/video/Makefile           |    1 +
 drivers/media/video/videobuf2-memops.c |  199 ++++++++++++++++++++++++++++++++
 include/media/videobuf2-memops.h       |   31 +++++
 4 files changed, 234 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 fef6a14..83ce858 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -52,6 +52,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 77c4f85..a97a2a0 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -115,6 +115,7 @@ 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..67ebdff
--- /dev/null
+++ b/drivers/media/video/videobuf2-memops.c
@@ -0,0 +1,199 @@
+/*
+ * videobuf2-memops.c - generic memory handling routines for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *	   Marek Szyprowski <m.szyprowski@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;
+	struct vm_area_struct *vma_copy;
+
+	vma_copy = kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
+	if (vma_copy == NULL)
+		return NULL;
+
+	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);
+
+	memcpy(vma_copy, vma, sizeof(*vma));
+done:
+	up_read(&mm->mmap_sem);
+
+	vma_copy->vm_mm = NULL;
+	vma_copy->vm_next = NULL;
+	vma_copy->vm_prev = NULL;
+
+	return vma_copy;
+}
+
+/**
+ * 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);
+
+	kfree(vma);
+}
+
+MODULE_DESCRIPTION("common memory handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak");
+MODULE_LICENSE("GPL");
diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h
new file mode 100644
index 0000000..3257411
--- /dev/null
+++ b/include/media/videobuf2-memops.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef _MEDIA_VIDEOBUF2_MEMOPS_H
+#define _MEDIA_VIDEOBUF2_MEMOPS_H
+
+#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);
+
+#endif
-- 
1.7.1.569.g6f426


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

* [PATCH 3/7] v4l: videobuf2: add vmalloc allocator
  2010-11-17  8:39 [PATCH/RFC v4 0/7] Videobuf2 framework Marek Szyprowski
  2010-11-17  8:39 ` [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Marek Szyprowski
  2010-11-17  8:39 ` [PATCH 2/7] v4l: videobuf2: add generic memory handling routines Marek Szyprowski
@ 2010-11-17  8:39 ` Marek Szyprowski
  2010-11-17  8:39 ` [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator Marek Szyprowski
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-17  8:39 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

From: Pawel Osciak <p.osciak@samsung.com>

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>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/video/Kconfig             |    5 +
 drivers/media/video/Makefile            |    1 +
 drivers/media/video/videobuf2-vmalloc.c |  177 +++++++++++++++++++++++++++++++
 include/media/videobuf2-vmalloc.h       |   16 +++
 4 files changed, 199 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 83ce858..9351423 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_MEMOPS
+	tristate
+
 #
 # Multimedia Video device configuration
 #
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index a97a2a0..538bee9 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -116,6 +116,7 @@ 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
 
 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.1.569.g6f426


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

* [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator
  2010-11-17  8:39 [PATCH/RFC v4 0/7] Videobuf2 framework Marek Szyprowski
                   ` (2 preceding siblings ...)
  2010-11-17  8:39 ` [PATCH 3/7] v4l: videobuf2: add vmalloc allocator Marek Szyprowski
@ 2010-11-17  8:39 ` Marek Szyprowski
  2010-11-18  7:14   ` Sewoon Park
  2010-11-17  8:39 ` [PATCH 5/7] v4l: videobuf2: add read() and write() emulator Marek Szyprowski
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-17  8:39 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

From: Pawel Osciak <p.osciak@samsung.com>

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>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/video/Kconfig                  |    5 +
 drivers/media/video/Makefile                 |    1 +
 drivers/media/video/videobuf2-dma-coherent.c |  208 ++++++++++++++++++++++++++
 include/media/videobuf2-dma-coherent.h       |   27 ++++
 4 files changed, 241 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 9351423..e7752ee1 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_MEMOPS
+	tristate
+
 config VIDEOBUF2_VMALLOC
 	select VIDEOBUF2_CORE
 	select VIDEOBUF2_MEMOPS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 538bee9..baa74e7 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -117,6 +117,7 @@ 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
+obj-$(CONFIG_VIDEOBUF2_DMA_COHERENT)	+= videobuf2-dma_coherent.o
 
 obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.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..761f366
--- /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..73d14ac
--- /dev/null
+++ b/include/media/videobuf2-dma-coherent.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#ifndef _MEDIA_VIDEOBUF2_DMA_COHERENT_H
+#define _MEDIA_VIDEOBUF2_DMA_COHERENT_H
+
+#include <media/videobuf2-core.h>
+
+static inline unsigned long vb2_dma_coherent_plane_paddr(
+		struct vb2_buffer *vb, unsigned int plane_no)
+{
+	return (unsigned long)vb2_plane_cookie(vb, plane_no);
+}
+
+void *vb2_dma_coherent_init(struct device *dev);
+void vb2_dma_coherent_cleanup(struct vb2_alloc_ctx *alloc_ctx);
+
+#endif
-- 
1.7.1.569.g6f426


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

* [PATCH 5/7] v4l: videobuf2: add read() and write() emulator
  2010-11-17  8:39 [PATCH/RFC v4 0/7] Videobuf2 framework Marek Szyprowski
                   ` (3 preceding siblings ...)
  2010-11-17  8:39 ` [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator Marek Szyprowski
@ 2010-11-17  8:39 ` Marek Szyprowski
  2010-11-17  8:39 ` [PATCH 6/7] v4l: vivi: port to videobuf2 Marek Szyprowski
  2010-11-17  8:39 ` [PATCH 7/7] v4l: videobuf2: add CMA allocator Marek Szyprowski
  6 siblings, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-17  8:39 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

Add a generic file io (read and write) emulator for videobuf2. It uses
MMAP memory type buffers and generic vb2 calls: req_bufs, qbuf and
dqbuf. Video date is being copied from mmap buffers to userspace with
standard copy_to_user() function. To add support for file io the driver
needs to provide an additional callback - read_setup or write_setup. It
should provide the default number of buffers used by emulator and flags.

With these flags one can detemine the style of read() or write()
emulation. By default 'streaming' style is used. With
VB2_FILEIO_READ_ONCE flag one can select 'one shot' mode for read()
emulator. With VB2_FILEIO_WRITE_IMMEDIATE flag one can select immediate
conversion of write calls to qbuf for write() emulator, so the vb2 will
not wait until each buffer is filled completely before queueing it to
the driver.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/video/videobuf2-core.c |  397 +++++++++++++++++++++++++++++++++-
 include/media/videobuf2-core.h       |   30 +++
 2 files changed, 426 insertions(+), 1 deletions(-)

diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 5c9d3d8..35acfe0 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -503,6 +503,11 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
 	unsigned int num_buffers, num_planes;
 	int ret = 0;
 
+	if (q->fileio) {
+		dprintk(1, "reqbufs: file io in progress\n");
+		return -EBUSY;
+	}
+
 	if (req->memory != V4L2_MEMORY_MMAP
 			&& req->memory != V4L2_MEMORY_USERPTR) {
 		dprintk(1, "reqbufs: unsupported memory type\n");
@@ -846,6 +851,10 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
 	struct vb2_buffer *vb;
 	int ret = 0;
 
+	if (q->fileio) {
+		dprintk(1, "qbuf: file io in progress\n");
+		return -EBUSY;
+	}
 
 	if (b->type != q->type) {
 		dprintk(1, "qbuf: invalid buffer type\n");
@@ -1019,6 +1028,10 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
 	struct vb2_buffer *vb = NULL;
 	int ret;
 
+	if (q->fileio) {
+		dprintk(1, "dqbuf: file io in progress\n");
+		return -EBUSY;
+	}
 
 	if (b->type != q->type) {
 		dprintk(1, "dqbuf: invalid buffer type\n");
@@ -1083,6 +1096,10 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
 	struct vb2_buffer *vb;
 	int ret = 0;
 
+	if (q->fileio) {
+		dprintk(1, "streamon: file io in progress\n");
+		return -EBUSY;
+	}
 
 	if (type != q->type) {
 		dprintk(1, "streamon: invalid stream type\n");
@@ -1174,6 +1191,10 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
 {
 	int ret = 0;
 
+	if (q->fileio) {
+		dprintk(1, "streamoff: file io in progress\n");
+		return -EBUSY;
+	}
 
 	if (type != q->type) {
 		dprintk(1, "streamoff: invalid stream type\n");
@@ -1312,6 +1333,9 @@ bool vb2_has_consumers(struct vb2_queue *q)
 }
 EXPORT_SYMBOL_GPL(vb2_has_consumers);
 
+static int __vb2_init_fileio(struct vb2_queue *q, int read);
+static int __vb2_cleanup_fileio(struct vb2_queue *q);
+
 /**
  * vb2_poll() - implements poll userspace operation
  * @q:		videobuf2 queue
@@ -1334,6 +1358,22 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
 	unsigned int ret = 0;
 	struct vb2_buffer *vb = NULL;
 
+	/*
+	 * Start file io emulator if streaming api has not been used yet.
+	 */
+	if (q->num_buffers == 0 && q->fileio == NULL) {
+		if (!V4L2_TYPE_IS_OUTPUT(q->type) && q->ops->read_setup) {
+			ret = __vb2_init_fileio(q, 1);
+			if (ret)
+				goto end;
+		}
+		if (V4L2_TYPE_IS_OUTPUT(q->type) && q->ops->write_setup) {
+			ret = __vb2_init_fileio(q, 1);
+			if (ret == 0)
+				ret = POLLOUT | POLLWRNORM;
+			goto end;
+		}
+	}
 
 	/*
 	 * There is nothing to wait for if no buffers have already been queued.
@@ -1424,11 +1464,366 @@ EXPORT_SYMBOL_GPL(vb2_queue_init);
  */
 void vb2_queue_release(struct vb2_queue *q)
 {
+	__vb2_cleanup_fileio(q);
 	__vb2_queue_cancel(q);
 	__vb2_queue_free(q);
 }
 EXPORT_SYMBOL_GPL(vb2_queue_release);
 
+/**
+ * struct vb2_fileio_buf - buffer context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. This structure is used for
+ * tracking context related to the buffers.
+ */
+struct vb2_fileio_buf {
+	void *vaddr;
+	unsigned int size;
+	unsigned int pos;
+	unsigned int queued:1;
+};
+
+/**
+ * struct vb2_fileio_data - queue context used by file io emulator
+ *
+ * vb2 provides a compatibility layer and emulator of file io (read and
+ * write) calls on top of streaming API. For proper operation it required
+ * this structure to save the driver state between each call of the read
+ * or write function.
+ */
+struct vb2_fileio_data {
+	struct v4l2_requestbuffers req;
+	struct v4l2_buffer b;
+	struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME];
+	unsigned int index;
+	unsigned int q_count;
+	unsigned int dq_count;
+	unsigned int flags;
+};
+
+/**
+ * __vb2_init_fileio() - initialize file io emulator
+ * @q:		videobuf2 queue
+ * @read:	mode selector (1 means read, 0 means write)
+ */
+static int __vb2_init_fileio(struct vb2_queue *q, int read)
+{
+	struct vb2_fileio_data *fileio;
+	int i, ret;
+	unsigned int count=0, flags=0;
+
+	/*
+	 * Sanity check
+	 */
+	if ((read && !q->ops->read_setup) || (!read && !q->ops->write_setup))
+		BUG();
+
+	/*
+	 * Check if device supports mapping buffers to kernel virtual space.
+	 */
+	if (!q->alloc_ctx[0]->mem_ops->vaddr)
+		return -EBUSY;
+
+	/*
+	 * Check if steaming api has not been already activated.
+	 */
+	if (q->streaming || q->num_buffers > 0)
+		return -EBUSY;
+
+	/*
+	 * Basic checks done, lets try to set up file io emulator
+	 */
+	if (read)
+		ret = call_qop(q, read_setup, q, &count, &flags);
+	else
+		ret = call_qop(q, write_setup, q, &count, &flags);
+	if (ret)
+		return ret;
+
+	dprintk(3, "setting up file io: mode %s, count %d, flags %08x\n",
+		(read) ? "read" : "write", count, flags);
+
+	fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL);
+	if (fileio == NULL)
+		return -ENOMEM;
+
+	fileio->flags = flags;
+
+	/*
+	 * Request buffers and use MMAP type to force driver
+	 * to allocate buffers by itself.
+	 */
+	fileio->req.count = count;
+	fileio->req.memory = V4L2_MEMORY_MMAP;
+	fileio->req.type = q->type;
+	ret = vb2_reqbufs(q, &fileio->req);
+	if (ret)
+		goto err_kfree;
+
+	/*
+	 * Check if plane_count is correct
+	 * (multiplane buffers are not supported).
+	 */
+	if (q->bufs[0]->num_planes != 1) {
+		fileio->req.count = 0;
+		ret = -EBUSY;
+		goto err_reqbufs;
+	}
+
+	/*
+	 * Get kernel address of each buffer.
+	 */
+	for (i = 0; i < q->num_buffers; i++) {
+		fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0);
+		if (fileio->bufs[i].vaddr == NULL)
+			goto err_reqbufs;
+		fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
+	}
+
+	/*
+	 * Read mode requires pre queuing of all buffers.
+	 */
+	if (read) {
+		/*
+		 * Queue all buffers.
+		 */
+		for (i = 0; i < q->num_buffers; i++) {
+			struct v4l2_buffer *b = &fileio->b;
+			memset(b, 0, sizeof(*b));
+			b->type = q->type;
+			b->memory = q->memory;
+			b->index = i;
+			ret = vb2_qbuf(q, b);
+			if (ret)
+				goto err_reqbufs;
+			fileio->bufs[i].queued = 1;
+		}
+
+		/*
+		 * Start streaming.
+		 */
+		ret = vb2_streamon(q, q->type);
+		if (ret)
+			goto err_reqbufs;
+	}
+
+	q->fileio = fileio;
+
+	return ret;
+
+err_reqbufs:
+	vb2_reqbufs(q, &fileio->req);
+
+err_kfree:
+	kfree(fileio);
+	return ret;
+}
+
+/**
+ * __vb2_cleanup_fileio() - free resourced used by file io emulator
+ * @q:		videobuf2 queue
+ */
+static int __vb2_cleanup_fileio(struct vb2_queue *q)
+{
+	struct vb2_fileio_data *fileio = q->fileio;
+
+	if (fileio) {
+		/*
+		 * Hack fileio context to enable direct calls to vb2 ioctl
+		 * interface.
+		 */
+		q->fileio = NULL;
+
+		vb2_streamoff(q, q->type);
+		fileio->req.count = 0;
+		vb2_reqbufs(q, &fileio->req);
+		kfree(fileio);
+		dprintk(3, "file io emulator closed\n");
+	}
+	return 0;
+}
+
+/**
+ * __vb2_perform_fileio() - free resourced used by read() emulator
+ * @q:		videobuf2 queue
+ * @data:	pointed to target userspace buffer
+ * @count:	number of bytes to read or write
+ * @ppos:	file handle position tracking pointer
+ * @nonblock:	mode selector (1 means blocking calls, 0 means nonblocking)
+ * @read:	access mode selector (1 means read, 0 means write)
+ */
+static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
+		loff_t *ppos, int nonblock, int read)
+{
+	struct vb2_fileio_data *fileio;
+	struct vb2_fileio_buf *buf;
+	int ret, index;
+
+	dprintk(3, "file io: mode %s, offset %ld, count %d, %sblocking\n",
+		read ? "read" : "write", (long)*ppos, count,
+		nonblock ? "non" : "");
+
+	if (!data)
+		return -EINVAL;
+
+	/*
+	 * Initialize emulator on first call.
+	 */
+	if (!q->fileio) {
+		ret = __vb2_init_fileio(q, read);
+		dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
+		if (ret)
+			return ret;
+	}
+	fileio = q->fileio;
+
+	/*
+	 * Hack fileio context to enable direct calls to vb2 ioctl interface.
+	 * The pointer will be restored before returning from this function.
+	 */
+	q->fileio = NULL;
+
+	index = fileio->index;
+	buf = &fileio->bufs[index];
+
+	/*
+	 * Check if we need to dequeue the buffer.
+	 */
+	if (buf->queued) {
+		struct vb2_buffer *vb;
+
+		/*
+		 * Call vb2_dqbuf to get buffer back.
+		 */
+		memset(&fileio->b, 0, sizeof(fileio->b));
+		fileio->b.type = q->type;
+		fileio->b.memory = q->memory;
+		fileio->b.index = index;
+		ret = vb2_dqbuf(q, &fileio->b, nonblock);
+		dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
+		if (ret)
+			goto end;
+		fileio->dq_count += 1;
+
+		/*
+		 * Get number of bytes filled by the driver
+		 */
+		vb = q->bufs[index];
+		buf->size = vb2_get_plane_payload(vb, 0);
+		buf->queued = 0;
+	}
+
+	/*
+	 * Limit count on last few bytes of the buffer.
+	 */
+	if (buf->pos + count > buf->size) {
+		count = buf->size - buf->pos;
+		dprintk(5, "reducing read count: %d\n", count);
+	}
+
+	/*
+	 * Transfer data to userspace.
+	 */
+	dprintk(3, "file io: copying %d bytes - buffer %d, offset %d\n",
+		count, index, buf->pos);
+	if (read)
+		ret = copy_to_user(data, buf->vaddr + buf->pos, count);
+	else
+		ret = copy_from_user(buf->vaddr + buf->pos, data, count);
+	if (ret) {
+		dprintk(3, "file io: error copying data\n");
+		ret = -EFAULT;
+		goto end;
+	}
+
+	/*
+	 * Update counters.
+	 */
+	buf->pos += count;
+	*ppos += count;
+
+	/*
+	 * Queue next buffer if required.
+	 */
+	if (buf->pos == buf->size ||
+	   (!read && (fileio->flags & VB2_FILEIO_WRITE_IMMEDIATE))) {
+		/*
+		 * Check if this is the last buffer to read.
+		 */
+		if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) &&
+		    fileio->dq_count == 1) {
+			dprintk(3, "file io: read limit reached\n");
+			/*
+			 * Restore fileio pointer and release the context.
+			 */
+			q->fileio = fileio;
+			return __vb2_cleanup_fileio(q);
+		}
+
+		/*
+		 * Call vb2_qbuf and give buffer to the driver.
+		 */
+		memset(&fileio->b, 0, sizeof(fileio->b));
+		fileio->b.type = q->type;
+		fileio->b.memory = q->memory;
+		fileio->b.index = index;
+		fileio->b.bytesused = buf->pos;
+		ret = vb2_qbuf(q, &fileio->b);
+		dprintk(5, "file io: vb2_dbuf result: %d\n", ret);
+		if (ret)
+			goto end;
+
+		/*
+		 * Buffer has been queued, update the status
+		 */
+		buf->pos = 0;
+		buf->queued = 1;
+		buf->size = q->bufs[0]->v4l2_planes[0].length;
+		fileio->q_count += 1;
+
+		/*
+		 * Switch to the next buffer
+		 */
+		fileio->index = (index + 1) % q->num_buffers;
+
+		/*
+		 * Start streaming if required.
+		 */
+		if (!read && !q->streaming) {
+			ret = vb2_streamon(q, q->type);
+			if (ret)
+				goto end;
+		}
+	}
+
+	/*
+	 * Return proper number of bytes processed.
+	 */
+	if (ret == 0)
+		ret = count;
+end:
+	/*
+	 * Restore the fileio context and block vb2 ioctl interface.
+	 */
+	q->fileio = fileio;
+	return ret;
+}
+
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+		loff_t *ppos, int nonblocking)
+{
+	return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
+}
+EXPORT_SYMBOL_GPL(vb2_read);
+
+size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
+		loff_t *ppos, int nonblocking)
+{
+	return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0);
+}
+EXPORT_SYMBOL_GPL(vb2_write);
+
 MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
-MODULE_AUTHOR("Pawel Osciak");
+MODULE_AUTHOR("Pawel Osciak, Marek Szyprowski");
 MODULE_LICENSE("GPL");
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 98bc743..62139f9 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -18,6 +18,7 @@
 #include <linux/poll.h>
 
 struct vb2_alloc_ctx;
+struct vb2_fileio_data;
 
 /**
  * struct vb2_mem_ops - memory handling/memory allocator operations
@@ -95,6 +96,17 @@ struct vb2_plane {
 };
 
 /**
+ * enum vb2_fileio_flags - flags for selecting a mode of the file io emulator,
+ * by default the 'streaming' style is used by the file io emulator
+ * @VB2_FILEIO_READ_ONCE:	report EOF after reading the first buffer
+ * @VB2_FILEIO_WRITE_IMMEDIATE:	queue buffer after each write() call
+ */
+enum vb2_fileio_flags {
+	VB2_FILEIO_READ_ONCE		= (1<<0),
+	VB2_FILEIO_WRITE_IMMEDIATE	= (1<<1),
+};
+
+/**
  * 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
@@ -169,6 +181,12 @@ struct vb2_buffer {
  * @plane_setup:	called before memory allocation num_planes times;
  *			driver should return the required size of plane number
  *			plane_no
+ * @read_setup:		called before enabling read() style io; asks the driver
+ *			for the number of buffers and flags used by the
+ *			emulator; see vb2_fileio_flags for more information
+ * @write_setup:	called before enabling write() style io; asks the driver
+ *			for the number of buffers and flags used by the
+ *			emulator; see vb2_fileio_flags for more information
  * @lock:		aquire all locks taken before any other calls to vb2;
  *			required after sleeing in poll_wait function
  * @unlock:		release any locks taken before calling vb2 function;
@@ -211,6 +229,11 @@ struct vb2_ops {
 	int (*plane_setup)(struct vb2_queue *q,
 			   unsigned int plane_no, unsigned long *plane_size);
 
+	int (*read_setup)(struct vb2_queue *q,
+			  unsigned int *count, unsigned int *flags);
+	int (*write_setup)(struct vb2_queue *q,
+			   unsigned int *count, unsigned int *flags);
+
 	void (*lock)(struct vb2_queue *q);
 	void (*unlock)(struct vb2_queue *q);
 
@@ -245,6 +268,7 @@ struct vb2_ops {
  * @streaming:	current streaming state
  * @userptr_supported: true if queue supports USERPTR types
  * @mmap_supported: true if queue supports MMAP types
+ * @fileio:	file io emulator internal data, used only if emulator is active
  */
 struct vb2_queue {
 	enum v4l2_buf_type		type;
@@ -267,6 +291,8 @@ struct vb2_queue {
 	int				streaming:1;
 	int				userptr_supported:1;
 	int				mmap_supported:1;
+
+	struct vb2_fileio_data		*fileio;
 };
 
 void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
@@ -291,6 +317,10 @@ 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);
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+		loff_t *ppos, int nonblock);
+size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
+		loff_t *ppos, int nonblock);
 
 /**
  * vb2_is_streaming() - return streaming status of the queue
-- 
1.7.1.569.g6f426


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

* [PATCH 6/7] v4l: vivi: port to videobuf2
  2010-11-17  8:39 [PATCH/RFC v4 0/7] Videobuf2 framework Marek Szyprowski
                   ` (4 preceding siblings ...)
  2010-11-17  8:39 ` [PATCH 5/7] v4l: videobuf2: add read() and write() emulator Marek Szyprowski
@ 2010-11-17  8:39 ` Marek Szyprowski
  2010-11-17  8:39 ` [PATCH 7/7] v4l: videobuf2: add CMA allocator Marek Szyprowski
  6 siblings, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-17  8:39 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

Make vivi use videobuf2 in place of videobuf.

Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/video/Kconfig |    2 +-
 drivers/media/video/vivi.c  |  390 ++++++++++++++++++++++++-------------------
 2 files changed, 221 insertions(+), 171 deletions(-)

diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index e7752ee1..9315c0a 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -557,7 +557,7 @@ config VIDEO_VIVI
 	depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
 	depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
 	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 9797e5a..baaacf3 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 & Marek Szyprowski
+ *      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
@@ -23,10 +26,8 @@
 #include <linux/mutex.h>
 #include <linux/videodev2.h>
 #include <linux/kthread.h>
-#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 +43,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 +71,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)
 
@@ -133,16 +137,11 @@ static struct vivi_fmt *get_format(struct v4l2_format *f)
 	return &formats[k];
 }
 
-struct sg_to_addr {
-	int pos;
-	struct scatterlist *sg;
-};
-
 /* buffer for one video frame */
 struct vivi_buffer {
 	/* common v4l buffer stuff -- must be first */
-	struct videobuf_buffer vb;
-
+	struct vb2_buffer	vb;
+	struct list_head	list;
 	struct vivi_fmt        *fmt;
 };
 
@@ -190,9 +189,11 @@ 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;
+	unsigned int		   open_count;
 	u8 			   bars[9][3];
 	u8 			   line[MAX_WIDTH * 4];
 };
@@ -443,10 +444,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 wmax = dev->width;
+	int hmax = 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 +484,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 +505,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, list);
+	list_del(&buf->list);
 
-	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);
 }
@@ -571,17 +570,12 @@ static int vivi_thread(void *data)
 	return 0;
 }
 
-static void vivi_start_generating(struct file *file)
+static int vivi_start_generating(struct vivi_dev *dev)
 {
-	struct vivi_dev *dev = video_drvdata(file);
 	struct vivi_dmaqueue *dma_q = &dev->vidq;
 
 	dprintk(dev, 1, "%s\n", __func__);
 
-	if (test_and_set_bit(0, &dev->generating))
-		return;
-	file->private_data = dev;
-
 	/* Resets frame counters */
 	dev->ms = 0;
 	dev->mv_count = 0;
@@ -593,146 +587,217 @@ static void vivi_start_generating(struct file *file)
 
 	if (IS_ERR(dma_q->kthread)) {
 		v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
-		clear_bit(0, &dev->generating);
-		return;
+		return PTR_ERR(dma_q->kthread);
 	}
 	/* Wakes thread */
 	wake_up_interruptible(&dma_q->wq);
 
 	dprintk(dev, 1, "returning from %s\n", __func__);
+	return 0;
 }
 
-static void vivi_stop_generating(struct file *file)
+static void vivi_stop_generating(struct vivi_dev *dev)
 {
-	struct vivi_dev *dev = video_drvdata(file);
 	struct vivi_dmaqueue *dma_q = &dev->vidq;
 
 	dprintk(dev, 1, "%s\n", __func__);
 
-	if (!file->private_data)
-		return;
-	if (!test_and_clear_bit(0, &dev->generating))
-		return;
-
 	/* shutdown control thread */
 	if (dma_q->kthread) {
 		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)
-{
-	return test_bit(0, &dev->generating);
+	/*
+	 * Typical driver might need to wait here until dma engine stops.
+	 * In this case we can abort imiedetly, so it's just a noop.
+	 */
+
+	/* Release all active buffers */
+	while (!list_empty(&dma_q->active)) {
+		struct vivi_buffer *buf;
+		buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
+		list_del(&buf->list);
+		vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+		dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
+	}
 }
-
 /* ------------------------------------------------------------------
 	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)--;
+
+	*num_planes = 1;
+
+	dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
+		*num_buffers, size);
 
-	*size = dev->width * dev->height * 2;
+	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 struct vb2_buffer *buffer_alloc(struct vb2_queue *q)
+{
+	struct vivi_buffer *buf = kzalloc(sizeof(struct vivi_buffer), GFP_KERNEL);
+	if (buf)
+		return &buf->vb;
+	return NULL;
+}
+
+static void buffer_free(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+	struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
+	kfree(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);
 
-	dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state);
+	BUG_ON(NULL == dev->fmt);
+
+	/*
+	 * 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;
+	unsigned long flags = 0;
 
 	dprintk(dev, 1, "%s\n", __func__);
 
-	buf->vb.state = VIDEOBUF_QUEUED;
-	list_add_tail(&buf->vb.queue, &vidq->active);
+	spin_lock_irqsave(&dev->slock, flags);
+	list_add_tail(&buf->list, &vidq->active);
+	spin_unlock_irqrestore(&dev->slock, flags);
 }
 
-static void buffer_release(struct videobuf_queue *vq,
-			   struct videobuf_buffer *vb)
+static int start_streaming(struct vb2_queue *vq)
 {
-	struct vivi_dev *dev = vq->priv_data;
-	struct vivi_buffer *buf  = container_of(vb, struct vivi_buffer, vb);
+	struct vivi_dev *dev = vb2_get_drv_priv(vq);
+	dprintk(dev, 1, "%s\n", __func__);
+	return vivi_start_generating(dev);
+}
 
+/* abort streaming and wait for last buffer */
+static int stop_streaming(struct vb2_queue *vq)
+{
+	struct vivi_dev *dev = vb2_get_drv_priv(vq);
 	dprintk(dev, 1, "%s\n", __func__);
+	vivi_stop_generating(dev);
+	return 0;
+}
+
+static int read_setup(struct vb2_queue *vq, unsigned int *count,
+		       unsigned int *flags)
+{
+	*count = 2;
+	return 0;
+}
 
-	free_buffer(vq, buf);
+static void vivi_lock(struct vb2_queue *vq)
+{
+	struct vivi_dev *dev = vb2_get_drv_priv(vq);
+	mutex_lock(&dev->mutex);
 }
 
-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 void vivi_unlock(struct vb2_queue *vq)
+{
+	struct vivi_dev *dev = vb2_get_drv_priv(vq);
+	mutex_unlock(&dev->mutex);
+}
+	
+
+static struct vb2_ops vivi_video_qops = {
+	.queue_negotiate	= queue_negotiate,
+	.plane_setup		= plane_setup,
+	.buf_alloc		= buffer_alloc,
+	.buf_free		= buffer_free,
+	.buf_init		= buffer_init,
+	.buf_prepare		= buffer_prepare,
+	.buf_queue		= buffer_queue,
+	.start_streaming	= start_streaming,
+	.stop_streaming		= stop_streaming,
+	.read_setup		= read_setup,
+	.lock			= vivi_lock,
+	.unlock			= vivi_unlock,
 };
 
 /* ------------------------------------------------------------------
@@ -774,7 +839,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,91 +885,60 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 					struct v4l2_format *f)
 {
 	struct vivi_dev *dev = video_drvdata(file);
+	struct vb2_queue *q = &dev->vb_vidq;
 
 	int ret = vidioc_try_fmt_vid_cap(file, priv, f);
 	if (ret < 0)
 		return ret;
 
-	if (vivi_is_generating(dev)) {
+	if (vb2_is_streaming(q)) {
 		dprintk(dev, 1, "%s device busy\n", __func__);
-		ret = -EBUSY;
-		goto out;
+		return -EBUSY;
 	}
 
 	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;
-	ret = 0;
-out:
-	return ret;
+	dev->field = f->fmt.pix.field;
+
+	return 0;
 }
 
 static int vidioc_reqbufs(struct file *file, void *priv,
 			  struct v4l2_requestbuffers *p)
 {
 	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);
-	if (ret)
-		return ret;
-
-	vivi_start_generating(file);
-	return 0;
+	return vb2_streamon(&dev->vb_vidq, i);
 }
 
 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);
-	if (!ret)
-		vivi_stop_generating(file);
-	return ret;
+	return vb2_streamoff(&dev->vb_vidq, i);
 }
 
 static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
@@ -1027,26 +1061,33 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
 	File operations for the device
    ------------------------------------------------------------------*/
 
+static int vivi_open(struct file *file)
+{
+	struct vivi_dev *dev = video_drvdata(file);
+
+	dprintk(dev, 1, "%s, %p\n", __func__, file);
+	dev->open_count++;
+	return 0;
+}
+
 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);
+	dprintk(dev, 1, "read called\n");
+	return vb2_read(&dev->vb_vidq, data, count, ppos,
+		       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)
@@ -1054,10 +1095,11 @@ static int vivi_close(struct file *file)
 	struct video_device  *vdev = video_devdata(file);
 	struct vivi_dev *dev = video_drvdata(file);
 
-	vivi_stop_generating(file);
+	dprintk(dev, 1, "close called (dev=%s), file %p\n",
+		video_device_node_name(vdev), file);
 
-	dprintk(dev, 1, "close called (dev=%s)\n",
-		video_device_node_name(vdev));
+	if (--dev->open_count == 0)
+		vb2_queue_release(&dev->vb_vidq);
 	return 0;
 }
 
@@ -1068,8 +1110,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,
 		(unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
@@ -1079,6 +1120,7 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
 
 static const struct v4l2_file_operations vivi_fops = {
 	.owner		= THIS_MODULE,
+	.open		= vivi_open,
 	.release        = vivi_close,
 	.read           = vivi_read,
 	.poll		= vivi_poll,
@@ -1105,9 +1147,6 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
 	.vidioc_queryctrl     = vidioc_queryctrl,
 	.vidioc_g_ctrl        = vidioc_g_ctrl,
 	.vidioc_s_ctrl        = vidioc_s_ctrl,
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-	.vidiocgmbuf          = vidiocgmbuf,
-#endif
 };
 
 static struct video_device vivi_template = {
@@ -1141,6 +1180,8 @@ static int vivi_release(void)
 		kfree(dev);
 	}
 
+	vb2_vmalloc_cleanup(alloc_ctx);
+
 	return 0;
 }
 
@@ -1171,13 +1212,10 @@ static int __init vivi_create_instance(int inst)
 
 	/* initialize locks */
 	spin_lock_init(&dev->slock);
+	vb2_queue_init(&dev->vb_vidq, &vivi_video_qops, alloc_ctx,
+		       V4L2_BUF_TYPE_VIDEO_CAPTURE, dev);
 	mutex_init(&dev->mutex);
 
-	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, &dev->mutex);
-
 	/* init video dma queues */
 	INIT_LIST_HEAD(&dev->vidq.active);
 	init_waitqueue_head(&dev->vidq.wq);
@@ -1190,6 +1228,11 @@ static int __init vivi_create_instance(int inst)
 	*vfd = vivi_template;
 	vfd->debug = debug;
 	vfd->v4l2_dev = &dev->v4l2_dev;
+
+	/*
+	 * Provide a mutex to v4l2 core. It will be used to protect
+	 * all fops and v4l2 ioctls.
+	 */
 	vfd->lock = &dev->mutex;
 
 	ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
@@ -1235,6 +1278,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.1.569.g6f426


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

* [PATCH 7/7] v4l: videobuf2: add CMA allocator
  2010-11-17  8:39 [PATCH/RFC v4 0/7] Videobuf2 framework Marek Szyprowski
                   ` (5 preceding siblings ...)
  2010-11-17  8:39 ` [PATCH 6/7] v4l: vivi: port to videobuf2 Marek Szyprowski
@ 2010-11-17  8:39 ` Marek Szyprowski
  6 siblings, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-17  8:39 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

From: 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>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/video/Kconfig         |    5 +
 drivers/media/video/Makefile        |    1 +
 drivers/media/video/videobuf2-cma.c |  250 +++++++++++++++++++++++++++++++++++
 include/media/videobuf2-cma.h       |   31 +++++
 4 files changed, 287 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 9315c0a..eec6c3f 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -65,6 +65,11 @@ config VIDEOBUF2_VMALLOC
 	select VIDEOBUF2_MEMOPS
 	tristate
 
+config VIDEOBUF2_CMA
+	depends on CMA
+	select VIDEOBUF2_CORE
+	select VIDEOBUF2_MEMOPS
+	tristate
 #
 # Multimedia Video device configuration
 #
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index baa74e7..6f917ee 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_VIDEOBUF2_CORE)		+= videobuf2-core.o
 obj-$(CONFIG_VIDEOBUF2_MEMOPS)		+= videobuf2-memops.o
 obj-$(CONFIG_VIDEOBUF2_VMALLOC)		+= videobuf2-vmalloc.o
 obj-$(CONFIG_VIDEOBUF2_DMA_COHERENT)	+= videobuf2-dma_coherent.o
+obj-$(CONFIG_VIDEOBUF2_CMA)		+= videobuf2-cma.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..157d7ac
--- /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 (IS_ERR((void *)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_cookie(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,
+	.cookie		= vb2_cma_cookie,
+	.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..de64615
--- /dev/null
+++ b/include/media/videobuf2-cma.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+static inline unsigned long vb2_cma_plane_paddr(struct vb2_buffer *vb,
+						unsigned int plane_no)
+{
+	return (unsigned long)vb2_plane_cookie(vb, plane_no);
+}
+
+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.1.569.g6f426


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

* RE: [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator
  2010-11-17  8:39 ` [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator Marek Szyprowski
@ 2010-11-18  7:14   ` Sewoon Park
  2010-11-18 15:19     ` Marek Szyprowski
  0 siblings, 1 reply; 20+ messages in thread
From: Sewoon Park @ 2010-11-18  7:14 UTC (permalink / raw)
  To: 'Marek Szyprowski', linux-media; +Cc: pawel, kyungmin.park

Hi Marek~ 

Marek Szyprowski wrote:
> Sent: Wednesday, November 17, 2010 5:40 PM
> To: linux-media@vger.kernel.org
> Cc: m.szyprowski@samsung.com; pawel@osciak.com; kyungmin.park@samsung.com
> Subject: [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator
> 
> From: Pawel Osciak <p.osciak@samsung.com>
> 
> 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>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> CC: Pawel Osciak <pawel@osciak.com>
> ---
>  drivers/media/video/Kconfig                  |    5 +
>  drivers/media/video/Makefile                 |    1 +
>  drivers/media/video/videobuf2-dma-coherent.c |  208
> ++++++++++++++++++++++++++
>  include/media/videobuf2-dma-coherent.h       |   27 ++++
>  4 files changed, 241 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 9351423..e7752ee1 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_MEMOPS
> +	tristate
> +
>  config VIDEOBUF2_VMALLOC
>  	select VIDEOBUF2_CORE
>  	select VIDEOBUF2_MEMOPS
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index 538bee9..baa74e7 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -117,6 +117,7 @@ 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
> +obj-$(CONFIG_VIDEOBUF2_DMA_COHERENT)	+= videobuf2-dma_coherent.o
> 
>  obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.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..761f366
> --- /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;
> +};

(snip)

> +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,

The "paddr" is not exist in vb2_mem_ops after [PATCH v4 xxx] lists.
I think you should fix from paddr to cookie like CMA allocator.

> +	.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,
> +};
> +

(snip)

> --
> 1.7.1.569.g6f426
> 
> --
> 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] 20+ messages in thread

* Re: [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-11-17  8:39 ` [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Marek Szyprowski
@ 2010-11-18  9:17   ` Hans Verkuil
  2010-11-18 12:26     ` Andy Walls
                       ` (2 more replies)
  0 siblings, 3 replies; 20+ messages in thread
From: Hans Verkuil @ 2010-11-18  9:17 UTC (permalink / raw)
  To: Marek Szyprowski; +Cc: linux-media, pawel, kyungmin.park

Hi Marek!

Some comments below...

On Wednesday, November 17, 2010 09:39:28 Marek Szyprowski wrote:
> From: Pawel Osciak <p.osciak@samsung.com>
> 
> 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: Marek Szyprowski <m.szyprowski@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> CC: Pawel Osciak <pawel@osciak.com>
> ---
>  drivers/media/video/Kconfig          |    3 +
>  drivers/media/video/Makefile         |    2 +
>  drivers/media/video/videobuf2-core.c | 1434 ++++++++++++++++++++++++++++++++++
>  include/media/videobuf2-core.h       |  373 +++++++++
>  4 files changed, 1812 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 ac16e81..fef6a14 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -49,6 +49,9 @@ 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 af79d47..77c4f85 100644
> --- a/drivers/media/video/Makefile
> +++ b/drivers/media/video/Makefile
> @@ -114,6 +114,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..5c9d3d8
> --- /dev/null
> +++ b/drivers/media/video/videobuf2-core.c
> @@ -0,0 +1,1434 @@
> +/*
> + * videobuf2-core.c - V4L2 driver helper framework
> + *
> + * Copyright (C) 2010 Samsung Electronics
> + *
> + * Author: Pawel Osciak <p.osciak@samsung.com>
> + *	   Marek Szyprowski <m.szyprowski@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)

Why not use mem_ops in the call_memop macro? That would simplify it a bit.

> +
> +#define call_qop(q, op, args...)					\
> +	(((q)->ops->op) ? ((q)->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_buf_alloc() - allocate a singe vb2_buffer structure
> + *
> + * Returns the pointer to the allocated structure.
> + */
> +void *__vb2_buf_alloc(struct vb2_queue *q)
> +{
> +	struct vb2_buffer *vb;
> +	if (q->ops->buf_alloc)
> +		vb = q->ops->buf_alloc(q);
> +	else
> +		vb = kzalloc(sizeof(struct vb2_buffer), GFP_KERNEL);
> +	return vb;
> +}
> +
> +/**
> + * __vb2_buf_free() - free a singe vb2_buffer structure
> + *
> + * Frees memory used by the vb2_buffer structure.
> + */
> +void __vb2_buf_free(struct vb2_queue *q, struct vb2_buffer *vb)
> +{
> +	if (q->ops->buf_free)
> +		q->ops->buf_free(q, vb);
> +	else
> +		kfree(vb);
> +}
> +
> +/**
> + * __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 = call_qop(q, 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 = __vb2_buf_alloc(q);
> +		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);
> +				__vb2_buf_free(q, vb);
> +				break;
> +			}
> +			/*
> +			 * Call the driver-provided buffer initialization
> +			 * callback, if given. An error in initialization
> +			 * results in queue setup failure.
> +			 */
> +			ret = call_qop(q, buf_init, vb);
> +			if (ret) {
> +				dprintk(1, "Buffer %d %p initialization"
> +					" failed\n", buffer, vb);
> +				__vb2_buf_mem_free(vb);
> +				__vb2_buf_free(q, 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;
> +		__vb2_buf_free(q, 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;
> +		b->bytesused = vb->v4l2_planes[0].bytesused;
> +		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;
> +
> +	if (b->type != q->type) {
> +		dprintk(1, "querybuf: wrong buffer type\n");
> +		goto done;

Just to return -EINVAL;

> +	}
> +
> +	if (b->index >= q->num_buffers) {
> +		dprintk(1, "querybuf: buffer index out of range\n");
> +		goto done;

ditto

> +	}
> +	vb = q->bufs[b->index];
> +	if (NULL == vb) {
> +		/* Should never happen */
> +		dprintk(1, "querybuf: no such buffer\n");
> +		goto done;

ditto

> +	}
> +
> +	ret = __fill_v4l2_buffer(vb, b);

Just do return __file_v4l2_buffer(vb, b);

> +done:
> +	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;
> +	}
> +
> +	if (req->type != q->type) {
> +		dprintk(1, "reqbufs: queue type invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	if (q->streaming) {
> +		dprintk(1, "reqbufs: streaming active\n");
> +		return -EBUSY;
> +	}
> +
> +	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);

OK, I have a problem here. How do I detect as an application whether a driver
supports MMAP and/or USERPTR?

What I am using in qv4l2 (and it doesn't work properly with videobuf) is that
I call REQBUFS with count == 0 for MMAP and for USERPTR and I check whether it
returns 0 or -EINVAL.

It seems a reasonable test since it doesn't allocate anything. It would be nice
if vb2 can check for the memory field here based on what the driver supports.

Ideally we should make this an official part of the spec (or have some other
mechanism to find out what it supported).

> +		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:
> +	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_cookie() - Return allocator specific cookie for the 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.

This comment should be extended since it now returns a cookie instead of a
physical address. Some explanation would be useful here.

> + */
> +void *vb2_plane_cookie(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, cookie, vb->planes[plane_no].mem_priv);
> +}
> +EXPORT_SYMBOL_GPL(vb2_plane_cookie);
> +
> +/**
> + * 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(&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.
> +	 */
> +	ret = call_qop(q, 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;
> +	vb->state = VB2_BUF_STATE_ACTIVE;
> +	q->ops->buf_queue(vb);
> +}
> +
> +/**
> + * 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 = 0;
> +
> +
> +	if (b->type != q->type) {
> +		dprintk(1, "qbuf: invalid buffer type\n");
> +		return -EINVAL;
> +	}
> +
> +	if (b->index >= q->num_buffers) {
> +		dprintk(1, "qbuf: buffer index out of range\n");
> +		return -EINVAL;
> +	}
> +
> +	vb = q->bufs[b->index];
> +	if (NULL == vb) {
> +		/* Should never happen */
> +		dprintk(1, "qbuf: buffer is NULL\n");
> +		return -EINVAL;
> +	}
> +
> +	if (b->memory != q->memory) {
> +		dprintk(1, "qbuf: invalid memory type\n");
> +		return -EINVAL;
> +	}
> +
> +	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> +		dprintk(1, "qbuf: buffer already in use\n");
> +		return -EINVAL;
> +	}
> +
> +	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;
> +
> +	ret = call_qop(q, 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:
> +	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)
> +{
> +checks:
> +	if (!q->streaming) {
> +		dprintk(1, "Streaming off, will not wait for buffers\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Buffers may be added to vb_done_list without holding the driver's
> +	 * lock, but removal is performed only while holding both driver's
> +	 * lock and the vb_done_lock spinlock. Thus we can be sure that as
> +	 * long as we hold lock, the list will remain not empty if this
> +	 * check succeeds.
> +	 */
> +	if (list_empty(&q->done_list)) {
> +		int retval;
> +		if (nonblocking) {
> +			dprintk(1, "Nonblocking and no buffers to dequeue, "
> +					"will not wait\n");
> +			return -EAGAIN;
> +		}
> +
> +		/*
> +		 * We are streaming and nonblocking, wait for another buffer to
> +		 * become ready or for streamoff. Driver's lock is released to
> +		 * allow streamoff or qbuf to be called while waiting.
> +		 */
> +		call_qop(q, unlock, q);
> +
> +		/*
> +		 * 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);
> +		call_qop(q, lock, q);
> +
> +		if (retval)
> +			return -EINTR;
> +
> +		goto checks;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * __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;
> +
> +	/*
> +	 * Drivers 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;
> +
> +
> +	if (b->type != q->type) {
> +		dprintk(1, "dqbuf: invalid buffer type\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = __vb2_get_done_vb(q, &vb, nonblocking);
> +	if (ret < 0) {
> +		dprintk(1, "dqbuf: error getting next done buffer\n");
> +		goto done;
> +	}
> +
> +	ret = call_qop(q, 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:
> +	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;
> +
> +
> +	if (type != q->type) {
> +		dprintk(1, "streamon: invalid stream type\n");
> +		return -EINVAL;
> +	}
> +
> +	if (q->streaming) {
> +		dprintk(1, "streamon: already streaming\n");
> +		return -EBUSY;
> +	}
> +
> +	/*
> +	 * 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;
> +
> +	/*
> +	 * Let driver notice that streaming state has been enabled.
> +	 */
> +	call_qop(q, start_streaming, q);
> +
> +	/*
> +	 * 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:
> +	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)
> +{
> +	/*
> +	 * Tell driver to stop all dma transactions and release all queued
> +	 * buffers
> +	 */
> +
> +	if (q->streaming)
> +		call_qop(q, stop_streaming, q);
> +	q->streaming = 0;
> +
> +	/*
> +	 * 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_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;
> +
> +
> +	if (type != q->type) {
> +		dprintk(1, "streamoff: invalid stream type\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!q->streaming) {
> +		dprintk(1, "streamoff: not streaming\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * 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");
> +	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 (V4L2_TYPE_IS_OUTPUT(q->type) &&
> +	   (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))) {
> +		dprintk(1, "Invalid vma flags (need VM_WRITE | VM_SHARED)\n");
> +		return ret;
> +	} else if (!(vma->vm_flags & VM_READ) || !(vma->vm_flags & VM_SHARED)) {
> +		dprintk(1, "Invalid vma flags (need VM_READ | VM_SHARED)\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * 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:
> +	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;
> +
> +
> +	/*
> +	 * There is nothing to wait for if no buffers have already been queued.
> +	 */
> +	if (list_empty(&q->queued_list)) {
> +		ret = POLLERR;
> +		goto end;

Just do return POLLERR;

> +	}
> +
> +	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;

Just do return 0;

> +
> +	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;

Again just return.

> +	}
> +end:
> +	return ret;

return 0;

> +}
> +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
> + * @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,
> +			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->type = type;
> +	q->drv_priv = drv_priv;
> +
> +	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)
> +{
> +	__vb2_queue_cancel(q);
> +	__vb2_queue_free(q);
> +}
> +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..98bc743
> --- /dev/null
> +++ b/include/media/videobuf2-core.h
> @@ -0,0 +1,373 @@
> +/*
> + * 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>
> +
> +struct vb2_alloc_ctx;
> +
> +/**
> + * 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

'get' and 'put' imply reference counting. However, I don't believe that's the
case for USERPTR. If I am right, then I would like to see different names for
these callbacks.

> + * @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

Outdated comment, replaced by cookie.

> + * @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

Mention that mmap increases the refcount for the memory (it's effectively
a 'get', the counterpart of the 'put' callback).

Perhaps mmap should be renamed get_mmap and put renamed to put_mmap?

> + *
> + * 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);
> +	void		*(*cookie)(void *buf_priv);
> +
> +	unsigned int	(*num_users)(void *buf_priv);
> +
> +	int		(*mmap)(void *buf_priv, struct vm_area_struct *vma);
> +};
> +
> +/**
> + * 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_plane {
> +	void			*mem_priv;
> +	int			mapped:1;
> +};
> +
> +/**
> + * 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_queue;
> +
> +/**
> + * 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;
> +
> +	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
> + * @lock:		aquire all locks taken before any other calls to vb2;

typo: acquire. Actually, I'd probably say 'reacquire'.

> + *			required after sleeing in poll_wait function

typo: sleeing -> sleeping

> + * @unlock:		release any locks taken before calling vb2 function;
> + *			required for a safe to call poll_wait function

I would swap the unlock and lock comments since unlock will be called before
lock. Some more background info might help as well.

> + * @buf_alloc:		called to allocate a struct vb2_buffer; driver usually
> + *			embeds it in its own custom buffer structure; returns
> + *			a pointer to vb2_buffer or NULL if failed; if not
> + *			provided kmalloc(sizeof(struct vb2_buffer, GFP_KERNEL)
> + *			is used
> + * @buf_free:		called to free the structure allocated by @buf_alloc;
> + *			if not provided kfree(vb) is used
> + * @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
> + * @start_streaming:	called once before entering 'streaming' state; enables
> + *			driver to recieve buffers over buf_queue() callback
> + * @stop_streaming:	called when 'streaming' state must be disabled; driver
> + *			should stop any dma transactions or wait until they
> + *			finish and give back all buffers it got from buf_queue()
> + *			callback
> + * @buf_queue:		passes buffer vb to the driver; driver may start
> + *			hardware operation on this buffer; driver should give
> + *			the buffer back by calling vb2_buffer_done() function
> + */
> +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 (*lock)(struct vb2_queue *q);
> +	void (*unlock)(struct vb2_queue *q);
> +
> +	struct vb2_buffer *(*buf_alloc)(struct vb2_queue *q);
> +	void (*buf_free)(struct vb2_queue *q, 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);
> +
> +	int (*start_streaming)(struct vb2_queue *q);
> +	int (*stop_streaming)(struct vb2_queue *q);
> +
> +	void (*buf_queue)(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
> + * @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 list_head		queued_list;
> +
> +	struct list_head		done_list;
> +	spinlock_t			done_lock;
> +	wait_queue_head_t		done_wq;
> +
> +	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;
> +};
> +
> +void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
> +void *vb2_plane_cookie(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,
> +			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_is_streaming() - return streaming status of the queue
> + * @q:		videobuf queue
> + */
> +static inline bool vb2_is_streaming(struct vb2_queue *q)
> +{
> +	return q->streaming;
> +}
> +
> +/**
> + * 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)
> +{
> +	vb->v4l2_planes[plane_no].bytesused = size;
> +}
> +
> +/**
> + * vb2_get_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 unsigned long vb2_get_plane_payload(struct vb2_buffer *vb,
> +				 unsigned int plane_no)
> +{
> +	return vb->v4l2_planes[plane_no].bytesused;
> +}
> +
> +/**
> + * 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

else is not needed.

> +		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;
> +}
> +
> +#endif /* _MEDIA_VIDEOBUF2_CORE_H */
> 

Great job! Thanks for all the hard work!

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by Cisco

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

* Re: [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-11-18  9:17   ` Hans Verkuil
@ 2010-11-18 12:26     ` Andy Walls
  2010-11-18 12:46       ` Hans Verkuil
  2010-11-18 16:52       ` Marek Szyprowski
  2010-11-18 15:51     ` Pawel Osciak
  2010-11-18 16:26     ` Marek Szyprowski
  2 siblings, 2 replies; 20+ messages in thread
From: Andy Walls @ 2010-11-18 12:26 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Marek Szyprowski, linux-media, pawel, kyungmin.park

Hi Hans and Marek,

Some meta-comments below... ;)

On Thu, 2010-11-18 at 10:17 +0100, Hans Verkuil wrote:
> Hi Marek!
> 
> Some comments below...
> 
> On Wednesday, November 17, 2010 09:39:28 Marek Szyprowski wrote:
> > From: Pawel Osciak <p.osciak@samsung.com>
> > 
> > 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: Marek Szyprowski <m.szyprowski@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > CC: Pawel Osciak <pawel@osciak.com>
> > ---
> >  drivers/media/video/Kconfig          |    3 +
> >  drivers/media/video/Makefile         |    2 +
> >  drivers/media/video/videobuf2-core.c | 1434 ++++++++++++++++++++++++++++++++++
> >  include/media/videobuf2-core.h       |  373 +++++++++
> >  4 files changed, 1812 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 ac16e81..fef6a14 100644
> > --- a/drivers/media/video/Kconfig
> > +++ b/drivers/media/video/Kconfig
> > @@ -49,6 +49,9 @@ 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 af79d47..77c4f85 100644
> > --- a/drivers/media/video/Makefile
> > +++ b/drivers/media/video/Makefile
> > @@ -114,6 +114,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..5c9d3d8
> > --- /dev/null
> > +++ b/drivers/media/video/videobuf2-core.c
> > @@ -0,0 +1,1434 @@
> > +/*
> > + * videobuf2-core.c - V4L2 driver helper framework
> > + *
> > + * Copyright (C) 2010 Samsung Electronics
> > + *
> > + * Author: Pawel Osciak <p.osciak@samsung.com>
> > + *	   Marek Szyprowski <m.szyprowski@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)
> 
> Why not use mem_ops in the call_memop macro? That would simplify it a bit.

I think you meant

"Why not use the the mem_ops macro in the call_memop macro?" 

Asking the user to pass in a mem_ops pointer as an argument to the
call_memop() macro isn't a simplification, but that's how I first read
your comment.

> > +
> > +#define call_qop(q, op, args...)					\
> > +	(((q)->ops->op) ? ((q)->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_buf_alloc() - allocate a singe vb2_buffer structure
> > + *
> > + * Returns the pointer to the allocated structure.
> > + */
> > +void *__vb2_buf_alloc(struct vb2_queue *q)
> > +{
> > +	struct vb2_buffer *vb;
> > +	if (q->ops->buf_alloc)
> > +		vb = q->ops->buf_alloc(q);
> > +	else
> > +		vb = kzalloc(sizeof(struct vb2_buffer), GFP_KERNEL);
> > +	return vb;
> > +}
> > +
> > +/**
> > + * __vb2_buf_free() - free a singe vb2_buffer structure
> > + *
> > + * Frees memory used by the vb2_buffer structure.
> > + */
> > +void __vb2_buf_free(struct vb2_queue *q, struct vb2_buffer *vb)
> > +{
> > +	if (q->ops->buf_free)
> > +		q->ops->buf_free(q, vb);
> > +	else
> > +		kfree(vb);
> > +}
> > +
> > +/**
> > + * __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 = call_qop(q, 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 = __vb2_buf_alloc(q);
> > +		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);
> > +				__vb2_buf_free(q, vb);
> > +				break;
> > +			}
> > +			/*
> > +			 * Call the driver-provided buffer initialization
> > +			 * callback, if given. An error in initialization
> > +			 * results in queue setup failure.
> > +			 */
> > +			ret = call_qop(q, buf_init, vb);
> > +			if (ret) {
> > +				dprintk(1, "Buffer %d %p initialization"
> > +					" failed\n", buffer, vb);
> > +				__vb2_buf_mem_free(vb);
> > +				__vb2_buf_free(q, 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;
> > +		__vb2_buf_free(q, 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;
> > +		b->bytesused = vb->v4l2_planes[0].bytesused;
> > +		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;
> > +
> > +	if (b->type != q->type) {
> > +		dprintk(1, "querybuf: wrong buffer type\n");
> > +		goto done;
> 
> Just to return -EINVAL;
> 
> > +	}
> > +
> > +	if (b->index >= q->num_buffers) {
> > +		dprintk(1, "querybuf: buffer index out of range\n");
> > +		goto done;
> 
> ditto
> 
> > +	}
> > +	vb = q->bufs[b->index];
> > +	if (NULL == vb) {
> > +		/* Should never happen */
> > +		dprintk(1, "querybuf: no such buffer\n");
> > +		goto done;
> 
> ditto
> 
> > +	}
> > +
> > +	ret = __fill_v4l2_buffer(vb, b);
> 
> Just do return __file_v4l2_buffer(vb, b);
> 
> > +done:
> > +	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;
> > +	}
> > +
> > +	if (req->type != q->type) {
> > +		dprintk(1, "reqbufs: queue type invalid\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (q->streaming) {
> > +		dprintk(1, "reqbufs: streaming active\n");
> > +		return -EBUSY;
> > +	}
> > +
> > +	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);
> 
> OK, I have a problem here. How do I detect as an application whether a driver
> supports MMAP and/or USERPTR?
> 
> What I am using in qv4l2 (and it doesn't work properly with videobuf) is that
> I call REQBUFS with count == 0 for MMAP and for USERPTR and I check whether it
> returns 0 or -EINVAL.


> It seems a reasonable test since it doesn't allocate anything. It would be nice
> if vb2 can check for the memory field here based on what the driver supports.
> 
> Ideally we should make this an official part of the spec (or have some other
> mechanism to find out what it supported).

Well it seems to be documented, just not clearly:

First paragraph here:
http://linuxtv.org/downloads/v4l-dvb-apis/mmap.html

First & second Paragraph here:
http://linuxtv.org/downloads/v4l-dvb-apis/userp.html
(which mentions not allocating things.)

And in the description here:
http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-reqbufs.html


I suppose  adding flags to VIDIOC_QUERYCAP results would remove all the
hokey algorithmic probing of what the driver should already know.
 http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-querycap.html

V4L2_CAP_STREAMING            0x04000000
V4L2_CAP_STREAMING_MMAP       0x08000000 
V4L2_CAP_STREAMING_USER_PTR   0x10000000

> > +		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:
> > +	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_cookie() - Return allocator specific cookie for the 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.
> 
> This comment should be extended since it now returns a cookie instead of a
> physical address. Some explanation would be useful here.
> 
> > + */
> > +void *vb2_plane_cookie(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, cookie, vb->planes[plane_no].mem_priv);
> > +}
> > +EXPORT_SYMBOL_GPL(vb2_plane_cookie);
> > +
> > +/**
> > + * 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(&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.
> > +	 */
> > +	ret = call_qop(q, 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;
> > +	vb->state = VB2_BUF_STATE_ACTIVE;
> > +	q->ops->buf_queue(vb);
> > +}
> > +
> > +/**
> > + * 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 = 0;
> > +
> > +
> > +	if (b->type != q->type) {
> > +		dprintk(1, "qbuf: invalid buffer type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (b->index >= q->num_buffers) {
> > +		dprintk(1, "qbuf: buffer index out of range\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	vb = q->bufs[b->index];
> > +	if (NULL == vb) {
> > +		/* Should never happen */
> > +		dprintk(1, "qbuf: buffer is NULL\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (b->memory != q->memory) {
> > +		dprintk(1, "qbuf: invalid memory type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> > +		dprintk(1, "qbuf: buffer already in use\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	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;
> > +
> > +	ret = call_qop(q, 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:
> > +	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)
> > +{
> > +checks:
> > +	if (!q->streaming) {
> > +		dprintk(1, "Streaming off, will not wait for buffers\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * Buffers may be added to vb_done_list without holding the driver's
> > +	 * lock, but removal is performed only while holding both driver's
> > +	 * lock and the vb_done_lock spinlock. Thus we can be sure that as
> > +	 * long as we hold lock, the list will remain not empty if this
> > +	 * check succeeds.
> > +	 */
> > +	if (list_empty(&q->done_list)) {
> > +		int retval;
> > +		if (nonblocking) {
> > +			dprintk(1, "Nonblocking and no buffers to dequeue, "
> > +					"will not wait\n");
> > +			return -EAGAIN;
> > +		}
> > +
> > +		/*
> > +		 * We are streaming and nonblocking, wait for another buffer to
> > +		 * become ready or for streamoff. Driver's lock is released to
> > +		 * allow streamoff or qbuf to be called while waiting.
> > +		 */
> > +		call_qop(q, unlock, q);
> > +
> > +		/*
> > +		 * 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);
> > +		call_qop(q, lock, q);
> > +
> > +		if (retval)
> > +			return -EINTR;
> > +
> > +		goto checks;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/**
> > + * __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;
> > +
> > +	/*
> > +	 * Drivers 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;
> > +
> > +
> > +	if (b->type != q->type) {
> > +		dprintk(1, "dqbuf: invalid buffer type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = __vb2_get_done_vb(q, &vb, nonblocking);
> > +	if (ret < 0) {
> > +		dprintk(1, "dqbuf: error getting next done buffer\n");
> > +		goto done;
> > +	}
> > +
> > +	ret = call_qop(q, 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:
> > +	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;
> > +
> > +
> > +	if (type != q->type) {
> > +		dprintk(1, "streamon: invalid stream type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (q->streaming) {
> > +		dprintk(1, "streamon: already streaming\n");
> > +		return -EBUSY;
> > +	}
> > +
> > +	/*
> > +	 * 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;
> > +
> > +	/*
> > +	 * Let driver notice that streaming state has been enabled.
> > +	 */
> > +	call_qop(q, start_streaming, q);
> > +
> > +	/*
> > +	 * 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:
> > +	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)
> > +{
> > +	/*
> > +	 * Tell driver to stop all dma transactions and release all queued
> > +	 * buffers
> > +	 */
> > +
> > +	if (q->streaming)
> > +		call_qop(q, stop_streaming, q);
> > +	q->streaming = 0;
> > +
> > +	/*
> > +	 * 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_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;
> > +
> > +
> > +	if (type != q->type) {
> > +		dprintk(1, "streamoff: invalid stream type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (!q->streaming) {
> > +		dprintk(1, "streamoff: not streaming\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * 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");
> > +	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 (V4L2_TYPE_IS_OUTPUT(q->type) &&
> > +	   (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))) {
> > +		dprintk(1, "Invalid vma flags (need VM_WRITE | VM_SHARED)\n");
> > +		return ret;
> > +	} else if (!(vma->vm_flags & VM_READ) || !(vma->vm_flags & VM_SHARED)) {
> > +		dprintk(1, "Invalid vma flags (need VM_READ | VM_SHARED)\n");
> > +		return ret;
> > +	}
> > +
> > +	/*
> > +	 * 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:
> > +	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;
> > +
> > +
> > +	/*
> > +	 * There is nothing to wait for if no buffers have already been queued.
> > +	 */
> > +	if (list_empty(&q->queued_list)) {
> > +		ret = POLLERR;
> > +		goto end;
> 
> Just do return POLLERR;
> 
> > +	}
> > +
> > +	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;
> 
> Just do return 0;
> 
> > +
> > +	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;
> 
> Again just return.
> 
> > +	}
> > +end:
> > +	return ret;
> 
> return 0;
> 
> > +}
> > +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
> > + * @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,
> > +			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->type = type;
> > +	q->drv_priv = drv_priv;
> > +
> > +	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)
> > +{
> > +	__vb2_queue_cancel(q);
> > +	__vb2_queue_free(q);
> > +}
> > +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..98bc743
> > --- /dev/null
> > +++ b/include/media/videobuf2-core.h
> > @@ -0,0 +1,373 @@
> > +/*
> > + * 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>
> > +
> > +struct vb2_alloc_ctx;
> > +
> > +/**
> > + * 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
> 
> 'get' and 'put' imply reference counting. However, I don't believe that's the
> case for USERPTR. If I am right, then I would like to see different names for
> these callbacks.
> 
> > + * @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
> 
> Outdated comment, replaced by cookie.
> 
> > + * @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
> 
> Mention that mmap increases the refcount for the memory (it's effectively
> a 'get', the counterpart of the 'put' callback).
> 
> Perhaps mmap should be renamed get_mmap and put renamed to put_mmap?
> 
> > + *
> > + * 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);
> > +	void		*(*cookie)(void *buf_priv);
> > +
> > +	unsigned int	(*num_users)(void *buf_priv);
> > +
> > +	int		(*mmap)(void *buf_priv, struct vm_area_struct *vma);
> > +};
> > +
> > +/**
> > + * 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_plane {
> > +	void			*mem_priv;
> > +	int			mapped:1;
> > +};
> > +
> > +/**
> > + * 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_queue;
> > +
> > +/**
> > + * 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;
> > +
> > +	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
> > + * @lock:		aquire all locks taken before any other calls to vb2;
> 
> typo: acquire. Actually, I'd probably say 'reacquire'.
> 
> > + *			required after sleeing in poll_wait function
> 
> typo: sleeing -> sleeping
> 
> > + * @unlock:		release any locks taken before calling vb2 function;
> > + *			required for a safe to call poll_wait function
> 
> I would swap the unlock and lock comments since unlock will be called before
> lock. Some more background info might help as well.
> 
> > + * @buf_alloc:		called to allocate a struct vb2_buffer; driver usually
> > + *			embeds it in its own custom buffer structure; returns
> > + *			a pointer to vb2_buffer or NULL if failed; if not
> > + *			provided kmalloc(sizeof(struct vb2_buffer, GFP_KERNEL)
> > + *			is used
> > + * @buf_free:		called to free the structure allocated by @buf_alloc;
> > + *			if not provided kfree(vb) is used
> > + * @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
> > + * @start_streaming:	called once before entering 'streaming' state; enables
> > + *			driver to recieve buffers over buf_queue() callback
> > + * @stop_streaming:	called when 'streaming' state must be disabled; driver
> > + *			should stop any dma transactions or wait until they
> > + *			finish and give back all buffers it got from buf_queue()
> > + *			callback
> > + * @buf_queue:		passes buffer vb to the driver; driver may start
> > + *			hardware operation on this buffer; driver should give
> > + *			the buffer back by calling vb2_buffer_done() function
> > + */
> > +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 (*lock)(struct vb2_queue *q);
> > +	void (*unlock)(struct vb2_queue *q);
> > +
> > +	struct vb2_buffer *(*buf_alloc)(struct vb2_queue *q);
> > +	void (*buf_free)(struct vb2_queue *q, 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);
> > +
> > +	int (*start_streaming)(struct vb2_queue *q);
> > +	int (*stop_streaming)(struct vb2_queue *q);
> > +
> > +	void (*buf_queue)(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
> > + * @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 list_head		queued_list;
> > +
> > +	struct list_head		done_list;
> > +	spinlock_t			done_lock;
> > +	wait_queue_head_t		done_wq;
> > +
> > +	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;
> > +};
> > +
> > +void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
> > +void *vb2_plane_cookie(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,
> > +			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_is_streaming() - return streaming status of the queue
> > + * @q:		videobuf queue
> > + */
> > +static inline bool vb2_is_streaming(struct vb2_queue *q)
> > +{
> > +	return q->streaming;
> > +}
> > +
> > +/**
> > + * 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)
> > +{
> > +	vb->v4l2_planes[plane_no].bytesused = size;
> > +}
> > +
> > +/**
> > + * vb2_get_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 unsigned long vb2_get_plane_payload(struct vb2_buffer *vb,
> > +				 unsigned int plane_no)
> > +{
> > +	return vb->v4l2_planes[plane_no].bytesused;
> > +}
> > +
> > +/**
> > + * 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
> 
> else is not needed.
> 
> > +		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;
> > +}
> > +
> > +#endif /* _MEDIA_VIDEOBUF2_CORE_H */
> > 
> 
> Great job! Thanks for all the hard work!

Ditto!

R,
Andy

> 	Hans
> 




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

* Re: [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-11-18 12:26     ` Andy Walls
@ 2010-11-18 12:46       ` Hans Verkuil
  2010-11-18 16:52       ` Marek Szyprowski
  1 sibling, 0 replies; 20+ messages in thread
From: Hans Verkuil @ 2010-11-18 12:46 UTC (permalink / raw)
  To: Andy Walls; +Cc: Marek Szyprowski, linux-media, pawel, kyungmin.park


> Hi Hans and Marek,
>
> Some meta-comments below... ;)
>
> On Thu, 2010-11-18 at 10:17 +0100, Hans Verkuil wrote:
>> > +#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)
>>
>> Why not use mem_ops in the call_memop macro? That would simplify it a
>> bit.
>
> I think you meant
>
> "Why not use the the mem_ops macro in the call_memop macro?"

Yes, that's what I meant.

>
> Asking the user to pass in a mem_ops pointer as an argument to the
> call_memop() macro isn't a simplification, but that's how I first read
> your comment.

<snip>

>> > +
>> > +	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);
>>
>> OK, I have a problem here. How do I detect as an application whether a
>> driver
>> supports MMAP and/or USERPTR?
>>
>> What I am using in qv4l2 (and it doesn't work properly with videobuf) is
>> that
>> I call REQBUFS with count == 0 for MMAP and for USERPTR and I check
>> whether it
>> returns 0 or -EINVAL.
>
>
>> It seems a reasonable test since it doesn't allocate anything. It would
>> be nice
>> if vb2 can check for the memory field here based on what the driver
>> supports.
>>
>> Ideally we should make this an official part of the spec (or have some
>> other
>> mechanism to find out what it supported).
>
> Well it seems to be documented, just not clearly:
>
> First paragraph here:
> http://linuxtv.org/downloads/v4l-dvb-apis/mmap.html
>
> First & second Paragraph here:
> http://linuxtv.org/downloads/v4l-dvb-apis/userp.html
> (which mentions not allocating things.)
>
> And in the description here:
> http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-reqbufs.html

As you said, it is documented, sort of.

> I suppose  adding flags to VIDIOC_QUERYCAP results would remove all the
> hokey algorithmic probing of what the driver should already know.
>  http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-querycap.html
>
> V4L2_CAP_STREAMING            0x04000000
> V4L2_CAP_STREAMING_MMAP       0x08000000
> V4L2_CAP_STREAMING_USER_PTR   0x10000000

I don't think there is any need for yet more cap bits. If I can call
REQBUFS with count of 0 and it returns -EINVAL if the memory is incorrect,
then I'm happy. There is a small complication: the memory check should
happen before the check whether another capture is in progress. Otherwise
I would get back EBUSY and still not know if my requested memory type is
ok or not.

Regards,

       Hans

-- 
Hans Verkuil - video4linux developer - sponsored by Cisco


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

* RE: [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator
  2010-11-18  7:14   ` Sewoon Park
@ 2010-11-18 15:19     ` Marek Szyprowski
  0 siblings, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-18 15:19 UTC (permalink / raw)
  To: 'Sewoon Park', linux-media; +Cc: pawel, kyungmin.park

Hello,

On Thursday, November 18, 2010 8:15 AM Sewoon Park wrote:

> Marek Szyprowski wrote:
> > Sent: Wednesday, November 17, 2010 5:40 PM
> > To: linux-media@vger.kernel.org
> > Cc: m.szyprowski@samsung.com; pawel@osciak.com; kyungmin.park@samsung.com
> > Subject: [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator
> >
> > From: Pawel Osciak <p.osciak@samsung.com>
> >
> > 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>
> > Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> > CC: Pawel Osciak <pawel@osciak.com>
> > ---
> >  drivers/media/video/Kconfig                  |    5 +
> >  drivers/media/video/Makefile                 |    1 +
> >  drivers/media/video/videobuf2-dma-coherent.c |  208
> > ++++++++++++++++++++++++++
> >  include/media/videobuf2-dma-coherent.h       |   27 ++++
> >  4 files changed, 241 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 9351423..e7752ee1 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_MEMOPS
> > +	tristate
> > +
> >  config VIDEOBUF2_VMALLOC
> >  	select VIDEOBUF2_CORE
> >  	select VIDEOBUF2_MEMOPS
> > diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> > index 538bee9..baa74e7 100644
> > --- a/drivers/media/video/Makefile
> > +++ b/drivers/media/video/Makefile
> > @@ -117,6 +117,7 @@ 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
> > +obj-$(CONFIG_VIDEOBUF2_DMA_COHERENT)	+= videobuf2-dma_coherent.o
> >
> >  obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.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..761f366
> > --- /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;
> > +};
> 
> (snip)
> 
> > +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,
> 
> The "paddr" is not exist in vb2_mem_ops after [PATCH v4 xxx] lists.
> I think you should fix from paddr to cookie like CMA allocator.

Right, my fault. Looks I've mixed something and forgot to update this patch before
posting. Thanks for spotting this!

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


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

* Re: [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-11-18  9:17   ` Hans Verkuil
  2010-11-18 12:26     ` Andy Walls
@ 2010-11-18 15:51     ` Pawel Osciak
  2010-11-18 16:26     ` Marek Szyprowski
  2 siblings, 0 replies; 20+ messages in thread
From: Pawel Osciak @ 2010-11-18 15:51 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Marek Szyprowski, linux-media, kyungmin.park

Hi Hans,

On Thu, Nov 18, 2010 at 01:17, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> Hi Marek!
>
> Some comments below...
>
> On Wednesday, November 17, 2010 09:39:28 Marek Szyprowski wrote:
>> From: Pawel Osciak <p.osciak@samsung.com>
>> +/**
>> + * 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
>
> 'get' and 'put' imply reference counting. However, I don't believe that's the
> case for USERPTR. If I am right, then I would like to see different names for
> these callbacks.

Actually, they are intended for reference counting. The idea is to
allow using the same memory for creating pipelines. Please take a look
at the second patch in the series, generic implementations of get()
and put() are in there.


-- 
Best regards,
Pawel Osciak

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

* RE: [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-11-18  9:17   ` Hans Verkuil
  2010-11-18 12:26     ` Andy Walls
  2010-11-18 15:51     ` Pawel Osciak
@ 2010-11-18 16:26     ` Marek Szyprowski
  2 siblings, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-18 16:26 UTC (permalink / raw)
  To: 'Hans Verkuil'; +Cc: linux-media, pawel, kyungmin.park

Hello,

On Thursday, November 18, 2010 10:18 AM Hans Verkuil wrote:

> Hi Marek!
> 
> Some comments below...

Thanks for a review! Looks that most of the items you've found are just some
incorrect code style issues. They are just leftovers from removing vb_lock
and friends. I'm sorry for that. Looks that I've hurried too much...

> On Wednesday, November 17, 2010 09:39:28 Marek Szyprowski wrote:
> > From: Pawel Osciak <p.osciak@samsung.com>
> >
> > 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: Marek Szyprowski <m.szyprowski@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > CC: Pawel Osciak <pawel@osciak.com>
> > ---
> >  drivers/media/video/Kconfig          |    3 +
> >  drivers/media/video/Makefile         |    2 +
> >  drivers/media/video/videobuf2-core.c | 1434 ++++++++++++++++++++++++++++++++++
> >  include/media/videobuf2-core.h       |  373 +++++++++
> >  4 files changed, 1812 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 ac16e81..fef6a14 100644
> > --- a/drivers/media/video/Kconfig
> > +++ b/drivers/media/video/Kconfig
> > @@ -49,6 +49,9 @@ 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 af79d47..77c4f85 100644
> > --- a/drivers/media/video/Makefile
> > +++ b/drivers/media/video/Makefile
> > @@ -114,6 +114,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..5c9d3d8
> > --- /dev/null
> > +++ b/drivers/media/video/videobuf2-core.c
> > @@ -0,0 +1,1434 @@
> > +/*
> > + * videobuf2-core.c - V4L2 driver helper framework
> > + *
> > + * Copyright (C) 2010 Samsung Electronics
> > + *
> > + * Author: Pawel Osciak <p.osciak@samsung.com>
> > + *	   Marek Szyprowski <m.szyprowski@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)
> 
> Why not use mem_ops in the call_memop macro? That would simplify it a bit.

Right. I will update this macro.

> 
> > +
> > +#define call_qop(q, op, args...)					\
> > +	(((q)->ops->op) ? ((q)->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_buf_alloc() - allocate a singe vb2_buffer structure
> > + *
> > + * Returns the pointer to the allocated structure.
> > + */
> > +void *__vb2_buf_alloc(struct vb2_queue *q)
> > +{
> > +	struct vb2_buffer *vb;
> > +	if (q->ops->buf_alloc)
> > +		vb = q->ops->buf_alloc(q);
> > +	else
> > +		vb = kzalloc(sizeof(struct vb2_buffer), GFP_KERNEL);
> > +	return vb;
> > +}
> > +
> > +/**
> > + * __vb2_buf_free() - free a singe vb2_buffer structure
> > + *
> > + * Frees memory used by the vb2_buffer structure.
> > + */
> > +void __vb2_buf_free(struct vb2_queue *q, struct vb2_buffer *vb)
> > +{
> > +	if (q->ops->buf_free)
> > +		q->ops->buf_free(q, vb);
> > +	else
> > +		kfree(vb);
> > +}
> > +
> > +/**
> > + * __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 = call_qop(q, 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 = __vb2_buf_alloc(q);
> > +		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);
> > +				__vb2_buf_free(q, vb);
> > +				break;
> > +			}
> > +			/*
> > +			 * Call the driver-provided buffer initialization
> > +			 * callback, if given. An error in initialization
> > +			 * results in queue setup failure.
> > +			 */
> > +			ret = call_qop(q, buf_init, vb);
> > +			if (ret) {
> > +				dprintk(1, "Buffer %d %p initialization"
> > +					" failed\n", buffer, vb);
> > +				__vb2_buf_mem_free(vb);
> > +				__vb2_buf_free(q, 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;
> > +		__vb2_buf_free(q, 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;
> > +		b->bytesused = vb->v4l2_planes[0].bytesused;
> > +		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;
> > +
> > +	if (b->type != q->type) {
> > +		dprintk(1, "querybuf: wrong buffer type\n");
> > +		goto done;
> 
> Just to return -EINVAL;
> 
> > +	}
> > +
> > +	if (b->index >= q->num_buffers) {
> > +		dprintk(1, "querybuf: buffer index out of range\n");
> > +		goto done;
> 
> ditto
> 
> > +	}
> > +	vb = q->bufs[b->index];
> > +	if (NULL == vb) {
> > +		/* Should never happen */
> > +		dprintk(1, "querybuf: no such buffer\n");
> > +		goto done;
> 
> ditto
> 
> > +	}
> > +
> > +	ret = __fill_v4l2_buffer(vb, b);
> 
> Just do return __file_v4l2_buffer(vb, b);
> 
> > +done:
> > +	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;
> > +	}
> > +
> > +	if (req->type != q->type) {
> > +		dprintk(1, "reqbufs: queue type invalid\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (q->streaming) {
> > +		dprintk(1, "reqbufs: streaming active\n");
> > +		return -EBUSY;
> > +	}
> > +
> > +	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);
> 
> OK, I have a problem here. How do I detect as an application whether a driver
> supports MMAP and/or USERPTR?
> 
> What I am using in qv4l2 (and it doesn't work properly with videobuf) is that
> I call REQBUFS with count == 0 for MMAP and for USERPTR and I check whether it
> returns 0 or -EINVAL.

I'm not really sure this is a good idea. What if the other application is already
streaming? In such case you will either break the streaming or get an -EINVAL if
freeing the buffers is not yet possible (I assume that some buffers are mmaped
and cannot be freed on this request).

> It seems a reasonable test since it doesn't allocate anything. It would be nice
> if vb2 can check for the memory field here based on what the driver supports.
> 
> Ideally we should make this an official part of the spec (or have some other
> mechanism to find out what it supported).

I think that we should use some different method. Maybe some new flags to
VIDEOC_QUERYCAP?

> 
> > +		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:
> > +	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_cookie() - Return allocator specific cookie for the 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.
> 
> This comment should be extended since it now returns a cookie instead of a
> physical address. Some explanation would be useful here.

Definitely right.

> > + */
> > +void *vb2_plane_cookie(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, cookie, vb->planes[plane_no].mem_priv);
> > +}
> > +EXPORT_SYMBOL_GPL(vb2_plane_cookie);
> > +
> > +/**
> > + * 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(&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.
> > +	 */
> > +	ret = call_qop(q, 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;
> > +	vb->state = VB2_BUF_STATE_ACTIVE;
> > +	q->ops->buf_queue(vb);
> > +}
> > +
> > +/**
> > + * 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 = 0;
> > +
> > +
> > +	if (b->type != q->type) {
> > +		dprintk(1, "qbuf: invalid buffer type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (b->index >= q->num_buffers) {
> > +		dprintk(1, "qbuf: buffer index out of range\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	vb = q->bufs[b->index];
> > +	if (NULL == vb) {
> > +		/* Should never happen */
> > +		dprintk(1, "qbuf: buffer is NULL\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (b->memory != q->memory) {
> > +		dprintk(1, "qbuf: invalid memory type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
> > +		dprintk(1, "qbuf: buffer already in use\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	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;
> > +
> > +	ret = call_qop(q, 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:
> > +	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)
> > +{
> > +checks:
> > +	if (!q->streaming) {
> > +		dprintk(1, "Streaming off, will not wait for buffers\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * Buffers may be added to vb_done_list without holding the driver's
> > +	 * lock, but removal is performed only while holding both driver's
> > +	 * lock and the vb_done_lock spinlock. Thus we can be sure that as
> > +	 * long as we hold lock, the list will remain not empty if this
> > +	 * check succeeds.
> > +	 */
> > +	if (list_empty(&q->done_list)) {
> > +		int retval;
> > +		if (nonblocking) {
> > +			dprintk(1, "Nonblocking and no buffers to dequeue, "
> > +					"will not wait\n");
> > +			return -EAGAIN;
> > +		}
> > +
> > +		/*
> > +		 * We are streaming and nonblocking, wait for another buffer to
> > +		 * become ready or for streamoff. Driver's lock is released to
> > +		 * allow streamoff or qbuf to be called while waiting.
> > +		 */
> > +		call_qop(q, unlock, q);
> > +
> > +		/*
> > +		 * 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);
> > +		call_qop(q, lock, q);
> > +
> > +		if (retval)
> > +			return -EINTR;
> > +
> > +		goto checks;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/**
> > + * __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;
> > +
> > +	/*
> > +	 * Drivers 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;
> > +
> > +
> > +	if (b->type != q->type) {
> > +		dprintk(1, "dqbuf: invalid buffer type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	ret = __vb2_get_done_vb(q, &vb, nonblocking);
> > +	if (ret < 0) {
> > +		dprintk(1, "dqbuf: error getting next done buffer\n");
> > +		goto done;
> > +	}
> > +
> > +	ret = call_qop(q, 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:
> > +	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;
> > +
> > +
> > +	if (type != q->type) {
> > +		dprintk(1, "streamon: invalid stream type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (q->streaming) {
> > +		dprintk(1, "streamon: already streaming\n");
> > +		return -EBUSY;
> > +	}
> > +
> > +	/*
> > +	 * 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;
> > +
> > +	/*
> > +	 * Let driver notice that streaming state has been enabled.
> > +	 */
> > +	call_qop(q, start_streaming, q);
> > +
> > +	/*
> > +	 * 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:
> > +	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)
> > +{
> > +	/*
> > +	 * Tell driver to stop all dma transactions and release all queued
> > +	 * buffers
> > +	 */
> > +
> > +	if (q->streaming)
> > +		call_qop(q, stop_streaming, q);
> > +	q->streaming = 0;
> > +
> > +	/*
> > +	 * 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_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;
> > +
> > +
> > +	if (type != q->type) {
> > +		dprintk(1, "streamoff: invalid stream type\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (!q->streaming) {
> > +		dprintk(1, "streamoff: not streaming\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * 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");
> > +	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 (V4L2_TYPE_IS_OUTPUT(q->type) &&
> > +	   (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))) {
> > +		dprintk(1, "Invalid vma flags (need VM_WRITE | VM_SHARED)\n");
> > +		return ret;
> > +	} else if (!(vma->vm_flags & VM_READ) || !(vma->vm_flags & VM_SHARED)) {
> > +		dprintk(1, "Invalid vma flags (need VM_READ | VM_SHARED)\n");
> > +		return ret;
> > +	}
> > +
> > +	/*
> > +	 * 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:
> > +	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;
> > +
> > +
> > +	/*
> > +	 * There is nothing to wait for if no buffers have already been queued.
> > +	 */
> > +	if (list_empty(&q->queued_list)) {
> > +		ret = POLLERR;
> > +		goto end;
> 
> Just do return POLLERR;
> 
> > +	}
> > +
> > +	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;
> 
> Just do return 0;
> 
> > +
> > +	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;
> 
> Again just return.
> 
> > +	}
> > +end:
> > +	return ret;
> 
> return 0;
> 
> > +}
> > +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
> > + * @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,
> > +			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->type = type;
> > +	q->drv_priv = drv_priv;
> > +
> > +	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)
> > +{
> > +	__vb2_queue_cancel(q);
> > +	__vb2_queue_free(q);
> > +}
> > +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..98bc743
> > --- /dev/null
> > +++ b/include/media/videobuf2-core.h
> > @@ -0,0 +1,373 @@
> > +/*
> > + * 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>
> > +
> > +struct vb2_alloc_ctx;
> > +
> > +/**
> > + * 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
> 
> 'get' and 'put' imply reference counting. However, I don't believe that's the
> case for USERPTR. If I am right, then I would like to see different names for
> these callbacks.

Functions that initializes access to USERPTR memory might (and in many cases
should) perform some kind of reference counting. If an allocator performs an
access to the system memory described by a set of pages (like dma-sg allocator),
the reference counting is not needed. However dma-conting and CMA allocators can
access a physically contiguous memory taken from user pointer (if it really
points into such memory). It can be a mmaped framebuffer or other v4l2
device's buffers, so proper reference counting is really required.

> > + * @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
> 
> Outdated comment, replaced by cookie.

Right, thx

> 
> > + * @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
> 
> Mention that mmap increases the refcount for the memory (it's effectively
> a 'get', the counterpart of the 'put' callback).
> 
> Perhaps mmap should be renamed get_mmap and put renamed to put_mmap?

Put is not really a counterpart of mmap. Put releases the buffer from a
vb2/driver, but the buffer can still be mmaped and used by application
(this happens when the application closes the file descriptor). The final
put will be called from vm_area close() callback which is called when the
application removes the mapping (calls unmap or dies).

The name 'get_mmap' will be really misleading. The main purpose of this callback
is to create a userspace maping for a buffer. Increasing a reference count in 
this case is just a side effect.

> > + *
> > + * 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);
> > +	void		*(*cookie)(void *buf_priv);
> > +
> > +	unsigned int	(*num_users)(void *buf_priv);
> > +
> > +	int		(*mmap)(void *buf_priv, struct vm_area_struct *vma);
> > +};
> > +
> > +/**
> > + * 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_plane {
> > +	void			*mem_priv;
> > +	int			mapped:1;
> > +};
> > +
> > +/**
> > + * 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_queue;
> > +
> > +/**
> > + * 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;
> > +
> > +	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
> > + * @lock:		aquire all locks taken before any other calls to vb2;
> 
> typo: acquire. Actually, I'd probably say 'reacquire'.
> 
> > + *			required after sleeing in poll_wait function
> 
> typo: sleeing -> sleeping
> 
> > + * @unlock:		release any locks taken before calling vb2 function;
> > + *			required for a safe to call poll_wait function
> 
> I would swap the unlock and lock comments since unlock will be called before
> lock. Some more background info might help as well.

OK, I will try to extend this a bit and I will put unlock comment first.

> 
> > + * @buf_alloc:		called to allocate a struct vb2_buffer; driver usually
> > + *			embeds it in its own custom buffer structure; returns
> > + *			a pointer to vb2_buffer or NULL if failed; if not
> > + *			provided kmalloc(sizeof(struct vb2_buffer, GFP_KERNEL)
> > + *			is used
> > + * @buf_free:		called to free the structure allocated by @buf_alloc;
> > + *			if not provided kfree(vb) is used
> > + * @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
> > + * @start_streaming:	called once before entering 'streaming' state; enables
> > + *			driver to recieve buffers over buf_queue() callback
> > + * @stop_streaming:	called when 'streaming' state must be disabled; driver
> > + *			should stop any dma transactions or wait until they
> > + *			finish and give back all buffers it got from buf_queue()
> > + *			callback
> > + * @buf_queue:		passes buffer vb to the driver; driver may start
> > + *			hardware operation on this buffer; driver should give
> > + *			the buffer back by calling vb2_buffer_done() function
> > + */
> > +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 (*lock)(struct vb2_queue *q);
> > +	void (*unlock)(struct vb2_queue *q);
> > +
> > +	struct vb2_buffer *(*buf_alloc)(struct vb2_queue *q);
> > +	void (*buf_free)(struct vb2_queue *q, 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);
> > +
> > +	int (*start_streaming)(struct vb2_queue *q);
> > +	int (*stop_streaming)(struct vb2_queue *q);
> > +
> > +	void (*buf_queue)(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
> > + * @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 list_head		queued_list;
> > +
> > +	struct list_head		done_list;
> > +	spinlock_t			done_lock;
> > +	wait_queue_head_t		done_wq;
> > +
> > +	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;
> > +};
> > +
> > +void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
> > +void *vb2_plane_cookie(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,
> > +			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_is_streaming() - return streaming status of the queue
> > + * @q:		videobuf queue
> > + */
> > +static inline bool vb2_is_streaming(struct vb2_queue *q)
> > +{
> > +	return q->streaming;
> > +}
> > +
> > +/**
> > + * 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)
> > +{
> > +	vb->v4l2_planes[plane_no].bytesused = size;
> > +}
> > +
> > +/**
> > + * vb2_get_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 unsigned long vb2_get_plane_payload(struct vb2_buffer *vb,
> > +				 unsigned int plane_no)
> > +{
> > +	return vb->v4l2_planes[plane_no].bytesused;
> > +}
> > +
> > +/**
> > + * 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
> 
> else is not needed.
> 
> > +		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;
> > +}
> > +
> > +#endif /* _MEDIA_VIDEOBUF2_CORE_H */
> >
> 
> Great job! Thanks for all the hard work!
> 

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


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

* RE: [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-11-18 12:26     ` Andy Walls
  2010-11-18 12:46       ` Hans Verkuil
@ 2010-11-18 16:52       ` Marek Szyprowski
  1 sibling, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-18 16:52 UTC (permalink / raw)
  To: 'Andy Walls', 'Hans Verkuil'
  Cc: linux-media, pawel, kyungmin.park

Hello,

On Thursday, November 18, 2010 1:27 PM Andy Walls wrote:

> Hi Hans and Marek,
> 
> Some meta-comments below... ;)
> 
> On Thu, 2010-11-18 at 10:17 +0100, Hans Verkuil wrote:
> > Hi Marek!
> >
> > Some comments below...
> >
> > On Wednesday, November 17, 2010 09:39:28 Marek Szyprowski wrote:

> ...

> > > +/**
> > > + * 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;
> > > +	}
> > > +
> > > +	if (req->type != q->type) {
> > > +		dprintk(1, "reqbufs: queue type invalid\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	if (q->streaming) {
> > > +		dprintk(1, "reqbufs: streaming active\n");
> > > +		return -EBUSY;
> > > +	}
> > > +
> > > +	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);
> >
> > OK, I have a problem here. How do I detect as an application whether a driver
> > supports MMAP and/or USERPTR?
> >
> > What I am using in qv4l2 (and it doesn't work properly with videobuf) is that
> > I call REQBUFS with count == 0 for MMAP and for USERPTR and I check whether it
> > returns 0 or -EINVAL.
> 
> 
> > It seems a reasonable test since it doesn't allocate anything. It would be nice
> > if vb2 can check for the memory field here based on what the driver supports.
> >
> > Ideally we should make this an official part of the spec (or have some other
> > mechanism to find out what it supported).
> 
> Well it seems to be documented, just not clearly:
> 
> First paragraph here:
> http://linuxtv.org/downloads/v4l-dvb-apis/mmap.html
> 
> First & second Paragraph here:
> http://linuxtv.org/downloads/v4l-dvb-apis/userp.html
> (which mentions not allocating things.)
> 
> And in the description here:
> http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-reqbufs.html
> 
> 
> I suppose  adding flags to VIDIOC_QUERYCAP results would remove all the
> hokey algorithmic probing of what the driver should already know.
>  http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-querycap.html
> 
> V4L2_CAP_STREAMING            0x04000000
> V4L2_CAP_STREAMING_MMAP       0x08000000
> V4L2_CAP_STREAMING_USER_PTR   0x10000000

Good idea! This way querying memory access types will be much less intrusive :)

> ...

> >
> > Great job! Thanks for all the hard work!
> 
> Ditto!

Thx :) 

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


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

* [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-11-19 15:55 [PATCH/RFC v5 0/7] Videobuf2 framework Marek Szyprowski
@ 2010-11-19 15:55 ` Marek Szyprowski
  0 siblings, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-11-19 15:55 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

From: Pawel Osciak <p.osciak@samsung.com>

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: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/video/Kconfig          |    3 +
 drivers/media/video/Makefile         |    2 +
 drivers/media/video/videobuf2-core.c | 1435 ++++++++++++++++++++++++++++++++++
 include/media/videobuf2-core.h       |  376 +++++++++
 4 files changed, 1816 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 ac16e81..fef6a14 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -49,6 +49,9 @@ 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 af79d47..77c4f85 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -114,6 +114,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..828803f
--- /dev/null
+++ b/drivers/media/video/videobuf2-core.c
@@ -0,0 +1,1435 @@
+/*
+ * videobuf2-core.c - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *	   Marek Szyprowski <m.szyprowski@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...)				\
+	((mem_ops(q, plane)->op) ?					\
+		(mem_ops(q, plane)->op(args)) : 0)
+
+#define call_qop(q, op, args...)					\
+	(((q)->ops->op) ? ((q)->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_buf_alloc() - allocate a singe vb2_buffer structure
+ *
+ * Returns the pointer to the allocated structure.
+ */
+void *__vb2_buf_alloc(struct vb2_queue *q)
+{
+	struct vb2_buffer *vb;
+	if (q->ops->buf_alloc)
+		vb = q->ops->buf_alloc(q);
+	else
+		vb = kzalloc(sizeof(struct vb2_buffer), GFP_KERNEL);
+	return vb;
+}
+
+/**
+ * __vb2_buf_free() - free a singe vb2_buffer structure
+ *
+ * Frees memory used by the vb2_buffer structure.
+ */
+void __vb2_buf_free(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+	if (q->ops->buf_free)
+		q->ops->buf_free(q, vb);
+	else
+		kfree(vb);
+}
+
+/**
+ * __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 = call_qop(q, 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 = __vb2_buf_alloc(q);
+		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);
+				__vb2_buf_free(q, vb);
+				break;
+			}
+			/*
+			 * Call the driver-provided buffer initialization
+			 * callback, if given. An error in initialization
+			 * results in queue setup failure.
+			 */
+			ret = call_qop(q, buf_init, vb);
+			if (ret) {
+				dprintk(1, "Buffer %d %p initialization"
+					" failed\n", buffer, vb);
+				__vb2_buf_mem_free(vb);
+				__vb2_buf_free(q, 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;
+		__vb2_buf_free(q, 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;
+		b->bytesused = vb->v4l2_planes[0].bytesused;
+		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;
+
+	if (b->type != q->type) {
+		dprintk(1, "querybuf: wrong buffer type\n");
+		return -EINVAL;
+	}
+
+	if (b->index >= q->num_buffers) {
+		dprintk(1, "querybuf: buffer index out of range\n");
+		return -EINVAL;
+	}
+	vb = q->bufs[b->index];
+	if (NULL == vb) {
+		/* Should never happen */
+		dprintk(1, "querybuf: no such buffer\n");
+		return -EINVAL;
+	}
+
+	return __fill_v4l2_buffer(vb, b);
+}
+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;
+	}
+
+	if (req->type != q->type) {
+		dprintk(1, "reqbufs: queue type invalid\n");
+		return -EINVAL;
+	}
+
+	if (q->streaming) {
+		dprintk(1, "reqbufs: streaming active\n");
+		return -EBUSY;
+	}
+
+	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:
+	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_cookie() - Return allocator specific cookie for the 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 an allocator specific cookie for a given plane if
+ * available, NULL otherwise. The allocator should provide some simple static
+ * inline function which converts this cookie to the allocator specific type
+ * that can be used directly by the driver to access the buffer. This can be
+ * for example physical address, pointer to scatter list or iommu mapping.
+ */
+void *vb2_plane_cookie(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, cookie, vb->planes[plane_no].mem_priv);
+}
+EXPORT_SYMBOL_GPL(vb2_plane_cookie);
+
+/**
+ * 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(&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.
+	 */
+	ret = call_qop(q, 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;
+	vb->state = VB2_BUF_STATE_ACTIVE;
+	q->ops->buf_queue(vb);
+}
+
+/**
+ * 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 = 0;
+
+
+	if (b->type != q->type) {
+		dprintk(1, "qbuf: invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	if (b->index >= q->num_buffers) {
+		dprintk(1, "qbuf: buffer index out of range\n");
+		return -EINVAL;
+	}
+
+	vb = q->bufs[b->index];
+	if (NULL == vb) {
+		/* Should never happen */
+		dprintk(1, "qbuf: buffer is NULL\n");
+		return -EINVAL;
+	}
+
+	if (b->memory != q->memory) {
+		dprintk(1, "qbuf: invalid memory type\n");
+		return -EINVAL;
+	}
+
+	if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+		dprintk(1, "qbuf: buffer already in use\n");
+		return -EINVAL;
+	}
+
+	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;
+
+	ret = call_qop(q, 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:
+	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)
+{
+checks:
+	if (!q->streaming) {
+		dprintk(1, "Streaming off, will not wait for buffers\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Buffers may be added to vb_done_list without holding the driver's
+	 * lock, but removal is performed only while holding both driver's
+	 * lock and the vb_done_lock spinlock. Thus we can be sure that as
+	 * long as we hold lock, the list will remain not empty if this
+	 * check succeeds.
+	 */
+	if (list_empty(&q->done_list)) {
+		int retval;
+		if (nonblocking) {
+			dprintk(1, "Nonblocking and no buffers to dequeue, "
+					"will not wait\n");
+			return -EAGAIN;
+		}
+
+		/*
+		 * We are streaming and nonblocking, wait for another buffer to
+		 * become ready or for streamoff. Driver's lock is released to
+		 * allow streamoff or qbuf to be called while waiting.
+		 */
+		call_qop(q, unlock, q);
+
+		/*
+		 * 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);
+		call_qop(q, lock, q);
+
+		if (retval)
+			return -EINTR;
+
+		goto checks;
+	}
+	return 0;
+}
+
+/**
+ * __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;
+
+	/*
+	 * Drivers 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;
+
+
+	if (b->type != q->type) {
+		dprintk(1, "dqbuf: invalid buffer type\n");
+		return -EINVAL;
+	}
+
+	ret = __vb2_get_done_vb(q, &vb, nonblocking);
+	if (ret < 0) {
+		dprintk(1, "dqbuf: error getting next done buffer\n");
+		goto done;
+	}
+
+	ret = call_qop(q, 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:
+	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;
+
+
+	if (type != q->type) {
+		dprintk(1, "streamon: invalid stream type\n");
+		return -EINVAL;
+	}
+
+	if (q->streaming) {
+		dprintk(1, "streamon: already streaming\n");
+		return -EBUSY;
+	}
+
+	/*
+	 * 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;
+
+	/*
+	 * Let driver notice that streaming state has been enabled.
+	 */
+	call_qop(q, start_streaming, q);
+
+	/*
+	 * 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:
+	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)
+{
+	int i;
+	/*
+	 * Tell driver to stop all dma transactions and release all queued
+	 * buffers
+	 */
+
+	if (q->streaming)
+		call_qop(q, stop_streaming, q);
+	q->streaming = 0;
+
+	/*
+	 * 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_all(&q->done_wq);
+
+	/*
+	 * Reinitialize all buffers for next use.
+	 */
+	for (i = 0; i < q->num_buffers; ++i)
+		q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED;
+}
+
+/**
+ * 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;
+
+
+	if (type != q->type) {
+		dprintk(1, "streamoff: invalid stream type\n");
+		return -EINVAL;
+	}
+
+	if (!q->streaming) {
+		dprintk(1, "streamoff: not streaming\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * 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");
+	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 (V4L2_TYPE_IS_OUTPUT(q->type) &&
+	   (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))) {
+		dprintk(1, "Invalid vma flags (need VM_WRITE | VM_SHARED)\n");
+		return ret;
+	} else if (!(vma->vm_flags & VM_READ) || !(vma->vm_flags & VM_SHARED)) {
+		dprintk(1, "Invalid vma flags (need VM_READ | VM_SHARED)\n");
+		return ret;
+	}
+
+	/*
+	 * 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:
+	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;
+
+
+	/*
+	 * There is nothing to wait for if no buffers have already been queued.
+	 */
+	if (list_empty(&q->queued_list))
+		return POLLERR;
+
+	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 && (vb->state == VB2_BUF_STATE_DONE
+			|| vb->state == VB2_BUF_STATE_ERROR)) {
+		if (V4L2_TYPE_IS_OUTPUT(q->type))
+			return POLLOUT | POLLWRNORM;
+		else
+			return POLLIN | POLLRDNORM;
+	}
+	return 0;
+}
+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
+ * @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,
+			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->type = type;
+	q->drv_priv = drv_priv;
+
+	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)
+{
+	__vb2_queue_cancel(q);
+	__vb2_queue_free(q);
+}
+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..5235a0d
--- /dev/null
+++ b/include/media/videobuf2-core.h
@@ -0,0 +1,376 @@
+/*
+ * 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>
+
+struct vb2_alloc_ctx;
+
+/**
+ * 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
+ * @cookie:	return allocator specific cookie for 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);
+	void		*(*cookie)(void *buf_priv);
+
+	unsigned int	(*num_users)(void *buf_priv);
+
+	int		(*mmap)(void *buf_priv, struct vm_area_struct *vma);
+};
+
+/**
+ * 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_plane {
+	void			*mem_priv;
+	int			mapped:1;
+};
+
+/**
+ * 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_queue;
+
+/**
+ * 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;
+
+	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
+ * @unlock:		release any locks taken while calling vb2 functions;
+ *			it is called before poll_wait function in vb2_poll
+ *			implementation; required to avoid deadlock when vb2_poll
+ *			function waits for a buffer
+ * @lock:		reacquire all locks released in the previous callback;
+ *			required to continue operation after sleeping in
+ *			poll_wait function
+ * @buf_alloc:		called to allocate a struct vb2_buffer; driver usually
+ *			embeds it in its own custom buffer structure; returns
+ *			a pointer to vb2_buffer or NULL if failed; if not
+ *			provided kmalloc(sizeof(struct vb2_buffer, GFP_KERNEL)
+ *			is used
+ * @buf_free:		called to free the structure allocated by @buf_alloc;
+ *			if not provided kfree(vb) is used
+ * @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
+ * @start_streaming:	called once before entering 'streaming' state; enables
+ *			driver to recieve buffers over buf_queue() callback
+ * @stop_streaming:	called when 'streaming' state must be disabled; driver
+ *			should stop any dma transactions or wait until they
+ *			finish and give back all buffers it got from buf_queue()
+ *			callback
+ * @buf_queue:		passes buffer vb to the driver; driver may start
+ *			hardware operation on this buffer; driver should give
+ *			the buffer back by calling vb2_buffer_done() function
+ */
+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 (*unlock)(struct vb2_queue *q);
+	void (*lock)(struct vb2_queue *q);
+
+	struct vb2_buffer *(*buf_alloc)(struct vb2_queue *q);
+	void (*buf_free)(struct vb2_queue *q, 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);
+
+	int (*start_streaming)(struct vb2_queue *q);
+	int (*stop_streaming)(struct vb2_queue *q);
+
+	void (*buf_queue)(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
+ * @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 list_head		queued_list;
+
+	struct list_head		done_list;
+	spinlock_t			done_lock;
+	wait_queue_head_t		done_wq;
+
+	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;
+};
+
+void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
+void *vb2_plane_cookie(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,
+			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_is_streaming() - return streaming status of the queue
+ * @q:		videobuf queue
+ */
+static inline bool vb2_is_streaming(struct vb2_queue *q)
+{
+	return q->streaming;
+}
+
+/**
+ * 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)
+{
+	vb->v4l2_planes[plane_no].bytesused = size;
+}
+
+/**
+ * vb2_get_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 unsigned long vb2_get_plane_payload(struct vb2_buffer *vb,
+				 unsigned int plane_no)
+{
+	return vb->v4l2_planes[plane_no].bytesused;
+}
+
+/**
+ * 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;
+	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;
+}
+
+#endif /* _MEDIA_VIDEOBUF2_CORE_H */
-- 
1.7.1.569.g6f426


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

* RE: [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-10-25  0:17   ` Pawel Osciak
@ 2010-10-25 10:13     ` Marek Szyprowski
  0 siblings, 0 replies; 20+ messages in thread
From: Marek Szyprowski @ 2010-10-25 10:13 UTC (permalink / raw)
  To: 'Pawel Osciak'; +Cc: linux-media, kyungmin.park

Hello,

On Monday, October 25, 2010 2:17 AM Pawel Osciak wrote:

> On Tue, Oct 19, 2010 at 23:41, Marek Szyprowski
> <m.szyprowski@samsung.com> wrote:
> > From: Pawel Osciak <p.osciak@samsung.com>
> 
> > +/**
> > + * __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)
> > +{
> > +       /*
> > +        * Tell driver to stop all dma transactions and release all queued
> > +        * buffers
> > +        */
> 
> Just being picky, but those doesn't neccesarily are "dma" transactions.

Ok, I will change this comment.

> 
> > +       if (q->streaming && q->ops->stop_streaming)
> > +               q->ops->stop_streaming(q);
> > +       q->streaming = 0;
> > +
> > +       /*
> > +        * 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_all(&q->done_wq);
> 
> Any reason for replacing wake_up_interruptible_all with wake_up_all?

IMHO there is no reason to limit it to wake_up_interruptible_all. 

Initially I thought that the driver MIGHT want to implement stop_streaming()
on top of this wait_queue, but later I abandoned this idea...

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


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

* Re: [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-10-20  6:41 ` [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Marek Szyprowski
@ 2010-10-25  0:17   ` Pawel Osciak
  2010-10-25 10:13     ` Marek Szyprowski
  0 siblings, 1 reply; 20+ messages in thread
From: Pawel Osciak @ 2010-10-25  0:17 UTC (permalink / raw)
  To: Marek Szyprowski; +Cc: linux-media, kyungmin.park

Hi Marek,
Thanks for your patches, I went through your changes. Minor comments below.

On Tue, Oct 19, 2010 at 23:41, Marek Szyprowski
<m.szyprowski@samsung.com> wrote:
> From: Pawel Osciak <p.osciak@samsung.com>

> +/**
> + * __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)
> +{
> +       /*
> +        * Tell driver to stop all dma transactions and release all queued
> +        * buffers
> +        */

Just being picky, but those doesn't neccesarily are "dma" transactions.

> +       if (q->streaming && q->ops->stop_streaming)
> +               q->ops->stop_streaming(q);
> +       q->streaming = 0;
> +
> +       /*
> +        * 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_all(&q->done_wq);

Any reason for replacing wake_up_interruptible_all with wake_up_all?



-- 
Best regards,
Pawel Osciak

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

* [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework
  2010-10-20  6:41 [PATCH/RFC v3 0/7] Videobuf2 framework Marek Szyprowski
@ 2010-10-20  6:41 ` Marek Szyprowski
  2010-10-25  0:17   ` Pawel Osciak
  0 siblings, 1 reply; 20+ messages in thread
From: Marek Szyprowski @ 2010-10-20  6:41 UTC (permalink / raw)
  To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park

From: Pawel Osciak <p.osciak@samsung.com>

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: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
---
 drivers/media/video/Kconfig          |    3 +
 drivers/media/video/Makefile         |    2 +
 drivers/media/video/videobuf2-core.c | 1440 ++++++++++++++++++++++++++++++++++
 include/media/videobuf2-core.h       |  371 +++++++++
 4 files changed, 1816 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..9bf2fe1 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -49,6 +49,9 @@ 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..4a29c49
--- /dev/null
+++ b/drivers/media/video/videobuf2-core.c
@@ -0,0 +1,1440 @@
+/*
+ * videobuf2-core.c - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *	   Marek Szyprowski <m.szyprowski@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(&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;
+	vb->state = VB2_BUF_STATE_ACTIVE;
+	q->ops->buf_queue(vb);
+}
+
+/**
+ * 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)
+{
+checks:
+	if (!q->streaming) {
+		dprintk(1, "Streaming off, will not wait for buffers\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * 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)) {
+		int retval;
+		if (nonblocking) {
+			dprintk(1, "Nonblocking and no buffers to dequeue, "
+					"will not wait\n");
+			return -EAGAIN;
+		}
+
+		/*
+		 * 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)
+			return -EINTR;
+
+		goto checks;
+	}
+	return 0;
+}
+
+/**
+ * __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;
+
+	/*
+	 * Let driver notice that streaming state has been enabled.
+	 */
+	if (q->ops->start_streaming)
+		q->ops->start_streaming(q);
+
+	/*
+	 * 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)
+{
+	/*
+	 * Tell driver to stop all dma transactions and release all queued
+	 * buffers
+	 */
+	if (q->streaming && q->ops->stop_streaming)
+		q->ops->stop_streaming(q);
+	q->streaming = 0;
+
+	/*
+	 * 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_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 (V4L2_TYPE_IS_OUTPUT(q->type) &&
+	   (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))) {
+		dprintk(1, "Invalid vma flags (need VM_WRITE | VM_SHARED)\n");
+		return ret;
+	} else if (!(vma->vm_flags & VM_READ) || !(vma->vm_flags & VM_SHARED)) {
+		dprintk(1, "Invalid vma flags (need VM_READ | 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
+ * @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,
+			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->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..76d0b4e
--- /dev/null
+++ b/include/media/videobuf2-core.h
@@ -0,0 +1,371 @@
+/*
+ * 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>
+
+struct vb2_alloc_ctx;
+
+/**
+ * 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);
+};
+
+/**
+ * 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_plane {
+	void			*mem_priv;
+	int			mapped:1;
+};
+
+/**
+ * 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_queue;
+
+/**
+ * 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;
+
+	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_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
+ * @start_streaming:	called once before entering 'streaming' state; enables
+ *			driver to recieve buffers over buf_queue() callback
+ * @stop_streaming:	called when 'streaming' state must be disabled; driver
+ *			should stop any dma transactions or wait until they
+ *			finish and give back all buffers it got from buf_queue()
+ *			callback
+ * @buf_queue:		passes buffer vb to the driver; driver may start
+ *			hardware operation on this buffer; driver should give
+ *			the buffer back by calling vb2_buffer_done() function
+ */
+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);
+
+	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);
+
+	int (*start_streaming)(struct vb2_queue *q);
+	int (*stop_streaming)(struct vb2_queue *q);
+	void (*buf_queue)(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
+ * @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;
+
+	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;
+};
+
+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,
+			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_is_streaming() - return streaming status of the queue
+ * @q:		videobuf queue
+ */
+static inline bool vb2_is_streaming(struct vb2_queue *q)
+{
+	return q->streaming;
+}
+
+/**
+ * vb2_lock() - lock the queue
+ * @q:		videobuf queue
+ *
+ * This function can be used to obtain internal vb2 lock and block any
+ * concurrent calls to vb2. Usefull for implementing S_FMT and similar
+ * operations that requires exclusive access to driver and it's queue.
+ */
+static inline void vb2_lock(struct vb2_queue *q)
+{
+	mutex_lock(&q->vb_lock);
+}
+
+/**
+ * vb2_unlock() - unlock the queue
+ * @q:		videobuf queue
+ *
+ * This function can be used to release internal vb2 lock and let it
+ * any pending calls to vb2.
+ */
+static inline void vb2_unlock(struct vb2_queue *q)
+{
+	mutex_unlock(&q->vb_lock);
+}
+
+/**
+ * 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;
+}
+
+#endif /* _MEDIA_VIDEOBUF2_CORE_H */
-- 
1.7.1.569.g6f426


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

end of thread, other threads:[~2010-11-19 15:55 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-11-17  8:39 [PATCH/RFC v4 0/7] Videobuf2 framework Marek Szyprowski
2010-11-17  8:39 ` [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Marek Szyprowski
2010-11-18  9:17   ` Hans Verkuil
2010-11-18 12:26     ` Andy Walls
2010-11-18 12:46       ` Hans Verkuil
2010-11-18 16:52       ` Marek Szyprowski
2010-11-18 15:51     ` Pawel Osciak
2010-11-18 16:26     ` Marek Szyprowski
2010-11-17  8:39 ` [PATCH 2/7] v4l: videobuf2: add generic memory handling routines Marek Szyprowski
2010-11-17  8:39 ` [PATCH 3/7] v4l: videobuf2: add vmalloc allocator Marek Szyprowski
2010-11-17  8:39 ` [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator Marek Szyprowski
2010-11-18  7:14   ` Sewoon Park
2010-11-18 15:19     ` Marek Szyprowski
2010-11-17  8:39 ` [PATCH 5/7] v4l: videobuf2: add read() and write() emulator Marek Szyprowski
2010-11-17  8:39 ` [PATCH 6/7] v4l: vivi: port to videobuf2 Marek Szyprowski
2010-11-17  8:39 ` [PATCH 7/7] v4l: videobuf2: add CMA allocator Marek Szyprowski
  -- strict thread matches above, loose matches on Subject: below --
2010-11-19 15:55 [PATCH/RFC v5 0/7] Videobuf2 framework Marek Szyprowski
2010-11-19 15:55 ` [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Marek Szyprowski
2010-10-20  6:41 [PATCH/RFC v3 0/7] Videobuf2 framework Marek Szyprowski
2010-10-20  6:41 ` [PATCH 1/7] v4l: add videobuf2 Video for Linux 2 driver framework Marek Szyprowski
2010-10-25  0:17   ` Pawel Osciak
2010-10-25 10:13     ` Marek Szyprowski

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.