File Index
Files and directories
Sign in
FILE

voice.c

File preview with syntax highlighting.

linux-6.19.14/sound/pci/emu10k1/voice.c 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
 24static 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
 67static 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
 77int 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	int result;
 81
 82	if (snd_BUG_ON(!rvoice))
 83		return -EINVAL;
 84	if (snd_BUG_ON(!count))
 85		return -EINVAL;
 86	if (snd_BUG_ON(!channels))
 87		return -EINVAL;
 88
 89	guard(spinlock_irqsave)(&emu->voice_lock);
 90	for (int got = 0; got < channels; ) {
 91		result = voice_alloc(emu, type, count, epcm, &rvoice[got]);
 92		if (result == 0) {
 93			got++;
 94			/*
 95			dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
 96			        rvoice[got - 1]->number, got, want);
 97			*/
 98			continue;
 99		}
100		if (type != EMU10K1_SYNTH && emu->get_synth_voice) {
101			/* free a voice from synth */
102			result = emu->get_synth_voice(emu);
103			if (result >= 0) {
104				voice_free(emu, &emu->voices[result]);
105				continue;
106			}
107		}
108		for (int i = 0; i < got; i++) {
109			for (int j = 0; j < count; j++)
110				voice_free(emu, rvoice[i] + j);
111			rvoice[i] = NULL;
112		}
113		break;
114	}
115
116	return result;
117}
118
119EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
120
121int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
122			   struct snd_emu10k1_voice *pvoice)
123{
124	int last;
125
126	if (snd_BUG_ON(!pvoice))
127		return -EINVAL;
128	guard(spinlock_irqsave)(&emu->voice_lock);
129	do {
130		last = pvoice->last;
131		voice_free(emu, pvoice++);
132	} while (!last);
133	return 0;
134}
135
136EXPORT_SYMBOL(snd_emu10k1_voice_free);
~/ Read-only file preview.