* [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
2010-10-20 6:41 ` [PATCH 2/7] v4l: videobuf2: add generic memory handling routines Marek Szyprowski
` (6 subsequent siblings)
7 siblings, 1 reply; 19+ 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] 19+ 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; 19+ 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] 19+ 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; 19+ 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] 19+ messages in thread
* [PATCH 2/7] v4l: videobuf2: add generic memory handling routines
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-20 6:41 ` Marek Szyprowski
2010-10-20 6:41 ` [PATCH 3/7] v4l: videobuf2: add vmalloc allocator Marek Szyprowski
` (5 subsequent siblings)
7 siblings, 0 replies; 19+ 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>
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 9bf2fe1..2acb0f8 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 e66f53b..77cc798 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -118,6 +118,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] 19+ messages in thread* [PATCH 3/7] v4l: videobuf2: add vmalloc allocator
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-20 6:41 ` [PATCH 2/7] v4l: videobuf2: add generic memory handling routines Marek Szyprowski
@ 2010-10-20 6:41 ` Marek Szyprowski
2010-10-20 6:41 ` [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator Marek Szyprowski
` (4 subsequent siblings)
7 siblings, 0 replies; 19+ 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>
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 2acb0f8..83d49a7 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 77cc798..18f68fc 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -119,6 +119,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] 19+ messages in thread* [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator
2010-10-20 6:41 [PATCH/RFC v3 0/7] Videobuf2 framework Marek Szyprowski
` (2 preceding siblings ...)
2010-10-20 6:41 ` [PATCH 3/7] v4l: videobuf2: add vmalloc allocator Marek Szyprowski
@ 2010-10-20 6:41 ` Marek Szyprowski
2010-10-20 6:41 ` [PATCH 5/7] v4l: videobuf2: add read() emulator Marek Szyprowski
` (3 subsequent siblings)
7 siblings, 0 replies; 19+ 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>
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 | 21 +++
4 files changed, 235 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 83d49a7..2143112 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 18f68fc..b3ae7d4 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -120,6 +120,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..102b1a7
--- /dev/null
+++ b/include/media/videobuf2-dma-coherent.h
@@ -0,0 +1,21 @@
+/*
+ * 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>
+
+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] 19+ messages in thread* [PATCH 5/7] v4l: videobuf2: add read() emulator
2010-10-20 6:41 [PATCH/RFC v3 0/7] Videobuf2 framework Marek Szyprowski
` (3 preceding siblings ...)
2010-10-20 6:41 ` [PATCH 4/7] v4l: videobuf2: add DMA coherent allocator Marek Szyprowski
@ 2010-10-20 6:41 ` Marek Szyprowski
2010-10-25 0:12 ` Pawel Osciak
2010-10-20 6:41 ` [PATCH 6/7] v4l: vivi: port to videobuf2 Marek Szyprowski
` (2 subsequent siblings)
7 siblings, 1 reply; 19+ messages in thread
From: Marek Szyprowski @ 2010-10-20 6:41 UTC (permalink / raw)
To: linux-media; +Cc: m.szyprowski, pawel, kyungmin.park
Add a generic read() 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 read() support to the driver, only one additional
structure should be provides which defines the default number of buffers
used by emulator and detemines the style of read() emulation
('streaming' or 'one shot').
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 | 322 ++++++++++++++++++++++++++++++++--
include/media/videobuf2-core.h | 21 +++
2 files changed, 325 insertions(+), 18 deletions(-)
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 4a29c49..ab00246 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -471,7 +471,7 @@ static bool __buffers_in_use(struct vb2_queue *q)
* 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)
+static int __vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
unsigned int num_buffers, num_planes;
int ret = 0;
@@ -482,8 +482,6 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
return -EINVAL;
}
- mutex_lock(&q->vb_lock);
-
if (req->type != q->type) {
dprintk(1, "reqbufs: queue type invalid\n");
ret = -EINVAL;
@@ -567,6 +565,15 @@ end:
mutex_unlock(&q->vb_lock);
return ret;
}
+
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+ int ret = 0;
+ mutex_lock(&q->vb_lock);
+ ret = (q->read_data) ? -EBUSY : __vb2_reqbufs(q, req);
+ mutex_unlock(&q->vb_lock);
+ return ret;
+}
EXPORT_SYMBOL_GPL(vb2_reqbufs);
/**
@@ -821,14 +828,11 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
* 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)
+static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
struct vb2_buffer *vb;
- int ret;
-
- mutex_lock(&q->vb_lock);
+ int ret = -EINVAL;
- ret = -EINVAL;
if (b->type != q->type) {
dprintk(1, "qbuf: invalid buffer type\n");
goto done;
@@ -887,6 +891,14 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
ret = 0;
done:
+ return ret;
+}
+
+int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ int ret = 0;
+ mutex_lock(&q->vb_lock);
+ ret = (q->read_data) ? -EBUSY : __vb2_qbuf(q, b);
mutex_unlock(&q->vb_lock);
return ret;
}
@@ -996,13 +1008,11 @@ end:
* 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)
+static 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;
@@ -1047,6 +1057,14 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
vb->state = VB2_BUF_STATE_DEQUEUED;
done:
+ return ret;
+}
+
+int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
+{
+ int ret;
+ mutex_lock(&q->vb_lock);
+ ret = (q->read_data) ? -EBUSY : __vb2_dqbuf(q, b, nonblocking);
mutex_unlock(&q->vb_lock);
return ret;
}
@@ -1065,13 +1083,11 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf);
* 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)
+static 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;
@@ -1113,6 +1129,14 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
dprintk(3, "Streamon successful\n");
done:
+ return ret;
+}
+
+int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+ int ret;
+ mutex_lock(&q->vb_lock);
+ ret = (q->read_data) ? -EBUSY : __vb2_streamon(q, type);
mutex_unlock(&q->vb_lock);
return ret;
}
@@ -1161,12 +1185,10 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
* 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)
+static 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;
@@ -1187,6 +1209,14 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
dprintk(3, "Streamoff successful\n");
end:
+ return ret;
+}
+
+int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
+{
+ int ret;
+ mutex_lock(&q->vb_lock);
+ ret = (q->read_data) ? -EBUSY : __vb2_streamoff(q, type);
mutex_unlock(&q->vb_lock);
return ret;
}
@@ -1311,6 +1341,9 @@ bool vb2_has_consumers(struct vb2_queue *q)
}
EXPORT_SYMBOL_GPL(vb2_has_consumers);
+static int __vb2_init_read(struct vb2_queue *q);
+static int __vb2_cleanup_read(struct vb2_queue *q);
+
/**
* vb2_poll() - implements poll userspace operation
* @q: videobuf2 queue
@@ -1336,6 +1369,15 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
mutex_lock(&q->vb_lock);
/*
+ * Start read() emulator if streaming api has not been used yet.
+ */
+ if (q->num_buffers == 0 && q->read_data == NULL && q->read_ctx) {
+ ret = __vb2_init_read(q);
+ if (ret)
+ goto end;
+ }
+
+ /*
* There is nothing to wait for if no buffers have already been queued.
*/
if (list_empty(&q->queued_list)) {
@@ -1378,12 +1420,15 @@ EXPORT_SYMBOL_GPL(vb2_poll);
* 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
+ * @read_ctx: parameters for read() api to be used; can be NULL if no read
+ * callback is used
* @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,
+ const struct vb2_read_ctx *read_ctx,
enum v4l2_buf_type type, void *drv_priv)
{
unsigned int i;
@@ -1403,6 +1448,7 @@ int vb2_queue_init(struct vb2_queue *q, const struct vb2_ops *ops,
for (i = 0; i < VIDEO_MAX_PLANES; ++i)
q->alloc_ctx[i] = alloc_ctx;
+ q->read_ctx = read_ctx;
q->type = type;
q->drv_priv = drv_priv;
@@ -1428,6 +1474,7 @@ void vb2_queue_release(struct vb2_queue *q)
{
mutex_lock(&q->vb_lock);
+ __vb2_cleanup_read(q);
__vb2_queue_cancel(q);
__vb2_queue_free(q);
@@ -1435,6 +1482,245 @@ void vb2_queue_release(struct vb2_queue *q)
}
EXPORT_SYMBOL_GPL(vb2_queue_release);
+/**
+ * struct vb2_read_data - internal structure used by read() emulator
+ *
+ * vb2 provides a compatibility layer and emulator of read() calls on top
+ * of streaming api. For proper operation it required this structure to
+ * save the driver state between each call of the read function.
+ */
+struct vb2_read_data {
+ struct v4l2_requestbuffers req;
+ struct v4l2_buffer b;
+ void *buff_vaddr[VIDEO_MAX_FRAME];
+ unsigned cur_offset;
+ unsigned cur_bufsize;
+ unsigned read_offset;
+ int cur_buffer;
+ int buffer_count;
+};
+
+/**
+ * __vb2_init_read() - initialize read() emulator
+ * @q: videobuf2 queue
+ */
+static int __vb2_init_read(struct vb2_queue *q)
+{
+ struct vb2_read_data *rd;
+ int i, ret;
+
+ if (!q->read_ctx)
+ 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 read emulator
+ */
+ dprintk(3, "setting up read() emulator\n");
+
+ q->read_data = kmalloc(sizeof(struct vb2_read_data), GFP_KERNEL);
+ if (q->read_data == NULL)
+ return -ENOMEM;
+
+ memset(q->read_data, 0, sizeof(*q->read_data));
+ rd = q->read_data;
+
+ /*
+ * Request buffers and use MMAP type to force driver
+ * to allocate buffers by itself.
+ */
+ rd->req.count = q->read_ctx->num_bufs;
+ rd->req.memory = V4L2_MEMORY_MMAP;
+ rd->req.type = q->type;
+ ret = __vb2_reqbufs(q, &rd->req);
+ if (ret)
+ goto err_kfree;
+
+ /*
+ * Check if plane_count is correct
+ * (multiplane buffers are not supported).
+ */
+ if (q->bufs[0]->num_planes != 1) {
+ rd->req.count = 0;
+ ret = -EBUSY;
+ goto err_reqbufs;
+ }
+
+ /*
+ * Get kernel address of each buffer.
+ */
+ for (i = 0; i < q->num_buffers; i++)
+ rd->buff_vaddr[i] = vb2_plane_vaddr(q->bufs[i], 0);
+
+ /*
+ * Queue all buffers.
+ */
+ for (i = 0; i < q->num_buffers; i++) {
+ memset(&rd->b, 0, sizeof(rd->b));
+ rd->b.type = q->type;
+ rd->b.memory = q->memory;
+ rd->b.index = i;
+ ret = __vb2_qbuf(q, &rd->b);
+ if (ret)
+ goto err_reqbufs;
+ }
+
+ /*
+ * Start streaming.
+ */
+ ret = __vb2_streamon(q, q->type);
+ if (ret)
+ goto err_reqbufs;
+
+ return ret;
+
+err_reqbufs:
+ __vb2_reqbufs(q, &rd->req);
+
+err_kfree:
+ kfree(q->read_data);
+ return ret;
+}
+
+/**
+ * __vb2_cleanup_read() - free resourced used by read() emulator
+ * @q: videobuf2 queue
+ */
+static int __vb2_cleanup_read(struct vb2_queue *q)
+{
+ struct vb2_read_data *rd = q->read_data;
+ if (rd) {
+ __vb2_streamoff(q, q->type);
+ rd->req.count = 0;
+ __vb2_reqbufs(q, &rd->req);
+ kfree(rd);
+ q->read_data = NULL;
+ dprintk(3, "read() emulator released\n");
+ }
+ return 0;
+}
+
+size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
+{
+ struct vb2_read_data *rd;
+ int ret;
+
+ dprintk(3, "read: offset %ld, count %d, %sblocking\n", (long)*ppos,
+ count, nonblocking ? "non" : "");
+
+ if (!data)
+ return -EINVAL;
+
+ mutex_lock(&q->vb_lock);
+
+ /*
+ * Initialize emulator on first read() call.
+ */
+ if (!q->read_data) {
+ ret = __vb2_init_read(q);
+ dprintk(3, "read: vb2_init_read result: %d\n", ret);
+ if (ret)
+ goto end;
+ }
+
+ rd = q->read_data;
+
+ /*
+ * Current buffer is empty...
+ */
+ if (rd->cur_offset == 0 && rd->cur_bufsize == 0) {
+ /*
+ * ... check if this was the last buffer to read.
+ */
+ if (q->read_ctx->read_once &&
+ rd->buffer_count == q->read_ctx->num_bufs) {
+ ret = __vb2_cleanup_read(q);
+ goto end;
+ }
+
+ /*
+ * ... or call vb2_dqbuf to get next buffer with data.
+ */
+ memset(&rd->b, 0, sizeof(rd->b));
+ rd->b.type = q->type;
+ rd->b.memory = q->memory;
+ rd->b.index = rd->cur_buffer;
+ ret = __vb2_dqbuf(q, &rd->b, nonblocking);
+ dprintk(5, "read: vb2_dqbuf result: %d\n", ret);
+ if (ret)
+ goto end;
+ rd->buffer_count += 1;
+ rd->cur_bufsize = rd->b.length;
+ }
+
+ /*
+ * Limit count on last few bytes of the buffer.
+ */
+ if (count + rd->cur_offset > rd->cur_bufsize) {
+ count = rd->cur_bufsize - rd->cur_offset;
+ dprintk(5, "reducing read count: %d\n", count);
+ }
+
+ /*
+ * Transfer data to userspace.
+ */
+ dprintk(3, "read: copying %d data from buffer %d (offset %d bytes)\n",
+ count, rd->cur_buffer, rd->cur_offset);
+ if (copy_to_user(data,
+ rd->buff_vaddr[rd->cur_buffer] + rd->cur_offset,
+ count)) {
+ dprintk(3, "read: error copying data\n");
+ ret = -EFAULT;
+ goto end;
+ }
+
+ /*
+ * Update counters.
+ */
+ rd->cur_offset += count;
+ rd->read_offset += count;
+ *ppos += count;
+
+ /*
+ * Queue next buffer if required.
+ */
+ if (rd->cur_offset == rd->cur_bufsize) {
+ memset(&rd->b, 0, sizeof(rd->b));
+ rd->b.type = q->type;
+ rd->b.memory = q->memory;
+ rd->b.index = rd->cur_buffer;
+ ret = __vb2_qbuf(q, &rd->b);
+ dprintk(5, "read: vb2_dbuf result: %d\n", ret);
+ if (ret)
+ goto end;
+ rd->cur_buffer = (rd->cur_buffer + 1) % q->num_buffers;
+ rd->cur_offset = 0;
+ rd->cur_bufsize = 0;
+ }
+
+ /*
+ * Return proper number of bytes read.
+ */
+ if (ret == 0)
+ ret = count;
+end:
+ mutex_unlock(&q->vb_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vb2_read);
+
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 76d0b4e..8c1ce06 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_read_data;
/**
* struct vb2_mem_ops - memory handling/memory allocator operations
@@ -210,6 +211,18 @@ struct vb2_ops {
};
/**
+ * struct vb2_read_ctx - defaults for read() callback emulator
+ *
+ * @num_bufs: number of buffers used by the emulator
+ * @read_once: determines weather read emulator return EOF after processing
+ * @num_bufs or queues buffer again for new data
+ */
+struct vb2_read_ctx {
+ int num_bufs;
+ int read_once;
+};
+
+/**
* struct vb2_queue - a videobuf queue
*
* @type: current queue type
@@ -227,6 +240,8 @@ struct vb2_ops {
* @streaming: current streaming state
* @userptr_supported: true if queue supports USERPTR types
* @mmap_supported: true if queue supports MMAP types
+ * @read_ctx: defaults for read() emulator, can be NULL
+ * @read_data: read() emulator internal data, used only if emulator is active
*/
struct vb2_queue {
enum v4l2_buf_type type;
@@ -250,6 +265,9 @@ struct vb2_queue {
int streaming:1;
int userptr_supported:1;
int mmap_supported:1;
+
+ const struct vb2_read_ctx *read_ctx;
+ struct vb2_read_data *read_data;
};
void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
@@ -262,6 +280,7 @@ 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,
+ const struct vb2_read_ctx *read_ctx,
enum v4l2_buf_type type, void *drv_priv);
void vb2_queue_release(struct vb2_queue *q);
@@ -273,6 +292,8 @@ 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 nonblocking);
/**
* vb2_is_streaming() - return streaming status of the queue
--
1.7.1.569.g6f426
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH 5/7] v4l: videobuf2: add read() emulator
2010-10-20 6:41 ` [PATCH 5/7] v4l: videobuf2: add read() emulator Marek Szyprowski
@ 2010-10-25 0:12 ` Pawel Osciak
2010-10-25 10:52 ` Marek Szyprowski
0 siblings, 1 reply; 19+ messages in thread
From: Pawel Osciak @ 2010-10-25 0:12 UTC (permalink / raw)
To: Marek Szyprowski; +Cc: linux-media, kyungmin.park
Hi Marek,
This is a pretty crafty patch, you've managed to make it nice and
clean, without adding complexity to streaming code. Nice job. A few of
my comments below.
On Tue, Oct 19, 2010 at 23:41, Marek Szyprowski
<m.szyprowski@samsung.com> wrote:
> Add a generic read() 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 read() support to the driver, only one additional
> structure should be provides which defines the default number of buffers
> used by emulator and detemines the style of read() emulation
> ('streaming' or 'one shot').
>
> 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 | 322 ++++++++++++++++++++++++++++++++--
> include/media/videobuf2-core.h | 21 +++
> 2 files changed, 325 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
> index 4a29c49..ab00246 100644
> --- a/drivers/media/video/videobuf2-core.c
> +++ b/drivers/media/video/videobuf2-core.c
> @@ -471,7 +471,7 @@ static bool __buffers_in_use(struct vb2_queue *q)
> * 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)
> +static int __vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> {
> unsigned int num_buffers, num_planes;
> int ret = 0;
> @@ -482,8 +482,6 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> return -EINVAL;
> }
>
> - mutex_lock(&q->vb_lock);
> -
> if (req->type != q->type) {
> dprintk(1, "reqbufs: queue type invalid\n");
> ret = -EINVAL;
> @@ -567,6 +565,15 @@ end:
> mutex_unlock(&q->vb_lock);
> return ret;
> }
> +
> +int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> +{
> + int ret = 0;
> + mutex_lock(&q->vb_lock);
> + ret = (q->read_data) ? -EBUSY : __vb2_reqbufs(q, req);
> + mutex_unlock(&q->vb_lock);
> + return ret;
> +}
> EXPORT_SYMBOL_GPL(vb2_reqbufs);
>
> /**
> @@ -821,14 +828,11 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
> * 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)
> +static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
> {
> struct vb2_buffer *vb;
> - int ret;
> -
> - mutex_lock(&q->vb_lock);
> + int ret = -EINVAL;
>
> - ret = -EINVAL;
> if (b->type != q->type) {
> dprintk(1, "qbuf: invalid buffer type\n");
> goto done;
> @@ -887,6 +891,14 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
> dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
> ret = 0;
> done:
> + return ret;
> +}
> +
> +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
> +{
> + int ret = 0;
> + mutex_lock(&q->vb_lock);
> + ret = (q->read_data) ? -EBUSY : __vb2_qbuf(q, b);
> mutex_unlock(&q->vb_lock);
> return ret;
> }
> @@ -996,13 +1008,11 @@ end:
> * 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)
> +static 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;
> @@ -1047,6 +1057,14 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
> vb->state = VB2_BUF_STATE_DEQUEUED;
>
> done:
> + return ret;
> +}
> +
> +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
> +{
> + int ret;
> + mutex_lock(&q->vb_lock);
> + ret = (q->read_data) ? -EBUSY : __vb2_dqbuf(q, b, nonblocking);
> mutex_unlock(&q->vb_lock);
> return ret;
> }
> @@ -1065,13 +1083,11 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf);
> * 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)
> +static 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;
> @@ -1113,6 +1129,14 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
>
> dprintk(3, "Streamon successful\n");
> done:
> + return ret;
> +}
> +
> +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
> +{
> + int ret;
> + mutex_lock(&q->vb_lock);
> + ret = (q->read_data) ? -EBUSY : __vb2_streamon(q, type);
> mutex_unlock(&q->vb_lock);
> return ret;
> }
> @@ -1161,12 +1185,10 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
> * 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)
> +static 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;
> @@ -1187,6 +1209,14 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
>
> dprintk(3, "Streamoff successful\n");
> end:
> + return ret;
> +}
> +
> +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
> +{
> + int ret;
> + mutex_lock(&q->vb_lock);
> + ret = (q->read_data) ? -EBUSY : __vb2_streamoff(q, type);
> mutex_unlock(&q->vb_lock);
> return ret;
> }
> @@ -1311,6 +1341,9 @@ bool vb2_has_consumers(struct vb2_queue *q)
> }
> EXPORT_SYMBOL_GPL(vb2_has_consumers);
>
> +static int __vb2_init_read(struct vb2_queue *q);
> +static int __vb2_cleanup_read(struct vb2_queue *q);
> +
> /**
> * vb2_poll() - implements poll userspace operation
> * @q: videobuf2 queue
> @@ -1336,6 +1369,15 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
> mutex_lock(&q->vb_lock);
>
> /*
> + * Start read() emulator if streaming api has not been used yet.
> + */
> + if (q->num_buffers == 0 && q->read_data == NULL && q->read_ctx) {
> + ret = __vb2_init_read(q);
> + if (ret)
> + goto end;
> + }
> +
> + /*
> * There is nothing to wait for if no buffers have already been queued.
> */
> if (list_empty(&q->queued_list)) {
> @@ -1378,12 +1420,15 @@ EXPORT_SYMBOL_GPL(vb2_poll);
> * 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
> + * @read_ctx: parameters for read() api to be used; can be NULL if no read
> + * callback is used
> * @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,
> + const struct vb2_read_ctx *read_ctx,
> enum v4l2_buf_type type, void *drv_priv)
> {
> unsigned int i;
> @@ -1403,6 +1448,7 @@ int vb2_queue_init(struct vb2_queue *q, const struct vb2_ops *ops,
> for (i = 0; i < VIDEO_MAX_PLANES; ++i)
> q->alloc_ctx[i] = alloc_ctx;
>
> + q->read_ctx = read_ctx;
> q->type = type;
> q->drv_priv = drv_priv;
>
> @@ -1428,6 +1474,7 @@ void vb2_queue_release(struct vb2_queue *q)
> {
> mutex_lock(&q->vb_lock);
>
> + __vb2_cleanup_read(q);
> __vb2_queue_cancel(q);
> __vb2_queue_free(q);
>
> @@ -1435,6 +1482,245 @@ void vb2_queue_release(struct vb2_queue *q)
> }
> EXPORT_SYMBOL_GPL(vb2_queue_release);
>
> +/**
> + * struct vb2_read_data - internal structure used by read() emulator
> + *
> + * vb2 provides a compatibility layer and emulator of read() calls on top
> + * of streaming api. For proper operation it required this structure to
> + * save the driver state between each call of the read function.
> + */
> +struct vb2_read_data {
> + struct v4l2_requestbuffers req;
> + struct v4l2_buffer b;
> + void *buff_vaddr[VIDEO_MAX_FRAME];
> + unsigned cur_offset;
> + unsigned cur_bufsize;
> + unsigned read_offset;
> + int cur_buffer;
> + int buffer_count;
> +};
> +
> +/**
> + * __vb2_init_read() - initialize read() emulator
> + * @q: videobuf2 queue
> + */
> +static int __vb2_init_read(struct vb2_queue *q)
> +{
> + struct vb2_read_data *rd;
> + int i, ret;
> +
> + if (!q->read_ctx)
> + 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 read emulator
> + */
> + dprintk(3, "setting up read() emulator\n");
> +
> + q->read_data = kmalloc(sizeof(struct vb2_read_data), GFP_KERNEL);
> + if (q->read_data == NULL)
> + return -ENOMEM;
> +
> + memset(q->read_data, 0, sizeof(*q->read_data));
kzalloc() ?
> + rd = q->read_data;
> +
> + /*
> + * Request buffers and use MMAP type to force driver
> + * to allocate buffers by itself.
> + */
> + rd->req.count = q->read_ctx->num_bufs;
> + rd->req.memory = V4L2_MEMORY_MMAP;
> + rd->req.type = q->type;
> + ret = __vb2_reqbufs(q, &rd->req);
> + if (ret)
> + goto err_kfree;
> +
> + /*
> + * Check if plane_count is correct
> + * (multiplane buffers are not supported).
> + */
> + if (q->bufs[0]->num_planes != 1) {
> + rd->req.count = 0;
> + ret = -EBUSY;
> + goto err_reqbufs;
> + }
> +
> + /*
> + * Get kernel address of each buffer.
> + */
> + for (i = 0; i < q->num_buffers; i++)
> + rd->buff_vaddr[i] = vb2_plane_vaddr(q->bufs[i], 0);
> +
Even if the driver provided a vaddr() callback, it may still return a
NULL. I'd be better to verify that here.
> + /*
> + * Queue all buffers.
> + */
> + for (i = 0; i < q->num_buffers; i++) {
> + memset(&rd->b, 0, sizeof(rd->b));
> + rd->b.type = q->type;
> + rd->b.memory = q->memory;
> + rd->b.index = i;
> + ret = __vb2_qbuf(q, &rd->b);
> + if (ret)
> + goto err_reqbufs;
> + }
> +
> + /*
> + * Start streaming.
> + */
> + ret = __vb2_streamon(q, q->type);
> + if (ret)
> + goto err_reqbufs;
> +
> + return ret;
> +
> +err_reqbufs:
> + __vb2_reqbufs(q, &rd->req);
> +
> +err_kfree:
> + kfree(q->read_data);
> + return ret;
> +}
> +
> +/**
> + * __vb2_cleanup_read() - free resourced used by read() emulator
> + * @q: videobuf2 queue
> + */
> +static int __vb2_cleanup_read(struct vb2_queue *q)
> +{
> + struct vb2_read_data *rd = q->read_data;
> + if (rd) {
> + __vb2_streamoff(q, q->type);
> + rd->req.count = 0;
> + __vb2_reqbufs(q, &rd->req);
> + kfree(rd);
> + q->read_data = NULL;
> + dprintk(3, "read() emulator released\n");
> + }
> + return 0;
> +}
> +
> +size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
> + loff_t *ppos, int nonblocking)
> +{
> + struct vb2_read_data *rd;
> + int ret;
> +
> + dprintk(3, "read: offset %ld, count %d, %sblocking\n", (long)*ppos,
> + count, nonblocking ? "non" : "");
> +
> + if (!data)
> + return -EINVAL;
> +
> + mutex_lock(&q->vb_lock);
> +
> + /*
> + * Initialize emulator on first read() call.
> + */
> + if (!q->read_data) {
> + ret = __vb2_init_read(q);
> + dprintk(3, "read: vb2_init_read result: %d\n", ret);
> + if (ret)
> + goto end;
> + }
> +
> + rd = q->read_data;
> +
> + /*
> + * Current buffer is empty...
> + */
> + if (rd->cur_offset == 0 && rd->cur_bufsize == 0) {
> + /*
> + * ... check if this was the last buffer to read.
> + */
> + if (q->read_ctx->read_once &&
> + rd->buffer_count == q->read_ctx->num_bufs) {
> + ret = __vb2_cleanup_read(q);
> + goto end;
> + }
> +
> + /*
> + * ... or call vb2_dqbuf to get next buffer with data.
> + */
> + memset(&rd->b, 0, sizeof(rd->b));
> + rd->b.type = q->type;
> + rd->b.memory = q->memory;
> + rd->b.index = rd->cur_buffer;
> + ret = __vb2_dqbuf(q, &rd->b, nonblocking);
> + dprintk(5, "read: vb2_dqbuf result: %d\n", ret);
> + if (ret)
> + goto end;
> + rd->buffer_count += 1;
> + rd->cur_bufsize = rd->b.length;
This is tricky. Since you are supporting both non-multiplanar types
and 1-plane multiplanar, in the latter case this value will be in
b.m.planes[0]->length instead.
> + }
> +
> + /*
> + * Limit count on last few bytes of the buffer.
> + */
This comment is confusing. Maybe something like "If the user requested
more bytes than we can fit in buffer, reduce that number" would be
better?
> + if (count + rd->cur_offset > rd->cur_bufsize) {
> + count = rd->cur_bufsize - rd->cur_offset;
> + dprintk(5, "reducing read count: %d\n", count);
> + }
> +
> + /*
> + * Transfer data to userspace.
> + */
> + dprintk(3, "read: copying %d data from buffer %d (offset %d bytes)\n",
> + count, rd->cur_buffer, rd->cur_offset);
> + if (copy_to_user(data,
> + rd->buff_vaddr[rd->cur_buffer] + rd->cur_offset,
> + count)) {
> + dprintk(3, "read: error copying data\n");
> + ret = -EFAULT;
> + goto end;
> + }
> +
> + /*
> + * Update counters.
> + */
> + rd->cur_offset += count;
> + rd->read_offset += count;
> + *ppos += count;
> +
> + /*
> + * Queue next buffer if required.
> + */
> + if (rd->cur_offset == rd->cur_bufsize) {
This should actually be rd->cur_offset == rd->b.planes[0].bytesused
for 1-plane multiplanar and rd->b.bytesused for non-multiplanar. (See
also my comment about length above). You want to get the size of
actual video data, which may be smaller than buffer size.
By the way, vb2 internally always stores those values in planes[0],
even for non-multiplanar types, which makes things more consistent,
but you are using dqbuf and everything gets moved back to v4l2_buffer
struct there.
> + memset(&rd->b, 0, sizeof(rd->b));
> + rd->b.type = q->type;
> + rd->b.memory = q->memory;
> + rd->b.index = rd->cur_buffer;
> + ret = __vb2_qbuf(q, &rd->b);
> + dprintk(5, "read: vb2_dbuf result: %d\n", ret);
> + if (ret)
> + goto end;
> + rd->cur_buffer = (rd->cur_buffer + 1) % q->num_buffers;
> + rd->cur_offset = 0;
> + rd->cur_bufsize = 0;
> + }
> +
> + /*
> + * Return proper number of bytes read.
> + */
> + if (ret == 0)
> + ret = count;
> +end:
> + mutex_unlock(&q->vb_lock);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(vb2_read);
> +
> 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 76d0b4e..8c1ce06 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_read_data;
>
> /**
> * struct vb2_mem_ops - memory handling/memory allocator operations
> @@ -210,6 +211,18 @@ struct vb2_ops {
> };
>
> /**
> + * struct vb2_read_ctx - defaults for read() callback emulator
> + *
> + * @num_bufs: number of buffers used by the emulator
> + * @read_once: determines weather read emulator return EOF after processing
> + * @num_bufs or queues buffer again for new data
> + */
> +struct vb2_read_ctx {
> + int num_bufs;
> + int read_once;
> +};
> +
> +/**
> * struct vb2_queue - a videobuf queue
> *
> * @type: current queue type
> @@ -227,6 +240,8 @@ struct vb2_ops {
> * @streaming: current streaming state
> * @userptr_supported: true if queue supports USERPTR types
> * @mmap_supported: true if queue supports MMAP types
> + * @read_ctx: defaults for read() emulator, can be NULL
> + * @read_data: read() emulator internal data, used only if emulator is active
> */
> struct vb2_queue {
> enum v4l2_buf_type type;
> @@ -250,6 +265,9 @@ struct vb2_queue {
> int streaming:1;
> int userptr_supported:1;
> int mmap_supported:1;
> +
> + const struct vb2_read_ctx *read_ctx;
> + struct vb2_read_data *read_data;
> };
>
> void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
> @@ -262,6 +280,7 @@ 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,
> + const struct vb2_read_ctx *read_ctx,
> enum v4l2_buf_type type, void *drv_priv);
> void vb2_queue_release(struct vb2_queue *q);
>
> @@ -273,6 +292,8 @@ 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 nonblocking);
>
> /**
> * vb2_is_streaming() - return streaming status of the queue
> --
> 1.7.1.569.g6f426
>
>
--
Best regards,
Pawel Osciak
^ permalink raw reply [flat|nested] 19+ messages in thread* RE: [PATCH 5/7] v4l: videobuf2: add read() emulator
2010-10-25 0:12 ` Pawel Osciak
@ 2010-10-25 10:52 ` Marek Szyprowski
0 siblings, 0 replies; 19+ messages in thread
From: Marek Szyprowski @ 2010-10-25 10:52 UTC (permalink / raw)
To: 'Pawel Osciak'; +Cc: linux-media, kyungmin.park
Hello,
On Monday, October 25, 2010 2:13 AM Pawel Osciak wrote:
> Hi Marek,
> This is a pretty crafty patch, you've managed to make it nice and
> clean, without adding complexity to streaming code. Nice job. A few of
> my comments below.
Thanks!
> On Tue, Oct 19, 2010 at 23:41, Marek Szyprowski
> <m.szyprowski@samsung.com> wrote:
> > Add a generic read() 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 read() support to the driver, only one additional
> > structure should be provides which defines the default number of buffers
> > used by emulator and detemines the style of read() emulation
> > ('streaming' or 'one shot').
> >
> > 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 | 322 ++++++++++++++++++++++++++++++++--
> > include/media/videobuf2-core.h | 21 +++
> > 2 files changed, 325 insertions(+), 18 deletions(-)
> >
> > diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
> > index 4a29c49..ab00246 100644
> > --- a/drivers/media/video/videobuf2-core.c
> > +++ b/drivers/media/video/videobuf2-core.c
> > @@ -471,7 +471,7 @@ static bool __buffers_in_use(struct vb2_queue *q)
> > * 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)
> > +static int __vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> > {
> > unsigned int num_buffers, num_planes;
> > int ret = 0;
> > @@ -482,8 +482,6 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> > return -EINVAL;
> > }
> >
> > - mutex_lock(&q->vb_lock);
> > -
> > if (req->type != q->type) {
> > dprintk(1, "reqbufs: queue type invalid\n");
> > ret = -EINVAL;
> > @@ -567,6 +565,15 @@ end:
> > mutex_unlock(&q->vb_lock);
> > return ret;
> > }
> > +
> > +int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
> > +{
> > + int ret = 0;
> > + mutex_lock(&q->vb_lock);
> > + ret = (q->read_data) ? -EBUSY : __vb2_reqbufs(q, req);
> > + mutex_unlock(&q->vb_lock);
> > + return ret;
> > +}
> > EXPORT_SYMBOL_GPL(vb2_reqbufs);
> >
> > /**
> > @@ -821,14 +828,11 @@ static void __enqueue_in_driver(struct vb2_buffer *vb)
> > * 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)
> > +static int __vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
> > {
> > struct vb2_buffer *vb;
> > - int ret;
> > -
> > - mutex_lock(&q->vb_lock);
> > + int ret = -EINVAL;
> >
> > - ret = -EINVAL;
> > if (b->type != q->type) {
> > dprintk(1, "qbuf: invalid buffer type\n");
> > goto done;
> > @@ -887,6 +891,14 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
> > dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
> > ret = 0;
> > done:
> > + return ret;
> > +}
> > +
> > +int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
> > +{
> > + int ret = 0;
> > + mutex_lock(&q->vb_lock);
> > + ret = (q->read_data) ? -EBUSY : __vb2_qbuf(q, b);
> > mutex_unlock(&q->vb_lock);
> > return ret;
> > }
> > @@ -996,13 +1008,11 @@ end:
> > * 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)
> > +static 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;
> > @@ -1047,6 +1057,14 @@ int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
> > vb->state = VB2_BUF_STATE_DEQUEUED;
> >
> > done:
> > + return ret;
> > +}
> > +
> > +int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
> > +{
> > + int ret;
> > + mutex_lock(&q->vb_lock);
> > + ret = (q->read_data) ? -EBUSY : __vb2_dqbuf(q, b, nonblocking);
> > mutex_unlock(&q->vb_lock);
> > return ret;
> > }
> > @@ -1065,13 +1083,11 @@ EXPORT_SYMBOL_GPL(vb2_dqbuf);
> > * 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)
> > +static 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;
> > @@ -1113,6 +1129,14 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
> >
> > dprintk(3, "Streamon successful\n");
> > done:
> > + return ret;
> > +}
> > +
> > +int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
> > +{
> > + int ret;
> > + mutex_lock(&q->vb_lock);
> > + ret = (q->read_data) ? -EBUSY : __vb2_streamon(q, type);
> > mutex_unlock(&q->vb_lock);
> > return ret;
> > }
> > @@ -1161,12 +1185,10 @@ static void __vb2_queue_cancel(struct vb2_queue *q)
> > * 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)
> > +static 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;
> > @@ -1187,6 +1209,14 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
> >
> > dprintk(3, "Streamoff successful\n");
> > end:
> > + return ret;
> > +}
> > +
> > +int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type)
> > +{
> > + int ret;
> > + mutex_lock(&q->vb_lock);
> > + ret = (q->read_data) ? -EBUSY : __vb2_streamoff(q, type);
> > mutex_unlock(&q->vb_lock);
> > return ret;
> > }
> > @@ -1311,6 +1341,9 @@ bool vb2_has_consumers(struct vb2_queue *q)
> > }
> > EXPORT_SYMBOL_GPL(vb2_has_consumers);
> >
> > +static int __vb2_init_read(struct vb2_queue *q);
> > +static int __vb2_cleanup_read(struct vb2_queue *q);
> > +
> > /**
> > * vb2_poll() - implements poll userspace operation
> > * @q: videobuf2 queue
> > @@ -1336,6 +1369,15 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table
> *wait)
> > mutex_lock(&q->vb_lock);
> >
> > /*
> > + * Start read() emulator if streaming api has not been used yet.
> > + */
> > + if (q->num_buffers == 0 && q->read_data == NULL && q->read_ctx) {
> > + ret = __vb2_init_read(q);
> > + if (ret)
> > + goto end;
> > + }
> > +
> > + /*
> > * There is nothing to wait for if no buffers have already been queued.
> > */
> > if (list_empty(&q->queued_list)) {
> > @@ -1378,12 +1420,15 @@ EXPORT_SYMBOL_GPL(vb2_poll);
> > * 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
> > + * @read_ctx: parameters for read() api to be used; can be NULL if no read
> > + * callback is used
> > * @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,
> > + const struct vb2_read_ctx *read_ctx,
> > enum v4l2_buf_type type, void *drv_priv)
> > {
> > unsigned int i;
> > @@ -1403,6 +1448,7 @@ int vb2_queue_init(struct vb2_queue *q, const struct vb2_ops *ops,
> > for (i = 0; i < VIDEO_MAX_PLANES; ++i)
> > q->alloc_ctx[i] = alloc_ctx;
> >
> > + q->read_ctx = read_ctx;
> > q->type = type;
> > q->drv_priv = drv_priv;
> >
> > @@ -1428,6 +1474,7 @@ void vb2_queue_release(struct vb2_queue *q)
> > {
> > mutex_lock(&q->vb_lock);
> >
> > + __vb2_cleanup_read(q);
> > __vb2_queue_cancel(q);
> > __vb2_queue_free(q);
> >
> > @@ -1435,6 +1482,245 @@ void vb2_queue_release(struct vb2_queue *q)
> > }
> > EXPORT_SYMBOL_GPL(vb2_queue_release);
> >
> > +/**
> > + * struct vb2_read_data - internal structure used by read() emulator
> > + *
> > + * vb2 provides a compatibility layer and emulator of read() calls on top
> > + * of streaming api. For proper operation it required this structure to
> > + * save the driver state between each call of the read function.
> > + */
> > +struct vb2_read_data {
> > + struct v4l2_requestbuffers req;
> > + struct v4l2_buffer b;
> > + void *buff_vaddr[VIDEO_MAX_FRAME];
> > + unsigned cur_offset;
> > + unsigned cur_bufsize;
> > + unsigned read_offset;
> > + int cur_buffer;
> > + int buffer_count;
> > +};
> > +
> > +/**
> > + * __vb2_init_read() - initialize read() emulator
> > + * @q: videobuf2 queue
> > + */
> > +static int __vb2_init_read(struct vb2_queue *q)
> > +{
> > + struct vb2_read_data *rd;
> > + int i, ret;
> > +
> > + if (!q->read_ctx)
> > + 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 read emulator
> > + */
> > + dprintk(3, "setting up read() emulator\n");
> > +
> > + q->read_data = kmalloc(sizeof(struct vb2_read_data), GFP_KERNEL);
> > + if (q->read_data == NULL)
> > + return -ENOMEM;
> > +
> > + memset(q->read_data, 0, sizeof(*q->read_data));
>
> kzalloc() ?
Right
>
> > + rd = q->read_data;
> > +
> > + /*
> > + * Request buffers and use MMAP type to force driver
> > + * to allocate buffers by itself.
> > + */
> > + rd->req.count = q->read_ctx->num_bufs;
> > + rd->req.memory = V4L2_MEMORY_MMAP;
> > + rd->req.type = q->type;
> > + ret = __vb2_reqbufs(q, &rd->req);
> > + if (ret)
> > + goto err_kfree;
> > +
> > + /*
> > + * Check if plane_count is correct
> > + * (multiplane buffers are not supported).
> > + */
> > + if (q->bufs[0]->num_planes != 1) {
> > + rd->req.count = 0;
> > + ret = -EBUSY;
> > + goto err_reqbufs;
> > + }
> > +
> > + /*
> > + * Get kernel address of each buffer.
> > + */
> > + for (i = 0; i < q->num_buffers; i++)
> > + rd->buff_vaddr[i] = vb2_plane_vaddr(q->bufs[i], 0);
> > +
>
> Even if the driver provided a vaddr() callback, it may still return a
> NULL. I'd be better to verify that here.
Right, I will fix this.
> > + /*
> > + * Queue all buffers.
> > + */
> > + for (i = 0; i < q->num_buffers; i++) {
> > + memset(&rd->b, 0, sizeof(rd->b));
> > + rd->b.type = q->type;
> > + rd->b.memory = q->memory;
> > + rd->b.index = i;
> > + ret = __vb2_qbuf(q, &rd->b);
> > + if (ret)
> > + goto err_reqbufs;
> > + }
> > +
> > + /*
> > + * Start streaming.
> > + */
> > + ret = __vb2_streamon(q, q->type);
> > + if (ret)
> > + goto err_reqbufs;
> > +
> > + return ret;
> > +
> > +err_reqbufs:
> > + __vb2_reqbufs(q, &rd->req);
> > +
> > +err_kfree:
> > + kfree(q->read_data);
> > + return ret;
> > +}
> > +
> > +/**
> > + * __vb2_cleanup_read() - free resourced used by read() emulator
> > + * @q: videobuf2 queue
> > + */
> > +static int __vb2_cleanup_read(struct vb2_queue *q)
> > +{
> > + struct vb2_read_data *rd = q->read_data;
> > + if (rd) {
> > + __vb2_streamoff(q, q->type);
> > + rd->req.count = 0;
> > + __vb2_reqbufs(q, &rd->req);
> > + kfree(rd);
> > + q->read_data = NULL;
> > + dprintk(3, "read() emulator released\n");
> > + }
> > + return 0;
> > +}
> > +
> > +size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
> > + loff_t *ppos, int nonblocking)
> > +{
> > + struct vb2_read_data *rd;
> > + int ret;
> > +
> > + dprintk(3, "read: offset %ld, count %d, %sblocking\n", (long)*ppos,
> > + count, nonblocking ? "non" : "");
> > +
> > + if (!data)
> > + return -EINVAL;
> > +
> > + mutex_lock(&q->vb_lock);
> > +
> > + /*
> > + * Initialize emulator on first read() call.
> > + */
> > + if (!q->read_data) {
> > + ret = __vb2_init_read(q);
> > + dprintk(3, "read: vb2_init_read result: %d\n", ret);
> > + if (ret)
> > + goto end;
> > + }
> > +
> > + rd = q->read_data;
> > +
> > + /*
> > + * Current buffer is empty...
> > + */
> > + if (rd->cur_offset == 0 && rd->cur_bufsize == 0) {
> > + /*
> > + * ... check if this was the last buffer to read.
> > + */
> > + if (q->read_ctx->read_once &&
> > + rd->buffer_count == q->read_ctx->num_bufs) {
> > + ret = __vb2_cleanup_read(q);
> > + goto end;
> > + }
> > +
> > + /*
> > + * ... or call vb2_dqbuf to get next buffer with data.
> > + */
> > + memset(&rd->b, 0, sizeof(rd->b));
> > + rd->b.type = q->type;
> > + rd->b.memory = q->memory;
> > + rd->b.index = rd->cur_buffer;
> > + ret = __vb2_dqbuf(q, &rd->b, nonblocking);
> > + dprintk(5, "read: vb2_dqbuf result: %d\n", ret);
> > + if (ret)
> > + goto end;
> > + rd->buffer_count += 1;
> > + rd->cur_bufsize = rd->b.length;
>
> This is tricky. Since you are supporting both non-multiplanar types
> and 1-plane multiplanar, in the latter case this value will be in
> b.m.planes[0]->length instead.
Well, in fact I should use b.bytesused here. I will update the code to read
it from q->bufs->planes[0].bytesused.
>
> > + }
> > +
> > + /*
> > + * Limit count on last few bytes of the buffer.
> > + */
>
> This comment is confusing. Maybe something like "If the user requested
> more bytes than we can fit in buffer, reduce that number" would be
> better?
OK
> > + if (count + rd->cur_offset > rd->cur_bufsize) {
> > + count = rd->cur_bufsize - rd->cur_offset;
> > + dprintk(5, "reducing read count: %d\n", count);
> > + }
> > +
> > + /*
> > + * Transfer data to userspace.
> > + */
> > + dprintk(3, "read: copying %d data from buffer %d (offset %d bytes)\n",
> > + count, rd->cur_buffer, rd->cur_offset);
> > + if (copy_to_user(data,
> > + rd->buff_vaddr[rd->cur_buffer] + rd->cur_offset,
> > + count)) {
> > + dprintk(3, "read: error copying data\n");
> > + ret = -EFAULT;
> > + goto end;
> > + }
> > +
> > + /*
> > + * Update counters.
> > + */
> > + rd->cur_offset += count;
> > + rd->read_offset += count;
> > + *ppos += count;
> > +
> > + /*
> > + * Queue next buffer if required.
> > + */
> > + if (rd->cur_offset == rd->cur_bufsize) {
>
> This should actually be rd->cur_offset == rd->b.planes[0].bytesused
> for 1-plane multiplanar and rd->b.bytesused for non-multiplanar. (See
> also my comment about length above). You want to get the size of
> actual video data, which may be smaller than buffer size.
Right, I used wrong field here.
> By the way, vb2 internally always stores those values in planes[0],
> even for non-multiplanar types, which makes things more consistent,
> but you are using dqbuf and everything gets moved back to v4l2_buffer
> struct there.
Right, I will use it then to simplify the code;
> <snip>
Best regards
--
Marek Szyprowski
Samsung Poland R&D Center
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH 6/7] v4l: vivi: port to videobuf2
2010-10-20 6:41 [PATCH/RFC v3 0/7] Videobuf2 framework Marek Szyprowski
` (4 preceding siblings ...)
2010-10-20 6:41 ` [PATCH 5/7] v4l: videobuf2: add read() emulator Marek Szyprowski
@ 2010-10-20 6:41 ` Marek Szyprowski
2010-10-20 6:41 ` [PATCH 7/7] v4l: videobuf2: add CMA allocator Marek Szyprowski
2010-10-20 7:14 ` [PATCH/RFC v3 0/7] Videobuf2 framework Hans Verkuil
7 siblings, 0 replies; 19+ 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>
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 | 348 +++++++++++++++++++++++--------------------
2 files changed, 186 insertions(+), 164 deletions(-)
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 2143112..4dd14c2 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) && FONTS
select FONT_8x16
- select VIDEOBUF_VMALLOC
+ select VIDEOBUF2_VMALLOC
default n
---help---
Enables a virtual video driver. This device shows a color bar
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index e17b6fe..662de4f 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;
+ atomic_t 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,182 @@ 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;
+ size = dev->width * dev->height * 2;
- if (0 == *count)
- *count = 32;
+ if (0 == *num_buffers)
+ *num_buffers = 32;
- while (*size * *count > vid_limit * 1024 * 1024)
- (*count)--;
+ while (size * *num_buffers > vid_limit * 1024 * 1024)
+ (*num_buffers)--;
- dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__,
- *count, *size);
+ *num_planes = 1;
+
+ dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
+ *num_buffers, size);
return 0;
}
-static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf)
+static int plane_setup(struct vb2_queue *vq, unsigned int plane_no,
+ unsigned long *plane_size)
{
- struct vivi_dev *dev = vq->priv_data;
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+
+ if (plane_no > 0)
+ return -EINVAL;
- dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state);
+ *plane_size = dev->width * dev->height * 2;
- videobuf_vmalloc_free(&buf->vb);
- dprintk(dev, 1, "free_buffer: freed\n");
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
+ dprintk(dev, 1, "%s: plane: %d, size: %ld\n",
+ __func__, plane_no, *plane_size);
+
+ return 0;
}
-static int
-buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int buffer_init(struct vb2_buffer *vb)
{
- struct vivi_dev *dev = vq->priv_data;
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+
+ BUG_ON(NULL == dev->fmt);
+
+ /*
+ * 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.
+ */
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ 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);
+}
- free_buffer(vq, buf);
+/* 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 struct videobuf_queue_ops vivi_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
+static struct vb2_ops vivi_video_qops = {
+ .queue_negotiate = queue_negotiate,
+ .plane_setup = plane_setup,
+ .buf_init = buffer_init,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+static struct vb2_read_ctx vivi_read_ctx = {
+ .num_bufs = 2,
};
/* ------------------------------------------------------------------
@@ -774,7 +804,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,15 +850,15 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vivi_dev *dev = video_drvdata(file);
- struct videobuf_queue *q = &dev->vb_vidq;
+ struct vb2_queue *q = &dev->vb_vidq;
int ret = vidioc_try_fmt_vid_cap(file, priv, f);
if (ret < 0)
return ret;
- mutex_lock(&q->vb_lock);
+ vb2_lock(q);
- if (vivi_is_generating(dev)) {
+ if (vb2_is_streaming(q)) {
dprintk(dev, 1, "%s device busy\n", __func__);
ret = -EBUSY;
goto out;
@@ -837,10 +867,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
dev->fmt = get_format(f);
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
- dev->vb_vidq.field = f->fmt.pix.field;
+ dev->field = f->fmt.pix.field;
ret = 0;
out:
- mutex_unlock(&q->vb_lock);
+ vb2_unlock(q);
return ret;
}
@@ -849,66 +879,42 @@ static int vidioc_reqbufs(struct file *file, void *priv,
{
struct vivi_dev *dev = video_drvdata(file);
- return videobuf_reqbufs(&dev->vb_vidq, p);
+ return vb2_reqbufs(&dev->vb_vidq, p);
}
static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vivi_dev *dev = video_drvdata(file);
- return videobuf_querybuf(&dev->vb_vidq, p);
+ return vb2_querybuf(&dev->vb_vidq, p);
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vivi_dev *dev = video_drvdata(file);
- return videobuf_qbuf(&dev->vb_vidq, p);
+ return vb2_qbuf(&dev->vb_vidq, p);
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
struct vivi_dev *dev = video_drvdata(file);
- return videobuf_dqbuf(&dev->vb_vidq, p,
- file->f_flags & O_NONBLOCK);
+ return vb2_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);
-}
-#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)
@@ -1031,26 +1037,25 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
File operations for the device
------------------------------------------------------------------*/
-static ssize_t
-vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+static int vivi_open(struct file *file)
{
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, "%s, %p\n", __func__, file);
+
+ atomic_inc(&dev->open_count);
+ return 0;
}
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)
@@ -1058,10 +1063,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 (atomic_dec_and_test(&dev->open_count))
+ vb2_queue_release(&dev->vb_vidq);
return 0;
}
@@ -1072,7 +1078,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,
@@ -1081,13 +1087,25 @@ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
return ret;
}
+static ssize_t vivi_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ dprintk(dev, 1, "read called\n");
+
+ return vb2_read(&dev->vb_vidq, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+}
+
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
+ .open = vivi_open,
.release = vivi_close,
- .read = vivi_read,
.poll = vivi_poll,
.ioctl = video_ioctl2, /* V4L2 ioctl handler */
.mmap = vivi_mmap,
+ .read = vivi_read,
};
static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
@@ -1109,9 +1127,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 = {
@@ -1145,6 +1160,8 @@ static int vivi_release(void)
kfree(dev);
}
+ vb2_vmalloc_cleanup(alloc_ctx);
+
return 0;
}
@@ -1173,10 +1190,8 @@ static int __init vivi_create_instance(int inst)
dev->saturation = 127;
dev->hue = 0;
- videobuf_queue_vmalloc_init(&dev->vb_vidq, &vivi_video_qops,
- NULL, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct vivi_buffer), dev);
+ vb2_queue_init(&dev->vb_vidq, &vivi_video_qops, alloc_ctx,
+ &vivi_read_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE, dev);
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
@@ -1238,6 +1253,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] 19+ messages in thread* [PATCH 7/7] v4l: videobuf2: add CMA allocator
2010-10-20 6:41 [PATCH/RFC v3 0/7] Videobuf2 framework Marek Szyprowski
` (5 preceding siblings ...)
2010-10-20 6:41 ` [PATCH 6/7] v4l: vivi: port to videobuf2 Marek Szyprowski
@ 2010-10-20 6:41 ` Marek Szyprowski
2010-10-20 7:14 ` [PATCH/RFC v3 0/7] Videobuf2 framework Hans Verkuil
7 siblings, 0 replies; 19+ 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>
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 | 25 ++++
4 files changed, 281 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 4dd14c2..2da9254 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 b3ae7d4..d1fc8cf 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -121,6 +121,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..d949c25
--- /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_paddr(void *buf_priv)
+{
+ struct vb2_cma_buf *buf = buf_priv;
+
+ return buf->paddr;
+}
+
+static unsigned int vb2_cma_num_users(void *buf_priv)
+{
+ struct vb2_cma_buf *buf = buf_priv;
+
+ return buf->refcount;
+}
+
+static void vb2_cma_vm_open(struct vm_area_struct *vma)
+{
+ struct vb2_cma_buf *buf = vma->vm_private_data;
+
+ printk(KERN_DEBUG "%s cma_priv: %p, refcount: %d, "
+ "vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
+ vma->vm_start, vma->vm_end);
+
+ buf->refcount++;
+}
+
+static void vb2_cma_vm_close(struct vm_area_struct *vma)
+{
+ struct vb2_cma_buf *buf = vma->vm_private_data;
+
+ printk(KERN_DEBUG "%s cma_priv: %p, refcount: %d, "
+ "vma: %08lx-%08lx\n", __func__, buf, buf->refcount,
+ vma->vm_start, vma->vm_end);
+
+ vb2_cma_put(buf);
+}
+
+static const struct vm_operations_struct vb2_cma_vm_ops = {
+ .open = vb2_cma_vm_open,
+ .close = vb2_cma_vm_close,
+};
+
+static int vb2_cma_mmap(void *buf_priv, struct vm_area_struct *vma)
+{
+ struct vb2_cma_buf *buf = buf_priv;
+
+ if (!buf) {
+ printk(KERN_ERR "No memory to map\n");
+ return -EINVAL;
+ }
+
+ return vb2_mmap_pfn_range(vma, buf->paddr, buf->size,
+ &vb2_cma_vm_ops, buf);
+}
+
+static void *vb2_cma_get_userptr(unsigned long vaddr, unsigned long size)
+{
+ struct vb2_cma_buf *buf;
+ unsigned long paddr = 0;
+ int ret;
+
+ buf = kzalloc(sizeof *buf, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ buf->vma = vb2_get_userptr(vaddr);
+ if (!buf->vma) {
+ printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n",
+ vaddr);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = vb2_contig_verify_userptr(buf->vma, vaddr, size, &paddr);
+ if (ret) {
+ vb2_put_userptr(buf->vma);
+ goto done;
+ }
+
+ buf->size = size;
+ buf->paddr = paddr;
+
+ return buf;
+
+done:
+ kfree(buf);
+ return ERR_PTR(ret);
+}
+
+static void vb2_cma_put_userptr(void *mem_priv)
+{
+ struct vb2_cma_buf *buf = mem_priv;
+
+ if (!buf)
+ return;
+
+ vb2_put_userptr(buf->vma);
+ kfree(buf);
+}
+
+static const struct vb2_mem_ops vb2_cma_ops = {
+ .alloc = vb2_cma_alloc,
+ .put = vb2_cma_put,
+ .paddr = vb2_cma_paddr,
+ .mmap = vb2_cma_mmap,
+ .get_userptr = vb2_cma_get_userptr,
+ .put_userptr = vb2_cma_put_userptr,
+ .num_users = vb2_cma_num_users,
+};
+
+struct vb2_alloc_ctx *vb2_cma_init(struct device *dev, const char *type,
+ unsigned long alignment)
+{
+ struct vb2_cma_conf *conf;
+
+ conf = kzalloc(sizeof *conf, GFP_KERNEL);
+ if (!conf)
+ return ERR_PTR(-ENOMEM);
+
+ conf->dev = dev;
+ conf->type = type;
+ conf->alignment = alignment;
+ conf->alloc_ctx.mem_ops = &vb2_cma_ops;
+
+ return &conf->alloc_ctx;
+}
+EXPORT_SYMBOL_GPL(vb2_cma_init);
+
+void vb2_cma_cleanup(struct vb2_alloc_ctx *alloc_ctx)
+{
+ struct vb2_cma_conf *conf =
+ container_of(alloc_ctx, struct vb2_cma_conf, alloc_ctx);
+
+ kfree(conf);
+}
+EXPORT_SYMBOL_GPL(vb2_cma_cleanup);
+
+struct vb2_alloc_ctx **vb2_cma_init_multi(struct device *dev,
+ unsigned int num_planes,
+ const char *types[],
+ unsigned long alignments[])
+{
+ struct vb2_alloc_ctx **alloc_ctxes;
+ struct vb2_cma_conf *cma_conf;
+ unsigned int i;
+
+ alloc_ctxes = kzalloc((sizeof *alloc_ctxes + sizeof *cma_conf)
+ * num_planes, GFP_KERNEL);
+ if (!alloc_ctxes)
+ return ERR_PTR(-ENOMEM);
+
+ cma_conf = (void *)(alloc_ctxes + num_planes);
+
+ for (i = 0; i < num_planes; ++i, ++cma_conf) {
+ alloc_ctxes[i] = &cma_conf->alloc_ctx;
+ cma_conf->dev = dev;
+ cma_conf->type = types[i];
+ cma_conf->alignment = alignments[i];
+ cma_conf->alloc_ctx.mem_ops = &vb2_cma_ops;
+ }
+
+ return alloc_ctxes;
+}
+EXPORT_SYMBOL_GPL(vb2_cma_init_multi);
+
+void vb2_cma_cleanup_multi(struct vb2_alloc_ctx **alloc_ctxes)
+{
+ kfree(alloc_ctxes);
+}
+EXPORT_SYMBOL_GPL(vb2_cma_cleanup_multi);
+
+MODULE_DESCRIPTION("CMA allocator handling routines for videobuf2");
+MODULE_AUTHOR("Pawel Osciak");
+MODULE_LICENSE("GPL");
diff --git a/include/media/videobuf2-cma.h b/include/media/videobuf2-cma.h
new file mode 100644
index 0000000..557eeb0
--- /dev/null
+++ b/include/media/videobuf2-cma.h
@@ -0,0 +1,25 @@
+/*
+ * videobuf2-cma.h - CMA memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <p.osciak@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ */
+
+struct vb2_alloc_ctx *vb2_cma_init(struct device *dev, const char *type,
+ unsigned long alignment);
+void vb2_cma_cleanup(struct vb2_alloc_ctx *alloc_ctx);
+
+struct vb2_alloc_ctx **vb2_cma_init_multi(struct device *dev,
+ unsigned int num_planes, const char *types[],
+ unsigned long alignments[]);
+void vb2_cma_cleanup_multi(struct vb2_alloc_ctx **alloc_ctxes);
+
+struct vb2_alloc_ctx *vb2_cma_init(struct device *dev, const char *type,
+ unsigned long alignment);
+void vb2_cma_cleanup(struct vb2_alloc_ctx *alloc_ctx);
+
--
1.7.1.569.g6f426
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [PATCH/RFC v3 0/7] Videobuf2 framework
2010-10-20 6:41 [PATCH/RFC v3 0/7] Videobuf2 framework Marek Szyprowski
` (6 preceding siblings ...)
2010-10-20 6:41 ` [PATCH 7/7] v4l: videobuf2: add CMA allocator Marek Szyprowski
@ 2010-10-20 7:14 ` Hans Verkuil
2010-10-20 7:18 ` Kyungmin Park
2010-10-20 7:40 ` Marek Szyprowski
7 siblings, 2 replies; 19+ messages in thread
From: Hans Verkuil @ 2010-10-20 7:14 UTC (permalink / raw)
To: Marek Szyprowski; +Cc: linux-media, pawel, kyungmin.park
On Wednesday, October 20, 2010 08:41:06 Marek Szyprowski wrote:
> Hello,
>
> As I promissed I continue the development of the VideoBuf2 at Samsung
> until Pawel finds some spare time to help us. This is a third version of
> the framework. Besides the minor bugfixes here and there I've added a
> complete read() callback emulator. This emulator provides 2 types of
> read() operation - 'streaming' and 'one shot'. It is suitable to replace
> both videobuf_read_stream() and videobuf_read_one() methods from the old
> videobuf.
One thing I never understood: what is the point of supporting 'one shot' read
mode? Why not support just streaming? Does anyone know?
Another question: how hard is it to support write mode as well? I think
vb2 should support both. I suspect that once we have a read emulator it isn't
difficult to make a write emulator too.
A last remark: the locking has changed recently in videobuf due to the work
done on eliminating the BKL. It's probably a good idea to incorporate those
changes as well in vb2.
Regards,
Hans
>
> Taking into account the size of the patches and the number of lines I've
> changed, I've decided to keep the Pawel signed-off attribute and
> authorship to correctly credit him. If this is against kernel rules,
> feel free to let me know.
>
> Best regards
> --
> Marek Szyprowski
> Samsung Poland R&D Center
>
>
> 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:
>
> Pawel Osciak (6):
> 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: vivi: port to videobuf2
> v4l: videobuf2: add CMA allocator
>
> Marek Szyprowski (1):
> v4l: videobuf2: add read() emulator
>
> drivers/media/video/Kconfig | 23 +-
> drivers/media/video/Makefile | 6 +
> drivers/media/video/videobuf2-cma.c | 250 ++++
> drivers/media/video/videobuf2-core.c | 1726 ++++++++++++++++++++++++++
> 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 | 348 +++---
> include/media/videobuf2-cma.h | 25 +
> include/media/videobuf2-core.h | 392 ++++++
> include/media/videobuf2-dma-coherent.h | 21 +
> include/media/videobuf2-memops.h | 31 +
> include/media/videobuf2-vmalloc.h | 16 +
> 13 files changed, 3258 insertions(+), 164 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
>
>
--
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH/RFC v3 0/7] Videobuf2 framework
2010-10-20 7:14 ` [PATCH/RFC v3 0/7] Videobuf2 framework Hans Verkuil
@ 2010-10-20 7:18 ` Kyungmin Park
2010-10-20 12:10 ` Andy Walls
2010-10-20 7:40 ` Marek Szyprowski
1 sibling, 1 reply; 19+ messages in thread
From: Kyungmin Park @ 2010-10-20 7:18 UTC (permalink / raw)
To: Hans Verkuil; +Cc: Marek Szyprowski, linux-media, pawel
On Wed, Oct 20, 2010 at 4:14 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> On Wednesday, October 20, 2010 08:41:06 Marek Szyprowski wrote:
>> Hello,
>>
>> As I promissed I continue the development of the VideoBuf2 at Samsung
>> until Pawel finds some spare time to help us. This is a third version of
>> the framework. Besides the minor bugfixes here and there I've added a
>> complete read() callback emulator. This emulator provides 2 types of
>> read() operation - 'streaming' and 'one shot'. It is suitable to replace
>> both videobuf_read_stream() and videobuf_read_one() methods from the old
>> videobuf.
>
> One thing I never understood: what is the point of supporting 'one shot' read
> mode? Why not support just streaming? Does anyone know?
>
> Another question: how hard is it to support write mode as well? I think
> vb2 should support both. I suspect that once we have a read emulator it isn't
> difficult to make a write emulator too.
One thing consideration is that with this implementation we can't meet
merge window for 2.6.37.
How do you think?
Thank you,
Kyungmin Park
>
> A last remark: the locking has changed recently in videobuf due to the work
> done on eliminating the BKL. It's probably a good idea to incorporate those
> changes as well in vb2.
>
> Regards,
>
> Hans
>
>>
>> Taking into account the size of the patches and the number of lines I've
>> changed, I've decided to keep the Pawel signed-off attribute and
>> authorship to correctly credit him. If this is against kernel rules,
>> feel free to let me know.
>>
>> Best regards
>> --
>> Marek Szyprowski
>> Samsung Poland R&D Center
>>
>>
>> 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:
>>
>> Pawel Osciak (6):
>> 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: vivi: port to videobuf2
>> v4l: videobuf2: add CMA allocator
>>
>> Marek Szyprowski (1):
>> v4l: videobuf2: add read() emulator
>>
>> drivers/media/video/Kconfig | 23 +-
>> drivers/media/video/Makefile | 6 +
>> drivers/media/video/videobuf2-cma.c | 250 ++++
>> drivers/media/video/videobuf2-core.c | 1726 ++++++++++++++++++++++++++
>> 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 | 348 +++---
>> include/media/videobuf2-cma.h | 25 +
>> include/media/videobuf2-core.h | 392 ++++++
>> include/media/videobuf2-dma-coherent.h | 21 +
>> include/media/videobuf2-memops.h | 31 +
>> include/media/videobuf2-vmalloc.h | 16 +
>> 13 files changed, 3258 insertions(+), 164 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
>>
>>
>
> --
> Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
> --
> 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] 19+ messages in thread* Re: [PATCH/RFC v3 0/7] Videobuf2 framework
2010-10-20 7:18 ` Kyungmin Park
@ 2010-10-20 12:10 ` Andy Walls
2010-10-20 12:28 ` Hans Verkuil
0 siblings, 1 reply; 19+ messages in thread
From: Andy Walls @ 2010-10-20 12:10 UTC (permalink / raw)
To: Kyungmin Park; +Cc: Hans Verkuil, Marek Szyprowski, linux-media, pawel
On Wed, 2010-10-20 at 16:18 +0900, Kyungmin Park wrote:
> On Wed, Oct 20, 2010 at 4:14 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
> > On Wednesday, October 20, 2010 08:41:06 Marek Szyprowski wrote:
> >> Hello,
> >>
> >> As I promissed I continue the development of the VideoBuf2 at Samsung
> >> until Pawel finds some spare time to help us. This is a third version of
> >> the framework. Besides the minor bugfixes here and there I've added a
> >> complete read() callback emulator. This emulator provides 2 types of
> >> read() operation - 'streaming' and 'one shot'. It is suitable to replace
> >> both videobuf_read_stream() and videobuf_read_one() methods from the old
> >> videobuf.
> >
> > One thing I never understood: what is the point of supporting 'one shot' read
> > mode? Why not support just streaming? Does anyone know?
> >
> > Another question: how hard is it to support write mode as well? I think
> > vb2 should support both. I suspect that once we have a read emulator it isn't
> > difficult to make a write emulator too.
> One thing consideration is that with this implementation we can't meet
> merge window for 2.6.37.
> How do you think?
>
> Thank you,
> Kyungmin Park
I'll agree that we should not let requests for features not currently
used by user-space impede a functional replacement from being committed.
I'm assuming the original videobuf never had write() support or that no
userspace apps used it.
My $0.02.
Regards,
Andy
> > A last remark: the locking has changed recently in videobuf due to the work
> > done on eliminating the BKL. It's probably a good idea to incorporate those
> > changes as well in vb2.
> >
> > Regards,
> >
> > Hans
> >
> >>
> >> Taking into account the size of the patches and the number of lines I've
> >> changed, I've decided to keep the Pawel signed-off attribute and
> >> authorship to correctly credit him. If this is against kernel rules,
> >> feel free to let me know.
> >>
> >> Best regards
> >> --
> >> Marek Szyprowski
> >> Samsung Poland R&D Center
> >>
> >>
> >> 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:
> >>
> >> Pawel Osciak (6):
> >> 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: vivi: port to videobuf2
> >> v4l: videobuf2: add CMA allocator
> >>
> >> Marek Szyprowski (1):
> >> v4l: videobuf2: add read() emulator
> >>
> >> drivers/media/video/Kconfig | 23 +-
> >> drivers/media/video/Makefile | 6 +
> >> drivers/media/video/videobuf2-cma.c | 250 ++++
> >> drivers/media/video/videobuf2-core.c | 1726 ++++++++++++++++++++++++++
> >> 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 | 348 +++---
> >> include/media/videobuf2-cma.h | 25 +
> >> include/media/videobuf2-core.h | 392 ++++++
> >> include/media/videobuf2-dma-coherent.h | 21 +
> >> include/media/videobuf2-memops.h | 31 +
> >> include/media/videobuf2-vmalloc.h | 16 +
> >> 13 files changed, 3258 insertions(+), 164 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
> >>
> >>
> >
> > --
> > Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
> > --
> > 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
> >
> --
> 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] 19+ messages in thread* Re: [PATCH/RFC v3 0/7] Videobuf2 framework
2010-10-20 12:10 ` Andy Walls
@ 2010-10-20 12:28 ` Hans Verkuil
2010-10-21 0:57 ` Kyungmin Park
0 siblings, 1 reply; 19+ messages in thread
From: Hans Verkuil @ 2010-10-20 12:28 UTC (permalink / raw)
To: Andy Walls; +Cc: Kyungmin Park, Marek Szyprowski, linux-media, pawel
> On Wed, 2010-10-20 at 16:18 +0900, Kyungmin Park wrote:
>> On Wed, Oct 20, 2010 at 4:14 PM, Hans Verkuil <hverkuil@xs4all.nl>
>> wrote:
>> > On Wednesday, October 20, 2010 08:41:06 Marek Szyprowski wrote:
>> >> Hello,
>> >>
>> >> As I promissed I continue the development of the VideoBuf2 at Samsung
>> >> until Pawel finds some spare time to help us. This is a third version
>> of
>> >> the framework. Besides the minor bugfixes here and there I've added a
>> >> complete read() callback emulator. This emulator provides 2 types of
>> >> read() operation - 'streaming' and 'one shot'. It is suitable to
>> replace
>> >> both videobuf_read_stream() and videobuf_read_one() methods from the
>> old
>> >> videobuf.
>> >
>> > One thing I never understood: what is the point of supporting 'one
>> shot' read
>> > mode? Why not support just streaming? Does anyone know?
>> >
>> > Another question: how hard is it to support write mode as well? I
>> think
>> > vb2 should support both. I suspect that once we have a read emulator
>> it isn't
>> > difficult to make a write emulator too.
>> One thing consideration is that with this implementation we can't meet
>> merge window for 2.6.37.
>> How do you think?
>>
>> Thank you,
>> Kyungmin Park
>
> I'll agree that we should not let requests for features not currently
> used by user-space impede a functional replacement from being committed.
>
> I'm assuming the original videobuf never had write() support or that no
> userspace apps used it.
>
> My $0.02.
I certainly never expected to see videobuf2 to appear in 2.6.37. I'm
hoping for 2.6.38, but it requires that videobuf-dma-sg is also ported to
vb2.
The main reason why I am pushing for write now is that it is easy to add
since Marek just implemented the read version. So he has all the knowledge
needed to implement this still in his head.
It always bugged me that read was supported in videobuf but not write. We
can do better in videobuf2.
I think that it would be a good idea (just my opinion) to merge videobuf2
early in the next 2.6.38 kernel cycle, even if dma-sg isn't implemented
yet, provided we can get that missing piece in before that kernel cycle
ends. I think that is definitely doable.
Regards,
Hans
>
> Regards,
> Andy
>
>> > A last remark: the locking has changed recently in videobuf due to the
>> work
>> > done on eliminating the BKL. It's probably a good idea to incorporate
>> those
>> > changes as well in vb2.
>> >
>> > Regards,
>> >
>> > Hans
>> >
>> >>
>> >> Taking into account the size of the patches and the number of lines
>> I've
>> >> changed, I've decided to keep the Pawel signed-off attribute and
>> >> authorship to correctly credit him. If this is against kernel rules,
>> >> feel free to let me know.
>> >>
>> >> Best regards
>> >> --
>> >> Marek Szyprowski
>> >> Samsung Poland R&D Center
>> >>
>> >>
>> >> 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:
>> >>
>> >> Pawel Osciak (6):
>> >> 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: vivi: port to videobuf2
>> >> v4l: videobuf2: add CMA allocator
>> >>
>> >> Marek Szyprowski (1):
>> >> v4l: videobuf2: add read() emulator
>> >>
>> >> drivers/media/video/Kconfig | 23 +-
>> >> drivers/media/video/Makefile | 6 +
>> >> drivers/media/video/videobuf2-cma.c | 250 ++++
>> >> drivers/media/video/videobuf2-core.c | 1726
>> ++++++++++++++++++++++++++
>> >> 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 | 348 +++---
>> >> include/media/videobuf2-cma.h | 25 +
>> >> include/media/videobuf2-core.h | 392 ++++++
>> >> include/media/videobuf2-dma-coherent.h | 21 +
>> >> include/media/videobuf2-memops.h | 31 +
>> >> include/media/videobuf2-vmalloc.h | 16 +
>> >> 13 files changed, 3258 insertions(+), 164 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
>> >>
>> >>
>> >
>> > --
>> > Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of
>> Cisco
>> > --
>> > 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
>> >
>> --
>> 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
>
>
>
--
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [PATCH/RFC v3 0/7] Videobuf2 framework
2010-10-20 12:28 ` Hans Verkuil
@ 2010-10-21 0:57 ` Kyungmin Park
0 siblings, 0 replies; 19+ messages in thread
From: Kyungmin Park @ 2010-10-21 0:57 UTC (permalink / raw)
To: Hans Verkuil; +Cc: Andy Walls, Marek Szyprowski, linux-media, pawel
On Wed, Oct 20, 2010 at 9:28 PM, Hans Verkuil <hverkuil@xs4all.nl> wrote:
>
>> On Wed, 2010-10-20 at 16:18 +0900, Kyungmin Park wrote:
>>> On Wed, Oct 20, 2010 at 4:14 PM, Hans Verkuil <hverkuil@xs4all.nl>
>>> wrote:
>>> > On Wednesday, October 20, 2010 08:41:06 Marek Szyprowski wrote:
>>> >> Hello,
>>> >>
>>> >> As I promissed I continue the development of the VideoBuf2 at Samsung
>>> >> until Pawel finds some spare time to help us. This is a third version
>>> of
>>> >> the framework. Besides the minor bugfixes here and there I've added a
>>> >> complete read() callback emulator. This emulator provides 2 types of
>>> >> read() operation - 'streaming' and 'one shot'. It is suitable to
>>> replace
>>> >> both videobuf_read_stream() and videobuf_read_one() methods from the
>>> old
>>> >> videobuf.
>>> >
>>> > One thing I never understood: what is the point of supporting 'one
>>> shot' read
>>> > mode? Why not support just streaming? Does anyone know?
>>> >
>>> > Another question: how hard is it to support write mode as well? I
>>> think
>>> > vb2 should support both. I suspect that once we have a read emulator
>>> it isn't
>>> > difficult to make a write emulator too.
>>> One thing consideration is that with this implementation we can't meet
>>> merge window for 2.6.37.
>>> How do you think?
>>>
>>> Thank you,
>>> Kyungmin Park
>>
>> I'll agree that we should not let requests for features not currently
>> used by user-space impede a functional replacement from being committed.
>>
>> I'm assuming the original videobuf never had write() support or that no
>> userspace apps used it.
>>
>> My $0.02.
>
> I certainly never expected to see videobuf2 to appear in 2.6.37. I'm
> hoping for 2.6.38, but it requires that videobuf-dma-sg is also ported to
> vb2.
then how about to merge it as migration plan from videobuf to
videobuf2 at 2.6.37.
Since there are some users and modules such as MFC depends on
videobuf2. If it's merged at 2.6.38
it has to wait about 2~3 month again. and videobuf-dma-sg also have to
wait until that time.
I think give a chance to test videobuf2 at kernel 2.6.37 and then use
it at 2.6.38 finally.
Thank you,
Kyungmin Park
>
> The main reason why I am pushing for write now is that it is easy to add
> since Marek just implemented the read version. So he has all the knowledge
> needed to implement this still in his head.
>
> It always bugged me that read was supported in videobuf but not write. We
> can do better in videobuf2.
>
> I think that it would be a good idea (just my opinion) to merge videobuf2
> early in the next 2.6.38 kernel cycle, even if dma-sg isn't implemented
> yet, provided we can get that missing piece in before that kernel cycle
> ends. I think that is definitely doable.
>
> Regards,
>
> Hans
>
>>
>> Regards,
>> Andy
>>
>>> > A last remark: the locking has changed recently in videobuf due to the
>>> work
>>> > done on eliminating the BKL. It's probably a good idea to incorporate
>>> those
>>> > changes as well in vb2.
>>> >
>>> > Regards,
>>> >
>>> > Hans
>>> >
>>> >>
>>> >> Taking into account the size of the patches and the number of lines
>>> I've
>>> >> changed, I've decided to keep the Pawel signed-off attribute and
>>> >> authorship to correctly credit him. If this is against kernel rules,
>>> >> feel free to let me know.
>>> >>
>>> >> Best regards
>>> >> --
>>> >> Marek Szyprowski
>>> >> Samsung Poland R&D Center
>>> >>
>>> >>
>>> >> 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:
>>> >>
>>> >> Pawel Osciak (6):
>>> >> 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: vivi: port to videobuf2
>>> >> v4l: videobuf2: add CMA allocator
>>> >>
>>> >> Marek Szyprowski (1):
>>> >> v4l: videobuf2: add read() emulator
>>> >>
>>> >> drivers/media/video/Kconfig | 23 +-
>>> >> drivers/media/video/Makefile | 6 +
>>> >> drivers/media/video/videobuf2-cma.c | 250 ++++
>>> >> drivers/media/video/videobuf2-core.c | 1726
>>> ++++++++++++++++++++++++++
>>> >> 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 | 348 +++---
>>> >> include/media/videobuf2-cma.h | 25 +
>>> >> include/media/videobuf2-core.h | 392 ++++++
>>> >> include/media/videobuf2-dma-coherent.h | 21 +
>>> >> include/media/videobuf2-memops.h | 31 +
>>> >> include/media/videobuf2-vmalloc.h | 16 +
>>> >> 13 files changed, 3258 insertions(+), 164 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
>>> >>
>>> >>
>>> >
>>> > --
>>> > Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of
>>> Cisco
>>> > --
>>> > 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
>>> >
>>> --
>>> 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
>>
>>
>>
>
>
> --
> Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
>
> --
> 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] 19+ messages in thread
* RE: [PATCH/RFC v3 0/7] Videobuf2 framework
2010-10-20 7:14 ` [PATCH/RFC v3 0/7] Videobuf2 framework Hans Verkuil
2010-10-20 7:18 ` Kyungmin Park
@ 2010-10-20 7:40 ` Marek Szyprowski
2010-10-20 8:02 ` Hans Verkuil
1 sibling, 1 reply; 19+ messages in thread
From: Marek Szyprowski @ 2010-10-20 7:40 UTC (permalink / raw)
To: 'Hans Verkuil'; +Cc: linux-media, pawel, kyungmin.park
Hello,
On Wednesday, October 20, 2010 9:15 AM wrote:
> On Wednesday, October 20, 2010 08:41:06 Marek Szyprowski wrote:
> > Hello,
> >
> > As I promissed I continue the development of the VideoBuf2 at Samsung
> > until Pawel finds some spare time to help us. This is a third version of
> > the framework. Besides the minor bugfixes here and there I've added a
> > complete read() callback emulator. This emulator provides 2 types of
> > read() operation - 'streaming' and 'one shot'. It is suitable to replace
> > both videobuf_read_stream() and videobuf_read_one() methods from the old
> > videobuf.
>
> One thing I never understood: what is the point of supporting 'one shot' read
> mode? Why not support just streaming? Does anyone know?
I can imagine that some simple cameras that capture pure JPG frames might want
to use 'one shot' mode. This enables easier scripting and things like
'cat /dev/video >capture.jpg' working. If you think that 'one shot' mode should
be removed - let me know.
> Another question: how hard is it to support write mode as well? I think
> vb2 should support both. I suspect that once we have a read emulator it isn't
> difficult to make a write emulator too.
Well, that's possible. If you really think that write() emulator is also
required, I can implement both. This shouldn't be much work.
> A last remark: the locking has changed recently in videobuf due to the work
> done on eliminating the BKL. It's probably a good idea to incorporate those
> changes as well in vb2.
Yes, I noticed that there are a lot of changes in for-2.6.37 staging tree, I
will try to get through them and update relevant parts of vb2. The current
version the vb2 patches is based on 2.6.36-rc8. Kernel tree with vb2 patches
(and the required multiplane patches) will be available in a few hours on:
http://git.infradead.org/users/kmpark/linux-2.6-samsung/shortlog/refs/heads/v4l/vb2
Best regards
--
Marek Szyprowski
Samsung Poland R&D Center
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH/RFC v3 0/7] Videobuf2 framework
2010-10-20 7:40 ` Marek Szyprowski
@ 2010-10-20 8:02 ` Hans Verkuil
0 siblings, 0 replies; 19+ messages in thread
From: Hans Verkuil @ 2010-10-20 8:02 UTC (permalink / raw)
To: Marek Szyprowski; +Cc: linux-media, pawel, kyungmin.park, mchehab
> Hello,
>
> On Wednesday, October 20, 2010 9:15 AM wrote:
>
>> On Wednesday, October 20, 2010 08:41:06 Marek Szyprowski wrote:
>> > Hello,
>> >
>> > As I promissed I continue the development of the VideoBuf2 at Samsung
>> > until Pawel finds some spare time to help us. This is a third version
>> of
>> > the framework. Besides the minor bugfixes here and there I've added a
>> > complete read() callback emulator. This emulator provides 2 types of
>> > read() operation - 'streaming' and 'one shot'. It is suitable to
>> replace
>> > both videobuf_read_stream() and videobuf_read_one() methods from the
>> old
>> > videobuf.
>>
>> One thing I never understood: what is the point of supporting 'one shot'
>> read
>> mode? Why not support just streaming? Does anyone know?
>
> I can imagine that some simple cameras that capture pure JPG frames might
> want
> to use 'one shot' mode. This enables easier scripting and things like
> 'cat /dev/video >capture.jpg' working. If you think that 'one shot' mode
> should
> be removed - let me know.
I did a grep for videobuf_read_one in the sources and the only ones that
use it are bttv, saa7134, zr364xx, cx88 and cx23885. AFAIK zr364xx is the
only webcam driver in this set.
Mauro, do you know if there are any guidelines on what drivers are
supposed to use? Right now it seems random as to what drivers use.
The only constant I see is that vbi and compressed video (e.g. mpeg)
streaming is always using read_stream. Raw video seems to depend purely on
what the driver developer chose.
>> Another question: how hard is it to support write mode as well? I think
>> vb2 should support both. I suspect that once we have a read emulator it
>> isn't
>> difficult to make a write emulator too.
>
> Well, that's possible. If you really think that write() emulator is also
> required, I can implement both. This shouldn't be much work.
If it is not much work, then we should do that. The reason write wasn't
present before is simply that few drivers supported output streaming. But
that is changing these days so write support would definitely be a good
idea.
Regards,
Hans
>> A last remark: the locking has changed recently in videobuf due to the
>> work
>> done on eliminating the BKL. It's probably a good idea to incorporate
>> those
>> changes as well in vb2.
>
> Yes, I noticed that there are a lot of changes in for-2.6.37 staging tree,
> I
> will try to get through them and update relevant parts of vb2. The current
> version the vb2 patches is based on 2.6.36-rc8. Kernel tree with vb2
> patches
> (and the required multiplane patches) will be available in a few hours on:
>
> http://git.infradead.org/users/kmpark/linux-2.6-samsung/shortlog/refs/heads/v4l/vb2
>
> Best regards
> --
> Marek Szyprowski
> Samsung Poland R&D Center
>
>
--
Hans Verkuil - video4linux developer - sponsored by TANDBERG, part of Cisco
^ permalink raw reply [flat|nested] 19+ messages in thread