1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 4 * Lee Revell <rlrevell@joe-job.com> 5 * Oswald Buddenhagen <oswald.buddenhagen@gmx.de> 6 * Creative Labs, Inc. 7 * 8 * Routines for control of EMU10K1 chips - voice manager 9 */ 10 11 #include <linux/time.h> 12 #include <linux/export.h> 13 #include <sound/core.h> 14 #include <sound/emu10k1.h> 15 16 /* Previously the voice allocator started at 0 every time. The new voice 17 * allocator uses a round robin scheme. The next free voice is tracked in 18 * the card record and each allocation begins where the last left off. The 19 * hardware requires stereo interleaved voices be aligned to an even/odd 20 * boundary. 21 * --rlrevell 22 */ 23 24 static int voice_alloc(struct snd_emu10k1 *emu, int type, int number, 25 struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice) 26 { 27 struct snd_emu10k1_voice *voice; 28 int i, j, k, skip; 29 30 for (i = emu->next_free_voice, j = 0; j < NUM_G; i = (i + skip) % NUM_G, j += skip) { 31 /* 32 dev_dbg(emu->card->dev, "i %d j %d next free %d!\n", 33 i, j, emu->next_free_voice); 34 */ 35 36 /* stereo voices must be even/odd */ 37 if ((number > 1) && (i % 2)) { 38 skip = 1; 39 continue; 40 } 41 42 for (k = 0; k < number; k++) { 43 voice = &emu->voices[i + k]; 44 if (voice->use) { 45 skip = k + 1; 46 goto next; 47 } 48 } 49 50 for (k = 0; k < number; k++) { 51 voice = &emu->voices[i + k]; 52 voice->use = type; 53 voice->epcm = epcm; 54 /* dev_dbg(emu->card->dev, "allocated voice %d\n", i + k); */ 55 } 56 voice->last = 1; 57 58 *rvoice = &emu->voices[i]; 59 emu->next_free_voice = (i + number) % NUM_G; 60 return 0; 61 62 next: ; 63 } 64 return -ENOMEM; // -EBUSY would have been better 65 } 66 67 static void voice_free(struct snd_emu10k1 *emu, 68 struct snd_emu10k1_voice *pvoice) 69 { 70 if (pvoice->dirty) 71 snd_emu10k1_voice_init(emu, pvoice->number); 72 pvoice->interrupt = NULL; 73 pvoice->use = pvoice->dirty = pvoice->last = 0; 74 pvoice->epcm = NULL; 75 } 76 77 int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int count, int channels, 78 struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice) 79 { 80 int result; 81 82 if (snd_BUG_ON(!rvoice)) 83 return -EINVAL; 84 if (snd_BUG_ON(!count)) 85 return -EINVAL; 86 if (snd_BUG_ON(!channels)) 87 return -EINVAL; 88 89 guard(spinlock_irqsave)(&emu->voice_lock); 90 for (int got = 0; got < channels; ) { 91 result = voice_alloc(emu, type, count, epcm, &rvoice[got]); 92 if (result == 0) { 93 got++; 94 /* 95 dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n", 96 rvoice[got - 1]->number, got, want); 97 */ 98 continue; 99 } 100 if (type != EMU10K1_SYNTH && emu->get_synth_voice) { 101 /* free a voice from synth */ 102 result = emu->get_synth_voice(emu); 103 if (result >= 0) { 104 voice_free(emu, &emu->voices[result]); 105 continue; 106 } 107 } 108 for (int i = 0; i < got; i++) { 109 for (int j = 0; j < count; j++) 110 voice_free(emu, rvoice[i] + j); 111 rvoice[i] = NULL; 112 } 113 break; 114 } 115 116 return result; 117 } 118 119 EXPORT_SYMBOL(snd_emu10k1_voice_alloc); 120 121 int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, 122 struct snd_emu10k1_voice *pvoice) 123 { 124 int last; 125 126 if (snd_BUG_ON(!pvoice)) 127 return -EINVAL; 128 guard(spinlock_irqsave)(&emu->voice_lock); 129 do { 130 last = pvoice->last; 131 voice_free(emu, pvoice++); 132 } while (!last); 133 return 0; 134 } 135 136 EXPORT_SYMBOL(snd_emu10k1_voice_free); 137