linux/sound/pci/emu10k1/emu10k1_patch.c
<<
>>
Prefs
   1/*
   2 *  Patch transfer callback for Emu10k1
   3 *
   4 *  Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 */
  20/*
  21 * All the code for loading in a patch.  There is very little that is
  22 * chip specific here.  Just the actual writing to the board.
  23 */
  24
  25#include "emu10k1_synth_local.h"
  26
  27/*
  28 */
  29#define BLANK_LOOP_START        4
  30#define BLANK_LOOP_END          8
  31#define BLANK_LOOP_SIZE         12
  32#define BLANK_HEAD_SIZE         32
  33
  34/*
  35 * allocate a sample block and copy data from userspace
  36 */
  37int
  38snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
  39                       struct snd_util_memhdr *hdr,
  40                       const void __user *data, long count)
  41{
  42        int offset;
  43        int truesize, size, loopsize, blocksize;
  44        int loopend, sampleend;
  45        unsigned int start_addr;
  46        struct snd_emu10k1 *emu;
  47
  48        emu = rec->hw;
  49        if (snd_BUG_ON(!sp || !hdr))
  50                return -EINVAL;
  51
  52        if (sp->v.size == 0) {
  53                dev_dbg(emu->card->dev,
  54                        "emu: rom font for sample %d\n", sp->v.sample);
  55                return 0;
  56        }
  57
  58        /* recalculate address offset */
  59        sp->v.end -= sp->v.start;
  60        sp->v.loopstart -= sp->v.start;
  61        sp->v.loopend -= sp->v.start;
  62        sp->v.start = 0;
  63
  64        /* some samples have invalid data.  the addresses are corrected in voice info */
  65        sampleend = sp->v.end;
  66        if (sampleend > sp->v.size)
  67                sampleend = sp->v.size;
  68        loopend = sp->v.loopend;
  69        if (loopend > sampleend)
  70                loopend = sampleend;
  71
  72        /* be sure loop points start < end */
  73        if (sp->v.loopstart >= sp->v.loopend) {
  74                int tmp = sp->v.loopstart;
  75                sp->v.loopstart = sp->v.loopend;
  76                sp->v.loopend = tmp;
  77        }
  78
  79        /* compute true data size to be loaded */
  80        truesize = sp->v.size + BLANK_HEAD_SIZE;
  81        loopsize = 0;
  82#if 0 /* not supported */
  83        if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
  84                loopsize = sp->v.loopend - sp->v.loopstart;
  85        truesize += loopsize;
  86#endif
  87        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
  88                truesize += BLANK_LOOP_SIZE;
  89
  90        /* try to allocate a memory block */
  91        blocksize = truesize;
  92        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
  93                blocksize *= 2;
  94        sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
  95        if (sp->block == NULL) {
  96                dev_dbg(emu->card->dev,
  97                        "synth malloc failed (size=%d)\n", blocksize);
  98                /* not ENOMEM (for compatibility with OSS) */
  99                return -ENOSPC;
 100        }
 101        /* set the total size */
 102        sp->v.truesize = blocksize;
 103
 104        /* write blank samples at head */
 105        offset = 0;
 106        size = BLANK_HEAD_SIZE;
 107        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 108                size *= 2;
 109        if (offset + size > blocksize)
 110                return -EINVAL;
 111        snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
 112        offset += size;
 113
 114        /* copy start->loopend */
 115        size = loopend;
 116        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 117                size *= 2;
 118        if (offset + size > blocksize)
 119                return -EINVAL;
 120        if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
 121                snd_emu10k1_synth_free(emu, sp->block);
 122                sp->block = NULL;
 123                return -EFAULT;
 124        }
 125        offset += size;
 126        data += size;
 127
 128#if 0 /* not supported yet */
 129        /* handle reverse (or bidirectional) loop */
 130        if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
 131                /* copy loop in reverse */
 132                if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
 133                        int woffset;
 134                        unsigned short *wblock = (unsigned short*)block;
 135                        woffset = offset / 2;
 136                        if (offset + loopsize * 2 > blocksize)
 137                                return -EINVAL;
 138                        for (i = 0; i < loopsize; i++)
 139                                wblock[woffset + i] = wblock[woffset - i -1];
 140                        offset += loopsize * 2;
 141                } else {
 142                        if (offset + loopsize > blocksize)
 143                                return -EINVAL;
 144                        for (i = 0; i < loopsize; i++)
 145                                block[offset + i] = block[offset - i -1];
 146                        offset += loopsize;
 147                }
 148
 149                /* modify loop pointers */
 150                if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
 151                        sp->v.loopend += loopsize;
 152                } else {
 153                        sp->v.loopstart += loopsize;
 154                        sp->v.loopend += loopsize;
 155                }
 156                /* add sample pointer */
 157                sp->v.end += loopsize;
 158        }
 159#endif
 160
 161        /* loopend -> sample end */
 162        size = sp->v.size - loopend;
 163        if (size < 0)
 164                return -EINVAL;
 165        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 166                size *= 2;
 167        if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
 168                snd_emu10k1_synth_free(emu, sp->block);
 169                sp->block = NULL;
 170                return -EFAULT;
 171        }
 172        offset += size;
 173
 174        /* clear rest of samples (if any) */
 175        if (offset < blocksize)
 176                snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
 177
 178        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
 179                /* if no blank loop is attached in the sample, add it */
 180                if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
 181                        sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
 182                        sp->v.loopend = sp->v.end + BLANK_LOOP_END;
 183                }
 184        }
 185
 186#if 0 /* not supported yet */
 187        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
 188                /* unsigned -> signed */
 189                if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
 190                        unsigned short *wblock = (unsigned short*)block;
 191                        for (i = 0; i < truesize; i++)
 192                                wblock[i] ^= 0x8000;
 193                } else {
 194                        for (i = 0; i < truesize; i++)
 195                                block[i] ^= 0x80;
 196                }
 197        }
 198#endif
 199
 200        /* recalculate offset */
 201        start_addr = BLANK_HEAD_SIZE * 2;
 202        if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
 203                start_addr >>= 1;
 204        sp->v.start += start_addr;
 205        sp->v.end += start_addr;
 206        sp->v.loopstart += start_addr;
 207        sp->v.loopend += start_addr;
 208
 209        return 0;
 210}
 211
 212/*
 213 * free a sample block
 214 */
 215int
 216snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
 217                        struct snd_util_memhdr *hdr)
 218{
 219        struct snd_emu10k1 *emu;
 220
 221        emu = rec->hw;
 222        if (snd_BUG_ON(!sp || !hdr))
 223                return -EINVAL;
 224
 225        if (sp->block) {
 226                snd_emu10k1_synth_free(emu, sp->block);
 227                sp->block = NULL;
 228        }
 229        return 0;
 230}
 231
 232