📄 i810_audio.c
字号:
{// u16 w;// struct i810_card *card = state->card;// struct dmabuf *dmabuf = &state->dmabuf;// struct i810_channel *channel = dmabuf->channel; /* Enable AC-97 ADC (capture) */// if (dmabuf->fmt & I810_FMT_16BIT) {// if (dmabuf->fmt & I810_FMT_STEREO)}/* get current playback/recording dma buffer pointer (byte offset from LBA), called with spinlock held! */ extern __inline__ unsigned i810_get_dma_addr(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned int civ, offset; struct i810_channel *c = dmabuf->channel; if (!dmabuf->enable) return 0; do { civ = inb(state->card->iobase+c->port+OFF_CIV); offset = (civ + 1) * (dmabuf->dmasize/SG_LEN) - 2 * inw(state->card->iobase+c->port+OFF_PICB); /* CIV changed before we read PICB (very seldom) ? * then PICB was rubbish, so try again */ } while (civ != inb(state->card->iobase+c->port+OFF_CIV)); return offset;}static void resync_dma_ptrs(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct i810_channel *c = dmabuf->channel; int offset; offset = inb(state->card->iobase+c->port+OFF_CIV); offset *= (dmabuf->dmasize/SG_LEN); dmabuf->hwptr=dmabuf->swptr = offset;} /* Stop recording (lock held) */extern __inline__ void __stop_adc(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct i810_card *card = state->card; dmabuf->enable &= ~ADC_RUNNING; outb(0, card->iobase + PI_CR);}static void stop_adc(struct i810_state *state){ struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); __stop_adc(state); spin_unlock_irqrestore(&card->lock, flags);}static void start_adc(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) { dmabuf->enable |= ADC_RUNNING; outb((1<<4) | 1<<2 | 1, card->iobase + PI_CR); } spin_unlock_irqrestore(&card->lock, flags);}/* stop playback (lock held) */extern __inline__ void __stop_dac(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct i810_card *card = state->card; dmabuf->enable &= ~DAC_RUNNING; outb(0, card->iobase + PO_CR);}static void stop_dac(struct i810_state *state){ struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); __stop_dac(state); spin_unlock_irqrestore(&card->lock, flags);} static void start_dac(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) { if(!(dmabuf->enable&DAC_RUNNING)) { dmabuf->enable |= DAC_RUNNING; outb((1<<4) | 1<<2 | 1, card->iobase + PO_CR); } } spin_unlock_irqrestore(&card->lock, flags);}#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)#define DMABUF_MINORDER 1/* allocate DMA buffer, playback and recording buffer should be allocated seperately */static int alloc_dmabuf(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; void *rawbuf; int order; struct page *page, *pend; /* alloc as big a chunk as we can, FIXME: is this necessary ?? */ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) 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++) mem_map_reserve(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++) mem_map_unreserve(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 sg_item *sg; unsigned bytepersec; unsigned bufsize; unsigned long flags; int ret; unsigned fragsize; int i; spin_lock_irqsave(&state->card->lock, flags); resync_dma_ptrs(state); dmabuf->total_bytes = 0; dmabuf->count = dmabuf->error = 0; spin_unlock_irqrestore(&state->card->lock, flags); /* allocate DMA buffer if not allocated yet */ if (!dmabuf->rawbuf) if ((ret = alloc_dmabuf(state))) return ret; /* FIXME: figure out all this OSS fragment stuff */ bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt]; bufsize = PAGE_SIZE << dmabuf->buforder; if (dmabuf->ossfragshift) { if ((1000 << dmabuf->ossfragshift) < bytepersec) dmabuf->fragshift = ld2(bytepersec/1000); else dmabuf->fragshift = dmabuf->ossfragshift; } else { /* lets hand out reasonable big ass buffers by default */ dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2); } dmabuf->numfrag = bufsize >> dmabuf->fragshift; while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) { dmabuf->fragshift--; dmabuf->numfrag = bufsize >> dmabuf->fragshift; } dmabuf->fragsize = 1 << dmabuf->fragshift; if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag) dmabuf->numfrag = dmabuf->ossmaxfrags; dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt]; dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; memset(dmabuf->rawbuf, (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80, dmabuf->dmasize); /* * Now set up the ring */ sg=&dmabuf->channel->sg[0]; fragsize = bufsize / SG_LEN; /* * Load up 32 sg entries and take an interrupt at half * way (we might want more interrupts later..) */ for(i=0;i<32;i++) { sg->busaddr=virt_to_bus(dmabuf->rawbuf+fragsize*i); sg->control=(fragsize>>1); sg->control|=CON_IOC; sg++; } spin_lock_irqsave(&state->card->lock, flags); outb(2, state->card->iobase+dmabuf->channel->port+OFF_CR); /* reset DMA machine */ outl(virt_to_bus(&dmabuf->channel->sg[0]), state->card->iobase+dmabuf->channel->port+OFF_BDBAR); outb(16, state->card->iobase+dmabuf->channel->port+OFF_LVI); outb(0, state->card->iobase+dmabuf->channel->port+OFF_CIV); if (rec) { i810_rec_setup(state); } else { i810_play_setup(state); } spin_unlock_irqrestore(&state->card->lock, flags); /* set the ready flag for the dma buffer */ dmabuf->ready = 1;#ifdef DEBUG printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d, numfrag = %d, " "fragsize = %d dmasize = %d\n", dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, dmabuf->fragsize, dmabuf->dmasize);#endif return 0;}/* * Clear the rest of the last i810 dma buffer, normally there is no rest * because the OSS fragment size is the same as the size of this buffer. */static void i810_clear_tail(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned swptr; unsigned char silence = (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80; unsigned int len; unsigned long flags; spin_lock_irqsave(&state->card->lock, flags); swptr = dmabuf->swptr; spin_unlock_irqrestore(&state->card->lock, flags); if(dmabuf->dmasize) len = swptr % (dmabuf->dmasize/SG_LEN); else len = 0; memset(dmabuf->rawbuf + swptr, silence, len); spin_lock_irqsave(&state->card->lock, flags); dmabuf->swptr += len; dmabuf->count += len; spin_unlock_irqrestore(&state->card->lock, flags); /* restart the dma machine in case it is halted */ start_dac(state);}static int drain_dac(struct i810_state *state, int nonblock){ DECLARE_WAITQUEUE(wait, current); struct dmabuf *dmabuf = &state->dmabuf; unsigned long flags; unsigned long tmo; int count; if (dmabuf->mapped || !dmabuf->ready) return 0; add_wait_queue(&dmabuf->wait, &wait); for (;;) { /* It seems that we have to set the current state to TASK_INTERRUPTIBLE every time to make the process really go to sleep */ current->state = TASK_INTERRUPTIBLE; spin_lock_irqsave(&state->card->lock, flags); count = dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); if (count <= 0) break; if (signal_pending(current)) break; if (nonblock) { remove_wait_queue(&dmabuf->wait, &wait); current->state = TASK_RUNNING; return -EBUSY; } tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; tmo >>= sample_shift[dmabuf->fmt]; if (!schedule_timeout(tmo ? tmo : 1) && tmo){ printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n"); break; } } remove_wait_queue(&dmabuf->wait, &wait); current->state = TASK_RUNNING; if (signal_pending(current)) return -ERESTARTSYS; return 0;}/* 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, swptr; int clear_cnt = 0; int diff; unsigned char silence;// unsigned half_dmasize; /* update hardware pointer */ hwptr = i810_get_dma_addr(state); diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;// printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); dmabuf->hwptr = hwptr; dmabuf->total_bytes += diff; /* error handling and process wake up for DAC */ if (dmabuf->enable == ADC_RUNNING) { if (dmabuf->mapped) { dmabuf->count -= diff; if (dmabuf->count >= (signed)dmabuf->fragsize) wake_up(&dmabuf->wait); } else { dmabuf->count += diff; if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { /* buffer underrun or buffer overrun, we have no way to recover it here, just stop the machine and let the process force hwptr and swptr to sync */ __stop_adc(state); dmabuf->error++; } else if (!dmabuf->endcleared) { swptr = dmabuf->swptr; silence = (dmabuf->fmt & I810_FMT_16BIT ? 0 : 0x80); if (dmabuf->count < (signed) dmabuf->fragsize) { clear_cnt = dmabuf->fragsize; if ((swptr + clear_cnt) > dmabuf->dmasize) clear_cnt = dmabuf->dmasize - swptr; memset (dmabuf->rawbuf + swptr, silence, clear_cnt); dmabuf->endcleared = 1; } } wake_up(&dmabuf->wait); } } /* error handling and process wake up for DAC */ if (dmabuf->enable == DAC_RUNNING) { if (dmabuf->mapped) { dmabuf->count += diff; if (dmabuf->count >= (signed)dmabuf->fragsize) wake_up(&dmabuf->wait); } else { dmabuf->count -= diff; if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { /* buffer underrun or buffer overrun, we have no way to recover it here, just stop the machine and let the process force hwptr and swptr to sync */ __stop_dac(state); printk("DMA overrun on send\n"); dmabuf->error++; } wake_up(&dmabuf->wait); } }}static void i810_channel_interrupt(struct i810_card *card){ int i;// printk("CHANNEL IRQ .. "); for(i=0;i<NR_HW_CH;i++) { struct i810_state *state = card->states[i]; struct i810_channel *c; unsigned long port = card->iobase; u16 status; if(!state) continue; if(!state->dmabuf.ready) continue; c=state->dmabuf.channel; port+=c->port; // printk("PORT %lX (", port); status = inw(port + OFF_SR); // printk("ST%d ", status); if(status & DMA_INT_LVI) { /* Back to the start */// printk("LVI - STOP"); outb((inb(port+OFF_CIV)-1)&31, port+OFF_LVI); i810_update_ptr(state); outb(0, port + OFF_CR); } if(status & DMA_INT_COMPLETE) { int x; /* Keep the card chasing its tail */ outb(x=((inb(port+OFF_CIV)-1)&31), port+OFF_LVI); i810_update_ptr(state);// printk("COMP%d ",x); }// printk(")"); outw(status & DMA_INT_MASK, port + OFF_SR); }// printk("\n");}static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct i810_card *card = (struct i810_card *)dev_id; u32 status; spin_lock(&card->lock); status = inl(card->iobase + GLOB_STA); if(!(status & INT_MASK)) { spin_unlock(&card->lock); return; /* not for us */ }// printk("Interrupt %X: ", status); if(status & (INT_PO|INT_PI|INT_MC)) i810_channel_interrupt(card); /* clear 'em */ outl(status & INT_MASK, card->iobase + GLOB_STA); spin_unlock(&card->lock);}static loff_t i810_llseek(struct file *file, loff_t offset, int origin){ return -ESPIPE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -