RE: [PATCH v4 1/2] v4l: Add memory-to-memory device helper framework for videobuf.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> -----Original Message-----
> From: Pawel Osciak [mailto:p.osciak@xxxxxxxxxxx]
> Sent: Monday, April 19, 2010 6:00 PM
> To: linux-media@xxxxxxxxxxxxxxx
> Cc: p.osciak@xxxxxxxxxxx; m.szyprowski@xxxxxxxxxxx;
> kyungmin.park@xxxxxxxxxxx; Hiremath, Vaibhav
> Subject: [PATCH v4 1/2] v4l: Add memory-to-memory device helper framework
> for videobuf.
>
> A mem-to-mem device is a device that uses memory buffers passed by
> userspace applications for both their source and destination data. This
> is different from existing drivers, which utilize memory buffers for either
> input or output, but not both.
>
> In terms of V4L2 such a device would be both of OUTPUT and CAPTURE type.
>
> Examples of such devices would be: image 'resizers', 'rotators',
> 'colorspace converters', etc.
>
> This patch adds a separate Kconfig sub-menu for mem-to-mem devices as well.
[Hiremath, Vaibhav] Some minor comments (which just came across now) -

>
> Signed-off-by: Pawel Osciak <p.osciak@xxxxxxxxxxx>
> Signed-off-by: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
> Reviewed-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
> ---
>  drivers/media/video/Kconfig        |   14 +
>  drivers/media/video/Makefile       |    2 +
>  drivers/media/video/v4l2-mem2mem.c |  632
> ++++++++++++++++++++++++++++++++++++
>  include/media/v4l2-mem2mem.h       |  201 ++++++++++++
>  4 files changed, 849 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/media/video/v4l2-mem2mem.c
>  create mode 100644 include/media/v4l2-mem2mem.h
>
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index f8fc865..5fd041e 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -45,6 +45,10 @@ config VIDEO_TUNER
>       tristate
>       depends on MEDIA_TUNER
>
> +config V4L2_MEM2MEM_DEV
> +     tristate
> +     depends on VIDEOBUF_GEN
> +
>  #
>  # Multimedia Video device configuration
>  #
> @@ -1107,3 +1111,13 @@ config USB_S2255
>
>  endif # V4L_USB_DRIVERS
>  endif # VIDEO_CAPTURE_DRIVERS
> +
> +menuconfig V4L_MEM2MEM_DRIVERS
> +     bool "Memory-to-memory multimedia devices"
> +     depends on VIDEO_V4L2
> +     default n
> +     ---help---
> +       Say Y here to enable selecting drivers for V4L devices that
> +       use system memory for both source and destination buffers, as
> opposed
> +       to capture and output drivers, which use memory buffers for just
> +       one of those.
> diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
> index b88b617..e974680 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_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
> +
>  obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
>
>  obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
> diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-
> mem2mem.c
> new file mode 100644
> index 0000000..eee9514
> --- /dev/null
> +++ b/drivers/media/video/v4l2-mem2mem.c
> @@ -0,0 +1,632 @@
> +/*
> + * Memory-to-memory device framework for Video for Linux 2 and videobuf.
> + *
> + * Helper functions for devices that use videobuf buffers for both their
> + * source and destination.
> + *
> + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
> + * Pawel Osciak, <p.osciak@xxxxxxxxxxx>
> + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx>
> + *
> + * 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; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
[Hiremath, Vaibhav] Add one line here.

> +#include <media/videobuf-core.h>
> +#include <media/v4l2-mem2mem.h>
> +
> +MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
> +MODULE_AUTHOR("Pawel Osciak, <p.osciak@xxxxxxxxxxx>");
> +MODULE_LICENSE("GPL");
> +
> +static bool debug;
> +module_param(debug, bool, 0644);
> +
> +#define dprintk(fmt, arg...)                                         \
> +     do {                                                            \
> +             if (debug)                                              \
> +                     printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\
> +     } while (0)
> +
> +
> +/* Instance is already queued on the job_queue */
> +#define TRANS_QUEUED         (1 << 0)
> +/* Instance is currently running in hardware */
> +#define TRANS_RUNNING                (1 << 1)
> +
> +
> +/* Offset base for buffers on the destination queue - used to distinguish
> + * between source and destination buffers when mmapping - they receive the
> same
> + * offsets but for different queues */
> +#define DST_QUEUE_OFF_BASE   (1 << 30)
> +
> +
> +/**
> + * struct v4l2_m2m_dev - per-device context
> + * @curr_ctx:                currently running instance
> + * @job_queue:               instances queued to run
> + * @job_spinlock:    protects job_queue
> + * @m2m_ops:         driver callbacks
> + */
> +struct v4l2_m2m_dev {
> +     struct v4l2_m2m_ctx     *curr_ctx;
> +
> +     struct list_head        job_queue;
> +     spinlock_t              job_spinlock;
> +
> +     struct v4l2_m2m_ops     *m2m_ops;
> +};
> +
> +static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx
> *m2m_ctx,
> +                                             enum v4l2_buf_type type)
> +{
> +     switch (type) {
> +     case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +             return &m2m_ctx->cap_q_ctx;
> +     case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +             return &m2m_ctx->out_q_ctx;
> +     default:
> +             printk(KERN_ERR "Invalid buffer type\n");
> +             return NULL;
> +     }
> +}
> +
> +/**
> + * v4l2_m2m_get_vq() - return videobuf_queue for the given type
> + */
> +struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
> +                                    enum v4l2_buf_type type)
> +{
> +     struct v4l2_m2m_queue_ctx *q_ctx;
> +
> +     q_ctx = get_queue_ctx(m2m_ctx, type);
> +     if (!q_ctx)
> +             return NULL;
> +
> +     return &q_ctx->q;
> +}
> +EXPORT_SYMBOL(v4l2_m2m_get_vq);
> +
> +/**
> + * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers
> + */
> +void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type
> type)
> +{
> +     struct v4l2_m2m_queue_ctx *q_ctx;
> +     struct videobuf_buffer *vb = NULL;
> +     unsigned long flags;
> +
> +     q_ctx = get_queue_ctx(m2m_ctx, type);
> +     if (!q_ctx)
> +             return NULL;
> +
> +     spin_lock_irqsave(q_ctx->q.irqlock, flags);
> +
> +     if (list_empty(&q_ctx->rdy_queue))
> +             goto end;
> +
> +     vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer, queue);
> +     vb->state = VIDEOBUF_ACTIVE;
> +
> +end:
> +     spin_unlock_irqrestore(q_ctx->q.irqlock, flags);
> +     return vb;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf);
> +
> +/**
> + * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers
> and
> + * return it
> + */
> +void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type
> type)
> +{
> +     struct v4l2_m2m_queue_ctx *q_ctx;
> +     struct videobuf_buffer *vb = NULL;
> +     unsigned long flags;
> +
> +     q_ctx = get_queue_ctx(m2m_ctx, type);
> +     if (!q_ctx)
> +             return NULL;
> +
> +     spin_lock_irqsave(q_ctx->q.irqlock, flags);
> +     if (!list_empty(&q_ctx->rdy_queue)) {
> +             vb = list_entry(q_ctx->rdy_queue.next, struct videobuf_buffer,
> +                             queue);
> +             list_del(&vb->queue);
> +             q_ctx->num_rdy--;
> +     }
> +     spin_unlock_irqrestore(q_ctx->q.irqlock, flags);
> +
> +     return vb;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove);
> +
> +/*
> + * Scheduling handlers
> + */
> +
> +/**
> + * v4l2_m2m_get_curr_priv() - return driver private data for the currently
> + * running instance or NULL if no instance is running
> + */
> +void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev)
> +{
> +     unsigned long flags;
> +     void *ret = NULL;
> +
> +     spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
> +     if (m2m_dev->curr_ctx)
> +             ret = m2m_dev->curr_ctx->priv;
> +     spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
> +
> +     return ret;
> +}
> +EXPORT_SYMBOL(v4l2_m2m_get_curr_priv);
> +
> +/**
> + * v4l2_m2m_try_run() - select next job to perform and run it if possible
> + *
> + * Get next transaction (if present) from the waiting jobs list and run it.
> + */
> +static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev)
> +{
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
> +     if (NULL != m2m_dev->curr_ctx) {
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
> +             dprintk("Another instance is running, won't run now\n");
> +             return;
> +     }
> +
> +     if (list_empty(&m2m_dev->job_queue)) {
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
> +             dprintk("No job pending\n");
> +             return;
> +     }
> +
> +     m2m_dev->curr_ctx = list_entry(m2m_dev->job_queue.next,
> +                                struct v4l2_m2m_ctx, queue);
> +     m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING;
> +     spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
> +
> +     m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv);
> +}
> +
> +/**
> + * v4l2_m2m_try_schedule() - check whether an instance is ready to be added
> to
> + * the pending job queue and add it if so.
> + * @m2m_ctx: m2m context assigned to the instance to be checked
> + *
> + * There are three basic requirements an instance has to meet to be able to
> run:
> + * 1) at least one source buffer has to be queued,
> + * 2) at least one destination buffer has to be queued,
> + * 3) streaming has to be on.
> + *
> + * There may also be additional, custom requirements. In such case the
> driver
> + * should supply a custom callback (job_ready in v4l2_m2m_ops) that should
> + * return 1 if the instance is ready.
> + * An example of the above could be an instance that requires more than one
> + * src/dst buffer per transaction.
> + */
> +static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     struct v4l2_m2m_dev *m2m_dev;
> +     unsigned long flags_job, flags;
> +
> +     m2m_dev = m2m_ctx->m2m_dev;
> +     dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
> +
> +     if (!m2m_ctx->out_q_ctx.q.streaming
> +         || !m2m_ctx->cap_q_ctx.q.streaming) {
> +             dprintk("Streaming needs to be on for both queues\n");
> +             return;
> +     }
> +
> +     spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
> +     if (m2m_ctx->job_flags & TRANS_QUEUED) {
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
> +             dprintk("On job queue already\n");
> +             return;
> +     }
> +
> +     spin_lock_irqsave(m2m_ctx->out_q_ctx.q.irqlock, flags);
> +     if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) {
> +             spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags);
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
> +             dprintk("No input buffers available\n");
> +             return;
> +     }
> +     if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) {
> +             spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags);
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
> +             dprintk("No output buffers available\n");
> +             return;
> +     }
> +     spin_unlock_irqrestore(m2m_ctx->out_q_ctx.q.irqlock, flags);
> +
> +     if (m2m_dev->m2m_ops->job_ready
> +             && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
> +             dprintk("Driver not ready\n");
> +             return;
> +     }
> +
> +     list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
> +     m2m_ctx->job_flags |= TRANS_QUEUED;
> +
> +     spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
> +
> +     v4l2_m2m_try_run(m2m_dev);
> +}
> +
> +/**
> + * v4l2_m2m_job_finish() - inform the framework that a job has been
> finished
> + * and have it clean up
> + *
> + * Called by a driver to yield back the device after it has finished with
> it.
> + * Should be called as soon as possible after reaching a state which allows
> + * other instances to take control of the device.
> + *
> + * This function has to be called only after device_run() callback has been
> + * called on the driver. To prevent recursion, it should not be called
> directly
> + * from the device_run() callback though.
> + */
> +void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
> +                      struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
> +     if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
> +             dprintk("Called by an instance not currently running\n");
> +             return;
> +     }
> +
> +     list_del(&m2m_dev->curr_ctx->queue);
> +     m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
> +     m2m_dev->curr_ctx = NULL;
> +
> +     spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
> +
> +     /* This instance might have more buffers ready, but since we do not
> +      * allow more than one job on the job_queue per instance, each has
> +      * to be scheduled separately after the previous one finishes. */
> +     v4l2_m2m_try_schedule(m2m_ctx);
> +     v4l2_m2m_try_run(m2m_dev);
> +}
> +EXPORT_SYMBOL(v4l2_m2m_job_finish);
> +
> +/**
> + * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer
> + */
> +int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                  struct v4l2_requestbuffers *reqbufs)
> +{
> +     struct videobuf_queue *vq;
> +
> +     vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type);
> +     return videobuf_reqbufs(vq, reqbufs);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
> +
> +/**
> + * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer
> + *
> + * See v4l2_m2m_mmap() documentation for details.
> + */
> +int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                   struct v4l2_buffer *buf)
> +{
> +     struct videobuf_queue *vq;
> +     int ret;
> +
> +     vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> +     ret = videobuf_querybuf(vq, buf);
> +
> +     if (buf->memory == V4L2_MEMORY_MMAP
> +         && vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
> +             buf->m.offset += DST_QUEUE_OFF_BASE;
> +     }
[Hiremath, Vaibhav] Don't you think we should check for ret value also here? Should it be something -

if (!ret && buf->memory == V4L2_MEMORY_MMAP
                        && vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
        buf->m.offset += DST_QUEUE_OFF_BASE;
}

> +
> +     return ret;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
> +
> +/**
> + * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on
> + * the type
> + */
> +int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +               struct v4l2_buffer *buf)
> +{
> +     struct videobuf_queue *vq;
> +     int ret;
> +
> +     vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
> +     ret = videobuf_qbuf(vq, buf);
> +     if (!ret)
> +             v4l2_m2m_try_schedule(m2m_ctx);
> +
> +     return ret;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
> +
> +/**
> + * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on
> + * the type
> + */
> +int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                struct v4l2_buffer *buf)
> +{
> +     struct videobuf_queue *vq;
> +
> +     vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);


[Hiremath, Vaibhav] Does it make sense to check the return value here?

> +     return videobuf_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
> +
> +/**
> + * v4l2_m2m_streamon() - turn on streaming for a video queue
> + */
> +int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                   enum v4l2_buf_type type)
> +{
> +     struct videobuf_queue *vq;
> +     int ret;
> +
> +     vq = v4l2_m2m_get_vq(m2m_ctx, type);
> +     ret = videobuf_streamon(vq);
> +     if (!ret)
> +             v4l2_m2m_try_schedule(m2m_ctx);
> +
> +     return ret;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_streamon);
> +
> +/**
> + * v4l2_m2m_streamoff() - turn off streaming for a video queue
> + */
> +int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                    enum v4l2_buf_type type)
> +{
> +     struct videobuf_queue *vq;
> +
> +     vq = v4l2_m2m_get_vq(m2m_ctx, type);

[Hiremath, Vaibhav] Ditto and also applies to other places (wherever required).

Thanks,
Vaibhav

> +     return videobuf_streamoff(vq);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
> +
> +/**
> + * v4l2_m2m_poll() - poll replacement, for destination buffers only
> + *
> + * Call from the driver's poll() function. Will poll both queues. If a
> buffer
> + * is available to dequeue (with dqbuf) from the source queue, this will
> + * indicate that a non-blocking write can be performed, while read will be
> + * returned in case of the destination queue.
> + */
> +unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                        struct poll_table_struct *wait)
> +{
> +     struct videobuf_queue *src_q, *dst_q;
> +     struct videobuf_buffer *src_vb = NULL, *dst_vb = NULL;
> +     unsigned int rc = 0;
> +
> +     src_q = v4l2_m2m_get_src_vq(m2m_ctx);
> +     dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
> +
> +     mutex_lock(&src_q->vb_lock);
> +     mutex_lock(&dst_q->vb_lock);
> +
> +     if (src_q->streaming && !list_empty(&src_q->stream))
> +             src_vb = list_first_entry(&src_q->stream,
> +                                       struct videobuf_buffer, stream);
> +     if (dst_q->streaming && !list_empty(&dst_q->stream))
> +             dst_vb = list_first_entry(&dst_q->stream,
> +                                       struct videobuf_buffer, stream);
> +
> +     if (!src_vb && !dst_vb) {
> +             rc = POLLERR;
> +             goto end;
> +     }
> +
> +     if (src_vb) {
> +             poll_wait(file, &src_vb->done, wait);
> +             if (src_vb->state == VIDEOBUF_DONE
> +                 || src_vb->state == VIDEOBUF_ERROR)
> +                     rc |= POLLOUT | POLLWRNORM;
> +     }
> +     if (dst_vb) {
> +             poll_wait(file, &dst_vb->done, wait);
> +             if (dst_vb->state == VIDEOBUF_DONE
> +                 || dst_vb->state == VIDEOBUF_ERROR)
> +                     rc |= POLLIN | POLLRDNORM;
> +     }
> +
> +end:
> +     mutex_unlock(&dst_q->vb_lock);
> +     mutex_unlock(&src_q->vb_lock);
> +     return rc;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_poll);
> +
> +/**
> + * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer
> + *
> + * Call from driver's mmap() function. Will handle mmap() for both queues
> + * seamlessly for videobuffer, which will receive normal per-queue offsets
> and
> + * proper videobuf queue pointers. The differentiation is made outside
> videobuf
> + * by adding a predefined offset to buffers from one of the queues and
> + * subtracting it before passing it back to videobuf. Only drivers (and
> + * thus applications) receive modified offsets.
> + */
> +int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                      struct vm_area_struct *vma)
> +{
> +     unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
> +     struct videobuf_queue *vq;
> +
> +     if (offset < DST_QUEUE_OFF_BASE) {
> +             vq = v4l2_m2m_get_src_vq(m2m_ctx);
> +     } else {
> +             vq = v4l2_m2m_get_dst_vq(m2m_ctx);
> +             vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
> +     }
> +
> +     return videobuf_mmap_mapper(vq, vma);
> +}
> +EXPORT_SYMBOL(v4l2_m2m_mmap);
> +
> +/**
> + * v4l2_m2m_init() - initialize per-driver m2m data
> + *
> + * Usually called from driver's probe() function.
> + */
> +struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops)
> +{
> +     struct v4l2_m2m_dev *m2m_dev;
> +
> +     if (!m2m_ops)
> +             return ERR_PTR(-EINVAL);
> +
> +     BUG_ON(!m2m_ops->device_run);
> +     BUG_ON(!m2m_ops->job_abort);
> +
> +     m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL);
> +     if (!m2m_dev)
> +             return ERR_PTR(-ENOMEM);
> +
> +     m2m_dev->curr_ctx = NULL;
> +     m2m_dev->m2m_ops = m2m_ops;
> +     INIT_LIST_HEAD(&m2m_dev->job_queue);
> +     spin_lock_init(&m2m_dev->job_spinlock);
> +
> +     return m2m_dev;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_init);
> +
> +/**
> + * v4l2_m2m_release() - cleans up and frees a m2m_dev structure
> + *
> + * Usually called from driver's remove() function.
> + */
> +void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev)
> +{
> +     kfree(m2m_dev);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_release);
> +
> +/**
> + * v4l2_m2m_ctx_init() - allocate and initialize a m2m context
> + * @priv - driver's instance private data
> + * @m2m_dev - a previously initialized m2m_dev struct
> + * @vq_init - a callback for queue type-specific initialization function to
> be
> + * used for initializing videobuf_queues
> + *
> + * Usually called from driver's open() function.
> + */
> +struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev
> *m2m_dev,
> +                     void (*vq_init)(void *priv, struct videobuf_queue *,
> +                                     enum v4l2_buf_type))
> +{
> +     struct v4l2_m2m_ctx *m2m_ctx;
> +     struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx;
> +
> +     if (!vq_init)
> +             return ERR_PTR(-EINVAL);
> +
> +     m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL);
> +     if (!m2m_ctx)
> +             return ERR_PTR(-ENOMEM);
> +
> +     m2m_ctx->priv = priv;
> +     m2m_ctx->m2m_dev = m2m_dev;
> +
> +     out_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
> +     cap_q_ctx = get_queue_ctx(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> +
> +     INIT_LIST_HEAD(&out_q_ctx->rdy_queue);
> +     INIT_LIST_HEAD(&cap_q_ctx->rdy_queue);
> +
> +     INIT_LIST_HEAD(&m2m_ctx->queue);
> +
> +     vq_init(priv, &out_q_ctx->q, V4L2_BUF_TYPE_VIDEO_OUTPUT);
> +     vq_init(priv, &cap_q_ctx->q, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> +     out_q_ctx->q.priv_data = cap_q_ctx->q.priv_data = priv;
> +
> +     return m2m_ctx;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init);
> +
> +/**
> + * v4l2_m2m_ctx_release() - release m2m context
> + *
> + * Usually called from driver's release() function.
> + */
> +void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     struct v4l2_m2m_dev *m2m_dev;
> +     struct videobuf_buffer *vb;
> +     unsigned long flags;
> +
> +     m2m_dev = m2m_ctx->m2m_dev;
> +
> +     spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
> +     if (m2m_ctx->job_flags & TRANS_RUNNING) {
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
> +             m2m_dev->m2m_ops->job_abort(m2m_ctx->priv);
> +             dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx);
> +             vb = v4l2_m2m_next_dst_buf(m2m_ctx);
> +             BUG_ON(NULL == vb);
> +             wait_event(vb->done, vb->state != VIDEOBUF_ACTIVE
> +                                  && vb->state != VIDEOBUF_QUEUED);
> +     } else if (m2m_ctx->job_flags & TRANS_QUEUED) {
> +             list_del(&m2m_ctx->queue);
> +             m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING);
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
> +             dprintk("m2m_ctx: %p had been on queue and was removed\n",
> +                     m2m_ctx);
> +     } else {
> +             /* Do nothing, was not on queue/running */
> +             spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
> +     }
> +
> +     videobuf_stop(&m2m_ctx->cap_q_ctx.q);
> +     videobuf_stop(&m2m_ctx->out_q_ctx.q);
> +
> +     videobuf_mmap_free(&m2m_ctx->cap_q_ctx.q);
> +     videobuf_mmap_free(&m2m_ctx->out_q_ctx.q);
> +
> +     kfree(m2m_ctx);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release);
> +
> +/**
> + * v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list.
> + *
> + * Call from buf_queue(), videobuf_queue_ops callback.
> + *
> + * Locking: Caller holds q->irqlock (taken by videobuf before calling
> buf_queue
> + * callback in the driver).
> + */
> +void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue
> *vq,
> +                     struct videobuf_buffer *vb)
> +{
> +     struct v4l2_m2m_queue_ctx *q_ctx;
> +
> +     q_ctx = get_queue_ctx(m2m_ctx, vq->type);
> +     if (!q_ctx)
> +             return;
> +
> +     list_add_tail(&vb->queue, &q_ctx->rdy_queue);
> +     q_ctx->num_rdy++;
> +
> +     vb->state = VIDEOBUF_QUEUED;
> +}
> +EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
> +
> diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
> new file mode 100644
> index 0000000..8d149f1
> --- /dev/null
> +++ b/include/media/v4l2-mem2mem.h
> @@ -0,0 +1,201 @@
> +/*
> + * Memory-to-memory device framework for Video for Linux 2.
> + *
> + * Helper functions for devices that use memory buffers for both source
> + * and destination.
> + *
> + * Copyright (c) 2009 Samsung Electronics Co., Ltd.
> + * Pawel Osciak, <p.osciak@xxxxxxxxxxx>
> + * Marek Szyprowski, <m.szyprowski@xxxxxxxxxxx>
> + *
> + * 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; either version 2 of the
> + * License, or (at your option) any later version
> + */
> +
> +#ifndef _MEDIA_V4L2_MEM2MEM_H
> +#define _MEDIA_V4L2_MEM2MEM_H
> +
> +#include <media/videobuf-core.h>
> +
> +/**
> + * struct v4l2_m2m_ops - mem-to-mem device driver callbacks
> + * @device_run:      required. Begin the actual job (transaction) inside this
> + *           callback.
> + *           The job does NOT have to end before this callback returns
> + *           (and it will be the usual case). When the job finishes,
> + *           v4l2_m2m_job_finish() has to be called.
> + * @job_ready:       optional. Should return 0 if the driver does not have a
> job
> + *           fully prepared to run yet (i.e. it will not be able to finish a
> + *           transaction without sleeping). If not provided, it will be
> + *           assumed that one source and one destination buffer are all
> + *           that is required for the driver to perform one full transaction.
> + *           This method may not sleep.
> + * @job_abort:       required. Informs the driver that it has to abort the
> currently
> + *           running transaction as soon as possible (i.e. as soon as it can
> + *           stop the device safely; e.g. in the next interrupt handler),
> + *           even if the transaction would not have been finished by then.
> + *           After the driver performs the necessary steps, it has to call
> + *           v4l2_m2m_job_finish() (as if the transaction ended normally).
> + *           This function does not have to (and will usually not) wait
> + *           until the device enters a state when it can be stopped.
> + */
> +struct v4l2_m2m_ops {
> +     void (*device_run)(void *priv);
> +     int (*job_ready)(void *priv);
> +     void (*job_abort)(void *priv);
> +};
> +
> +struct v4l2_m2m_dev;
> +
> +struct v4l2_m2m_queue_ctx {
> +/* private: internal use only */
> +     struct videobuf_queue   q;
> +
> +     /* Queue for buffers ready to be processed as soon as this
> +      * instance receives access to the device */
> +     struct list_head        rdy_queue;
> +     u8                      num_rdy;
> +};
> +
> +struct v4l2_m2m_ctx {
> +/* private: internal use only */
> +     struct v4l2_m2m_dev             *m2m_dev;
> +
> +     /* Capture (output to memory) queue context */
> +     struct v4l2_m2m_queue_ctx       cap_q_ctx;
> +
> +     /* Output (input from memory) queue context */
> +     struct v4l2_m2m_queue_ctx       out_q_ctx;
> +
> +     /* For device job queue */
> +     struct list_head                queue;
> +     unsigned long                   job_flags;
> +
> +     /* Instance private data */
> +     void                            *priv;
> +};
> +
> +void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev);
> +
> +struct videobuf_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
> +                                    enum v4l2_buf_type type);
> +
> +void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
> +                      struct v4l2_m2m_ctx *m2m_ctx);
> +
> +int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                  struct v4l2_requestbuffers *reqbufs);
> +
> +int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                   struct v4l2_buffer *buf);
> +
> +int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +               struct v4l2_buffer *buf);
> +int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                struct v4l2_buffer *buf);
> +
> +int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                   enum v4l2_buf_type type);
> +int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                    enum v4l2_buf_type type);
> +
> +unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +                        struct poll_table_struct *wait);
> +
> +int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
> +               struct vm_area_struct *vma);
> +
> +struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops);
> +void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev);
> +
> +struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(void *priv, struct v4l2_m2m_dev
> *m2m_dev,
> +                     void (*vq_init)(void *priv, struct videobuf_queue *,
> +                                     enum v4l2_buf_type));
> +void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx);
> +
> +void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct videobuf_queue
> *vq,
> +                     struct videobuf_buffer *vb);
> +
> +/**
> + * v4l2_m2m_num_src_bufs_ready() - return the number of source buffers
> ready for
> + * use
> + */
> +static inline
> +unsigned int v4l2_m2m_num_src_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     return m2m_ctx->cap_q_ctx.num_rdy;
> +}
> +
> +/**
> + * v4l2_m2m_num_src_bufs_ready() - return the number of destination buffers
> + * ready for use
> + */
> +static inline
> +unsigned int v4l2_m2m_num_dst_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     return m2m_ctx->out_q_ctx.num_rdy;
> +}
> +
> +void *v4l2_m2m_next_buf(struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type
> type);
> +
> +/**
> + * v4l2_m2m_next_src_buf() - return next source buffer from the list of
> ready
> + * buffers
> + */
> +static inline void *v4l2_m2m_next_src_buf(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     return v4l2_m2m_next_buf(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
> +}
> +
> +/**
> + * v4l2_m2m_next_dst_buf() - return next destination buffer from the list
> of
> + * ready buffers
> + */
> +static inline void *v4l2_m2m_next_dst_buf(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     return v4l2_m2m_next_buf(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> +}
> +
> +/**
> + * v4l2_m2m_get_src_vq() - return videobuf_queue for source buffers
> + */
> +static inline
> +struct videobuf_queue *v4l2_m2m_get_src_vq(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     return v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
> +}
> +
> +/**
> + * v4l2_m2m_get_dst_vq() - return videobuf_queue for destination buffers
> + */
> +static inline
> +struct videobuf_queue *v4l2_m2m_get_dst_vq(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     return v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> +}
> +
> +void *v4l2_m2m_buf_remove(struct v4l2_m2m_ctx *m2m_ctx,
> +                       enum v4l2_buf_type type);
> +
> +/**
> + * v4l2_m2m_src_buf_remove() - take off a source buffer from the list of
> ready
> + * buffers and return it
> + */
> +static inline void *v4l2_m2m_src_buf_remove(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     return v4l2_m2m_buf_remove(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
> +}
> +
> +/**
> + * v4l2_m2m_dst_buf_remove() - take off a destination buffer from the list
> of
> + * ready buffers and return it
> + */
> +static inline void *v4l2_m2m_dst_buf_remove(struct v4l2_m2m_ctx *m2m_ctx)
> +{
> +     return v4l2_m2m_buf_remove(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
> +}
> +
> +#endif /* _MEDIA_V4L2_MEM2MEM_H */
> +
> --
> 1.7.1.rc1.12.ga601

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux