2019-05-27 09:55:05 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 02:20:36 +04:00
|
|
|
/*
|
|
|
|
* Patch transfer callback for Emu10k1
|
|
|
|
*
|
|
|
|
* Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* 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
|
2005-11-17 16:50:13 +03:00
|
|
|
snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
|
|
|
|
struct snd_util_memhdr *hdr,
|
|
|
|
const void __user *data, long count)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int offset;
|
2020-07-02 22:35:54 +03:00
|
|
|
int truesize, size, blocksize;
|
|
|
|
__maybe_unused int loopsize;
|
2005-04-17 02:20:36 +04:00
|
|
|
int loopend, sampleend;
|
|
|
|
unsigned int start_addr;
|
2005-11-17 16:50:13 +03:00
|
|
|
struct snd_emu10k1 *emu;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
emu = rec->hw;
|
2008-08-08 19:12:14 +04:00
|
|
|
if (snd_BUG_ON(!sp || !hdr))
|
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (sp->v.size == 0) {
|
2014-02-25 20:02:09 +04:00
|
|
|
dev_dbg(emu->card->dev,
|
|
|
|
"emu: rom font for sample %d\n", sp->v.sample);
|
2005-04-17 02:20:36 +04:00
|
|
|
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 */
|
2018-07-17 18:00:43 +03:00
|
|
|
if (sp->v.loopstart >= sp->v.loopend)
|
|
|
|
swap(sp->v.loopstart, sp->v.loopend);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* 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) {
|
2014-02-25 20:02:09 +04:00
|
|
|
dev_dbg(emu->card->dev,
|
|
|
|
"synth malloc failed (size=%d)\n", blocksize);
|
2005-04-17 02:20:36 +04:00
|
|
|
/* 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;
|
2008-08-08 19:12:14 +04:00
|
|
|
if (offset + size > blocksize)
|
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
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;
|
2008-08-08 19:12:14 +04:00
|
|
|
if (offset + size > blocksize)
|
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
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;
|
|
|
|
|
2012-08-22 13:40:21 +04:00
|
|
|
#if 0 /* not supported yet */
|
2005-04-17 02:20:36 +04:00
|
|
|
/* 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;
|
2008-08-08 19:12:14 +04:00
|
|
|
if (offset + loopsize * 2 > blocksize)
|
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
for (i = 0; i < loopsize; i++)
|
|
|
|
wblock[woffset + i] = wblock[woffset - i -1];
|
|
|
|
offset += loopsize * 2;
|
|
|
|
} else {
|
2008-08-08 19:12:14 +04:00
|
|
|
if (offset + loopsize > blocksize)
|
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
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;
|
2008-08-08 19:12:14 +04:00
|
|
|
if (size < 0)
|
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
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
|
2005-11-17 16:50:13 +03:00
|
|
|
snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
|
|
|
|
struct snd_util_memhdr *hdr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2005-11-17 16:50:13 +03:00
|
|
|
struct snd_emu10k1 *emu;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
emu = rec->hw;
|
2008-08-08 19:12:14 +04:00
|
|
|
if (snd_BUG_ON(!sp || !hdr))
|
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (sp->block) {
|
|
|
|
snd_emu10k1_synth_free(emu, sp->block);
|
|
|
|
sp->block = NULL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|