1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * synth callback routines for Emu10k1 |
4 | * |
5 | * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> |
6 | */ |
7 | |
8 | #include <linux/export.h> |
9 | #include "emu10k1_synth_local.h" |
10 | #include <sound/asoundef.h> |
11 | |
12 | /* voice status */ |
13 | enum { |
14 | V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END |
15 | }; |
16 | |
17 | /* Keeps track of what we are finding */ |
18 | struct best_voice { |
19 | unsigned int time; |
20 | int voice; |
21 | }; |
22 | |
23 | /* |
24 | * prototypes |
25 | */ |
26 | static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw, |
27 | struct best_voice *best, int active_only); |
28 | static struct snd_emux_voice *get_voice(struct snd_emux *emux, |
29 | struct snd_emux_port *port); |
30 | static int start_voice(struct snd_emux_voice *vp); |
31 | static void trigger_voice(struct snd_emux_voice *vp); |
32 | static void release_voice(struct snd_emux_voice *vp); |
33 | static void update_voice(struct snd_emux_voice *vp, int update); |
34 | static void terminate_voice(struct snd_emux_voice *vp); |
35 | static void free_voice(struct snd_emux_voice *vp); |
36 | static u32 make_fmmod(struct snd_emux_voice *vp); |
37 | static u32 make_fm2frq2(struct snd_emux_voice *vp); |
38 | static int get_pitch_shift(struct snd_emux *emu); |
39 | |
40 | /* |
41 | * Ensure a value is between two points |
42 | * macro evaluates its args more than once, so changed to upper-case. |
43 | */ |
44 | #define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) |
45 | #define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) |
46 | |
47 | |
48 | /* |
49 | * set up operators |
50 | */ |
51 | static const struct snd_emux_operators emu10k1_ops = { |
52 | .owner = THIS_MODULE, |
53 | .get_voice = get_voice, |
54 | .prepare = start_voice, |
55 | .trigger = trigger_voice, |
56 | .release = release_voice, |
57 | .update = update_voice, |
58 | .terminate = terminate_voice, |
59 | .free_voice = free_voice, |
60 | .sample_new = snd_emu10k1_sample_new, |
61 | .sample_free = snd_emu10k1_sample_free, |
62 | .get_pitch_shift = get_pitch_shift, |
63 | }; |
64 | |
65 | void |
66 | snd_emu10k1_ops_setup(struct snd_emux *emux) |
67 | { |
68 | emux->ops = emu10k1_ops; |
69 | } |
70 | |
71 | |
72 | /* |
73 | * get more voice for pcm |
74 | * |
75 | * terminate most inactive voice and give it as a pcm voice. |
76 | * |
77 | * voice_lock is already held. |
78 | */ |
79 | int |
80 | snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw) |
81 | { |
82 | struct snd_emux *emu; |
83 | struct snd_emux_voice *vp; |
84 | struct best_voice best[V_END]; |
85 | int i; |
86 | |
87 | emu = hw->synth; |
88 | |
89 | lookup_voices(emux: emu, hw, best, active_only: 1); /* no OFF voices */ |
90 | for (i = 0; i < V_END; i++) { |
91 | if (best[i].voice >= 0) { |
92 | int ch; |
93 | vp = &emu->voices[best[i].voice]; |
94 | ch = vp->ch; |
95 | if (ch < 0) { |
96 | /* |
97 | dev_warn(emu->card->dev, |
98 | "synth_get_voice: ch < 0 (%d) ??", i); |
99 | */ |
100 | continue; |
101 | } |
102 | vp->emu->num_voices--; |
103 | vp->ch = -1; |
104 | vp->state = SNDRV_EMUX_ST_OFF; |
105 | return ch; |
106 | } |
107 | } |
108 | |
109 | /* not found */ |
110 | return -ENOMEM; |
111 | } |
112 | |
113 | |
114 | /* |
115 | * turn off the voice (not terminated) |
116 | */ |
117 | static void |
118 | release_voice(struct snd_emux_voice *vp) |
119 | { |
120 | struct snd_emu10k1 *hw; |
121 | |
122 | hw = vp->hw; |
123 | snd_emu10k1_ptr_write_multiple(emu: hw, chn: vp->ch, |
124 | DCYSUSM, (unsigned char)vp->reg.parm.modrelease | DCYSUSM_PHASE1_MASK, |
125 | DCYSUSV, (unsigned char)vp->reg.parm.volrelease | DCYSUSV_PHASE1_MASK | DCYSUSV_CHANNELENABLE_MASK, |
126 | REGLIST_END); |
127 | } |
128 | |
129 | |
130 | /* |
131 | * terminate the voice |
132 | */ |
133 | static void |
134 | terminate_voice(struct snd_emux_voice *vp) |
135 | { |
136 | struct snd_emu10k1 *hw; |
137 | |
138 | if (snd_BUG_ON(!vp)) |
139 | return; |
140 | hw = vp->hw; |
141 | snd_emu10k1_ptr_write_multiple(emu: hw, chn: vp->ch, |
142 | DCYSUSV, 0, |
143 | VTFT, VTFT_FILTERTARGET_MASK, |
144 | CVCF, CVCF_CURRENTFILTER_MASK, |
145 | PTRX, 0, |
146 | CPF, 0, |
147 | REGLIST_END); |
148 | if (vp->block) { |
149 | struct snd_emu10k1_memblk *emem; |
150 | emem = (struct snd_emu10k1_memblk *)vp->block; |
151 | if (emem->map_locked > 0) |
152 | emem->map_locked--; |
153 | } |
154 | } |
155 | |
156 | /* |
157 | * release the voice to system |
158 | */ |
159 | static void |
160 | free_voice(struct snd_emux_voice *vp) |
161 | { |
162 | struct snd_emu10k1 *hw; |
163 | |
164 | hw = vp->hw; |
165 | /* FIXME: emu10k1_synth is broken. */ |
166 | /* This can get called with hw == 0 */ |
167 | /* Problem apparent on plug, unplug then plug */ |
168 | /* on the Audigy 2 ZS Notebook. */ |
169 | if (hw && (vp->ch >= 0)) { |
170 | snd_emu10k1_voice_free(emu: hw, pvoice: &hw->voices[vp->ch]); |
171 | vp->emu->num_voices--; |
172 | vp->ch = -1; |
173 | } |
174 | } |
175 | |
176 | |
177 | /* |
178 | * update registers |
179 | */ |
180 | static void |
181 | update_voice(struct snd_emux_voice *vp, int update) |
182 | { |
183 | struct snd_emu10k1 *hw; |
184 | |
185 | hw = vp->hw; |
186 | if (update & SNDRV_EMUX_UPDATE_VOLUME) |
187 | snd_emu10k1_ptr_write(emu: hw, reg: IFATN_ATTENUATION, chn: vp->ch, data: vp->avol); |
188 | if (update & SNDRV_EMUX_UPDATE_PITCH) |
189 | snd_emu10k1_ptr_write(emu: hw, IP, chn: vp->ch, data: vp->apitch); |
190 | if (update & SNDRV_EMUX_UPDATE_PAN) { |
191 | snd_emu10k1_ptr_write(emu: hw, reg: PTRX_FXSENDAMOUNT_A, chn: vp->ch, data: vp->apan); |
192 | snd_emu10k1_ptr_write(emu: hw, reg: PTRX_FXSENDAMOUNT_B, chn: vp->ch, data: vp->aaux); |
193 | } |
194 | if (update & SNDRV_EMUX_UPDATE_FMMOD) |
195 | snd_emu10k1_ptr_write(emu: hw, FMMOD, chn: vp->ch, data: make_fmmod(vp)); |
196 | if (update & SNDRV_EMUX_UPDATE_TREMFREQ) |
197 | snd_emu10k1_ptr_write(emu: hw, TREMFRQ, chn: vp->ch, data: vp->reg.parm.tremfrq); |
198 | if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) |
199 | snd_emu10k1_ptr_write(emu: hw, FM2FRQ2, chn: vp->ch, data: make_fm2frq2(vp)); |
200 | if (update & SNDRV_EMUX_UPDATE_Q) |
201 | snd_emu10k1_ptr_write(emu: hw, reg: CCCA_RESONANCE, chn: vp->ch, data: vp->reg.parm.filterQ); |
202 | } |
203 | |
204 | |
205 | /* |
206 | * look up voice table - get the best voice in order of preference |
207 | */ |
208 | /* spinlock held! */ |
209 | static void |
210 | lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, |
211 | struct best_voice *best, int active_only) |
212 | { |
213 | struct snd_emux_voice *vp; |
214 | struct best_voice *bp; |
215 | int i; |
216 | |
217 | for (i = 0; i < V_END; i++) { |
218 | best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */ |
219 | best[i].voice = -1; |
220 | } |
221 | |
222 | /* |
223 | * Go through them all and get a best one to use. |
224 | * NOTE: could also look at volume and pick the quietest one. |
225 | */ |
226 | for (i = 0; i < emu->max_voices; i++) { |
227 | int state, val; |
228 | |
229 | vp = &emu->voices[i]; |
230 | state = vp->state; |
231 | if (state == SNDRV_EMUX_ST_OFF) { |
232 | if (vp->ch < 0) { |
233 | if (active_only) |
234 | continue; |
235 | bp = best + V_FREE; |
236 | } else |
237 | bp = best + V_OFF; |
238 | } |
239 | else if (state == SNDRV_EMUX_ST_RELEASED || |
240 | state == SNDRV_EMUX_ST_PENDING) { |
241 | bp = best + V_RELEASED; |
242 | #if 1 |
243 | val = snd_emu10k1_ptr_read(emu: hw, reg: CVCF_CURRENTVOL, chn: vp->ch); |
244 | if (! val) |
245 | bp = best + V_OFF; |
246 | #endif |
247 | } |
248 | else if (state == SNDRV_EMUX_ST_STANDBY) |
249 | continue; |
250 | else if (state & SNDRV_EMUX_ST_ON) |
251 | bp = best + V_PLAYING; |
252 | else |
253 | continue; |
254 | |
255 | /* check if sample is finished playing (non-looping only) */ |
256 | if (bp != best + V_OFF && bp != best + V_FREE && |
257 | (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { |
258 | val = snd_emu10k1_ptr_read(emu: hw, reg: CCCA_CURRADDR, chn: vp->ch); |
259 | if (val >= vp->reg.loopstart) |
260 | bp = best + V_OFF; |
261 | } |
262 | |
263 | if (vp->time < bp->time) { |
264 | bp->time = vp->time; |
265 | bp->voice = i; |
266 | } |
267 | } |
268 | } |
269 | |
270 | /* |
271 | * get an empty voice |
272 | * |
273 | * emu->voice_lock is already held. |
274 | */ |
275 | static struct snd_emux_voice * |
276 | get_voice(struct snd_emux *emu, struct snd_emux_port *port) |
277 | { |
278 | struct snd_emu10k1 *hw; |
279 | struct snd_emux_voice *vp; |
280 | struct best_voice best[V_END]; |
281 | int i; |
282 | |
283 | hw = emu->hw; |
284 | |
285 | lookup_voices(emu, hw, best, active_only: 0); |
286 | for (i = 0; i < V_END; i++) { |
287 | if (best[i].voice >= 0) { |
288 | vp = &emu->voices[best[i].voice]; |
289 | if (vp->ch < 0) { |
290 | /* allocate a voice */ |
291 | struct snd_emu10k1_voice *hwvoice; |
292 | if (snd_emu10k1_voice_alloc(emu: hw, type: EMU10K1_SYNTH, count: 1, channels: 1, NULL, rvoice: &hwvoice) < 0) |
293 | continue; |
294 | vp->ch = hwvoice->number; |
295 | emu->num_voices++; |
296 | } |
297 | return vp; |
298 | } |
299 | } |
300 | |
301 | /* not found */ |
302 | return NULL; |
303 | } |
304 | |
305 | /* |
306 | * prepare envelopes and LFOs |
307 | */ |
308 | static int |
309 | start_voice(struct snd_emux_voice *vp) |
310 | { |
311 | unsigned int temp; |
312 | int ch; |
313 | u32 psst, dsl, map, ccca, vtarget; |
314 | unsigned int addr, mapped_offset; |
315 | struct snd_midi_channel *chan; |
316 | struct snd_emu10k1 *hw; |
317 | struct snd_emu10k1_memblk *emem; |
318 | |
319 | hw = vp->hw; |
320 | ch = vp->ch; |
321 | if (snd_BUG_ON(ch < 0)) |
322 | return -EINVAL; |
323 | chan = vp->chan; |
324 | |
325 | emem = (struct snd_emu10k1_memblk *)vp->block; |
326 | if (emem == NULL) |
327 | return -EINVAL; |
328 | emem->map_locked++; |
329 | if (snd_emu10k1_memblk_map(emu: hw, blk: emem) < 0) { |
330 | /* dev_err(hw->card->devK, "emu: cannot map!\n"); */ |
331 | return -ENOMEM; |
332 | } |
333 | mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; |
334 | vp->reg.start += mapped_offset; |
335 | vp->reg.end += mapped_offset; |
336 | vp->reg.loopstart += mapped_offset; |
337 | vp->reg.loopend += mapped_offset; |
338 | |
339 | /* set channel routing */ |
340 | /* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */ |
341 | if (hw->audigy) { |
342 | temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | |
343 | (FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24); |
344 | snd_emu10k1_ptr_write(emu: hw, A_FXRT1, chn: ch, data: temp); |
345 | } else { |
346 | temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | |
347 | (FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28); |
348 | snd_emu10k1_ptr_write(emu: hw, FXRT, chn: ch, data: temp); |
349 | } |
350 | |
351 | temp = vp->reg.parm.reverb; |
352 | temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; |
353 | LIMITMAX(temp, 255); |
354 | addr = vp->reg.loopstart; |
355 | psst = (temp << 24) | addr; |
356 | |
357 | addr = vp->reg.loopend; |
358 | temp = vp->reg.parm.chorus; |
359 | temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; |
360 | LIMITMAX(temp, 255); |
361 | dsl = (temp << 24) | addr; |
362 | |
363 | map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0); |
364 | |
365 | addr = vp->reg.start; |
366 | temp = vp->reg.parm.filterQ; |
367 | ccca = (temp << 28) | addr; |
368 | if (vp->apitch < 0xe400) |
369 | ccca |= CCCA_INTERPROM_0; |
370 | else { |
371 | unsigned int shift = (vp->apitch - 0xe000) >> 10; |
372 | ccca |= shift << 25; |
373 | } |
374 | if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) |
375 | ccca |= CCCA_8BITSELECT; |
376 | |
377 | vtarget = (unsigned int)vp->vtarget << 16; |
378 | |
379 | snd_emu10k1_ptr_write_multiple(emu: hw, chn: ch, |
380 | /* channel to be silent and idle */ |
381 | DCYSUSV, 0, |
382 | VTFT, VTFT_FILTERTARGET_MASK, |
383 | CVCF, CVCF_CURRENTFILTER_MASK, |
384 | PTRX, 0, |
385 | CPF, 0, |
386 | |
387 | /* set pitch offset */ |
388 | IP, vp->apitch, |
389 | |
390 | /* set envelope parameters */ |
391 | ENVVAL, vp->reg.parm.moddelay, |
392 | ATKHLDM, vp->reg.parm.modatkhld, |
393 | DCYSUSM, vp->reg.parm.moddcysus, |
394 | ENVVOL, vp->reg.parm.voldelay, |
395 | ATKHLDV, vp->reg.parm.volatkhld, |
396 | /* decay/sustain parameter for volume envelope is used |
397 | for triggerg the voice */ |
398 | |
399 | /* cutoff and volume */ |
400 | IFATN, (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol, |
401 | |
402 | /* modulation envelope heights */ |
403 | PEFE, vp->reg.parm.pefe, |
404 | |
405 | /* lfo1/2 delay */ |
406 | LFOVAL1, vp->reg.parm.lfo1delay, |
407 | LFOVAL2, vp->reg.parm.lfo2delay, |
408 | |
409 | /* lfo1 pitch & cutoff shift */ |
410 | FMMOD, make_fmmod(vp), |
411 | /* lfo1 volume & freq */ |
412 | TREMFRQ, vp->reg.parm.tremfrq, |
413 | /* lfo2 pitch & freq */ |
414 | FM2FRQ2, make_fm2frq2(vp), |
415 | |
416 | /* reverb and loop start (reverb 8bit, MSB) */ |
417 | PSST, psst, |
418 | |
419 | /* chorus & loop end (chorus 8bit, MSB) */ |
420 | DSL, dsl, |
421 | |
422 | /* clear filter delay memory */ |
423 | Z1, 0, |
424 | Z2, 0, |
425 | |
426 | /* invalidate maps */ |
427 | MAPA, map, |
428 | MAPB, map, |
429 | |
430 | /* Q & current address (Q 4bit value, MSB) */ |
431 | CCCA, ccca, |
432 | |
433 | /* reset volume */ |
434 | VTFT, vtarget | vp->ftarget, |
435 | CVCF, vtarget | CVCF_CURRENTFILTER_MASK, |
436 | |
437 | REGLIST_END); |
438 | |
439 | hw->voices[ch].dirty = 1; |
440 | return 0; |
441 | } |
442 | |
443 | /* |
444 | * Start envelope |
445 | */ |
446 | static void |
447 | trigger_voice(struct snd_emux_voice *vp) |
448 | { |
449 | unsigned int ptarget; |
450 | struct snd_emu10k1 *hw; |
451 | struct snd_emu10k1_memblk *emem; |
452 | |
453 | hw = vp->hw; |
454 | |
455 | emem = (struct snd_emu10k1_memblk *)vp->block; |
456 | if (! emem || emem->mapped_page < 0) |
457 | return; /* not mapped */ |
458 | |
459 | #if 0 |
460 | ptarget = (unsigned int)vp->ptarget << 16; |
461 | #else |
462 | ptarget = IP_TO_CP(vp->apitch); |
463 | #endif |
464 | snd_emu10k1_ptr_write_multiple(emu: hw, chn: vp->ch, |
465 | /* set pitch target and pan (volume) */ |
466 | PTRX, ptarget | (vp->apan << 8) | vp->aaux, |
467 | |
468 | /* current pitch and fractional address */ |
469 | CPF, ptarget, |
470 | |
471 | /* enable envelope engine */ |
472 | DCYSUSV, vp->reg.parm.voldcysus | DCYSUSV_CHANNELENABLE_MASK, |
473 | |
474 | REGLIST_END); |
475 | } |
476 | |
477 | #define MOD_SENSE 18 |
478 | |
479 | /* calculate lfo1 modulation height and cutoff register */ |
480 | static u32 |
481 | make_fmmod(struct snd_emux_voice *vp) |
482 | { |
483 | short pitch; |
484 | unsigned char cutoff; |
485 | int modulation; |
486 | |
487 | pitch = (char)(vp->reg.parm.fmmod>>8); |
488 | cutoff = (vp->reg.parm.fmmod & 0xff); |
489 | modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; |
490 | pitch += (MOD_SENSE * modulation) / 1200; |
491 | LIMITVALUE(pitch, -128, 127); |
492 | return ((unsigned char)pitch << 8) | cutoff; |
493 | } |
494 | |
495 | /* calculate set lfo2 pitch & frequency register */ |
496 | static u32 |
497 | make_fm2frq2(struct snd_emux_voice *vp) |
498 | { |
499 | short pitch; |
500 | unsigned char freq; |
501 | int modulation; |
502 | |
503 | pitch = (char)(vp->reg.parm.fm2frq2>>8); |
504 | freq = vp->reg.parm.fm2frq2 & 0xff; |
505 | modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; |
506 | pitch += (MOD_SENSE * modulation) / 1200; |
507 | LIMITVALUE(pitch, -128, 127); |
508 | return ((unsigned char)pitch << 8) | freq; |
509 | } |
510 | |
511 | static int get_pitch_shift(struct snd_emux *emu) |
512 | { |
513 | struct snd_emu10k1 *hw = emu->hw; |
514 | |
515 | return (hw->card_capabilities->emu_model && |
516 | hw->emu1010.word_clock == 44100) ? 0 : -501; |
517 | } |
518 | |