aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/trident/trident_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/trident/trident_main.c')
-rw-r--r--sound/pci/trident/trident_main.c3991
1 files changed, 3991 insertions, 0 deletions
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
new file mode 100644
index 00000000000..ccd5ca2ba16
--- /dev/null
+++ b/sound/pci/trident/trident_main.c
@@ -0,0 +1,3991 @@
+/*
+ * Maintained by Jaroslav Kysela <perex@suse.cz>
+ * Originated by audio@tridentmicro.com
+ * Fri Feb 19 15:55:28 MST 1999
+ * Routines for control of Trident 4DWave (DX and NX) chip
+ *
+ * BUGS:
+ *
+ * TODO:
+ * ---
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * SiS7018 S/PDIF support by Thomas Winischhofer <thomas@winischhofer.net>
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/gameport.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/trident.h>
+#include <sound/asoundef.h>
+
+#include <asm/io.h>
+
+static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream);
+static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream);
+static irqreturn_t snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+#ifdef CONFIG_PM
+static int snd_trident_suspend(snd_card_t *card, pm_message_t state);
+static int snd_trident_resume(snd_card_t *card);
+#endif
+static int snd_trident_sis_reset(trident_t *trident);
+
+static void snd_trident_clear_voices(trident_t * trident, unsigned short v_min, unsigned short v_max);
+static int snd_trident_free(trident_t *trident);
+
+/*
+ * common I/O routines
+ */
+
+
+#if 0
+static void snd_trident_print_voice_regs(trident_t *trident, int voice)
+{
+ unsigned int val, tmp;
+
+ printk("Trident voice %i:\n", voice);
+ outb(voice, TRID_REG(trident, T4D_LFO_GC_CIR));
+ val = inl(TRID_REG(trident, CH_LBA));
+ printk("LBA: 0x%x\n", val);
+ val = inl(TRID_REG(trident, CH_GVSEL_PAN_VOL_CTRL_EC));
+ printk("GVSel: %i\n", val >> 31);
+ printk("Pan: 0x%x\n", (val >> 24) & 0x7f);
+ printk("Vol: 0x%x\n", (val >> 16) & 0xff);
+ printk("CTRL: 0x%x\n", (val >> 12) & 0x0f);
+ printk("EC: 0x%x\n", val & 0x0fff);
+ if (trident->device != TRIDENT_DEVICE_ID_NX) {
+ val = inl(TRID_REG(trident, CH_DX_CSO_ALPHA_FMS));
+ printk("CSO: 0x%x\n", val >> 16);
+ printk("Alpha: 0x%x\n", (val >> 4) & 0x0fff);
+ printk("FMS: 0x%x\n", val & 0x0f);
+ val = inl(TRID_REG(trident, CH_DX_ESO_DELTA));
+ printk("ESO: 0x%x\n", val >> 16);
+ printk("Delta: 0x%x\n", val & 0xffff);
+ val = inl(TRID_REG(trident, CH_DX_FMC_RVOL_CVOL));
+ } else { // TRIDENT_DEVICE_ID_NX
+ val = inl(TRID_REG(trident, CH_NX_DELTA_CSO));
+ tmp = (val >> 24) & 0xff;
+ printk("CSO: 0x%x\n", val & 0x00ffffff);
+ val = inl(TRID_REG(trident, CH_NX_DELTA_ESO));
+ tmp |= (val >> 16) & 0xff00;
+ printk("Delta: 0x%x\n", tmp);
+ printk("ESO: 0x%x\n", val & 0x00ffffff);
+ val = inl(TRID_REG(trident, CH_NX_ALPHA_FMS_FMC_RVOL_CVOL));
+ printk("Alpha: 0x%x\n", val >> 20);
+ printk("FMS: 0x%x\n", (val >> 16) & 0x0f);
+ }
+ printk("FMC: 0x%x\n", (val >> 14) & 3);
+ printk("RVol: 0x%x\n", (val >> 7) & 0x7f);
+ printk("CVol: 0x%x\n", val & 0x7f);
+}
+#endif
+
+/*---------------------------------------------------------------------------
+ unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg)
+
+ Description: This routine will do all of the reading from the external
+ CODEC (AC97).
+
+ Parameters: ac97 - ac97 codec structure
+ reg - CODEC register index, from AC97 Hal.
+
+ returns: 16 bit value read from the AC97.
+
+ ---------------------------------------------------------------------------*/
+static unsigned short snd_trident_codec_read(ac97_t *ac97, unsigned short reg)
+{
+ unsigned int data = 0, treg;
+ unsigned short count = 0xffff;
+ unsigned long flags;
+ trident_t *trident = ac97->private_data;
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ if (trident->device == TRIDENT_DEVICE_ID_DX) {
+ data = (DX_AC97_BUSY_READ | (reg & 0x000000ff));
+ outl(data, TRID_REG(trident, DX_ACR1_AC97_R));
+ do {
+ data = inl(TRID_REG(trident, DX_ACR1_AC97_R));
+ if ((data & DX_AC97_BUSY_READ) == 0)
+ break;
+ } while (--count);
+ } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ data = (NX_AC97_BUSY_READ | (reg & 0x000000ff));
+ treg = ac97->num == 0 ? NX_ACR2_AC97_R_PRIMARY : NX_ACR3_AC97_R_SECONDARY;
+ outl(data, TRID_REG(trident, treg));
+ do {
+ data = inl(TRID_REG(trident, treg));
+ if ((data & 0x00000C00) == 0)
+ break;
+ } while (--count);
+ } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ data = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
+ if (ac97->num == 1)
+ data |= SI_AC97_SECONDARY;
+ outl(data, TRID_REG(trident, SI_AC97_READ));
+ do {
+ data = inl(TRID_REG(trident, SI_AC97_READ));
+ if ((data & (SI_AC97_BUSY_READ)) == 0)
+ break;
+ } while (--count);
+ }
+
+ if (count == 0 && !trident->ac97_detect) {
+ snd_printk("ac97 codec read TIMEOUT [0x%x/0x%x]!!!\n", reg, data);
+ data = 0;
+ }
+
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ return ((unsigned short) (data >> 16));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata)
+
+ Description: This routine will do all of the writing to the external
+ CODEC (AC97).
+
+ Parameters: ac97 - ac97 codec structure
+ reg - CODEC register index, from AC97 Hal.
+ data - Lower 16 bits are the data to write to CODEC.
+
+ returns: TRUE if everything went ok, else FALSE.
+
+ ---------------------------------------------------------------------------*/
+static void snd_trident_codec_write(ac97_t *ac97, unsigned short reg, unsigned short wdata)
+{
+ unsigned int address, data;
+ unsigned short count = 0xffff;
+ unsigned long flags;
+ trident_t *trident = ac97->private_data;
+
+ data = ((unsigned long) wdata) << 16;
+
+ spin_lock_irqsave(&trident->reg_lock, flags);
+ if (trident->device == TRIDENT_DEVICE_ID_DX) {
+ address = DX_ACR0_AC97_W;
+
+ /* read AC-97 write register status */
+ do {
+ if ((inw(TRID_REG(trident, address)) & DX_AC97_BUSY_WRITE) == 0)
+ break;
+ } while (--count);
+
+ data |= (DX_AC97_BUSY_WRITE | (reg & 0x000000ff));
+ } else if (trident->device == TRIDENT_DEVICE_ID_NX) {
+ address = NX_ACR1_AC97_W;
+
+ /* read AC-97 write register status */
+ do {
+ if ((inw(TRID_REG(trident, address)) & NX_AC97_BUSY_WRITE) == 0)
+ break;
+ } while (--count);
+
+ data |= (NX_AC97_BUSY_WRITE | (ac97->num << 8) | (reg & 0x000000ff));
+ } else if (trident->device == TRIDENT_DEVICE_ID_SI7018) {
+ address = SI_AC97_WRITE;
+
+ /* read AC-97 write register status */
+ do {
+ if ((inw(TRID_REG(trident, address)) & (SI_AC97_BUSY_WRITE)) == 0)
+ break;
+ } while (--count);
+
+ data |= SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY | (reg & 0x000000ff);
+ if (ac97->num == 1)
+ data |= SI_AC97_SECONDARY;
+ } else {
+ address = 0; /* keep GCC happy */
+ count = 0; /* return */
+ }
+
+ if (count == 0) {
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+ return;
+ }
+ outl(data, TRID_REG(trident, address));
+ spin_unlock_irqrestore(&trident->reg_lock, flags);
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_enable_eso(trident_t *trident)
+
+ Description: This routine will enable end of loop interrupts.
+ End of loop interrupts will occur when a running
+ channel reaches ESO.
+ Also enables middle of loop interrupts.
+
+ Parameters: trident - pointer to target device class for 4DWave.
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_enable_eso(trident_t * trident)
+{
+ unsigned int val;
+
+ val = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+ val |= ENDLP_IE;
+ val |= MIDLP_IE;
+ if (trident->device == TRIDENT_DEVICE_ID_SI7018)
+ val |= BANK_B_EN;
+ outl(val, TRID_REG(trident, T4D_LFO_GC_CIR));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_disable_eso(trident_t *trident)
+
+ Description: This routine will disable end of loop interrupts.
+ End of loop interrupts will occur when a running
+ channel reaches ESO.
+ Also disables middle of loop interrupts.
+
+ Parameters:
+ trident - pointer to target device class for 4DWave.
+
+ returns: TRUE if everything went ok, else FALSE.
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_disable_eso(trident_t * trident)
+{
+ unsigned int tmp;
+
+ tmp = inl(TRID_REG(trident, T4D_LFO_GC_CIR));
+ tmp &= ~ENDLP_IE;
+ tmp &= ~MIDLP_IE;
+ outl(tmp, TRID_REG(trident, T4D_LFO_GC_CIR));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_start_voice(trident_t * trident, unsigned int voice)
+
+ Description: Start a voice, any channel 0 thru 63.
+ This routine automatically handles the fact that there are
+ more than 32 channels available.
+
+ Parameters : voice - Voice number 0 thru n.
+ trident - pointer to target device class for 4DWave.
+
+ Return Value: None.
+
+ ---------------------------------------------------------------------------*/
+
+void snd_trident_start_voice(trident_t * trident, unsigned int voice)
+{
+ unsigned int mask = 1 << (voice & 0x1f);
+ unsigned int reg = (voice & 0x20) ? T4D_START_B : T4D_START_A;
+
+ outl(mask, TRID_REG(trident, reg));
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_stop_voice(trident_t * trident, unsigned int voice)
+
+ Description: Stop a voice, any channel 0 thru 63.
+ This routine automatically handles the fact that there are
+ more than 32 channels available.
+
+ Parameters : voice - Voice number 0 thru n.
+ trident - pointer to target device class for 4DWave.
+
+ Return Value: None.
+
+ ---------------------------------------------------------------------------*/
+
+void snd_trident_stop_voice(trident_t * trident, unsigned int voice)
+{
+ unsigned int mask = 1 << (voice & 0x1f);
+ unsigned int reg = (voice & 0x20) ? T4D_STOP_B : T4D_STOP_A;
+
+ outl(mask, TRID_REG(trident, reg));
+}
+
+/*---------------------------------------------------------------------------
+ int snd_trident_allocate_pcm_channel(trident_t *trident)
+
+ Description: Allocate hardware channel in Bank B (32-63).
+
+ Parameters : trident - pointer to target device class for 4DWave.
+
+ Return Value: hardware channel - 32-63 or -1 when no channel is available
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_pcm_channel(trident_t * trident)
+{
+ int idx;
+
+ if (trident->ChanPCMcnt >= trident->ChanPCM)
+ return -1;
+ for (idx = 31; idx >= 0; idx--) {
+ if (!(trident->ChanMap[T4D_BANK_B] & (1 << idx))) {
+ trident->ChanMap[T4D_BANK_B] |= 1 << idx;
+ trident->ChanPCMcnt++;
+ return idx + 32;
+ }
+ }
+ return -1;
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_free_pcm_channel(int channel)
+
+ Description: Free hardware channel in Bank B (32-63)
+
+ Parameters : trident - pointer to target device class for 4DWave.
+ channel - hardware channel number 0-63
+
+ Return Value: none
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_free_pcm_channel(trident_t *trident, int channel)
+{
+ if (channel < 32 || channel > 63)
+ return;
+ channel &= 0x1f;
+ if (trident->ChanMap[T4D_BANK_B] & (1 << channel)) {
+ trident->ChanMap[T4D_BANK_B] &= ~(1 << channel);
+ trident->ChanPCMcnt--;
+ }
+}
+
+/*---------------------------------------------------------------------------
+ unsigned int snd_trident_allocate_synth_channel(void)
+
+ Description: Allocate hardware channel in Bank A (0-31).
+
+ Parameters : trident - pointer to target device class for 4DWave.
+
+ Return Value: hardware channel - 0-31 or -1 when no channel is available
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_allocate_synth_channel(trident_t * trident)
+{
+ int idx;
+
+ for (idx = 31; idx >= 0; idx--) {
+ if (!(trident->ChanMap[T4D_BANK_A] & (1 << idx))) {
+ trident->ChanMap[T4D_BANK_A] |= 1 << idx;
+ trident->synth.ChanSynthCount++;
+ return idx;
+ }
+ }
+ return -1;
+}
+
+/*---------------------------------------------------------------------------
+ void snd_trident_free_synth_channel( int channel )
+
+ Description: Free hardware channel in Bank B (0-31).
+
+ Parameters : trident - pointer to target device class for 4DWave.
+ channel - hardware channel number 0-63
+
+ Return Value: none
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_free_synth_channel(trident_t *trident, int channel)
+{
+ if (channel < 0 || channel > 31)
+ 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.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ Each register field.
+
+ ---------------------------------------------------------------------------*/
+
+void snd_trident_write_voice_regs(trident_t * trident,
+ snd_trident_voice_t * 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();
+ }
+
+ 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("written %i channel:\n", voice->number);
+ printk(" regs[0] = 0x%x/0x%x\n", regs[0], inl(TRID_REG(trident, CH_START + 0)));
+ printk(" regs[1] = 0x%x/0x%x\n", regs[1], inl(TRID_REG(trident, CH_START + 4)));
+ printk(" regs[2] = 0x%x/0x%x\n", regs[2], inl(TRID_REG(trident, CH_START + 8)));
+ printk(" regs[3] = 0x%x/0x%x\n", regs[3], inl(TRID_REG(trident, CH_START + 12)));
+ printk(" regs[4] = 0x%x/0x%x\n", regs[4], inl(TRID_REG(trident, CH_START + 16)));
+#endif
+}
+
+/*---------------------------------------------------------------------------
+ snd_trident_write_cso_reg
+
+ Description: This routine will write the new CSO offset
+ register to hardware.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ CSO - new CSO value
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_cso_reg(trident_t * trident, snd_trident_voice_t * 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.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ ESO - new ESO value
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_eso_reg(trident_t * trident, snd_trident_voice_t * 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.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ Vol - new voice volume
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_vol_reg(trident_t * trident, snd_trident_voice_t * 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("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.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ Pan - new pan value
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_pan_reg(trident_t * trident, snd_trident_voice_t * 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.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ RVol - new reverb volume
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_rvol_reg(trident_t * trident, snd_trident_voice_t * 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.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ voice - synthesizer voice structure
+ CVol - new chorus volume
+
+ ---------------------------------------------------------------------------*/
+
+static void snd_trident_write_cvol_reg(trident_t * trident, snd_trident_voice_t * 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.
+
+ Paramters: 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.
+
+ Paramters: 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.
+
+ Paramters: 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.
+
+ Paramters: trident - pointer to target device class for 4DWave.
+ substream - PCM substream
+
+ Returns: Control value.
+
+ ---------------------------------------------------------------------------*/
+static unsigned int snd_trident_control_mode(snd_pcm_substream_t *substream)
+{
+ unsigned int CTRL;
+ snd_pcm_runtime_t *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.
+
+ Paramters: substream - PCM substream class
+ cmd - what ioctl message to process
+ arg - additional message infoarg
+
+ Returns: Error status
+
+ ---------------------------------------------------------------------------*/
+
+static int snd_trident_ioctl(snd_pcm_substream_t * 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(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) 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(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *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(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * 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(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *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(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ snd_trident_voice_t *evoice = voice->extra;
+ snd_trident_pcm_mixer_t *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(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * 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(snd_pcm_substream_t * substream)
+{
+ trident_t *trident = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data;
+ unsigned int val, ESO_bytes;
+
+ spin_lock_irq(&trident->reg_lock);
+
+ // Initilize 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 = (unsig