diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/pci/trident |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/pci/trident')
-rw-r--r-- | sound/pci/trident/Makefile | 19 | ||||
-rw-r--r-- | sound/pci/trident/trident.c | 196 | ||||
-rw-r--r-- | sound/pci/trident/trident_main.c | 3991 | ||||
-rw-r--r-- | sound/pci/trident/trident_memory.c | 476 | ||||
-rw-r--r-- | sound/pci/trident/trident_synth.c | 1031 |
5 files changed, 5713 insertions, 0 deletions
diff --git a/sound/pci/trident/Makefile b/sound/pci/trident/Makefile new file mode 100644 index 00000000000..65bc5b70323 --- /dev/null +++ b/sound/pci/trident/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for ALSA +# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> +# + +snd-trident-objs := trident.o trident_main.o trident_memory.o +snd-trident-synth-objs := trident_synth.o + +# +# this function returns: +# "m" - CONFIG_SND_SEQUENCER is m +# <empty string> - CONFIG_SND_SEQUENCER is undefined +# otherwise parameter #1 value +# +sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) + +# Toplevel Module Dependency +obj-$(CONFIG_SND_TRIDENT) += snd-trident.o +obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-trident-synth.o diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c new file mode 100644 index 00000000000..ad58e08d66e --- /dev/null +++ b/sound/pci/trident/trident.c @@ -0,0 +1,196 @@ +/* + * Driver for Trident 4DWave DX/NX & SiS SI7018 Audio PCI soundcard + * + * Driver was originated by Trident <audio@tridentmicro.com> + * Fri Feb 19 15:55:28 MST 1999 + * + * + * 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 + * + */ + +#include <sound/driver.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/time.h> +#include <linux/moduleparam.h> +#include <sound/core.h> +#include <sound/trident.h> +#include <sound/initval.h> + +MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, <audio@tridentmicro.com>"); +MODULE_DESCRIPTION("Trident 4D-WaveDX/NX & SiS SI7018"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Trident,4DWave DX}," + "{Trident,4DWave NX}," + "{SiS,SI7018 PCI Audio}," + "{Best Union,Miss Melody 4DWave PCI}," + "{HIS,4DWave PCI}," + "{Warpspeed,ONSpeed 4DWave PCI}," + "{Aztech Systems,PCI 64-Q3D}," + "{Addonics,SV 750}," + "{CHIC,True Sound 4Dwave}," + "{Shark,Predator4D-PCI}," + "{Jaton,SonicWave 4D}," + "{Hoontech,SoundTrack Digital 4DWave NX}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 32}; +static int wavetable_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8192}; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Trident 4DWave PCI soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for Trident 4DWave PCI soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Trident 4DWave PCI soundcard."); +module_param_array(pcm_channels, int, NULL, 0444); +MODULE_PARM_DESC(pcm_channels, "Number of hardware channels assigned for PCM."); +module_param_array(wavetable_size, int, NULL, 0444); +MODULE_PARM_DESC(wavetable_size, "Maximum memory size in kB for wavetable synth."); + +static struct pci_device_id snd_trident_ids[] = { + { 0x1023, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave DX PCI Audio */ + { 0x1023, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave NX PCI Audio */ + { 0x1039, 0x7018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* SiS SI7018 PCI Audio */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_trident_ids); + +static int __devinit snd_trident_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + trident_t *trident; + const char *str; + int err, pcm_dev = 0; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_trident_create(card, pci, + pcm_channels[dev], + ((pci->vendor << 16) | pci->device) == TRIDENT_DEVICE_ID_SI7018 ? 1 : 2, + wavetable_size[dev], + &trident)) < 0) { + snd_card_free(card); + return err; + } + + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + str = "TRID4DWAVEDX"; + break; + case TRIDENT_DEVICE_ID_NX: + str = "TRID4DWAVENX"; + break; + case TRIDENT_DEVICE_ID_SI7018: + str = "SI7018"; + break; + default: + str = "Unknown"; + } + strcpy(card->driver, str); + if (trident->device == TRIDENT_DEVICE_ID_SI7018) { + strcpy(card->shortname, "SiS "); + } else { + strcpy(card->shortname, "Trident "); + } + strcat(card->shortname, card->driver); + sprintf(card->longname, "%s PCI Audio at 0x%lx, irq %d", + card->shortname, trident->port, trident->irq); + + if ((err = snd_trident_pcm(trident, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + switch (trident->device) { + case TRIDENT_DEVICE_ID_DX: + case TRIDENT_DEVICE_ID_NX: + if ((err = snd_trident_foldback_pcm(trident, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + break; + } + if (trident->device == TRIDENT_DEVICE_ID_NX || trident->device == TRIDENT_DEVICE_ID_SI7018) { + if ((err = snd_trident_spdif_pcm(trident, pcm_dev++, NULL)) < 0) { + snd_card_free(card); + return err; + } + } + if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE, + trident->midi_port, 1, + trident->irq, 0, &trident->rmidi)) < 0) { + snd_card_free(card); + return err; + } + +#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) + if ((err = snd_trident_attach_synthesizer(trident)) < 0) { + snd_card_free(card); + return err; + } +#endif + + snd_trident_create_gameport(trident); + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + return err; + } + pci_set_drvdata(pci, card); + dev++; + return 0; +} + +static void __devexit snd_trident_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "Trident4DWaveAudio", + .id_table = snd_trident_ids, + .probe = snd_trident_probe, + .remove = __devexit_p(snd_trident_remove), + SND_PCI_PM_CALLBACKS +}; + +static int __init alsa_card_trident_init(void) +{ + return pci_module_init(&driver); +} + +static void __exit alsa_card_trident_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_trident_init) +module_exit(alsa_card_trident_exit) 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 ge |