Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
0004  *                   Creative Labs, Inc.
0005  *                   Lee Revell <rlrevell@joe-job.com>
0006  *  Routines for control of EMU10K1 chips - voice manager
0007  *
0008  *  Rewrote voice allocator for multichannel support - rlrevell 12/2004
0009  * 
0010  *  BUGS:
0011  *    --
0012  *
0013  *  TODO:
0014  *    --
0015  */
0016 
0017 #include <linux/time.h>
0018 #include <linux/export.h>
0019 #include <sound/core.h>
0020 #include <sound/emu10k1.h>
0021 
0022 /* Previously the voice allocator started at 0 every time.  The new voice 
0023  * allocator uses a round robin scheme.  The next free voice is tracked in 
0024  * the card record and each allocation begins where the last left off.  The 
0025  * hardware requires stereo interleaved voices be aligned to an even/odd 
0026  * boundary.  For multichannel voice allocation we ensure than the block of 
0027  * voices does not cross the 32 voice boundary.  This simplifies the 
0028  * multichannel support and ensures we can use a single write to the 
0029  * (set|clear)_loop_stop registers.  Otherwise (for example) the voices would 
0030  * get out of sync when pausing/resuming a stream.
0031  *                          --rlrevell
0032  */
0033 
0034 static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
0035                struct snd_emu10k1_voice **rvoice)
0036 {
0037     struct snd_emu10k1_voice *voice;
0038     int i, j, k, first_voice, last_voice, skip;
0039 
0040     *rvoice = NULL;
0041     first_voice = last_voice = 0;
0042     for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
0043         /*
0044         dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",
0045                i, j, emu->next_free_voice);
0046         */
0047         i %= NUM_G;
0048 
0049         /* stereo voices must be even/odd */
0050         if ((number == 2) && (i % 2)) {
0051             i++;
0052             continue;
0053         }
0054             
0055         skip = 0;
0056         for (k = 0; k < number; k++) {
0057             voice = &emu->voices[(i+k) % NUM_G];
0058             if (voice->use) {
0059                 skip = 1;
0060                 break;
0061             }
0062         }
0063         if (!skip) {
0064             /* dev_dbg(emu->card->dev, "allocated voice %d\n", i); */
0065             first_voice = i;
0066             last_voice = (i + number) % NUM_G;
0067             emu->next_free_voice = last_voice;
0068             break;
0069         }
0070     }
0071     
0072     if (first_voice == last_voice)
0073         return -ENOMEM;
0074     
0075     for (i = 0; i < number; i++) {
0076         voice = &emu->voices[(first_voice + i) % NUM_G];
0077         /*
0078         dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
0079                voice->number, idx-first_voice+1, number);
0080         */
0081         voice->use = 1;
0082         switch (type) {
0083         case EMU10K1_PCM:
0084             voice->pcm = 1;
0085             break;
0086         case EMU10K1_SYNTH:
0087             voice->synth = 1;
0088             break;
0089         case EMU10K1_MIDI:
0090             voice->midi = 1;
0091             break;
0092         case EMU10K1_EFX:
0093             voice->efx = 1;
0094             break;
0095         }
0096     }
0097     *rvoice = &emu->voices[first_voice];
0098     return 0;
0099 }
0100 
0101 int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
0102                 struct snd_emu10k1_voice **rvoice)
0103 {
0104     unsigned long flags;
0105     int result;
0106 
0107     if (snd_BUG_ON(!rvoice))
0108         return -EINVAL;
0109     if (snd_BUG_ON(!number))
0110         return -EINVAL;
0111 
0112     spin_lock_irqsave(&emu->voice_lock, flags);
0113     for (;;) {
0114         result = voice_alloc(emu, type, number, rvoice);
0115         if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
0116             break;
0117 
0118         /* free a voice from synth */
0119         if (emu->get_synth_voice) {
0120             result = emu->get_synth_voice(emu);
0121             if (result >= 0) {
0122                 struct snd_emu10k1_voice *pvoice = &emu->voices[result];
0123                 pvoice->interrupt = NULL;
0124                 pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
0125                 pvoice->epcm = NULL;
0126             }
0127         }
0128         if (result < 0)
0129             break;
0130     }
0131     spin_unlock_irqrestore(&emu->voice_lock, flags);
0132 
0133     return result;
0134 }
0135 
0136 EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
0137 
0138 int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
0139                struct snd_emu10k1_voice *pvoice)
0140 {
0141     unsigned long flags;
0142 
0143     if (snd_BUG_ON(!pvoice))
0144         return -EINVAL;
0145     spin_lock_irqsave(&emu->voice_lock, flags);
0146     pvoice->interrupt = NULL;
0147     pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
0148     pvoice->epcm = NULL;
0149     snd_emu10k1_voice_init(emu, pvoice->number);
0150     spin_unlock_irqrestore(&emu->voice_lock, flags);
0151     return 0;
0152 }
0153 
0154 EXPORT_SYMBOL(snd_emu10k1_voice_free);