Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/pci/emu10k1/voice.c
10817 views
1
/*
2
* Copyright (c) by Jaroslav Kysela <[email protected]>
3
* Creative Labs, Inc.
4
* Lee Revell <[email protected]>
5
* Routines for control of EMU10K1 chips - voice manager
6
*
7
* Rewrote voice allocator for multichannel support - rlrevell 12/2004
8
*
9
* BUGS:
10
* --
11
*
12
* TODO:
13
* --
14
*
15
* This program is free software; you can redistribute it and/or modify
16
* it under the terms of the GNU General Public License as published by
17
* the Free Software Foundation; either version 2 of the License, or
18
* (at your option) any later version.
19
*
20
* This program is distributed in the hope that it will be useful,
21
* but WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
* GNU General Public License for more details.
24
*
25
* You should have received a copy of the GNU General Public License
26
* along with this program; if not, write to the Free Software
27
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
*
29
*/
30
31
#include <linux/time.h>
32
#include <sound/core.h>
33
#include <sound/emu10k1.h>
34
35
/* Previously the voice allocator started at 0 every time. The new voice
36
* allocator uses a round robin scheme. The next free voice is tracked in
37
* the card record and each allocation begins where the last left off. The
38
* hardware requires stereo interleaved voices be aligned to an even/odd
39
* boundary. For multichannel voice allocation we ensure than the block of
40
* voices does not cross the 32 voice boundary. This simplifies the
41
* multichannel support and ensures we can use a single write to the
42
* (set|clear)_loop_stop registers. Otherwise (for example) the voices would
43
* get out of sync when pausing/resuming a stream.
44
* --rlrevell
45
*/
46
47
static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
48
struct snd_emu10k1_voice **rvoice)
49
{
50
struct snd_emu10k1_voice *voice;
51
int i, j, k, first_voice, last_voice, skip;
52
53
*rvoice = NULL;
54
first_voice = last_voice = 0;
55
for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
56
/*
57
printk(KERN_DEBUG "i %d j %d next free %d!\n",
58
i, j, emu->next_free_voice);
59
*/
60
i %= NUM_G;
61
62
/* stereo voices must be even/odd */
63
if ((number == 2) && (i % 2)) {
64
i++;
65
continue;
66
}
67
68
skip = 0;
69
for (k = 0; k < number; k++) {
70
voice = &emu->voices[(i+k) % NUM_G];
71
if (voice->use) {
72
skip = 1;
73
break;
74
}
75
}
76
if (!skip) {
77
/* printk(KERN_DEBUG "allocated voice %d\n", i); */
78
first_voice = i;
79
last_voice = (i + number) % NUM_G;
80
emu->next_free_voice = last_voice;
81
break;
82
}
83
}
84
85
if (first_voice == last_voice)
86
return -ENOMEM;
87
88
for (i = 0; i < number; i++) {
89
voice = &emu->voices[(first_voice + i) % NUM_G];
90
/*
91
printk(kERN_DEBUG "voice alloc - %i, %i of %i\n",
92
voice->number, idx-first_voice+1, number);
93
*/
94
voice->use = 1;
95
switch (type) {
96
case EMU10K1_PCM:
97
voice->pcm = 1;
98
break;
99
case EMU10K1_SYNTH:
100
voice->synth = 1;
101
break;
102
case EMU10K1_MIDI:
103
voice->midi = 1;
104
break;
105
case EMU10K1_EFX:
106
voice->efx = 1;
107
break;
108
}
109
}
110
*rvoice = &emu->voices[first_voice];
111
return 0;
112
}
113
114
int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
115
struct snd_emu10k1_voice **rvoice)
116
{
117
unsigned long flags;
118
int result;
119
120
if (snd_BUG_ON(!rvoice))
121
return -EINVAL;
122
if (snd_BUG_ON(!number))
123
return -EINVAL;
124
125
spin_lock_irqsave(&emu->voice_lock, flags);
126
for (;;) {
127
result = voice_alloc(emu, type, number, rvoice);
128
if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
129
break;
130
131
/* free a voice from synth */
132
if (emu->get_synth_voice) {
133
result = emu->get_synth_voice(emu);
134
if (result >= 0) {
135
struct snd_emu10k1_voice *pvoice = &emu->voices[result];
136
pvoice->interrupt = NULL;
137
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
138
pvoice->epcm = NULL;
139
}
140
}
141
if (result < 0)
142
break;
143
}
144
spin_unlock_irqrestore(&emu->voice_lock, flags);
145
146
return result;
147
}
148
149
EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
150
151
int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
152
struct snd_emu10k1_voice *pvoice)
153
{
154
unsigned long flags;
155
156
if (snd_BUG_ON(!pvoice))
157
return -EINVAL;
158
spin_lock_irqsave(&emu->voice_lock, flags);
159
pvoice->interrupt = NULL;
160
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
161
pvoice->epcm = NULL;
162
snd_emu10k1_voice_init(emu, pvoice->number);
163
spin_unlock_irqrestore(&emu->voice_lock, flags);
164
return 0;
165
}
166
167
EXPORT_SYMBOL(snd_emu10k1_voice_free);
168
169