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