📄 trident.c
字号:
channel->eso -= 1; if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) { channel->attribute = 0; if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451) { if ((channel->num == ALI_SPDIF_IN_CHANNEL) || (channel->num == ALI_PCM_IN_CHANNEL)) ali_disable_special_channel(state->card, channel->num); else if ((inl(TRID_REG(state->card, ALI_GLOBAL_CONTROL)) & ALI_SPDIF_OUT_CH_ENABLE) && (channel->num == ALI_SPDIF_OUT_CHANNEL)) { ali_set_spdif_out_rate(state->card, state->dmabuf.rate); state->dmabuf.channel->delta = 0x1000; } } } channel->fm_vol = 0x0; channel->control = CHANNEL_LOOP; if (dmabuf->fmt & TRIDENT_FMT_16BIT) { /* 16-bits */ channel->control |= CHANNEL_16BITS; /* signed */ channel->control |= CHANNEL_SIGNED; } if (dmabuf->fmt & TRIDENT_FMT_STEREO) /* stereo */ channel->control |= CHANNEL_STEREO;#ifdef DEBUG printk("trident: trident_play_setup, LBA = 0x%08x, " "Delta = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", channel->lba, channel->delta, channel->eso, channel->control);#endif trident_write_voice_regs(state);}/* prepare channel attributes for recording */static void trident_rec_setup(struct trident_state *state){ u16 w; u8 bval; struct trident_card *card = state->card; struct dmabuf *dmabuf = &state->dmabuf; struct trident_channel *channel = dmabuf->channel; unsigned int rate; /* Enable AC-97 ADC (capture) */ switch (card->pci_id) { case PCI_DEVICE_ID_ALI_5451: ali_enable_special_channel(state); break; case PCI_DEVICE_ID_SI_7018: /* for 7018, the ac97 is always in playback/record (duplex) mode */ break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT)); outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); /* enable and set record channel */ outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH)); break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: w = inw(TRID_REG(card, T4D_MISCINT)); outw(w | 0x1000, TRID_REG(card, T4D_MISCINT)); /* enable and set record channel */ outb(0x80 | channel->num, TRID_REG(card, T4D_REC_CH)); break; case PCI_DEVICE_ID_INTERG_5050: /* don't know yet, using special channel 22 in GC1(0xd4)? */ break; default: return; } channel->lba = dmabuf->dma_handle; channel->delta = compute_rate_rec(dmabuf->rate); if ((card->pci_id == PCI_DEVICE_ID_ALI_5451) && (channel->num == ALI_SPDIF_IN_CHANNEL)) { rate = ali_get_spdif_in_rate(card); if (rate == 0) { printk(KERN_WARNING "trident: ALi 5451 S/PDIF input setup error!\n"); rate = 48000; } bval = inb(TRID_REG(card,ALI_SPDIF_CTRL)); if (bval & 0x10) { outb(bval,TRID_REG(card,ALI_SPDIF_CTRL)); printk(KERN_WARNING "trident: cleared ALi 5451 S/PDIF parity error flag.\n"); } if (rate != 48000) channel->delta = ((rate << 12) / dmabuf->rate) & 0x0000ffff; } channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; channel->eso -= 1; if (state->card->pci_id != PCI_DEVICE_ID_SI_7018) { channel->attribute = 0; } channel->fm_vol = 0x0; channel->control = CHANNEL_LOOP; if (dmabuf->fmt & TRIDENT_FMT_16BIT) { /* 16-bits */ channel->control |= CHANNEL_16BITS; /* signed */ channel->control |= CHANNEL_SIGNED; } if (dmabuf->fmt & TRIDENT_FMT_STEREO) /* stereo */ channel->control |= CHANNEL_STEREO;#ifdef DEBUG printk("trident: trident_rec_setup, LBA = 0x%08x, " "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", channel->lba, channel->delta, channel->eso, channel->control);#endif trident_write_voice_regs(state);}/* get current playback/recording dma buffer pointer (byte offset from LBA), called with spinlock held! */static inline unsigned trident_get_dma_addr(struct trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; u32 cso; if (!dmabuf->enable) return 0; outb(dmabuf->channel->num, TRID_REG(state->card, T4D_LFO_GC_CIR)); switch (state->card->pci_id) { case PCI_DEVICE_ID_ALI_5451: case PCI_DEVICE_ID_SI_7018: case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: case PCI_DEVICE_ID_INTERG_5050: /* 16 bits ESO, CSO for 7018 and DX */ cso = inw(TRID_REG(state->card, CH_DX_CSO_ALPHA_FMS + 2)); break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: /* 24 bits ESO, CSO for NX */ cso = inl(TRID_REG(state->card, CH_NX_DELTA_CSO)) & 0x00ffffff; break; default: return 0; }#ifdef DEBUG printk("trident: trident_get_dma_addr: chip reported channel: %d, " "cso = 0x%04x\n", dmabuf->channel->num, cso);#endif /* ESO and CSO are in units of Samples, convert to byte offset */ cso <<= sample_shift[dmabuf->fmt]; return (cso % dmabuf->dmasize);}/* Stop recording (lock held) */static inline void __stop_adc(struct trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned int chan_num = dmabuf->channel->num; struct trident_card *card = state->card; dmabuf->enable &= ~ADC_RUNNING; trident_stop_voice(card, chan_num); trident_disable_voice_irq(card, chan_num);}static void stop_adc(struct trident_state *state){ struct trident_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 trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned int chan_num = dmabuf->channel->num; struct trident_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; trident_enable_voice_irq(card, chan_num); trident_start_voice(card, chan_num); } spin_unlock_irqrestore(&card->lock, flags);}/* stop playback (lock held) */static inline void __stop_dac(struct trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned int chan_num = dmabuf->channel->num; struct trident_card *card = state->card; dmabuf->enable &= ~DAC_RUNNING; trident_stop_voice(card, chan_num); if (state->chans_num == 6) { trident_stop_voice(card, state->other_states[0]->dmabuf.channel->num); trident_stop_voice(card, state->other_states[1]->dmabuf.channel->num); trident_stop_voice(card, state->other_states[2]->dmabuf.channel->num); trident_stop_voice(card, state->other_states[3]->dmabuf.channel->num); } trident_disable_voice_irq(card, chan_num);}static void stop_dac(struct trident_state *state){ struct trident_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 trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned int chan_num = dmabuf->channel->num; struct trident_card *card = state->card; unsigned long flags; spin_lock_irqsave(&card->lock, flags); if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) { dmabuf->enable |= DAC_RUNNING; trident_enable_voice_irq(card, chan_num); trident_start_voice(card, chan_num); if (state->chans_num == 6) { trident_start_voice(card, state->other_states[0]->dmabuf.channel->num); trident_start_voice(card, state->other_states[1]->dmabuf.channel->num); trident_start_voice(card, state->other_states[2]->dmabuf.channel->num); trident_start_voice(card, state->other_states[3]->dmabuf.channel->num); } } 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 trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; void *rawbuf = NULL; 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("trident: 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 trident_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 trident_state *state, unsigned rec){ struct dmabuf *dmabuf = &state->dmabuf; unsigned bytepersec; struct trident_state *s = state; unsigned bufsize, dma_nums; unsigned long flags; int ret, i, order; struct page *page, *pend; lock_set_fmt(state); if (state->chans_num == 6) dma_nums = 5; else dma_nums = 1; for (i = 0; i < dma_nums; i++) { if (i > 0) { s = state->other_states[i - 1]; dmabuf = &s->dmabuf; dmabuf->fmt = state->dmabuf.fmt; dmabuf->rate = state->dmabuf.rate; } spin_lock_irqsave(&s->card->lock, flags); dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0; dmabuf->count = dmabuf->error = 0; spin_unlock_irqrestore(&s->card->lock, flags); /* allocate DMA buffer if not allocated yet */ if (!dmabuf->rawbuf) { if (i == 0) { if ((ret = alloc_dmabuf(state))) { unlock_set_fmt(state); return ret; } } else { if ((order = state->dmabuf.buforder - 1) >= DMABUF_MINORDER) { dmabuf->rawbuf = pci_alloc_consistent(state->card->pci_dev, PAGE_SIZE << order, &dmabuf->dma_handle); } if (!dmabuf->rawbuf) { free_pages((unsigned long)state->dmabuf.rawbuf, state->dmabuf.buforder); state->dmabuf.rawbuf = NULL; i-=2; for (; i >= 0; i--) { pci_free_consistent(state->card->pci_dev, PAGE_SIZE << state->other_states[i]->dmabuf.buforder, state->other_states[i]->dmabuf.rawbuf, state->other_states[i]->dmabuf.dma_handle); } unlock_set_fmt(state); return -ENOMEM; } dmabuf->ready = dmabuf->mapped = 0; dmabuf->buforder = order; pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << order) - 1); for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++) mem_map_reserve(page); } } /* 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 & TRIDENT_FMT_16BIT) ? 0 : 0x80, dmabuf->dmasize); spin_lock_irqsave(&s->card->lock, flags); if (rec) { trident_rec_setup(s); } else { trident_play_setup(s); } spin_unlock_irqrestore(&s->card->lock, flags); /* set the ready flag for the dma buffer */ dmabuf->ready = 1;#ifdef DEBUG printk("trident: prog_dmabuf(%d), sample rate = %d, format = %d, numfrag = %d, " "fragsize = %d dmasize = %d\n", dmabuf->channel->num, dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, dmabuf->fragsize, dmabuf->dmasize);#endif } unlock_set_fmt(state); return 0;}/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e. |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx| but we almost always get this |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------| so we have to clear the tail space to "silence" |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000|*/static void trident_clear_tail(struct trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; unsigned swptr; unsigned char silence = (dmabuf->fmt & TRIDENT_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 (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize) return; if (swptr < dmabuf->dmasize/2) len = dmabuf->dmasize/2 - swptr; else len = dmabuf->dmasize - swptr; memset(dmabuf->rawbuf + swptr, silence, len); if(state->card->pci_id != PCI_DEVICE_ID_ALI_5451) { spin_lock_irqsave(&state->card->lock, flags); dmabuf->swptr += len; dmabuf->count += len; spin_unlock_irqrestore(&state->card->lock, flags); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -