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 err = snd_cs46xx_create(card, pci, 74 external_amp[dev], thinkpad[dev], 75 &chip); 76 if (err < 0) { 77 snd_card_free(card); 78 return err; 79 } 80 card->private_data = chip; 81 chip->accept_valid = mmap_valid[dev]; 82 err = snd_cs46xx_pcm(chip, 0); 83 if (err < 0) { 84 snd_card_free(card); 85 return err; 86 } 87 #ifdef CONFIG_SND_CS46XX_NEW_DSP 88 err = snd_cs46xx_pcm_rear(chip, 1); 89 if (err < 0) { 90 snd_card_free(card); 91 return err; 92 } 93 err = snd_cs46xx_pcm_iec958(chip, 2); 94 if (err < 0) { 95 snd_card_free(card); 96 return err; 97 } 98 #endif 99 err = snd_cs46xx_mixer(chip, 2); 100 if (err < 0) { 101 snd_card_free(card); 102 return err; 103 } 104 #ifdef CONFIG_SND_CS46XX_NEW_DSP 105 if (chip->nr_ac97_codecs ==2) { 106 err = snd_cs46xx_pcm_center_lfe(chip, 3); 107 if (err < 0) { 108 snd_card_free(card); 109 return err; 110 } 111 } 112 #endif 113 err = snd_cs46xx_midi(chip, 0); 114 if (err < 0) { 115 snd_card_free(card); 116 return err; 117 } 118 err = snd_cs46xx_start_dsp(chip); 119 if (err < 0) { 120 snd_card_free(card); 121 return err; 122 } 123 124 125 snd_cs46xx_gameport(chip); 126 127 strcpy(card->driver, "CS46xx"); 128 strcpy(card->shortname, "Sound Fusion CS46xx"); 129 sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", 130 card->shortname, 131 chip->ba0_addr, 132 chip->ba1_addr, 133 chip->irq); 134 135 err = snd_card_register(card); 136 if (err < 0) { 137 snd_card_free(card); 138 return err; 139 } 140 141 pci_set_drvdata(pci, card); 142 dev++; 143 return 0; 144 } 145 146 static void snd_card_cs46xx_remove(struct pci_dev *pci) 147 { 148 snd_card_free(pci_get_drvdata(pci)); 149 } 150 151 static struct pci_driver cs46xx_driver = { 152 .name = KBUILD_MODNAME, 153 .id_table = snd_cs46xx_ids, 154 .probe = snd_card_cs46xx_probe, 155 .remove = snd_card_cs46xx_remove, 156 #ifdef CONFIG_PM_SLEEP 157 .driver = { 158 .pm = &snd_cs46xx_pm, 159 }, 160 #endif 161 }; 162 163 module_pci_driver(cs46xx_driver); 164