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