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