18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Gravis UltraSound Extreme soundcards 48c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/isa.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/time.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <asm/dma.h> 148c2ecf20Sopenharmony_ci#include <sound/core.h> 158c2ecf20Sopenharmony_ci#include <sound/gus.h> 168c2ecf20Sopenharmony_ci#include <sound/es1688.h> 178c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 188c2ecf20Sopenharmony_ci#include <sound/opl3.h> 198c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_AUTO_PROBE 208c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 218c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 228c2ecf20Sopenharmony_ci#include <sound/initval.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define CRD_NAME "Gravis UltraSound Extreme" 258c2ecf20Sopenharmony_ci#define DEV_NAME "gusextreme" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(CRD_NAME); 288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 308c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Extreme}}"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 338c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 348c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ 358c2ecf20Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */ 368c2ecf20Sopenharmony_cistatic long gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */ 378c2ecf20Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */ 388c2ecf20Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ 398c2ecf20Sopenharmony_cistatic int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */ 408c2ecf20Sopenharmony_cistatic int gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ 418c2ecf20Sopenharmony_cistatic int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */ 428c2ecf20Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; 438c2ecf20Sopenharmony_cistatic int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; 448c2ecf20Sopenharmony_ci /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ 458c2ecf20Sopenharmony_cistatic int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; 468c2ecf20Sopenharmony_cistatic int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard."); 508c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard."); 528c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard."); 548c2ecf20Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444); 558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver."); 568c2ecf20Sopenharmony_cimodule_param_hw_array(gf1_port, long, ioport, NULL, 0444); 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gf1_port, "GF1 port # for " CRD_NAME " driver (optional)."); 588c2ecf20Sopenharmony_cimodule_param_hw_array(mpu_port, long, ioport, NULL, 0444); 598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver."); 608c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444); 618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver."); 628c2ecf20Sopenharmony_cimodule_param_hw_array(mpu_irq, int, irq, NULL, 0444); 638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver."); 648c2ecf20Sopenharmony_cimodule_param_hw_array(gf1_irq, int, irq, NULL, 0444); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for " CRD_NAME " driver."); 668c2ecf20Sopenharmony_cimodule_param_hw_array(dma8, int, dma, NULL, 0444); 678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver."); 688c2ecf20Sopenharmony_cimodule_param_hw_array(dma1, int, dma, NULL, 0444); 698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma1, "GF1 DMA # for " CRD_NAME " driver."); 708c2ecf20Sopenharmony_cimodule_param_array(joystick_dac, int, NULL, 0444); 718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver."); 728c2ecf20Sopenharmony_cimodule_param_array(channels, int, NULL, 0444); 738c2ecf20Sopenharmony_ciMODULE_PARM_DESC(channels, "GF1 channels for " CRD_NAME " driver."); 748c2ecf20Sopenharmony_cimodule_param_array(pcm_channels, int, NULL, 0444); 758c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for " CRD_NAME " driver."); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int snd_gusextreme_match(struct device *dev, unsigned int n) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return enable[n]; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int snd_gusextreme_es1688_create(struct snd_card *card, 838c2ecf20Sopenharmony_ci struct snd_es1688 *chip, 848c2ecf20Sopenharmony_ci struct device *dev, unsigned int n) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci static const long possible_ports[] = {0x220, 0x240, 0x260}; 878c2ecf20Sopenharmony_ci static const int possible_irqs[] = {5, 9, 10, 7, -1}; 888c2ecf20Sopenharmony_ci static const int possible_dmas[] = {1, 3, 0, -1}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci int i, error; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (irq[n] == SNDRV_AUTO_IRQ) { 938c2ecf20Sopenharmony_ci irq[n] = snd_legacy_find_free_irq(possible_irqs); 948c2ecf20Sopenharmony_ci if (irq[n] < 0) { 958c2ecf20Sopenharmony_ci dev_err(dev, "unable to find a free IRQ for ES1688\n"); 968c2ecf20Sopenharmony_ci return -EBUSY; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci if (dma8[n] == SNDRV_AUTO_DMA) { 1008c2ecf20Sopenharmony_ci dma8[n] = snd_legacy_find_free_dma(possible_dmas); 1018c2ecf20Sopenharmony_ci if (dma8[n] < 0) { 1028c2ecf20Sopenharmony_ci dev_err(dev, "unable to find a free DMA for ES1688\n"); 1038c2ecf20Sopenharmony_ci return -EBUSY; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (port[n] != SNDRV_AUTO_PORT) 1088c2ecf20Sopenharmony_ci return snd_es1688_create(card, chip, port[n], mpu_port[n], 1098c2ecf20Sopenharmony_ci irq[n], mpu_irq[n], dma8[n], ES1688_HW_1688); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci i = 0; 1128c2ecf20Sopenharmony_ci do { 1138c2ecf20Sopenharmony_ci port[n] = possible_ports[i]; 1148c2ecf20Sopenharmony_ci error = snd_es1688_create(card, chip, port[n], mpu_port[n], 1158c2ecf20Sopenharmony_ci irq[n], mpu_irq[n], dma8[n], ES1688_HW_1688); 1168c2ecf20Sopenharmony_ci } while (error < 0 && ++i < ARRAY_SIZE(possible_ports)); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return error; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int snd_gusextreme_gus_card_create(struct snd_card *card, 1228c2ecf20Sopenharmony_ci struct device *dev, unsigned int n, 1238c2ecf20Sopenharmony_ci struct snd_gus_card **rgus) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci static const int possible_irqs[] = {11, 12, 15, 9, 5, 7, 3, -1}; 1268c2ecf20Sopenharmony_ci static const int possible_dmas[] = {5, 6, 7, 3, 1, -1}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (gf1_irq[n] == SNDRV_AUTO_IRQ) { 1298c2ecf20Sopenharmony_ci gf1_irq[n] = snd_legacy_find_free_irq(possible_irqs); 1308c2ecf20Sopenharmony_ci if (gf1_irq[n] < 0) { 1318c2ecf20Sopenharmony_ci dev_err(dev, "unable to find a free IRQ for GF1\n"); 1328c2ecf20Sopenharmony_ci return -EBUSY; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci if (dma1[n] == SNDRV_AUTO_DMA) { 1368c2ecf20Sopenharmony_ci dma1[n] = snd_legacy_find_free_dma(possible_dmas); 1378c2ecf20Sopenharmony_ci if (dma1[n] < 0) { 1388c2ecf20Sopenharmony_ci dev_err(dev, "unable to find a free DMA for GF1\n"); 1398c2ecf20Sopenharmony_ci return -EBUSY; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci return snd_gus_create(card, gf1_port[n], gf1_irq[n], dma1[n], -1, 1438c2ecf20Sopenharmony_ci 0, channels[n], pcm_channels[n], 0, rgus); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int snd_gusextreme_detect(struct snd_gus_card *gus, 1478c2ecf20Sopenharmony_ci struct snd_es1688 *es1688) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci unsigned long flags; 1508c2ecf20Sopenharmony_ci unsigned char d; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * This is main stuff - enable access to GF1 chip... 1548c2ecf20Sopenharmony_ci * I'm not sure, if this will work for card which have 1558c2ecf20Sopenharmony_ci * ES1688 chip in another place than 0x220. 1568c2ecf20Sopenharmony_ci * 1578c2ecf20Sopenharmony_ci * I used reverse-engineering in DOSEMU. [--jk] 1588c2ecf20Sopenharmony_ci * 1598c2ecf20Sopenharmony_ci * ULTRINIT.EXE: 1608c2ecf20Sopenharmony_ci * 0x230 = 0,2,3 1618c2ecf20Sopenharmony_ci * 0x240 = 2,0,1 1628c2ecf20Sopenharmony_ci * 0x250 = 2,0,3 1638c2ecf20Sopenharmony_ci * 0x260 = 2,2,1 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci spin_lock_irqsave(&es1688->mixer_lock, flags); 1678c2ecf20Sopenharmony_ci snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */ 1688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&es1688->mixer_lock, flags); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci spin_lock_irqsave(&es1688->reg_lock, flags); 1718c2ecf20Sopenharmony_ci outb(gus->gf1.port & 0x040 ? 2 : 0, ES1688P(es1688, INIT1)); 1728c2ecf20Sopenharmony_ci outb(0, 0x201); 1738c2ecf20Sopenharmony_ci outb(gus->gf1.port & 0x020 ? 2 : 0, ES1688P(es1688, INIT1)); 1748c2ecf20Sopenharmony_ci outb(0, 0x201); 1758c2ecf20Sopenharmony_ci outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1)); 1768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&es1688->reg_lock, flags); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci udelay(100); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ 1818c2ecf20Sopenharmony_ci if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { 1828c2ecf20Sopenharmony_ci snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); 1838c2ecf20Sopenharmony_ci return -EIO; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci udelay(160); 1868c2ecf20Sopenharmony_ci snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ 1878c2ecf20Sopenharmony_ci udelay(160); 1888c2ecf20Sopenharmony_ci if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { 1898c2ecf20Sopenharmony_ci snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); 1908c2ecf20Sopenharmony_ci return -EIO; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int snd_gusextreme_mixer(struct snd_card *card) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct snd_ctl_elem_id id1, id2; 1998c2ecf20Sopenharmony_ci int error; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci memset(&id1, 0, sizeof(id1)); 2028c2ecf20Sopenharmony_ci memset(&id2, 0, sizeof(id2)); 2038c2ecf20Sopenharmony_ci id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* reassign AUX to SYNTHESIZER */ 2068c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Volume"); 2078c2ecf20Sopenharmony_ci strcpy(id2.name, "Synth Playback Volume"); 2088c2ecf20Sopenharmony_ci error = snd_ctl_rename_id(card, &id1, &id2); 2098c2ecf20Sopenharmony_ci if (error < 0) 2108c2ecf20Sopenharmony_ci return error; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* reassign Master Playback Switch to Synth Playback Switch */ 2138c2ecf20Sopenharmony_ci strcpy(id1.name, "Master Playback Switch"); 2148c2ecf20Sopenharmony_ci strcpy(id2.name, "Synth Playback Switch"); 2158c2ecf20Sopenharmony_ci error = snd_ctl_rename_id(card, &id1, &id2); 2168c2ecf20Sopenharmony_ci if (error < 0) 2178c2ecf20Sopenharmony_ci return error; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int snd_gusextreme_probe(struct device *dev, unsigned int n) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct snd_card *card; 2258c2ecf20Sopenharmony_ci struct snd_gus_card *gus; 2268c2ecf20Sopenharmony_ci struct snd_es1688 *es1688; 2278c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 2288c2ecf20Sopenharmony_ci int error; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci error = snd_card_new(dev, index[n], id[n], THIS_MODULE, 2318c2ecf20Sopenharmony_ci sizeof(struct snd_es1688), &card); 2328c2ecf20Sopenharmony_ci if (error < 0) 2338c2ecf20Sopenharmony_ci return error; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci es1688 = card->private_data; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (mpu_port[n] == SNDRV_AUTO_PORT) 2388c2ecf20Sopenharmony_ci mpu_port[n] = 0; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (mpu_irq[n] > 15) 2418c2ecf20Sopenharmony_ci mpu_irq[n] = -1; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci error = snd_gusextreme_es1688_create(card, es1688, dev, n); 2448c2ecf20Sopenharmony_ci if (error < 0) 2458c2ecf20Sopenharmony_ci goto out; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (gf1_port[n] < 0) 2488c2ecf20Sopenharmony_ci gf1_port[n] = es1688->port + 0x20; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci error = snd_gusextreme_gus_card_create(card, dev, n, &gus); 2518c2ecf20Sopenharmony_ci if (error < 0) 2528c2ecf20Sopenharmony_ci goto out; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci error = snd_gusextreme_detect(gus, es1688); 2558c2ecf20Sopenharmony_ci if (error < 0) 2568c2ecf20Sopenharmony_ci goto out; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci gus->joystick_dac = joystick_dac[n]; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci error = snd_gus_initialize(gus); 2618c2ecf20Sopenharmony_ci if (error < 0) 2628c2ecf20Sopenharmony_ci goto out; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci error = -ENODEV; 2658c2ecf20Sopenharmony_ci if (!gus->ess_flag) { 2668c2ecf20Sopenharmony_ci dev_err(dev, "GUS Extreme soundcard was not " 2678c2ecf20Sopenharmony_ci "detected at 0x%lx\n", gus->gf1.port); 2688c2ecf20Sopenharmony_ci goto out; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci gus->codec_flag = 1; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci error = snd_es1688_pcm(card, es1688, 0); 2738c2ecf20Sopenharmony_ci if (error < 0) 2748c2ecf20Sopenharmony_ci goto out; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci error = snd_es1688_mixer(card, es1688); 2778c2ecf20Sopenharmony_ci if (error < 0) 2788c2ecf20Sopenharmony_ci goto out; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci snd_component_add(card, "ES1688"); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (pcm_channels[n] > 0) { 2838c2ecf20Sopenharmony_ci error = snd_gf1_pcm_new(gus, 1, 1); 2848c2ecf20Sopenharmony_ci if (error < 0) 2858c2ecf20Sopenharmony_ci goto out; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci error = snd_gf1_new_mixer(gus); 2898c2ecf20Sopenharmony_ci if (error < 0) 2908c2ecf20Sopenharmony_ci goto out; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci error = snd_gusextreme_mixer(card); 2938c2ecf20Sopenharmony_ci if (error < 0) 2948c2ecf20Sopenharmony_ci goto out; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (snd_opl3_create(card, es1688->port, es1688->port + 2, 2978c2ecf20Sopenharmony_ci OPL3_HW_OPL3, 0, &opl3) < 0) 2988c2ecf20Sopenharmony_ci dev_warn(dev, "opl3 not detected at 0x%lx\n", es1688->port); 2998c2ecf20Sopenharmony_ci else { 3008c2ecf20Sopenharmony_ci error = snd_opl3_hwdep_new(opl3, 0, 2, NULL); 3018c2ecf20Sopenharmony_ci if (error < 0) 3028c2ecf20Sopenharmony_ci goto out; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (es1688->mpu_port >= 0x300) { 3068c2ecf20Sopenharmony_ci error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, 3078c2ecf20Sopenharmony_ci es1688->mpu_port, 0, mpu_irq[n], NULL); 3088c2ecf20Sopenharmony_ci if (error < 0) 3098c2ecf20Sopenharmony_ci goto out; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, " 3138c2ecf20Sopenharmony_ci "irq %i&%i, dma %i&%i", es1688->port, 3148c2ecf20Sopenharmony_ci gus->gf1.irq, es1688->irq, gus->gf1.dma1, es1688->dma8); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci error = snd_card_register(card); 3178c2ecf20Sopenharmony_ci if (error < 0) 3188c2ecf20Sopenharmony_ci goto out; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci dev_set_drvdata(dev, card); 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ciout: snd_card_free(card); 3248c2ecf20Sopenharmony_ci return error; 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int snd_gusextreme_remove(struct device *dev, unsigned int n) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci snd_card_free(dev_get_drvdata(dev)); 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic struct isa_driver snd_gusextreme_driver = { 3348c2ecf20Sopenharmony_ci .match = snd_gusextreme_match, 3358c2ecf20Sopenharmony_ci .probe = snd_gusextreme_probe, 3368c2ecf20Sopenharmony_ci .remove = snd_gusextreme_remove, 3378c2ecf20Sopenharmony_ci#if 0 /* FIXME */ 3388c2ecf20Sopenharmony_ci .suspend = snd_gusextreme_suspend, 3398c2ecf20Sopenharmony_ci .resume = snd_gusextreme_resume, 3408c2ecf20Sopenharmony_ci#endif 3418c2ecf20Sopenharmony_ci .driver = { 3428c2ecf20Sopenharmony_ci .name = DEV_NAME 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci}; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cimodule_isa_driver(snd_gusextreme_driver, SNDRV_CARDS); 347