brintos

brintos / linux-shallow public Read only

0
0
Text · 3.4 KiB · 77fb542 Raw
141 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 manager9 */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 *							--rlrevell22 */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 better65}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	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 121EXPORT_SYMBOL(snd_emu10k1_voice_alloc);122 123int 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 140EXPORT_SYMBOL(snd_emu10k1_voice_free);141