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 /* 57 printk(KERN_DEBUG "i %d j %d next free %d!\n", 58 i, j, emu->next_free_voice); 59 */ 60 i %= NUM_G; 61 62 /* stereo voices must be even/odd */ 63 if ((number == 2) && (i % 2)) { 64 i++; 65 continue; 66 } 67 68 skip = 0; 69 for (k = 0; k < number; k++) { 70 voice = &emu->voices[(i+k) % NUM_G]; 71 if (voice->use) { 72 skip = 1; 73 break; 74 } 75 } 76 if (!skip) { 77 /* printk(KERN_DEBUG "allocated voice %d\n", i); */ 78 first_voice = i; 79 last_voice = (i + number) % NUM_G; 80 emu->next_free_voice = last_voice; 81 break; 82 } 83 } 84 85 if (first_voice == last_voice) 86 return -ENOMEM; 87 88 for (i = 0; i < number; i++) { 89 voice = &emu->voices[(first_voice + i) % NUM_G]; 90 /* 91 printk(kERN_DEBUG "voice alloc - %i, %i of %i\n", 92 voice->number, idx-first_voice+1, number); 93 */ 94 voice->use = 1; 95 switch (type) { 96 case EMU10K1_PCM: 97 voice->pcm = 1; 98 break; 99 case EMU10K1_SYNTH: 100 voice->synth = 1; 101 break; 102 case EMU10K1_MIDI: 103 voice->midi = 1; 104 break; 105 case EMU10K1_EFX: 106 voice->efx = 1; 107 break; 108 } 109 } 110 *rvoice = &emu->voices[first_voice]; 111 return 0; 112 } 113 114 int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number, 115 struct snd_emu10k1_voice **rvoice) 116 { 117 unsigned long flags; 118 int result; 119 120 if (snd_BUG_ON(!rvoice)) 121 return -EINVAL; 122 if (snd_BUG_ON(!number)) 123 return -EINVAL; 124 125 spin_lock_irqsave(&emu->voice_lock, flags); 126 for (;;) { 127 result = voice_alloc(emu, type, number, rvoice); 128 if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI) 129 break; 130 131 /* free a voice from synth */ 132 if (emu->get_synth_voice) { 133 result = emu->get_synth_voice(emu); 134 if (result >= 0) { 135 struct snd_emu10k1_voice *pvoice = &emu->voices[result]; 136 pvoice->interrupt = NULL; 137 pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0; 138 pvoice->epcm = NULL; 139 } 140 } 141 if (result < 0) 142 break; 143 } 144 spin_unlock_irqrestore(&emu->voice_lock, flags); 145 146 return result; 147 } 148 149 EXPORT_SYMBOL(snd_emu10k1_voice_alloc); 150 151 int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, 152 struct snd_emu10k1_voice *pvoice) 153 { 154 unsigned long flags; 155 156 if (snd_BUG_ON(!pvoice)) 157 return -EINVAL; 158 spin_lock_irqsave(&emu->voice_lock, flags); 159 pvoice->interrupt = NULL; 160 pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0; 161 pvoice->epcm = NULL; 162 snd_emu10k1_voice_init(emu, pvoice->number); 163 spin_unlock_irqrestore(&emu->voice_lock, flags); 164 return 0; 165 } 166 167 EXPORT_SYMBOL(snd_emu10k1_voice_free); 168