diff options
Diffstat (limited to 'ANDROID_3.4.5/sound/pci/trident/trident_main.c')
-rw-r--r-- | ANDROID_3.4.5/sound/pci/trident/trident_main.c | 3982 |
1 files changed, 0 insertions, 3982 deletions
diff --git a/ANDROID_3.4.5/sound/pci/trident/trident_main.c b/ANDROID_3.4.5/sound/pci/trident/trident_main.c deleted file mode 100644 index 61d3c0e8..00000000 --- a/ANDROID_3.4.5/sound/pci/trident/trident_main.c +++ /dev/null @@ -1,3982 +0,0 @@ -/* - * Maintained by Jaroslav Kysela <perex@perex.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 <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 <linux/dma-mapping.h> -#include <linux/export.h> - -#include <sound/core.h> -#include <sound/info.h> -#include <sound/control.h> -#include <sound/tlv.h> -#include <sound/trident.h> -#include <sound/asoundef.h> - -#include <asm/io.h> - -static int snd_trident_pcm_mixer_build(struct snd_trident *trident, - struct snd_trident_voice * voice, - struct snd_pcm_substream *substream); -static int snd_trident_pcm_mixer_free(struct snd_trident *trident, - struct snd_trident_voice * voice, - struct snd_pcm_substream *substream); -static irqreturn_t snd_trident_interrupt(int irq, void *dev_id); -static int snd_trident_sis_reset(struct snd_trident *trident); - -static void snd_trident_clear_voices(struct snd_trident * trident, - unsigned short v_min, unsigned short v_max); -static int snd_trident_free(struct snd_trident *trident); - -/* - * common I/O routines - */ - - -#if 0 -static void snd_trident_print_voice_regs(struct snd_trident *trident, int voice) -{ - unsigned int val, tmp; - - printk(KERN_DEBUG "Trident voice %i:\n", voice); - outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR)); - val = inl(TRID_REG(trident, CH_LBA)); - printk(KERN_DEBUG "LBA: 0x%x\n", val); - val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); - printk(KERN_DEBUG "GVSel: %i\n", val >> 31); - printk(KERN_DEBUG "Pan: 0x%x\n", (val >> 24) & 0x7f); - printk(KERN_DEBUG "Vol: 0x%x\n", (val >> 16) & 0xff); - printk(KERN_DEBUG "CTRL: 0x%x\n", (val >> 12) & 0x0f); - printk(KERN_DEBUG "EC: 0x%x\n", val & 0x0fff); - if (trident->device != TRIDENT_DEVICE_ID_NX) { - val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS)); - printk(KERN_DEBUG "CSO: 0x%x\n", val >> 16); - printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff); - printk(KERN_DEBUG "FMS: 0x%x\n", val & 0x0f); - val = inl(TRID_REG(trident, CH_DX_ESO_DELTA)); - printk(KERN_DEBUG "ESO: 0x%x\n", val >> 16); - printk(KERN_DEBUG "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(KERN_DEBUG "CSO: 0x%x\n", val & 0x00ffffff); - val = inl(TRID_REG(trident, CH_NX_DELTA_ESO)); - tmp |= (val >> 16) & 0xff00; - printk(KERN_DEBUG "Delta: 0x%x\n", tmp); - printk(KERN_DEBUG "ESO: 0x%x\n", val & 0x00ffffff); - val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL)); - printk(KERN_DEBUG "Alpha: 0x%x\n", val >> 20); - printk(KERN_DEBUG "FMS: 0x%x\n", (val >> 16) & 0x0f); - } - printk(KERN_DEBUG "FMC: 0x%x\n", (val >> 14) & 3); - printk(KERN_DEBUG "RVol: 0x%x\n", (val >> 7) & 0x7f); - printk(KERN_DEBUG "CVol: 0x%x\n", val & 0x7f); -} -#endif - -/*--------------------------------------------------------------------------- - unsigned short snd_trident_codec_read(struct snd_ac97 *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(struct snd_ac97 *ac97, unsigned short reg) -{ - unsigned int data = 0, treg; - unsigned short count = 0xffff; - unsigned long flags; - struct snd_trident *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(struct snd_ac97 *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(struct snd_ac97 *ac97, unsigned short reg, - unsigned short wdata) -{ - unsigned int address, data; - unsigned short count = 0xffff; - unsigned long flags; - struct snd_trident *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(struct snd_trident *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(struct snd_trident * 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(struct snd_trident *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(struct snd_trident * 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(struct snd_trident * 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(struct snd_trident * 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)); -} - -EXPORT_SYMBOL(snd_trident_start_voice); - -/*--------------------------------------------------------------------------- - void snd_trident_stop_voice(struct snd_trident * 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(struct snd_trident * 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)); -} - -EXPORT_SYMBOL(snd_trident_stop_voice); - -/*--------------------------------------------------------------------------- - int snd_trident_allocate_pcm_channel(struct snd_trident *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(struct snd_trident * 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(struct snd_trident *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(struct snd_trident * 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(struct snd_trident *trident, int channel) -{ - if (channel < 0 || channel > 31) - return; - channel &= 0x1f; - if (trident->ChanMap[T4D_BANK_A] & (1 << channel)) { - trident->ChanMap[T4D_BANK_A] &= ~(1 << channel); - trident->synth.ChanSynthCount--; - } -} - -/*--------------------------------------------------------------------------- - snd_trident_write_voice_regs - - Description: This routine will complete and write the 5 hardware channel - registers to hardware. - - Parameters: trident - pointer to target device class for 4DWave. - voice - synthesizer voice structure - Each register field. - - ---------------------------------------------------------------------------*/ - -void snd_trident_write_voice_regs(struct snd_trident * trident, - struct snd_trident_voice * voice) -{ - unsigned int FmcRvolCvol; - unsigned int regs[5]; - - regs[1] = voice->LBA; - regs[4] = (voice->GVSel << 31) | - ((voice->Pan & 0x0000007f) << 24) | - ((voice->CTRL & 0x0000000f) << 12); - FmcRvolCvol = ((voice->FMC & 3) << 14) | - ((voice->RVol & 0x7f) << 7) | - (voice->CVol & 0x7f); - - switch (trident->device) { - case TRIDENT_DEVICE_ID_SI7018: - regs[4] |= voice->number > 31 ? - (voice->Vol & 0x000003ff) : - ((voice->Vol & 0x00003fc) << (16-2)) | - (voice->EC & 0x00000fff); - regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | - (voice->FMS & 0x0000000f); - regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); - regs[3] = (voice->Attribute << 16) | FmcRvolCvol; - break; - case TRIDENT_DEVICE_ID_DX: - regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | - (voice->EC & 0x00000fff); - regs[0] = (voice->CSO << 16) | ((voice->Alpha & 0x00000fff) << 4) | - (voice->FMS & 0x0000000f); - regs[2] = (voice->ESO << 16) | (voice->Delta & 0x0ffff); - regs[3] = FmcRvolCvol; - break; - case TRIDENT_DEVICE_ID_NX: - regs[4] |= ((voice->Vol & 0x000003fc) << (16-2)) | - (voice->EC & 0x00000fff); - regs[0] = (voice->Delta << 24) | (voice->CSO & 0x00ffffff); - regs[2] = ((voice->Delta << 16) & 0xff000000) | - (voice->ESO & 0x00ffffff); - regs[3] = (voice->Alpha << 20) | - ((voice->FMS & 0x0000000f) << 16) | FmcRvolCvol; - break; - default: - snd_BUG(); - return; - } - - outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outl(regs[0], TRID_REG(trident, CH_START + 0)); - outl(regs[1], TRID_REG(trident, CH_START + 4)); - outl(regs[2], TRID_REG(trident, CH_START + 8)); - outl(regs[3], TRID_REG(trident, CH_START + 12)); - outl(regs[4], TRID_REG(trident, CH_START + 16)); - -#if 0 - printk(KERN_DEBUG "written %i channel:\n", voice->number); - printk(KERN_DEBUG " regs[0] = 0x%x/0x%x\n", - regs[0], inl(TRID_REG(trident, CH_START + 0))); - printk(KERN_DEBUG " regs[1] = 0x%x/0x%x\n", - regs[1], inl(TRID_REG(trident, CH_START + 4))); - printk(KERN_DEBUG " regs[2] = 0x%x/0x%x\n", - regs[2], inl(TRID_REG(trident, CH_START + 8))); - printk(KERN_DEBUG " regs[3] = 0x%x/0x%x\n", - regs[3], inl(TRID_REG(trident, CH_START + 12))); - printk(KERN_DEBUG " regs[4] = 0x%x/0x%x\n", - regs[4], inl(TRID_REG(trident, CH_START + 16))); -#endif -} - -EXPORT_SYMBOL(snd_trident_write_voice_regs); - -/*--------------------------------------------------------------------------- - snd_trident_write_cso_reg - - Description: This routine will write the new CSO offset - register to hardware. - - Parameters: trident - pointer to target device class for 4DWave. - voice - synthesizer voice structure - CSO - new CSO value - - ---------------------------------------------------------------------------*/ - -static void snd_trident_write_cso_reg(struct snd_trident * trident, - struct snd_trident_voice * voice, - unsigned int CSO) -{ - voice->CSO = CSO; - outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - if (trident->device != TRIDENT_DEVICE_ID_NX) { - outw(voice->CSO, TRID_REG(trident, CH_DX_CSO_ALPHA_FMS) + 2); - } else { - outl((voice->Delta << 24) | - (voice->CSO & 0x00ffffff), TRID_REG(trident, CH_NX_DELTA_CSO)); - } -} - -/*--------------------------------------------------------------------------- - snd_trident_write_eso_reg - - Description: This routine will write the new ESO offset - register to hardware. - - Parameters: trident - pointer to target device class for 4DWave. - voice - synthesizer voice structure - ESO - new ESO value - - ---------------------------------------------------------------------------*/ - -static void snd_trident_write_eso_reg(struct snd_trident * trident, - struct snd_trident_voice * voice, - unsigned int ESO) -{ - voice->ESO = ESO; - outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - if (trident->device != TRIDENT_DEVICE_ID_NX) { - outw(voice->ESO, TRID_REG(trident, CH_DX_ESO_DELTA) + 2); - } else { - outl(((voice->Delta << 16) & 0xff000000) | (voice->ESO & 0x00ffffff), - TRID_REG(trident, CH_NX_DELTA_ESO)); - } -} - -/*--------------------------------------------------------------------------- - snd_trident_write_vol_reg - - Description: This routine will write the new voice volume - register to hardware. - - Parameters: trident - pointer to target device class for 4DWave. - voice - synthesizer voice structure - Vol - new voice volume - - ---------------------------------------------------------------------------*/ - -static void snd_trident_write_vol_reg(struct snd_trident * trident, - struct snd_trident_voice * voice, - unsigned int Vol) -{ - voice->Vol = Vol; - outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - switch (trident->device) { - case TRIDENT_DEVICE_ID_DX: - case TRIDENT_DEVICE_ID_NX: - outb(voice->Vol >> 2, TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 2)); - break; - case TRIDENT_DEVICE_ID_SI7018: - /* printk(KERN_DEBUG "voice->Vol = 0x%x\n", voice->Vol); */ - outw((voice->CTRL << 12) | voice->Vol, - TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC)); - break; - } -} - -/*--------------------------------------------------------------------------- - snd_trident_write_pan_reg - - Description: This routine will write the new voice pan - register to hardware. - - Parameters: trident - pointer to target device class for 4DWave. - voice - synthesizer voice structure - Pan - new pan value - - ---------------------------------------------------------------------------*/ - -static void snd_trident_write_pan_reg(struct snd_trident * trident, - struct snd_trident_voice * voice, - unsigned int Pan) -{ - voice->Pan = Pan; - outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outb(((voice->GVSel & 0x01) << 7) | (voice->Pan & 0x7f), - TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC + 3)); -} - -/*--------------------------------------------------------------------------- - snd_trident_write_rvol_reg - - Description: This routine will write the new reverb volume - register to hardware. - - Parameters: trident - pointer to target device class for 4DWave. - voice - synthesizer voice structure - RVol - new reverb volume - - ---------------------------------------------------------------------------*/ - -static void snd_trident_write_rvol_reg(struct snd_trident * trident, - struct snd_trident_voice * voice, - unsigned int RVol) -{ - voice->RVol = RVol; - outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | - (voice->CVol & 0x007f), - TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? - CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); -} - -/*--------------------------------------------------------------------------- - snd_trident_write_cvol_reg - - Description: This routine will write the new chorus volume - register to hardware. - - Parameters: trident - pointer to target device class for 4DWave. - voice - synthesizer voice structure - CVol - new chorus volume - - ---------------------------------------------------------------------------*/ - -static void snd_trident_write_cvol_reg(struct snd_trident * trident, - struct snd_trident_voice * voice, - unsigned int CVol) -{ - voice->CVol = CVol; - outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - outw(((voice->FMC & 0x0003) << 14) | ((voice->RVol & 0x007f) << 7) | - (voice->CVol & 0x007f), - TRID_REG(trident, trident->device == TRIDENT_DEVICE_ID_NX ? - CH_NX_ALPHA_FMS_FMC_RVOL_CVOL : CH_DX_FMC_RVOL_CVOL)); -} - -/*--------------------------------------------------------------------------- - snd_trident_convert_rate - - Description: This routine converts rate in HZ to hardware delta value. - - Parameters: trident - pointer to target device class for 4DWave. - rate - Real or Virtual channel number. - - Returns: Delta value. - - ---------------------------------------------------------------------------*/ -static unsigned int snd_trident_convert_rate(unsigned int rate) -{ - unsigned 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) + 24000) / 48000) & 0x0000ffff; - return delta; -} - -/*--------------------------------------------------------------------------- - snd_trident_convert_adc_rate - - Description: This routine converts rate in HZ to hardware delta value. - - Parameters: trident - pointer to target device class for 4DWave. - rate - Real or Virtual channel number. - - Returns: Delta value. - - ---------------------------------------------------------------------------*/ -static unsigned int snd_trident_convert_adc_rate(unsigned int rate) -{ - unsigned 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 = 0x116a; - else if (rate == 8000) - delta = 0x6000; - else if (rate == 48000) - delta = 0x1000; - else - delta = ((48000 << 12) / rate) & 0x0000ffff; - return delta; -} - -/*--------------------------------------------------------------------------- - snd_trident_spurious_threshold - - Description: This routine converts rate in HZ to spurious threshold. - - Parameters: trident - pointer to target device class for 4DWave. - rate - Real or Virtual channel number. - - Returns: Delta value. - - ---------------------------------------------------------------------------*/ -static unsigned int snd_trident_spurious_threshold(unsigned int rate, - unsigned int period_size) -{ - unsigned int res = (rate * period_size) / 48000; - if (res < 64) - res = res / 2; - else - res -= 32; - return res; -} - -/*--------------------------------------------------------------------------- - snd_trident_control_mode - - Description: This routine returns a control mode for a PCM channel. - - Parameters: trident - pointer to target device class for 4DWave. - substream - PCM substream - - Returns: Control value. - - ---------------------------------------------------------------------------*/ -static unsigned int snd_trident_control_mode(struct snd_pcm_substream *substream) -{ - unsigned int CTRL; - struct snd_pcm_runtime *runtime = substream->runtime; - - /* set ctrl mode - CTRL default: 8-bit (unsigned) mono, loop mode enabled - */ - CTRL = 0x00000001; - if (snd_pcm_format_width(runtime->format) == 16) - CTRL |= 0x00000008; // 16-bit data - if (snd_pcm_format_signed(runtime->format)) - CTRL |= 0x00000002; // signed data - if (runtime->channels > 1) - CTRL |= 0x00000004; // stereo data - return CTRL; -} - -/* - * PCM part - */ - -/*--------------------------------------------------------------------------- - snd_trident_ioctl - - Description: Device I/O control handler for playback/capture parameters. - - Parameters: substream - PCM substream class - cmd - what ioctl message to process - arg - additional message infoarg - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_ioctl(struct snd_pcm_substream *substream, - unsigned int cmd, - void *arg) -{ - /* FIXME: it seems that with small periods the behaviour of - trident hardware is unpredictable and interrupt generator - is broken */ - return snd_pcm_lib_ioctl(substream, cmd, arg); -} - -/*--------------------------------------------------------------------------- - snd_trident_allocate_pcm_mem - - Description: Allocate PCM ring buffer for given substream - - Parameters: substream - PCM substream class - hw_params - hardware parameters - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_allocate_pcm_mem(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - int err; - - if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) - return err; - if (trident->tlb.entries) { - if (err > 0) { /* change */ - if (voice->memblk) - snd_trident_free_pages(trident, voice->memblk); - voice->memblk = snd_trident_alloc_pages(trident, substream); - if (voice->memblk == NULL) - return -ENOMEM; - } - } - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_allocate_evoice - - Description: Allocate extra voice as interrupt generator - - Parameters: substream - PCM substream class - hw_params - hardware parameters - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_allocate_evoice(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - struct snd_trident_voice *evoice = voice->extra; - - /* voice management */ - - if (params_buffer_size(hw_params) / 2 != params_period_size(hw_params)) { - if (evoice == NULL) { - evoice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); - if (evoice == NULL) - return -ENOMEM; - voice->extra = evoice; - evoice->substream = substream; - } - } else { - if (evoice != NULL) { - snd_trident_free_voice(trident, evoice); - voice->extra = evoice = NULL; - } - } - - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_hw_params - - Description: Set the hardware parameters for the playback device. - - Parameters: substream - PCM substream class - hw_params - hardware parameters - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int err; - - err = snd_trident_allocate_pcm_mem(substream, hw_params); - if (err >= 0) - err = snd_trident_allocate_evoice(substream, hw_params); - return err; -} - -/*--------------------------------------------------------------------------- - snd_trident_playback_hw_free - - Description: Release the hardware resources for the playback device. - - Parameters: substream - PCM substream class - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - struct snd_trident_voice *evoice = voice ? voice->extra : NULL; - - if (trident->tlb.entries) { - if (voice && voice->memblk) { - snd_trident_free_pages(trident, voice->memblk); - voice->memblk = NULL; - } - } - snd_pcm_lib_free_pages(substream); - if (evoice != NULL) { - snd_trident_free_voice(trident, evoice); - voice->extra = NULL; - } - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_playback_prepare - - Description: Prepare playback device for playback. - - Parameters: substream - PCM substream class - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_playback_prepare(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - struct snd_trident_voice *evoice = voice->extra; - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number]; - - spin_lock_irq(&trident->reg_lock); - - /* set delta (rate) value */ - voice->Delta = snd_trident_convert_rate(runtime->rate); - voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); - - /* set Loop Begin Address */ - if (voice->memblk) - voice->LBA = voice->memblk->offset; - else - voice->LBA = runtime->dma_addr; - - voice->CSO = 0; - voice->ESO = runtime->buffer_size - 1; /* in samples */ - voice->CTRL = snd_trident_control_mode(substream); - voice->FMC = 3; - voice->GVSel = 1; - voice->EC = 0; - voice->Alpha = 0; - voice->FMS = 0; - voice->Vol = mix->vol; - voice->RVol = mix->rvol; - voice->CVol = mix->cvol; - voice->Pan = mix->pan; - voice->Attribute = 0; -#if 0 - voice->Attribute = (1<<(30-16))|(2<<(26-16))| - (0<<(24-16))|(0x1f<<(19-16)); -#else - voice->Attribute = 0; -#endif - - snd_trident_write_voice_regs(trident, voice); - - if (evoice != NULL) { - evoice->Delta = voice->Delta; - evoice->spurious_threshold = voice->spurious_threshold; - evoice->LBA = voice->LBA; - evoice->CSO = 0; - evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */ - evoice->CTRL = voice->CTRL; - evoice->FMC = 3; - evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; - evoice->EC = 0; - evoice->Alpha = 0; - evoice->FMS = 0; - evoice->Vol = 0x3ff; /* mute */ - evoice->RVol = evoice->CVol = 0x7f; /* mute */ - evoice->Pan = 0x7f; /* mute */ -#if 0 - evoice->Attribute = (1<<(30-16))|(2<<(26-16))| - (0<<(24-16))|(0x1f<<(19-16)); -#else - evoice->Attribute = 0; -#endif - snd_trident_write_voice_regs(trident, evoice); - evoice->isync2 = 1; - evoice->isync_mark = runtime->period_size; - evoice->ESO = (runtime->period_size * 2) - 1; - } - - spin_unlock_irq(&trident->reg_lock); - - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_capture_hw_params - - Description: Set the hardware parameters for the capture device. - - Parameters: substream - PCM substream class - hw_params - hardware parameters - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - return snd_trident_allocate_pcm_mem(substream, hw_params); -} - -/*--------------------------------------------------------------------------- - snd_trident_capture_prepare - - Description: Prepare capture device for playback. - - Parameters: substream - PCM substream class - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_capture_prepare(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - unsigned int val, ESO_bytes; - - spin_lock_irq(&trident->reg_lock); - - // Initialize the channel and set channel Mode - outb(0, TRID_REG(trident, LEGACY_DMAR15)); - - // Set DMA channel operation mode register - outb(0x54, TRID_REG(trident, LEGACY_DMAR11)); - - // Set channel buffer Address, DMAR0 expects contiguous PCI memory area - voice->LBA = runtime->dma_addr; - outl(voice->LBA, TRID_REG(trident, LEGACY_DMAR0)); - if (voice->memblk) - voice->LBA = voice->memblk->offset; - - // set ESO - ESO_bytes = snd_pcm_lib_buffer_bytes(substream) - 1; - outb((ESO_bytes & 0x00ff0000) >> 16, TRID_REG(trident, LEGACY_DMAR6)); - outw((ESO_bytes & 0x0000ffff), TRID_REG(trident, LEGACY_DMAR4)); - ESO_bytes++; - - // Set channel sample rate, 4.12 format - val = (((unsigned int) 48000L << 12) + (runtime->rate/2)) / runtime->rate; - outw(val, TRID_REG(trident, T4D_SBDELTA_DELTA_R)); - - // Set channel interrupt blk length - if (snd_pcm_format_width(runtime->format) == 16) { - val = (unsigned short) ((ESO_bytes >> 1) - 1); - } else { - val = (unsigned short) (ESO_bytes - 1); - } - - outl((val << 16) | val, TRID_REG(trident, T4D_SBBL_SBCL)); - - // Right now, set format and start to run captureing, - // continuous run loop enable. - trident->bDMAStart = 0x19; // 0001 1001b - - if (snd_pcm_format_width(runtime->format) == 16) - trident->bDMAStart |= 0x80; - if (snd_pcm_format_signed(runtime->format)) - trident->bDMAStart |= 0x20; - if (runtime->channels > 1) - trident->bDMAStart |= 0x40; - - // Prepare capture intr channel - - voice->Delta = snd_trident_convert_rate(runtime->rate); - voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); - voice->isync = 1; - voice->isync_mark = runtime->period_size; - voice->isync_max = runtime->buffer_size; - - // Set voice parameters - voice->CSO = 0; - voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1; - voice->CTRL = snd_trident_control_mode(substream); - voice->FMC = 3; - voice->RVol = 0x7f; - voice->CVol = 0x7f; - voice->GVSel = 1; - voice->Pan = 0x7f; /* mute */ - voice->Vol = 0x3ff; /* mute */ - voice->EC = 0; - voice->Alpha = 0; - voice->FMS = 0; - voice->Attribute = 0; - - snd_trident_write_voice_regs(trident, voice); - - spin_unlock_irq(&trident->reg_lock); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_si7018_capture_hw_params - - Description: Set the hardware parameters for the capture device. - - Parameters: substream - PCM substream class - hw_params - hardware parameters - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_si7018_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int err; - - if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) - return err; - - return snd_trident_allocate_evoice(substream, hw_params); -} - -/*--------------------------------------------------------------------------- - snd_trident_si7018_capture_hw_free - - Description: Release the hardware resources for the capture device. - - Parameters: substream - PCM substream class - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_si7018_capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - struct snd_trident_voice *evoice = voice ? voice->extra : NULL; - - snd_pcm_lib_free_pages(substream); - if (evoice != NULL) { - snd_trident_free_voice(trident, evoice); - voice->extra = NULL; - } - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_si7018_capture_prepare - - Description: Prepare capture device for playback. - - Parameters: substream - PCM substream class - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_si7018_capture_prepare(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - struct snd_trident_voice *evoice = voice->extra; - - spin_lock_irq(&trident->reg_lock); - - voice->LBA = runtime->dma_addr; - voice->Delta = snd_trident_convert_adc_rate(runtime->rate); - voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); - - // Set voice parameters - voice->CSO = 0; - voice->ESO = runtime->buffer_size - 1; /* in samples */ - voice->CTRL = snd_trident_control_mode(substream); - voice->FMC = 0; - voice->RVol = 0; - voice->CVol = 0; - voice->GVSel = 1; - voice->Pan = T4D_DEFAULT_PCM_PAN; - voice->Vol = 0; - voice->EC = 0; - voice->Alpha = 0; - voice->FMS = 0; - - voice->Attribute = (2 << (30-16)) | - (2 << (26-16)) | - (2 << (24-16)) | - (1 << (23-16)); - - snd_trident_write_voice_regs(trident, voice); - - if (evoice != NULL) { - evoice->Delta = snd_trident_convert_rate(runtime->rate); - evoice->spurious_threshold = voice->spurious_threshold; - evoice->LBA = voice->LBA; - evoice->CSO = 0; - evoice->ESO = (runtime->period_size * 2) + 20 - 1; /* in samples, 20 means correction */ - evoice->CTRL = voice->CTRL; - evoice->FMC = 3; - evoice->GVSel = 0; - evoice->EC = 0; - evoice->Alpha = 0; - evoice->FMS = 0; - evoice->Vol = 0x3ff; /* mute */ - evoice->RVol = evoice->CVol = 0x7f; /* mute */ - evoice->Pan = 0x7f; /* mute */ - evoice->Attribute = 0; - snd_trident_write_voice_regs(trident, evoice); - evoice->isync2 = 1; - evoice->isync_mark = runtime->period_size; - evoice->ESO = (runtime->period_size * 2) - 1; - } - - spin_unlock_irq(&trident->reg_lock); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_foldback_prepare - - Description: Prepare foldback capture device for playback. - - Parameters: substream - PCM substream class - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_foldback_prepare(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - struct snd_trident_voice *evoice = voice->extra; - - spin_lock_irq(&trident->reg_lock); - - /* Set channel buffer Address */ - if (voice->memblk) - voice->LBA = voice->memblk->offset; - else - voice->LBA = runtime->dma_addr; - - /* set target ESO for channel */ - voice->ESO = runtime->buffer_size - 1; /* in samples */ - - /* set sample rate */ - voice->Delta = 0x1000; - voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size); - - voice->CSO = 0; - voice->CTRL = snd_trident_control_mode(substream); - voice->FMC = 3; - voice->RVol = 0x7f; - voice->CVol = 0x7f; - voice->GVSel = 1; - voice->Pan = 0x7f; /* mute */ - voice->Vol = 0x3ff; /* mute */ - voice->EC = 0; - voice->Alpha = 0; - voice->FMS = 0; - voice->Attribute = 0; - - /* set up capture channel */ - outb(((voice->number & 0x3f) | 0x80), TRID_REG(trident, T4D_RCI + voice->foldback_chan)); - - snd_trident_write_voice_regs(trident, voice); - - if (evoice != NULL) { - evoice->Delta = voice->Delta; - evoice->spurious_threshold = voice->spurious_threshold; - evoice->LBA = voice->LBA; - evoice->CSO = 0; - evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */ - evoice->CTRL = voice->CTRL; - evoice->FMC = 3; - evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; - evoice->EC = 0; - evoice->Alpha = 0; - evoice->FMS = 0; - evoice->Vol = 0x3ff; /* mute */ - evoice->RVol = evoice->CVol = 0x7f; /* mute */ - evoice->Pan = 0x7f; /* mute */ - evoice->Attribute = 0; - snd_trident_write_voice_regs(trident, evoice); - evoice->isync2 = 1; - evoice->isync_mark = runtime->period_size; - evoice->ESO = (runtime->period_size * 2) - 1; - } - - spin_unlock_irq(&trident->reg_lock); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_spdif_hw_params - - Description: Set the hardware parameters for the spdif device. - - Parameters: substream - PCM substream class - hw_params - hardware parameters - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_spdif_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - unsigned int old_bits = 0, change = 0; - int err; - - err = snd_trident_allocate_pcm_mem(substream, hw_params); - if (err < 0) - return err; - - if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - err = snd_trident_allocate_evoice(substream, hw_params); - if (err < 0) - return err; - } - - /* prepare SPDIF channel */ - spin_lock_irq(&trident->reg_lock); - old_bits = trident->spdif_pcm_bits; - if (old_bits & IEC958_AES0_PROFESSIONAL) - trident->spdif_pcm_bits &= ~IEC958_AES0_PRO_FS; - else - trident->spdif_pcm_bits &= ~(IEC958_AES3_CON_FS << 24); - if (params_rate(hw_params) >= 48000) { - trident->spdif_pcm_ctrl = 0x3c; // 48000 Hz - trident->spdif_pcm_bits |= - trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? - IEC958_AES0_PRO_FS_48000 : - (IEC958_AES3_CON_FS_48000 << 24); - } - else if (params_rate(hw_params) >= 44100) { - trident->spdif_pcm_ctrl = 0x3e; // 44100 Hz - trident->spdif_pcm_bits |= - trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? - IEC958_AES0_PRO_FS_44100 : - (IEC958_AES3_CON_FS_44100 << 24); - } - else { - trident->spdif_pcm_ctrl = 0x3d; // 32000 Hz - trident->spdif_pcm_bits |= - trident->spdif_bits & IEC958_AES0_PROFESSIONAL ? - IEC958_AES0_PRO_FS_32000 : - (IEC958_AES3_CON_FS_32000 << 24); - } - change = old_bits != trident->spdif_pcm_bits; - spin_unlock_irq(&trident->reg_lock); - - if (change) - snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE, &trident->spdif_pcm_ctl->id); - - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_spdif_prepare - - Description: Prepare SPDIF device for playback. - - Parameters: substream - PCM substream class - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_spdif_prepare(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - struct snd_trident_voice *evoice = voice->extra; - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[substream->number]; - unsigned int RESO, LBAO; - unsigned int temp; - - spin_lock_irq(&trident->reg_lock); - - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { - - /* set delta (rate) value */ - voice->Delta = snd_trident_convert_rate(runtime->rate); - voice->spurious_threshold = snd_trident_spurious_threshold(runtime->rate, runtime->period_size); - - /* set Loop Back Address */ - LBAO = runtime->dma_addr; - if (voice->memblk) - voice->LBA = voice->memblk->offset; - else - voice->LBA = LBAO; - - voice->isync = 1; - voice->isync3 = 1; - voice->isync_mark = runtime->period_size; - voice->isync_max = runtime->buffer_size; - - /* set target ESO for channel */ - RESO = runtime->buffer_size - 1; - voice->ESO = voice->isync_ESO = (runtime->period_size * 2) + 6 - 1; - - /* set ctrl mode */ - voice->CTRL = snd_trident_control_mode(substream); - - voice->FMC = 3; - voice->RVol = 0x7f; - voice->CVol = 0x7f; - voice->GVSel = 1; - voice->Pan = 0x7f; - voice->Vol = 0x3ff; - voice->EC = 0; - voice->CSO = 0; - voice->Alpha = 0; - voice->FMS = 0; - voice->Attribute = 0; - - /* prepare surrogate IRQ channel */ - snd_trident_write_voice_regs(trident, voice); - - outw((RESO & 0xffff), TRID_REG(trident, NX_SPESO)); - outb((RESO >> 16), TRID_REG(trident, NX_SPESO + 2)); - outl((LBAO & 0xfffffffc), TRID_REG(trident, NX_SPLBA)); - outw((voice->CSO & 0xffff), TRID_REG(trident, NX_SPCTRL_SPCSO)); - outb((voice->CSO >> 16), TRID_REG(trident, NX_SPCTRL_SPCSO + 2)); - - /* set SPDIF setting */ - outb(trident->spdif_pcm_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); - outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); - - } else { /* SiS */ - - /* set delta (rate) value */ - voice->Delta = 0x800; - voice->spurious_threshold = snd_trident_spurious_threshold(48000, runtime->period_size); - - /* set Loop Begin Address */ - if (voice->memblk) - voice->LBA = voice->memblk->offset; - else - voice->LBA = runtime->dma_addr; - - voice->CSO = 0; - voice->ESO = runtime->buffer_size - 1; /* in samples */ - voice->CTRL = snd_trident_control_mode(substream); - voice->FMC = 3; - voice->GVSel = 1; - voice->EC = 0; - voice->Alpha = 0; - voice->FMS = 0; - voice->Vol = mix->vol; - voice->RVol = mix->rvol; - voice->CVol = mix->cvol; - voice->Pan = mix->pan; - voice->Attribute = (1<<(30-16))|(7<<(26-16))| - (0<<(24-16))|(0<<(19-16)); - - snd_trident_write_voice_regs(trident, voice); - - if (evoice != NULL) { - evoice->Delta = voice->Delta; - evoice->spurious_threshold = voice->spurious_threshold; - evoice->LBA = voice->LBA; - evoice->CSO = 0; - evoice->ESO = (runtime->period_size * 2) + 4 - 1; /* in samples */ - evoice->CTRL = voice->CTRL; - evoice->FMC = 3; - evoice->GVSel = trident->device == TRIDENT_DEVICE_ID_SI7018 ? 0 : 1; - evoice->EC = 0; - evoice->Alpha = 0; - evoice->FMS = 0; - evoice->Vol = 0x3ff; /* mute */ - evoice->RVol = evoice->CVol = 0x7f; /* mute */ - evoice->Pan = 0x7f; /* mute */ - evoice->Attribute = 0; - snd_trident_write_voice_regs(trident, evoice); - evoice->isync2 = 1; - evoice->isync_mark = runtime->period_size; - evoice->ESO = (runtime->period_size * 2) - 1; - } - - outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS)); - temp = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); - temp &= ~(1<<19); - outl(temp, TRID_REG(trident, T4D_LFO_GC_CIR)); - temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - temp |= SPDIF_EN; - outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - } - - spin_unlock_irq(&trident->reg_lock); - - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_trigger - - Description: Start/stop devices - - Parameters: substream - PCM substream class - cmd - trigger command (STOP, GO) - - Returns: Error status - - ---------------------------------------------------------------------------*/ - -static int snd_trident_trigger(struct snd_pcm_substream *substream, - int cmd) - -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_substream *s; - unsigned int what, whati, capture_flag, spdif_flag; - struct snd_trident_voice *voice, *evoice; - unsigned int val, go; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: - go = 1; - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: - go = 0; - break; - default: - return -EINVAL; - } - what = whati = capture_flag = spdif_flag = 0; - spin_lock(&trident->reg_lock); - val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; - snd_pcm_group_for_each_entry(s, substream) { - if ((struct snd_trident *) snd_pcm_substream_chip(s) == trident) { - voice = s->runtime->private_data; - evoice = voice->extra; - what |= 1 << (voice->number & 0x1f); - if (evoice == NULL) { - whati |= 1 << (voice->number & 0x1f); - } else { - what |= 1 << (evoice->number & 0x1f); - whati |= 1 << (evoice->number & 0x1f); - if (go) - evoice->stimer = val; - } - if (go) { - voice->running = 1; - voice->stimer = val; - } else { - voice->running = 0; - } - snd_pcm_trigger_done(s, substream); - if (voice->capture) - capture_flag = 1; - if (voice->spdif) - spdif_flag = 1; - } - } - if (spdif_flag) { - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { - outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); - val = trident->spdif_pcm_ctrl; - if (!go) - val &= ~(0x28); - outb(val, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); - } else { - outl(trident->spdif_pcm_bits, TRID_REG(trident, SI_SPDIF_CS)); - val = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) | SPDIF_EN; - outl(val, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - } - } - if (!go) - outl(what, TRID_REG(trident, T4D_STOP_B)); - val = inl(TRID_REG(trident, T4D_AINTEN_B)); - if (go) { - val |= whati; - } else { - val &= ~whati; - } - outl(val, TRID_REG(trident, T4D_AINTEN_B)); - if (go) { - outl(what, TRID_REG(trident, T4D_START_B)); - - if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) - outb(trident->bDMAStart, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); - } else { - if (capture_flag && trident->device != TRIDENT_DEVICE_ID_SI7018) - outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); - } - spin_unlock(&trident->reg_lock); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_playback_pointer - - Description: This routine return the playback position - - Parameters: substream - PCM substream class - - Returns: position of buffer - - ---------------------------------------------------------------------------*/ - -static snd_pcm_uframes_t snd_trident_playback_pointer(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - unsigned int cso; - - if (!voice->running) - return 0; - - spin_lock(&trident->reg_lock); - - outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); - - if (trident->device != TRIDENT_DEVICE_ID_NX) { - cso = inw(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS + 2)); - } else { // ID_4DWAVE_NX - cso = (unsigned int) inl(TRID_REG(trident, CH_NX_DELTA_CSO)) & 0x00ffffff; - } - - spin_unlock(&trident->reg_lock); - - if (cso >= runtime->buffer_size) - cso = 0; - - return cso; -} - -/*--------------------------------------------------------------------------- - snd_trident_capture_pointer - - Description: This routine return the capture position - - Parameters: pcm1 - PCM device class - - Returns: position of buffer - - ---------------------------------------------------------------------------*/ - -static snd_pcm_uframes_t snd_trident_capture_pointer(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - unsigned int result; - - if (!voice->running) - return 0; - - result = inw(TRID_REG(trident, T4D_SBBL_SBCL)); - if (runtime->channels > 1) - result >>= 1; - if (result > 0) - result = runtime->buffer_size - result; - - return result; -} - -/*--------------------------------------------------------------------------- - snd_trident_spdif_pointer - - Description: This routine return the SPDIF playback position - - Parameters: substream - PCM substream class - - Returns: position of buffer - - ---------------------------------------------------------------------------*/ - -static snd_pcm_uframes_t snd_trident_spdif_pointer(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - unsigned int result; - - if (!voice->running) - return 0; - - result = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff; - - return result; -} - -/* - * Playback support device description - */ - -static struct snd_pcm_hardware snd_trident_playback = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), - .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, - .rate_min = 4000, - .rate_max = 48000, - .channels_min = 1, - .channels_max = 2, - .buffer_bytes_max = (256*1024), - .period_bytes_min = 64, - .period_bytes_max = (256*1024), - .periods_min = 1, - .periods_max = 1024, - .fifo_size = 0, -}; - -/* - * Capture support device description - */ - -static struct snd_pcm_hardware snd_trident_capture = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), - .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, - .rate_min = 4000, - .rate_max = 48000, - .channels_min = 1, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = 64, - .period_bytes_max = (128*1024), - .periods_min = 1, - .periods_max = 1024, - .fifo_size = 0, -}; - -/* - * Foldback capture support device description - */ - -static struct snd_pcm_hardware snd_trident_foldback = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = 64, - .period_bytes_max = (128*1024), - .periods_min = 1, - .periods_max = 1024, - .fifo_size = 0, -}; - -/* - * SPDIF playback support device description - */ - -static struct snd_pcm_hardware snd_trident_spdif = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000), - .rate_min = 32000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = 64, - .period_bytes_max = (128*1024), - .periods_min = 1, - .periods_max = 1024, - .fifo_size = 0, -}; - -static struct snd_pcm_hardware snd_trident_spdif_7018 = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = 64, - .period_bytes_max = (128*1024), - .periods_min = 1, - .periods_max = 1024, - .fifo_size = 0, -}; - -static void snd_trident_pcm_free_substream(struct snd_pcm_runtime *runtime) -{ - struct snd_trident_voice *voice = runtime->private_data; - struct snd_trident *trident; - - if (voice) { - trident = voice->trident; - snd_trident_free_voice(trident, voice); - } -} - -static int snd_trident_playback_open(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice; - - voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); - if (voice == NULL) - return -EAGAIN; - snd_trident_pcm_mixer_build(trident, voice, substream); - voice->substream = substream; - runtime->private_data = voice; - runtime->private_free = snd_trident_pcm_free_substream; - runtime->hw = snd_trident_playback; - snd_pcm_set_sync(substream); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_playback_close - - Description: This routine will close the 4DWave playback device. For now - we will simply free the dma transfer buffer. - - Parameters: substream - PCM substream class - - ---------------------------------------------------------------------------*/ -static int snd_trident_playback_close(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_trident_voice *voice = runtime->private_data; - - snd_trident_pcm_mixer_free(trident, voice, substream); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_spdif_open - - Description: This routine will open the 4DWave SPDIF device. - - Parameters: substream - PCM substream class - - Returns: status - success or failure flag - - ---------------------------------------------------------------------------*/ - -static int snd_trident_spdif_open(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_trident_voice *voice; - struct snd_pcm_runtime *runtime = substream->runtime; - - voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); - if (voice == NULL) - return -EAGAIN; - voice->spdif = 1; - voice->substream = substream; - spin_lock_irq(&trident->reg_lock); - trident->spdif_pcm_bits = trident->spdif_bits; - spin_unlock_irq(&trident->reg_lock); - - runtime->private_data = voice; - runtime->private_free = snd_trident_pcm_free_substream; - if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - runtime->hw = snd_trident_spdif; - } else { - runtime->hw = snd_trident_spdif_7018; - } - - trident->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | - SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); - - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); - return 0; -} - - -/*--------------------------------------------------------------------------- - snd_trident_spdif_close - - Description: This routine will close the 4DWave SPDIF device. - - Parameters: substream - PCM substream class - - ---------------------------------------------------------------------------*/ - -static int snd_trident_spdif_close(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - unsigned int temp; - - spin_lock_irq(&trident->reg_lock); - // restore default SPDIF setting - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { - outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); - outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); - } else { - outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); - temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - if (trident->spdif_ctrl) { - temp |= SPDIF_EN; - } else { - temp &= ~SPDIF_EN; - } - outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - } - spin_unlock_irq(&trident->reg_lock); - trident->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(trident->card, SNDRV_CTL_EVENT_MASK_VALUE | - SNDRV_CTL_EVENT_MASK_INFO, &trident->spdif_pcm_ctl->id); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_capture_open - - Description: This routine will open the 4DWave capture device. - - Parameters: substream - PCM substream class - - Returns: status - success or failure flag - - ---------------------------------------------------------------------------*/ - -static int snd_trident_capture_open(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_trident_voice *voice; - struct snd_pcm_runtime *runtime = substream->runtime; - - voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); - if (voice == NULL) - return -EAGAIN; - voice->capture = 1; - voice->substream = substream; - runtime->private_data = voice; - runtime->private_free = snd_trident_pcm_free_substream; - runtime->hw = snd_trident_capture; - snd_pcm_set_sync(substream); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_capture_close - - Description: This routine will close the 4DWave capture device. For now - we will simply free the dma transfer buffer. - - Parameters: substream - PCM substream class - - ---------------------------------------------------------------------------*/ -static int snd_trident_capture_close(struct snd_pcm_substream *substream) -{ - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_foldback_open - - Description: This routine will open the 4DWave foldback capture device. - - Parameters: substream - PCM substream class - - Returns: status - success or failure flag - - ---------------------------------------------------------------------------*/ - -static int snd_trident_foldback_open(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_trident_voice *voice; - struct snd_pcm_runtime *runtime = substream->runtime; - - voice = snd_trident_alloc_voice(trident, SNDRV_TRIDENT_VOICE_TYPE_PCM, 0, 0); - if (voice == NULL) - return -EAGAIN; - voice->foldback_chan = substream->number; - voice->substream = substream; - runtime->private_data = voice; - runtime->private_free = snd_trident_pcm_free_substream; - runtime->hw = snd_trident_foldback; - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_foldback_close - - Description: This routine will close the 4DWave foldback capture device. - For now we will simply free the dma transfer buffer. - - Parameters: substream - PCM substream class - - ---------------------------------------------------------------------------*/ -static int snd_trident_foldback_close(struct snd_pcm_substream *substream) -{ - struct snd_trident *trident = snd_pcm_substream_chip(substream); - struct snd_trident_voice *voice; - struct snd_pcm_runtime *runtime = substream->runtime; - voice = runtime->private_data; - - /* stop capture channel */ - spin_lock_irq(&trident->reg_lock); - outb(0x00, TRID_REG(trident, T4D_RCI + voice->foldback_chan)); - spin_unlock_irq(&trident->reg_lock); - return 0; -} - -/*--------------------------------------------------------------------------- - PCM operations - ---------------------------------------------------------------------------*/ - -static struct snd_pcm_ops snd_trident_playback_ops = { - .open = snd_trident_playback_open, - .close = snd_trident_playback_close, - .ioctl = snd_trident_ioctl, - .hw_params = snd_trident_hw_params, - .hw_free = snd_trident_hw_free, - .prepare = snd_trident_playback_prepare, - .trigger = snd_trident_trigger, - .pointer = snd_trident_playback_pointer, -}; - -static struct snd_pcm_ops snd_trident_nx_playback_ops = { - .open = snd_trident_playback_open, - .close = snd_trident_playback_close, - .ioctl = snd_trident_ioctl, - .hw_params = snd_trident_hw_params, - .hw_free = snd_trident_hw_free, - .prepare = snd_trident_playback_prepare, - .trigger = snd_trident_trigger, - .pointer = snd_trident_playback_pointer, - .page = snd_pcm_sgbuf_ops_page, -}; - -static struct snd_pcm_ops snd_trident_capture_ops = { - .open = snd_trident_capture_open, - .close = snd_trident_capture_close, - .ioctl = snd_trident_ioctl, - .hw_params = snd_trident_capture_hw_params, - .hw_free = snd_trident_hw_free, - .prepare = snd_trident_capture_prepare, - .trigger = snd_trident_trigger, - .pointer = snd_trident_capture_pointer, -}; - -static struct snd_pcm_ops snd_trident_si7018_capture_ops = { - .open = snd_trident_capture_open, - .close = snd_trident_capture_close, - .ioctl = snd_trident_ioctl, - .hw_params = snd_trident_si7018_capture_hw_params, - .hw_free = snd_trident_si7018_capture_hw_free, - .prepare = snd_trident_si7018_capture_prepare, - .trigger = snd_trident_trigger, - .pointer = snd_trident_playback_pointer, -}; - -static struct snd_pcm_ops snd_trident_foldback_ops = { - .open = snd_trident_foldback_open, - .close = snd_trident_foldback_close, - .ioctl = snd_trident_ioctl, - .hw_params = snd_trident_hw_params, - .hw_free = snd_trident_hw_free, - .prepare = snd_trident_foldback_prepare, - .trigger = snd_trident_trigger, - .pointer = snd_trident_playback_pointer, -}; - -static struct snd_pcm_ops snd_trident_nx_foldback_ops = { - .open = snd_trident_foldback_open, - .close = snd_trident_foldback_close, - .ioctl = snd_trident_ioctl, - .hw_params = snd_trident_hw_params, - .hw_free = snd_trident_hw_free, - .prepare = snd_trident_foldback_prepare, - .trigger = snd_trident_trigger, - .pointer = snd_trident_playback_pointer, - .page = snd_pcm_sgbuf_ops_page, -}; - -static struct snd_pcm_ops snd_trident_spdif_ops = { - .open = snd_trident_spdif_open, - .close = snd_trident_spdif_close, - .ioctl = snd_trident_ioctl, - .hw_params = snd_trident_spdif_hw_params, - .hw_free = snd_trident_hw_free, - .prepare = snd_trident_spdif_prepare, - .trigger = snd_trident_trigger, - .pointer = snd_trident_spdif_pointer, -}; - -static struct snd_pcm_ops snd_trident_spdif_7018_ops = { - .open = snd_trident_spdif_open, - .close = snd_trident_spdif_close, - .ioctl = snd_trident_ioctl, - .hw_params = snd_trident_spdif_hw_params, - .hw_free = snd_trident_hw_free, - .prepare = snd_trident_spdif_prepare, - .trigger = snd_trident_trigger, - .pointer = snd_trident_playback_pointer, -}; - -/*--------------------------------------------------------------------------- - snd_trident_pcm - - Description: This routine registers the 4DWave device for PCM support. - - Parameters: trident - pointer to target device class for 4DWave. - - Returns: None - - ---------------------------------------------------------------------------*/ - -int __devinit snd_trident_pcm(struct snd_trident * trident, - int device, struct snd_pcm ** rpcm) -{ - struct snd_pcm *pcm; - int err; - - if (rpcm) - *rpcm = NULL; - if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, trident->ChanPCM, 1, &pcm)) < 0) - return err; - - pcm->private_data = trident; - - if (trident->tlb.entries) { - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_nx_playback_ops); - } else { - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_playback_ops); - } - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - trident->device != TRIDENT_DEVICE_ID_SI7018 ? - &snd_trident_capture_ops : - &snd_trident_si7018_capture_ops); - - pcm->info_flags = 0; - pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; - strcpy(pcm->name, "Trident 4DWave"); - trident->pcm = pcm; - - if (trident->tlb.entries) { - struct snd_pcm_substream *substream; - for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) - snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, - snd_dma_pci_data(trident->pci), - 64*1024, 128*1024); - snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, - SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), - 64*1024, 128*1024); - } else { - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(trident->pci), 64*1024, 128*1024); - } - - if (rpcm) - *rpcm = pcm; - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_foldback_pcm - - Description: This routine registers the 4DWave device for foldback PCM support. - - Parameters: trident - pointer to target device class for 4DWave. - - Returns: None - - ---------------------------------------------------------------------------*/ - -int __devinit snd_trident_foldback_pcm(struct snd_trident * trident, - int device, struct snd_pcm ** rpcm) -{ - struct snd_pcm *foldback; - int err; - int num_chan = 3; - struct snd_pcm_substream *substream; - - if (rpcm) - *rpcm = NULL; - if (trident->device == TRIDENT_DEVICE_ID_NX) - num_chan = 4; - if ((err = snd_pcm_new(trident->card, "trident_dx_nx", device, 0, num_chan, &foldback)) < 0) - return err; - - foldback->private_data = trident; - if (trident->tlb.entries) - snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops); - else - snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_foldback_ops); - foldback->info_flags = 0; - strcpy(foldback->name, "Trident 4DWave"); - substream = foldback->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - strcpy(substream->name, "Front Mixer"); - substream = substream->next; - strcpy(substream->name, "Reverb Mixer"); - substream = substream->next; - strcpy(substream->name, "Chorus Mixer"); - if (num_chan == 4) { - substream = substream->next; - strcpy(substream->name, "Second AC'97 ADC"); - } - trident->foldback = foldback; - - if (trident->tlb.entries) - snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV_SG, - snd_dma_pci_data(trident->pci), 0, 128*1024); - else - snd_pcm_lib_preallocate_pages_for_all(foldback, SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(trident->pci), 64*1024, 128*1024); - - if (rpcm) - *rpcm = foldback; - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_spdif - - Description: This routine registers the 4DWave-NX device for SPDIF support. - - Parameters: trident - pointer to target device class for 4DWave-NX. - - Returns: None - - ---------------------------------------------------------------------------*/ - -int __devinit snd_trident_spdif_pcm(struct snd_trident * trident, - int device, struct snd_pcm ** rpcm) -{ - struct snd_pcm *spdif; - int err; - - if (rpcm) - *rpcm = NULL; - if ((err = snd_pcm_new(trident->card, "trident_dx_nx IEC958", device, 1, 0, &spdif)) < 0) - return err; - - spdif->private_data = trident; - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { - snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops); - } else { - snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_7018_ops); - } - spdif->info_flags = 0; - strcpy(spdif->name, "Trident 4DWave IEC958"); - trident->spdif = spdif; - - snd_pcm_lib_preallocate_pages_for_all(spdif, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), 64*1024, 128*1024); - - if (rpcm) - *rpcm = spdif; - return 0; -} - -/* - * Mixer part - */ - - -/*--------------------------------------------------------------------------- - snd_trident_spdif_control - - Description: enable/disable S/PDIF out from ac97 mixer - ---------------------------------------------------------------------------*/ - -#define snd_trident_spdif_control_info snd_ctl_boolean_mono_info - -static int snd_trident_spdif_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - unsigned char val; - - spin_lock_irq(&trident->reg_lock); - val = trident->spdif_ctrl; - ucontrol->value.integer.value[0] = val == kcontrol->private_value; - spin_unlock_irq(&trident->reg_lock); - return 0; -} - -static int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - unsigned char val; - int change; - - val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00; - spin_lock_irq(&trident->reg_lock); - /* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */ - change = trident->spdif_ctrl != val; - trident->spdif_ctrl = val; - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { - if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) { - outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); - outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); - } - } else { - if (trident->spdif == NULL) { - unsigned int temp; - outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); - temp = inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & ~SPDIF_EN; - if (val) - temp |= SPDIF_EN; - outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - } - } - spin_unlock_irq(&trident->reg_lock); - return change; -} - -static struct snd_kcontrol_new snd_trident_spdif_control __devinitdata = -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), - .info = snd_trident_spdif_control_info, - .get = snd_trident_spdif_control_get, - .put = snd_trident_spdif_control_put, - .private_value = 0x28, -}; - -/*--------------------------------------------------------------------------- - snd_trident_spdif_default - - Description: put/get the S/PDIF default settings - ---------------------------------------------------------------------------*/ - -static int snd_trident_spdif_default_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; - uinfo->count = 1; - return 0; -} - -static int snd_trident_spdif_default_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - - spin_lock_irq(&trident->reg_lock); - ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff; - ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff; - ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff; - ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff; - spin_unlock_irq(&trident->reg_lock); - return 0; -} - -static int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - unsigned int val; - int change; - - val = (ucontrol->value.iec958.status[0] << 0) | - (ucontrol->value.iec958.status[1] << 8) | - (ucontrol->value.iec958.status[2] << 16) | - (ucontrol->value.iec958.status[3] << 24); - spin_lock_irq(&trident->reg_lock); - change = trident->spdif_bits != val; - trident->spdif_bits = val; - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { - if ((inb(TRID_REG(trident, NX_SPCTRL_SPCSO + 3)) & 0x10) == 0) - outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); - } else { - if (trident->spdif == NULL) - outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); - } - spin_unlock_irq(&trident->reg_lock); - return change; -} - -static struct snd_kcontrol_new snd_trident_spdif_default __devinitdata = -{ - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), - .info = snd_trident_spdif_default_info, - .get = snd_trident_spdif_default_get, - .put = snd_trident_spdif_default_put -}; - -/*--------------------------------------------------------------------------- - snd_trident_spdif_mask - - Description: put/get the S/PDIF mask - ---------------------------------------------------------------------------*/ - -static int snd_trident_spdif_mask_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; - uinfo->count = 1; - return 0; -} - -static int snd_trident_spdif_mask_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.iec958.status[0] = 0xff; - ucontrol->value.iec958.status[1] = 0xff; - ucontrol->value.iec958.status[2] = 0xff; - ucontrol->value.iec958.status[3] = 0xff; - return 0; -} - -static struct snd_kcontrol_new snd_trident_spdif_mask __devinitdata = -{ - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), - .info = snd_trident_spdif_mask_info, - .get = snd_trident_spdif_mask_get, -}; - -/*--------------------------------------------------------------------------- - snd_trident_spdif_stream - - Description: put/get the S/PDIF stream settings - ---------------------------------------------------------------------------*/ - -static int snd_trident_spdif_stream_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; - uinfo->count = 1; - return 0; -} - -static int snd_trident_spdif_stream_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - - spin_lock_irq(&trident->reg_lock); - ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff; - ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff; - ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff; - ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff; - spin_unlock_irq(&trident->reg_lock); - return 0; -} - -static int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - unsigned int val; - int change; - - val = (ucontrol->value.iec958.status[0] << 0) | - (ucontrol->value.iec958.status[1] << 8) | - (ucontrol->value.iec958.status[2] << 16) | - (ucontrol->value.iec958.status[3] << 24); - spin_lock_irq(&trident->reg_lock); - change = trident->spdif_pcm_bits != val; - trident->spdif_pcm_bits = val; - if (trident->spdif != NULL) { - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { - outl(trident->spdif_pcm_bits, TRID_REG(trident, NX_SPCSTATUS)); - } else { - outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); - } - } - spin_unlock_irq(&trident->reg_lock); - return change; -} - -static struct snd_kcontrol_new snd_trident_spdif_stream __devinitdata = -{ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), - .info = snd_trident_spdif_stream_info, - .get = snd_trident_spdif_stream_get, - .put = snd_trident_spdif_stream_put -}; - -/*--------------------------------------------------------------------------- - snd_trident_ac97_control - - Description: enable/disable rear path for ac97 - ---------------------------------------------------------------------------*/ - -#define snd_trident_ac97_control_info snd_ctl_boolean_mono_info - -static int snd_trident_ac97_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - unsigned char val; - - spin_lock_irq(&trident->reg_lock); - val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0; - spin_unlock_irq(&trident->reg_lock); - return 0; -} - -static int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - unsigned char val; - int change = 0; - - spin_lock_irq(&trident->reg_lock); - val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - val &= ~(1 << kcontrol->private_value); - if (ucontrol->value.integer.value[0]) - val |= 1 << kcontrol->private_value; - change = val != trident->ac97_ctrl; - trident->ac97_ctrl = val; - outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - spin_unlock_irq(&trident->reg_lock); - return change; -} - -static struct snd_kcontrol_new snd_trident_ac97_rear_control __devinitdata = -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Rear Path", - .info = snd_trident_ac97_control_info, - .get = snd_trident_ac97_control_get, - .put = snd_trident_ac97_control_put, - .private_value = 4, -}; - -/*--------------------------------------------------------------------------- - snd_trident_vol_control - - Description: wave & music volume control - ---------------------------------------------------------------------------*/ - -static int snd_trident_vol_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 255; - return 0; -} - -static int snd_trident_vol_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - unsigned int val; - - val = trident->musicvol_wavevol; - ucontrol->value.integer.value[0] = 255 - ((val >> kcontrol->private_value) & 0xff); - ucontrol->value.integer.value[1] = 255 - ((val >> (kcontrol->private_value + 8)) & 0xff); - return 0; -} - -static const DECLARE_TLV_DB_SCALE(db_scale_gvol, -6375, 25, 0); - -static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - unsigned int val; - int change = 0; - - spin_lock_irq(&trident->reg_lock); - val = trident->musicvol_wavevol; - val &= ~(0xffff << kcontrol->private_value); - val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) | - ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value; - change = val != trident->musicvol_wavevol; - outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); - spin_unlock_irq(&trident->reg_lock); - return change; -} - -static struct snd_kcontrol_new snd_trident_vol_music_control __devinitdata = -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Music Playback Volume", - .info = snd_trident_vol_control_info, - .get = snd_trident_vol_control_get, - .put = snd_trident_vol_control_put, - .private_value = 16, - .tlv = { .p = db_scale_gvol }, -}; - -static struct snd_kcontrol_new snd_trident_vol_wave_control __devinitdata = -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Wave Playback Volume", - .info = snd_trident_vol_control_info, - .get = snd_trident_vol_control_get, - .put = snd_trident_vol_control_put, - .private_value = 0, - .tlv = { .p = db_scale_gvol }, -}; - -/*--------------------------------------------------------------------------- - snd_trident_pcm_vol_control - - Description: PCM front volume control - ---------------------------------------------------------------------------*/ - -static int snd_trident_pcm_vol_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 255; - if (trident->device == TRIDENT_DEVICE_ID_SI7018) - uinfo->value.integer.max = 1023; - return 0; -} - -static int snd_trident_pcm_vol_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; - - if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - ucontrol->value.integer.value[0] = 1023 - mix->vol; - } else { - ucontrol->value.integer.value[0] = 255 - (mix->vol>>2); - } - return 0; -} - -static int snd_trident_pcm_vol_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; - unsigned int val; - int change = 0; - - if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - val = 1023 - (ucontrol->value.integer.value[0] & 1023); - } else { - val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2; - } - spin_lock_irq(&trident->reg_lock); - change = val != mix->vol; - mix->vol = val; - if (mix->voice != NULL) - snd_trident_write_vol_reg(trident, mix->voice, val); - spin_unlock_irq(&trident->reg_lock); - return change; -} - -static struct snd_kcontrol_new snd_trident_pcm_vol_control __devinitdata = -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Front Playback Volume", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, - .count = 32, - .info = snd_trident_pcm_vol_control_info, - .get = snd_trident_pcm_vol_control_get, - .put = snd_trident_pcm_vol_control_put, - /* FIXME: no tlv yet */ -}; - -/*--------------------------------------------------------------------------- - snd_trident_pcm_pan_control - - Description: PCM front pan control - ---------------------------------------------------------------------------*/ - -static int snd_trident_pcm_pan_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 127; - return 0; -} - -static int snd_trident_pcm_pan_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; - - ucontrol->value.integer.value[0] = mix->pan; - if (ucontrol->value.integer.value[0] & 0x40) { - ucontrol->value.integer.value[0] = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)); - } else { - ucontrol->value.integer.value[0] |= 0x40; - } - return 0; -} - -static int snd_trident_pcm_pan_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; - unsigned char val; - int change = 0; - - if (ucontrol->value.integer.value[0] & 0x40) - val = ucontrol->value.integer.value[0] & 0x3f; - else - val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40; - spin_lock_irq(&trident->reg_lock); - change = val != mix->pan; - mix->pan = val; - if (mix->voice != NULL) - snd_trident_write_pan_reg(trident, mix->voice, val); - spin_unlock_irq(&trident->reg_lock); - return change; -} - -static struct snd_kcontrol_new snd_trident_pcm_pan_control __devinitdata = -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Pan Playback Control", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, - .count = 32, - .info = snd_trident_pcm_pan_control_info, - .get = snd_trident_pcm_pan_control_get, - .put = snd_trident_pcm_pan_control_put, -}; - -/*--------------------------------------------------------------------------- - snd_trident_pcm_rvol_control - - Description: PCM reverb volume control - ---------------------------------------------------------------------------*/ - -static int snd_trident_pcm_rvol_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 127; - return 0; -} - -static int snd_trident_pcm_rvol_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; - - ucontrol->value.integer.value[0] = 127 - mix->rvol; - return 0; -} - -static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; - unsigned short val; - int change = 0; - - val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); - spin_lock_irq(&trident->reg_lock); - change = val != mix->rvol; - mix->rvol = val; - if (mix->voice != NULL) - snd_trident_write_rvol_reg(trident, mix->voice, val); - spin_unlock_irq(&trident->reg_lock); - return change; -} - -static const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1); - -static struct snd_kcontrol_new snd_trident_pcm_rvol_control __devinitdata = -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Reverb Playback Volume", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, - .count = 32, - .info = snd_trident_pcm_rvol_control_info, - .get = snd_trident_pcm_rvol_control_get, - .put = snd_trident_pcm_rvol_control_put, - .tlv = { .p = db_scale_crvol }, -}; - -/*--------------------------------------------------------------------------- - snd_trident_pcm_cvol_control - - Description: PCM chorus volume control - ---------------------------------------------------------------------------*/ - -static int snd_trident_pcm_cvol_control_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 127; - return 0; -} - -static int snd_trident_pcm_cvol_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; - - ucontrol->value.integer.value[0] = 127 - mix->cvol; - return 0; -} - -static int snd_trident_pcm_cvol_control_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_trident *trident = snd_kcontrol_chip(kcontrol); - struct snd_trident_pcm_mixer *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; - unsigned short val; - int change = 0; - - val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); - spin_lock_irq(&trident->reg_lock); - change = val != mix->cvol; - mix->cvol = val; - if (mix->voice != NULL) - snd_trident_write_cvol_reg(trident, mix->voice, val); - spin_unlock_irq(&trident->reg_lock); - return change; -} - -static struct snd_kcontrol_new snd_trident_pcm_cvol_control __devinitdata = -{ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Chorus Playback Volume", - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, - .count = 32, - .info = snd_trident_pcm_cvol_control_info, - .get = snd_trident_pcm_cvol_control_get, - .put = snd_trident_pcm_cvol_control_put, - .tlv = { .p = db_scale_crvol }, -}; - -static void snd_trident_notify_pcm_change1(struct snd_card *card, - struct snd_kcontrol *kctl, - int num, int activate) -{ - struct snd_ctl_elem_id id; - - if (! kctl) - return; - if (activate) - kctl->vd[num].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - else - kctl->vd[num].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | - SNDRV_CTL_EVENT_MASK_INFO, - snd_ctl_build_ioff(&id, kctl, num)); -} - -static void snd_trident_notify_pcm_change(struct snd_trident *trident, - struct snd_trident_pcm_mixer *tmix, - int num, int activate) -{ - snd_trident_notify_pcm_change1(trident->card, trident->ctl_vol, num, activate); - snd_trident_notify_pcm_change1(trident->card, trident->ctl_pan, num, activate); - snd_trident_notify_pcm_change1(trident->card, trident->ctl_rvol, num, activate); - snd_trident_notify_pcm_change1(trident->card, trident->ctl_cvol, num, activate); -} - -static int snd_trident_pcm_mixer_build(struct snd_trident *trident, - struct snd_trident_voice *voice, - struct snd_pcm_substream *substream) -{ - struct snd_trident_pcm_mixer *tmix; - - if (snd_BUG_ON(!trident || !voice || !substream)) - return -EINVAL; - tmix = &trident->pcm_mixer[substream->number]; - tmix->voice = voice; - tmix->vol = T4D_DEFAULT_PCM_VOL; - tmix->pan = T4D_DEFAULT_PCM_PAN; - tmix->rvol = T4D_DEFAULT_PCM_RVOL; - tmix->cvol = T4D_DEFAULT_PCM_CVOL; - snd_trident_notify_pcm_change(trident, tmix, substream->number, 1); - return 0; -} - -static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_trident_voice *voice, struct snd_pcm_substream *substream) -{ - struct snd_trident_pcm_mixer *tmix; - - if (snd_BUG_ON(!trident || !substream)) - return -EINVAL; - tmix = &trident->pcm_mixer[substream->number]; - tmix->voice = NULL; - snd_trident_notify_pcm_change(trident, tmix, substream->number, 0); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_mixer - - Description: This routine registers the 4DWave device for mixer support. - - Parameters: trident - pointer to target device class for 4DWave. - - Returns: None - - ---------------------------------------------------------------------------*/ - -static int __devinit snd_trident_mixer(struct snd_trident * trident, int pcm_spdif_device) -{ - struct snd_ac97_template _ac97; - struct snd_card *card = trident->card; - struct snd_kcontrol *kctl; - struct snd_ctl_elem_value *uctl; - int idx, err, retries = 2; - static struct snd_ac97_bus_ops ops = { - .write = snd_trident_codec_write, - .read = snd_trident_codec_read, - }; - - uctl = kzalloc(sizeof(*uctl), GFP_KERNEL); - if (!uctl) - return -ENOMEM; - - if ((err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus)) < 0) - goto __out; - - memset(&_ac97, 0, sizeof(_ac97)); - _ac97.private_data = trident; - trident->ac97_detect = 1; - - __again: - if ((err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97)) < 0) { - if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - if ((err = snd_trident_sis_reset(trident)) < 0) - goto __out; - if (retries-- > 0) - goto __again; - err = -EIO; - } - goto __out; - } - - /* secondary codec? */ - if (trident->device == TRIDENT_DEVICE_ID_SI7018 && - (inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) { - _ac97.num = 1; - err = snd_ac97_mixer(trident->ac97_bus, &_ac97, &trident->ac97_sec); - if (err < 0) - snd_printk(KERN_ERR "SI7018: the secondary codec - invalid access\n"); -#if 0 // only for my testing purpose --jk - { - struct snd_ac97 *mc97; - err = snd_ac97_modem(trident->card, &_ac97, &mc97); - if (err < 0) - snd_printk(KERN_ERR "snd_ac97_modem returned error %i\n", err); - } -#endif - } - - trident->ac97_detect = 0; - - if (trident->device != TRIDENT_DEVICE_ID_SI7018) { - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_wave_control, trident))) < 0) - goto __out; - kctl->put(kctl, uctl); - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_vol_music_control, trident))) < 0) - goto __out; - kctl->put(kctl, uctl); - outl(trident->musicvol_wavevol = 0x00000000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); - } else { - outl(trident->musicvol_wavevol = 0xffff0000, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); - } - - for (idx = 0; idx < 32; idx++) { - struct snd_trident_pcm_mixer *tmix; - - tmix = &trident->pcm_mixer[idx]; - tmix->voice = NULL; - } - if ((trident->ctl_vol = snd_ctl_new1(&snd_trident_pcm_vol_control, trident)) == NULL) - goto __nomem; - if ((err = snd_ctl_add(card, trident->ctl_vol))) - goto __out; - - if ((trident->ctl_pan = snd_ctl_new1(&snd_trident_pcm_pan_control, trident)) == NULL) - goto __nomem; - if ((err = snd_ctl_add(card, trident->ctl_pan))) - goto __out; - - if ((trident->ctl_rvol = snd_ctl_new1(&snd_trident_pcm_rvol_control, trident)) == NULL) - goto __nomem; - if ((err = snd_ctl_add(card, trident->ctl_rvol))) - goto __out; - - if ((trident->ctl_cvol = snd_ctl_new1(&snd_trident_pcm_cvol_control, trident)) == NULL) - goto __nomem; - if ((err = snd_ctl_add(card, trident->ctl_cvol))) - goto __out; - - if (trident->device == TRIDENT_DEVICE_ID_NX) { - if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_trident_ac97_rear_control, trident))) < 0) - goto __out; - kctl->put(kctl, uctl); - } - if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) { - - kctl = snd_ctl_new1(&snd_trident_spdif_control, trident); - if (kctl == NULL) { - err = -ENOMEM; - goto __out; - } - if (trident->ac97->ext_id & AC97_EI_SPDIF) - kctl->id.index++; - if (trident->ac97_sec && (trident->ac97_sec->ext_id & AC97_EI_SPDIF)) - kctl->id.index++; - idx = kctl->id.index; - if ((err = snd_ctl_add(card, kctl)) < 0) - goto __out; - kctl->put(kctl, uctl); - - kctl = snd_ctl_new1(&snd_trident_spdif_default, trident); - if (kctl == NULL) { - err = -ENOMEM; - goto __out; - } - kctl->id.index = idx; - kctl->id.device = pcm_spdif_device; - if ((err = snd_ctl_add(card, kctl)) < 0) - goto __out; - - kctl = snd_ctl_new1(&snd_trident_spdif_mask, trident); - if (kctl == NULL) { - err = -ENOMEM; - goto __out; - } - kctl->id.index = idx; - kctl->id.device = pcm_spdif_device; - if ((err = snd_ctl_add(card, kctl)) < 0) - goto __out; - - kctl = snd_ctl_new1(&snd_trident_spdif_stream, trident); - if (kctl == NULL) { - err = -ENOMEM; - goto __out; - } - kctl->id.index = idx; - kctl->id.device = pcm_spdif_device; - if ((err = snd_ctl_add(card, kctl)) < 0) - goto __out; - trident->spdif_pcm_ctl = kctl; - } - - err = 0; - goto __out; - - __nomem: - err = -ENOMEM; - - __out: - kfree(uctl); - - return err; -} - -/* - * gameport interface - */ - -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) - -static unsigned char snd_trident_gameport_read(struct gameport *gameport) -{ - struct snd_trident *chip = gameport_get_port_data(gameport); - - if (snd_BUG_ON(!chip)) - return 0; - return inb(TRID_REG(chip, GAMEPORT_LEGACY)); -} - -static void snd_trident_gameport_trigger(struct gameport *gameport) -{ - struct snd_trident *chip = gameport_get_port_data(gameport); - - if (snd_BUG_ON(!chip)) - return; - outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY)); -} - -static int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons) -{ - struct snd_trident *chip = gameport_get_port_data(gameport); - int i; - - if (snd_BUG_ON(!chip)) - return 0; - - *buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf; - - for (i = 0; i < 4; i++) { - axes[i] = inw(TRID_REG(chip, GAMEPORT_AXES + i * 2)); - if (axes[i] == 0xffff) axes[i] = -1; - } - - return 0; -} - -static int snd_trident_gameport_open(struct gameport *gameport, int mode) -{ - struct snd_trident *chip = gameport_get_port_data(gameport); - - if (snd_BUG_ON(!chip)) - return 0; - - switch (mode) { - case GAMEPORT_MODE_COOKED: - outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR)); - msleep(20); - return 0; - case GAMEPORT_MODE_RAW: - outb(0, TRID_REG(chip, GAMEPORT_GCR)); - return 0; - default: - return -1; - } -} - -int __devinit snd_trident_create_gameport(struct snd_trident *chip) -{ - struct gameport *gp; - - chip->gameport = gp = gameport_allocate_port(); - if (!gp) { - printk(KERN_ERR "trident: cannot allocate memory for gameport\n"); - return -ENOMEM; - } - - gameport_set_name(gp, "Trident 4DWave"); - gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); - gameport_set_dev_parent(gp, &chip->pci->dev); - - gameport_set_port_data(gp, chip); - gp->fuzz = 64; - gp->read = snd_trident_gameport_read; - gp->trigger = snd_trident_gameport_trigger; - gp->cooked_read = snd_trident_gameport_cooked_read; - gp->open = snd_trident_gameport_open; - - gameport_register_port(gp); - - return 0; -} - -static inline void snd_trident_free_gameport(struct snd_trident *chip) -{ - if (chip->gameport) { - gameport_unregister_port(chip->gameport); - chip->gameport = NULL; - } -} -#else -int __devinit snd_trident_create_gameport(struct snd_trident *chip) { return -ENOSYS; } -static inline void snd_trident_free_gameport(struct snd_trident *chip) { } -#endif /* CONFIG_GAMEPORT */ - -/* - * delay for 1 tick - */ -static inline void do_delay(struct snd_trident *chip) -{ - schedule_timeout_uninterruptible(1); -} - -/* - * SiS reset routine - */ - -static int snd_trident_sis_reset(struct snd_trident *trident) -{ - unsigned long end_time; - unsigned int i; - int r; - - r = trident->in_suspend ? 0 : 2; /* count of retries */ - __si7018_retry: - pci_write_config_byte(trident->pci, 0x46, 0x04); /* SOFTWARE RESET */ - udelay(100); - pci_write_config_byte(trident->pci, 0x46, 0x00); - udelay(100); - /* disable AC97 GPIO interrupt */ - outb(0x00, TRID_REG(trident, SI_AC97_GPIO)); - /* initialize serial interface, force cold reset */ - i = PCMOUT|SURROUT|CENTEROUT|LFEOUT|SECONDARY_ID|COLD_RESET; - outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - udelay(1000); - /* remove cold reset */ - i &= ~COLD_RESET; - outl(i, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - udelay(2000); - /* wait, until the codec is ready */ - end_time = (jiffies + (HZ * 3) / 4) + 1; - do { - if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_PRIMARY_READY) != 0) - goto __si7018_ok; - do_delay(trident); - } while (time_after_eq(end_time, jiffies)); - snd_printk(KERN_ERR "AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL))); - if (r-- > 0) { - end_time = jiffies + HZ; - do { - do_delay(trident); - } while (time_after_eq(end_time, jiffies)); - goto __si7018_retry; - } - __si7018_ok: - /* wait for the second codec */ - do { - if ((inl(TRID_REG(trident, SI_SERIAL_INTF_CTRL)) & SI_AC97_SECONDARY_READY) != 0) - break; - do_delay(trident); - } while (time_after_eq(end_time, jiffies)); - /* enable 64 channel mode */ - outl(BANK_B_EN, TRID_REG(trident, T4D_LFO_GC_CIR)); - return 0; -} - -/* - * /proc interface - */ - -static void snd_trident_proc_read(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - struct snd_trident *trident = entry->private_data; - char *s; - - switch (trident->device) { - case TRIDENT_DEVICE_ID_SI7018: - s = "SiS 7018 Audio"; - break; - case TRIDENT_DEVICE_ID_DX: - s = "Trident 4DWave PCI DX"; - break; - case TRIDENT_DEVICE_ID_NX: - s = "Trident 4DWave PCI NX"; - break; - default: - s = "???"; - } - snd_iprintf(buffer, "%s\n\n", s); - snd_iprintf(buffer, "Spurious IRQs : %d\n", trident->spurious_irq_count); - snd_iprintf(buffer, "Spurious IRQ dlta: %d\n", trident->spurious_irq_max_delta); - if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) - snd_iprintf(buffer, "IEC958 Mixer Out : %s\n", trident->spdif_ctrl == 0x28 ? "on" : "off"); - if (trident->device == TRIDENT_DEVICE_ID_NX) { - snd_iprintf(buffer, "Rear Speakers : %s\n", trident->ac97_ctrl & 0x00000010 ? "on" : "off"); - if (trident->tlb.entries) { - snd_iprintf(buffer,"\nVirtual Memory\n"); - snd_iprintf(buffer, "Memory Maximum : %d\n", trident->tlb.memhdr->size); - snd_iprintf(buffer, "Memory Used : %d\n", trident->tlb.memhdr->used); - snd_iprintf(buffer, "Memory Free : %d\n", snd_util_mem_avail(trident->tlb.memhdr)); - } - } -} - -static void __devinit snd_trident_proc_init(struct snd_trident * trident) -{ - struct snd_info_entry *entry; - const char *s = "trident"; - - if (trident->device == TRIDENT_DEVICE_ID_SI7018) - s = "sis7018"; - if (! snd_card_proc_new(trident->card, s, &entry)) - snd_info_set_text_ops(entry, trident, snd_trident_proc_read); -} - -static int snd_trident_dev_free(struct snd_device *device) -{ - struct snd_trident *trident = device->device_data; - return snd_trident_free(trident); -} - -/*--------------------------------------------------------------------------- - snd_trident_tlb_alloc - - Description: Allocate and set up the TLB page table on 4D NX. - Each entry has 4 bytes (physical PCI address). - - Parameters: trident - pointer to target device class for 4DWave. - - Returns: 0 or negative error code - - ---------------------------------------------------------------------------*/ - -static int __devinit snd_trident_tlb_alloc(struct snd_trident *trident) -{ - int i; - - /* TLB array must be aligned to 16kB !!! so we allocate - 32kB region and correct offset when necessary */ - - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), - 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) { - snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n"); - return -ENOMEM; - } - trident->tlb.entries = (unsigned int*)ALIGN((unsigned long)trident->tlb.buffer.area, SNDRV_TRIDENT_MAX_PAGES * 4); - trident->tlb.entries_dmaaddr = ALIGN(trident->tlb.buffer.addr, SNDRV_TRIDENT_MAX_PAGES * 4); - /* allocate shadow TLB page table (virtual addresses) */ - trident->tlb.shadow_entries = vmalloc(SNDRV_TRIDENT_MAX_PAGES*sizeof(unsigned long)); - if (trident->tlb.shadow_entries == NULL) { - snd_printk(KERN_ERR "trident: unable to allocate shadow TLB entries\n"); - return -ENOMEM; - } - /* allocate and setup silent page and initialise TLB entries */ - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), - SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) { - snd_printk(KERN_ERR "trident: unable to allocate silent page\n"); - return -ENOMEM; - } - memset(trident->tlb.silent_page.area, 0, SNDRV_TRIDENT_PAGE_SIZE); - for (i = 0; i < SNDRV_TRIDENT_MAX_PAGES; i++) { - trident->tlb.entries[i] = cpu_to_le32(trident->tlb.silent_page.addr & ~(SNDRV_TRIDENT_PAGE_SIZE-1)); - trident->tlb.shadow_entries[i] = (unsigned long)trident->tlb.silent_page.area; - } - - /* use emu memory block manager code to manage tlb page allocation */ - trident->tlb.memhdr = snd_util_memhdr_new(SNDRV_TRIDENT_PAGE_SIZE * SNDRV_TRIDENT_MAX_PAGES); - if (trident->tlb.memhdr == NULL) - return -ENOMEM; - - trident->tlb.memhdr->block_extra_size = sizeof(struct snd_trident_memblk_arg); - return 0; -} - -/* - * initialize 4D DX chip - */ - -static void snd_trident_stop_all_voices(struct snd_trident *trident) -{ - outl(0xffffffff, TRID_REG(trident, T4D_STOP_A)); - outl(0xffffffff, TRID_REG(trident, T4D_STOP_B)); - outl(0, TRID_REG(trident, T4D_AINTEN_A)); - outl(0, TRID_REG(trident, T4D_AINTEN_B)); -} - -static int snd_trident_4d_dx_init(struct snd_trident *trident) -{ - struct pci_dev *pci = trident->pci; - unsigned long end_time; - - /* reset the legacy configuration and whole audio/wavetable block */ - pci_write_config_dword(pci, 0x40, 0); /* DDMA */ - pci_write_config_byte(pci, 0x44, 0); /* ports */ - pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ - pci_write_config_byte(pci, 0x46, 4); /* reset */ - udelay(100); - pci_write_config_byte(pci, 0x46, 0); /* release reset */ - udelay(100); - - /* warm reset of the AC'97 codec */ - outl(0x00000001, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - udelay(100); - outl(0x00000000, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - /* DAC on, disable SB IRQ and try to force ADC valid signal */ - trident->ac97_ctrl = 0x0000004a; - outl(trident->ac97_ctrl, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - /* wait, until the codec is ready */ - end_time = (jiffies + (HZ * 3) / 4) + 1; - do { - if ((inl(TRID_REG(trident, DX_ACR2_AC97_COM_STAT)) & 0x0010) != 0) - goto __dx_ok; - do_delay(trident); - } while (time_after_eq(end_time, jiffies)); - snd_printk(KERN_ERR "AC'97 codec ready error\n"); - return -EIO; - - __dx_ok: - snd_trident_stop_all_voices(trident); - - return 0; -} - -/* - * initialize 4D NX chip - */ -static int snd_trident_4d_nx_init(struct snd_trident *trident) -{ - struct pci_dev *pci = trident->pci; - unsigned long end_time; - - /* reset the legacy configuration and whole audio/wavetable block */ - pci_write_config_dword(pci, 0x40, 0); /* DDMA */ - pci_write_config_byte(pci, 0x44, 0); /* ports */ - pci_write_config_byte(pci, 0x45, 0); /* Legacy DMA */ - - pci_write_config_byte(pci, 0x46, 1); /* reset */ - udelay(100); - pci_write_config_byte(pci, 0x46, 0); /* release reset */ - udelay(100); - - /* warm reset of the AC'97 codec */ - outl(0x00000001, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - udelay(100); - outl(0x00000000, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - /* wait, until the codec is ready */ - end_time = (jiffies + (HZ * 3) / 4) + 1; - do { - if ((inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)) & 0x0008) != 0) - goto __nx_ok; - do_delay(trident); - } while (time_after_eq(end_time, jiffies)); - snd_printk(KERN_ERR "AC'97 codec ready error [0x%x]\n", inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT))); - return -EIO; - - __nx_ok: - /* DAC on */ - trident->ac97_ctrl = 0x00000002; - outl(trident->ac97_ctrl, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - /* disable SB IRQ */ - outl(NX_SB_IRQ_DISABLE, TRID_REG(trident, T4D_MISCINT)); - - snd_trident_stop_all_voices(trident); - - if (trident->tlb.entries != NULL) { - unsigned int i; - /* enable virtual addressing via TLB */ - i = trident->tlb.entries_dmaaddr; - i |= 0x00000001; - outl(i, TRID_REG(trident, NX_TLBC)); - } else { - outl(0, TRID_REG(trident, NX_TLBC)); - } - /* initialize S/PDIF */ - outl(trident->spdif_bits, TRID_REG(trident, NX_SPCSTATUS)); - outb(trident->spdif_ctrl, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); - - return 0; -} - -/* - * initialize sis7018 chip - */ -static int snd_trident_sis_init(struct snd_trident *trident) -{ - int err; - - if ((err = snd_trident_sis_reset(trident)) < 0) - return err; - - snd_trident_stop_all_voices(trident); - - /* initialize S/PDIF */ - outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); - - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_create - - Description: This routine will create the device specific class for - the 4DWave card. It will also perform basic initialization. - - Parameters: card - which card to create - pci - interface to PCI bus resource info - dma1ptr - playback dma buffer - dma2ptr - capture dma buffer - irqptr - interrupt resource info - - Returns: 4DWave device class private data - - ---------------------------------------------------------------------------*/ - -int __devinit snd_trident_create(struct snd_card *card, - struct pci_dev *pci, - int pcm_streams, - int pcm_spdif_device, - int max_wavetable_size, - struct snd_trident ** rtrident) -{ - struct snd_trident *trident; - int i, err; - struct snd_trident_voice *voice; - struct snd_trident_pcm_mixer *tmix; - static struct snd_device_ops ops = { - .dev_free = snd_trident_dev_free, - }; - - *rtrident = NULL; - - /* enable PCI device */ - if ((err = pci_enable_device(pci)) < 0) - return err; - /* check, if we can restrict PCI DMA transfers to 30 bits */ - if (pci_set_dma_mask(pci, DMA_BIT_MASK(30)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(30)) < 0) { - snd_printk(KERN_ERR "architecture does not support 30bit PCI busmaster DMA\n"); - pci_disable_device(pci); - return -ENXIO; - } - - trident = kzalloc(sizeof(*trident), GFP_KERNEL); - if (trident == NULL) { - pci_disable_device(pci); - return -ENOMEM; - } - trident->device = (pci->vendor << 16) | pci->device; - trident->card = card; - trident->pci = pci; - spin_lock_init(&trident->reg_lock); - spin_lock_init(&trident->event_lock); - spin_lock_init(&trident->voice_alloc); - if (pcm_streams < 1) - pcm_streams = 1; - if (pcm_streams > 32) - pcm_streams = 32; - trident->ChanPCM = pcm_streams; - if (max_wavetable_size < 0 ) - max_wavetable_size = 0; - trident->synth.max_size = max_wavetable_size * 1024; - trident->irq = -1; - - trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE); - pci_set_master(pci); - - if ((err = pci_request_regions(pci, "Trident Audio")) < 0) { - kfree(trident); - pci_disable_device(pci); - return err; - } - trident->port = pci_resource_start(pci, 0); - - if (request_irq(pci->irq, snd_trident_interrupt, IRQF_SHARED, - KBUILD_MODNAME, trident)) { - snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); - snd_trident_free(trident); - return -EBUSY; - } - trident->irq = pci->irq; - - /* allocate 16k-aligned TLB for NX cards */ - trident->tlb.entries = NULL; - trident->tlb.buffer.area = NULL; - if (trident->device == TRIDENT_DEVICE_ID_NX) { - if ((err = snd_trident_tlb_alloc(trident)) < 0) { - snd_trident_free(trident); - return err; - } - } - - trident->spdif_bits = trident->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; - - /* initialize chip */ - switch (trident->device) { - case TRIDENT_DEVICE_ID_DX: - err = snd_trident_4d_dx_init(trident); - break; - case TRIDENT_DEVICE_ID_NX: - err = snd_trident_4d_nx_init(trident); - break; - case TRIDENT_DEVICE_ID_SI7018: - err = snd_trident_sis_init(trident); - break; - default: - snd_BUG(); - break; - } - if (err < 0) { - snd_trident_free(trident); - return err; - } - - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, trident, &ops)) < 0) { - snd_trident_free(trident); - return err; - } - - if ((err = snd_trident_mixer(trident, pcm_spdif_device)) < 0) - return err; - - /* initialise synth voices */ - for (i = 0; i < 64; i++) { - voice = &trident->synth.voices[i]; - voice->number = i; - voice->trident = trident; - } - /* initialize pcm mixer entries */ - for (i = 0; i < 32; i++) { - tmix = &trident->pcm_mixer[i]; - tmix->vol = T4D_DEFAULT_PCM_VOL; - tmix->pan = T4D_DEFAULT_PCM_PAN; - tmix->rvol = T4D_DEFAULT_PCM_RVOL; - tmix->cvol = T4D_DEFAULT_PCM_CVOL; - } - - snd_trident_enable_eso(trident); - - snd_trident_proc_init(trident); - snd_card_set_dev(card, &pci->dev); - *rtrident = trident; - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_free - - Description: This routine will free the device specific class for - the 4DWave card. - - Parameters: trident - device specific private data for 4DWave card - - Returns: None. - - ---------------------------------------------------------------------------*/ - -static int snd_trident_free(struct snd_trident *trident) -{ - snd_trident_free_gameport(trident); - snd_trident_disable_eso(trident); - // Disable S/PDIF out - if (trident->device == TRIDENT_DEVICE_ID_NX) - outb(0x00, TRID_REG(trident, NX_SPCTRL_SPCSO + 3)); - else if (trident->device == TRIDENT_DEVICE_ID_SI7018) { - outl(0, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - } - if (trident->irq >= 0) - free_irq(trident->irq, trident); - if (trident->tlb.buffer.area) { - outl(0, TRID_REG(trident, NX_TLBC)); - if (trident->tlb.memhdr) - snd_util_memhdr_free(trident->tlb.memhdr); - if (trident->tlb.silent_page.area) - snd_dma_free_pages(&trident->tlb.silent_page); - vfree(trident->tlb.shadow_entries); - snd_dma_free_pages(&trident->tlb.buffer); - } - pci_release_regions(trident->pci); - pci_disable_device(trident->pci); - kfree(trident); - return 0; -} - -/*--------------------------------------------------------------------------- - snd_trident_interrupt - - Description: ISR for Trident 4DWave device - - Parameters: trident - device specific private data for 4DWave card - - Problems: It seems that Trident chips generates interrupts more than - one time in special cases. The spurious interrupts are - detected via sample timer (T4D_STIMER) and computing - corresponding delta value. The limits are detected with - the method try & fail so it is possible that it won't - work on all computers. [jaroslav] - - Returns: None. - - ---------------------------------------------------------------------------*/ - -static irqreturn_t snd_trident_interrupt(int irq, void *dev_id) -{ - struct snd_trident *trident = dev_id; - unsigned int audio_int, chn_int, stimer, channel, mask, tmp; - int delta; - struct snd_trident_voice *voice; - - audio_int = inl(TRID_REG(trident, T4D_MISCINT)); - if ((audio_int & (ADDRESS_IRQ|MPU401_IRQ)) == 0) - return IRQ_NONE; - if (audio_int & ADDRESS_IRQ) { - // get interrupt status for all channels - spin_lock(&trident->reg_lock); - stimer = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; - chn_int = inl(TRID_REG(trident, T4D_AINT_A)); - if (chn_int == 0) - goto __skip1; - outl(chn_int, TRID_REG(trident, T4D_AINT_A)); /* ack */ - __skip1: - chn_int = inl(TRID_REG(trident, T4D_AINT_B)); - if (chn_int == 0) - goto __skip2; - for (channel = 63; channel >= 32; channel--) { - mask = 1 << (channel&0x1f); - if ((chn_int & mask) == 0) - continue; - voice = &trident->synth.voices[channel]; - if (!voice->pcm || voice->substream == NULL) { - outl(mask, TRID_REG(trident, T4D_STOP_B)); - continue; - } - delta = (int)stimer - (int)voice->stimer; - if (delta < 0) - delta = -delta; - if ((unsigned int)delta < voice->spurious_threshold) { - /* do some statistics here */ - trident->spurious_irq_count++; - if (trident->spurious_irq_max_delta < (unsigned int)delta) - trident->spurious_irq_max_delta = delta; - continue; - } - voice->stimer = stimer; - if (voice->isync) { - if (!voice->isync3) { - tmp = inw(TRID_REG(trident, T4D_SBBL_SBCL)); - if (trident->bDMAStart & 0x40) - tmp >>= 1; - if (tmp > 0) - tmp = voice->isync_max - tmp; - } else { - tmp = inl(TRID_REG(trident, NX_SPCTRL_SPCSO)) & 0x00ffffff; - } - if (tmp < voice->isync_mark) { - if (tmp > 0x10) - tmp = voice->isync_ESO - 7; - else - tmp = voice->isync_ESO + 2; - /* update ESO for IRQ voice to preserve sync */ - snd_trident_stop_voice(trident, voice->number); - snd_trident_write_eso_reg(trident, voice, tmp); - snd_trident_start_voice(trident, voice->number); - } - } else if (voice->isync2) { - voice->isync2 = 0; - /* write original ESO and update CSO for IRQ voice to preserve sync */ - snd_trident_stop_voice(trident, voice->number); - snd_trident_write_cso_reg(trident, voice, voice->isync_mark); - snd_trident_write_eso_reg(trident, voice, voice->ESO); - snd_trident_start_voice(trident, voice->number); - } -#if 0 - if (voice->extra) { - /* update CSO for extra voice to preserve sync */ - snd_trident_stop_voice(trident, voice->extra->number); - snd_trident_write_cso_reg(trident, voice->extra, 0); - snd_trident_start_voice(trident, voice->extra->number); - } -#endif - spin_unlock(&trident->reg_lock); - snd_pcm_period_elapsed(voice->substream); - spin_lock(&trident->reg_lock); - } - outl(chn_int, TRID_REG(trident, T4D_AINT_B)); /* ack */ - __skip2: - spin_unlock(&trident->reg_lock); - } - if (audio_int & MPU401_IRQ) { - if (trident->rmidi) { - snd_mpu401_uart_interrupt(irq, trident->rmidi->private_data); - } else { - inb(TRID_REG(trident, T4D_MPUR0)); - } - } - // outl((ST_TARGET_REACHED | MIXER_OVERFLOW | MIXER_UNDERFLOW), TRID_REG(trident, T4D_MISCINT)); - return IRQ_HANDLED; -} - -struct snd_trident_voice *snd_trident_alloc_voice(struct snd_trident * trident, int type, int client, int port) -{ - struct snd_trident_voice *pvoice; - unsigned long flags; - int idx; - - spin_lock_irqsave(&trident->voice_alloc, flags); - if (type == SNDRV_TRIDENT_VOICE_TYPE_PCM) { - idx = snd_trident_allocate_pcm_channel(trident); - if(idx < 0) { - spin_unlock_irqrestore(&trident->voice_alloc, flags); - return NULL; - } - pvoice = &trident->synth.voices[idx]; - pvoice->use = 1; - pvoice->pcm = 1; - pvoice->capture = 0; - pvoice->spdif = 0; - pvoice->memblk = NULL; - pvoice->substream = NULL; - spin_unlock_irqrestore(&trident->voice_alloc, flags); - return pvoice; - } - if (type == SNDRV_TRIDENT_VOICE_TYPE_SYNTH) { - idx = snd_trident_allocate_synth_channel(trident); - if(idx < 0) { - spin_unlock_irqrestore(&trident->voice_alloc, flags); - return NULL; - } - pvoice = &trident->synth.voices[idx]; - pvoice->use = 1; - pvoice->synth = 1; - pvoice->client = client; - pvoice->port = port; - pvoice->memblk = NULL; - spin_unlock_irqrestore(&trident->voice_alloc, flags); - return pvoice; - } - if (type == SNDRV_TRIDENT_VOICE_TYPE_MIDI) { - } - spin_unlock_irqrestore(&trident->voice_alloc, flags); - return NULL; -} - -EXPORT_SYMBOL(snd_trident_alloc_voice); - -void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice) -{ - unsigned long flags; - void (*private_free)(struct snd_trident_voice *); - void *private_data; - - if (voice == NULL || !voice->use) - return; - snd_trident_clear_voices(trident, voice->number, voice->number); - spin_lock_irqsave(&trident->voice_alloc, flags); - private_free = voice->private_free; - private_data = voice->private_data; - voice->private_free = NULL; - voice->private_data = NULL; - if (voice->pcm) - snd_trident_free_pcm_channel(trident, voice->number); - if (voice->synth) - snd_trident_free_synth_channel(trident, voice->number); - voice->use = voice->pcm = voice->synth = voice->midi = 0; - voice->capture = voice->spdif = 0; - voice->sample_ops = NULL; - voice->substream = NULL; - voice->extra = NULL; - spin_unlock_irqrestore(&trident->voice_alloc, flags); - if (private_free) - private_free(voice); -} - -EXPORT_SYMBOL(snd_trident_free_voice); - -static void snd_trident_clear_voices(struct snd_trident * trident, unsigned short v_min, unsigned short v_max) -{ - unsigned int i, val, mask[2] = { 0, 0 }; - - if (snd_BUG_ON(v_min > 63 || v_max > 63)) - return; - for (i = v_min; i <= v_max; i++) - mask[i >> 5] |= 1 << (i & 0x1f); - if (mask[0]) { - outl(mask[0], TRID_REG(trident, T4D_STOP_A)); - val = inl(TRID_REG(trident, T4D_AINTEN_A)); - outl(val & ~mask[0], TRID_REG(trident, T4D_AINTEN_A)); - } - if (mask[1]) { - outl(mask[1], TRID_REG(trident, T4D_STOP_B)); - val = inl(TRID_REG(trident, T4D_AINTEN_B)); - outl(val & ~mask[1], TRID_REG(trident, T4D_AINTEN_B)); - } -} - -#ifdef CONFIG_PM -int snd_trident_suspend(struct pci_dev *pci, pm_message_t state) -{ - struct snd_card *card = pci_get_drvdata(pci); - struct snd_trident *trident = card->private_data; - - trident->in_suspend = 1; - snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(trident->pcm); - snd_pcm_suspend_all(trident->foldback); - snd_pcm_suspend_all(trident->spdif); - - snd_ac97_suspend(trident->ac97); - snd_ac97_suspend(trident->ac97_sec); - - pci_disable_device(pci); - pci_save_state(pci); - pci_set_power_state(pci, pci_choose_state(pci, state)); - return 0; -} - -int snd_trident_resume(struct pci_dev *pci) -{ - struct snd_card *card = pci_get_drvdata(pci); - struct snd_trident *trident = card->private_data; - - pci_set_power_state(pci, PCI_D0); - pci_restore_state(pci); - if (pci_enable_device(pci) < 0) { - printk(KERN_ERR "trident: pci_enable_device failed, " - "disabling device\n"); - snd_card_disconnect(card); - return -EIO; - } - pci_set_master(pci); - - switch (trident->device) { - case TRIDENT_DEVICE_ID_DX: - snd_trident_4d_dx_init(trident); - break; - case TRIDENT_DEVICE_ID_NX: - snd_trident_4d_nx_init(trident); - break; - case TRIDENT_DEVICE_ID_SI7018: - snd_trident_sis_init(trident); - break; - } - - snd_ac97_resume(trident->ac97); - snd_ac97_resume(trident->ac97_sec); - - /* restore some registers */ - outl(trident->musicvol_wavevol, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); - - snd_trident_enable_eso(trident); - - snd_power_change_state(card, SNDRV_CTL_POWER_D0); - trident->in_suspend = 0; - return 0; -} -#endif /* CONFIG_PM */ |