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 MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,Sound Fusion (CS4280)}," 25 "{Cirrus Logic,Sound Fusion (CS4610)}," 26 "{Cirrus Logic,Sound Fusion (CS4612)}," 27 "{Cirrus Logic,Sound Fusion (CS4615)}," 28 "{Cirrus Logic,Sound Fusion (CS4622)}," 29 "{Cirrus Logic,Sound Fusion (CS4624)}," 30 "{Cirrus Logic,Sound Fusion (CS4630)}}"); 31 32 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 33 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 34 static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 35 static bool external_amp[SNDRV_CARDS]; 36 static bool thinkpad[SNDRV_CARDS]; 37 static bool mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 38 39 module_param_array(index, int, NULL, 0444); 40 MODULE_PARM_DESC(index, "Index value for the CS46xx soundcard."); 41 module_param_array(id, charp, NULL, 0444); 42 MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard."); 43 module_param_array(enable, bool, NULL, 0444); 44 MODULE_PARM_DESC(enable, "Enable CS46xx soundcard."); 45 module_param_array(external_amp, bool, NULL, 0444); 46 MODULE_PARM_DESC(external_amp, "Force to enable external amplifier."); 47 module_param_array(thinkpad, bool, NULL, 0444); 48 MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control."); 49 module_param_array(mmap_valid, bool, NULL, 0444); 50 MODULE_PARM_DESC(mmap_valid, "Support OSS mmap."); 51 52 static const struct pci_device_id snd_cs46xx_ids[] = { 53 { PCI_VDEVICE(CIRRUS, 0x6001), 0, }, /* CS4280 */ 54 { PCI_VDEVICE(CIRRUS, 0x6003), 0, }, /* CS4612 */ 55 { PCI_VDEVICE(CIRRUS, 0x6004), 0, }, /* CS4615 */ 56 { 0, } 57 }; 58 59 MODULE_DEVICE_TABLE(pci, snd_cs46xx_ids); 60 61 static int snd_card_cs46xx_probe(struct pci_dev *pci, 62 const struct pci_device_id *pci_id) 63 { 64 static int dev; 65 struct snd_card *card; 66 struct snd_cs46xx *chip; 67 int err; 68 69 if (dev >= SNDRV_CARDS) 70 return -ENODEV; 71 if (!enable[dev]) { 72 dev++; 73 return -ENOENT; 74 } 75 76 err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 77 0, &card); 78 if (err < 0) 79 return err; 80 if ((err = snd_cs46xx_create(card, pci, 81 external_amp[dev], thinkpad[dev], 82 &chip)) < 0) { 83 snd_card_free(card); 84 return err; 85 } 86 card->private_data = chip; 87 chip->accept_valid = mmap_valid[dev]; 88 if ((err = snd_cs46xx_pcm(chip, 0)) < 0) { 89 snd_card_free(card); 90 return err; 91 } 92 #ifdef CONFIG_SND_CS46XX_NEW_DSP 93 if ((err = snd_cs46xx_pcm_rear(chip, 1)) < 0) { 94 snd_card_free(card); 95 return err; 96 } 97 if ((err = snd_cs46xx_pcm_iec958(chip, 2)) < 0) { 98 snd_card_free(card); 99 return err; 100 } 101 #endif 102 if ((err = snd_cs46xx_mixer(chip, 2)) < 0) { 103 snd_card_free(card); 104 return err; 105 } 106 #ifdef CONFIG_SND_CS46XX_NEW_DSP 107 if (chip->nr_ac97_codecs ==2) { 108 if ((err = snd_cs46xx_pcm_center_lfe(chip, 3)) < 0) { 109 snd_card_free(card); 110 return err; 111 } 112 } 113 #endif 114 if ((err = snd_cs46xx_midi(chip, 0)) < 0) { 115 snd_card_free(card); 116 return err; 117 } 118 if ((err = snd_cs46xx_start_dsp(chip)) < 0) { 119 snd_card_free(card); 120 return err; 121 } 122 123 124 snd_cs46xx_gameport(chip); 125 126 strcpy(card->driver, "CS46xx"); 127 strcpy(card->shortname, "Sound Fusion CS46xx"); 128 sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", 129 card->shortname, 130 chip->ba0_addr, 131 chip->ba1_addr, 132 chip->irq); 133 134 if ((err = snd_card_register(card)) < 0) { 135 snd_card_free(card); 136 return err; 137 } 138 139 pci_set_drvdata(pci, card); 140 dev++; 141 return 0; 142 } 143 144 static void snd_card_cs46xx_remove(struct pci_dev *pci) 145 { 146 snd_card_free(pci_get_drvdata(pci)); 147 } 148 149 static struct pci_driver cs46xx_driver = { 150 .name = KBUILD_MODNAME, 151 .id_table = snd_cs46xx_ids, 152 .probe = snd_card_cs46xx_probe, 153 .remove = snd_card_cs46xx_remove, 154 #ifdef CONFIG_PM_SLEEP 155 .driver = { 156 .pm = &snd_cs46xx_pm, 157 }, 158 #endif 159 }; 160 161 module_pci_driver(cs46xx_driver); 162