📄 i810_audio.c
字号:
* S/PDIF, we turn off the analog output. This may not be * the right thing to do. * * Assumptions: * The DSP sample rate must already be set to a supported * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. */static void i810_set_spdif_output(struct i810_state *state, int slots, int rate){ int vol; int aud_reg; struct ac97_codec *codec = state->card->ac97_codec[0]; if(!(state->card->ac97_features & 4)) {#ifdef DEBUG printk(KERN_WARNING "i810_audio: S/PDIF transmitter not available.\n");#endif state->card->ac97_status &= ~SPDIF_ON; } else { if ( slots == -1 ) { /* Turn off S/PDIF */ aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); /* If the volume wasn't muted before we turned on S/PDIF, unmute it */ if ( !(state->card->ac97_status & VOL_MUTED) ) { aud_reg = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (aud_reg & ~VOL_MUTED)); } state->card->ac97_status &= ~(VOL_MUTED | SPDIF_ON); return; } vol = i810_ac97_get(codec, AC97_MASTER_VOL_STEREO); state->card->ac97_status = vol & VOL_MUTED; /* Set S/PDIF transmitter sample rate */ aud_reg = i810_ac97_get(codec, AC97_SPDIF_CONTROL); switch ( rate ) { case 32000: aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_32K; break; case 44100: aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_44K; break; case 48000: aud_reg = (aud_reg & AC97_SC_SPSR_MASK) | AC97_SC_SPSR_48K; break; default:#ifdef DEBUG printk(KERN_WARNING "i810_audio: %d sample rate not supported by S/PDIF.\n", rate);#endif /* turn off S/PDIF */ aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); state->card->ac97_status &= ~SPDIF_ON; return; } i810_ac97_set(codec, AC97_SPDIF_CONTROL, aud_reg); aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); aud_reg = (aud_reg & AC97_EA_SLOT_MASK) | slots | AC97_EA_VRA | AC97_EA_SPDIF; i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg); state->card->ac97_status |= SPDIF_ON; /* Check to make sure the configuration is valid */ aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); if ( ! (aud_reg & 0x0400) ) {#ifdef DEBUG printk(KERN_WARNING "i810_audio: S/PDIF transmitter configuration not valid (0x%04x).\n", aud_reg);#endif /* turn off S/PDIF */ i810_ac97_set(codec, AC97_EXTENDED_STATUS, (aud_reg & ~AC97_EA_SPDIF)); state->card->ac97_status &= ~SPDIF_ON; return; } /* Mute the analog output */ /* Should this only mute the PCM volume??? */ i810_ac97_set(codec, AC97_MASTER_VOL_STEREO, (vol | VOL_MUTED)); }}/* i810_set_dac_channels * * Configure the codec's multi-channel DACs * * The logic is backwards. Setting the bit to 1 turns off the DAC. * * What about the ICH? We currently configure it using the * SNDCTL_DSP_CHANNELS ioctl. If we're turnning on the DAC, * does that imply that we want the ICH set to support * these channels? * * TODO: * vailidate that the codec really supports these DACs * before turning them on. */static void i810_set_dac_channels(struct i810_state *state, int channel){ int aud_reg; struct ac97_codec *codec = state->card->ac97_codec[0]; aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS); aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK; state->card->ac97_status &= ~(SURR_ON | CENTER_LFE_ON); switch ( channel ) { case 2: /* always enabled */ break; case 4: aud_reg &= ~AC97_EA_PRJ; state->card->ac97_status |= SURR_ON; break; case 6: aud_reg &= ~(AC97_EA_PRJ | AC97_EA_PRI | AC97_EA_PRK); state->card->ac97_status |= SURR_ON | CENTER_LFE_ON; break; default: break; } i810_ac97_set(codec, AC97_EXTENDED_STATUS, aud_reg);}/* set playback sample rate */static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate){ struct dmabuf *dmabuf = &state->dmabuf; u32 new_rate; struct ac97_codec *codec=state->card->ac97_codec[0]; if(!(state->card->ac97_features&0x0001)) { dmabuf->rate = clocking;#ifdef DEBUG printk("Asked for %d Hz, but ac97_features says we only do %dHz. Sorry!\n", rate,clocking);#endif return clocking; } if (rate > 48000) rate = 48000; if (rate < 8000) rate = 8000; dmabuf->rate = rate; /* * Adjust for misclocked crap */ rate = ( rate * clocking)/48000; if(strict_clocking && rate < 8000) { rate = 8000; dmabuf->rate = (rate * 48000)/clocking; } new_rate=ac97_set_dac_rate(codec, rate); if(new_rate != rate) { dmabuf->rate = (new_rate * 48000)/clocking; }#ifdef DEBUG printk("i810_audio: called i810_set_dac_rate : asked for %d, got %d\n", rate, dmabuf->rate);#endif rate = new_rate; return dmabuf->rate;}/* set recording sample rate */static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate){ struct dmabuf *dmabuf = &state->dmabuf; u32 new_rate; struct ac97_codec *codec=state->card->ac97_codec[0]; if(!(state->card->ac97_features&0x0001)) { dmabuf->rate = clocking; return clocking; } if (rate > 48000) rate = 48000; if (rate < 8000) rate = 8000; dmabuf->rate = rate; /* * Adjust for misclocked crap */ rate = ( rate * clocking)/48000; if(strict_clocking && rate < 8000) { rate = 8000; dmabuf->rate = (rate * 48000)/clocking; } new_rate = ac97_set_adc_rate(codec, rate); if(new_rate != rate) { dmabuf->rate = (new_rate * 48000)/clocking; rate = new_rate; }#ifdef DEBUG printk("i810_audio: called i810_set_adc_rate : rate = %d/%d\n", dmabuf->rate, rate);#endif return dmabuf->rate;}/* get current playback/recording dma buffer pointer (byte offset from LBA), called with spinlock held! */ static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec){ struct dmabuf *dmabuf = &state->dmabuf; unsigned int civ, offset, port, port_picb, bytes = 2; if (!dmabuf->enable) return 0; if (rec) port = state->card->iobase + dmabuf->read_channel->port; else port = state->card->iobase + dmabuf->write_channel->port; if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) { port_picb = port + OFF_SR; bytes = 1; } else port_picb = port + OFF_PICB; do { civ = inb(port+OFF_CIV) & 31; offset = inw(port_picb); /* Must have a delay here! */ if(offset == 0) udelay(1); /* Reread both registers and make sure that that total * offset from the first reading to the second is 0. * There is an issue with SiS hardware where it will count * picb down to 0, then update civ to the next value, * then set the new picb to fragsize bytes. We can catch * it between the civ update and the picb update, making * it look as though we are 1 fragsize ahead of where we * are. The next to we get the address though, it will * be back in the right place, and we will suddenly think * we just went forward dmasize - fragsize bytes, causing * totally stupid *huge* dma overrun messages. We are * assuming that the 1us delay is more than long enough * that we won't have to worry about the chip still being * out of sync with reality ;-) */ } while (civ != (inb(port+OFF_CIV) & 31) || offset != inw(port_picb)); return (((civ + 1) * dmabuf->fragsize - (bytes * offset)) % dmabuf->dmasize);}/* Stop recording (lock held) */static 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); // wait for the card to acknowledge shutdown while( inb(card->iobase + PI_CR) != 0 ) ; // now clear any latent interrupt bits (like the halt bit) if(card->pci_id == PCI_DEVICE_ID_SI_7012) outb( inb(card->iobase + PI_PICB), card->iobase + PI_PICB ); else outb( inb(card->iobase + PI_SR), card->iobase + PI_SR ); outl( inl(card->iobase + GLOB_STA) & INT_PI, card->iobase + GLOB_STA);}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 inline void __start_adc(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; if (dmabuf->count < dmabuf->dmasize && dmabuf->ready && !dmabuf->enable && (dmabuf->trigger & PCM_ENABLE_INPUT)) { dmabuf->enable |= ADC_RUNNING; outb((1<<4) | (1<<2) | 1, state->card->iobase + PI_CR); }}static void start_adc(struct i810_state *state){ struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); __start_adc(state); spin_unlock_irqrestore(&card->lock, flags);}/* stop playback (lock held) */static 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); // wait for the card to acknowledge shutdown while( inb(card->iobase + PO_CR) != 0 ) ; // now clear any latent interrupt bits (like the halt bit) if(card->pci_id == PCI_DEVICE_ID_SI_7012) outb( inb(card->iobase + PO_PICB), card->iobase + PO_PICB ); else outb( inb(card->iobase + PO_SR), card->iobase + PO_SR ); outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA);}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 inline void __start_dac(struct i810_state *state){ struct dmabuf *dmabuf = &state->dmabuf; if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) { dmabuf->enable |= DAC_RUNNING; outb((1<<4) | (1<<2) | 1, state->card->iobase + PO_CR); }}static void start_dac(struct i810_state *state){ struct i810_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); __start_dac(state); spin_unlock_irqrestore(&card->lock, flags);}#define DMABUF_DEFAULTORDER (16-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= NULL; int order, size; struct page *page, *pend; /* If we don't have any oss frag params, then use our default ones */ if(dmabuf->ossmaxfrags == 0) dmabuf->ossmaxfrags = 4; if(dmabuf->ossfragsize == 0) dmabuf->ossfragsize = (PAGE_SIZE<<DMABUF_DEFAULTORDER)/dmabuf->ossmaxfrags; size = dmabuf->ossfragsize * dmabuf->ossmaxfrags; if(dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size) return 0; /* alloc enough to satisfy the oss params */ 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++) 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 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -