Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=f4071b85ea0ca3bd06f63c330562b4cfdffa8473
Commit:     f4071b85ea0ca3bd06f63c330562b4cfdffa8473
Parent:     3562c43be8cfd6e300508d7c33acebf3369eacd3
Author:     Hans Verkuil <[EMAIL PROTECTED]>
AuthorDate: Sat Jul 28 12:07:12 2007 -0300
Committer:  Mauro Carvalho Chehab <[EMAIL PROTECTED]>
CommitDate: Tue Oct 9 22:05:32 2007 -0300

    V4L/DVB (6046): ivtv: always steal full frames if out of buffers.
    
    When there are no more free buffers, then buffers are stolen from the
    predma queue. Buffers should be stolen from the head of that queue (which
    is where the most recently added buffers are) and all buffers belonging
    to a frame should be stolen. Otherwise 'half-frames' would remain in the
    queue, which leads to ugly playback and complete sync failure for YUV
    buffers.
    
    Signed-off-by: Hans Verkuil <[EMAIL PROTECTED]>
    Signed-off-by: Mauro Carvalho Chehab <[EMAIL PROTECTED]>
---
 drivers/media/video/ivtv/ivtv-driver.h  |    9 ++++-
 drivers/media/video/ivtv/ivtv-fileops.c |    3 +-
 drivers/media/video/ivtv/ivtv-irq.c     |    5 ++-
 drivers/media/video/ivtv/ivtv-queue.c   |   54 ++++++++++++++++++------------
 4 files changed, 45 insertions(+), 26 deletions(-)

diff --git a/drivers/media/video/ivtv/ivtv-driver.h 
b/drivers/media/video/ivtv/ivtv-driver.h
index 0a1c16a..f5de2fd 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -356,7 +356,7 @@ struct ivtv_mailbox_data {
 };
 
 /* per-buffer bit flags */
-#define IVTV_F_B_NEED_BUF_SWAP  0      /* this buffer should be byte swapped */
+#define IVTV_F_B_NEED_BUF_SWAP  (1 << 0)       /* this buffer should be byte 
swapped */
 
 /* per-stream, s_flags */
 #define IVTV_F_S_DMA_PENDING   0       /* this stream has pending DMA */
@@ -437,7 +437,8 @@ struct ivtv_dma_page_info {
 struct ivtv_buffer {
        struct list_head list;
        dma_addr_t dma_handle;
-       unsigned long b_flags;
+       unsigned short b_flags;
+       unsigned short dma_xfer_cnt;
        char *buf;
 
        u32 bytesused;
@@ -487,6 +488,10 @@ struct ivtv_stream {
        struct ivtv_queue q_dma;        /* waiting for DMA */
        struct ivtv_queue q_predma;     /* waiting for DMA */
 
+       /* DMA xfer counter, buffers belonging to the same DMA
+          xfer will have the same dma_xfer_cnt. */
+       u16 dma_xfer_cnt;
+
        /* Base Dev SG Array for cx23415/6 */
        struct ivtv_SG_element *SGarray;
        struct ivtv_SG_element *PIOarray;
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c 
b/drivers/media/video/ivtv/ivtv-fileops.c
index 908e640..076b008 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -247,8 +247,9 @@ static struct ivtv_buffer *ivtv_get_buffer(struct 
ivtv_stream *s, int non_block,
                /* do we have new data? */
                buf = ivtv_dequeue(s, &s->q_full);
                if (buf) {
-                       if (!test_and_clear_bit(IVTV_F_B_NEED_BUF_SWAP, 
&buf->b_flags))
+                       if ((buf->b_flags & IVTV_F_B_NEED_BUF_SWAP) == 0)
                                return buf;
+                       buf->b_flags &= ~IVTV_F_B_NEED_BUF_SWAP;
                        if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
                                /* byteswap MPG data */
                                ivtv_buf_swap(buf);
diff --git a/drivers/media/video/ivtv/ivtv-irq.c 
b/drivers/media/video/ivtv/ivtv-irq.c
index a97d55f..8644f3d 100644
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -214,6 +214,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 
data[CX2341X_MBOX_MA
                s->SGarray[idx].src = cpu_to_le32(offset);
                s->SGarray[idx].size = cpu_to_le32(s->buf_size);
                buf->bytesused = (size < s->buf_size) ? size : s->buf_size;
+               buf->dma_xfer_cnt = s->dma_xfer_cnt;
 
                s->q_predma.bytesused += buf->bytesused;
                size -= buf->bytesused;
@@ -286,7 +287,7 @@ static void dma_post(struct ivtv_stream *s)
                /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */
                if (s->type == IVTV_ENC_STREAM_TYPE_MPG ||
                    s->type == IVTV_ENC_STREAM_TYPE_VBI)
-                       set_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags);
+                       buf->b_flags |= IVTV_F_B_NEED_BUF_SWAP;
        }
        if (buf)
                buf->bytesused += s->dma_last_offset;
@@ -396,12 +397,14 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s)
                }
                itv->vbi.dma_offset = s_vbi->dma_offset;
                s_vbi->SG_length = 0;
+               s_vbi->dma_xfer_cnt++;
                set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
                IVTV_DEBUG_HI_DMA("include DMA for %s\n", s->name);
        }
 
        /* Mark last buffer size for Interrupt flag */
        s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
+       s->dma_xfer_cnt++;
 
        if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
                set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
diff --git a/drivers/media/video/ivtv/ivtv-queue.c 
b/drivers/media/video/ivtv/ivtv-queue.c
index a04f938..bff75ae 100644
--- a/drivers/media/video/ivtv/ivtv-queue.c
+++ b/drivers/media/video/ivtv/ivtv-queue.c
@@ -60,6 +60,7 @@ void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer 
*buf, struct ivtv_qu
                buf->bytesused = 0;
                buf->readpos = 0;
                buf->b_flags = 0;
+               buf->dma_xfer_cnt = 0;
        }
        spin_lock_irqsave(&s->qlock, flags);
        list_add_tail(&buf->list, &q->list);
@@ -87,7 +88,7 @@ struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, 
struct ivtv_queue *q)
 }
 
 static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
-               struct ivtv_queue *to, int clear, int full)
+               struct ivtv_queue *to, int clear)
 {
        struct ivtv_buffer *buf = list_entry(from->list.next, struct 
ivtv_buffer, list);
 
@@ -97,13 +98,7 @@ static void ivtv_queue_move_buf(struct ivtv_stream *s, 
struct ivtv_queue *from,
        from->bytesused -= buf->bytesused - buf->readpos;
        /* special handling for q_free */
        if (clear)
-               buf->bytesused = buf->readpos = buf->b_flags = 0;
-       else if (full) {
-               /* special handling for stolen buffers, assume
-                  all bytes are used. */
-               buf->bytesused = s->buf_size;
-               buf->readpos = buf->b_flags = 0;
-       }
+               buf->bytesused = buf->readpos = buf->b_flags = 
buf->dma_xfer_cnt = 0;
        to->buffers++;
        to->length += s->buf_size;
        to->bytesused += buf->bytesused - buf->readpos;
@@ -112,7 +107,7 @@ static void ivtv_queue_move_buf(struct ivtv_stream *s, 
struct ivtv_queue *from,
 /* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
    If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
    If 'steal' != NULL, then buffers may also taken from that queue if
-   needed.
+   needed, but only if 'from' is the free queue.
 
    The buffer is automatically cleared if it goes to the free queue. It is
    also cleared if buffers need to be taken from the 'steal' queue and
@@ -133,7 +128,7 @@ int ivtv_queue_move(struct ivtv_stream *s, struct 
ivtv_queue *from, struct ivtv_
        int rc = 0;
        int from_free = from == &s->q_free;
        int to_free = to == &s->q_free;
-       int bytes_available;
+       int bytes_available, bytes_steal;
 
        spin_lock_irqsave(&s->qlock, flags);
        if (needed_bytes == 0) {
@@ -142,32 +137,47 @@ int ivtv_queue_move(struct ivtv_stream *s, struct 
ivtv_queue *from, struct ivtv_
        }
 
        bytes_available = from_free ? from->length : from->bytesused;
-       bytes_available += steal ? steal->length : 0;
+       bytes_steal = (from_free && steal) ? steal->length : 0;
 
-       if (bytes_available < needed_bytes) {
+       if (bytes_available + bytes_steal < needed_bytes) {
                spin_unlock_irqrestore(&s->qlock, flags);
                return -ENOMEM;
        }
+       while (bytes_available < needed_bytes) {
+               struct ivtv_buffer *buf = list_entry(steal->list.prev, struct 
ivtv_buffer, list);
+               u16 dma_xfer_cnt = buf->dma_xfer_cnt;
+
+               /* move buffers from the tail of the 'steal' queue to the tail 
of the
+                  'from' queue. Always copy all the buffers with the same 
dma_xfer_cnt
+                  value, this ensures that you do not end up with partial 
frame data
+                  if one frame is stored in multiple buffers. */
+               while (dma_xfer_cnt == buf->dma_xfer_cnt) {
+                       list_move_tail(steal->list.prev, &from->list);
+                       rc++;
+                       steal->buffers--;
+                       steal->length -= s->buf_size;
+                       steal->bytesused -= buf->bytesused - buf->readpos;
+                       buf->bytesused = buf->readpos = buf->b_flags = 
buf->dma_xfer_cnt = 0;
+                       from->buffers++;
+                       from->length += s->buf_size;
+                       bytes_available += s->buf_size;
+                       if (list_empty(&steal->list))
+                               break;
+                       buf = list_entry(steal->list.prev, struct ivtv_buffer, 
list);
+               }
+       }
        if (from_free) {
                u32 old_length = to->length;
 
                while (to->length - old_length < needed_bytes) {
-                       if (list_empty(&from->list))
-                               from = steal;
-                       if (from == steal)
-                               rc++;           /* keep track of 'stolen' 
buffers */
-                       ivtv_queue_move_buf(s, from, to, 1, 0);
+                       ivtv_queue_move_buf(s, from, to, 1);
                }
        }
        else {
                u32 old_bytesused = to->bytesused;
 
                while (to->bytesused - old_bytesused < needed_bytes) {
-                       if (list_empty(&from->list))
-                               from = steal;
-                       if (from == steal)
-                               rc++;           /* keep track of 'stolen' 
buffers */
-                       ivtv_queue_move_buf(s, from, to, to_free, rc);
+                       ivtv_queue_move_buf(s, from, to, to_free);
                }
        }
        spin_unlock_irqrestore(&s->qlock, flags);
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to