1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
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 manager
9 */
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 * --rlrevell
22 */
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 better
65}
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, voice: 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, number: count, epcm, rvoice: &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, pvoice: &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, pvoice: rvoice[i] + j);
112 rvoice[i] = NULL;
113 }
114 break;
115 }
116 spin_unlock_irqrestore(lock: &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: pvoice++);
135 } while (!last);
136 spin_unlock_irqrestore(lock: &emu->voice_lock, flags);
137 return 0;
138}
139
140EXPORT_SYMBOL(snd_emu10k1_voice_free);
141

source code of linux/sound/pci/emu10k1/voice.c