11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * ALSA driver for the Aureal Vortex family of soundprocessors. 31da177e4SLinus Torvalds * Author: Manuel Jander (mjander@embedded.cl) 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * This driver is the result of the OpenVortex Project from Savannah 61da177e4SLinus Torvalds * (savannah.nongnu.org/projects/openvortex). I would like to thank 71da177e4SLinus Torvalds * the developers of OpenVortex, Jeff Muizelaar and Kester Maddock, from 81da177e4SLinus Torvalds * whom i got plenty of help, and their codebase was invaluable. 91da177e4SLinus Torvalds * Thanks to the ALSA developers, they helped a lot working out 101da177e4SLinus Torvalds * the ALSA part. 111da177e4SLinus Torvalds * Thanks also to Sourceforge for maintaining the old binary drivers, 121da177e4SLinus Torvalds * and the forum, where developers could comunicate. 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * Now at least i can play Legacy DOOM with MIDI music :-) 151da177e4SLinus Torvalds */ 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds #include "au88x0.h" 181da177e4SLinus Torvalds #include <linux/init.h> 191da177e4SLinus Torvalds #include <linux/pci.h> 201da177e4SLinus Torvalds #include <linux/slab.h> 211da177e4SLinus Torvalds #include <linux/interrupt.h> 2265a77217SPaul Gortmaker #include <linux/module.h> 2358da3a23STobias Klauser #include <linux/dma-mapping.h> 241da177e4SLinus Torvalds #include <sound/initval.h> 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds // module parameters (see "Module Parameters") 271da177e4SLinus Torvalds static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 281da177e4SLinus Torvalds static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 29a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 301da177e4SLinus Torvalds static int pcifix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 255 }; 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds module_param_array(index, int, NULL, 0444); 331da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); 341da177e4SLinus Torvalds module_param_array(id, charp, NULL, 0444); 351da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); 361da177e4SLinus Torvalds module_param_array(enable, bool, NULL, 0444); 371da177e4SLinus Torvalds MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); 381da177e4SLinus Torvalds module_param_array(pcifix, int, NULL, 0444); 391da177e4SLinus Torvalds MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard."); 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds MODULE_DESCRIPTION("Aureal vortex"); 421da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 431da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}"); 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, snd_vortex_ids); 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds static void vortex_fix_latency(struct pci_dev *vortex) 481da177e4SLinus Torvalds { 491da177e4SLinus Torvalds int rc; 501da177e4SLinus Torvalds if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) { 511da177e4SLinus Torvalds printk(KERN_INFO CARD_NAME 521da177e4SLinus Torvalds ": vortex latency is 0xff\n"); 531da177e4SLinus Torvalds } else { 541da177e4SLinus Torvalds printk(KERN_WARNING CARD_NAME 551da177e4SLinus Torvalds ": could not set vortex latency: pci error 0x%x\n", rc); 561da177e4SLinus Torvalds } 571da177e4SLinus Torvalds } 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds static void vortex_fix_agp_bridge(struct pci_dev *via) 601da177e4SLinus Torvalds { 611da177e4SLinus Torvalds int rc; 621da177e4SLinus Torvalds u8 value; 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds /* 651da177e4SLinus Torvalds * only set the bit (Extend PCI#2 Internal Master for 661da177e4SLinus Torvalds * Efficient Handling of Dummy Requests) if the can 671da177e4SLinus Torvalds * read the config and it is not already set 681da177e4SLinus Torvalds */ 691da177e4SLinus Torvalds 701da177e4SLinus Torvalds if (!(rc = pci_read_config_byte(via, 0x42, &value)) 711da177e4SLinus Torvalds && ((value & 0x10) 721da177e4SLinus Torvalds || !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) { 731da177e4SLinus Torvalds printk(KERN_INFO CARD_NAME 741da177e4SLinus Torvalds ": bridge config is 0x%x\n", value | 0x10); 751da177e4SLinus Torvalds } else { 761da177e4SLinus Torvalds printk(KERN_WARNING CARD_NAME 771da177e4SLinus Torvalds ": could not set vortex latency: pci error 0x%x\n", rc); 781da177e4SLinus Torvalds } 791da177e4SLinus Torvalds } 801da177e4SLinus Torvalds 81e23e7a14SBill Pemberton static void snd_vortex_workaround(struct pci_dev *vortex, int fix) 821da177e4SLinus Torvalds { 830dd119f7SJiri Slaby struct pci_dev *via = NULL; 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds /* autodetect if workarounds are required */ 861da177e4SLinus Torvalds if (fix == 255) { 871da177e4SLinus Torvalds /* VIA KT133 */ 880dd119f7SJiri Slaby via = pci_get_device(PCI_VENDOR_ID_VIA, 890dd119f7SJiri Slaby PCI_DEVICE_ID_VIA_8365_1, NULL); 901da177e4SLinus Torvalds /* VIA Apollo */ 911da177e4SLinus Torvalds if (via == NULL) { 920dd119f7SJiri Slaby via = pci_get_device(PCI_VENDOR_ID_VIA, 930dd119f7SJiri Slaby PCI_DEVICE_ID_VIA_82C598_1, NULL); 941da177e4SLinus Torvalds /* AMD Irongate */ 950dd119f7SJiri Slaby if (via == NULL) 960dd119f7SJiri Slaby via = pci_get_device(PCI_VENDOR_ID_AMD, 970dd119f7SJiri Slaby PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL); 981da177e4SLinus Torvalds } 991da177e4SLinus Torvalds if (via) { 1001da177e4SLinus Torvalds printk(KERN_INFO CARD_NAME ": Activating latency workaround...\n"); 1011da177e4SLinus Torvalds vortex_fix_latency(vortex); 1021da177e4SLinus Torvalds vortex_fix_agp_bridge(via); 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds } else { 1051da177e4SLinus Torvalds if (fix & 0x1) 1061da177e4SLinus Torvalds vortex_fix_latency(vortex); 1070dd119f7SJiri Slaby if ((fix & 0x2) && (via = pci_get_device(PCI_VENDOR_ID_VIA, 1080dd119f7SJiri Slaby PCI_DEVICE_ID_VIA_8365_1, NULL))) 1091da177e4SLinus Torvalds vortex_fix_agp_bridge(via); 1100dd119f7SJiri Slaby if ((fix & 0x4) && (via = pci_get_device(PCI_VENDOR_ID_VIA, 1110dd119f7SJiri Slaby PCI_DEVICE_ID_VIA_82C598_1, NULL))) 1121da177e4SLinus Torvalds vortex_fix_agp_bridge(via); 1130dd119f7SJiri Slaby if ((fix & 0x8) && (via = pci_get_device(PCI_VENDOR_ID_AMD, 1140dd119f7SJiri Slaby PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL))) 1151da177e4SLinus Torvalds vortex_fix_agp_bridge(via); 1161da177e4SLinus Torvalds } 1170dd119f7SJiri Slaby pci_dev_put(via); 1181da177e4SLinus Torvalds } 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds // component-destructor 1211da177e4SLinus Torvalds // (see "Management of Cards and Components") 1222fd16874STakashi Iwai static int snd_vortex_dev_free(struct snd_device *device) 1231da177e4SLinus Torvalds { 1241da177e4SLinus Torvalds vortex_t *vortex = device->device_data; 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds vortex_gameport_unregister(vortex); 1271da177e4SLinus Torvalds vortex_core_shutdown(vortex); 1281da177e4SLinus Torvalds // Take down PCI interface. 1291da177e4SLinus Torvalds free_irq(vortex->irq, vortex); 1308a238c7bSAmol Lad iounmap(vortex->mmio); 1311da177e4SLinus Torvalds pci_release_regions(vortex->pci_dev); 1321da177e4SLinus Torvalds pci_disable_device(vortex->pci_dev); 1331da177e4SLinus Torvalds kfree(vortex); 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds return 0; 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds // chip-specific constructor 1391da177e4SLinus Torvalds // (see "Management of Cards and Components") 140e23e7a14SBill Pemberton static int 1412fd16874STakashi Iwai snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) 1421da177e4SLinus Torvalds { 1431da177e4SLinus Torvalds vortex_t *chip; 1441da177e4SLinus Torvalds int err; 1452fd16874STakashi Iwai static struct snd_device_ops ops = { 1461da177e4SLinus Torvalds .dev_free = snd_vortex_dev_free, 1471da177e4SLinus Torvalds }; 1481da177e4SLinus Torvalds 1491da177e4SLinus Torvalds *rchip = NULL; 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds // check PCI availability (DMA). 1521da177e4SLinus Torvalds if ((err = pci_enable_device(pci)) < 0) 1531da177e4SLinus Torvalds return err; 154284901a9SYang Hongyang if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 || 155284901a9SYang Hongyang pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { 1561da177e4SLinus Torvalds printk(KERN_ERR "error to set DMA mask\n"); 15797c67b65STakashi Iwai pci_disable_device(pci); 1581da177e4SLinus Torvalds return -ENXIO; 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 161e560d8d8STakashi Iwai chip = kzalloc(sizeof(*chip), GFP_KERNEL); 16297c67b65STakashi Iwai if (chip == NULL) { 16397c67b65STakashi Iwai pci_disable_device(pci); 1641da177e4SLinus Torvalds return -ENOMEM; 16597c67b65STakashi Iwai } 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds chip->card = card; 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds // initialize the stuff 1701da177e4SLinus Torvalds chip->pci_dev = pci; 1711da177e4SLinus Torvalds chip->io = pci_resource_start(pci, 0); 1721da177e4SLinus Torvalds chip->vendor = pci->vendor; 1731da177e4SLinus Torvalds chip->device = pci->device; 1741da177e4SLinus Torvalds chip->card = card; 1751da177e4SLinus Torvalds chip->irq = -1; 1761da177e4SLinus Torvalds 1771da177e4SLinus Torvalds // (1) PCI resource allocation 1781da177e4SLinus Torvalds // Get MMIO area 1791da177e4SLinus Torvalds // 1801da177e4SLinus Torvalds if ((err = pci_request_regions(pci, CARD_NAME_SHORT)) != 0) 1811da177e4SLinus Torvalds goto regions_out; 1821da177e4SLinus Torvalds 1832f5ad54eSArjan van de Ven chip->mmio = pci_ioremap_bar(pci, 0); 1841da177e4SLinus Torvalds if (!chip->mmio) { 1851da177e4SLinus Torvalds printk(KERN_ERR "MMIO area remap failed.\n"); 1861da177e4SLinus Torvalds err = -ENOMEM; 1871da177e4SLinus Torvalds goto ioremap_out; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds /* Init audio core. 1911da177e4SLinus Torvalds * This must be done before we do request_irq otherwise we can get spurious 1923a4fa0a2SRobert P. J. Day * interrupts that we do not handle properly and make a mess of things */ 1931da177e4SLinus Torvalds if ((err = vortex_core_init(chip)) != 0) { 1941da177e4SLinus Torvalds printk(KERN_ERR "hw core init failed\n"); 1951da177e4SLinus Torvalds goto core_out; 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds if ((err = request_irq(pci->irq, vortex_interrupt, 199934c2b6dSTakashi Iwai IRQF_SHARED, KBUILD_MODNAME, 2001da177e4SLinus Torvalds chip)) != 0) { 2011da177e4SLinus Torvalds printk(KERN_ERR "cannot grab irq\n"); 2021da177e4SLinus Torvalds goto irq_out; 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds chip->irq = pci->irq; 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds pci_set_master(pci); 2071da177e4SLinus Torvalds // End of PCI setup. 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds // Register alsa root device. 2101da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { 2111da177e4SLinus Torvalds goto alloc_out; 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds 21497c67b65STakashi Iwai snd_card_set_dev(card, &pci->dev); 21597c67b65STakashi Iwai 2161da177e4SLinus Torvalds *rchip = chip; 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds return 0; 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds alloc_out: 2211da177e4SLinus Torvalds free_irq(chip->irq, chip); 2221da177e4SLinus Torvalds irq_out: 2231da177e4SLinus Torvalds vortex_core_shutdown(chip); 2241da177e4SLinus Torvalds core_out: 2251da177e4SLinus Torvalds iounmap(chip->mmio); 2261da177e4SLinus Torvalds ioremap_out: 2271da177e4SLinus Torvalds pci_release_regions(chip->pci_dev); 2281da177e4SLinus Torvalds regions_out: 2291da177e4SLinus Torvalds pci_disable_device(chip->pci_dev); 2301da177e4SLinus Torvalds //FIXME: this not the right place to unregister the gameport 2311da177e4SLinus Torvalds vortex_gameport_unregister(chip); 2326ed44ad3SJesper Juhl kfree(chip); 2331da177e4SLinus Torvalds return err; 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds // constructor -- see "Constructor" sub-section 237e23e7a14SBill Pemberton static int 2381da177e4SLinus Torvalds snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) 2391da177e4SLinus Torvalds { 2401da177e4SLinus Torvalds static int dev; 2412fd16874STakashi Iwai struct snd_card *card; 2421da177e4SLinus Torvalds vortex_t *chip; 2431da177e4SLinus Torvalds int err; 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds // (1) 2461da177e4SLinus Torvalds if (dev >= SNDRV_CARDS) 2471da177e4SLinus Torvalds return -ENODEV; 2481da177e4SLinus Torvalds if (!enable[dev]) { 2491da177e4SLinus Torvalds dev++; 2501da177e4SLinus Torvalds return -ENOENT; 2511da177e4SLinus Torvalds } 2521da177e4SLinus Torvalds // (2) 253e58de7baSTakashi Iwai err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); 254e58de7baSTakashi Iwai if (err < 0) 255e58de7baSTakashi Iwai return err; 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds // (3) 2581da177e4SLinus Torvalds if ((err = snd_vortex_create(card, pci, &chip)) < 0) { 2591da177e4SLinus Torvalds snd_card_free(card); 2601da177e4SLinus Torvalds return err; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds snd_vortex_workaround(pci, pcifix[dev]); 263520290e4SAlan Horstmann 264520290e4SAlan Horstmann // Card details needed in snd_vortex_midi 265520290e4SAlan Horstmann strcpy(card->driver, CARD_NAME_SHORT); 266520290e4SAlan Horstmann sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT); 267520290e4SAlan Horstmann sprintf(card->longname, "%s at 0x%lx irq %i", 268520290e4SAlan Horstmann card->shortname, chip->io, chip->irq); 269520290e4SAlan Horstmann 2701da177e4SLinus Torvalds // (4) Alloc components. 2717b32486cSRaymond Yau err = snd_vortex_mixer(chip); 2727b32486cSRaymond Yau if (err < 0) { 2737b32486cSRaymond Yau snd_card_free(card); 2747b32486cSRaymond Yau return err; 2757b32486cSRaymond Yau } 2761da177e4SLinus Torvalds // ADB pcm. 27749b9c40eSRaymond Yau err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_PCM); 27849b9c40eSRaymond Yau if (err < 0) { 2791da177e4SLinus Torvalds snd_card_free(card); 2801da177e4SLinus Torvalds return err; 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds #ifndef CHIP_AU8820 2831da177e4SLinus Torvalds // ADB SPDIF 2841da177e4SLinus Torvalds if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, 1)) < 0) { 2851da177e4SLinus Torvalds snd_card_free(card); 2861da177e4SLinus Torvalds return err; 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds // A3D 2891da177e4SLinus Torvalds if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D)) < 0) { 2901da177e4SLinus Torvalds snd_card_free(card); 2911da177e4SLinus Torvalds return err; 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds #endif 2941da177e4SLinus Torvalds /* 2951da177e4SLinus Torvalds // ADB I2S 2961da177e4SLinus Torvalds if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) { 2971da177e4SLinus Torvalds snd_card_free(card); 2981da177e4SLinus Torvalds return err; 2991da177e4SLinus Torvalds } 3001da177e4SLinus Torvalds */ 3011da177e4SLinus Torvalds #ifndef CHIP_AU8810 3021da177e4SLinus Torvalds // WT pcm. 3031da177e4SLinus Torvalds if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT)) < 0) { 3041da177e4SLinus Torvalds snd_card_free(card); 3051da177e4SLinus Torvalds return err; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds #endif 3081da177e4SLinus Torvalds if ((err = snd_vortex_midi(chip)) < 0) { 3091da177e4SLinus Torvalds snd_card_free(card); 3101da177e4SLinus Torvalds return err; 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds vortex_gameport_register(chip); 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds #if 0 3161da177e4SLinus Torvalds if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH, 3171da177e4SLinus Torvalds sizeof(snd_vortex_synth_arg_t), &wave) < 0 3181da177e4SLinus Torvalds || wave == NULL) { 31999b359baSTakashi Iwai snd_printk(KERN_ERR "Can't initialize Aureal wavetable synth\n"); 3201da177e4SLinus Torvalds } else { 3211da177e4SLinus Torvalds snd_vortex_synth_arg_t *arg; 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); 3241da177e4SLinus Torvalds strcpy(wave->name, "Aureal Synth"); 3251da177e4SLinus Torvalds arg->hwptr = vortex; 3261da177e4SLinus Torvalds arg->index = 1; 3271da177e4SLinus Torvalds arg->seq_ports = seq_ports[dev]; 3281da177e4SLinus Torvalds arg->max_voices = max_synth_voices[dev]; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds #endif 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds // (5) 3331da177e4SLinus Torvalds if ((err = pci_read_config_word(pci, PCI_DEVICE_ID, 3341da177e4SLinus Torvalds &(chip->device))) < 0) { 3351da177e4SLinus Torvalds snd_card_free(card); 3361da177e4SLinus Torvalds return err; 3371da177e4SLinus Torvalds } 3381da177e4SLinus Torvalds if ((err = pci_read_config_word(pci, PCI_VENDOR_ID, 3391da177e4SLinus Torvalds &(chip->vendor))) < 0) { 3401da177e4SLinus Torvalds snd_card_free(card); 3411da177e4SLinus Torvalds return err; 3421da177e4SLinus Torvalds } 34344c10138SAuke Kok chip->rev = pci->revision; 3441da177e4SLinus Torvalds #ifdef CHIP_AU8830 3451da177e4SLinus Torvalds if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) { 3461da177e4SLinus Torvalds printk(KERN_ALERT 3471da177e4SLinus Torvalds "vortex: The revision (%x) of your card has not been seen before.\n", 3481da177e4SLinus Torvalds chip->rev); 3491da177e4SLinus Torvalds printk(KERN_ALERT 3501da177e4SLinus Torvalds "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n"); 3511da177e4SLinus Torvalds snd_card_free(card); 3521da177e4SLinus Torvalds err = -ENODEV; 3531da177e4SLinus Torvalds return err; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds #endif 3561da177e4SLinus Torvalds 3571da177e4SLinus Torvalds // (6) 3581da177e4SLinus Torvalds if ((err = snd_card_register(card)) < 0) { 3591da177e4SLinus Torvalds snd_card_free(card); 3601da177e4SLinus Torvalds return err; 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds // (7) 3631da177e4SLinus Torvalds pci_set_drvdata(pci, card); 3641da177e4SLinus Torvalds dev++; 3651da177e4SLinus Torvalds vortex_connect_default(chip, 1); 3661da177e4SLinus Torvalds vortex_enable_int(chip); 3671da177e4SLinus Torvalds return 0; 3681da177e4SLinus Torvalds } 3691da177e4SLinus Torvalds 3701da177e4SLinus Torvalds // destructor -- see "Destructor" sub-section 371e23e7a14SBill Pemberton static void snd_vortex_remove(struct pci_dev *pci) 3721da177e4SLinus Torvalds { 3731da177e4SLinus Torvalds snd_card_free(pci_get_drvdata(pci)); 3741da177e4SLinus Torvalds pci_set_drvdata(pci, NULL); 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds 3771da177e4SLinus Torvalds // pci_driver definition 378e9f66d9bSTakashi Iwai static struct pci_driver vortex_driver = { 3793733e424STakashi Iwai .name = KBUILD_MODNAME, 3801da177e4SLinus Torvalds .id_table = snd_vortex_ids, 3811da177e4SLinus Torvalds .probe = snd_vortex_probe, 382e23e7a14SBill Pemberton .remove = snd_vortex_remove, 3831da177e4SLinus Torvalds }; 3841da177e4SLinus Torvalds 385e9f66d9bSTakashi Iwai module_pci_driver(vortex_driver); 386