#include <linux/time.h>
#include <linux/export.h>
#include <sound/core.h>
#include <sound/emu10k1.h>
static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)
{
struct snd_emu10k1_voice *voice;
int i, j, k, skip;
for (i = emu->next_free_voice, j = 0; j < NUM_G; i = (i + skip) % NUM_G, j += skip) {
if ((number > 1) && (i % 2)) {
skip = 1;
continue;
}
for (k = 0; k < number; k++) {
voice = &emu->voices[i + k];
if (voice->use) {
skip = k + 1;
goto next;
}
}
for (k = 0; k < number; k++) {
voice = &emu->voices[i + k];
voice->use = type;
voice->epcm = epcm;
}
voice->last = 1;
*rvoice = &emu->voices[i];
emu->next_free_voice = (i + number) % NUM_G;
return 0;
next: ;
}
return -ENOMEM;
}
static void voice_free(struct snd_emu10k1 *emu,
struct snd_emu10k1_voice *pvoice)
{
if (pvoice->dirty)
snd_emu10k1_voice_init(emu, pvoice->number);
pvoice->interrupt = NULL;
pvoice->use = pvoice->dirty = pvoice->last = 0;
pvoice->epcm = NULL;
}
int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int count, int channels,
struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)
{
unsigned long flags;
int result;
if (snd_BUG_ON(!rvoice))
return -EINVAL;
if (snd_BUG_ON(!count))
return -EINVAL;
if (snd_BUG_ON(!channels))
return -EINVAL;
spin_lock_irqsave(&emu->voice_lock, flags);
for (int got = 0; got < channels; ) {
result = voice_alloc(emu, type, count, epcm, &rvoice[got]);
if (result == 0) {
got++;
continue;
}
if (type != EMU10K1_SYNTH && emu->get_synth_voice) {
result = emu->get_synth_voice(emu);
if (result >= 0) {
voice_free(emu, &emu->voices[result]);
continue;
}
}
for (int i = 0; i < got; i++) {
for (int j = 0; j < count; j++)
voice_free(emu, rvoice[i] + j);
rvoice[i] = NULL;
}
break;
}
spin_unlock_irqrestore(&emu->voice_lock, flags);
return result;
}
EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
struct snd_emu10k1_voice *pvoice)
{
unsigned long flags;
int last;
if (snd_BUG_ON(!pvoice))
return -EINVAL;
spin_lock_irqsave(&emu->voice_lock, flags);
do {
last = pvoice->last;
voice_free(emu, pvoice++);
} while (!last);
spin_unlock_irqrestore(&emu->voice_lock, flags);
return 0;
}
EXPORT_SYMBOL