Drivers can now finish processing on and return video buffers in
an arbitrary order. Before this patch, this was possible in a FIFO order
only. This is useful e.g. for video codecs, which often need to hold some
buffers (e.g. keyframes) for longer periods of time than others.

Signed-off-by: Pawel Osciak <p.osc...@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
---
 drivers/media/video/videobuf-core.c |  160 +++++++++++++++++++++++-----------
 include/media/videobuf-core.h       |    9 ++
 2 files changed, 117 insertions(+), 52 deletions(-)

diff --git a/drivers/media/video/videobuf-core.c 
b/drivers/media/video/videobuf-core.c
index 77899ca..ea5fd39 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -95,6 +95,27 @@ int videobuf_waiton(struct videobuf_buffer *vb, int 
non_blocking, int intr)
 }
 EXPORT_SYMBOL_GPL(videobuf_waiton);
 
+int videobuf_has_consumers(struct videobuf_queue *q)
+{
+       return waitqueue_active(&q->vb_done_wait);
+}
+EXPORT_SYMBOL_GPL(videobuf_has_consumers);
+
+void videobuf_buf_finish(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&q->vb_done_lock, flags);
+       list_add_tail(&vb->done_list, &q->vb_done_list);
+       spin_unlock_irqrestore(&q->vb_done_lock, flags);
+
+       spin_lock_irqsave(q->irqlock, flags);
+       wake_up(&vb->done);
+       wake_up_interruptible(&q->vb_done_wait);
+       spin_unlock_irqrestore(q->irqlock, flags);
+}
+EXPORT_SYMBOL_GPL(videobuf_buf_finish);
+
 int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb,
                    struct v4l2_framebuffer *fbuf)
 {
@@ -153,7 +174,10 @@ void videobuf_queue_core_init(struct videobuf_queue *q,
 
        mutex_init(&q->vb_lock);
        init_waitqueue_head(&q->wait);
+       init_waitqueue_head(&q->vb_done_wait);
        INIT_LIST_HEAD(&q->stream);
+       INIT_LIST_HEAD(&q->vb_done_list);
+       spin_lock_init(&q->vb_done_lock);
 }
 EXPORT_SYMBOL_GPL(videobuf_queue_core_init);
 
@@ -217,6 +241,7 @@ void videobuf_queue_cancel(struct videobuf_queue *q)
                        wake_up_all(&q->bufs[i]->done);
                }
        }
+       wake_up_all(&q->vb_done_wait);
        spin_unlock_irqrestore(q->irqlock, flags);
 
        /* free all buffers + clear queue */
@@ -603,67 +628,81 @@ done:
 EXPORT_SYMBOL_GPL(videobuf_qbuf);
 
 /* Locking: Caller holds q->vb_lock */
-static int stream_next_buffer_check_queue(struct videobuf_queue *q, int 
noblock)
+static int wait_for_buffer(struct videobuf_queue *q, int nonblocking)
 {
-       int retval;
+       int retval = 0;
 
 checks:
        if (!q->streaming) {
-               dprintk(1, "next_buffer: Not streaming\n");
+               dprintk(1, "Not streaming\n");
                retval = -EINVAL;
-               goto done;
+               goto end;
        }
 
-       if (list_empty(&q->stream)) {
-               if (noblock) {
+       /*
+        * 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->vb_done_list)) {
+               if (nonblocking) {
+                       dprintk(1, "Nonblocking and no buffers to dequeue\n");
                        retval = -EAGAIN;
-                       dprintk(2, "next_buffer: no buffers to dequeue\n");
-                       goto done;
-               } else {
-                       dprintk(2, "next_buffer: waiting on buffer\n");
-
-                       /* Drop lock to avoid deadlock with qbuf */
-                       mutex_unlock(&q->vb_lock);
-
-                       /* Checking list_empty and streaming is safe without
-                        * locks because we goto checks to validate while
-                        * holding locks before proceeding */
-                       retval = wait_event_interruptible(q->wait,
-                               !list_empty(&q->stream) || !q->streaming);
-                       mutex_lock(&q->vb_lock);
-
-                       if (retval)
-                               goto done;
-
-                       goto checks;
+                       goto end;
                }
-       }
 
-       retval = 0;
+               /*
+                * We are streaming and nonblocking, wait for another buffer to
+                * become ready or for streamoff. vb_lock is released to allow
+                * streamoff and qbuf in parallel.
+                */
+               mutex_unlock(&q->vb_lock);
+               /*
+                * Although the mutex is released here, we will be reevaluating
+                * both conditions again after reacquiring it.
+                */
+               retval = wait_event_interruptible(q->vb_done_wait,
+                               !list_empty(&q->vb_done_list) || !q->streaming);
+               mutex_lock(&q->vb_lock);
+
+               if (retval)
+                       goto end;
+
+               goto checks;
+       }
 
-done:
+       /*
+        * At least one buffer is on the vb_done_list and nothing can be removed
+        * from it without acquiring the vb_lock, which we are holding now.
+        */
+end:
        return retval;
 }
 
 /* Locking: Caller holds q->vb_lock */
-static int stream_next_buffer(struct videobuf_queue *q,
-                       struct videobuf_buffer **vb, int nonblocking)
+static int get_done_buffer(struct videobuf_queue *q,
+                          struct videobuf_buffer **vb, int nonblocking)
 {
-       int retval;
-       struct videobuf_buffer *buf = NULL;
-
-       retval = stream_next_buffer_check_queue(q, nonblocking);
-       if (retval)
-               goto done;
-
-       buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
-       retval = videobuf_waiton(buf, nonblocking, 1);
-       if (retval < 0)
-               goto done;
-
-       *vb = buf;
-done:
-       return retval;
+       unsigned long flags;
+       int ret = 0;
+
+       ret = wait_for_buffer(q, nonblocking);
+       if (ret)
+               goto end;
+
+       /*
+        * vb_lock has been held since we last verified that vb_done_list is
+        * not empty, no need for another list_empty().
+        */
+       spin_lock_irqsave(&q->vb_done_lock, flags);
+       *vb = list_first_entry(&q->vb_done_list, struct videobuf_buffer,
+                               done_list);
+       list_del(&((*vb)->done_list));
+       spin_unlock_irqrestore(&q->vb_done_lock, flags);
+
+end:
+       return ret;
 }
 
 int videobuf_dqbuf(struct videobuf_queue *q,
@@ -676,7 +715,7 @@ int videobuf_dqbuf(struct videobuf_queue *q,
 
        mutex_lock(&q->vb_lock);
 
-       retval = stream_next_buffer(q, &buf, nonblocking);
+       retval = get_done_buffer(q, &buf, nonblocking);
        if (retval < 0) {
                dprintk(1, "dqbuf: next_buffer error: %i\n", retval);
                goto done;
@@ -1055,12 +1094,25 @@ unsigned int videobuf_poll_stream(struct file *file,
 {
        struct videobuf_buffer *buf = NULL;
        unsigned int rc = 0;
+       unsigned long flags;
 
        mutex_lock(&q->vb_lock);
        if (q->streaming) {
-               if (!list_empty(&q->stream))
-                       buf = list_entry(q->stream.next,
-                                        struct videobuf_buffer, stream);
+               if (list_empty(&q->stream)) {
+                       rc = POLLERR;
+                       goto end;
+               }
+
+               poll_wait(file, &q->vb_done_wait, wait);
+
+               spin_lock_irqsave(&q->vb_done_lock, flags);
+               if (!list_empty(&q->vb_done_list))
+                       buf = list_first_entry(&q->vb_done_list,
+                                               struct videobuf_buffer,
+                                               done_list);
+               spin_unlock_irqrestore(&q->vb_done_lock, flags);
+               if (!buf)
+                       goto end;
        } else {
                if (!q->reading)
                        __videobuf_read_start(q);
@@ -1074,12 +1126,15 @@ unsigned int videobuf_poll_stream(struct file *file,
                        q->read_off = 0;
                }
                buf = q->read_buf;
+               if (!buf) {
+                       rc = POLLERR;
+                       goto end;
+               }
+
+               poll_wait(file, &buf->done, wait);
        }
-       if (!buf)
-               rc = POLLERR;
 
        if (0 == rc) {
-               poll_wait(file, &buf->done, wait);
                if (buf->state == VIDEOBUF_DONE ||
                    buf->state == VIDEOBUF_ERROR) {
                        switch (q->type) {
@@ -1094,6 +1149,7 @@ unsigned int videobuf_poll_stream(struct file *file,
                        }
                }
        }
+end:
        mutex_unlock(&q->vb_lock);
        return rc;
 }
diff --git a/include/media/videobuf-core.h b/include/media/videobuf-core.h
index b1f7bf4..7b1cc94 100644
--- a/include/media/videobuf-core.h
+++ b/include/media/videobuf-core.h
@@ -82,6 +82,7 @@ struct videobuf_buffer {
        enum v4l2_field         field;
        enum videobuf_state     state;
        struct list_head        stream;  /* QBUF/DQBUF list */
+       struct list_head        done_list;
 
        /* touched by irq handler */
        struct list_head        queue;
@@ -160,6 +161,10 @@ struct videobuf_queue {
 
        wait_queue_head_t          wait; /* wait if queue is empty */
 
+       wait_queue_head_t          vb_done_wait;
+       struct list_head           vb_done_list;
+       spinlock_t                 vb_done_lock;
+
        enum v4l2_buf_type         type;
        unsigned int               inputs; /* for V4L2_BUF_FLAG_INPUT */
        unsigned int               msize;
@@ -206,6 +211,8 @@ void videobuf_queue_core_init(struct videobuf_queue *q,
 int  videobuf_queue_is_busy(struct videobuf_queue *q);
 void videobuf_queue_cancel(struct videobuf_queue *q);
 
+void videobuf_buf_finish(struct videobuf_queue *q, struct videobuf_buffer *vb);
+
 enum v4l2_field videobuf_next_field(struct videobuf_queue *q);
 int videobuf_reqbufs(struct videobuf_queue *q,
                     struct v4l2_requestbuffers *req);
@@ -235,6 +242,8 @@ unsigned int videobuf_poll_stream(struct file *file,
                                  struct videobuf_queue *q,
                                  poll_table *wait);
 
+int videobuf_has_consumers(struct videobuf_queue *q);
+
 int videobuf_mmap_setup(struct videobuf_queue *q,
                        unsigned int bcount, unsigned int bsize,
                        enum v4l2_memory memory);
-- 
1.7.1.rc1.12.ga601

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

Reply via email to