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_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 70 sizeof(*chip), &card); 71 if (err < 0) 72 return err; 73 chip = card->private_data; 74 err = snd_cs46xx_create(card, pci, 75 external_amp[dev], thinkpad[dev]); 76 if (err < 0) 77 return err; 78 card->private_data = chip; 79 chip->accept_valid = mmap_valid[dev]; 80 err = snd_cs46xx_pcm(chip, 0); 81 if (err < 0) 82 return err; 83 #ifdef CONFIG_SND_CS46XX_NEW_DSP 84 err = snd_cs46xx_pcm_rear(chip, 1); 85 if (err < 0) 86 return err; 87 err = snd_cs46xx_pcm_iec958(chip, 2); 88 if (err < 0) 89 return err; 90 #endif 91 err = snd_cs46xx_mixer(chip, 2); 92 if (err < 0) 93 return err; 94 #ifdef CONFIG_SND_CS46XX_NEW_DSP 95 if (chip->nr_ac97_codecs ==2) { 96 err = snd_cs46xx_pcm_center_lfe(chip, 3); 97 if (err < 0) 98 return err; 99 } 100 #endif 101 err = snd_cs46xx_midi(chip, 0); 102 if (err < 0) 103 return err; 104 err = snd_cs46xx_start_dsp(chip); 105 if (err < 0) 106 return err; 107 108 snd_cs46xx_gameport(chip); 109 110 strcpy(card->driver, "CS46xx"); 111 strcpy(card->shortname, "Sound Fusion CS46xx"); 112 sprintf(card->longname, "%s at 0x%lx/0x%lx, irq %i", 113 card->shortname, 114 chip->ba0_addr, 115 chip->ba1_addr, 116 chip->irq); 117 118 err = snd_card_register(card); 119 if (err < 0) 120 return err; 121 122 pci_set_drvdata(pci, card); 123 dev++; 124 return 0; 125 } 126 127 static struct pci_driver cs46xx_driver = { 128 .name = KBUILD_MODNAME, 129 .id_table = snd_cs46xx_ids, 130 .probe = snd_card_cs46xx_probe, 131 #ifdef CONFIG_PM_SLEEP 132 .driver = { 133 .pm = &snd_cs46xx_pm, 134 }, 135 #endif 136 }; 137 138 module_pci_driver(cs46xx_driver); 139