1 /* 2 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 3 * Creative Labs, Inc. 4 * Lee Revell <rlrevell@joe-job.com> 5 * Routines for control of EMU10K1 chips - voice manager 6 * 7 * Rewrote voice allocator for multichannel support - rlrevell 12/2004 8 * 9 * BUGS: 10 * -- 11 * 12 * TODO: 13 * -- 14 * 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 2 of the License, or 18 * (at your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program; if not, write to the Free Software 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 * 29 */ 30 31 #include <linux/time.h> 32 #include <sound/core.h> 33 #include <sound/emu10k1.h> 34 35 /* Previously the voice allocator started at 0 every time. The new voice 36 * allocator uses a round robin scheme. The next free voice is tracked in 37 * the card record and each allocation begins where the last left off. The 38 * hardware requires stereo interleaved voices be aligned to an even/odd 39 * boundary. For multichannel voice allocation we ensure than the block of 40 * voices does not cross the 32 voice boundary. This simplifies the 41 * multichannel support and ensures we can use a single write to the 42 * (set|clear)_loop_stop registers. Otherwise (for example) the voices would 43 * get out of sync when pausing/resuming a stream. 44 * --rlrevell 45 */ 46 47 static int voice_alloc(struct snd_emu10k1 *emu, int type, int number, 48 struct snd_emu10k1_voice **rvoice) 49 { 50 struct snd_emu10k1_voice *voice; 51 int i, j, k, first_voice, last_voice, skip; 52 53 *rvoice = NULL; 54 first_voice = last_voice = 0; 55 for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) { 56 // printk("i %d j %d next free %d!\n", i, j, emu->next_free_voice); 57 i %= NUM_G; 58 59 /* stereo voices must be even/odd */ 60 if ((number == 2) && (i % 2)) { 61 i++; 62 continue; 63 } 64 65 skip = 0; 66 for (k = 0; k < number; k++) { 67 voice = &emu->voices[(i+k) % NUM_G]; 68 if (voice->use) { 69 skip = 1; 70 break; 71 } 72 } 73 if (!skip) { 74 // printk("allocated voice %d\n", i); 75 first_voice = i; 76 last_voice = (i + number) % NUM_G; 77 emu->next_free_voice = last_voice; 78 break; 79 } 80 } 81 82 if (first_voice == last_voice) 83 return -ENOMEM; 84 85 for (i = 0; i < number; i++) { 86 voice = &emu->voices[(first_voice + i) % NUM_G]; 87 // printk("voice alloc - %i, %i of %i\n", voice->number, idx-first_voice+1, number); 88 voice->use = 1; 89 switch (type) { 90 case EMU10K1_PCM: 91 voice->pcm = 1; 92 break; 93 case EMU10K1_SYNTH: 94 voice->synth = 1; 95 break; 96 case EMU10K1_MIDI: 97 voice->midi = 1; 98 break; 99 case EMU10K1_EFX: 100 voice->efx = 1; 101 break; 102 } 103 } 104 *rvoice = &emu->voices[first_voice]; 105 return 0; 106 } 107 108 int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number, 109 struct snd_emu10k1_voice **rvoice) 110 { 111 unsigned long flags; 112 int result; 113 114 snd_assert(rvoice != NULL, return -EINVAL); 115 snd_assert(number, return -EINVAL); 116 117 spin_lock_irqsave(&emu->voice_lock, flags); 118 for (;;) { 119 result = voice_alloc(emu, type, number, rvoice); 120 if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI) 121 break; 122 123 /* free a voice from synth */ 124 if (emu->get_synth_voice) { 125 result = emu->get_synth_voice(emu); 126 if (result >= 0) { 127 struct snd_emu10k1_voice *pvoice = &emu->voices[result]; 128 pvoice->interrupt = NULL; 129 pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0; 130 pvoice->epcm = NULL; 131 } 132 } 133 if (result < 0) 134 break; 135 } 136 spin_unlock_irqrestore(&emu->voice_lock, flags); 137 138 return result; 139 } 140 141 EXPORT_SYMBOL(snd_emu10k1_voice_alloc); 142 143 int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, 144 struct snd_emu10k1_voice *pvoice) 145 { 146 unsigned long flags; 147 148 snd_assert(pvoice != NULL, return -EINVAL); 149 spin_lock_irqsave(&emu->voice_lock, flags); 150 pvoice->interrupt = NULL; 151 pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0; 152 pvoice->epcm = NULL; 153 snd_emu10k1_voice_init(emu, pvoice->number); 154 spin_unlock_irqrestore(&emu->voice_lock, flags); 155 return 0; 156 } 157 158 EXPORT_SYMBOL(snd_emu10k1_voice_free); 159