diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index cf3803c..02a0f55 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -177,6 +177,19 @@ config SND_ES18XX To compile this driver as a module, choose M here: the module will be called snd-es18xx. +config SND_GALAXY + tristate "Aztech Sound Galaxy Driver" + depends on SND + select SND_CS4231_LIB + select SND_MPU401_UART + select SND_OPL3_LIB + select SND_PCM + help + Say Y here to include support for Aztech Sound Galaxy cards. + + To compile this driver as a module, choose M here: the module + will be called snd-galaxy. + config SND_GUS_SYNTH tristate diff --git a/sound/isa/Makefile b/sound/isa/Makefile index bb317cc..28bf545 100644 --- a/sound/isa/Makefile +++ b/sound/isa/Makefile @@ -12,6 +12,7 @@ snd-es18xx-objs := es18xx.o snd-opl3sa2-objs := opl3sa2.o snd-sgalaxy-objs := sgalaxy.o snd-sscape-objs := sscape.o +snd-galaxy-objs := galaxy.o # Toplevel Module Dependency obj-$(CONFIG_SND_ADLIB) += snd-adlib.o @@ -23,6 +24,7 @@ obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o +obj-$(CONFIG_SND_GALAXY) += snd-galaxy.o obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ opti9xx/ \ sb/ wavefront/ diff --git a/sound/isa/galaxy.c b/sound/isa/galaxy.c new file mode 100644 index 0000000..8f8e9af --- /dev/null +++ b/sound/isa/galaxy.c @@ -0,0 +1,674 @@ +/* + * Aztech Sound Galaxy Driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CRD_NAME "Aztech Sound Galaxy" +#define DRV_NAME "GALAXY" +#define DEV_NAME "galaxy" + +MODULE_DESCRIPTION(CRD_NAME); +MODULE_AUTHOR("Rene Herman"); +MODULE_LICENSE("GPL"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard."); + +static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; +static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; +static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; +static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; + +module_param_array(port, long, NULL, 0444); +MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver."); +module_param_array(wss_port, long, NULL, 0444); +MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver."); +module_param_array(mpu_port, long, NULL, 0444); +MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver."); +module_param_array(fm_port, long, NULL, 0444); +MODULE_PARM_DESC(fm_port, "OPL3 port # for " CRD_NAME " driver."); +module_param_array(irq, int, NULL, 0444); +MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver."); +module_param_array(mpu_irq, int, NULL, 0444); +MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver."); +module_param_array(dma1, int, NULL, 0444); +MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver."); +module_param_array(dma2, int, NULL, 0444); +MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver."); + +/* + * Sound Galaxy 32-bit serial EEPROM + */ + +#define GALAXY_PORT_CONFIG 1024 + +#define GALAXY_CONFIG_SBDMA_DISABLE (0 << 30) +#define GALAXY_CONFIG_SBDMA_0 (1 << 30) +#define GALAXY_CONFIG_SBDMA_1 (2 << 30) +#define GALAXY_CONFIG_SBDMA_3 (3 << 30) + +#define GALAXY_CONFIG_SBIRQ_10 (1 << 29) +#define GALAXY_CONFIG_SBIRQ_7 (1 << 28) +#define GALAXY_CONFIG_SBIRQ_5 (1 << 27) +#define GALAXY_CONFIG_SBIRQ_2 (1 << 26) + +#define GALAXY_CONFIG_SBA_220 (0 << 24) +#define GALAXY_CONFIG_SBA_240 (1 << 24) +#define GALAXY_CONFIG_SBA_260 (2 << 24) +#define GALAXY_CONFIG_SBA_280 (3 << 24) +#define GALAXY_CONFIG_SBA_MASK GALAXY_CONFIG_SBA_280 + +#define GALAXY_CONFIG_CDA_310 (0 << 22) +#define GALAXY_CONFIG_CDA_320 (1 << 22) +#define GALAXY_CONFIG_CDA_340 (2 << 22) +#define GALAXY_CONFIG_CDA_350 (3 << 22) +#define GALAXY_CONFIG_CDA_MASK GALAXY_CONFIG_CDA_350 + +#define GALAXY_CONFIG_MPU_ENABLE (1 << 21) +#define GALAXY_CONFIG_MPUA_300 (0 << 20) +#define GALAXY_CONFIG_MPUA_330 (1 << 20) + +#define GALAXY_CONFIG_GAME_ENABLE (1 << 19) + +#define GALAXY_CONFIG_WSS_ENABLE (1 << 18) +#define GALAXY_CONFIG_WSSA_530 (0 << 16) +#define GALAXY_CONFIG_WSSA_604 (1 << 16) +#define GALAXY_CONFIG_WSSA_E80 (2 << 16) +#define GALAXY_CONFIG_WSSA_F40 (3 << 16) + +#define GALAXY_CONFIG_CDDMA16_DISABLE (0 << 14) +#define GALAXY_CONFIG_CDDMA16_5 (1 << 14) +#define GALAXY_CONFIG_CDDMA16_6 (2 << 14) +#define GALAXY_CONFIG_CDDMA16_7 (3 << 14) +#define GALAXY_CONFIG_CDDMA16_MASK GALAXY_CONFIG_CDDMA16_7 + +#define GALAXY_CONFIG_CDDMA8_DISABLE (0 << 12) +#define GALAXY_CONFIG_CDDMA8_0 (1 << 12) +#define GALAXY_CONFIG_CDDMA8_1 (2 << 12) +#define GALAXY_CONFIG_CDDMA8_3 (3 << 12) +#define GALAXY_CONFIG_CDDMA8_MASK GALAXY_CONFIG_CDDMA8_3 + +#define GALAXY_CONFIG_CD_DISABLE (0 << 8) +#define GALAXY_CONFIG_CD_PANASONIC (1 << 8) +#define GALAXY_CONFIG_CD_SONY (2 << 8) +#define GALAXY_CONFIG_CD_MITSUMI (3 << 8) +#define GALAXY_CONFIG_CD_AZTECH (4 << 8) +#define GALAXY_CONFIG_CD_UNUSED_0 (5 << 8) +#define GALAXY_CONFIG_CD_UNUSED_1 (6 << 8) +#define GALAXY_CONFIG_CD_UNUSED_2 (7 << 8) +#define GALAXY_CONFIG_CD_MASK GALAXY_CONFIG_CD_UNUSED_2 + +#define GALAXY_CONFIG_CDIRQ_15 (1 << 7) +#define GALAXY_CONFIG_CDIRQ_12 (1 << 6) +#define GALAXY_CONFIG_CDIRQ_11 (1 << 5) +#define GALAXY_CONFIG_CDIRQ_5 (1 << 4) +#define GALAXY_CONFIG_CDIRQ_MASK (\ + GALAXY_CONFIG_CDIRQ_15 | GALAXY_CONFIG_CDIRQ_12 |\ + GALAXY_CONFIG_CDIRQ_11 | GALAXY_CONFIG_CDIRQ_5) + +#define GALAXY_CONFIG_MPUIRQ_10 (1 << 3) +#define GALAXY_CONFIG_MPUIRQ_7 (1 << 2) +#define GALAXY_CONFIG_MPUIRQ_5 (1 << 1) +#define GALAXY_CONFIG_MPUIRQ_2 (1 << 0) + +#define GALAXY_CONFIG_MASK (\ + GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CDA_MASK |\ + GALAXY_CONFIG_CDDMA16_MASK | GALAXY_CONFIG_CDDMA8_MASK |\ + GALAXY_CONFIG_CD_MASK | GALAXY_CONFIG_CDIRQ_MASK) + +/* + * WSS 8-bit configuration register + */ + +#define WSS_PORT_CONFIG 0 + +#define WSS_CONFIG_IRQ_7 (1 << 3) +#define WSS_CONFIG_IRQ_9 (2 << 3) +#define WSS_CONFIG_IRQ_10 (3 << 3) +#define WSS_CONFIG_IRQ_11 (4 << 3) + +#define WSS_CONFIG_DUPLEX (1 << 2) + +#define WSS_CONFIG_DMA_0 (1 << 0) +#define WSS_CONFIG_DMA_1 (2 << 0) +#define WSS_CONFIG_DMA_3 (3 << 0) + +#define WSS_PORT_SIGNATURE 3 +#define WSS_SIGNATURE 4 + +/* + * Generic SB DSP registers + */ + +#define DSP_PORT_RESET 0x6 +#define DSP_PORT_READ 0xa +#define DSP_PORT_COMMAND 0xc +#define DSP_PORT_STATUS 0xc +#define DSP_PORT_DATA_AVAIL 0xe + +#define DSP_SIGNATURE 0xaa + +/* + * Sound Galaxy specific DSP commands. + */ + +#define DSP_COMMAND_GALAXY 9 + +#define GALAXY_COMMAND_WSSMODE 0 +#define GALAXY_COMMAND_SBPMODE 1 +#define GALAXY_COMMAND_MPU 2 +#define GALAXY_COMMAND_MPUA 4 + +#define GALAXY_MODE_WSS GALAXY_COMMAND_WSSMODE +#define GALAXY_MODE_SBP GALAXY_COMMAND_SBPMODE + +struct snd_galaxy { + void __iomem *port; + void __iomem *config_port; + void __iomem *wss_port; + struct resource *res_port; + struct resource *res_config_port; + struct resource *res_wss_port; + u32 config; +}; + +static u32 config[SNDRV_CARDS]; +static u8 wss_config[SNDRV_CARDS]; + +static int __devinit snd_galaxy_match(struct device *dev, unsigned int n) +{ + if (!enable[n]) + return 0; + + switch (port[n]) { + case SNDRV_AUTO_PORT: + snd_printk(KERN_ERR "%s: please specify port\n", dev->bus_id); + return 0; + case 0x220: + config[n] |= GALAXY_CONFIG_SBA_220; + break; + case 0x240: + config[n] |= GALAXY_CONFIG_SBA_240; + break; + default: + snd_printk(KERN_ERR "%s: invalid port %#lx\n", dev->bus_id, + port[n]); + return 0; + } + + switch (wss_port[n]) { + case SNDRV_AUTO_PORT: + snd_printk(KERN_ERR "%s: please specify wss_port\n", + dev->bus_id); + return 0; + case 0x530: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530; + break; + case 0x604: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604; + break; + case 0xe80: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80; + break; + case 0xf40: + config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40; + break; + default: + snd_printk(KERN_ERR "%s: invalid WSS port %#lx\n", dev->bus_id, + wss_port[n]); + return 0; + } + + switch (irq[n]) { + case SNDRV_AUTO_IRQ: + snd_printk(KERN_ERR "%s: please specify irq\n", dev->bus_id); + return 0; + case 7: + wss_config[n] |= WSS_CONFIG_IRQ_7; + break; + case 9: + case 2: + wss_config[n] |= WSS_CONFIG_IRQ_9; + break; + case 10: + wss_config[n] |= WSS_CONFIG_IRQ_10; + break; + case 11: + wss_config[n] |= WSS_CONFIG_IRQ_11; + break; + default: + snd_printk(KERN_ERR "%s: invalid IRQ %d\n", dev->bus_id, + irq[n]); + return 0; + } + + switch (dma1[n]) { + case SNDRV_AUTO_DMA: + snd_printk(KERN_ERR "%s: please specify dma1\n", + dev->bus_id); + return 0; + case 0: + wss_config[n] |= WSS_CONFIG_DMA_0; + break; + case 1: + wss_config[n] |= WSS_CONFIG_DMA_1; + break; + case 3: + wss_config[n] |= WSS_CONFIG_DMA_3; + break; + default: + snd_printk(KERN_ERR "%s: invalid playback DMA %d\n", + dev->bus_id, dma1[n]); + return 0; + } + + if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) { + dma2[n] = -1; + goto mpu; + } + + wss_config[n] |= WSS_CONFIG_DUPLEX; + switch (dma2[n]) { + case 1: + if (dma1[n] == 0) + case 0: + break; + default: + snd_printk(KERN_ERR "%s: invalid capture DMA %d\n", + dev->bus_id, dma2[n]); + return 0; + } + +mpu: + switch (mpu_port[n]) { + case SNDRV_AUTO_PORT: + snd_printk(KERN_WARNING "%s: mpu_port not specified; not using " + "MPU-401\n", dev->bus_id); + mpu_port[n] = -1; + goto fm; + case 0x300: + config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300; + break; + case 0x330: + config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330; + break; + default: + snd_printk(KERN_ERR "%s: invalid mpu port %#lx\n", dev->bus_id, + mpu_port[n]); + return 0; + } + + if (mpu_irq[n] == irq[n]) { + snd_printk(KERN_ERR "%s: cannot share IRQ between WSS and " + "MPU-401\n", dev->bus_id); + return 0; + } + + switch (mpu_irq[n]) { + case SNDRV_AUTO_IRQ: + snd_printk(KERN_WARNING "%s: mpu_irq not specified; using " + "polling mode\n", dev->bus_id); + mpu_irq[n] = -1; + break; + case 2: + case 9: + config[n] |= GALAXY_CONFIG_MPUIRQ_2; + break; + case 5: + config[n] |= GALAXY_CONFIG_MPUIRQ_5; + break; + case 7: + config[n] |= GALAXY_CONFIG_MPUIRQ_7; + break; + case 10: + config[n] |= GALAXY_CONFIG_MPUIRQ_10; + break; + default: + snd_printk(KERN_ERR "%s: invalid mpu irq %d\n", dev->bus_id, + irq[n]); + return 0; + } + +fm: + switch (fm_port[n]) { + case SNDRV_AUTO_PORT: + snd_printk(KERN_WARNING "%s: fm_port not specified: not using " + "OPL3\n", dev->bus_id); + fm_port[n] = -1; + break; + case 0x388: + break; + default: + snd_printk(KERN_ERR "%s: illegal fm port %#lx\n", dev->bus_id, + fm_port[n]); + return 0; + } + + config[n] |= GALAXY_CONFIG_GAME_ENABLE; + return 1; +} + +static int __devinit __reset(struct snd_galaxy *galaxy) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(100); + + iowrite8(1, galaxy->port + DSP_PORT_RESET); + udelay(10); + iowrite8(0, galaxy->port + DSP_PORT_RESET); + udelay(10); + + while (!(ioread8(galaxy->port + DSP_PORT_DATA_AVAIL) & 0x80)) { + if (time_after(jiffies, timeout)) + return -EIO; + cpu_relax(); + } + if (ioread8(galaxy->port + DSP_PORT_READ) != DSP_SIGNATURE) + return -ENODEV; + + return 0; +} + +static int __devinit __command(struct snd_galaxy *galaxy, unsigned char cmd) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(100); + + while (ioread8(galaxy->port + DSP_PORT_STATUS) & 0x80) { + if (time_after(jiffies, timeout)) + return -EIO; + cpu_relax(); + } + iowrite8(cmd, galaxy->port + DSP_PORT_COMMAND); + + return 0; +} + +static int __devinit snd_galaxy_set(struct snd_galaxy *galaxy, + unsigned char cmd, int set) +{ + int err; + + err = __command(galaxy, DSP_COMMAND_GALAXY); + if (err < 0) + return err; + + err = __command(galaxy, cmd); + if (err < 0) + return err; + + err = __command(galaxy, set ? 255 : 0); + + return err; +} + +static u32 __devinit __get_config(struct snd_galaxy *galaxy) +{ + return ioread32be(galaxy->config_port); +} + +static void __devinit __set_config(struct snd_galaxy *galaxy, u32 config) +{ + unsigned char tmp = ioread8(galaxy->config_port + 4); + + iowrite8(tmp | 0x80, galaxy->config_port + 4); + udelay(10); + iowrite32be(config, galaxy->config_port); + udelay(10); + iowrite8(tmp & 0x7f, galaxy->config_port + 4); +} + +static int __devinit snd_galaxy_config(struct snd_galaxy *galaxy, u32 config) +{ + int err; + + err = snd_galaxy_set(galaxy, GALAXY_COMMAND_MPU, + config & GALAXY_CONFIG_MPU_ENABLE); + if (err < 0) + return err; + + err = snd_galaxy_set(galaxy, GALAXY_COMMAND_MPUA, + config & GALAXY_CONFIG_MPUA_330); + if (err < 0) + return err; + + galaxy->config = __get_config(galaxy); + __set_config(galaxy, (galaxy->config & GALAXY_CONFIG_MASK) | config); + + msleep(100); + return 0; +} + +static int __devinit snd_galaxy_setmode(struct snd_galaxy *galaxy, int mode) +{ + int err; + + err = __reset(galaxy); + if (err < 0) + return err; + + err = __command(galaxy, DSP_COMMAND_GALAXY); + if (err < 0) + return err; + + err = __command(galaxy, mode); + if (err < 0) + return err; + + msleep(100); + return 0; +} + +static unsigned char __devinit __wss_signature(struct snd_galaxy *galaxy) +{ + return ioread8(galaxy->wss_port + WSS_PORT_SIGNATURE) & 0x3f; +} + +static void __devinit __wss_config(struct snd_galaxy *galaxy, u8 wss_config) +{ + iowrite8(wss_config, galaxy->wss_port + WSS_PORT_CONFIG); +} + +static int __devinit snd_galaxy_wss_config(struct snd_galaxy *galaxy, + u8 wss_config) +{ + if (__wss_signature(galaxy) != WSS_SIGNATURE) + return -ENODEV; + + __wss_config(galaxy, wss_config); + + msleep(100); + return 0; +} + +static void snd_galaxy_free(struct snd_card *card) +{ + struct snd_galaxy *galaxy = card->private_data; + + if (galaxy->wss_port) { + __wss_config(galaxy, 0); + ioport_unmap(galaxy->wss_port); + release_and_free_resource(galaxy->res_wss_port); + } + if (galaxy->config_port) { + __set_config(galaxy, galaxy->config); + ioport_unmap(galaxy->config_port); + release_and_free_resource(galaxy->res_config_port); + } + if (galaxy->port) { + ioport_unmap(galaxy->port); + release_and_free_resource(galaxy->res_port); + } +} + +static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n) +{ + struct snd_galaxy *galaxy; + struct snd_cs4231 *chip; + struct snd_card *card; + int err; + + card = snd_card_new(index[n], id[n], THIS_MODULE, sizeof *galaxy); + if (!card) + return -EINVAL; + + snd_card_set_dev(card, dev); + + card->private_free = snd_galaxy_free; + galaxy = card->private_data; + + galaxy->res_port = request_region(port[n], 16, DRV_NAME); + if (!galaxy->res_port) { + err = -EBUSY; + goto error; + } + galaxy->port = ioport_map(port[n], 16); + + err = __reset(galaxy); + if (err < 0) { + snd_printd("%s: DSP not found at %#lx\n", dev->bus_id, port[n]); + goto error; + } + + galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG, + 16, DRV_NAME); + if (!galaxy->res_config_port) { + err = -EBUSY; + goto error; + } + galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16); + + err = snd_galaxy_config(galaxy, config[n]); + if (err < 0) + goto error; + + galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME); + if (!galaxy->res_wss_port) { + err = -EBUSY; + goto error; + } + galaxy->wss_port = ioport_map(wss_port[n], 4); + + err = snd_galaxy_wss_config(galaxy, wss_config[n]); + if (err < 0) { + snd_printd("%s: WSS not found at %#lx\n", dev->bus_id, + wss_port[n]); + goto error; + } + + err = snd_galaxy_setmode(galaxy, GALAXY_MODE_WSS); + if (err < 0) + goto error; + + strcpy(card->driver, DRV_NAME); + strcpy(card->shortname, DRV_NAME); + + err = snd_cs4231_create(card, wss_port[n] + 4, -1, irq[n], dma1[n], + dma2[n], CS4231_HW_DETECT, 0, &chip); + if (err < 0) + return err; + + sprintf(card->longname, "%s at %#lx, irq %d, dma %d/%d", + card->shortname, chip->port, chip->irq, chip->dma1, chip->dma2); + + err = snd_cs4231_pcm(chip, 0, NULL); + if (err < 0) + return err; + + err = snd_cs4231_mixer(chip); + if (err < 0) + return err; + + err = snd_cs4231_timer(chip, 0, NULL); + if (err < 0) + return err; + + if (mpu_port[n] >= 0) { + err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, + mpu_port[n], 0, mpu_irq[n], + IRQF_DISABLED, NULL); + if (err < 0) + goto error; + } + + if (fm_port[n] >= 0) { + struct snd_opl3 *opl3; + + err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2, + OPL3_HW_AUTO, 0, &opl3); + if (err < 0) { + snd_printk(KERN_ERR "%s: no OPL device at %#lx\n", + dev->bus_id, fm_port[n]); + goto error; + } + err = snd_opl3_timer_new(opl3, 1, 2); + if (err < 0) + goto error; + + err = snd_opl3_hwdep_new(opl3, 0, 1, NULL); + if (err < 0) + goto error; + } + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(dev, card); + return 0; + +error: + snd_card_free(card); + return err; +} + +static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n) +{ + snd_card_free(dev_get_drvdata(dev)); + dev_set_drvdata(dev, NULL); + return 0; +} + +static struct isa_driver snd_galaxy_driver = { + .match = snd_galaxy_match, + .probe = snd_galaxy_probe, + .remove = __devexit_p(snd_galaxy_remove), + + .driver = { + .name = DEV_NAME + } +}; + +static int __init alsa_card_galaxy_init(void) +{ + return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS); +} + +static void __exit alsa_card_galaxy_exit(void) +{ + isa_unregister_driver(&snd_galaxy_driver); +} + +module_init(alsa_card_galaxy_init); +module_exit(alsa_card_galaxy_exit);