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 <sound/core.h>
  33#include <sound/emu10k1.h>
  34
  35/* Previously the voice allocator started at 0 every time.  The new voice 
  36 * allocator uses a round robin scheme.  The next free voice is tracked in 
  37 * the card record and each allocation begins where the last left off.  The 
  38 * hardware requires stereo interleaved voices be aligned to an even/odd 
  39 * boundary.  For multichannel voice allocation we ensure than the block of 
  40 * voices does not cross the 32 voice boundary.  This simplifies the 
  41 * multichannel support and ensures we can use a single write to the 
  42 * (set|clear)_loop_stop registers.  Otherwise (for example) the voices would 
  43 * get out of sync when pausing/resuming a stream.
  44 *                                                      --rlrevell
  45 */
  46
  47static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
  48                       struct snd_emu10k1_voice **rvoice)
  49{
  50        struct snd_emu10k1_voice *voice;
  51        int i, j, k, first_voice, last_voice, skip;
  52
  53        *rvoice = NULL;
  54        first_voice = last_voice = 0;
  55        for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
  56                /*
  57                printk(KERN_DEBUG "i %d j %d next free %d!\n",
  58                       i, j, emu->next_free_voice);
  59                */
  60                i %= NUM_G;
  61
  62                /* stereo voices must be even/odd */
  63                if ((number == 2) && (i % 2)) {
  64                        i++;
  65                        continue;
  66                }
  67                        
  68                skip = 0;
  69                for (k = 0; k < number; k++) {
  70                        voice = &emu->voices[(i+k) % NUM_G];
  71                        if (voice->use) {
  72                                skip = 1;
  73                                break;
  74                        }
  75                }
  76                if (!skip) {
  77                        /* printk(KERN_DEBUG "allocated voice %d\n", i); */
  78                        first_voice = i;
  79                        last_voice = (i + number) % NUM_G;
  80                        emu->next_free_voice = last_voice;
  81                        break;
  82                }
  83        }
  84        
  85        if (first_voice == last_voice)
  86                return -ENOMEM;
  87        
  88        for (i = 0; i < number; i++) {
  89                voice = &emu->voices[(first_voice + i) % NUM_G];
  90                /*
  91                printk(kERN_DEBUG "voice alloc - %i, %i of %i\n",
  92                       voice->number, idx-first_voice+1, number);
  93                */
  94                voice->use = 1;
  95                switch (type) {
  96                case EMU10K1_PCM:
  97                        voice->pcm = 1;
  98                        break;
  99                case EMU10K1_SYNTH:
 100                        voice->synth = 1;
 101                        break;
 102                case EMU10K1_MIDI:
 103                        voice->midi = 1;
 104                        break;
 105                case EMU10K1_EFX:
 106                        voice->efx = 1;
 107                        break;
 108                }
 109        }
 110        *rvoice = &emu->voices[first_voice];
 111        return 0;
 112}
 113
 114int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
 115                            struct snd_emu10k1_voice **rvoice)
 116{
 117        unsigned long flags;
 118        int result;
 119
 120        if (snd_BUG_ON(!rvoice))
 121                return -EINVAL;
 122        if (snd_BUG_ON(!number))
 123                return -EINVAL;
 124
 125        spin_lock_irqsave(&emu->voice_lock, flags);
 126        for (;;) {
 127                result = voice_alloc(emu, type, number, rvoice);
 128                if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
 129                        break;
 130
 131                /* free a voice from synth */
 132                if (emu->get_synth_voice) {
 133                        result = emu->get_synth_voice(emu);
 134                        if (result >= 0) {
 135                                struct snd_emu10k1_voice *pvoice = &emu->voices[result];
 136                                pvoice->interrupt = NULL;
 137                                pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
 138                                pvoice->epcm = NULL;
 139                        }
 140                }
 141                if (result < 0)
 142                        break;
 143        }
 144        spin_unlock_irqrestore(&emu->voice_lock, flags);
 145
 146        return result;
 147}
 148
 149EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
 150
 151int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
 152                           struct snd_emu10k1_voice *pvoice)
 153{
 154        unsigned long flags;
 155
 156        if (snd_BUG_ON(!pvoice))
 157                return -EINVAL;
 158        spin_lock_irqsave(&emu->voice_lock, flags);
 159        pvoice->interrupt = NULL;
 160        pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
 161        pvoice->epcm = NULL;
 162        snd_emu10k1_voice_init(emu, pvoice->number);
 163        spin_unlock_irqrestore(&emu->voice_lock, flags);
 164        return 0;
 165}
 166
 167EXPORT_SYMBOL(snd_emu10k1_voice_free);
 168