xref: /openbmc/linux/sound/pci/emu10k1/voice.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
3c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
41da177e4SLinus Torvalds  *                   Lee Revell <rlrevell@joe-job.com>
5*6d68d9cbSOswald Buddenhagen  *                   Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
6*6d68d9cbSOswald Buddenhagen  *                   Creative Labs, Inc.
71da177e4SLinus Torvalds  *
8*6d68d9cbSOswald Buddenhagen  *  Routines for control of EMU10K1 chips - voice manager
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #include <linux/time.h>
12d81a6d71SPaul Gortmaker #include <linux/export.h>
131da177e4SLinus Torvalds #include <sound/core.h>
141da177e4SLinus Torvalds #include <sound/emu10k1.h>
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds /* Previously the voice allocator started at 0 every time.  The new voice
171da177e4SLinus Torvalds  * allocator uses a round robin scheme.  The next free voice is tracked in
181da177e4SLinus Torvalds  * the card record and each allocation begins where the last left off.  The
191da177e4SLinus Torvalds  * hardware requires stereo interleaved voices be aligned to an even/odd
20a915d604SOswald Buddenhagen  * boundary.
211da177e4SLinus Torvalds  *							--rlrevell
221da177e4SLinus Torvalds  */
231da177e4SLinus Torvalds 
voice_alloc(struct snd_emu10k1 * emu,int type,int number,struct snd_emu10k1_pcm * epcm,struct snd_emu10k1_voice ** rvoice)24eb4698f3STakashi Iwai static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
25b4fea2d3SOswald Buddenhagen 		       struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)
261da177e4SLinus Torvalds {
27eb4698f3STakashi Iwai 	struct snd_emu10k1_voice *voice;
28a915d604SOswald Buddenhagen 	int i, j, k, skip;
291da177e4SLinus Torvalds 
30a915d604SOswald Buddenhagen 	for (i = emu->next_free_voice, j = 0; j < NUM_G; i = (i + skip) % NUM_G, j += skip) {
3128a97c19STakashi Iwai 		/*
326f002b02STakashi Iwai 		dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",
3328a97c19STakashi Iwai 		       i, j, emu->next_free_voice);
3428a97c19STakashi Iwai 		*/
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds 		/* stereo voices must be even/odd */
37a915d604SOswald Buddenhagen 		if ((number > 1) && (i % 2)) {
38a915d604SOswald Buddenhagen 			skip = 1;
391da177e4SLinus Torvalds 			continue;
401da177e4SLinus Torvalds 		}
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds 		for (k = 0; k < number; k++) {
43a915d604SOswald Buddenhagen 			voice = &emu->voices[i + k];
441da177e4SLinus Torvalds 			if (voice->use) {
45a915d604SOswald Buddenhagen 				skip = k + 1;
46a915d604SOswald Buddenhagen 				goto next;
471da177e4SLinus Torvalds 			}
481da177e4SLinus Torvalds 		}
491da177e4SLinus Torvalds 
50a915d604SOswald Buddenhagen 		for (k = 0; k < number; k++) {
51a915d604SOswald Buddenhagen 			voice = &emu->voices[i + k];
52b840f8d8SOswald Buddenhagen 			voice->use = type;
53b4fea2d3SOswald Buddenhagen 			voice->epcm = epcm;
54a915d604SOswald Buddenhagen 			/* dev_dbg(emu->card->dev, "allocated voice %d\n", i + k); */
551da177e4SLinus Torvalds 		}
56a915d604SOswald Buddenhagen 		voice->last = 1;
57a915d604SOswald Buddenhagen 
58a915d604SOswald Buddenhagen 		*rvoice = &emu->voices[i];
59a915d604SOswald Buddenhagen 		emu->next_free_voice = (i + number) % NUM_G;
601da177e4SLinus Torvalds 		return 0;
61a915d604SOswald Buddenhagen 
62a915d604SOswald Buddenhagen 	next: ;
63a915d604SOswald Buddenhagen 	}
64a915d604SOswald Buddenhagen 	return -ENOMEM;  // -EBUSY would have been better
651da177e4SLinus Torvalds }
661da177e4SLinus Torvalds 
voice_free(struct snd_emu10k1 * emu,struct snd_emu10k1_voice * pvoice)673eb5b1d0SOswald Buddenhagen static void voice_free(struct snd_emu10k1 *emu,
683eb5b1d0SOswald Buddenhagen 		       struct snd_emu10k1_voice *pvoice)
693eb5b1d0SOswald Buddenhagen {
7082a9fa6eSOswald Buddenhagen 	if (pvoice->dirty)
713eb5b1d0SOswald Buddenhagen 		snd_emu10k1_voice_init(emu, pvoice->number);
723eb5b1d0SOswald Buddenhagen 	pvoice->interrupt = NULL;
73a915d604SOswald Buddenhagen 	pvoice->use = pvoice->dirty = pvoice->last = 0;
743eb5b1d0SOswald Buddenhagen 	pvoice->epcm = NULL;
753eb5b1d0SOswald Buddenhagen }
763eb5b1d0SOswald Buddenhagen 
snd_emu10k1_voice_alloc(struct snd_emu10k1 * emu,int type,int count,int channels,struct snd_emu10k1_pcm * epcm,struct snd_emu10k1_voice ** rvoice)77a915d604SOswald Buddenhagen int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int count, int channels,
78b4fea2d3SOswald Buddenhagen 			    struct snd_emu10k1_pcm *epcm, struct snd_emu10k1_voice **rvoice)
791da177e4SLinus Torvalds {
801da177e4SLinus Torvalds 	unsigned long flags;
811da177e4SLinus Torvalds 	int result;
821da177e4SLinus Torvalds 
83da3cec35STakashi Iwai 	if (snd_BUG_ON(!rvoice))
84da3cec35STakashi Iwai 		return -EINVAL;
85a915d604SOswald Buddenhagen 	if (snd_BUG_ON(!count))
86a915d604SOswald Buddenhagen 		return -EINVAL;
87a915d604SOswald Buddenhagen 	if (snd_BUG_ON(!channels))
88da3cec35STakashi Iwai 		return -EINVAL;
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
91a915d604SOswald Buddenhagen 	for (int got = 0; got < channels; ) {
92a915d604SOswald Buddenhagen 		result = voice_alloc(emu, type, count, epcm, &rvoice[got]);
93a915d604SOswald Buddenhagen 		if (result == 0) {
94a915d604SOswald Buddenhagen 			got++;
95a915d604SOswald Buddenhagen 			/*
96a915d604SOswald Buddenhagen 			dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
97a915d604SOswald Buddenhagen 			        rvoice[got - 1]->number, got, want);
98a915d604SOswald Buddenhagen 			*/
99a915d604SOswald Buddenhagen 			continue;
1001da177e4SLinus Torvalds 		}
101a915d604SOswald Buddenhagen 		if (type != EMU10K1_SYNTH && emu->get_synth_voice) {
102a915d604SOswald Buddenhagen 			/* free a voice from synth */
103a915d604SOswald Buddenhagen 			result = emu->get_synth_voice(emu);
104a915d604SOswald Buddenhagen 			if (result >= 0) {
105a915d604SOswald Buddenhagen 				voice_free(emu, &emu->voices[result]);
106a915d604SOswald Buddenhagen 				continue;
107a915d604SOswald Buddenhagen 			}
108a915d604SOswald Buddenhagen 		}
109a915d604SOswald Buddenhagen 		for (int i = 0; i < got; i++) {
110a915d604SOswald Buddenhagen 			for (int j = 0; j < count; j++)
111a915d604SOswald Buddenhagen 				voice_free(emu, rvoice[i] + j);
112a915d604SOswald Buddenhagen 			rvoice[i] = NULL;
113a915d604SOswald Buddenhagen 		}
1141da177e4SLinus Torvalds 		break;
1151da177e4SLinus Torvalds 	}
1161da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	return result;
1191da177e4SLinus Torvalds }
1201da177e4SLinus Torvalds 
1212dd31deeSTakashi Iwai EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
1222dd31deeSTakashi Iwai 
snd_emu10k1_voice_free(struct snd_emu10k1 * emu,struct snd_emu10k1_voice * pvoice)123eb4698f3STakashi Iwai int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
124eb4698f3STakashi Iwai 			   struct snd_emu10k1_voice *pvoice)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	unsigned long flags;
127a915d604SOswald Buddenhagen 	int last;
1281da177e4SLinus Torvalds 
129da3cec35STakashi Iwai 	if (snd_BUG_ON(!pvoice))
130da3cec35STakashi Iwai 		return -EINVAL;
1311da177e4SLinus Torvalds 	spin_lock_irqsave(&emu->voice_lock, flags);
132a915d604SOswald Buddenhagen 	do {
133a915d604SOswald Buddenhagen 		last = pvoice->last;
134a915d604SOswald Buddenhagen 		voice_free(emu, pvoice++);
135a915d604SOswald Buddenhagen 	} while (!last);
1361da177e4SLinus Torvalds 	spin_unlock_irqrestore(&emu->voice_lock, flags);
1371da177e4SLinus Torvalds 	return 0;
1381da177e4SLinus Torvalds }
1392dd31deeSTakashi Iwai 
1402dd31deeSTakashi Iwai EXPORT_SYMBOL(snd_emu10k1_voice_free);
141