Linux 6.7-rc7
[linux-modified.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  *                   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
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);