1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * The driver for the Cirrus Logic's Sound Fusion CS46XX based soundcards 4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 5 */ 6 7 /* 8 NOTES: 9 - sometimes the sound is metallic and sibilant, unloading and 10 reloading the module may solve this. 11 */ 12 13 #include <linux/pci.h> 14 #include <linux/time.h> 15 #include <linux/init.h> 16 #include <linux/module.h> 17 #include <sound/core.h> 18 #include "cs46xx.h" 19 #include <sound/initval.h> 20 21 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 22 MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX"); 23 MODULE_LICENSE("GPL"); 24 25 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 26 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 27 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 28 static bool external_amp[SNDRV_CARDS]; 29 static bool thinkpad[SNDRV_CARDS]; 30 static bool mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 31 32 module_param_array(index, int, NULL, 0444); 33 MODULE_PARM_DESC(index, "Index value for the CS46xx soundcard."); 34 module_param_array(id, charp, NULL, 0444); 35 MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard."); 36 module_param_array(enable, bool, NULL, 0444); 37 MODULE_PARM_DESC(enable, "Enable CS46xx soundcard."); 38 module_param_array(external_amp, bool, NULL, 0444); 39 MODULE_PARM_DESC(external_amp, "Force to enable external amplifier."); 40 module_param_array(thinkpad, bool, NULL, 0444); 41 MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control."); 42 module_param_array(mmap_valid, bool, NULL, 0444); 43 MODULE_PARM_DESC(mmap_valid, "Support OSS mmap."); 44 45 static const struct pci_device_id snd_cs46xx_ids[] = { 46 { PCI_VDEVICE(CIRRUS, 0x6001), 0, }, /* CS4280 */ 47 { PCI_VDEVICE(CIRRUS, 0x6003), 0, }, /* CS4612 */ 48 { PCI_VDEVICE(CIRRUS, 0x6004), 0, }, /* CS4615 */ 49 { 0, } 50 }; 51 52 MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids); 53 54 static int snd_card_cs46xx_probe(struct pci_dev *pci, 55 const struct pci_device_id *pci_id) 56 { 57 static int dev; 58 struct snd_card *card; 59 struct snd_cs46xx *chip; 60 int err; 61 62 if (dev >= SNDRV_CARDS) 63 return -ENODEV; 64 if (!enable[dev]) { 65 dev++; 66 return -ENOENT; 67 } 68 69 err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 70 0, &card); 71 if (err < 0) 72 return err; 73 if ((err = snd_cs46xx_create(card, pci, 74 external_amp[dev], thinkpad[dev], 75 &chip)) < 0) { 76 snd_card_free(card); 77 return err; 78 } 79 card->private_data = chip; 80 chip->accept_valid = mmap_valid[dev]; 81 if ((err = snd_cs46xx_pcm(chip, 0)) < 0) { 82 snd_card_free(card); 83 return err; 84 } 85 #ifdef CONFIG_SND_CS46XX_NEW_DSP 86 if ((err = snd_cs46xx_pcm_rear(chip, 1)) < 0) { 87 snd_card_free(card); 88 return err; 89 } 90 if ((err = snd_cs46xx_pcm_iec958(chip, 2)) < 0) { 91 snd_card_free(card); 92 return err; 93 } 94 #endif 95 if ((err = snd_cs46xx_mixer(chip, 2)) < 0) { 96 snd_card_free(card); 97 return err; 98 } 99 #ifdef CONFIG_SND_CS46XX_NEW_DSP 100 if (chip->nr_ac97_codecs ==2) { 101 if ((err = snd_cs46xx_pcm_center_lfe(chip, 3)) < 0) { 102 snd_card_free(card); 103 return err; 104 } 105 } 106 #endif 107 if ((err = snd_cs46xx_midi(chip, 0)) < 0) { 108 snd_card_free(card); 109 return err; 110 } 111 if ((err = snd_cs46xx_start_dsp(chip)) < 0) { 112 snd_card_free(card); 113 return err; 114 } 115 116 117 snd_cs46xx_gameport(chip); 118 119 strcpy(card->driver, "CS46xx"); 120 strcpy(card->shortname, "Sound Fusion CS46xx"); 121 sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", 122 card->shortname, 123 chip->ba0_addr, 124 chip->ba1_addr, 125 chip->irq); 126 127 if ((err = snd_card_register(card)) < 0) { 128 snd_card_free(card); 129 return err; 130 } 131 132 pci_set_drvdata(pci, card); 133 dev++; 134 return 0; 135 } 136 137 static void snd_card_cs46xx_remove(struct pci_dev *pci) 138 { 139 snd_card_free(pci_get_drvdata(pci)); 140 } 141 142 static struct pci_driver cs46xx_driver = { 143 .name = KBUILD_MODNAME, 144 .id_table = snd_cs46xx_ids, 145 .probe = snd_card_cs46xx_probe, 146 .remove = snd_card_cs46xx_remove, 147 #ifdef CONFIG_PM_SLEEP 148 .driver = { 149 .pm = &snd_cs46xx_pm, 150 }, 151 #endif 152 }; 153 154 module_pci_driver(cs46xx_driver); 155