trident.c
来自「linux 内核源代码」· C语言 代码 · 共 2,292 行 · 第 1/5 页
C
2,292 行
{ int delta; if (rate == 44100) delta = 0x116a; else if (rate == 8000) delta = 0x6000; else if (rate == 48000) delta = 0x1000; else delta = ((48000 << 12) / rate) & 0x0000ffff; return delta;}/* set playback sample rate */static unsigned inttrident_set_dac_rate(struct trident_state *state, unsigned int rate){ struct dmabuf *dmabuf = &state->dmabuf; if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; dmabuf->rate = rate; dmabuf->channel->delta = compute_rate_play(rate); trident_write_voice_regs(state); pr_debug("trident: called trident_set_dac_rate : rate = %d\n", rate); return rate;}/* set recording sample rate */static unsigned inttrident_set_adc_rate(struct trident_state *state, unsigned int rate){ struct dmabuf *dmabuf = &state->dmabuf; if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; dmabuf->rate = rate; dmabuf->channel->delta = compute_rate_rec(rate); trident_write_voice_regs(state); pr_debug("trident: called trident_set_adc_rate : rate = %d\n", rate); return rate;}/* prepare channel attributes for playback */static voidtrident_play_setup(struct trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct trident_channel *channel = dmabuf->channel; channel->lba = dmabuf->dma_handle; channel->delta = compute_rate_play(dmabuf->rate); channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; 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; pr_debug("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); trident_write_voice_regs(state);}/* prepare channel attributes for recording */static voidtrident_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; pr_debug("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); trident_write_voice_regs(state);}/* get current playback/recording dma buffer pointer (byte offset from LBA), called with spinlock held! */static inline unsignedtrident_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; } pr_debug("trident: trident_get_dma_addr: chip reported channel: %d, " "cso = 0x%04x\n", dmabuf->channel->num, cso); /* 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 voidstop_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 voidstart_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 voidstop_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 voidstart_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/* alloc a DMA buffer of with a buffer of this order */static intalloc_dmabuf(struct dmabuf *dmabuf, struct pci_dev *pci_dev, int order){ void *rawbuf = NULL; struct page *page, *pend; if (!(rawbuf = pci_alloc_consistent(pci_dev, PAGE_SIZE << order, &dmabuf->dma_handle))) return -ENOMEM; pr_debug("trident: allocated %ld (order = %d) bytes at %p\n", PAGE_SIZE << order, order, rawbuf); dmabuf->ready = dmabuf->mapped = 0; dmabuf->rawbuf = rawbuf; dmabuf->buforder = order; /* now mark the pages as reserved; otherwise */ /* remap_pfn_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;}/* allocate the main DMA buffer, playback and recording buffer should be *//* allocated separately */static intalloc_main_dmabuf(struct trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; int order; int ret = -ENOMEM; /* alloc as big a chunk as we can, FIXME: is this necessary ?? */ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { if (!(ret = alloc_dmabuf(dmabuf, state->card->pci_dev, order))) return 0; /* else try again */ } return ret;}/* deallocate a DMA buffer */static voiddealloc_dmabuf(struct dmabuf *dmabuf, struct pci_dev *pci_dev){ 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(pci_dev, PAGE_SIZE << dmabuf->buforder, dmabuf->rawbuf, dmabuf->dma_handle); dmabuf->rawbuf = NULL; } dmabuf->mapped = dmabuf->ready = 0;}static intprog_dmabuf(struct trident_state *state, enum dmabuf_mode rec){ struct dmabuf *dmabuf = &state->dmabuf; unsigned bytepersec; struct trident_state *s = state; unsigned bufsize, dma_nums; unsigned long flags; int ret, i, order; if ((ret = lock_set_fmt(state)) < 0) return ret; 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_main_dmabuf(state))) { unlock_set_fmt(state); return ret; } } else { ret = -ENOMEM; order = state->dmabuf.buforder - 1; if (order >= DMABUF_MINORDER) { ret = alloc_dmabuf(dmabuf, state->card->pci_dev, order); } if (ret) { /* release the main DMA buffer */ dealloc_dmabuf(&state->dmabuf, state->card->pci_dev); /* release the auxiliary DMA buffers */ for (i -= 2; i >= 0; i--) dealloc_dmabuf(&state->other_states[i]->dmabuf, state->card->pci_dev); unlock_set_fmt(state); return ret; } } }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?