141 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>4 * Lee Revell <rlrevell@joe-job.com>5 * Oswald Buddenhagen <oswald.buddenhagen@gmx.de>6 * Creative Labs, Inc.7 *8 * Routines for control of EMU10K1 chips - voice manager9 */10 11#include <linux/time.h>12#include <linux/export.h>13#include <sound/core.h>14#include <sound/emu10k1.h>15 16/* Previously the voice allocator started at 0 every time. The new voice 17 * allocator uses a round robin scheme. The next free voice is tracked in 18 * the card record and each allocation begins where the last left off. The 19 * hardware requires stereo interleaved voices be aligned to an even/odd 20 * boundary.21 * --rlrevell22 */23 24static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,25 struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)26{27 struct snd_emu10k1_voice *voice;28 int i, j, k, skip;29 30 for (i = emu->next_free_voice, j = 0; j < NUM_G; i = (i + skip) % NUM_G, j += skip) {31 /*32 dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",33 i, j, emu->next_free_voice);34 */35 36 /* stereo voices must be even/odd */37 if ((number > 1) && (i % 2)) {38 skip = 1;39 continue;40 }41 42 for (k = 0; k < number; k++) {43 voice = &emu->voices[i + k];44 if (voice->use) {45 skip = k + 1;46 goto next;47 }48 }49 50 for (k = 0; k < number; k++) {51 voice = &emu->voices[i + k];52 voice->use = type;53 voice->epcm = epcm;54 /* dev_dbg(emu->card->dev, "allocated voice %d\n", i + k); */55 }56 voice->last = 1;57 58 *rvoice = &emu->voices[i];59 emu->next_free_voice = (i + number) % NUM_G;60 return 0;61 62 next: ;63 }64 return -ENOMEM; // -EBUSY would have been better65}66 67static void voice_free(struct snd_emu10k1 *emu,68 struct snd_emu10k1_voice *pvoice)69{70 if (pvoice->dirty)71 snd_emu10k1_voice_init(emu, pvoice->number);72 pvoice->interrupt = NULL;73 pvoice->use = pvoice->dirty = pvoice->last = 0;74 pvoice->epcm = NULL;75}76 77int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int count, int channels,78 struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)79{80 unsigned long flags;81 int result;82 83 if (snd_BUG_ON(!rvoice))84 return -EINVAL;85 if (snd_BUG_ON(!count))86 return -EINVAL;87 if (snd_BUG_ON(!channels))88 return -EINVAL;89 90 spin_lock_irqsave(&emu->voice_lock, flags);91 for (int got = 0; got < channels; ) {92 result = voice_alloc(emu, type, count, epcm, &rvoice[got]);93 if (result == 0) {94 got++;95 /*96 dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",97 rvoice[got - 1]->number, got, want);98 */99 continue;100 }101 if (type != EMU10K1_SYNTH && emu->get_synth_voice) {102 /* free a voice from synth */103 result = emu->get_synth_voice(emu);104 if (result >= 0) {105 voice_free(emu, &emu->voices[result]);106 continue;107 }108 }109 for (int i = 0; i < got; i++) {110 for (int j = 0; j < count; j++)111 voice_free(emu, rvoice[i] + j);112 rvoice[i] = NULL;113 }114 break;115 }116 spin_unlock_irqrestore(&emu->voice_lock, flags);117 118 return result;119}120 121EXPORT_SYMBOL(snd_emu10k1_voice_alloc);122 123int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,124 struct snd_emu10k1_voice *pvoice)125{126 unsigned long flags;127 int last;128 129 if (snd_BUG_ON(!pvoice))130 return -EINVAL;131 spin_lock_irqsave(&emu->voice_lock, flags);132 do {133 last = pvoice->last;134 voice_free(emu, pvoice++);135 } while (!last);136 spin_unlock_irqrestore(&emu->voice_lock, flags);137 return 0;138}139 140EXPORT_SYMBOL(snd_emu10k1_voice_free);141