Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[safe/jmp/linux-2.6] / drivers / media / video / ivtv / ivtv-irq.c
index c3a047b..fba150a 100644 (file)
  */
 
 #include "ivtv-driver.h"
-#include "ivtv-firmware.h"
-#include "ivtv-fileops.h"
 #include "ivtv-queue.h"
 #include "ivtv-udma.h"
 #include "ivtv-irq.h"
-#include "ivtv-ioctl.h"
 #include "ivtv-mailbox.h"
 #include "ivtv-vbi.h"
 #include "ivtv-yuv.h"
 
 #define DMA_MAGIC_COOKIE 0x000001fe
 
-#define SLICED_VBI_PIO 1
-
 static void ivtv_dma_dec_start(struct ivtv_stream *s);
 
 static const int ivtv_stream_map[] = {
@@ -42,12 +37,37 @@ static const int ivtv_stream_map[] = {
        IVTV_ENC_STREAM_TYPE_VBI,
 };
 
-static inline int ivtv_use_pio(struct ivtv_stream *s)
+
+static void ivtv_pio_work_handler(struct ivtv *itv)
 {
-       struct ivtv *itv = s->itv;
+       struct ivtv_stream *s = &itv->streams[itv->cur_pio_stream];
+       struct ivtv_buffer *buf;
+       int i = 0;
+
+       IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n");
+       if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS ||
+                       s->v4l2dev == NULL || !ivtv_use_pio(s)) {
+               itv->cur_pio_stream = -1;
+               /* trigger PIO complete user interrupt */
+               write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
+               return;
+       }
+       IVTV_DEBUG_HI_DMA("Process PIO %s\n", s->name);
+       list_for_each_entry(buf, &s->q_dma.list, list) {
+               u32 size = s->sg_processing[i].size & 0x3ffff;
 
-       return s->dma == PCI_DMA_NONE ||
-           (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set);
+               /* Copy the data from the card to the buffer */
+               if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+                       memcpy_fromio(buf->buf, itv->dec_mem + s->sg_processing[i].src - IVTV_DECODER_OFFSET, size);
+               }
+               else {
+                       memcpy_fromio(buf->buf, itv->enc_mem + s->sg_processing[i].src, size);
+               }
+               i++;
+               if (i == s->sg_processing_size)
+                       break;
+       }
+       write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
 }
 
 void ivtv_irq_work_handler(struct work_struct *work)
@@ -56,8 +76,11 @@ void ivtv_irq_work_handler(struct work_struct *work)
 
        DEFINE_WAIT(wait);
 
+       if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags))
+               ivtv_pio_work_handler(itv);
+
        if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags))
-               vbi_work_handler(itv);
+               ivtv_vbi_work_handler(itv);
 
        if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags))
                ivtv_yuv_work_handler(itv);
@@ -71,12 +94,11 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
 {
        struct ivtv *itv = s->itv;
        struct ivtv_buffer *buf;
-       struct list_head *p;
        u32 bytes_needed = 0;
        u32 offset, size;
        u32 UVoffset = 0, UVsize = 0;
        int skip_bufs = s->q_predma.buffers;
-       int idx = s->SG_length;
+       int idx = s->sg_pending_size;
        int rc;
 
        /* sanity checks */
@@ -94,7 +116,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
                case IVTV_ENC_STREAM_TYPE_MPG:
                        offset = data[1];
                        size = data[2];
-                       s->dma_pts = 0;
+                       s->pending_pts = 0;
                        break;
 
                case IVTV_ENC_STREAM_TYPE_YUV:
@@ -102,13 +124,13 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
                        size = data[2];
                        UVoffset = data[3];
                        UVsize = data[4];
-                       s->dma_pts = ((u64) data[5] << 32) | data[6];
+                       s->pending_pts = ((u64) data[5] << 32) | data[6];
                        break;
 
                case IVTV_ENC_STREAM_TYPE_PCM:
                        offset = data[1] + 12;
                        size = data[2] - 12;
-                       s->dma_pts = read_dec(offset - 8) |
+                       s->pending_pts = read_dec(offset - 8) |
                                ((u64)(read_dec(offset - 12)) << 32);
                        if (itv->has_cx23415)
                                offset += IVTV_DECODER_OFFSET;
@@ -121,13 +143,13 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
                                IVTV_DEBUG_INFO("VBI offset == 0\n");
                                return -1;
                        }
-                       s->dma_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
+                       s->pending_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
                        break;
 
                case IVTV_DEC_STREAM_TYPE_VBI:
                        size = read_dec(itv->vbi.dec_start + 4) + 8;
                        offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start;
-                       s->dma_pts = 0;
+                       s->pending_pts = 0;
                        offset += IVTV_DECODER_OFFSET;
                        break;
                default:
@@ -136,17 +158,17 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
        }
 
        /* if this is the start of the DMA then fill in the magic cookie */
-       if (s->SG_length == 0) {
+       if (s->sg_pending_size == 0 && ivtv_use_dma(s)) {
                if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
                    s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
-                       s->dma_backup = read_dec(offset - IVTV_DECODER_OFFSET);
+                       s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET);
                        write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
                }
                else {
-                       s->dma_backup = read_enc(offset);
+                       s->pending_backup = read_enc(offset);
                        write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
                }
-               s->dma_offset = offset;
+               s->pending_offset = offset;
        }
 
        bytes_needed = size;
@@ -158,7 +180,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
                bytes_needed += UVsize;
        }
 
-       IVTV_DEBUG_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
+       IVTV_DEBUG_HI_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
                ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset);
 
        rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed);
@@ -173,29 +195,17 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
        }
        s->buffers_stolen = rc;
 
-       /* got the buffers, now fill in SGarray (DMA) or copy the data from the card
-          to the buffers (PIO). */
+       /* got the buffers, now fill in sg_pending */
        buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
        memset(buf->buf, 0, 128);
-       list_for_each(p, &s->q_predma.list) {
-               struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
-
+       list_for_each_entry(buf, &s->q_predma.list, list) {
                if (skip_bufs-- > 0)
                        continue;
-               if (!ivtv_use_pio(s)) {
-                       s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle);
-                       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;
-
-               /* If PIO, then copy the data from the card to the buffer */
-               if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
-                       memcpy_fromio(buf->buf, itv->dec_mem + offset - IVTV_DECODER_OFFSET, buf->bytesused);
-               }
-               else if (ivtv_use_pio(s)) {
-                       memcpy_fromio(buf->buf, itv->enc_mem + offset, buf->bytesused);
-               }
+               s->sg_pending[idx].dst = buf->dma_handle;
+               s->sg_pending[idx].src = offset;
+               s->sg_pending[idx].size = s->buf_size;
+               buf->bytesused = min(size, s->buf_size);
+               buf->dma_xfer_cnt = s->dma_xfer_cnt;
 
                s->q_predma.bytesused += buf->bytesused;
                size -= buf->bytesused;
@@ -211,7 +221,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
                }
                idx++;
        }
-       s->SG_length = idx;
+       s->sg_pending_size = idx;
        return 0;
 }
 
@@ -221,24 +231,19 @@ static void dma_post(struct ivtv_stream *s)
        struct ivtv_buffer *buf = NULL;
        struct list_head *p;
        u32 offset;
-       u32 *u32buf;
+       __le32 *u32buf;
        int x = 0;
 
-       if (ivtv_use_pio(s)) {
-               if (s->q_predma.bytesused)
-                       ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
-               s->SG_length = 0;
-       }
-       IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
+       IVTV_DEBUG_HI_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
                        s->name, s->dma_offset);
        list_for_each(p, &s->q_dma.list) {
                buf = list_entry(p, struct ivtv_buffer, list);
-               u32buf = (u32 *)buf->buf;
+               u32buf = (__le32 *)buf->buf;
 
                /* Sync Buffer */
                ivtv_buf_sync_for_cpu(s, buf);
 
-               if (x == 0) {
+               if (x == 0 && ivtv_use_dma(s)) {
                        offset = s->dma_last_offset;
                        if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
                        {
@@ -273,15 +278,17 @@ 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;
        if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
-               /* Parse and Groom VBI Data */
-               s->q_dma.bytesused -= buf->bytesused;
-               ivtv_process_vbi_data(itv, buf, 0, s->type);
-               s->q_dma.bytesused += buf->bytesused;
+               list_for_each_entry(buf, &s->q_dma.list, list) {
+                       /* Parse and Groom VBI Data */
+                       s->q_dma.bytesused -= buf->bytesused;
+                       ivtv_process_vbi_data(itv, buf, 0, s->type);
+                       s->q_dma.bytesused += buf->bytesused;
+               }
                if (s->id == -1) {
                        ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
                        return;
@@ -295,40 +302,61 @@ static void dma_post(struct ivtv_stream *s)
 void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
 {
        struct ivtv *itv = s->itv;
+       struct yuv_playback_info *yi = &itv->yuv_info;
+       u8 frame = yi->draw_frame;
+       struct yuv_frame_info *f = &yi->new_frame_info[frame];
        struct ivtv_buffer *buf;
-       struct list_head *p;
-       u32 y_size = itv->params.height * itv->params.width;
+       u32 y_size = 720 * ((f->src_h + 31) & ~31);
        u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
        int y_done = 0;
        int bytes_written = 0;
        unsigned long flags = 0;
        int idx = 0;
 
-       IVTV_DEBUG_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
-       buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
-       list_for_each(p, &s->q_predma.list) {
-               struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+       IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
 
+       /* Insert buffer block for YUV if needed */
+       if (s->type == IVTV_DEC_STREAM_TYPE_YUV && f->offset_y) {
+               if (yi->blanking_dmaptr) {
+                       s->sg_pending[idx].src = yi->blanking_dmaptr;
+                       s->sg_pending[idx].dst = offset;
+                       s->sg_pending[idx].size = 720 * 16;
+               }
+               offset += 720 * 16;
+               idx++;
+       }
+
+       list_for_each_entry(buf, &s->q_predma.list, list) {
                /* YUV UV Offset from Y Buffer */
-               if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) {
+               if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done &&
+                               (bytes_written + buf->bytesused) >= y_size) {
+                       s->sg_pending[idx].src = buf->dma_handle;
+                       s->sg_pending[idx].dst = offset;
+                       s->sg_pending[idx].size = y_size - bytes_written;
                        offset = uv_offset;
+                       if (s->sg_pending[idx].size != buf->bytesused) {
+                               idx++;
+                               s->sg_pending[idx].src =
+                                 buf->dma_handle + s->sg_pending[idx - 1].size;
+                               s->sg_pending[idx].dst = offset;
+                               s->sg_pending[idx].size =
+                                  buf->bytesused - s->sg_pending[idx - 1].size;
+                               offset += s->sg_pending[idx].size;
+                       }
                        y_done = 1;
+               } else {
+                       s->sg_pending[idx].src = buf->dma_handle;
+                       s->sg_pending[idx].dst = offset;
+                       s->sg_pending[idx].size = buf->bytesused;
+                       offset += buf->bytesused;
                }
-               s->SGarray[idx].src = cpu_to_le32(buf->dma_handle);
-               s->SGarray[idx].dst = cpu_to_le32(offset);
-               s->SGarray[idx].size = cpu_to_le32(buf->bytesused);
-
-               offset += buf->bytesused;
                bytes_written += buf->bytesused;
 
                /* Sync SG buffers */
                ivtv_buf_sync_for_device(s, buf);
                idx++;
        }
-       s->SG_length = idx;
-
-       /* Mark last buffer size for Interrupt flag */
-       s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
+       s->sg_pending_size = idx;
 
        /* Sync Hardware SG List of buffers */
        ivtv_stream_sync_for_device(s);
@@ -344,6 +372,38 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
                spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
 }
 
+static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s)
+{
+       struct ivtv *itv = s->itv;
+
+       s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
+       s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
+       s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
+       s->sg_processed++;
+       /* Sync Hardware SG List of buffers */
+       ivtv_stream_sync_for_device(s);
+       write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR);
+       write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
+       itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
+       add_timer(&itv->dma_timer);
+}
+
+static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s)
+{
+       struct ivtv *itv = s->itv;
+
+       s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
+       s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
+       s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
+       s->sg_processed++;
+       /* Sync Hardware SG List of buffers */
+       ivtv_stream_sync_for_device(s);
+       write_reg(s->sg_handle, IVTV_REG_DECDMAADDR);
+       write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+       itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
+       add_timer(&itv->dma_timer);
+}
+
 /* start the encoder DMA */
 static void ivtv_dma_enc_start(struct ivtv_stream *s)
 {
@@ -351,10 +411,13 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s)
        struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
        int i;
 
+       IVTV_DEBUG_HI_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name);
+
        if (s->q_predma.bytesused)
                ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
-       IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
-       s->SGarray[s->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256);
+
+       if (ivtv_use_dma(s))
+               s->sg_pending[s->sg_pending_size - 1].size += 256;
 
        /* If this is an MPEG stream, and VBI data is also pending, then append the
           VBI DMA to the MPEG DMA and transfer both sets of data at once.
@@ -365,30 +428,42 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s)
           sure we only use the MPEG DMA to transfer the VBI DMA if both are in
           use. This way no conflicts occur. */
        clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
-       if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length &&
-                       s->SG_length + s_vbi->SG_length <= s->buffers) {
+       if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->sg_pending_size &&
+                       s->sg_pending_size + s_vbi->sg_pending_size <= s->buffers) {
                ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
-               s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256);
-               for (i = 0; i < s_vbi->SG_length; i++) {
-                       s->SGarray[s->SG_length++] = s_vbi->SGarray[i];
+               if (ivtv_use_dma(s_vbi))
+                       s_vbi->sg_pending[s_vbi->sg_pending_size - 1].size += 256;
+               for (i = 0; i < s_vbi->sg_pending_size; i++) {
+                       s->sg_pending[s->sg_pending_size++] = s_vbi->sg_pending[i];
                }
-               itv->vbi.dma_offset = s_vbi->dma_offset;
-               s_vbi->SG_length = 0;
+               s_vbi->dma_offset = s_vbi->pending_offset;
+               s_vbi->sg_pending_size = 0;
+               s_vbi->dma_xfer_cnt++;
                set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
-               IVTV_DEBUG_DMA("include DMA for %s\n", s->name);
+               IVTV_DEBUG_HI_DMA("include DMA for %s\n", s_vbi->name);
        }
 
-       /* Mark last buffer size for Interrupt flag */
-       s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000);
+       s->dma_xfer_cnt++;
+       memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
+       s->sg_processing_size = s->sg_pending_size;
+       s->sg_pending_size = 0;
+       s->sg_processed = 0;
+       s->dma_offset = s->pending_offset;
+       s->dma_backup = s->pending_backup;
+       s->dma_pts = s->pending_pts;
 
-       /* Sync Hardware SG List of buffers */
-       ivtv_stream_sync_for_device(s);
-       write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR);
-       write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
-       set_bit(IVTV_F_I_DMA, &itv->i_flags);
-       itv->cur_dma_stream = s->type;
-       itv->dma_timer.expires = jiffies + HZ / 10;
-       add_timer(&itv->dma_timer);
+       if (ivtv_use_pio(s)) {
+               set_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags);
+               set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+               set_bit(IVTV_F_I_PIO, &itv->i_flags);
+               itv->cur_pio_stream = s->type;
+       }
+       else {
+               itv->dma_retries = 0;
+               ivtv_dma_enc_start_xfer(s);
+               set_bit(IVTV_F_I_DMA, &itv->i_flags);
+               itv->cur_dma_stream = s->type;
+       }
 }
 
 static void ivtv_dma_dec_start(struct ivtv_stream *s)
@@ -397,40 +472,61 @@ static void ivtv_dma_dec_start(struct ivtv_stream *s)
 
        if (s->q_predma.bytesused)
                ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
-       IVTV_DEBUG_DMA("start DMA for %s\n", s->name);
-       /* put SG Handle into register 0x0c */
-       write_reg(s->SG_handle, IVTV_REG_DECDMAADDR);
-       write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+       s->dma_xfer_cnt++;
+       memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
+       s->sg_processing_size = s->sg_pending_size;
+       s->sg_pending_size = 0;
+       s->sg_processed = 0;
+
+       IVTV_DEBUG_HI_DMA("start DMA for %s\n", s->name);
+       itv->dma_retries = 0;
+       ivtv_dma_dec_start_xfer(s);
        set_bit(IVTV_F_I_DMA, &itv->i_flags);
        itv->cur_dma_stream = s->type;
-       itv->dma_timer.expires = jiffies + HZ / 10;
-       add_timer(&itv->dma_timer);
 }
 
 static void ivtv_irq_dma_read(struct ivtv *itv)
 {
        struct ivtv_stream *s = NULL;
        struct ivtv_buffer *buf;
-       int hw_stream_type;
+       int hw_stream_type = 0;
+
+       IVTV_DEBUG_HI_IRQ("DEC DMA READ\n");
 
-       IVTV_DEBUG_IRQ("DEC DMA READ\n");
        del_timer(&itv->dma_timer);
-       if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
-               IVTV_DEBUG_WARN("DEC DMA ERROR %x\n", read_reg(IVTV_REG_DMASTATUS));
-               write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
-       }
+
+       if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && itv->cur_dma_stream < 0)
+               return;
+
        if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
-               if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
-                       s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
-                       hw_stream_type = 2;
+               s = &itv->streams[itv->cur_dma_stream];
+               ivtv_stream_sync_for_cpu(s);
+
+               if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
+                       IVTV_DEBUG_WARN("DEC DMA ERROR %x (xfer %d of %d, retry %d)\n",
+                                       read_reg(IVTV_REG_DMASTATUS),
+                                       s->sg_processed, s->sg_processing_size, itv->dma_retries);
+                       write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+                       if (itv->dma_retries == 3) {
+                               /* Too many retries, give up on this frame */
+                               itv->dma_retries = 0;
+                               s->sg_processed = s->sg_processing_size;
+                       }
+                       else {
+                               /* Retry, starting with the first xfer segment.
+                                  Just retrying the current segment is not sufficient. */
+                               s->sg_processed = 0;
+                               itv->dma_retries++;
+                       }
                }
-               else {
-                       s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
-                       hw_stream_type = 0;
+               if (s->sg_processed < s->sg_processing_size) {
+                       /* DMA next buffer */
+                       ivtv_dma_dec_start_xfer(s);
+                       return;
                }
-               IVTV_DEBUG_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
-
-               ivtv_stream_sync_for_cpu(s);
+               if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
+                       hw_stream_type = 2;
+               IVTV_DEBUG_HI_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
 
                /* For some reason must kick the firmware, like PIO mode,
                   I think this tells the firmware we are done and the size
@@ -459,32 +555,73 @@ static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
        u32 data[CX2341X_MBOX_MAX_DATA];
        struct ivtv_stream *s;
 
-       del_timer(&itv->dma_timer);
        ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
-       IVTV_DEBUG_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]);
-       if (test_and_clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags))
-               data[1] = 3;
-       else if (data[1] > 2)
+       IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream);
+
+       del_timer(&itv->dma_timer);
+
+       if (itv->cur_dma_stream < 0)
                return;
-       s = &itv->streams[ivtv_stream_map[data[1]]];
+
+       s = &itv->streams[itv->cur_dma_stream];
+       ivtv_stream_sync_for_cpu(s);
+
        if (data[0] & 0x18) {
-               IVTV_DEBUG_WARN("ENC DMA ERROR %x\n", data[0]);
+               IVTV_DEBUG_WARN("ENC DMA ERROR %x (offset %08x, xfer %d of %d, retry %d)\n", data[0],
+                       s->dma_offset, s->sg_processed, s->sg_processing_size, itv->dma_retries);
                write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
-               ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[1]);
+               if (itv->dma_retries == 3) {
+                       /* Too many retries, give up on this frame */
+                       itv->dma_retries = 0;
+                       s->sg_processed = s->sg_processing_size;
+               }
+               else {
+                       /* Retry, starting with the first xfer segment.
+                          Just retrying the current segment is not sufficient. */
+                       s->sg_processed = 0;
+                       itv->dma_retries++;
+               }
+       }
+       if (s->sg_processed < s->sg_processing_size) {
+               /* DMA next buffer */
+               ivtv_dma_enc_start_xfer(s);
+               return;
        }
-       s->SG_length = 0;
        clear_bit(IVTV_F_I_DMA, &itv->i_flags);
        itv->cur_dma_stream = -1;
        dma_post(s);
-       ivtv_stream_sync_for_cpu(s);
        if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
-               u32 tmp;
+               s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+               dma_post(s);
+       }
+       s->sg_processing_size = 0;
+       s->sg_processed = 0;
+       wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_pio_complete(struct ivtv *itv)
+{
+       struct ivtv_stream *s;
 
+       if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS) {
+               itv->cur_pio_stream = -1;
+               return;
+       }
+       s = &itv->streams[itv->cur_pio_stream];
+       IVTV_DEBUG_HI_IRQ("ENC PIO COMPLETE %s\n", s->name);
+       clear_bit(IVTV_F_I_PIO, &itv->i_flags);
+       itv->cur_pio_stream = -1;
+       dma_post(s);
+       if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+               ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 0);
+       else if (s->type == IVTV_ENC_STREAM_TYPE_YUV)
+               ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 1);
+       else if (s->type == IVTV_ENC_STREAM_TYPE_PCM)
+               ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 2);
+       clear_bit(IVTV_F_I_PIO, &itv->i_flags);
+       if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
                s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
-               tmp = s->dma_offset;
-               s->dma_offset = itv->vbi.dma_offset;
                dma_post(s);
-               s->dma_offset = tmp;
        }
        wake_up(&itv->dma_waitq);
 }
@@ -496,19 +633,23 @@ static void ivtv_irq_dma_err(struct ivtv *itv)
        del_timer(&itv->dma_timer);
        ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data);
        IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
-                                       read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+                               read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+       write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
        if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&
            itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {
                struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];
 
                /* retry */
-               write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
                if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
                        ivtv_dma_dec_start(s);
                else
                        ivtv_dma_enc_start(s);
                return;
        }
+       if (test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+               ivtv_udma_start(itv);
+               return;
+       }
        clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
        clear_bit(IVTV_F_I_DMA, &itv->i_flags);
        itv->cur_dma_stream = -1;
@@ -522,23 +663,16 @@ static void ivtv_irq_enc_start_cap(struct ivtv *itv)
 
        /* Get DMA destination and size arguments from card */
        ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data);
-       IVTV_DEBUG_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
+       IVTV_DEBUG_HI_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
 
        if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
                IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n",
                                data[0], data[1], data[2]);
                return;
        }
-       clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
        s = &itv->streams[ivtv_stream_map[data[0]]];
        if (!stream_enc_dma_append(s, data)) {
-               if (ivtv_use_pio(s)) {
-                       dma_post(s);
-                       ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[0]);
-               }
-               else {
-                       set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
-               }
+               set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
        }
 }
 
@@ -548,18 +682,9 @@ static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
        u32 data[CX2341X_MBOX_MAX_DATA];
        struct ivtv_stream *s;
 
-       IVTV_DEBUG_IRQ("ENC START VBI CAP\n");
+       IVTV_DEBUG_HI_IRQ("ENC START VBI CAP\n");
        s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
 
-       if (ivtv_use_pio(s)) {
-               if (stream_enc_dma_append(s, data))
-                       return;
-               if (s->q_predma.bytesused)
-                       ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
-               s->SG_length = 0;
-               dma_post(s);
-               return;
-       }
        /* If more than two VBI buffers are pending, then
           clear the old ones and start with this new one.
           This can happen during transition stages when MPEG capturing is
@@ -568,33 +693,30 @@ static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
           DMA the data. Since at most four VBI DMA buffers are available,
           we just drop the old requests when there are already three
           requests queued. */
-       if (s->SG_length > 2) {
-               struct list_head *p;
-               list_for_each(p, &s->q_predma.list) {
-                       struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list);
+       if (s->sg_pending_size > 2) {
+               struct ivtv_buffer *buf;
+               list_for_each_entry(buf, &s->q_predma.list, list)
                        ivtv_buf_sync_for_cpu(s, buf);
-               }
                ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
-               s->SG_length = 0;
+               s->sg_pending_size = 0;
        }
        /* if we can append the data, and the MPEG stream isn't capturing,
           then start a DMA request for just the VBI data. */
        if (!stream_enc_dma_append(s, data) &&
                        !test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) {
-               set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
-               set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+               set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
        }
 }
 
-static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv)
+static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv)
 {
        u32 data[CX2341X_MBOX_MAX_DATA];
        struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
 
-       IVTV_DEBUG_IRQ("DEC VBI REINSERT\n");
+       IVTV_DEBUG_HI_IRQ("DEC VBI REINSERT\n");
        if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
                        !stream_enc_dma_append(s, data)) {
-               dma_post(s);
+               set_bit(IVTV_F_S_PIO_PENDING, &s->s_flags);
        }
 }
 
@@ -607,21 +729,26 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv)
        ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data);
 
        if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
-               itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2;
-               itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0];
+               itv->dma_data_req_size =
+                                1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
+               itv->dma_data_req_offset = data[1];
+               if (atomic_read(&itv->yuv_info.next_dma_frame) >= 0)
+                       ivtv_yuv_frame_complete(itv);
                s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
        }
        else {
-               itv->dma_data_req_size = data[2] >= 0x10000 ? 0x10000 : data[2];
+               itv->dma_data_req_size = min_t(u32, data[2], 0x10000);
                itv->dma_data_req_offset = data[1];
                s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
        }
-       IVTV_DEBUG_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
+       IVTV_DEBUG_HI_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
                       itv->dma_data_req_offset, itv->dma_data_req_size);
        if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) {
                set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
        }
        else {
+               if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+                       ivtv_yuv_setup_stream_frame(itv);
                clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
                ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
                ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0);
@@ -638,28 +765,33 @@ static void ivtv_irq_vsync(struct ivtv *itv)
         * one vsync per frame.
         */
        unsigned int frame = read_reg(0x28c0) & 1;
+       struct yuv_playback_info *yi = &itv->yuv_info;
        int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame);
+       struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame];
 
        if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
 
-       if (((frame ^ itv->yuv_info.lace_sync_field) == 0 && ((itv->lastVsyncFrame & 1) ^ itv->yuv_info.lace_sync_field)) ||
-                       (frame != (itv->lastVsyncFrame & 1) && !itv->yuv_info.frame_interlaced)) {
+       if (((frame ^ f->sync_field) == 0 &&
+               ((itv->last_vsync_field & 1) ^ f->sync_field)) ||
+                       (frame != (itv->last_vsync_field & 1) && !f->interlaced)) {
                int next_dma_frame = last_dma_frame;
 
-               if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) {
-                       write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
-                       write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
-                       write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
-                       write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
-                       next_dma_frame = (next_dma_frame + 1) & 0x3;
-                       atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame);
+               if (!(f->interlaced && f->delay && yi->fields_lapsed < 1)) {
+                       if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) {
+                               write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
+                               write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
+                               write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
+                               write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
+                               next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS;
+                               atomic_set(&yi->next_dma_frame, next_dma_frame);
+                               yi->fields_lapsed = -1;
+                       }
                }
        }
-       if (frame != (itv->lastVsyncFrame & 1)) {
+       if (frame != (itv->last_vsync_field & 1)) {
                struct ivtv_stream *s = ivtv_get_output_stream(itv);
-               int work = 0;
 
-               itv->lastVsyncFrame += 1;
+               itv->last_vsync_field += 1;
                if (frame == 0) {
                        clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
                        clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
@@ -676,30 +808,35 @@ static void ivtv_irq_vsync(struct ivtv *itv)
                        wake_up(&s->waitq);
 
                /* Send VBI to saa7127 */
-               if (frame) {
+               if (frame && (itv->output_mode == OUT_PASSTHROUGH ||
+                       test_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags) ||
+                       test_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags) ||
+                       test_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags))) {
                        set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags);
-                       work = 1;
+                       set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
                }
 
                /* Check if we need to update the yuv registers */
-               if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) {
-                       if (!itv->yuv_info.new_frame_info[last_dma_frame].update)
-                               last_dma_frame = (last_dma_frame - 1) & 3;
-
-                       if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) {
-                               itv->yuv_info.update_frame = last_dma_frame;
-                               itv->yuv_info.new_frame_info[last_dma_frame].update = 0;
-                               itv->yuv_info.yuv_forced_update = 0;
+               if ((yi->yuv_forced_update || f->update) && last_dma_frame != -1) {
+                       if (!f->update) {
+                               last_dma_frame = (u8)(last_dma_frame - 1) % IVTV_YUV_BUFFERS;
+                               f = &yi->new_frame_info[last_dma_frame];
+                       }
+
+                       if (f->src_w) {
+                               yi->update_frame = last_dma_frame;
+                               f->update = 0;
+                               yi->yuv_forced_update = 0;
                                set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
-                               work = 1;
+                               set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
                        }
                }
-               if (work)
-                       queue_work(itv->irq_work_queues, &itv->irq_work_queue);
+
+               yi->fields_lapsed++;
        }
 }
 
-#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ)
+#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ | IVTV_IRQ_DEC_VBI_RE_INSERT)
 
 irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
 {
@@ -724,7 +861,7 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
                 */
                if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
                        /* vsync is enabled, see if we're in a new field */
-                       if ((itv->lastVsyncFrame & 1) != (read_reg(0x28c0) & 1)) {
+                       if ((itv->last_vsync_field & 1) != (read_reg(0x28c0) & 1)) {
                                /* New field, looks like we missed it */
                                IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16);
                                vsync_force = 1;
@@ -741,10 +878,10 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
        /* Exclude interrupts noted below from the output, otherwise the log is flooded with
           these messages */
        if (combo & ~0xff6d0400)
-               IVTV_DEBUG_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
+               IVTV_DEBUG_HI_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
 
        if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
-               IVTV_DEBUG_IRQ("DEC DMA COMPLETE\n");
+               IVTV_DEBUG_HI_IRQ("DEC DMA COMPLETE\n");
        }
 
        if (combo & IVTV_IRQ_DMA_READ) {
@@ -755,6 +892,10 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
                ivtv_irq_enc_dma_complete(itv);
        }
 
+       if (combo & IVTV_IRQ_ENC_PIO_COMPLETE) {
+               ivtv_irq_enc_pio_complete(itv);
+       }
+
        if (combo & IVTV_IRQ_DMA_ERR) {
                ivtv_irq_dma_err(itv);
        }
@@ -768,13 +909,13 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
        }
 
        if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
-               ivtv_irq_dev_vbi_reinsert(itv);
+               ivtv_irq_dec_vbi_reinsert(itv);
        }
 
        if (combo & IVTV_IRQ_ENC_EOS) {
                IVTV_DEBUG_IRQ("ENC EOS\n");
                set_bit(IVTV_F_I_EOS, &itv->i_flags);
-               wake_up(&itv->cap_w);
+               wake_up(&itv->eos_waitq);
        }
 
        if (combo & IVTV_IRQ_DEC_DATA_REQ) {
@@ -796,8 +937,9 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
        }
 
        if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+               itv->irq_rr_idx++;
                for (i = 0; i < IVTV_MAX_STREAMS; i++) {
-                       int idx = (i + itv->irq_rr_idx++) % IVTV_MAX_STREAMS;
+                       int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
                        struct ivtv_stream *s = &itv->streams[idx];
 
                        if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags))
@@ -813,6 +955,24 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
                }
        }
 
+       if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) {
+               itv->irq_rr_idx++;
+               for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+                       int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
+                       struct ivtv_stream *s = &itv->streams[idx];
+
+                       if (!test_and_clear_bit(IVTV_F_S_PIO_PENDING, &s->s_flags))
+                               continue;
+                       if (s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type < IVTV_DEC_STREAM_TYPE_MPG)
+                               ivtv_dma_enc_start(s);
+                       break;
+               }
+       }
+
+       if (test_and_clear_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags)) {
+               queue_work(itv->irq_work_queues, &itv->irq_work_queue);
+       }
+
        spin_unlock(&itv->dma_reg_lock);
 
        /* If we've just handled a 'forced' vsync, it's safest to say it