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. For multichannel voice allocation we ensure than the block of 27 * voices does not cross the 32 voice boundary. This simplifies the 28 * multichannel support and ensures we can use a single write to the 29 * (set|clear)_loop_stop registers. Otherwise (for example) the voices would 30 * get out of sync when pausing/resuming a stream. 31 * --rlrevell 32 */ 33 34 static int voice_alloc(struct snd_emu10k1 *emu, int type, int number, 35 struct snd_emu10k1_voice **rvoice) 36 { 37 struct snd_emu10k1_voice *voice; 38 int i, j, k, first_voice, last_voice, skip; 39 40 *rvoice = NULL; 41 first_voice = last_voice = 0; 42 for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) { 43 /* 44 dev_dbg(emu->card->dev, "i %d j %d next free %d!\n", 45 i, j, emu->next_free_voice); 46 */ 47 i %= NUM_G; 48 49 /* stereo voices must be even/odd */ 50 if ((number == 2) && (i % 2)) { 51 i++; 52 continue; 53 } 54 55 skip = 0; 56 for (k = 0; k < number; k++) { 57 voice = &emu->voices[(i+k) % NUM_G]; 58 if (voice->use) { 59 skip = 1; 60 break; 61 } 62 } 63 if (!skip) { 64 /* dev_dbg(emu->card->dev, "allocated voice %d\n", i); */ 65 first_voice = i; 66 last_voice = (i + number) % NUM_G; 67 emu->next_free_voice = last_voice; 68 break; 69 } 70 } 71 72 if (first_voice == last_voice) 73 return -ENOMEM; 74 75 for (i = 0; i < number; i++) { 76 voice = &emu->voices[(first_voice + i) % NUM_G]; 77 /* 78 dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n", 79 voice->number, idx-first_voice+1, number); 80 */ 81 voice->use = 1; 82 switch (type) { 83 case EMU10K1_PCM: 84 voice->pcm = 1; 85 break; 86 case EMU10K1_SYNTH: 87 voice->synth = 1; 88 break; 89 case EMU10K1_MIDI: 90 voice->midi = 1; 91 break; 92 case EMU10K1_EFX: 93 voice->efx = 1; 94 break; 95 } 96 } 97 *rvoice = &emu->voices[first_voice]; 98 return 0; 99 } 100 101 int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number, 102 struct snd_emu10k1_voice **rvoice) 103 { 104 unsigned long flags; 105 int result; 106 107 if (snd_BUG_ON(!rvoice)) 108 return -EINVAL; 109 if (snd_BUG_ON(!number)) 110 return -EINVAL; 111 112 spin_lock_irqsave(&emu->voice_lock, flags); 113 for (;;) { 114 result = voice_alloc(emu, type, number, rvoice); 115 if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI) 116 break; 117 118 /* free a voice from synth */ 119 if (emu->get_synth_voice) { 120 result = emu->get_synth_voice(emu); 121 if (result >= 0) { 122 struct snd_emu10k1_voice *pvoice = &emu->voices[result]; 123 pvoice->interrupt = NULL; 124 pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0; 125 pvoice->epcm = NULL; 126 } 127 } 128 if (result < 0) 129 break; 130 } 131 spin_unlock_irqrestore(&emu->voice_lock, flags); 132 133 return result; 134 } 135 136 EXPORT_SYMBOL(snd_emu10k1_voice_alloc); 137 138 int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, 139 struct snd_emu10k1_voice *pvoice) 140 { 141 unsigned long flags; 142 143 if (snd_BUG_ON(!pvoice)) 144 return -EINVAL; 145 spin_lock_irqsave(&emu->voice_lock, flags); 146 pvoice->interrupt = NULL; 147 pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0; 148 pvoice->epcm = NULL; 149 snd_emu10k1_voice_init(emu, pvoice->number); 150 spin_unlock_irqrestore(&emu->voice_lock, flags); 151 return 0; 152 } 153 154 EXPORT_SYMBOL(snd_emu10k1_voice_free); 155