📄 i810_audio.c
字号:
for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { if ( (PAGE_SIZE<<order) > size ) continue; if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, PAGE_SIZE << order, &dmabuf->dma_handle))) break; } if (!rawbuf) return -ENOMEM;#ifdef DEBUG printk("i810_audio: allocated %ld (order = %d) bytes at %p\n", PAGE_SIZE << order, order, rawbuf);#endif dmabuf->ready = dmabuf->mapped = 0; dmabuf->rawbuf = rawbuf; dmabuf->buforder = order; /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1); for (page = virt_to_page(rawbuf); page <= pend; page++) SetPageReserved(page); return 0;}/* free DMA buffer */static void dealloc_dmabuf(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct page *page, *pend; if (dmabuf->rawbuf) { /* undo marking the pages as reserved */ pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) ClearPageReserved(page); pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder, dmabuf->rawbuf, dmabuf->dma_handle); } dmabuf->rawbuf = NULL; dmabuf->mapped = dmabuf->ready = 0;}static int prog_dmabuf(struct i810_state *state, unsigned rec){ struct dmabuf *dmabuf = &state->dmabuf; struct i810_channel *c; struct sg_item *sg; unsigned long flags; int ret; unsigned fragint; int i; spin_lock_irqsave(&state->card->lock, flags); if(dmabuf->enable & DAC_RUNNING) __stop_dac(state); if(dmabuf->enable & ADC_RUNNING) __stop_adc(state); dmabuf->total_bytes = 0; dmabuf->count = dmabuf->error = 0; dmabuf->swptr = dmabuf->hwptr = 0; spin_unlock_irqrestore(&state->card->lock, flags); /* allocate DMA buffer, let alloc_dmabuf determine if we are already * allocated well enough or if we should replace the current buffer * (assuming one is already allocated, if it isn't, then allocate it). */ if ((ret = alloc_dmabuf(state))) return ret; /* FIXME: figure out all this OSS fragment stuff */ /* I did, it now does what it should according to the OSS API. DL */ /* We may not have realloced our dmabuf, but the fragment size to * fragment number ratio may have changed, so go ahead and reprogram * things */ dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder; dmabuf->numfrag = SG_LEN; dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag; dmabuf->fragsamples = dmabuf->fragsize >> 1; dmabuf->fragshift = ffs(dmabuf->fragsize) - 1; dmabuf->userfragsize = dmabuf->ossfragsize; dmabuf->userfrags = dmabuf->dmasize/dmabuf->ossfragsize; memset(dmabuf->rawbuf, 0, dmabuf->dmasize); if(dmabuf->ossmaxfrags == 4) { fragint = 8; } else if (dmabuf->ossmaxfrags == 8) { fragint = 4; } else if (dmabuf->ossmaxfrags == 16) { fragint = 2; } else { fragint = 1; } /* * Now set up the ring */ if(dmabuf->read_channel) c = dmabuf->read_channel; else c = dmabuf->write_channel; while(c != NULL) { sg=&c->sg[0]; /* * Load up 32 sg entries and take an interrupt at half * way (we might want more interrupts later..) */ for(i=0;i<dmabuf->numfrag;i++) { sg->busaddr=(u32)dmabuf->dma_handle+dmabuf->fragsize*i; // the card will always be doing 16bit stereo sg->control=dmabuf->fragsamples; if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) sg->control <<= 1; sg->control|=CON_BUFPAD; // set us up to get IOC interrupts as often as needed to // satisfy numfrag requirements, no more if( ((i+1) % fragint) == 0) { sg->control|=CON_IOC; } sg++; } spin_lock_irqsave(&state->card->lock, flags); I810_IOWRITEB(2, state->card, c->port+OFF_CR); /* reset DMA machine */ while( I810_IOREADB(state->card, c->port+OFF_CR) & 0x02 ) ; I810_IOWRITEL((u32)state->card->chandma + c->num*sizeof(struct i810_channel), state->card, c->port+OFF_BDBAR); CIV_TO_LVI(state->card, c->port, 0); spin_unlock_irqrestore(&state->card->lock, flags); if(c != dmabuf->write_channel) c = dmabuf->write_channel; else c = NULL; } /* set the ready flag for the dma buffer */ dmabuf->ready = 1;#ifdef DEBUG printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, " "fragsize = %d dmasize = %d\n", dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, dmabuf->fragsize, dmabuf->dmasize);#endif return 0;}static void __i810_update_lvi(struct i810_state *state, int rec){ struct dmabuf *dmabuf = &state->dmabuf; int x, port; int trigger; int count, fragsize; void (*start)(struct i810_state *); count = dmabuf->count; if (rec) { port = dmabuf->read_channel->port; trigger = PCM_ENABLE_INPUT; start = __start_adc; count = dmabuf->dmasize - count; } else { port = dmabuf->write_channel->port; trigger = PCM_ENABLE_OUTPUT; start = __start_dac; } /* Do not process partial fragments. */ fragsize = dmabuf->fragsize; if (count < fragsize) return; if (!dmabuf->enable && dmabuf->ready) { if (!(dmabuf->trigger & trigger)) return; start(state); while (!(I810_IOREADB(state->card, port + OFF_CR) & ((1<<4) | (1<<2)))) ; } /* MASKP2(swptr, fragsize) - 1 is the tail of our transfer */ x = MODULOP2(MASKP2(dmabuf->swptr, fragsize) - 1, dmabuf->dmasize); x >>= dmabuf->fragshift; I810_IOWRITEB(x, state->card, port + OFF_LVI);}static void i810_update_lvi(struct i810_state *state, int rec){ struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; if(!dmabuf->ready) return; spin_lock_irqsave(&state->card->lock, flags); __i810_update_lvi(state, rec); spin_unlock_irqrestore(&state->card->lock, flags);}/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */static void i810_update_ptr(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned hwptr; unsigned fragmask, dmamask; int diff; fragmask = MASKP2(~0, dmabuf->fragsize); dmamask = MODULOP2(~0, dmabuf->dmasize); /* error handling and process wake up for ADC */ if (dmabuf->enable == ADC_RUNNING) { /* update hardware pointer */ hwptr = i810_get_dma_addr(state, 1) & fragmask; diff = (hwptr - dmabuf->hwptr) & dmamask;#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);#endif dmabuf->hwptr = hwptr; dmabuf->total_bytes += diff; dmabuf->count += diff; if (dmabuf->count > dmabuf->dmasize) { /* buffer underrun or buffer overrun */ /* this is normal for the end of a read */ /* only give an error if we went past the */ /* last valid sg entry */ if (GET_CIV(state->card, PI_BASE) != GET_LVI(state->card, PI_BASE)) { printk(KERN_WARNING "i810_audio: DMA overrun on read\n"); dmabuf->error++; } } if (diff) wake_up(&dmabuf->wait); } /* error handling and process wake up for DAC */ if (dmabuf->enable == DAC_RUNNING) { /* update hardware pointer */ hwptr = i810_get_dma_addr(state, 0) & fragmask; diff = (hwptr - dmabuf->hwptr) & dmamask;#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);#endif dmabuf->hwptr = hwptr; dmabuf->total_bytes += diff; dmabuf->count -= diff; if (dmabuf->count < 0) { /* buffer underrun or buffer overrun */ /* this is normal for the end of a write */ /* only give an error if we went past the */ /* last valid sg entry */ if (GET_CIV(state->card, PO_BASE) != GET_LVI(state->card, PO_BASE)) { printk(KERN_WARNING "i810_audio: DMA overrun on write\n"); printk("i810_audio: CIV %d, LVI %d, hwptr %x, " "count %d\n", GET_CIV(state->card, PO_BASE), GET_LVI(state->card, PO_BASE), dmabuf->hwptr, dmabuf->count); dmabuf->error++; } } if (diff) wake_up(&dmabuf->wait); }}static inline int i810_get_free_write_space(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; int free; i810_update_ptr(state); // catch underruns during playback if (dmabuf->count < 0) { dmabuf->count = 0; dmabuf->swptr = dmabuf->hwptr; } free = dmabuf->dmasize - dmabuf->count; if(free < 0) return(0); return(free);}static inline int i810_get_available_read_data(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; int avail; i810_update_ptr(state); // catch overruns during record if (dmabuf->count > dmabuf->dmasize) { dmabuf->count = dmabuf->dmasize; dmabuf->swptr = dmabuf->hwptr; } avail = dmabuf->count; if(avail < 0) return(0); return(avail);}static inline void fill_partial_frag(struct dmabuf *dmabuf){ unsigned fragsize; unsigned swptr, len; fragsize = dmabuf->fragsize; swptr = dmabuf->swptr; len = fragsize - MODULOP2(dmabuf->swptr, fragsize); if (len == fragsize) return; memset(dmabuf->rawbuf + swptr, '\0', len); dmabuf->swptr = MODULOP2(swptr + len, dmabuf->dmasize); dmabuf->count += len;}static int drain_dac(struct i810_state *state, int signals_allowed){ DECLARE_WAITQUEUE(wait, current); struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; unsigned long tmo; int count; if (!dmabuf->ready) return 0; if(dmabuf->mapped) { stop_dac(state); return 0; } spin_lock_irqsave(&state->card->lock, flags); fill_partial_frag(dmabuf); /* * This will make sure that our LVI is correct, that our * pointer is updated, and that the DAC is running. We * have to force the setting of dmabuf->trigger to avoid * any possible deadlocks. */ dmabuf->trigger = PCM_ENABLE_OUTPUT; __i810_update_lvi(state, 0); spin_unlock_irqrestore(&state->card->lock, flags); add_wait_queue(&dmabuf->wait, &wait); for (;;) { spin_lock_irqsave(&state->card->lock, flags); i810_update_ptr(state); count = dmabuf->count; /* It seems that we have to set the current state to * TASK_INTERRUPTIBLE every time to make the process * really go to sleep. This also has to be *after* the * update_ptr() call because update_ptr is likely to * do a wake_up() which will unset this before we ever * try to sleep, resuling in a tight loop in this code * instead of actually sleeping and waiting for an * interrupt to wake us up! */ __set_current_state(signals_allowed ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); spin_unlock_irqrestore(&state->card->lock, flags); if (count <= 0) break; if (signal_pending(current) && signals_allowed) { break; } /* * set the timeout to significantly longer than it *should* * take for the DAC to drain the DMA buffer */ tmo = (count * HZ) / (dmabuf->rate); if (!schedule_timeout(tmo >= 2 ? tmo : 2)){ printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n"); count = 0; break; } } set_current_state(TASK_RUNNING); remove_wait_queue(&dmabuf->wait, &wait); if(count > 0 && signal_pending(current) && signals_allowed) return -ERESTARTSYS; stop_dac(state); return 0;}static void i810_channel_interrupt(struct i810_card *card){ int i, count; #ifdef DEBUG_INTERRUPTS printk("CHANNEL ");#endif for(i=0;i<NR_HW_CH;i++) { struct i810_state *state = card->states[i]; struct i810_channel *c; struct dmabuf *dmabuf; unsigned long port; u16 status; if(!state) continue; if(!state->dmabuf.ready) continue; dmabuf = &state->dmabuf; if(dmabuf->enable & DAC_RUNNING) { c=dmabuf->write_channel; } else if(dmabuf->enable & ADC_RUNNING) { c=dmabuf->read_channel; } else /* This can occur going from R/W to close */ continue; port = c->port; if(card->pci_id == PCI_DEVICE_ID_SI_7012) status = I810_IOREADW(card, port + OFF_PICB); else status = I810_IOREADW(card, port + OFF_SR);#ifdef DEBUG_INTERRUPTS printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status);#endif if(status & DMA_INT_COMPLETE) { /* only wake_up() waiters if this interrupt signals * us being beyond a userfragsize of data open or * available, and i810_update_ptr() does that for * us */ i810_update_ptr(state);#ifdef DEBUG_INTERRUPTS
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -