18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Gravis UltraSound MAX soundcard 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/wss.h> 178c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ 188c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA 198c2ecf20Sopenharmony_ci#include <sound/initval.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Gravis UltraSound MAX"); 238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 248c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound MAX}}"); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 278c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 288c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */ 298c2ecf20Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */ 308c2ecf20Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */ 318c2ecf20Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ 328c2ecf20Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ 338c2ecf20Sopenharmony_cistatic int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; 348c2ecf20Sopenharmony_ci /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ 358c2ecf20Sopenharmony_cistatic int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24}; 368c2ecf20Sopenharmony_cistatic int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for GUS MAX soundcard."); 408c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 418c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for GUS MAX soundcard."); 428c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable GUS MAX soundcard."); 448c2ecf20Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444); 458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for GUS MAX driver."); 468c2ecf20Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444); 478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for GUS MAX driver."); 488c2ecf20Sopenharmony_cimodule_param_hw_array(dma1, int, dma, NULL, 0444); 498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma1, "DMA1 # for GUS MAX driver."); 508c2ecf20Sopenharmony_cimodule_param_hw_array(dma2, int, dma, NULL, 0444); 518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma2, "DMA2 # for GUS MAX driver."); 528c2ecf20Sopenharmony_cimodule_param_array(joystick_dac, int, NULL, 0444); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver."); 548c2ecf20Sopenharmony_cimodule_param_array(channels, int, NULL, 0444); 558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(channels, "Used GF1 channels for GUS MAX driver."); 568c2ecf20Sopenharmony_cimodule_param_array(pcm_channels, int, NULL, 0444); 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS MAX driver."); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct snd_gusmax { 608c2ecf20Sopenharmony_ci int irq; 618c2ecf20Sopenharmony_ci struct snd_card *card; 628c2ecf20Sopenharmony_ci struct snd_gus_card *gus; 638c2ecf20Sopenharmony_ci struct snd_wss *wss; 648c2ecf20Sopenharmony_ci unsigned short gus_status_reg; 658c2ecf20Sopenharmony_ci unsigned short pcm_status_reg; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define PFX "gusmax: " 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int snd_gusmax_detect(struct snd_gus_card *gus) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci unsigned char d; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */ 758c2ecf20Sopenharmony_ci if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 0) { 768c2ecf20Sopenharmony_ci snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d); 778c2ecf20Sopenharmony_ci return -ENODEV; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci udelay(160); 808c2ecf20Sopenharmony_ci snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */ 818c2ecf20Sopenharmony_ci udelay(160); 828c2ecf20Sopenharmony_ci if (((d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET)) & 0x07) != 1) { 838c2ecf20Sopenharmony_ci snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d); 848c2ecf20Sopenharmony_ci return -ENODEV; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic irqreturn_t snd_gusmax_interrupt(int irq, void *dev_id) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct snd_gusmax *maxcard = dev_id; 938c2ecf20Sopenharmony_ci int loop, max = 5; 948c2ecf20Sopenharmony_ci int handled = 0; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci do { 978c2ecf20Sopenharmony_ci loop = 0; 988c2ecf20Sopenharmony_ci if (inb(maxcard->gus_status_reg)) { 998c2ecf20Sopenharmony_ci handled = 1; 1008c2ecf20Sopenharmony_ci snd_gus_interrupt(irq, maxcard->gus); 1018c2ecf20Sopenharmony_ci loop++; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */ 1048c2ecf20Sopenharmony_ci handled = 1; 1058c2ecf20Sopenharmony_ci snd_wss_interrupt(irq, maxcard->wss); 1068c2ecf20Sopenharmony_ci loop++; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci } while (loop && --max > 0); 1098c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void snd_gusmax_init(int dev, struct snd_card *card, 1138c2ecf20Sopenharmony_ci struct snd_gus_card *gus) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci gus->equal_irq = 1; 1168c2ecf20Sopenharmony_ci gus->codec_flag = 1; 1178c2ecf20Sopenharmony_ci gus->joystick_dac = joystick_dac[dev]; 1188c2ecf20Sopenharmony_ci /* init control register */ 1198c2ecf20Sopenharmony_ci gus->max_cntrl_val = (gus->gf1.port >> 4) & 0x0f; 1208c2ecf20Sopenharmony_ci if (gus->gf1.dma1 > 3) 1218c2ecf20Sopenharmony_ci gus->max_cntrl_val |= 0x10; 1228c2ecf20Sopenharmony_ci if (gus->gf1.dma2 > 3) 1238c2ecf20Sopenharmony_ci gus->max_cntrl_val |= 0x20; 1248c2ecf20Sopenharmony_ci gus->max_cntrl_val |= 0x40; 1258c2ecf20Sopenharmony_ci outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT)); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int snd_gusmax_mixer(struct snd_wss *chip) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct snd_card *card = chip->card; 1318c2ecf20Sopenharmony_ci struct snd_ctl_elem_id id1, id2; 1328c2ecf20Sopenharmony_ci int err; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci memset(&id1, 0, sizeof(id1)); 1358c2ecf20Sopenharmony_ci memset(&id2, 0, sizeof(id2)); 1368c2ecf20Sopenharmony_ci id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 1378c2ecf20Sopenharmony_ci /* reassign AUXA to SYNTHESIZER */ 1388c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); 1398c2ecf20Sopenharmony_ci strcpy(id2.name, "Synth Playback Switch"); 1408c2ecf20Sopenharmony_ci if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) 1418c2ecf20Sopenharmony_ci return err; 1428c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Volume"); 1438c2ecf20Sopenharmony_ci strcpy(id2.name, "Synth Playback Volume"); 1448c2ecf20Sopenharmony_ci if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci /* reassign AUXB to CD */ 1478c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; 1488c2ecf20Sopenharmony_ci strcpy(id2.name, "CD Playback Switch"); 1498c2ecf20Sopenharmony_ci if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) 1508c2ecf20Sopenharmony_ci return err; 1518c2ecf20Sopenharmony_ci strcpy(id1.name, "Aux Playback Volume"); 1528c2ecf20Sopenharmony_ci strcpy(id2.name, "CD Playback Volume"); 1538c2ecf20Sopenharmony_ci if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) 1548c2ecf20Sopenharmony_ci return err; 1558c2ecf20Sopenharmony_ci#if 0 1568c2ecf20Sopenharmony_ci /* reassign Mono Input to MIC */ 1578c2ecf20Sopenharmony_ci if (snd_mixer_group_rename(mixer, 1588c2ecf20Sopenharmony_ci SNDRV_MIXER_IN_MONO, 0, 1598c2ecf20Sopenharmony_ci SNDRV_MIXER_IN_MIC, 0) < 0) 1608c2ecf20Sopenharmony_ci goto __error; 1618c2ecf20Sopenharmony_ci if (snd_mixer_elem_rename(mixer, 1628c2ecf20Sopenharmony_ci SNDRV_MIXER_IN_MONO, 0, SNDRV_MIXER_ETYPE_INPUT, 1638c2ecf20Sopenharmony_ci SNDRV_MIXER_IN_MIC, 0) < 0) 1648c2ecf20Sopenharmony_ci goto __error; 1658c2ecf20Sopenharmony_ci if (snd_mixer_elem_rename(mixer, 1668c2ecf20Sopenharmony_ci "Mono Capture Volume", 0, SNDRV_MIXER_ETYPE_VOLUME1, 1678c2ecf20Sopenharmony_ci "Mic Capture Volume", 0) < 0) 1688c2ecf20Sopenharmony_ci goto __error; 1698c2ecf20Sopenharmony_ci if (snd_mixer_elem_rename(mixer, 1708c2ecf20Sopenharmony_ci "Mono Capture Switch", 0, SNDRV_MIXER_ETYPE_SWITCH1, 1718c2ecf20Sopenharmony_ci "Mic Capture Switch", 0) < 0) 1728c2ecf20Sopenharmony_ci goto __error; 1738c2ecf20Sopenharmony_ci#endif 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void snd_gusmax_free(struct snd_card *card) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct snd_gusmax *maxcard = card->private_data; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (maxcard == NULL) 1828c2ecf20Sopenharmony_ci return; 1838c2ecf20Sopenharmony_ci if (maxcard->irq >= 0) 1848c2ecf20Sopenharmony_ci free_irq(maxcard->irq, (void *)maxcard); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int snd_gusmax_match(struct device *pdev, unsigned int dev) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci return enable[dev]; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int snd_gusmax_probe(struct device *pdev, unsigned int dev) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1}; 1958c2ecf20Sopenharmony_ci static const int possible_dmas[] = {5, 6, 7, 1, 3, -1}; 1968c2ecf20Sopenharmony_ci int xirq, xdma1, xdma2, err; 1978c2ecf20Sopenharmony_ci struct snd_card *card; 1988c2ecf20Sopenharmony_ci struct snd_gus_card *gus = NULL; 1998c2ecf20Sopenharmony_ci struct snd_wss *wss; 2008c2ecf20Sopenharmony_ci struct snd_gusmax *maxcard; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci err = snd_card_new(pdev, index[dev], id[dev], THIS_MODULE, 2038c2ecf20Sopenharmony_ci sizeof(struct snd_gusmax), &card); 2048c2ecf20Sopenharmony_ci if (err < 0) 2058c2ecf20Sopenharmony_ci return err; 2068c2ecf20Sopenharmony_ci card->private_free = snd_gusmax_free; 2078c2ecf20Sopenharmony_ci maxcard = card->private_data; 2088c2ecf20Sopenharmony_ci maxcard->card = card; 2098c2ecf20Sopenharmony_ci maxcard->irq = -1; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci xirq = irq[dev]; 2128c2ecf20Sopenharmony_ci if (xirq == SNDRV_AUTO_IRQ) { 2138c2ecf20Sopenharmony_ci if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) { 2148c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free IRQ\n"); 2158c2ecf20Sopenharmony_ci err = -EBUSY; 2168c2ecf20Sopenharmony_ci goto _err; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci xdma1 = dma1[dev]; 2208c2ecf20Sopenharmony_ci if (xdma1 == SNDRV_AUTO_DMA) { 2218c2ecf20Sopenharmony_ci if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) { 2228c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free DMA1\n"); 2238c2ecf20Sopenharmony_ci err = -EBUSY; 2248c2ecf20Sopenharmony_ci goto _err; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci xdma2 = dma2[dev]; 2288c2ecf20Sopenharmony_ci if (xdma2 == SNDRV_AUTO_DMA) { 2298c2ecf20Sopenharmony_ci if ((xdma2 = snd_legacy_find_free_dma(possible_dmas)) < 0) { 2308c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to find a free DMA2\n"); 2318c2ecf20Sopenharmony_ci err = -EBUSY; 2328c2ecf20Sopenharmony_ci goto _err; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (port[dev] != SNDRV_AUTO_PORT) { 2378c2ecf20Sopenharmony_ci err = snd_gus_create(card, 2388c2ecf20Sopenharmony_ci port[dev], 2398c2ecf20Sopenharmony_ci -xirq, xdma1, xdma2, 2408c2ecf20Sopenharmony_ci 0, channels[dev], 2418c2ecf20Sopenharmony_ci pcm_channels[dev], 2428c2ecf20Sopenharmony_ci 0, &gus); 2438c2ecf20Sopenharmony_ci } else { 2448c2ecf20Sopenharmony_ci static const unsigned long possible_ports[] = { 2458c2ecf20Sopenharmony_ci 0x220, 0x230, 0x240, 0x250, 0x260 2468c2ecf20Sopenharmony_ci }; 2478c2ecf20Sopenharmony_ci int i; 2488c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(possible_ports); i++) { 2498c2ecf20Sopenharmony_ci err = snd_gus_create(card, 2508c2ecf20Sopenharmony_ci possible_ports[i], 2518c2ecf20Sopenharmony_ci -xirq, xdma1, xdma2, 2528c2ecf20Sopenharmony_ci 0, channels[dev], 2538c2ecf20Sopenharmony_ci pcm_channels[dev], 2548c2ecf20Sopenharmony_ci 0, &gus); 2558c2ecf20Sopenharmony_ci if (err >= 0) { 2568c2ecf20Sopenharmony_ci port[dev] = possible_ports[i]; 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci if (err < 0) 2628c2ecf20Sopenharmony_ci goto _err; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if ((err = snd_gusmax_detect(gus)) < 0) 2658c2ecf20Sopenharmony_ci goto _err; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci maxcard->gus_status_reg = gus->gf1.reg_irqstat; 2688c2ecf20Sopenharmony_ci maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2; 2698c2ecf20Sopenharmony_ci snd_gusmax_init(dev, card, gus); 2708c2ecf20Sopenharmony_ci if ((err = snd_gus_initialize(gus)) < 0) 2718c2ecf20Sopenharmony_ci goto _err; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (!gus->max_flag) { 2748c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port); 2758c2ecf20Sopenharmony_ci err = -ENODEV; 2768c2ecf20Sopenharmony_ci goto _err; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (request_irq(xirq, snd_gusmax_interrupt, 0, "GUS MAX", (void *)maxcard)) { 2808c2ecf20Sopenharmony_ci snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq); 2818c2ecf20Sopenharmony_ci err = -EBUSY; 2828c2ecf20Sopenharmony_ci goto _err; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci maxcard->irq = xirq; 2858c2ecf20Sopenharmony_ci card->sync_irq = maxcard->irq; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci err = snd_wss_create(card, 2888c2ecf20Sopenharmony_ci gus->gf1.port + 0x10c, -1, xirq, 2898c2ecf20Sopenharmony_ci xdma2 < 0 ? xdma1 : xdma2, xdma1, 2908c2ecf20Sopenharmony_ci WSS_HW_DETECT, 2918c2ecf20Sopenharmony_ci WSS_HWSHARE_IRQ | 2928c2ecf20Sopenharmony_ci WSS_HWSHARE_DMA1 | 2938c2ecf20Sopenharmony_ci WSS_HWSHARE_DMA2, 2948c2ecf20Sopenharmony_ci &wss); 2958c2ecf20Sopenharmony_ci if (err < 0) 2968c2ecf20Sopenharmony_ci goto _err; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci err = snd_wss_pcm(wss, 0); 2998c2ecf20Sopenharmony_ci if (err < 0) 3008c2ecf20Sopenharmony_ci goto _err; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci err = snd_wss_mixer(wss); 3038c2ecf20Sopenharmony_ci if (err < 0) 3048c2ecf20Sopenharmony_ci goto _err; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci err = snd_wss_timer(wss, 2); 3078c2ecf20Sopenharmony_ci if (err < 0) 3088c2ecf20Sopenharmony_ci goto _err; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (pcm_channels[dev] > 0) { 3118c2ecf20Sopenharmony_ci if ((err = snd_gf1_pcm_new(gus, 1, 1)) < 0) 3128c2ecf20Sopenharmony_ci goto _err; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci err = snd_gusmax_mixer(wss); 3158c2ecf20Sopenharmony_ci if (err < 0) 3168c2ecf20Sopenharmony_ci goto _err; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci err = snd_gf1_rawmidi_new(gus, 0); 3198c2ecf20Sopenharmony_ci if (err < 0) 3208c2ecf20Sopenharmony_ci goto _err; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1); 3238c2ecf20Sopenharmony_ci if (xdma2 >= 0) 3248c2ecf20Sopenharmony_ci sprintf(card->longname + strlen(card->longname), "&%i", xdma2); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci err = snd_card_register(card); 3278c2ecf20Sopenharmony_ci if (err < 0) 3288c2ecf20Sopenharmony_ci goto _err; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci maxcard->gus = gus; 3318c2ecf20Sopenharmony_ci maxcard->wss = wss; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci dev_set_drvdata(pdev, card); 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci _err: 3378c2ecf20Sopenharmony_ci snd_card_free(card); 3388c2ecf20Sopenharmony_ci return err; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int snd_gusmax_remove(struct device *devptr, unsigned int dev) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci snd_card_free(dev_get_drvdata(devptr)); 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci#define DEV_NAME "gusmax" 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic struct isa_driver snd_gusmax_driver = { 3508c2ecf20Sopenharmony_ci .match = snd_gusmax_match, 3518c2ecf20Sopenharmony_ci .probe = snd_gusmax_probe, 3528c2ecf20Sopenharmony_ci .remove = snd_gusmax_remove, 3538c2ecf20Sopenharmony_ci /* FIXME: suspend/resume */ 3548c2ecf20Sopenharmony_ci .driver = { 3558c2ecf20Sopenharmony_ci .name = DEV_NAME 3568c2ecf20Sopenharmony_ci }, 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cimodule_isa_driver(snd_gusmax_driver, SNDRV_CARDS); 360