Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: ISA: New Aztech Sound Galaxy driver

This is a new driver for Aztech Sound Galaxy ISA soundcards based on the
AZT1605 and AZT2316 chipsets. It's constructed as two seperate drivers
for either chipset generated from the same source file, with (very)
minimal ifdeffery.

The drivers check the SB DSP version to decide if they are being loaded
for the right chip. AZT1605 returns 2.1 by default and AZT2316 3.1.
This isn't full-proof as the DSP version can actually be set through
software but it's close enough -- as far as I've been able to see, the
DSP version can not be stored in the EEPROM and the cards will therefore
startup with the defaults.

This distinction could (with the same success rate) also be used to
decide which chip we're looking at at runtime meaning a single, merged
driver is also an option but I feel it's actually nicer this way. A
merged driver would have to postpone translating the passed in resource
values to the card configuration until it knew which one it was looking
at and would need to postpone erring out on mpu_irq=10 for azt1605 and
mpu_irq=3 for azt2316.

The drivers have been tested on various cards. For snd-azt1605:

FCC-ID I38-MMSN811: Aztech Sound Galaxy Nova 16 Extra
FCC-ID I38-MMSN822: Aztech Sound Galaxy Pro 16 II

and for snd-azt2316:

FCC-ID I38-MMSN824: Aztech Sound Galaxy Pro 16 AB
FCC-ID I38-MMSN826: Trust Sound Expert DeLuxe Wave 32 (05201)
FCC-ID I38-MMSN830: Trust Sound Expert DeLuxe 16+ (05202)
FCC-ID I38-MMSN837: Packard Bell ISA Soundcard 030069
FCC-ID I38-MMSN846: Trust Sound Expert DeLuxe 16-3D (06300)
FCC-ID I38-MMSN847: Trust Sound Expert DeLuxe Wave 32-3D (06301)
FCC-ID I38-MMSN852: Aztech Sound Galaxy Waverider Pro 32-3D

826 and 846 were also marketed directly by Aztech and then known as:

FCC-ID I38-MMSN826: Aztech Sound Galaxy Waverider 32+
FCC-ID I38-MMSN846: Aztech Sound Galaxy Nova 16 Extra II-3D

Together, these cover the AZT1605 and AT2316A, AZT2316R and AZT2316-S
chipsets. All cards work fully -- full-duplex PCM, MIDI and FM. Full
duplex is a little flaky on some.

I38-MSN811 tends to not work in full-duplex but sometimes does with the
highest success rate being achieved when you first start the capture and
then a playback instead of the other way around (it's a CS4231-KL
codec).

The cards with an AD1845XP codec (my I38-MMSN826 and one of my
I38-MMSN830s) are also somewhat duplex-challenged. Sometimes full-duplex
works, sometimes not and this varies from try to try. This seems likely
to be a timing problem somewhere inside wss-lib.

I38-MMSN826 has an additional "ICS2115 WaveFront" wavetable synth
onboard that isn't supported yet. The wavetable synths on I38-MMSN847
and I38-MMSN852 are wired directly to the standard MPU-401 UART and the
AUX1 input on the codec and work without problem.

CD-ROM audio on the cards is routed to the codec "Line" input, Line-In
to its Aux input, and FM/Wavetable to its AUX1 input. I did not rename
the controls due to the capture source enumeration: I see that
capture-source overrides are hardcoded in wss-lib and this is just too
ugly to live.

Versus the old snd-sgalaxy driver these drivers add support for the
models without a configuration EEPROM (which are common), full-duplex,
MPU-401 UART and OPL3. In the future they might grow support for that
ICS2115 WaveFront synth on 826 and an hwdep interface to write to the
EEPROM on the models that have one.

Signed-off-by: Rene Herman <rene.herman@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

René Herman and committed by
Takashi Iwai
49531192 f6cec0ae

+959 -1
+68
Documentation/sound/alsa/ALSA-Configuration.txt
··· 300 300 control correctly. If you have problems regarding this, try 301 301 another ALSA compliant mixer (alsamixer works). 302 302 303 + Module snd-azt1605 304 + ------------------ 305 + 306 + Module for Aztech Sound Galaxy soundcards based on the Aztech AZT1605 307 + chipset. 308 + 309 + port - port # for BASE (0x220,0x240,0x260,0x280) 310 + wss_port - port # for WSS (0x530,0x604,0xe80,0xf40) 311 + irq - IRQ # for WSS (7,9,10,11) 312 + dma1 - DMA # for WSS playback (0,1,3) 313 + dma2 - DMA # for WSS capture (0,1), -1 = disabled (default) 314 + mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default) 315 + mpu_irq - IRQ # for MPU-401 UART (3,5,7,9), -1 = disabled (default) 316 + fm_port - port # for OPL3 (0x388), -1 = disabled (default) 317 + 318 + This module supports multiple cards. It does not support autoprobe: port, 319 + wss_port, irq and dma1 have to be specified. The other values are 320 + optional. 321 + 322 + "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240) 323 + or the value stored in the card's EEPROM for cards that have an EEPROM and 324 + their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can 325 + be choosen freely from the options enumerated above. 326 + 327 + If dma2 is specified and different from dma1, the card will operate in 328 + full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to 329 + enable capture since only channels 0 and 1 are available for capture. 330 + 331 + Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0 332 + mpu_port=0x330 mpu_irq=9 fm_port=0x388". 333 + 334 + Whatever IRQ and DMA channels you pick, be sure to reserve them for 335 + legacy ISA in your BIOS. 336 + 337 + Module snd-azt2316 338 + ------------------ 339 + 340 + Module for Aztech Sound Galaxy soundcards based on the Aztech AZT2316 341 + chipset. 342 + 343 + port - port # for BASE (0x220,0x240,0x260,0x280) 344 + wss_port - port # for WSS (0x530,0x604,0xe80,0xf40) 345 + irq - IRQ # for WSS (7,9,10,11) 346 + dma1 - DMA # for WSS playback (0,1,3) 347 + dma2 - DMA # for WSS capture (0,1), -1 = disabled (default) 348 + mpu_port - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default) 349 + mpu_irq - IRQ # for MPU-401 UART (5,7,9,10), -1 = disabled (default) 350 + fm_port - port # for OPL3 (0x388), -1 = disabled (default) 351 + 352 + This module supports multiple cards. It does not support autoprobe: port, 353 + wss_port, irq and dma1 have to be specified. The other values are 354 + optional. 355 + 356 + "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240) 357 + or the value stored in the card's EEPROM for cards that have an EEPROM and 358 + their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can 359 + be choosen freely from the options enumerated above. 360 + 361 + If dma2 is specified and different from dma1, the card will operate in 362 + full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to 363 + enable capture since only channels 0 and 1 are available for capture. 364 + 365 + Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0 366 + mpu_port=0x330 mpu_irq=9 fm_port=0x388". 367 + 368 + Whatever IRQ and DMA channels you pick, be sure to reserve them for 369 + legacy ISA in your BIOS. 370 + 303 371 Module snd-aw2 304 372 -------------- 305 373
+26
sound/isa/Kconfig
··· 77 77 To compile this driver as a module, choose M here: the module 78 78 will be called snd-als100. 79 79 80 + config SND_AZT1605 81 + tristate "Aztech AZT1605 Driver" 82 + depends on SND 83 + select SND_WSS_LIB 84 + select SND_MPU401_UART 85 + select SND_OPL3_LIB 86 + help 87 + Say Y here to include support for Aztech Sound Galaxy cards 88 + based on the AZT1605 chipset. 89 + 90 + To compile this driver as a module, choose M here: the module 91 + will be called snd-azt1605. 92 + 93 + config SND_AZT2316 94 + tristate "Aztech AZT2316 Driver" 95 + depends on SND 96 + select SND_WSS_LIB 97 + select SND_MPU401_UART 98 + select SND_OPL3_LIB 99 + help 100 + Say Y here to include support for Aztech Sound Galaxy cards 101 + based on the AZT2316 chipset. 102 + 103 + To compile this driver as a module, choose M here: the module 104 + will be called snd-azt2316. 105 + 80 106 config SND_AZT2320 81 107 tristate "Aztech Systems AZT2320" 82 108 depends on PNP
+1 -1
sound/isa/Makefile
··· 24 24 obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o 25 25 obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o 26 26 27 - obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \ 27 + obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ galaxy/ gus/ msnd/ opti9xx/ \ 28 28 sb/ wavefront/ wss/
+10
sound/isa/galaxy/Makefile
··· 1 + # 2 + # Makefile for ALSA 3 + # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> 4 + # 5 + 6 + snd-azt1605-objs := azt1605.o 7 + snd-azt2316-objs := azt2316.o 8 + 9 + obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o 10 + obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o
+91
sound/isa/galaxy/azt1605.c
··· 1 + /* 2 + * Aztech AZT1605 Driver 3 + * Copyright (C) 2007,2010 Rene Herman 4 + * 5 + * This program is free software: you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License as published by 7 + * the Free Software Foundation, either version 2 of the License, or 8 + * (at your option) any later version. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 + * 18 + */ 19 + 20 + #define AZT1605 21 + 22 + #define CRD_NAME "Aztech AZT1605" 23 + #define DRV_NAME "AZT1605" 24 + #define DEV_NAME "azt1605" 25 + 26 + #define GALAXY_DSP_MAJOR 2 27 + #define GALAXY_DSP_MINOR 1 28 + 29 + #define GALAXY_CONFIG_SIZE 3 30 + 31 + /* 32 + * 24-bit config register 33 + */ 34 + 35 + #define GALAXY_CONFIG_SBA_220 (0 << 0) 36 + #define GALAXY_CONFIG_SBA_240 (1 << 0) 37 + #define GALAXY_CONFIG_SBA_260 (2 << 0) 38 + #define GALAXY_CONFIG_SBA_280 (3 << 0) 39 + #define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280 40 + 41 + #define GALAXY_CONFIG_MPUA_300 (0 << 2) 42 + #define GALAXY_CONFIG_MPUA_330 (1 << 2) 43 + 44 + #define GALAXY_CONFIG_MPU_ENABLE (1 << 3) 45 + 46 + #define GALAXY_CONFIG_GAME_ENABLE (1 << 4) 47 + 48 + #define GALAXY_CONFIG_CD_PANASONIC (1 << 5) 49 + #define GALAXY_CONFIG_CD_MITSUMI (1 << 6) 50 + #define GALAXY_CONFIG_CD_MASK (\ 51 + GALAXY_CONFIG_CD_PANASONIC | GALAXY_CONFIG_CD_MITSUMI) 52 + 53 + #define GALAXY_CONFIG_UNUSED (1 << 7) 54 + #define GALAXY_CONFIG_UNUSED_MASK GALAXY_CONFIG_UNUSED 55 + 56 + #define GALAXY_CONFIG_SBIRQ_2 (1 << 8) 57 + #define GALAXY_CONFIG_SBIRQ_3 (1 << 9) 58 + #define GALAXY_CONFIG_SBIRQ_5 (1 << 10) 59 + #define GALAXY_CONFIG_SBIRQ_7 (1 << 11) 60 + 61 + #define GALAXY_CONFIG_MPUIRQ_2 (1 << 12) 62 + #define GALAXY_CONFIG_MPUIRQ_3 (1 << 13) 63 + #define GALAXY_CONFIG_MPUIRQ_5 (1 << 14) 64 + #define GALAXY_CONFIG_MPUIRQ_7 (1 << 15) 65 + 66 + #define GALAXY_CONFIG_WSSA_530 (0 << 16) 67 + #define GALAXY_CONFIG_WSSA_604 (1 << 16) 68 + #define GALAXY_CONFIG_WSSA_E80 (2 << 16) 69 + #define GALAXY_CONFIG_WSSA_F40 (3 << 16) 70 + 71 + #define GALAXY_CONFIG_WSS_ENABLE (1 << 18) 72 + 73 + #define GALAXY_CONFIG_CDIRQ_11 (1 << 19) 74 + #define GALAXY_CONFIG_CDIRQ_12 (1 << 20) 75 + #define GALAXY_CONFIG_CDIRQ_15 (1 << 21) 76 + #define GALAXY_CONFIG_CDIRQ_MASK (\ 77 + GALAXY_CONFIG_CDIRQ_11 | GALAXY_CONFIG_CDIRQ_12 |\ 78 + GALAXY_CONFIG_CDIRQ_15) 79 + 80 + #define GALAXY_CONFIG_CDDMA_DISABLE (0 << 22) 81 + #define GALAXY_CONFIG_CDDMA_0 (1 << 22) 82 + #define GALAXY_CONFIG_CDDMA_1 (2 << 22) 83 + #define GALAXY_CONFIG_CDDMA_3 (3 << 22) 84 + #define GALAXY_CONFIG_CDDMA_MASK GALAXY_CONFIG_CDDMA_3 85 + 86 + #define GALAXY_CONFIG_MASK (\ 87 + GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CD_MASK |\ 88 + GALAXY_CONFIG_UNUSED_MASK | GALAXY_CONFIG_CDIRQ_MASK |\ 89 + GALAXY_CONFIG_CDDMA_MASK) 90 + 91 + #include "galaxy.c"
+111
sound/isa/galaxy/azt2316.c
··· 1 + /* 2 + * Aztech AZT2316 Driver 3 + * Copyright (C) 2007,2010 Rene Herman 4 + * 5 + * This program is free software: you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License as published by 7 + * the Free Software Foundation, either version 2 of the License, or 8 + * (at your option) any later version. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 + * 18 + */ 19 + 20 + #define AZT2316 21 + 22 + #define CRD_NAME "Aztech AZT2316" 23 + #define DRV_NAME "AZT2316" 24 + #define DEV_NAME "azt2316" 25 + 26 + #define GALAXY_DSP_MAJOR 3 27 + #define GALAXY_DSP_MINOR 1 28 + 29 + #define GALAXY_CONFIG_SIZE 4 30 + 31 + /* 32 + * 32-bit config register 33 + */ 34 + 35 + #define GALAXY_CONFIG_SBA_220 (0 << 0) 36 + #define GALAXY_CONFIG_SBA_240 (1 << 0) 37 + #define GALAXY_CONFIG_SBA_260 (2 << 0) 38 + #define GALAXY_CONFIG_SBA_280 (3 << 0) 39 + #define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280 40 + 41 + #define GALAXY_CONFIG_SBIRQ_2 (1 << 2) 42 + #define GALAXY_CONFIG_SBIRQ_5 (1 << 3) 43 + #define GALAXY_CONFIG_SBIRQ_7 (1 << 4) 44 + #define GALAXY_CONFIG_SBIRQ_10 (1 << 5) 45 + 46 + #define GALAXY_CONFIG_SBDMA_DISABLE (0 << 6) 47 + #define GALAXY_CONFIG_SBDMA_0 (1 << 6) 48 + #define GALAXY_CONFIG_SBDMA_1 (2 << 6) 49 + #define GALAXY_CONFIG_SBDMA_3 (3 << 6) 50 + 51 + #define GALAXY_CONFIG_WSSA_530 (0 << 8) 52 + #define GALAXY_CONFIG_WSSA_604 (1 << 8) 53 + #define GALAXY_CONFIG_WSSA_E80 (2 << 8) 54 + #define GALAXY_CONFIG_WSSA_F40 (3 << 8) 55 + 56 + #define GALAXY_CONFIG_WSS_ENABLE (1 << 10) 57 + 58 + #define GALAXY_CONFIG_GAME_ENABLE (1 << 11) 59 + 60 + #define GALAXY_CONFIG_MPUA_300 (0 << 12) 61 + #define GALAXY_CONFIG_MPUA_330 (1 << 12) 62 + 63 + #define GALAXY_CONFIG_MPU_ENABLE (1 << 13) 64 + 65 + #define GALAXY_CONFIG_CDA_310 (0 << 14) 66 + #define GALAXY_CONFIG_CDA_320 (1 << 14) 67 + #define GALAXY_CONFIG_CDA_340 (2 << 14) 68 + #define GALAXY_CONFIG_CDA_350 (3 << 14) 69 + #define GALAXY_CONFIG_CDA_MASK GALAXY_CONFIG_CDA_350 70 + 71 + #define GALAXY_CONFIG_CD_DISABLE (0 << 16) 72 + #define GALAXY_CONFIG_CD_PANASONIC (1 << 16) 73 + #define GALAXY_CONFIG_CD_SONY (2 << 16) 74 + #define GALAXY_CONFIG_CD_MITSUMI (3 << 16) 75 + #define GALAXY_CONFIG_CD_AZTECH (4 << 16) 76 + #define GALAXY_CONFIG_CD_UNUSED_5 (5 << 16) 77 + #define GALAXY_CONFIG_CD_UNUSED_6 (6 << 16) 78 + #define GALAXY_CONFIG_CD_UNUSED_7 (7 << 16) 79 + #define GALAXY_CONFIG_CD_MASK GALAXY_CONFIG_CD_UNUSED_7 80 + 81 + #define GALAXY_CONFIG_CDDMA8_DISABLE (0 << 20) 82 + #define GALAXY_CONFIG_CDDMA8_0 (1 << 20) 83 + #define GALAXY_CONFIG_CDDMA8_1 (2 << 20) 84 + #define GALAXY_CONFIG_CDDMA8_3 (3 << 20) 85 + #define GALAXY_CONFIG_CDDMA8_MASK GALAXY_CONFIG_CDDMA8_3 86 + 87 + #define GALAXY_CONFIG_CDDMA16_DISABLE (0 << 22) 88 + #define GALAXY_CONFIG_CDDMA16_5 (1 << 22) 89 + #define GALAXY_CONFIG_CDDMA16_6 (2 << 22) 90 + #define GALAXY_CONFIG_CDDMA16_7 (3 << 22) 91 + #define GALAXY_CONFIG_CDDMA16_MASK GALAXY_CONFIG_CDDMA16_7 92 + 93 + #define GALAXY_CONFIG_MPUIRQ_2 (1 << 24) 94 + #define GALAXY_CONFIG_MPUIRQ_5 (1 << 25) 95 + #define GALAXY_CONFIG_MPUIRQ_7 (1 << 26) 96 + #define GALAXY_CONFIG_MPUIRQ_10 (1 << 27) 97 + 98 + #define GALAXY_CONFIG_CDIRQ_5 (1 << 28) 99 + #define GALAXY_CONFIG_CDIRQ_11 (1 << 29) 100 + #define GALAXY_CONFIG_CDIRQ_12 (1 << 30) 101 + #define GALAXY_CONFIG_CDIRQ_15 (1 << 31) 102 + #define GALAXY_CONFIG_CDIRQ_MASK (\ 103 + GALAXY_CONFIG_CDIRQ_5 | GALAXY_CONFIG_CDIRQ_11 |\ 104 + GALAXY_CONFIG_CDIRQ_12 | GALAXY_CONFIG_CDIRQ_15) 105 + 106 + #define GALAXY_CONFIG_MASK (\ 107 + GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CDA_MASK |\ 108 + GALAXY_CONFIG_CD_MASK | GALAXY_CONFIG_CDDMA16_MASK |\ 109 + GALAXY_CONFIG_CDDMA8_MASK | GALAXY_CONFIG_CDIRQ_MASK) 110 + 111 + #include "galaxy.c"
+652
sound/isa/galaxy/galaxy.c
··· 1 + /* 2 + * Aztech AZT1605/AZT2316 Driver 3 + * Copyright (C) 2007,2010 Rene Herman 4 + * 5 + * This program is free software: you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License as published by 7 + * the Free Software Foundation, either version 2 of the License, or 8 + * (at your option) any later version. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * You should have received a copy of the GNU General Public License 16 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 + * 18 + */ 19 + 20 + #include <linux/kernel.h> 21 + #include <linux/module.h> 22 + #include <linux/isa.h> 23 + #include <linux/delay.h> 24 + #include <linux/io.h> 25 + #include <asm/processor.h> 26 + #include <sound/core.h> 27 + #include <sound/initval.h> 28 + #include <sound/wss.h> 29 + #include <sound/mpu401.h> 30 + #include <sound/opl3.h> 31 + 32 + MODULE_DESCRIPTION(CRD_NAME); 33 + MODULE_AUTHOR("Rene Herman"); 34 + MODULE_LICENSE("GPL"); 35 + 36 + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 37 + static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 38 + static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; 39 + 40 + module_param_array(index, int, NULL, 0444); 41 + MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard."); 42 + module_param_array(id, charp, NULL, 0444); 43 + MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard."); 44 + module_param_array(enable, bool, NULL, 0444); 45 + MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard."); 46 + 47 + static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 48 + static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 49 + static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 50 + static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; 51 + static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; 52 + static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; 53 + static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; 54 + static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; 55 + 56 + module_param_array(port, long, NULL, 0444); 57 + MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver."); 58 + module_param_array(wss_port, long, NULL, 0444); 59 + MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver."); 60 + module_param_array(mpu_port, long, NULL, 0444); 61 + MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver."); 62 + module_param_array(fm_port, long, NULL, 0444); 63 + MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver."); 64 + module_param_array(irq, int, NULL, 0444); 65 + MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver."); 66 + module_param_array(mpu_irq, int, NULL, 0444); 67 + MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver."); 68 + module_param_array(dma1, int, NULL, 0444); 69 + MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver."); 70 + module_param_array(dma2, int, NULL, 0444); 71 + MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver."); 72 + 73 + /* 74 + * Generic SB DSP support routines 75 + */ 76 + 77 + #define DSP_PORT_RESET 0x6 78 + #define DSP_PORT_READ 0xa 79 + #define DSP_PORT_COMMAND 0xc 80 + #define DSP_PORT_STATUS 0xc 81 + #define DSP_PORT_DATA_AVAIL 0xe 82 + 83 + #define DSP_SIGNATURE 0xaa 84 + 85 + #define DSP_COMMAND_GET_VERSION 0xe1 86 + 87 + static int __devinit dsp_get_byte(void __iomem *port, u8 *val) 88 + { 89 + int loops = 1000; 90 + 91 + while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) { 92 + if (!loops--) 93 + return -EIO; 94 + cpu_relax(); 95 + } 96 + *val = ioread8(port + DSP_PORT_READ); 97 + return 0; 98 + } 99 + 100 + static int __devinit dsp_reset(void __iomem *port) 101 + { 102 + u8 val; 103 + 104 + iowrite8(1, port + DSP_PORT_RESET); 105 + udelay(10); 106 + iowrite8(0, port + DSP_PORT_RESET); 107 + 108 + if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE) 109 + return -ENODEV; 110 + 111 + return 0; 112 + } 113 + 114 + static int __devinit dsp_command(void __iomem *port, u8 cmd) 115 + { 116 + int loops = 1000; 117 + 118 + while (ioread8(port + DSP_PORT_STATUS) & 0x80) { 119 + if (!loops--) 120 + return -EIO; 121 + cpu_relax(); 122 + } 123 + iowrite8(cmd, port + DSP_PORT_COMMAND); 124 + return 0; 125 + } 126 + 127 + static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor) 128 + { 129 + int err; 130 + 131 + err = dsp_command(port, DSP_COMMAND_GET_VERSION); 132 + if (err < 0) 133 + return err; 134 + 135 + err = dsp_get_byte(port, major); 136 + if (err < 0) 137 + return err; 138 + 139 + err = dsp_get_byte(port, minor); 140 + if (err < 0) 141 + return err; 142 + 143 + return 0; 144 + } 145 + 146 + /* 147 + * Generic WSS support routines 148 + */ 149 + 150 + #define WSS_CONFIG_DMA_0 (1 << 0) 151 + #define WSS_CONFIG_DMA_1 (2 << 0) 152 + #define WSS_CONFIG_DMA_3 (3 << 0) 153 + #define WSS_CONFIG_DUPLEX (1 << 2) 154 + #define WSS_CONFIG_IRQ_7 (1 << 3) 155 + #define WSS_CONFIG_IRQ_9 (2 << 3) 156 + #define WSS_CONFIG_IRQ_10 (3 << 3) 157 + #define WSS_CONFIG_IRQ_11 (4 << 3) 158 + 159 + #define WSS_PORT_CONFIG 0 160 + #define WSS_PORT_SIGNATURE 3 161 + 162 + #define WSS_SIGNATURE 4 163 + 164 + static int __devinit wss_detect(void __iomem *wss_port) 165 + { 166 + if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE) 167 + return -ENODEV; 168 + 169 + return 0; 170 + } 171 + 172 + static void wss_set_config(void __iomem *wss_port, u8 wss_config) 173 + { 174 + iowrite8(wss_config, wss_port + WSS_PORT_CONFIG); 175 + } 176 + 177 + /* 178 + * Aztech Sound Galaxy specifics 179 + */ 180 + 181 + #define GALAXY_PORT_CONFIG 1024 182 + #define CONFIG_PORT_SET 4 183 + 184 + #define DSP_COMMAND_GALAXY_8 8 185 + #define GALAXY_COMMAND_GET_TYPE 5 186 + 187 + #define DSP_COMMAND_GALAXY_9 9 188 + #define GALAXY_COMMAND_WSSMODE 0 189 + #define GALAXY_COMMAND_SB8MODE 1 190 + 191 + #define GALAXY_MODE_WSS GALAXY_COMMAND_WSSMODE 192 + #define GALAXY_MODE_SB8 GALAXY_COMMAND_SB8MODE 193 + 194 + struct snd_galaxy { 195 + void __iomem *port; 196 + void __iomem *config_port; 197 + void __iomem *wss_port; 198 + u32 config; 199 + struct resource *res_port; 200 + struct resource *res_config_port; 201 + struct resource *res_wss_port; 202 + }; 203 + 204 + static u32 config[SNDRV_CARDS]; 205 + static u8 wss_config[SNDRV_CARDS]; 206 + 207 + static int __devinit snd_galaxy_match(struct device *dev, unsigned int n) 208 + { 209 + if (!enable[n]) 210 + return 0; 211 + 212 + switch (port[n]) { 213 + case SNDRV_AUTO_PORT: 214 + dev_err(dev, "please specify port\n"); 215 + return 0; 216 + case 0x220: 217 + config[n] |= GALAXY_CONFIG_SBA_220; 218 + break; 219 + case 0x240: 220 + config[n] |= GALAXY_CONFIG_SBA_240; 221 + break; 222 + case 0x260: 223 + config[n] |= GALAXY_CONFIG_SBA_260; 224 + break; 225 + case 0x280: 226 + config[n] |= GALAXY_CONFIG_SBA_280; 227 + break; 228 + default: 229 + dev_err(dev, "invalid port %#lx\n", port[n]); 230 + return 0; 231 + } 232 + 233 + switch (wss_port[n]) { 234 + case SNDRV_AUTO_PORT: 235 + dev_err(dev, "please specify wss_port\n"); 236 + return 0; 237 + case 0x530: 238 + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530; 239 + break; 240 + case 0x604: 241 + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604; 242 + break; 243 + case 0xe80: 244 + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80; 245 + break; 246 + case 0xf40: 247 + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40; 248 + break; 249 + default: 250 + dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]); 251 + return 0; 252 + } 253 + 254 + switch (irq[n]) { 255 + case SNDRV_AUTO_IRQ: 256 + dev_err(dev, "please specify irq\n"); 257 + return 0; 258 + case 7: 259 + wss_config[n] |= WSS_CONFIG_IRQ_7; 260 + break; 261 + case 2: 262 + irq[n] = 9; 263 + case 9: 264 + wss_config[n] |= WSS_CONFIG_IRQ_9; 265 + break; 266 + case 10: 267 + wss_config[n] |= WSS_CONFIG_IRQ_10; 268 + break; 269 + case 11: 270 + wss_config[n] |= WSS_CONFIG_IRQ_11; 271 + break; 272 + default: 273 + dev_err(dev, "invalid IRQ %d\n", irq[n]); 274 + return 0; 275 + } 276 + 277 + switch (dma1[n]) { 278 + case SNDRV_AUTO_DMA: 279 + dev_err(dev, "please specify dma1\n"); 280 + return 0; 281 + case 0: 282 + wss_config[n] |= WSS_CONFIG_DMA_0; 283 + break; 284 + case 1: 285 + wss_config[n] |= WSS_CONFIG_DMA_1; 286 + break; 287 + case 3: 288 + wss_config[n] |= WSS_CONFIG_DMA_3; 289 + break; 290 + default: 291 + dev_err(dev, "invalid playback DMA %d\n", dma1[n]); 292 + return 0; 293 + } 294 + 295 + if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) { 296 + dma2[n] = -1; 297 + goto mpu; 298 + } 299 + 300 + wss_config[n] |= WSS_CONFIG_DUPLEX; 301 + switch (dma2[n]) { 302 + case 0: 303 + break; 304 + case 1: 305 + if (dma1[n] == 0) 306 + break; 307 + default: 308 + dev_err(dev, "invalid capture DMA %d\n", dma2[n]); 309 + return 0; 310 + } 311 + 312 + mpu: 313 + switch (mpu_port[n]) { 314 + case SNDRV_AUTO_PORT: 315 + dev_warn(dev, "mpu_port not specified; not using MPU-401\n"); 316 + mpu_port[n] = -1; 317 + goto fm; 318 + case 0x300: 319 + config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300; 320 + break; 321 + case 0x330: 322 + config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330; 323 + break; 324 + default: 325 + dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]); 326 + return 0; 327 + } 328 + 329 + switch (mpu_irq[n]) { 330 + case SNDRV_AUTO_IRQ: 331 + dev_warn(dev, "mpu_irq not specified: using polling mode\n"); 332 + mpu_irq[n] = -1; 333 + break; 334 + case 2: 335 + mpu_irq[n] = 9; 336 + case 9: 337 + config[n] |= GALAXY_CONFIG_MPUIRQ_2; 338 + break; 339 + #ifdef AZT1605 340 + case 3: 341 + config[n] |= GALAXY_CONFIG_MPUIRQ_3; 342 + break; 343 + #endif 344 + case 5: 345 + config[n] |= GALAXY_CONFIG_MPUIRQ_5; 346 + break; 347 + case 7: 348 + config[n] |= GALAXY_CONFIG_MPUIRQ_7; 349 + break; 350 + #ifdef AZT2316 351 + case 10: 352 + config[n] |= GALAXY_CONFIG_MPUIRQ_10; 353 + break; 354 + #endif 355 + default: 356 + dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]); 357 + return 0; 358 + } 359 + 360 + if (mpu_irq[n] == irq[n]) { 361 + dev_err(dev, "cannot share IRQ between WSS and MPU-401\n"); 362 + return 0; 363 + } 364 + 365 + fm: 366 + switch (fm_port[n]) { 367 + case SNDRV_AUTO_PORT: 368 + dev_warn(dev, "fm_port not specified: not using OPL3\n"); 369 + fm_port[n] = -1; 370 + break; 371 + case 0x388: 372 + break; 373 + default: 374 + dev_err(dev, "illegal FM port %#lx\n", fm_port[n]); 375 + return 0; 376 + } 377 + 378 + config[n] |= GALAXY_CONFIG_GAME_ENABLE; 379 + return 1; 380 + } 381 + 382 + static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type) 383 + { 384 + u8 major; 385 + u8 minor; 386 + int err; 387 + 388 + err = dsp_reset(galaxy->port); 389 + if (err < 0) 390 + return err; 391 + 392 + err = dsp_get_version(galaxy->port, &major, &minor); 393 + if (err < 0) 394 + return err; 395 + 396 + if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR) 397 + return -ENODEV; 398 + 399 + err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8); 400 + if (err < 0) 401 + return err; 402 + 403 + err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE); 404 + if (err < 0) 405 + return err; 406 + 407 + err = dsp_get_byte(galaxy->port, type); 408 + if (err < 0) 409 + return err; 410 + 411 + return 0; 412 + } 413 + 414 + static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode) 415 + { 416 + int err; 417 + 418 + err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9); 419 + if (err < 0) 420 + return err; 421 + 422 + err = dsp_command(galaxy->port, mode); 423 + if (err < 0) 424 + return err; 425 + 426 + #ifdef AZT1605 427 + /* 428 + * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again 429 + */ 430 + err = dsp_reset(galaxy->port); 431 + if (err < 0) 432 + return err; 433 + #endif 434 + 435 + return 0; 436 + } 437 + 438 + static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config) 439 + { 440 + u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET); 441 + int i; 442 + 443 + iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET); 444 + for (i = 0; i < GALAXY_CONFIG_SIZE; i++) { 445 + iowrite8(config, galaxy->config_port + i); 446 + config >>= 8; 447 + } 448 + iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET); 449 + msleep(10); 450 + } 451 + 452 + static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config) 453 + { 454 + int i; 455 + 456 + for (i = GALAXY_CONFIG_SIZE; i; i--) { 457 + u8 tmp = ioread8(galaxy->config_port + i - 1); 458 + galaxy->config = (galaxy->config << 8) | tmp; 459 + } 460 + config |= galaxy->config & GALAXY_CONFIG_MASK; 461 + galaxy_set_config(galaxy, config); 462 + } 463 + 464 + static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config) 465 + { 466 + int err; 467 + 468 + err = wss_detect(galaxy->wss_port); 469 + if (err < 0) 470 + return err; 471 + 472 + wss_set_config(galaxy->wss_port, wss_config); 473 + 474 + err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS); 475 + if (err < 0) 476 + return err; 477 + 478 + return 0; 479 + } 480 + 481 + static void snd_galaxy_free(struct snd_card *card) 482 + { 483 + struct snd_galaxy *galaxy = card->private_data; 484 + 485 + if (galaxy->wss_port) { 486 + wss_set_config(galaxy->wss_port, 0); 487 + ioport_unmap(galaxy->wss_port); 488 + release_and_free_resource(galaxy->res_wss_port); 489 + } 490 + if (galaxy->config_port) { 491 + galaxy_set_config(galaxy, galaxy->config); 492 + ioport_unmap(galaxy->config_port); 493 + release_and_free_resource(galaxy->res_config_port); 494 + } 495 + if (galaxy->port) { 496 + ioport_unmap(galaxy->port); 497 + release_and_free_resource(galaxy->res_port); 498 + } 499 + } 500 + 501 + static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n) 502 + { 503 + struct snd_galaxy *galaxy; 504 + struct snd_wss *chip; 505 + struct snd_card *card; 506 + u8 type; 507 + int err; 508 + 509 + err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy, 510 + &card); 511 + if (err < 0) 512 + return err; 513 + 514 + snd_card_set_dev(card, dev); 515 + 516 + card->private_free = snd_galaxy_free; 517 + galaxy = card->private_data; 518 + 519 + galaxy->res_port = request_region(port[n], 16, DRV_NAME); 520 + if (!galaxy->res_port) { 521 + dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n], 522 + port[n] + 15); 523 + err = -EBUSY; 524 + goto error; 525 + } 526 + galaxy->port = ioport_map(port[n], 16); 527 + 528 + err = galaxy_init(galaxy, &type); 529 + if (err < 0) { 530 + dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]); 531 + goto error; 532 + } 533 + dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]); 534 + 535 + galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG, 536 + 16, DRV_NAME); 537 + if (!galaxy->res_config_port) { 538 + dev_err(dev, "could not grab ports %#lx-%#lx\n", 539 + port[n] + GALAXY_PORT_CONFIG, 540 + port[n] + GALAXY_PORT_CONFIG + 15); 541 + err = -EBUSY; 542 + goto error; 543 + } 544 + galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16); 545 + 546 + galaxy_config(galaxy, config[n]); 547 + 548 + galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME); 549 + if (!galaxy->res_wss_port) { 550 + dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n], 551 + wss_port[n] + 3); 552 + err = -EBUSY; 553 + goto error; 554 + } 555 + galaxy->wss_port = ioport_map(wss_port[n], 4); 556 + 557 + err = galaxy_wss_config(galaxy, wss_config[n]); 558 + if (err < 0) { 559 + dev_err(dev, "could not configure WSS\n"); 560 + goto error; 561 + } 562 + 563 + strcpy(card->driver, DRV_NAME); 564 + strcpy(card->shortname, DRV_NAME); 565 + sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d", 566 + card->shortname, port[n], wss_port[n], irq[n], dma1[n], 567 + dma2[n]); 568 + 569 + err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n], 570 + dma2[n], WSS_HW_DETECT, 0, &chip); 571 + if (err < 0) 572 + goto error; 573 + 574 + err = snd_wss_pcm(chip, 0, NULL); 575 + if (err < 0) 576 + goto error; 577 + 578 + err = snd_wss_mixer(chip); 579 + if (err < 0) 580 + goto error; 581 + 582 + err = snd_wss_timer(chip, 0, NULL); 583 + if (err < 0) 584 + goto error; 585 + 586 + if (mpu_port[n] >= 0) { 587 + err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, 588 + mpu_port[n], 0, mpu_irq[n], 589 + IRQF_DISABLED, NULL); 590 + if (err < 0) 591 + goto error; 592 + } 593 + 594 + if (fm_port[n] >= 0) { 595 + struct snd_opl3 *opl3; 596 + 597 + err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2, 598 + OPL3_HW_AUTO, 0, &opl3); 599 + if (err < 0) { 600 + dev_err(dev, "no OPL device at %#lx\n", fm_port[n]); 601 + goto error; 602 + } 603 + err = snd_opl3_timer_new(opl3, 1, 2); 604 + if (err < 0) 605 + goto error; 606 + 607 + err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); 608 + if (err < 0) 609 + goto error; 610 + } 611 + 612 + err = snd_card_register(card); 613 + if (err < 0) 614 + goto error; 615 + 616 + dev_set_drvdata(dev, card); 617 + return 0; 618 + 619 + error: 620 + snd_card_free(card); 621 + return err; 622 + } 623 + 624 + static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n) 625 + { 626 + snd_card_free(dev_get_drvdata(dev)); 627 + dev_set_drvdata(dev, NULL); 628 + return 0; 629 + } 630 + 631 + static struct isa_driver snd_galaxy_driver = { 632 + .match = snd_galaxy_match, 633 + .probe = snd_galaxy_probe, 634 + .remove = __devexit_p(snd_galaxy_remove), 635 + 636 + .driver = { 637 + .name = DEV_NAME 638 + } 639 + }; 640 + 641 + static int __init alsa_card_galaxy_init(void) 642 + { 643 + return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS); 644 + } 645 + 646 + static void __exit alsa_card_galaxy_exit(void) 647 + { 648 + isa_unregister_driver(&snd_galaxy_driver); 649 + } 650 + 651 + module_init(alsa_card_galaxy_init); 652 + module_exit(alsa_card_galaxy_exit);