Merge tag 'pstore-v5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees...
[linux-2.6-microblaze.git] / sound / pci / emu10k1 / voice.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4  *                   Creative Labs, Inc.
5  *                   Lee Revell <rlrevell@joe-job.com>
6  *  Routines for control of EMU10K1 chips - voice manager
7  *
8  *  Rewrote voice allocator for multichannel support - rlrevell 12/2004
9  * 
10  *  BUGS:
11  *    --
12  *
13  *  TODO:
14  *    --
15  */
16
17 #include <linux/time.h>
18 #include <linux/export.h>
19 #include <sound/core.h>
20 #include <sound/emu10k1.h>
21
22 /* Previously the voice allocator started at 0 every time.  The new voice 
23  * allocator uses a round robin scheme.  The next free voice is tracked in 
24  * the card record and each allocation begins where the last left off.  The 
25  * hardware requires stereo interleaved voices be aligned to an even/odd 
26  * boundary.  For multichannel voice allocation we ensure than the block of 
27  * voices does not cross the 32 voice boundary.  This simplifies the 
28  * multichannel support and ensures we can use a single write to the 
29  * (set|clear)_loop_stop registers.  Otherwise (for example) the voices would 
30  * get out of sync when pausing/resuming a stream.
31  *                                                      --rlrevell
32  */
33
34 static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
35                        struct snd_emu10k1_voice **rvoice)
36 {
37         struct snd_emu10k1_voice *voice;
38         int i, j, k, first_voice, last_voice, skip;
39
40         *rvoice = NULL;
41         first_voice = last_voice = 0;
42         for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
43                 /*
44                 dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",
45                        i, j, emu->next_free_voice);
46                 */
47                 i %= NUM_G;
48
49                 /* stereo voices must be even/odd */
50                 if ((number == 2) && (i % 2)) {
51                         i++;
52                         continue;
53                 }
54                         
55                 skip = 0;
56                 for (k = 0; k < number; k++) {
57                         voice = &emu->voices[(i+k) % NUM_G];
58                         if (voice->use) {
59                                 skip = 1;
60                                 break;
61                         }
62                 }
63                 if (!skip) {
64                         /* dev_dbg(emu->card->dev, "allocated voice %d\n", i); */
65                         first_voice = i;
66                         last_voice = (i + number) % NUM_G;
67                         emu->next_free_voice = last_voice;
68                         break;
69                 }
70         }
71         
72         if (first_voice == last_voice)
73                 return -ENOMEM;
74         
75         for (i = 0; i < number; i++) {
76                 voice = &emu->voices[(first_voice + i) % NUM_G];
77                 /*
78                 dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
79                        voice->number, idx-first_voice+1, number);
80                 */
81                 voice->use = 1;
82                 switch (type) {
83                 case EMU10K1_PCM:
84                         voice->pcm = 1;
85                         break;
86                 case EMU10K1_SYNTH:
87                         voice->synth = 1;
88                         break;
89                 case EMU10K1_MIDI:
90                         voice->midi = 1;
91                         break;
92                 case EMU10K1_EFX:
93                         voice->efx = 1;
94                         break;
95                 }
96         }
97         *rvoice = &emu->voices[first_voice];
98         return 0;
99 }
100
101 int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
102                             struct snd_emu10k1_voice **rvoice)
103 {
104         unsigned long flags;
105         int result;
106
107         if (snd_BUG_ON(!rvoice))
108                 return -EINVAL;
109         if (snd_BUG_ON(!number))
110                 return -EINVAL;
111
112         spin_lock_irqsave(&emu->voice_lock, flags);
113         for (;;) {
114                 result = voice_alloc(emu, type, number, rvoice);
115                 if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
116                         break;
117
118                 /* free a voice from synth */
119                 if (emu->get_synth_voice) {
120                         result = emu->get_synth_voice(emu);
121                         if (result >= 0) {
122                                 struct snd_emu10k1_voice *pvoice = &emu->voices[result];
123                                 pvoice->interrupt = NULL;
124                                 pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
125                                 pvoice->epcm = NULL;
126                         }
127                 }
128                 if (result < 0)
129                         break;
130         }
131         spin_unlock_irqrestore(&emu->voice_lock, flags);
132
133         return result;
134 }
135
136 EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
137
138 int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
139                            struct snd_emu10k1_voice *pvoice)
140 {
141         unsigned long flags;
142
143         if (snd_BUG_ON(!pvoice))
144                 return -EINVAL;
145         spin_lock_irqsave(&emu->voice_lock, flags);
146         pvoice->interrupt = NULL;
147         pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
148         pvoice->epcm = NULL;
149         snd_emu10k1_voice_init(emu, pvoice->number);
150         spin_unlock_irqrestore(&emu->voice_lock, flags);
151         return 0;
152 }
153
154 EXPORT_SYMBOL(snd_emu10k1_voice_free);