diff -urp ivtv-latest/driver/ivtv-driver.c ivtv/driver/ivtv-driver.c --- ivtv-latest/driver/ivtv-driver.c 2003-11-08 22:46:13.000000000 +0100 +++ ivtv/driver/ivtv-driver.c 2003-11-09 01:17:29.000000000 +0100 @@ -207,38 +207,47 @@ int ivtv_enq_buf(struct ivtv *itv, struc return 0; } +inline void __ivtv_del_buf(struct ivtv_buffer_list *queue, + struct ivtv_buffer *buffer) { + list_del_init(&buffer->list); + queue->elements--; +} + /* called to remove the buffer returned by _peek_ functions */ void ivtv_del_buf(struct ivtv *itv, struct ivtv_buffer_list *queue, struct ivtv_buffer *buffer) { unsigned long flags; spin_lock_irqsave(&itv->lock, flags); - list_del_init(&buffer->list); - queue->elements--; + __ivtv_del_buf(queue, buffer); spin_unlock_irqrestore(&itv->lock, flags); } /* returns first item in queue, doesn't dequeue */ -struct ivtv_buffer *ivtv_deq_peek_head(struct ivtv *itv, - struct ivtv_buffer_list *queue) { +struct ivtv_buffer *__ivtv_deq_peek_head(struct ivtv_buffer_list *queue) { struct ivtv_buffer *buf; - unsigned long flags; - - spin_lock_irqsave(&itv->lock, flags); /* make sure list has something to DeQ */ if (list_empty(&queue->list)) { IVTV_DEBUG(IVTV_DEBUG_INFO,"DeQ from empty list!\n"); queue->elements = 0; - spin_unlock_irqrestore(&itv->lock, flags); return NULL; } /* grab next element of the queue */ - buf = list_entry(queue->list.next, struct ivtv_buffer, list); - + return list_entry(queue->list.next, struct ivtv_buffer, list); +} + +struct ivtv_buffer *ivtv_deq_peek_head(struct ivtv *itv, + struct ivtv_buffer_list *queue) { + unsigned long flags; + struct ivtv_buffer *buffer; + + spin_lock_irqsave(&itv->lock, flags); + buffer = __ivtv_deq_peek_head(queue); spin_unlock_irqrestore(&itv->lock, flags); - return buf; + + return buffer; } /* removes buffer from the head */ @@ -1061,15 +1070,18 @@ void ivtv_dec_timeout(unsigned long arg) /* FIXME mpg only :/ */ struct ivtv_v4l2_stream *stream = &itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG]; + unsigned long flags; if (!test_bit(IVTV_F_S_DMAP, &stream->s_flags)) return; IVTV_DEBUG(IVTV_DEBUG_ERR, "ivtv_dec_timeout: lost IRQ; resetting...\n"); + spin_lock_irqsave(&itv->lock, flags); ivtv_dec_DMA_done(itv); /* kick it off again! */ set_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags); ivtv_dec_sched_DMA(itv); + spin_unlock_irqrestore(&itv->lock, flags); } #if 0 @@ -1144,6 +1156,8 @@ void ivtv_irq_handler(int irq, void *dev return; } + spin_lock(&itv->lock); + /* ivtv_show_irq_status(itv, stat, itv->irqmask, readl(itv->reg_mem + IVTV_REG_DMASTATUS)); @@ -1154,19 +1168,11 @@ void ivtv_irq_handler(int irq, void *dev writel(combo, (itv->reg_mem + IVTV_REG_IRQSTATUS)); if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) { -#if 0 - tasklet_schedule(&itv->dma_done_tq); -#else - ivtv_DMA_done_tasklet((unsigned long) itv); -#endif + ivtv_DMA_done(itv); IVTV_DEBUG(IVTV_DEBUG_IRQ, "Processed DMA-complete\n"); } if (combo & IVTV_IRQ_ENC_START_CAP) { -#if 0 - tasklet_schedule(&itv->dma_sched_tq); -#else - ivtv_sched_DMA_tasklet((unsigned long) itv); -#endif + ivtv_sched_DMA(itv); IVTV_DEBUG(IVTV_DEBUG_IRQ, "Processed enc-startcap\n"); } if (combo & IVTV_IRQ_ENC_EOS) { @@ -1216,8 +1222,7 @@ void ivtv_irq_handler(int irq, void *dev stat, itv->irqmask, combo); } */ - - return; + spin_unlock(&itv->lock); } void ivtv_irq_dec_vsync(struct ivtv* itv) { @@ -1247,12 +1252,11 @@ void ivtv_show_debug_flags(struct ivtv * printk("\n"); } -void ivtv_DMA_done_tasklet(unsigned long arg) { +void ivtv_DMA_done(struct ivtv *itv) { u32 result; int y,stmtype=-1; struct ivtv_v4l2_stream *stream=NULL; struct ivtv_buffer *buf; - struct ivtv *itv = (struct ivtv *) arg; IVTV_DEBUG(IVTV_DEBUG_INFO, "DMA Done tasklet\n"); @@ -1270,11 +1274,11 @@ void ivtv_DMA_done_tasklet(unsigned long stream = &itv->v4l2.streams[stmtype]; -#if 0 /* Silently return if we're in an overflow state */ - if (test_bit(IVTV_F_S_OVERFLOW, &stream->s_flags)) + if (test_bit(IVTV_F_S_OVERFLOW, &stream->s_flags)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "silently returning\n"); return; -#endif + } /* check DMA status register */ result = readl(itv->reg_mem + IVTV_REG_DMASTATUS); @@ -1306,7 +1310,7 @@ void ivtv_DMA_done_tasklet(unsigned long } } /* put in the 'done' queue */ - ivtv_enq_buf(itv, &stream->full_q, buf); + __ivtv_enq_buf(&stream->full_q, buf); } /*wake up client*/ @@ -1316,13 +1320,11 @@ debug: ivtv_show_debug_flags(itv); } +/* must hold itv->lock */ int ivtv_ignore_DMA_req(struct ivtv *itv, u32 type) { u32 data[IVTV_MBOX_MAX_DATA]; - unsigned long flags; int ret = 0; - spin_lock_irqsave(&itv->lock, flags); - set_bit(IVTV_F_S_OVERFLOW, &itv->v4l2.streams[type].s_flags); data[0] = type; @@ -1336,19 +1338,17 @@ int ivtv_ignore_DMA_req(struct ivtv *itv if (!ret) set_bit(IVTV_F_S_DMAP, &itv->v4l2.streams[type].s_flags); - spin_unlock_irqrestore(&itv->lock, flags); - return 0; + return ret; } /* FIXME this function is getting too long. split it up? */ -void ivtv_sched_DMA_tasklet(unsigned long arg) { +void ivtv_sched_DMA(struct ivtv *itv) { u32 data[IVTV_MBOX_MAX_DATA], result; u32 type, size, offset; u32 UVsize=0, UVoffset=0, pts_stamp=0; int x, bufs_needed; int uvflag=0; - struct ivtv *itv = (struct ivtv *) arg; struct ivtv_buffer *buf; LIST_HEAD(free_list); long sequence; @@ -1431,7 +1431,7 @@ void ivtv_sched_DMA_tasklet(unsigned lon for (x = 0; x < bufs_needed; x++) { struct ivtv_v4l2_stream *st = &itv->v4l2.streams[type]; - buf = ivtv_deq_buf(itv, &st->free_q); + buf = __ivtv_deq_buf(&st->free_q); if (!buf) break; @@ -1449,15 +1449,10 @@ void ivtv_sched_DMA_tasklet(unsigned lon while (!list_empty(&free_list)) { buf = list_entry(free_list.next, struct ivtv_buffer, list); list_del_init(&buf->list); - ivtv_enq_buf(itv, &itv->v4l2.streams[type].free_q, buf); + __ivtv_enq_buf(&itv->v4l2.streams[type].free_q, buf); } /* mark overflow condition, next free will restart dma req */ set_bit(IVTV_F_S_OVERFLOW, &itv->v4l2.streams[type].s_flags); - /* FIXME: must wait a little bit, perhaps use timer to fire - * this tasklet */ -#if 1 - tasklet_schedule(&itv->dma_sched_tq); -#endif return; } @@ -1510,7 +1505,7 @@ void ivtv_sched_DMA_tasklet(unsigned lon itv->SGarray[x].dst = buf->dma_handle; /* FIXME need to add pts stuff, index, etc. */ - ivtv_enq_buf(itv, &itv->v4l2.streams[type].dma_q, buf); + __ivtv_enq_buf(&itv->v4l2.streams[type].dma_q, buf); } /* This should wrap gracefully */ @@ -1538,6 +1533,15 @@ void ivtv_sched_DMA_tasklet(unsigned lon ivtv_api_irqsafe(itv->enc_mbox, IVTV_API_SCHED_DMA_TO_HOST, 4, &data[0]); } +void ivtv_sched_DMA_tasklet(unsigned long arg) { + struct ivtv *itv = (struct ivtv *) arg; + unsigned long flags; + + spin_lock_irqsave(&itv->lock, flags); + ivtv_sched_DMA(itv); + spin_unlock_irqrestore(&itv->lock, flags); +} + /* FIXME this function does way more than it should */ static int __devinit ivtv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { int retval=0; @@ -1625,7 +1629,6 @@ static int __devinit ivtv_probe(struct p itv->dec_timeout.function = ivtv_dec_timeout; itv->dec_timeout.data = (unsigned long)itv; - tasklet_init(&itv->dma_done_tq, ivtv_DMA_done_tasklet, (unsigned long) itv); tasklet_init(&itv->dma_sched_tq, ivtv_sched_DMA_tasklet, (unsigned long) itv); IVTV_DEBUG(IVTV_DEBUG_INFO, "base addr: 0x%08x\n", itv->base_addr); @@ -2001,7 +2004,7 @@ static int ivtv_YUV_fixup(struct ivtv_v4 #endif long ivtv_read(struct ivtv_open_id *id, char *ubuf, size_t count, int block) { - int x, sleepctr, datalen, retval=0; + int x, sleepctr, datalen, retval=0, freed=0; struct ivtv *itv = id->itv; size_t newcount; unsigned long tid; @@ -2113,9 +2116,7 @@ long ivtv_read(struct ivtv_open_id *id, if ((buf->buffer.bytesused == 0)) { ivtv_del_buf(itv, &st->full_q, buf); ivtv_enq_buf(itv, &st->free_q, buf); - - if (test_and_clear_bit(IVTV_F_S_OVERFLOW, &st->s_flags)) - tasklet_schedule(&itv->dma_sched_tq); + freed++; buf = ivtv_deq_peek_head(itv, &st->full_q); if (buf) { @@ -2134,7 +2135,10 @@ long ivtv_read(struct ivtv_open_id *id, } } } /* end of while */ - + + if (freed && test_and_clear_bit(IVTV_F_S_OVERFLOW, &st->s_flags)) + tasklet_schedule(&itv->dma_sched_tq); + /*FIXME unlock */ if (retval != 0) { IVTV_DEBUG(IVTV_DEBUG_INFO, "Returning %d\n", retval); @@ -2194,6 +2198,7 @@ int ivtv_fill_dec_buffers(struct ivtv_op * deemed necessary. */ while (bytesread == 0) { DECLARE_WAITQUEUE(wait, current); + unsigned long flags; buf = NULL; add_wait_queue(&stream->waitq, &wait); @@ -2210,7 +2215,9 @@ int ivtv_fill_dec_buffers(struct ivtv_op break; } + spin_lock_irqsave(&itv->lock, flags); ivtv_dec_sched_DMA(id->itv); + spin_unlock_irqrestore(&itv->lock, flags); } while (!buf); set_current_state(TASK_RUNNING); remove_wait_queue(&stream->waitq, &wait); @@ -2280,7 +2287,6 @@ void ivtv_dec_sched_DMA(struct ivtv *itv struct ivtv_v4l2_stream *stream=NULL; u32 data[IVTV_MBOX_MAX_DATA], result; u32 mem_offset, mem_size, hw_stream_type, buffer_bytes; - unsigned long flags; IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv_dec_sched_DMA\n"); @@ -2331,7 +2337,7 @@ void ivtv_dec_sched_DMA(struct ivtv *itv while ((max > x) && (mem_size > bytes_written)) { /* send a maximum of 'max' buffers */ - buf = ivtv_deq_peek_head(itv, &stream->full_q); + buf = __ivtv_deq_peek_head(&stream->full_q); if (buf == NULL) { IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: No more buffers to send\n"); @@ -2371,8 +2377,8 @@ void ivtv_dec_sched_DMA(struct ivtv *itv /* buffer is empty? */ if (buf->buffer.bytesused == 0) { - ivtv_del_buf(itv, &stream->full_q, buf); - ivtv_enq_buf(itv, &stream->dma_q, buf); + __ivtv_del_buf(&stream->full_q, buf); + __ivtv_enq_buf(&stream->dma_q, buf); } x++; } @@ -2391,16 +2397,12 @@ void ivtv_dec_sched_DMA(struct ivtv *itv data[1] = bytes_written; data[2] = hw_stream_type; - /* better to mask off the DEC interrupt... */ - spin_lock_irqsave(&itv->lock, flags); - /* note that we're DMA'ing */ mod_timer(&itv->dec_timeout, jiffies + DEC_DMA_TIMEOUT); set_bit(IVTV_F_S_DMAP, &stream->s_flags); set_bit(IVTV_F_I_BUSY, &itv->i_flags); ivtv_api_irqsafe(itv->dec_mbox, IVTV_API_DEC_DMA_FROM_HOST, 3,&data[0]); - spin_unlock_irqrestore(&itv->lock, flags); IVTV_DEBUG(IVTV_DEBUG_INFO, "Sched DEC dma: addr: 0x%08x, array_size 0x%08x, type 0x%08x\n", @@ -2410,16 +2412,12 @@ void ivtv_dec_sched_DMA(struct ivtv *itv void ivtv_dec_DMA_done(struct ivtv *itv) { struct ivtv_v4l2_stream *stream=NULL; struct ivtv_buffer *buf; - unsigned long flags; - int y, stmtype=-1; + int y, stmtype=-1, freed=0; IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: DMA Done tasklet\n"); - spin_lock_irqsave(&itv->lock, flags); - if (!test_and_clear_bit(IVTV_F_I_BUSY, &itv->i_flags)) { IVTV_DEBUG(IVTV_DEBUG_ERR, "DMAP not set\n"); - spin_unlock_irqrestore(&itv->lock, flags); goto debug; } @@ -2432,8 +2430,6 @@ void ivtv_dec_DMA_done(struct ivtv *itv) } } - spin_unlock_irqrestore(&itv->lock, flags); - /* Handle OSD DMA */ if (test_and_clear_bit(IVTV_F_I_OSD_DMA, &itv->i_flags)) { IVTV_DEBUG(IVTV_DEBUG_INFO, "OSD: DMA Done\n"); @@ -2464,12 +2460,13 @@ void ivtv_dec_DMA_done(struct ivtv *itv) buf->readpos = 0; /* put in the 'done' queue */ - ivtv_enq_buf(itv, &stream->free_q, buf); - - if (test_and_clear_bit(IVTV_F_S_OVERFLOW, &stream->s_flags)) - tasklet_schedule(&itv->dma_sched_tq); + __ivtv_enq_buf(&stream->free_q, buf); + freed++; } + if (freed && test_and_clear_bit(IVTV_F_S_OVERFLOW, &stream->s_flags)) + tasklet_schedule(&itv->dma_sched_tq); + /* wake up queue filler */ wake_up(&stream->waitq); wake_up(&itv->dec_master_w); @@ -2513,6 +2510,7 @@ int ivtv_get_timing_info(struct ivtv *it ssize_t ivtv_write(struct ivtv_open_id *id, const char *ubuf, size_t count) { int bytes_written=0, ret; + unsigned long flags; IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv_write\n"); @@ -2529,7 +2527,9 @@ ssize_t ivtv_write(struct ivtv_open_id * } /* send it! it'll return right away if no data needed */ + spin_lock_irqsave(&id->itv->lock, flags); ivtv_dec_sched_DMA(id->itv); + spin_unlock_irqrestore(&id->itv->lock, flags); } IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: returning %d\n", bytes_written); diff -urp ivtv-latest/driver/ivtv.h ivtv/driver/ivtv.h --- ivtv-latest/driver/ivtv.h 2003-11-08 22:46:13.000000000 +0100 +++ ivtv/driver/ivtv.h 2003-11-09 01:16:02.000000000 +0100 @@ -678,7 +678,7 @@ struct ivtv { /* FIXME should use part of v4l2_performace instead */ unsigned long trans_id; - struct tasklet_struct dma_sched_tq, dma_done_tq; + struct tasklet_struct dma_sched_tq; u32 enc_fw_ver, dec_fw_ver, base_addr; /*is base_addr needed? */ u32 irqmask; @@ -808,8 +808,8 @@ int ivtv_move_queue(struct ivtv *itv, st /* Hardware/IRQ */ void ivtv_irq_handler(int irq, void *dev_id, struct pt_regs *regs); -void ivtv_DMA_done_tasklet(unsigned long arg); -void ivtv_sched_DMA_tasklet(unsigned long arg); +void ivtv_DMA_done(struct ivtv *itv); +void ivtv_sched_DMA(struct ivtv *itv); void ivtv_dec_DMA_done(struct ivtv *itv); void ivtv_dec_sched_DMA(struct ivtv *itv); extern void ivtv_set_irq_mask(struct ivtv *itv, unsigned long mask);