| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- * Lee Revell <rlrevell@joe-job.com>
- * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
- * Creative Labs, Inc.
- *
- * Routines for control of EMU10K1 chips - voice manager
- */
- #include <linux/time.h>
- #include <linux/export.h>
- #include <sound/core.h>
- #include <sound/emu10k1.h>
- /* Previously the voice allocator started at 0 every time. The new voice
- * allocator uses a round robin scheme. The next free voice is tracked in
- * the card record and each allocation begins where the last left off. The
- * hardware requires stereo interleaved voices be aligned to an even/odd
- * boundary.
- * --rlrevell
- */
- 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) {
- /*
- dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",
- i, j, emu->next_free_voice);
- */
- /* stereo voices must be even/odd */
- 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;
- /* dev_dbg(emu->card->dev, "allocated voice %d\n", i + k); */
- }
- voice->last = 1;
- *rvoice = &emu->voices[i];
- emu->next_free_voice = (i + number) % NUM_G;
- return 0;
- next: ;
- }
- return -ENOMEM; // -EBUSY would have been better
- }
- 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++;
- /*
- dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
- rvoice[got - 1]->number, got, want);
- */
- continue;
- }
- if (type != EMU10K1_SYNTH && emu->get_synth_voice) {
- /* free a voice from synth */
- 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(snd_emu10k1_voice_free);
|