📄 trident_main.c
字号:
/* * Maintained by Jaroslav Kysela <perex@suse.cz> * Originated by audio@tridentmicro.com * Fri Feb 19 15:55:28 MST 1999 * Routines for control of Trident 4DWave (DX and NX) chip * * BUGS: * * TODO: * --- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net> */#include <sound/driver.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/gameport.h>#include <sound/core.h>#include <sound/info.h>#include <sound/control.h>#include <sound/trident.h>#include <sound/asoundef.h>#include <asm/io.h>static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream);static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream);static irqreturn_t snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs);#ifdef CONFIG_PMstatic int snd_trident_suspend(snd_card_t *card, pm_message_t state);static int snd_trident_resume(snd_card_t *card);#endifstatic int snd_trident_sis_reset(trident_t *trident);static void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max);static int snd_trident_free(trident_t *trident);/* * common I/O routines */#if 0static void snd_trident_print_voice_regs(trident_t *trident, int voice){ unsigned int val, tmp; printk("Trident voice %i:\n", voice); outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR)); val = inl(TRID_REG(trident, CH_LBA)); printk("LBA: 0x%x\n", val); val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); printk("GVSel: %i\n", val >> 31); printk("Pan: 0x%x\n", (val >> 24) & 0x7f); printk("Vol: 0x%x\n", (val >> 16) & 0xff); printk("CTRL: 0x%x\n", (val >> 12) & 0x0f); printk("EC: 0x%x\n", val & 0x0fff); if (trident->device != TRIDENT_DEVICE_ID_NX) { val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS)); printk("CSO: 0x%x\n", val >> 16); printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff); printk("FMS: 0x%x\n", val & 0x0f); val = inl(TRID_REG(trident, CH_DX_ESO_DELTA)); printk("ESO: 0x%x\n", val >> 16); printk("Delta: 0x%x\n", val & 0xffff); val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL)); } else { // TRIDENT_DEVICE_ID_NX val = inl(TRID_REG(trident, CH_NX_DELTA_CSO)); tmp = (val >> 24) & 0xff; printk("CSO: 0x%x\n", val & 0x00ffffff); val = inl(TRID_REG(trident, CH_NX_DELTA_ESO)); tmp |= (val >> 16) & 0xff00; printk("Delta: 0x%x\n", tmp); printk("ESO: 0x%x\n", val & 0x00ffffff); val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL)); printk("Alpha: 0x%x\n", val >> 20); printk("FMS: 0x%x\n", (val >> 16) & 0x0f); } printk("FMC: 0x%x\n", (val >> 14) & 3); printk("RVol: 0x%x\n", (val >> 7) & 0x7f); printk("CVol: 0x%x\n", val & 0x7f);}#endif/*--------------------------------------------------------------------------- unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg) Description: This routine will do all of the reading from the external CODEC (AC97). Parameters: ac97 - ac97 codec structure reg - CODEC register index, from AC97 Hal. returns: 16 bit value read from the AC97. ---------------------------------------------------------------------------*/static unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg){ unsigned int data = 0, treg; unsigned short count = 0xffff; unsigned long flags; trident_t *trident = ac97->private_data; spin_lock_irqsave(&trident->reg_lock, flags); if (trident->device == TRIDENT_DEVICE_ID_DX) { data = (DX_AC97_BUSY_READ | (reg & 0x000000ff)); outl(data, TRID_REG(trident, DX_ACR1_AC97_R)); do { data = inl(TRID_REG(trident, DX_ACR1_AC97_R)); if ((data & DX_AC97_BUSY_READ) == 0) break; } while (--count); } else if (trident->device == TRIDENT_DEVICE_ID_NX) { data = (NX_AC97_BUSY_READ | (reg & 0x000000ff)); treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY; outl(data, TRID_REG(trident, treg)); do { data = inl(TRID_REG(trident, treg)); if ((data & 0x00000C00) == 0) break; } while (--count); } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); if (ac97->num == 1) data |= SI_AC97_SECONDARY; outl(data, TRID_REG(trident, SI_AC97_READ)); do { data = inl(TRID_REG(trident, SI_AC97_READ)); if ((data & (SI_AC97_BUSY_READ)) == 0) break; } while (--count); } if (count == 0 && !trident->ac97_detect) { snd_printk(KERN_ERR "ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n", reg, data); data = 0; } spin_unlock_irqrestore(&trident->reg_lock, flags); return ((unsigned short) (data >> 16));}/*--------------------------------------------------------------------------- void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata) Description: This routine will do all of the writing to the external CODEC (AC97). Parameters: ac97 - ac97 codec structure reg - CODEC register index, from AC97 Hal. data - Lower 16 bits are the data to write to CODEC. returns: TRUE if everything went ok, else FALSE. ---------------------------------------------------------------------------*/static void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata){ unsigned int address, data; unsigned short count = 0xffff; unsigned long flags; trident_t *trident = ac97->private_data; data = ((unsigned long) wdata) << 16; spin_lock_irqsave(&trident->reg_lock, flags); if (trident->device == TRIDENT_DEVICE_ID_DX) { address = DX_ACR0_AC97_W; /* read AC-97 write register status */ do { if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0) break; } while (--count); data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff)); } else if (trident->device == TRIDENT_DEVICE_ID_NX) { address = NX_ACR1_AC97_W; /* read AC-97 write register status */ do { if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0) break; } while (--count); data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff)); } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { address = SI_AC97_WRITE; /* read AC-97 write register status */ do { if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0) break; } while (--count); data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff); if (ac97->num == 1) data |= SI_AC97_SECONDARY; } else { address = 0; /* keep GCC happy */ count = 0; /* return */ } if (count == 0) { spin_unlock_irqrestore(&trident->reg_lock, flags); return; } outl(data, TRID_REG(trident, address)); spin_unlock_irqrestore(&trident->reg_lock, flags);}/*--------------------------------------------------------------------------- void snd_trident_enable_eso(trident_t *trident) Description: This routine will enable end of loop interrupts. End of loop interrupts will occur when a running channel reaches ESO. Also enables middle of loop interrupts. Parameters: trident - pointer to target device class for 4DWave. ---------------------------------------------------------------------------*/static void snd_trident_enable_eso(trident_t * trident){ unsigned int val; val = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); val |= ENDLP_IE; val |= MIDLP_IE; if (trident->device == TRIDENT_DEVICE_ID_SI7018) val |= BANK_B_EN; outl(val, TRID_REG(trident, T4D_LFO_GC_CIR));}/*--------------------------------------------------------------------------- void snd_trident_disable_eso(trident_t *trident) Description: This routine will disable end of loop interrupts. End of loop interrupts will occur when a running channel reaches ESO. Also disables middle of loop interrupts. Parameters: trident - pointer to target device class for 4DWave. returns: TRUE if everything went ok, else FALSE. ---------------------------------------------------------------------------*/static void snd_trident_disable_eso(trident_t * trident){ unsigned int tmp; tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); tmp &= ~ENDLP_IE; tmp &= ~MIDLP_IE; outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR));}/*--------------------------------------------------------------------------- void snd_trident_start_voice(trident_t * trident, unsigned int voice) Description: Start a voice, any channel 0 thru 63. This routine automatically handles the fact that there are more than 32 channels available. Parameters : voice - Voice number 0 thru n. trident - pointer to target device class for 4DWave. Return Value: None. ---------------------------------------------------------------------------*/void snd_trident_start_voice(trident_t * trident, unsigned int voice){ unsigned int mask = 1 << (voice & 0x1f); unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A; outl(mask, TRID_REG(trident, reg));}/*--------------------------------------------------------------------------- void snd_trident_stop_voice(trident_t * trident, unsigned int voice) Description: Stop a voice, any channel 0 thru 63. This routine automatically handles the fact that there are more than 32 channels available. Parameters : voice - Voice number 0 thru n. trident - pointer to target device class for 4DWave. Return Value: None. ---------------------------------------------------------------------------*/void snd_trident_stop_voice(trident_t * trident, unsigned int voice){ unsigned int mask = 1 << (voice & 0x1f); unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A; outl(mask, TRID_REG(trident, reg));}/*--------------------------------------------------------------------------- int snd_trident_allocate_pcm_channel(trident_t *trident) Description: Allocate hardware channel in Bank B (32-63). Parameters : trident - pointer to target device class for 4DWave. Return Value: hardware channel - 32-63 or -1 when no channel is available ---------------------------------------------------------------------------*/static int snd_trident_allocate_pcm_channel(trident_t * trident){ int idx; if (trident->ChanPCMcnt >= trident->ChanPCM) return -1; for (idx = 31; idx >= 0; idx--) { if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) { trident->ChanMap[T4D_BANK_B] |= 1 << idx; trident->ChanPCMcnt++; return idx + 32; } } return -1;}/*--------------------------------------------------------------------------- void snd_trident_free_pcm_channel(int channel) Description: Free hardware channel in Bank B (32-63) Parameters : trident - pointer to target device class for 4DWave. channel - hardware channel number 0-63 Return Value: none ---------------------------------------------------------------------------*/static void snd_trident_free_pcm_channel(trident_t *trident, int channel){ if (channel < 32 || channel > 63) return; channel &= 0x1f; if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) { trident->ChanMap[T4D_BANK_B] &= ~(1 << channel); trident->ChanPCMcnt--; }}/*--------------------------------------------------------------------------- unsigned int snd_trident_allocate_synth_channel(void) Description: Allocate hardware channel in Bank A (0-31). Parameters : trident - pointer to target device class for 4DWave. Return Value: hardware channel - 0-31 or -1 when no channel is available ---------------------------------------------------------------------------*/static int snd_trident_allocate_synth_channel(trident_t * trident){ int idx; for (idx = 31; idx >= 0; idx--) { if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) { trident->ChanMap[T4D_BANK_A] |= 1 << idx; trident->synth.ChanSynthCount++; return idx; } } return -1;}/*--------------------------------------------------------------------------- void snd_trident_free_synth_channel( int channel ) Description: Free hardware channel in Bank B (0-31). Parameters : trident - pointer to target device class for 4DWave. channel - hardware channel number 0-63 Return Value: none ---------------------------------------------------------------------------*/static void snd_trident_free_synth_channel(trident_t *trident, int channel){ if (channel < 0 || channel > 31)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -