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

    V4L/DVB (6047): ivtv: Fix scatter/gather DMA timeouts
    
    It turns out that the cx23415/6 DMA engine cannot do scatter/gather DMA
    reliably. Every so often depending on the phase of the moon and your
    hardware configuration the cx2341x DMA engine simply chokes on it and
    you have to reboot to get it working again.
    
    This change replaced the scatter/gather DMA by single transfers at a time,
    where the driver is now responsible for DMA-ing each buffer.
    
    UDMA is still done using scatter/gather DMA, that will be fixed soon.
    
    Many thanks to Mark Bryars <[EMAIL PROTECTED]> for discovering
    the link between scatter/gather and the DMA timeouts.
    
    Signed-off-by: Hans Verkuil <[EMAIL PROTECTED]>
    Signed-off-by: Mauro Carvalho Chehab <[EMAIL PROTECTED]>
---
 drivers/media/video/ivtv/ivtv-driver.h  |   22 ++-
 drivers/media/video/ivtv/ivtv-irq.c     |  234 +++++++++++++++++++------------
 drivers/media/video/ivtv/ivtv-queue.c   |   63 +++++----
 drivers/media/video/ivtv/ivtv-queue.h   |    8 +-
 drivers/media/video/ivtv/ivtv-streams.c |    2 +-
 5 files changed, 196 insertions(+), 133 deletions(-)

diff --git a/drivers/media/video/ivtv/ivtv-driver.h 
b/drivers/media/video/ivtv/ivtv-driver.h
index f5de2fd..e80f9f6 100644
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ b/drivers/media/video/ivtv/ivtv-driver.h
@@ -382,7 +382,6 @@ struct ivtv_mailbox_data {
 #define IVTV_F_I_RADIO_USER       5    /* The radio tuner is selected */
 #define IVTV_F_I_DIG_RST          6    /* Reset digitizer */
 #define IVTV_F_I_DEC_YUV          7    /* YUV instead of MPG is being decoded 
*/
-#define IVTV_F_I_ENC_VBI          8    /* VBI DMA */
 #define IVTV_F_I_UPDATE_CC        9    /* CC should be updated */
 #define IVTV_F_I_UPDATE_WSS       10   /* WSS should be updated */
 #define IVTV_F_I_UPDATE_VPS       11   /* VPS should be updated */
@@ -405,7 +404,7 @@ struct ivtv_mailbox_data {
 #define IVTV_F_I_EV_VSYNC_ENABLED  31  /* VSYNC event enabled */
 
 /* Scatter-Gather array element, used in DMA transfers */
-struct ivtv_SG_element {
+struct ivtv_sg_element {
        u32 src;
        u32 dst;
        u32 size;
@@ -417,7 +416,7 @@ struct ivtv_user_dma {
        struct page *map[IVTV_DMA_SG_OSD_ENT];
 
        /* Base Dev SG Array for cx23415/6 */
-       struct ivtv_SG_element SGarray[IVTV_DMA_SG_OSD_ENT];
+       struct ivtv_sg_element SGarray[IVTV_DMA_SG_OSD_ENT];
        dma_addr_t SG_handle;
        int SG_length;
 
@@ -468,6 +467,10 @@ struct ivtv_stream {
        int dma;                /* can be PCI_DMA_TODEVICE,
                                   PCI_DMA_FROMDEVICE or
                                   PCI_DMA_NONE */
+       u32 pending_offset;
+       u32 pending_backup;
+       u64 pending_pts;
+
        u32 dma_offset;
        u32 dma_backup;
        u64 dma_pts;
@@ -493,10 +496,13 @@ struct ivtv_stream {
        u16 dma_xfer_cnt;
 
        /* Base Dev SG Array for cx23415/6 */
-       struct ivtv_SG_element *SGarray;
-       struct ivtv_SG_element *PIOarray;
-       dma_addr_t SG_handle;
-       int SG_length;
+       struct ivtv_sg_element *sg_pending;
+       struct ivtv_sg_element *sg_processing;
+       struct ivtv_sg_element *sg_dma;
+       dma_addr_t sg_handle;
+       int sg_pending_size;
+       int sg_processing_size;
+       int sg_processed;
 
        /* SG List of Buffers */
        struct scatterlist *SGlist;
@@ -637,7 +643,6 @@ struct vbi_info {
        u32 enc_start, enc_size;
        int fpi;
        u32 frame;
-       u32 dma_offset;
        u8 cc_data_odd[256];
        u8 cc_data_even[256];
        int cc_pos;
@@ -724,6 +729,7 @@ struct ivtv {
        int cur_pio_stream;     /* index of stream doing PIO */
        u32 dma_data_req_offset;
        u32 dma_data_req_size;
+       int dma_retries;
        int output_mode;        /* NONE, MPG, YUV, UDMA YUV, passthrough */
        spinlock_t lock;        /* lock access to this struct */
        int search_pack_header;
diff --git a/drivers/media/video/ivtv/ivtv-irq.c 
b/drivers/media/video/ivtv/ivtv-irq.c
index 8644f3d..9695e53 100644
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ b/drivers/media/video/ivtv/ivtv-irq.c
@@ -60,18 +60,18 @@ static void ivtv_pio_work_handler(struct ivtv *itv)
        buf = list_entry(s->q_dma.list.next, struct ivtv_buffer, list);
        list_for_each(p, &s->q_dma.list) {
                struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, 
list);
-               u32 size = s->PIOarray[i].size & 0x3ffff;
+               u32 size = s->sg_processing[i].size & 0x3ffff;
 
                /* 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->PIOarray[i].src - IVTV_DECODER_OFFSET, size);
+                       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->PIOarray[i].src, size);
+                       memcpy_fromio(buf->buf, itv->enc_mem + 
s->sg_processing[i].src, size);
                }
-               if (s->PIOarray[i].size & 0x80000000)
-                       break;
                i++;
+               if (i == s->sg_processing_size)
+                       break;
        }
        write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
 }
@@ -105,7 +105,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 
data[CX2341X_MBOX_MA
        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 */
@@ -123,7 +123,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:
@@ -131,13 +131,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;
@@ -150,13 +150,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:
@@ -165,17 +165,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) {
                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;
@@ -202,7 +202,7 @@ 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) */
+       /* 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) {
@@ -210,9 +210,9 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 
data[CX2341X_MBOX_MA
 
                if (skip_bufs-- > 0)
                        continue;
-               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);
+               s->sg_pending[idx].dst = buf->dma_handle;
+               s->sg_pending[idx].src = offset;
+               s->sg_pending[idx].size = s->buf_size;
                buf->bytesused = (size < s->buf_size) ? size : s->buf_size;
                buf->dma_xfer_cnt = s->dma_xfer_cnt;
 
@@ -230,7 +230,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;
 }
 
@@ -332,9 +332,9 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 
offset, int lock)
                        offset = uv_offset;
                        y_done = 1;
                }
-               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);
+               s->sg_pending[idx].src = buf->dma_handle;
+               s->sg_pending[idx].dst = offset;
+               s->sg_pending[idx].size = buf->bytesused;
 
                offset += buf->bytesused;
                bytes_written += buf->bytesused;
@@ -343,10 +343,7 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, 
u32 offset, int lock)
                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);
@@ -362,6 +359,34 @@ 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);
+}
+
+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);
+}
+
 /* start the encoder DMA */
 static void ivtv_dma_enc_start(struct ivtv_stream *s)
 {
@@ -375,8 +400,7 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s)
                ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, 
s->q_predma.bytesused);
 
        if (ivtv_use_dma(s))
-               s->SGarray[s->SG_length - 1].size =
-                       cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 
1].size) + 256);
+               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.
@@ -387,45 +411,39 @@ 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);
                if (ivtv_use_dma(s_vbi))
-                       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];
+                       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_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);
-       else
-               clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
+       memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_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;
 
        if (ivtv_use_pio(s)) {
-               for (i = 0; i < s->SG_length; i++) {
-                       s->PIOarray[i].src = le32_to_cpu(s->SGarray[i].src);
-                       s->PIOarray[i].size = le32_to_cpu(s->SGarray[i].size);
-               }
                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 {
-               /* 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_retries = 0;
+               ivtv_dma_enc_start_xfer(s);
                set_bit(IVTV_F_I_DMA, &itv->i_flags);
                itv->cur_dma_stream = s->type;
                itv->dma_timer.expires = jiffies + msecs_to_jiffies(100);
@@ -439,10 +457,15 @@ 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);
+       s->dma_xfer_cnt++;
+       memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_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);
-       /* 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);
+       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 + msecs_to_jiffies(100);
@@ -453,27 +476,42 @@ 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");
-       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) 
{
+               del_timer(&itv->dma_timer);
+               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) {
+                               itv->dma_retries = 0;
+                       }
+                       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;
                }
+               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);
 
-               ivtv_stream_sync_for_cpu(s);
-
                /* For some reason must kick the firmware, like PIO mode,
                   I think this tells the firmware we are done and the size
                   of the xfer so it can calculate what we need next.
@@ -490,6 +528,7 @@ static void ivtv_irq_dma_read(struct ivtv *itv)
                }
                wake_up(&s->waitq);
        }
+       del_timer(&itv->dma_timer);
        clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
        clear_bit(IVTV_F_I_DMA, &itv->i_flags);
        itv->cur_dma_stream = -1;
@@ -501,33 +540,44 @@ 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_HI_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);
+       if (itv->cur_dma_stream < 0) {
+               del_timer(&itv->dma_timer);
                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) {
+                       itv->dma_retries = 0;
+               }
+               else {
+                       /* Retry, starting with the first xfer segment.
+                          Just retrying the current segment is not sufficient. 
*/
+                       s->sg_processed = 0;
+                       itv->dma_retries++;
+               }
        }
-       s->SG_length = 0;
+       if (s->sg_processed < s->sg_processing_size) {
+               /* DMA next buffer */
+               ivtv_dma_enc_start_xfer(s);
+               return;
+       }
+       del_timer(&itv->dma_timer);
        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];
-               tmp = s->dma_offset;
-               s->dma_offset = itv->vbi.dma_offset;
                dma_post(s);
-               s->dma_offset = tmp;
        }
+       s->sg_processing_size = 0;
+       s->sg_processed = 0;
        wake_up(&itv->dma_waitq);
 }
 
@@ -541,8 +591,7 @@ static void ivtv_irq_enc_pio_complete(struct ivtv *itv)
        }
        s = &itv->streams[itv->cur_pio_stream];
        IVTV_DEBUG_HI_IRQ("ENC PIO COMPLETE %s\n", s->name);
-       s->SG_length = 0;
-       clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags);
+       s->sg_pending_size = 0;
        clear_bit(IVTV_F_I_PIO, &itv->i_flags);
        itv->cur_pio_stream = -1;
        dma_post(s);
@@ -554,13 +603,8 @@ static void ivtv_irq_enc_pio_complete(struct ivtv *itv)
                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)) {
-               u32 tmp;
-
                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);
 }
@@ -572,19 +616,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;
@@ -628,14 +676,14 @@ 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) {
+       if (s->sg_pending_size > 2) {
                struct list_head *p;
                list_for_each(p, &s->q_predma.list) {
                        struct ivtv_buffer *buf = list_entry(p, struct 
ivtv_buffer, 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. */
diff --git a/drivers/media/video/ivtv/ivtv-queue.c 
b/drivers/media/video/ivtv/ivtv-queue.c
index bff75ae..d9a1478 100644
--- a/drivers/media/video/ivtv/ivtv-queue.c
+++ b/drivers/media/video/ivtv/ivtv-queue.c
@@ -195,7 +195,7 @@ void ivtv_flush_queues(struct ivtv_stream *s)
 int ivtv_stream_alloc(struct ivtv_stream *s)
 {
        struct ivtv *itv = s->itv;
-       int SGsize = sizeof(struct ivtv_SG_element) * s->buffers;
+       int SGsize = sizeof(struct ivtv_sg_element) * s->buffers;
        int i;
 
        if (s->buffers == 0)
@@ -205,27 +205,33 @@ int ivtv_stream_alloc(struct ivtv_stream *s)
                s->dma != PCI_DMA_NONE ? "DMA " : "",
                s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 
1024);
 
-       if (ivtv_might_use_pio(s)) {
-               s->PIOarray = (struct ivtv_SG_element *)kzalloc(SGsize, 
GFP_KERNEL);
-               if (s->PIOarray == NULL) {
-                       IVTV_ERR("Could not allocate PIOarray for %s stream\n", 
s->name);
-                       return -ENOMEM;
-               }
+       s->sg_pending = (struct ivtv_sg_element *)kzalloc(SGsize, GFP_KERNEL);
+       if (s->sg_pending == NULL) {
+               IVTV_ERR("Could not allocate sg_pending for %s stream\n", 
s->name);
+               return -ENOMEM;
        }
+       s->sg_pending_size = 0;
 
-       /* Allocate DMA SG Arrays */
-       s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL);
-       if (s->SGarray == NULL) {
-               IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name);
-               if (ivtv_might_use_pio(s)) {
-                       kfree(s->PIOarray);
-                       s->PIOarray = NULL;
-               }
+       s->sg_processing = (struct ivtv_sg_element *)kzalloc(SGsize, 
GFP_KERNEL);
+       if (s->sg_processing == NULL) {
+               IVTV_ERR("Could not allocate sg_processing for %s stream\n", 
s->name);
+               kfree(s->sg_pending);
+               s->sg_pending = NULL;
+               return -ENOMEM;
+       }
+       s->sg_processing_size = 0;
+
+       s->sg_dma = (struct ivtv_sg_element *)kzalloc(sizeof(struct 
ivtv_sg_element), GFP_KERNEL);
+       if (s->sg_dma == NULL) {
+               IVTV_ERR("Could not allocate sg_dma for %s stream\n", s->name);
+               kfree(s->sg_pending);
+               s->sg_pending = NULL;
+               kfree(s->sg_processing);
+               s->sg_processing = NULL;
                return -ENOMEM;
        }
-       s->SG_length = 0;
        if (ivtv_might_use_dma(s)) {
-               s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, 
s->dma);
+               s->sg_handle = pci_map_single(itv->dev, s->sg_dma, 
sizeof(struct ivtv_sg_element), s->dma);
                ivtv_stream_sync_for_cpu(s);
        }
 
@@ -272,16 +278,19 @@ void ivtv_stream_free(struct ivtv_stream *s)
        }
 
        /* Free SG Array/Lists */
-       if (s->SGarray != NULL) {
-               if (s->SG_handle != IVTV_DMA_UNMAPPED) {
-                       pci_unmap_single(s->itv->dev, s->SG_handle,
-                                sizeof(struct ivtv_SG_element) * s->buffers, 
PCI_DMA_TODEVICE);
-                       s->SG_handle = IVTV_DMA_UNMAPPED;
+       if (s->sg_dma != NULL) {
+               if (s->sg_handle != IVTV_DMA_UNMAPPED) {
+                       pci_unmap_single(s->itv->dev, s->sg_handle,
+                                sizeof(struct ivtv_sg_element), 
PCI_DMA_TODEVICE);
+                       s->sg_handle = IVTV_DMA_UNMAPPED;
                }
-               kfree(s->SGarray);
-               kfree(s->PIOarray);
-               s->PIOarray = NULL;
-               s->SGarray = NULL;
-               s->SG_length = 0;
+               kfree(s->sg_pending);
+               kfree(s->sg_processing);
+               kfree(s->sg_dma);
+               s->sg_pending = NULL;
+               s->sg_processing = NULL;
+               s->sg_dma = NULL;
+               s->sg_pending_size = 0;
+               s->sg_processing_size = 0;
        }
 }
diff --git a/drivers/media/video/ivtv/ivtv-queue.h 
b/drivers/media/video/ivtv/ivtv-queue.h
index 2ed8d54..14a9f7f 100644
--- a/drivers/media/video/ivtv/ivtv-queue.h
+++ b/drivers/media/video/ivtv/ivtv-queue.h
@@ -79,13 +79,13 @@ void ivtv_stream_free(struct ivtv_stream *s);
 static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s)
 {
        if (ivtv_use_dma(s))
-               pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle,
-                       sizeof(struct ivtv_SG_element) * s->buffers, 
PCI_DMA_TODEVICE);
+               pci_dma_sync_single_for_cpu(s->itv->dev, s->sg_handle,
+                       sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
 }
 
 static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s)
 {
        if (ivtv_use_dma(s))
-               pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle,
-                       sizeof(struct ivtv_SG_element) * s->buffers, 
PCI_DMA_TODEVICE);
+               pci_dma_sync_single_for_device(s->itv->dev, s->sg_handle,
+                       sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
 }
diff --git a/drivers/media/video/ivtv/ivtv-streams.c 
b/drivers/media/video/ivtv/ivtv-streams.c
index 0582b9d..d1cc366 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -154,7 +154,7 @@ static void ivtv_stream_init(struct ivtv *itv, int type)
        spin_lock_init(&s->qlock);
        init_waitqueue_head(&s->waitq);
        s->id = -1;
-       s->SG_handle = IVTV_DMA_UNMAPPED;
+       s->sg_handle = IVTV_DMA_UNMAPPED;
        ivtv_queue_init(&s->q_free);
        ivtv_queue_init(&s->q_full);
        ivtv_queue_init(&s->q_dma);
-
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