V4L/DVB (6046): ivtv: always steal full frames if out of buffers.
authorHans Verkuil <hverkuil@xs4all.nl>
Sat, 28 Jul 2007 15:07:12 +0000 (12:07 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Wed, 10 Oct 2007 01:05:32 +0000 (22:05 -0300)
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 <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
drivers/media/video/ivtv/ivtv-driver.h
drivers/media/video/ivtv/ivtv-fileops.c
drivers/media/video/ivtv/ivtv-irq.c
drivers/media/video/ivtv/ivtv-queue.c

index 0a1c16ad10bb95e8f8dbcb1fc9e4a75213f3f948..f5de2fd01b1a3d8f75ed99f30754f8ef6fee40e2 100644 (file)
@@ -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;
index 908e640c274dc9f00318a761e78de6eb804dd363..076b0089a2b0b76dbdec284048720a16b4ad3d7f 100644 (file)
@@ -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);
index a97d55fd227730b27f5d8e10193ef87309d4e74e..8644f3dda31ea661051d3555ee24f30cfc736b1c 100644 (file)
@@ -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);
index a04f9387f63df4127668d42dfa9143154ce4e0c1..bff75aeee0a08e4c165b15640c2432bb90594d7f 100644 (file)
@@ -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);