1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Routines for Gravis UltraSound soundcards |
4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
5 | */ |
6 | |
7 | #include <linux/init.h> |
8 | #include <linux/interrupt.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/ioport.h> |
12 | #include <linux/module.h> |
13 | #include <sound/core.h> |
14 | #include <sound/gus.h> |
15 | #include <sound/control.h> |
16 | |
17 | #include <asm/dma.h> |
18 | |
19 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>" ); |
20 | MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards" ); |
21 | MODULE_LICENSE("GPL" ); |
22 | |
23 | static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches); |
24 | |
25 | int snd_gus_use_inc(struct snd_gus_card * gus) |
26 | { |
27 | if (!try_module_get(module: gus->card->module)) |
28 | return 0; |
29 | return 1; |
30 | } |
31 | |
32 | void snd_gus_use_dec(struct snd_gus_card * gus) |
33 | { |
34 | module_put(module: gus->card->module); |
35 | } |
36 | |
37 | static int snd_gus_joystick_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
38 | { |
39 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
40 | uinfo->count = 1; |
41 | uinfo->value.integer.min = 0; |
42 | uinfo->value.integer.max = 31; |
43 | return 0; |
44 | } |
45 | |
46 | static int snd_gus_joystick_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
47 | { |
48 | struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); |
49 | |
50 | ucontrol->value.integer.value[0] = gus->joystick_dac & 31; |
51 | return 0; |
52 | } |
53 | |
54 | static int snd_gus_joystick_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
55 | { |
56 | struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); |
57 | unsigned long flags; |
58 | int change; |
59 | unsigned char nval; |
60 | |
61 | nval = ucontrol->value.integer.value[0] & 31; |
62 | spin_lock_irqsave(&gus->reg_lock, flags); |
63 | change = gus->joystick_dac != nval; |
64 | gus->joystick_dac = nval; |
65 | snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, data: gus->joystick_dac); |
66 | spin_unlock_irqrestore(lock: &gus->reg_lock, flags); |
67 | return change; |
68 | } |
69 | |
70 | static const struct snd_kcontrol_new snd_gus_joystick_control = { |
71 | .iface = SNDRV_CTL_ELEM_IFACE_CARD, |
72 | .name = "Joystick Speed" , |
73 | .info = snd_gus_joystick_info, |
74 | .get = snd_gus_joystick_get, |
75 | .put = snd_gus_joystick_put |
76 | }; |
77 | |
78 | static void snd_gus_init_control(struct snd_gus_card *gus) |
79 | { |
80 | if (!gus->ace_flag) |
81 | snd_ctl_add(card: gus->card, kcontrol: snd_ctl_new1(kcontrolnew: &snd_gus_joystick_control, private_data: gus)); |
82 | } |
83 | |
84 | /* |
85 | * |
86 | */ |
87 | |
88 | static int snd_gus_free(struct snd_gus_card *gus) |
89 | { |
90 | if (gus->gf1.res_port2 == NULL) |
91 | goto __hw_end; |
92 | snd_gf1_stop(gus); |
93 | snd_gus_init_dma_irq(gus, latches: 0); |
94 | __hw_end: |
95 | release_and_free_resource(res: gus->gf1.res_port1); |
96 | release_and_free_resource(res: gus->gf1.res_port2); |
97 | if (gus->gf1.irq >= 0) |
98 | free_irq(gus->gf1.irq, (void *) gus); |
99 | if (gus->gf1.dma1 >= 0) { |
100 | disable_dma(dmanr: gus->gf1.dma1); |
101 | free_dma(dmanr: gus->gf1.dma1); |
102 | } |
103 | if (!gus->equal_dma && gus->gf1.dma2 >= 0) { |
104 | disable_dma(dmanr: gus->gf1.dma2); |
105 | free_dma(dmanr: gus->gf1.dma2); |
106 | } |
107 | kfree(objp: gus); |
108 | return 0; |
109 | } |
110 | |
111 | static int snd_gus_dev_free(struct snd_device *device) |
112 | { |
113 | struct snd_gus_card *gus = device->device_data; |
114 | return snd_gus_free(gus); |
115 | } |
116 | |
117 | int snd_gus_create(struct snd_card *card, |
118 | unsigned long port, |
119 | int irq, int dma1, int dma2, |
120 | int timer_dev, |
121 | int voices, |
122 | int pcm_channels, |
123 | int effect, |
124 | struct snd_gus_card **rgus) |
125 | { |
126 | struct snd_gus_card *gus; |
127 | int err; |
128 | static const struct snd_device_ops ops = { |
129 | .dev_free = snd_gus_dev_free, |
130 | }; |
131 | |
132 | *rgus = NULL; |
133 | gus = kzalloc(size: sizeof(*gus), GFP_KERNEL); |
134 | if (gus == NULL) |
135 | return -ENOMEM; |
136 | spin_lock_init(&gus->reg_lock); |
137 | spin_lock_init(&gus->voice_alloc); |
138 | spin_lock_init(&gus->active_voice_lock); |
139 | spin_lock_init(&gus->event_lock); |
140 | spin_lock_init(&gus->dma_lock); |
141 | spin_lock_init(&gus->pcm_volume_level_lock); |
142 | spin_lock_init(&gus->uart_cmd_lock); |
143 | mutex_init(&gus->dma_mutex); |
144 | gus->gf1.irq = -1; |
145 | gus->gf1.dma1 = -1; |
146 | gus->gf1.dma2 = -1; |
147 | gus->card = card; |
148 | gus->gf1.port = port; |
149 | /* fill register variables for speedup */ |
150 | gus->gf1.reg_page = GUSP(gus, GF1PAGE); |
151 | gus->gf1.reg_regsel = GUSP(gus, GF1REGSEL); |
152 | gus->gf1.reg_data8 = GUSP(gus, GF1DATAHIGH); |
153 | gus->gf1.reg_data16 = GUSP(gus, GF1DATALOW); |
154 | gus->gf1.reg_irqstat = GUSP(gus, IRQSTAT); |
155 | gus->gf1.reg_dram = GUSP(gus, DRAM); |
156 | gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL); |
157 | gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA); |
158 | /* allocate resources */ |
159 | gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)" ); |
160 | if (!gus->gf1.res_port1) { |
161 | snd_printk(KERN_ERR "gus: can't grab SB port 0x%lx\n" , port); |
162 | snd_gus_free(gus); |
163 | return -EBUSY; |
164 | } |
165 | gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)" ); |
166 | if (!gus->gf1.res_port2) { |
167 | snd_printk(KERN_ERR "gus: can't grab synth port 0x%lx\n" , port + 0x100); |
168 | snd_gus_free(gus); |
169 | return -EBUSY; |
170 | } |
171 | if (irq >= 0 && request_irq(irq, handler: snd_gus_interrupt, flags: 0, name: "GUS GF1" , dev: (void *) gus)) { |
172 | snd_printk(KERN_ERR "gus: can't grab irq %d\n" , irq); |
173 | snd_gus_free(gus); |
174 | return -EBUSY; |
175 | } |
176 | gus->gf1.irq = irq; |
177 | card->sync_irq = irq; |
178 | if (request_dma(dmanr: dma1, device_id: "GUS - 1" )) { |
179 | snd_printk(KERN_ERR "gus: can't grab DMA1 %d\n" , dma1); |
180 | snd_gus_free(gus); |
181 | return -EBUSY; |
182 | } |
183 | gus->gf1.dma1 = dma1; |
184 | if (dma2 >= 0 && dma1 != dma2) { |
185 | if (request_dma(dmanr: dma2, device_id: "GUS - 2" )) { |
186 | snd_printk(KERN_ERR "gus: can't grab DMA2 %d\n" , dma2); |
187 | snd_gus_free(gus); |
188 | return -EBUSY; |
189 | } |
190 | gus->gf1.dma2 = dma2; |
191 | } else { |
192 | gus->gf1.dma2 = gus->gf1.dma1; |
193 | gus->equal_dma = 1; |
194 | } |
195 | gus->timer_dev = timer_dev; |
196 | if (voices < 14) |
197 | voices = 14; |
198 | if (voices > 32) |
199 | voices = 32; |
200 | if (pcm_channels < 0) |
201 | pcm_channels = 0; |
202 | if (pcm_channels > 8) |
203 | pcm_channels = 8; |
204 | pcm_channels++; |
205 | pcm_channels &= ~1; |
206 | gus->gf1.effect = effect ? 1 : 0; |
207 | gus->gf1.active_voices = voices; |
208 | gus->gf1.pcm_channels = pcm_channels; |
209 | gus->gf1.volume_ramp = 25; |
210 | gus->gf1.smooth_pan = 1; |
211 | err = snd_device_new(card, type: SNDRV_DEV_LOWLEVEL, device_data: gus, ops: &ops); |
212 | if (err < 0) { |
213 | snd_gus_free(gus); |
214 | return err; |
215 | } |
216 | *rgus = gus; |
217 | return 0; |
218 | } |
219 | |
220 | /* |
221 | * Memory detection routine for plain GF1 soundcards |
222 | */ |
223 | |
224 | static int snd_gus_detect_memory(struct snd_gus_card * gus) |
225 | { |
226 | int l, idx, local; |
227 | unsigned char d; |
228 | |
229 | snd_gf1_poke(gus, addr: 0L, data: 0xaa); |
230 | snd_gf1_poke(gus, addr: 1L, data: 0x55); |
231 | if (snd_gf1_peek(gus, addr: 0L) != 0xaa || snd_gf1_peek(gus, addr: 1L) != 0x55) { |
232 | snd_printk(KERN_ERR "plain GF1 card at 0x%lx without onboard DRAM?\n" , gus->gf1.port); |
233 | return -ENOMEM; |
234 | } |
235 | for (idx = 1, d = 0xab; idx < 4; idx++, d++) { |
236 | local = idx << 18; |
237 | snd_gf1_poke(gus, addr: local, data: d); |
238 | snd_gf1_poke(gus, addr: local + 1, data: d + 1); |
239 | if (snd_gf1_peek(gus, addr: local) != d || |
240 | snd_gf1_peek(gus, addr: local + 1) != d + 1 || |
241 | snd_gf1_peek(gus, addr: 0L) != 0xaa) |
242 | break; |
243 | } |
244 | #if 1 |
245 | gus->gf1.memory = idx << 18; |
246 | #else |
247 | gus->gf1.memory = 256 * 1024; |
248 | #endif |
249 | for (l = 0, local = gus->gf1.memory; l < 4; l++, local -= 256 * 1024) { |
250 | gus->gf1.mem_alloc.banks_8[l].address = |
251 | gus->gf1.mem_alloc.banks_8[l].size = 0; |
252 | gus->gf1.mem_alloc.banks_16[l].address = l << 18; |
253 | gus->gf1.mem_alloc.banks_16[l].size = local > 0 ? 256 * 1024 : 0; |
254 | } |
255 | gus->gf1.mem_alloc.banks_8[0].size = gus->gf1.memory; |
256 | return 0; /* some memory were detected */ |
257 | } |
258 | |
259 | static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches) |
260 | { |
261 | struct snd_card *card; |
262 | unsigned long flags; |
263 | int irq, dma1, dma2; |
264 | static const unsigned char irqs[16] = |
265 | {0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7}; |
266 | static const unsigned char dmas[8] = |
267 | {6, 1, 0, 2, 0, 3, 4, 5}; |
268 | |
269 | if (snd_BUG_ON(!gus)) |
270 | return -EINVAL; |
271 | card = gus->card; |
272 | if (snd_BUG_ON(!card)) |
273 | return -EINVAL; |
274 | |
275 | gus->mix_cntrl_reg &= 0xf8; |
276 | gus->mix_cntrl_reg |= 0x01; /* disable MIC, LINE IN, enable LINE OUT */ |
277 | if (gus->codec_flag || gus->ess_flag) { |
278 | gus->mix_cntrl_reg &= ~1; /* enable LINE IN */ |
279 | gus->mix_cntrl_reg |= 4; /* enable MIC */ |
280 | } |
281 | dma1 = gus->gf1.dma1; |
282 | dma1 = abs(dma1); |
283 | dma1 = dmas[dma1 & 7]; |
284 | dma2 = gus->gf1.dma2; |
285 | dma2 = abs(dma2); |
286 | dma2 = dmas[dma2 & 7]; |
287 | dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3); |
288 | |
289 | if ((dma1 & 7) == 0 || (dma2 & 7) == 0) { |
290 | snd_printk(KERN_ERR "Error! DMA isn't defined.\n" ); |
291 | return -EINVAL; |
292 | } |
293 | irq = gus->gf1.irq; |
294 | irq = abs(irq); |
295 | irq = irqs[irq & 0x0f]; |
296 | if (irq == 0) { |
297 | snd_printk(KERN_ERR "Error! IRQ isn't defined.\n" ); |
298 | return -EINVAL; |
299 | } |
300 | irq |= 0x40; |
301 | #if 0 |
302 | card->mixer.mix_ctrl_reg |= 0x10; |
303 | #endif |
304 | |
305 | spin_lock_irqsave(&gus->reg_lock, flags); |
306 | outb(value: 5, GUSP(gus, REGCNTRLS)); |
307 | outb(value: gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); |
308 | outb(value: 0x00, GUSP(gus, IRQDMACNTRLREG)); |
309 | outb(value: 0, GUSP(gus, REGCNTRLS)); |
310 | spin_unlock_irqrestore(lock: &gus->reg_lock, flags); |
311 | |
312 | udelay(100); |
313 | |
314 | spin_lock_irqsave(&gus->reg_lock, flags); |
315 | outb(value: 0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); |
316 | outb(value: dma1, GUSP(gus, IRQDMACNTRLREG)); |
317 | if (latches) { |
318 | outb(value: 0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); |
319 | outb(value: irq, GUSP(gus, IRQDMACNTRLREG)); |
320 | } |
321 | spin_unlock_irqrestore(lock: &gus->reg_lock, flags); |
322 | |
323 | udelay(100); |
324 | |
325 | spin_lock_irqsave(&gus->reg_lock, flags); |
326 | outb(value: 0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); |
327 | outb(value: dma1, GUSP(gus, IRQDMACNTRLREG)); |
328 | if (latches) { |
329 | outb(value: 0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); |
330 | outb(value: irq, GUSP(gus, IRQDMACNTRLREG)); |
331 | } |
332 | spin_unlock_irqrestore(lock: &gus->reg_lock, flags); |
333 | |
334 | snd_gf1_delay(gus); |
335 | |
336 | if (latches) |
337 | gus->mix_cntrl_reg |= 0x08; /* enable latches */ |
338 | else |
339 | gus->mix_cntrl_reg &= ~0x08; /* disable latches */ |
340 | spin_lock_irqsave(&gus->reg_lock, flags); |
341 | outb(value: gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG)); |
342 | outb(value: 0, GUSP(gus, GF1PAGE)); |
343 | spin_unlock_irqrestore(lock: &gus->reg_lock, flags); |
344 | |
345 | return 0; |
346 | } |
347 | |
348 | static int snd_gus_check_version(struct snd_gus_card * gus) |
349 | { |
350 | unsigned long flags; |
351 | unsigned char val, rev; |
352 | struct snd_card *card; |
353 | |
354 | card = gus->card; |
355 | spin_lock_irqsave(&gus->reg_lock, flags); |
356 | outb(value: 0x20, GUSP(gus, REGCNTRLS)); |
357 | val = inb(GUSP(gus, REGCNTRLS)); |
358 | rev = inb(GUSP(gus, BOARDVERSION)); |
359 | spin_unlock_irqrestore(lock: &gus->reg_lock, flags); |
360 | snd_printdd("GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n" , gus->gf1.port, val, rev); |
361 | strcpy(p: card->driver, q: "GUS" ); |
362 | strcpy(p: card->longname, q: "Gravis UltraSound Classic (2.4)" ); |
363 | if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) { |
364 | if (rev >= 5 && rev <= 9) { |
365 | gus->ics_flag = 1; |
366 | if (rev == 5) |
367 | gus->ics_flipped = 1; |
368 | card->longname[27] = '3'; |
369 | card->longname[29] = rev == 5 ? '5' : '7'; |
370 | } |
371 | if (rev >= 10 && rev != 255) { |
372 | if (rev >= 10 && rev <= 11) { |
373 | strcpy(p: card->driver, q: "GUS MAX" ); |
374 | strcpy(p: card->longname, q: "Gravis UltraSound MAX" ); |
375 | gus->max_flag = 1; |
376 | } else if (rev == 0x30) { |
377 | strcpy(p: card->driver, q: "GUS ACE" ); |
378 | strcpy(p: card->longname, q: "Gravis UltraSound Ace" ); |
379 | gus->ace_flag = 1; |
380 | } else if (rev == 0x50) { |
381 | strcpy(p: card->driver, q: "GUS Extreme" ); |
382 | strcpy(p: card->longname, q: "Gravis UltraSound Extreme" ); |
383 | gus->ess_flag = 1; |
384 | } else { |
385 | snd_printk(KERN_ERR "unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n" , gus->gf1.port, rev, val); |
386 | snd_printk(KERN_ERR " please - report to <perex@perex.cz>\n" ); |
387 | } |
388 | } |
389 | } |
390 | strscpy(p: card->shortname, q: card->longname, size: sizeof(card->shortname)); |
391 | gus->uart_enable = 1; /* standard GUSes doesn't have midi uart trouble */ |
392 | snd_gus_init_control(gus); |
393 | return 0; |
394 | } |
395 | |
396 | int snd_gus_initialize(struct snd_gus_card *gus) |
397 | { |
398 | int err; |
399 | |
400 | if (!gus->interwave) { |
401 | err = snd_gus_check_version(gus); |
402 | if (err < 0) { |
403 | snd_printk(KERN_ERR "version check failed\n" ); |
404 | return err; |
405 | } |
406 | err = snd_gus_detect_memory(gus); |
407 | if (err < 0) |
408 | return err; |
409 | } |
410 | err = snd_gus_init_dma_irq(gus, latches: 1); |
411 | if (err < 0) |
412 | return err; |
413 | snd_gf1_start(gus); |
414 | gus->initialized = 1; |
415 | return 0; |
416 | } |
417 | |
418 | /* gus_io.c */ |
419 | EXPORT_SYMBOL(snd_gf1_delay); |
420 | EXPORT_SYMBOL(snd_gf1_write8); |
421 | EXPORT_SYMBOL(snd_gf1_look8); |
422 | EXPORT_SYMBOL(snd_gf1_write16); |
423 | EXPORT_SYMBOL(snd_gf1_look16); |
424 | EXPORT_SYMBOL(snd_gf1_i_write8); |
425 | EXPORT_SYMBOL(snd_gf1_i_look8); |
426 | EXPORT_SYMBOL(snd_gf1_i_look16); |
427 | EXPORT_SYMBOL(snd_gf1_dram_addr); |
428 | EXPORT_SYMBOL(snd_gf1_write_addr); |
429 | EXPORT_SYMBOL(snd_gf1_poke); |
430 | EXPORT_SYMBOL(snd_gf1_peek); |
431 | /* gus_reset.c */ |
432 | EXPORT_SYMBOL(snd_gf1_alloc_voice); |
433 | EXPORT_SYMBOL(snd_gf1_free_voice); |
434 | EXPORT_SYMBOL(snd_gf1_ctrl_stop); |
435 | EXPORT_SYMBOL(snd_gf1_stop_voice); |
436 | /* gus_mixer.c */ |
437 | EXPORT_SYMBOL(snd_gf1_new_mixer); |
438 | /* gus_pcm.c */ |
439 | EXPORT_SYMBOL(snd_gf1_pcm_new); |
440 | /* gus.c */ |
441 | EXPORT_SYMBOL(snd_gus_use_inc); |
442 | EXPORT_SYMBOL(snd_gus_use_dec); |
443 | EXPORT_SYMBOL(snd_gus_create); |
444 | EXPORT_SYMBOL(snd_gus_initialize); |
445 | /* gus_irq.c */ |
446 | EXPORT_SYMBOL(snd_gus_interrupt); |
447 | /* gus_uart.c */ |
448 | EXPORT_SYMBOL(snd_gf1_rawmidi_new); |
449 | /* gus_dram.c */ |
450 | EXPORT_SYMBOL(snd_gus_dram_write); |
451 | EXPORT_SYMBOL(snd_gus_dram_read); |
452 | /* gus_volume.c */ |
453 | EXPORT_SYMBOL(snd_gf1_lvol_to_gvol_raw); |
454 | EXPORT_SYMBOL(snd_gf1_translate_freq); |
455 | /* gus_mem.c */ |
456 | EXPORT_SYMBOL(snd_gf1_mem_alloc); |
457 | EXPORT_SYMBOL(snd_gf1_mem_xfree); |
458 | EXPORT_SYMBOL(snd_gf1_mem_free); |
459 | EXPORT_SYMBOL(snd_gf1_mem_lock); |
460 | |