1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ALSA driver for the Aureal Vortex family of soundprocessors. |
4 | * Author: Manuel Jander (mjander@embedded.cl) |
5 | * |
6 | * This driver is the result of the OpenVortex Project from Savannah |
7 | * (savannah.nongnu.org/projects/openvortex). I would like to thank |
8 | * the developers of OpenVortex, Jeff Muizelaar and Kester Maddock, from |
9 | * whom i got plenty of help, and their codebase was invaluable. |
10 | * Thanks to the ALSA developers, they helped a lot working out |
11 | * the ALSA part. |
12 | * Thanks also to Sourceforge for maintaining the old binary drivers, |
13 | * and the forum, where developers could communicate. |
14 | * |
15 | * Now at least i can play Legacy DOOM with MIDI music :-) |
16 | */ |
17 | |
18 | #include "au88x0.h" |
19 | #include <linux/init.h> |
20 | #include <linux/pci.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/interrupt.h> |
23 | #include <linux/module.h> |
24 | #include <linux/dma-mapping.h> |
25 | #include <sound/initval.h> |
26 | |
27 | // module parameters (see "Module Parameters") |
28 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; |
29 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; |
30 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; |
31 | static int pcifix[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 255 }; |
32 | |
33 | module_param_array(index, int, NULL, 0444); |
34 | MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard." ); |
35 | module_param_array(id, charp, NULL, 0444); |
36 | MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard." ); |
37 | module_param_array(enable, bool, NULL, 0444); |
38 | MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard." ); |
39 | module_param_array(pcifix, int, NULL, 0444); |
40 | MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard." ); |
41 | |
42 | MODULE_DESCRIPTION("Aureal vortex" ); |
43 | MODULE_LICENSE("GPL" ); |
44 | MODULE_DEVICE_TABLE(pci, snd_vortex_ids); |
45 | |
46 | static void vortex_fix_latency(struct pci_dev *vortex) |
47 | { |
48 | int rc; |
49 | rc = pci_write_config_byte(dev: vortex, where: 0x40, val: 0xff); |
50 | if (!rc) { |
51 | dev_info(&vortex->dev, "vortex latency is 0xff\n" ); |
52 | } else { |
53 | dev_warn(&vortex->dev, |
54 | "could not set vortex latency: pci error 0x%x\n" , rc); |
55 | } |
56 | } |
57 | |
58 | static void vortex_fix_agp_bridge(struct pci_dev *via) |
59 | { |
60 | int rc; |
61 | u8 value; |
62 | |
63 | /* |
64 | * only set the bit (Extend PCI#2 Internal Master for |
65 | * Efficient Handling of Dummy Requests) if the can |
66 | * read the config and it is not already set |
67 | */ |
68 | |
69 | rc = pci_read_config_byte(dev: via, where: 0x42, val: &value); |
70 | if (!rc) { |
71 | if (!(value & 0x10)) |
72 | rc = pci_write_config_byte(dev: via, where: 0x42, val: value | 0x10); |
73 | } |
74 | if (!rc) { |
75 | dev_info(&via->dev, "bridge config is 0x%x\n" , value | 0x10); |
76 | } else { |
77 | dev_warn(&via->dev, |
78 | "could not set vortex latency: pci error 0x%x\n" , rc); |
79 | } |
80 | } |
81 | |
82 | static void snd_vortex_workaround(struct pci_dev *vortex, int fix) |
83 | { |
84 | struct pci_dev *via = NULL; |
85 | |
86 | /* autodetect if workarounds are required */ |
87 | if (fix == 255) { |
88 | /* VIA KT133 */ |
89 | via = pci_get_device(PCI_VENDOR_ID_VIA, |
90 | PCI_DEVICE_ID_VIA_8365_1, NULL); |
91 | /* VIA Apollo */ |
92 | if (via == NULL) { |
93 | via = pci_get_device(PCI_VENDOR_ID_VIA, |
94 | PCI_DEVICE_ID_VIA_82C598_1, NULL); |
95 | /* AMD Irongate */ |
96 | if (via == NULL) |
97 | via = pci_get_device(PCI_VENDOR_ID_AMD, |
98 | PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL); |
99 | } |
100 | if (via) { |
101 | dev_info(&vortex->dev, |
102 | "Activating latency workaround...\n" ); |
103 | vortex_fix_latency(vortex); |
104 | vortex_fix_agp_bridge(via); |
105 | } |
106 | } else { |
107 | if (fix & 0x1) |
108 | vortex_fix_latency(vortex); |
109 | if (fix & 0x2) |
110 | via = pci_get_device(PCI_VENDOR_ID_VIA, |
111 | PCI_DEVICE_ID_VIA_8365_1, NULL); |
112 | else if (fix & 0x4) |
113 | via = pci_get_device(PCI_VENDOR_ID_VIA, |
114 | PCI_DEVICE_ID_VIA_82C598_1, NULL); |
115 | else if (fix & 0x8) |
116 | via = pci_get_device(PCI_VENDOR_ID_AMD, |
117 | PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL); |
118 | if (via) |
119 | vortex_fix_agp_bridge(via); |
120 | } |
121 | pci_dev_put(dev: via); |
122 | } |
123 | |
124 | // component-destructor |
125 | // (see "Management of Cards and Components") |
126 | static void snd_vortex_free(struct snd_card *card) |
127 | { |
128 | vortex_t *vortex = card->private_data; |
129 | |
130 | vortex_gameport_unregister(vortex); |
131 | vortex_core_shutdown(vortex); |
132 | } |
133 | |
134 | // chip-specific constructor |
135 | // (see "Management of Cards and Components") |
136 | static int |
137 | snd_vortex_create(struct snd_card *card, struct pci_dev *pci) |
138 | { |
139 | vortex_t *chip = card->private_data; |
140 | int err; |
141 | |
142 | // check PCI availability (DMA). |
143 | err = pcim_enable_device(pdev: pci); |
144 | if (err < 0) |
145 | return err; |
146 | if (dma_set_mask_and_coherent(dev: &pci->dev, DMA_BIT_MASK(32))) { |
147 | dev_err(card->dev, "error to set DMA mask\n" ); |
148 | return -ENXIO; |
149 | } |
150 | |
151 | chip->card = card; |
152 | |
153 | // initialize the stuff |
154 | chip->pci_dev = pci; |
155 | chip->vendor = pci->vendor; |
156 | chip->device = pci->device; |
157 | chip->card = card; |
158 | chip->irq = -1; |
159 | |
160 | // (1) PCI resource allocation |
161 | // Get MMIO area |
162 | // |
163 | err = pcim_iomap_regions(pdev: pci, mask: 1 << 0, CARD_NAME_SHORT); |
164 | if (err) |
165 | return err; |
166 | |
167 | chip->io = pci_resource_start(pci, 0); |
168 | chip->mmio = pcim_iomap_table(pdev: pci)[0]; |
169 | |
170 | /* Init audio core. |
171 | * This must be done before we do request_irq otherwise we can get spurious |
172 | * interrupts that we do not handle properly and make a mess of things */ |
173 | err = vortex_core_init(vortex: chip); |
174 | if (err) { |
175 | dev_err(card->dev, "hw core init failed\n" ); |
176 | return err; |
177 | } |
178 | |
179 | err = devm_request_irq(dev: &pci->dev, irq: pci->irq, handler: vortex_interrupt, |
180 | IRQF_SHARED, KBUILD_MODNAME, dev_id: chip); |
181 | if (err) { |
182 | dev_err(card->dev, "cannot grab irq\n" ); |
183 | return err; |
184 | } |
185 | chip->irq = pci->irq; |
186 | card->sync_irq = chip->irq; |
187 | card->private_free = snd_vortex_free; |
188 | |
189 | pci_set_master(dev: pci); |
190 | // End of PCI setup. |
191 | return 0; |
192 | } |
193 | |
194 | // constructor -- see "Constructor" sub-section |
195 | static int |
196 | __snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) |
197 | { |
198 | static int dev; |
199 | struct snd_card *card; |
200 | vortex_t *chip; |
201 | int err; |
202 | |
203 | // (1) |
204 | if (dev >= SNDRV_CARDS) |
205 | return -ENODEV; |
206 | if (!enable[dev]) { |
207 | dev++; |
208 | return -ENOENT; |
209 | } |
210 | // (2) |
211 | err = snd_devm_card_new(parent: &pci->dev, idx: index[dev], xid: id[dev], THIS_MODULE, |
212 | extra_size: sizeof(*chip), card_ret: &card); |
213 | if (err < 0) |
214 | return err; |
215 | chip = card->private_data; |
216 | |
217 | // (3) |
218 | err = snd_vortex_create(card, pci); |
219 | if (err < 0) |
220 | return err; |
221 | snd_vortex_workaround(vortex: pci, fix: pcifix[dev]); |
222 | |
223 | // Card details needed in snd_vortex_midi |
224 | strcpy(p: card->driver, CARD_NAME_SHORT); |
225 | sprintf(buf: card->shortname, fmt: "Aureal Vortex %s" , CARD_NAME_SHORT); |
226 | sprintf(buf: card->longname, fmt: "%s at 0x%lx irq %i" , |
227 | card->shortname, chip->io, chip->irq); |
228 | |
229 | // (4) Alloc components. |
230 | err = snd_vortex_mixer(vortex: chip); |
231 | if (err < 0) |
232 | return err; |
233 | // ADB pcm. |
234 | err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_PCM); |
235 | if (err < 0) |
236 | return err; |
237 | #ifndef CHIP_AU8820 |
238 | // ADB SPDIF |
239 | err = snd_vortex_new_pcm(chip, VORTEX_PCM_SPDIF, nr: 1); |
240 | if (err < 0) |
241 | return err; |
242 | // A3D |
243 | err = snd_vortex_new_pcm(chip, VORTEX_PCM_A3D, NR_A3D); |
244 | if (err < 0) |
245 | return err; |
246 | #endif |
247 | /* |
248 | // ADB I2S |
249 | if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_I2S, 1)) < 0) { |
250 | return err; |
251 | } |
252 | */ |
253 | #ifndef CHIP_AU8810 |
254 | // WT pcm. |
255 | err = snd_vortex_new_pcm(chip, VORTEX_PCM_WT, NR_WT); |
256 | if (err < 0) |
257 | return err; |
258 | #endif |
259 | err = snd_vortex_midi(vortex: chip); |
260 | if (err < 0) |
261 | return err; |
262 | |
263 | vortex_gameport_register(vortex: chip); |
264 | |
265 | #if 0 |
266 | if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH, |
267 | sizeof(snd_vortex_synth_arg_t), &wave) < 0 |
268 | || wave == NULL) { |
269 | dev_err(card->dev, "Can't initialize Aureal wavetable synth\n" ); |
270 | } else { |
271 | snd_vortex_synth_arg_t *arg; |
272 | |
273 | arg = SNDRV_SEQ_DEVICE_ARGPTR(wave); |
274 | strcpy(wave->name, "Aureal Synth" ); |
275 | arg->hwptr = vortex; |
276 | arg->index = 1; |
277 | arg->seq_ports = seq_ports[dev]; |
278 | arg->max_voices = max_synth_voices[dev]; |
279 | } |
280 | #endif |
281 | |
282 | // (5) |
283 | err = pci_read_config_word(dev: pci, PCI_DEVICE_ID, val: &chip->device); |
284 | if (err < 0) |
285 | return err; |
286 | err = pci_read_config_word(dev: pci, PCI_VENDOR_ID, val: &chip->vendor); |
287 | if (err < 0) |
288 | return err; |
289 | chip->rev = pci->revision; |
290 | #ifdef CHIP_AU8830 |
291 | if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) { |
292 | dev_alert(card->dev, |
293 | "The revision (%x) of your card has not been seen before.\n" , |
294 | chip->rev); |
295 | dev_alert(card->dev, |
296 | "Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n" ); |
297 | return -ENODEV; |
298 | } |
299 | #endif |
300 | |
301 | // (6) |
302 | err = snd_card_register(card); |
303 | if (err < 0) |
304 | return err; |
305 | // (7) |
306 | pci_set_drvdata(pdev: pci, data: card); |
307 | dev++; |
308 | vortex_connect_default(vortex: chip, en: 1); |
309 | vortex_enable_int(card: chip); |
310 | return 0; |
311 | } |
312 | |
313 | static int |
314 | snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) |
315 | { |
316 | return snd_card_free_on_error(dev: &pci->dev, ret: __snd_vortex_probe(pci, pci_id)); |
317 | } |
318 | |
319 | // pci_driver definition |
320 | static struct pci_driver vortex_driver = { |
321 | .name = KBUILD_MODNAME, |
322 | .id_table = snd_vortex_ids, |
323 | .probe = snd_vortex_probe, |
324 | }; |
325 | |
326 | module_pci_driver(vortex_driver); |
327 | |