Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/pci/emu10k1/voice.c
26451 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (c) by Jaroslav Kysela <[email protected]>
4
* Lee Revell <[email protected]>
5
* Oswald Buddenhagen <[email protected]>
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
24
static 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
67
static 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
77
int 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
121
EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
122
123
int 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
140
EXPORT_SYMBOL(snd_emu10k1_voice_free);
141
142