linux/sound/pci/emu10k1/voice.c
<<
>>
Prefs
   1/*
   2 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
   3 *                   Creative Labs, Inc.
   4 *                   Lee Revell <rlrevell@joe-job.com>
   5 *  Routines for control of EMU10K1 chips - voice manager
   6 *
   7 *  Rewrote voice allocator for multichannel support - rlrevell 12/2004
   8 * 
   9 *  BUGS:
  10 *    --
  11 *
  12 *  TODO:
  13 *    --
  14 *
  15 *   This program is free software; you can redistribute it and/or modify
  16 *   it under the terms of the GNU General Public License as published by
  17 *   the Free Software Foundation; either version 2 of the License, or
  18 *   (at your option) any later version.
  19 *
  20 *   This program is distributed in the hope that it will be useful,
  21 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  22 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23 *   GNU General Public License for more details.
  24 *
  25 *   You should have received a copy of the GNU General Public License
  26 *   along with this program; if not, write to the Free Software
  27 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  28 *
  29 */
  30
  31#include <linux/time.h>
  32#include <linux/export.h>
  33#include <sound/core.h>
  34#include <sound/emu10k1.h>
  35
  36/* Previously the voice allocator started at 0 every time.  The new voice 
  37 * allocator uses a round robin scheme.  The next free voice is tracked in 
  38 * the card record and each allocation begins where the last left off.  The 
  39 * hardware requires stereo interleaved voices be aligned to an even/odd 
  40 * boundary.  For multichannel voice allocation we ensure than the block of 
  41 * voices does not cross the 32 voice boundary.  This simplifies the 
  42 * multichannel support and ensures we can use a single write to the 
  43 * (set|clear)_loop_stop registers.  Otherwise (for example) the voices would 
  44 * get out of sync when pausing/resuming a stream.
  45 *                                                      --rlrevell
  46 */
  47
  48static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
  49                       struct snd_emu10k1_voice **rvoice)
  50{
  51        struct snd_emu10k1_voice *voice;
  52        int i, j, k, first_voice, last_voice, skip;
  53
  54        *rvoice = NULL;
  55        first_voice = last_voice = 0;
  56        for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
  57                /*
  58                printk(KERN_DEBUG "i %d j %d next free %d!\n",
  59                       i, j, emu->next_free_voice);
  60                */
  61                i %= NUM_G;
  62
  63                /* stereo voices must be even/odd */
  64                if ((number == 2) && (i % 2)) {
  65                        i++;
  66                        continue;
  67                }
  68                        
  69                skip = 0;
  70                for (k = 0; k < number; k++) {
  71                        voice = &emu->voices[(i+k) % NUM_G];
  72                        if (voice->use) {
  73                                skip = 1;
  74                                break;
  75                        }
  76                }
  77                if (!skip) {
  78                        /* printk(KERN_DEBUG "allocated voice %d\n", i); */
  79                        first_voice = i;
  80                        last_voice = (i + number) % NUM_G;
  81                        emu->next_free_voice = last_voice;
  82                        break;
  83                }
  84        }
  85        
  86        if (first_voice == last_voice)
  87                return -ENOMEM;
  88        
  89        for (i = 0; i < number; i++) {
  90                voice = &emu->voices[(first_voice + i) % NUM_G];
  91                /*
  92                printk(kERN_DEBUG "voice alloc - %i, %i of %i\n",
  93                       voice->number, idx-first_voice+1, number);
  94                */
  95                voice->use = 1;
  96                switch (type) {
  97                case EMU10K1_PCM:
  98                        voice->pcm = 1;
  99                        break;
 100                case EMU10K1_SYNTH:
 101                        voice->synth = 1;
 102                        break;
 103                case EMU10K1_MIDI:
 104                        voice->midi = 1;
 105                        break;
 106                case EMU10K1_EFX:
 107                        voice->efx = 1;
 108                        break;
 109                }
 110        }
 111        *rvoice = &emu->voices[first_voice];
 112        return 0;
 113}
 114
 115int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
 116                            struct snd_emu10k1_voice **rvoice)
 117{
 118        unsigned long flags;
 119        int result;
 120
 121        if (snd_BUG_ON(!rvoice))
 122                return -EINVAL;
 123        if (snd_BUG_ON(!number))
 124                return -EINVAL;
 125
 126        spin_lock_irqsave(&emu->voice_lock, flags);
 127        for (;;) {
 128                result = voice_alloc(emu, type, number, rvoice);
 129                if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
 130                        break;
 131
 132                /* free a voice from synth */
 133                if (emu->get_synth_voice) {
 134                        result = emu->get_synth_voice(emu);
 135                        if (result >= 0) {
 136                                struct snd_emu10k1_voice *pvoice = &emu->voices[result];
 137                                pvoice->interrupt = NULL;
 138                                pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
 139                                pvoice->epcm = NULL;
 140                        }
 141                }
 142                if (result < 0)
 143                        break;
 144        }
 145        spin_unlock_irqrestore(&emu->voice_lock, flags);
 146
 147        return result;
 148}
 149
 150EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
 151
 152int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
 153                           struct snd_emu10k1_voice *pvoice)
 154{
 155        unsigned long flags;
 156
 157        if (snd_BUG_ON(!pvoice))
 158                return -EINVAL;
 159        spin_lock_irqsave(&emu->voice_lock, flags);
 160        pvoice->interrupt = NULL;
 161        pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
 162        pvoice->epcm = NULL;
 163        snd_emu10k1_voice_init(emu, pvoice->number);
 164        spin_unlock_irqrestore(&emu->voice_lock, flags);
 165        return 0;
 166}
 167
 168EXPORT_SYMBOL(snd_emu10k1_voice_free);
 169