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