📄 trident.c
字号:
static void trident_disable_voice_irq(struct trident_card * card, unsigned int channel){ unsigned int mask = 1 << (channel & 0x1f); struct trident_pcm_bank *bank = &card->banks[channel >> 5]; u32 reg, addr = bank->addresses->aint_en; reg = inl(TRID_REG(card, addr)); reg &= ~mask; outl(reg, TRID_REG(card, addr)); /* Ack the channel in case the interrupt was set before we disable it. */ outl(mask, TRID_REG(card, bank->addresses->aint));#ifdef DEBUG reg = inl(TRID_REG(card, T4D_AINTEN_B)); printk("trident: disabled IRQ on channel %d, AINTEN_B = 0x%08x\n", channel, reg);#endif}static void trident_start_voice(struct trident_card * card, unsigned int channel){ unsigned int mask = 1 << (channel & 0x1f); struct trident_pcm_bank *bank = &card->banks[channel >> 5]; u32 addr = bank->addresses->start;#ifdef DEBUG u32 reg;#endif outl(mask, TRID_REG(card, addr));#ifdef DEBUG reg = inl(TRID_REG(card, T4D_START_B)); printk("trident: start voice on channel %d, START_B = 0x%08x\n", channel, reg);#endif}static void trident_stop_voice(struct trident_card * card, unsigned int channel){ unsigned int mask = 1 << (channel & 0x1f); struct trident_pcm_bank *bank = &card->banks[channel >> 5]; u32 addr = bank->addresses->stop;#ifdef DEBUG u32 reg;#endif outl(mask, TRID_REG(card, addr));#ifdef DEBUG reg = inl(TRID_REG(card, T4D_STOP_B)); printk("trident: stop voice on channel %d, STOP_B = 0x%08x\n", channel, reg);#endif}static u32 trident_get_interrupt_mask (struct trident_card * card, unsigned int channel){ struct trident_pcm_bank *bank = &card->banks[channel]; u32 addr = bank->addresses->aint; return inl(TRID_REG(card, addr));}static int trident_check_channel_interrupt(struct trident_card * card, unsigned int channel){ unsigned int mask = 1 << (channel & 0x1f); u32 reg = trident_get_interrupt_mask (card, channel >> 5);#ifdef DEBUG if (reg & mask) printk("trident: channel %d has interrupt, AINT_B = 0x%08x\n", channel, reg);#endif return (reg & mask) ? TRUE : FALSE;}static void trident_ack_channel_interrupt(struct trident_card * card, unsigned int channel){ unsigned int mask = 1 << (channel & 0x1f); struct trident_pcm_bank *bank = &card->banks[channel >> 5]; u32 reg, addr = bank->addresses->aint; reg = inl(TRID_REG(card, addr)); reg &= mask; outl(reg, TRID_REG(card, addr));#ifdef DEBUG reg = inl(TRID_REG(card, T4D_AINT_B)); printk("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", channel, reg);#endif}static struct trident_channel * trident_alloc_pcm_channel(struct trident_card *card){ struct trident_pcm_bank *bank; int idx; bank = &card->banks[BANK_B]; for (idx = 31; idx >= 0; idx--) { if (!(bank->bitmap & (1 << idx))) { struct trident_channel *channel = &bank->channels[idx]; bank->bitmap |= 1 << idx; channel->num = idx + 32; return channel; } } /* no more free channels avaliable */ printk(KERN_ERR "trident: no more channels available on Bank B.\n"); return NULL;}static void trident_free_pcm_channel(struct trident_card *card, unsigned int channel){ int bank; if (channel < 31 || channel > 63) return; bank = channel >> 5; channel = channel & 0x1f; card->banks[bank].bitmap &= ~(1 << (channel));}/* called with spin lock held */static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel){ int i; if (channel > 63) return FALSE; /* select hardware channel to write */ outb(channel, TRID_REG(card, T4D_LFO_GC_CIR)); /* Output the channel registers, but don't write register three to an ALI chip. */ for (i = 0; i < CHANNEL_REGS; i++) { if (i == 3 && card->pci_id == PCI_DEVICE_ID_ALI_5451) continue; outl(data[i], TRID_REG(card, CHANNEL_START + 4*i)); } return TRUE;}/* called with spin lock held */static int trident_write_voice_regs(struct trident_state *state){ unsigned int data[CHANNEL_REGS + 1]; struct trident_channel *channel; channel = state->dmabuf.channel; data[1] = channel->lba; data[4] = channel->control; switch (state->card->pci_id) { case PCI_DEVICE_ID_ALI_5451: data[0] = 0; /* Current Sample Offset */ data[2] = (channel->eso << 16) | (channel->delta & 0xffff); data[3] = 0; break; case PCI_DEVICE_ID_SI_7018: data[0] = 0; /* Current Sample Offset */ data[2] = (channel->eso << 16) | (channel->delta & 0xffff); data[3] = (channel->attribute << 16) | (channel->fm_vol & 0xffff); break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: data[0] = 0; /* Current Sample Offset */ data[2] = (channel->eso << 16) | (channel->delta & 0xffff); data[3] = channel->fm_vol & 0xffff; break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: data[0] = (channel->delta << 24); data[2] = ((channel->delta << 16) & 0xff000000) | (channel->eso & 0x00ffffff); data[3] = channel->fm_vol & 0xffff; break; default: return FALSE; } return trident_load_channel_registers(state->card, data, channel->num);}static int compute_rate_play(u32 rate){ int delta; /* We special case 44100 and 8000 since rounding with the equation does not give us an accurate enough value. For 11025 and 22050 the equation gives us the best answer. All other frequencies will also use the equation. JDW */ if (rate == 44100) delta = 0xeb3; else if (rate == 8000) delta = 0x2ab; else if (rate == 48000) delta = 0x1000; else delta = (((rate << 12) + rate) / 48000) & 0x0000ffff; return delta;}static int compute_rate_rec(u32 rate){ 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 int trident_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);#ifdef DEBUG printk("trident: called trident_set_dac_rate : rate = %d\n", rate);#endif return rate;}/* set recording sample rate */static unsigned int trident_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);#ifdef DEBUG printk("trident: called trident_set_adc_rate : rate = %d\n", rate);#endif return rate;}/* prepare channel attributes for playback */ static void trident_play_setup(struct trident_state *state){ struct dmabuf *dmabuf = &state->dmabuf; struct trident_channel *channel = dmabuf->channel; channel->lba = virt_to_bus(dmabuf->rawbuf); 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;#ifdef DEBUG printk("trident: trident_play_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);}/* prepare channel attributes for recording */static void trident_rec_setup(struct trident_state *state){ u16 w; 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; default: return; } channel->lba = virt_to_bus(dmabuf->rawbuf); 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 != 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! */extern __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: /* 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) */extern __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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -