Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / sound / pci / emu10k1 / emu10k1_patch.c
diff --git a/kernel/sound/pci/emu10k1/emu10k1_patch.c b/kernel/sound/pci/emu10k1/emu10k1_patch.c
new file mode 100644 (file)
index 0000000..0e069ae
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ *  Patch transfer callback for Emu10k1
+ *
+ *  Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+/*
+ * All the code for loading in a patch.  There is very little that is
+ * chip specific here.  Just the actual writing to the board.
+ */
+
+#include "emu10k1_synth_local.h"
+
+/*
+ */
+#define BLANK_LOOP_START       4
+#define BLANK_LOOP_END         8
+#define BLANK_LOOP_SIZE                12
+#define BLANK_HEAD_SIZE                32
+
+/*
+ * allocate a sample block and copy data from userspace
+ */
+int
+snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
+                      struct snd_util_memhdr *hdr,
+                      const void __user *data, long count)
+{
+       int offset;
+       int truesize, size, loopsize, blocksize;
+       int loopend, sampleend;
+       unsigned int start_addr;
+       struct snd_emu10k1 *emu;
+
+       emu = rec->hw;
+       if (snd_BUG_ON(!sp || !hdr))
+               return -EINVAL;
+
+       if (sp->v.size == 0) {
+               dev_dbg(emu->card->dev,
+                       "emu: rom font for sample %d\n", sp->v.sample);
+               return 0;
+       }
+
+       /* recalculate address offset */
+       sp->v.end -= sp->v.start;
+       sp->v.loopstart -= sp->v.start;
+       sp->v.loopend -= sp->v.start;
+       sp->v.start = 0;
+
+       /* some samples have invalid data.  the addresses are corrected in voice info */
+       sampleend = sp->v.end;
+       if (sampleend > sp->v.size)
+               sampleend = sp->v.size;
+       loopend = sp->v.loopend;
+       if (loopend > sampleend)
+               loopend = sampleend;
+
+       /* be sure loop points start < end */
+       if (sp->v.loopstart >= sp->v.loopend) {
+               int tmp = sp->v.loopstart;
+               sp->v.loopstart = sp->v.loopend;
+               sp->v.loopend = tmp;
+       }
+
+       /* compute true data size to be loaded */
+       truesize = sp->v.size + BLANK_HEAD_SIZE;
+       loopsize = 0;
+#if 0 /* not supported */
+       if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
+               loopsize = sp->v.loopend - sp->v.loopstart;
+       truesize += loopsize;
+#endif
+       if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+               truesize += BLANK_LOOP_SIZE;
+
+       /* try to allocate a memory block */
+       blocksize = truesize;
+       if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+               blocksize *= 2;
+       sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
+       if (sp->block == NULL) {
+               dev_dbg(emu->card->dev,
+                       "synth malloc failed (size=%d)\n", blocksize);
+               /* not ENOMEM (for compatibility with OSS) */
+               return -ENOSPC;
+       }
+       /* set the total size */
+       sp->v.truesize = blocksize;
+
+       /* write blank samples at head */
+       offset = 0;
+       size = BLANK_HEAD_SIZE;
+       if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+               size *= 2;
+       if (offset + size > blocksize)
+               return -EINVAL;
+       snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
+       offset += size;
+
+       /* copy start->loopend */
+       size = loopend;
+       if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+               size *= 2;
+       if (offset + size > blocksize)
+               return -EINVAL;
+       if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+               snd_emu10k1_synth_free(emu, sp->block);
+               sp->block = NULL;
+               return -EFAULT;
+       }
+       offset += size;
+       data += size;
+
+#if 0 /* not supported yet */
+       /* handle reverse (or bidirectional) loop */
+       if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
+               /* copy loop in reverse */
+               if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+                       int woffset;
+                       unsigned short *wblock = (unsigned short*)block;
+                       woffset = offset / 2;
+                       if (offset + loopsize * 2 > blocksize)
+                               return -EINVAL;
+                       for (i = 0; i < loopsize; i++)
+                               wblock[woffset + i] = wblock[woffset - i -1];
+                       offset += loopsize * 2;
+               } else {
+                       if (offset + loopsize > blocksize)
+                               return -EINVAL;
+                       for (i = 0; i < loopsize; i++)
+                               block[offset + i] = block[offset - i -1];
+                       offset += loopsize;
+               }
+
+               /* modify loop pointers */
+               if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
+                       sp->v.loopend += loopsize;
+               } else {
+                       sp->v.loopstart += loopsize;
+                       sp->v.loopend += loopsize;
+               }
+               /* add sample pointer */
+               sp->v.end += loopsize;
+       }
+#endif
+
+       /* loopend -> sample end */
+       size = sp->v.size - loopend;
+       if (size < 0)
+               return -EINVAL;
+       if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+               size *= 2;
+       if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+               snd_emu10k1_synth_free(emu, sp->block);
+               sp->block = NULL;
+               return -EFAULT;
+       }
+       offset += size;
+
+       /* clear rest of samples (if any) */
+       if (offset < blocksize)
+               snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
+
+       if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
+               /* if no blank loop is attached in the sample, add it */
+               if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+                       sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+                       sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+               }
+       }
+
+#if 0 /* not supported yet */
+       if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
+               /* unsigned -> signed */
+               if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+                       unsigned short *wblock = (unsigned short*)block;
+                       for (i = 0; i < truesize; i++)
+                               wblock[i] ^= 0x8000;
+               } else {
+                       for (i = 0; i < truesize; i++)
+                               block[i] ^= 0x80;
+               }
+       }
+#endif
+
+       /* recalculate offset */
+       start_addr = BLANK_HEAD_SIZE * 2;
+       if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+               start_addr >>= 1;
+       sp->v.start += start_addr;
+       sp->v.end += start_addr;
+       sp->v.loopstart += start_addr;
+       sp->v.loopend += start_addr;
+
+       return 0;
+}
+
+/*
+ * free a sample block
+ */
+int
+snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
+                       struct snd_util_memhdr *hdr)
+{
+       struct snd_emu10k1 *emu;
+
+       emu = rec->hw;
+       if (snd_BUG_ON(!sp || !hdr))
+               return -EINVAL;
+
+       if (sp->block) {
+               snd_emu10k1_synth_free(emu, sp->block);
+               sp->block = NULL;
+       }
+       return 0;
+}
+