diff options
Diffstat (limited to 'sound/pci/emu10k1/emu10k1_patch.c')
| -rw-r--r-- | sound/pci/emu10k1/emu10k1_patch.c | 229 |
1 files changed, 85 insertions, 144 deletions
diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c index 662a45876a8b..806b4f95cad1 100644 --- a/sound/pci/emu10k1/emu10k1_patch.c +++ b/sound/pci/emu10k1/emu10k1_patch.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * 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 @@ -29,7 +16,7 @@ #define BLANK_LOOP_START 4 #define BLANK_LOOP_END 8 #define BLANK_LOOP_SIZE 12 -#define BLANK_HEAD_SIZE 32 +#define BLANK_HEAD_SIZE 3 /* * allocate a sample block and copy data from userspace @@ -39,60 +26,81 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, struct snd_util_memhdr *hdr, const void __user *data, long count) { + u8 fill; + u32 xor; + int shift; int offset; - int truesize, size, loopsize, blocksize; - int loopend, sampleend; - unsigned int start_addr; + int truesize, size, blocksize; + int loop_start, loop_end, loop_size, data_end, unroll; struct snd_emu10k1 *emu; emu = rec->hw; if (snd_BUG_ON(!sp || !hdr)) return -EINVAL; - if (sp->v.size == 0) { - snd_printd("emu: rom font for sample %d\n", sp->v.sample); - return 0; + if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP | SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { + /* should instead return -ENOTSUPP; but compatibility */ + dev_warn(emu->card->dev, + "Emu10k1 wavetable patch %d with unsupported loop feature\n", + sp->v.sample); } - /* 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; + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { + shift = 0; + fill = 0x80; + xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0 : 0x80808080; + } else { + shift = 1; + fill = 0; + xor = (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) ? 0x80008000 : 0; } /* 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) + if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { truesize += BLANK_LOOP_SIZE; + /* 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; + } + } + + loop_start = sp->v.loopstart; + loop_end = sp->v.loopend; + loop_size = loop_end - loop_start; + if (!loop_size) + return -EINVAL; + data_end = sp->v.end; + + /* recalculate offset */ + sp->v.start += BLANK_HEAD_SIZE; + sp->v.end += BLANK_HEAD_SIZE; + sp->v.loopstart += BLANK_HEAD_SIZE; + sp->v.loopend += BLANK_HEAD_SIZE; + + // Automatic pre-filling of the cache does not work in the presence + // of loops (*), and we don't want to fill it manually, as that is + // fiddly and slow. So we unroll the loop until the loop end is + // beyond the cache size. + // (*) Strictly speaking, a single iteration is supported (that's + // how it works when the playback engine runs), but handling this + // special case is not worth it. + unroll = 0; + while (sp->v.loopend < 64) { + truesize += loop_size; + sp->v.loopstart += loop_size; + sp->v.loopend += loop_size; + sp->v.end += loop_size; + unroll++; + } /* try to allocate a memory block */ - blocksize = truesize; - if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) - blocksize *= 2; + blocksize = truesize << shift; sp->block = snd_emu10k1_synth_alloc(emu, blocksize); if (sp->block == NULL) { - snd_printd("emu10k1: synth malloc failed (size=%d)\n", blocksize); + dev_dbg(emu->card->dev, + "synth malloc failed (size=%d)\n", blocksize); /* not ENOMEM (for compatibility with OSS) */ return -ENOSPC; } @@ -101,110 +109,43 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, /* 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); + size = BLANK_HEAD_SIZE << shift; + snd_emu10k1_synth_memset(emu, sp->block, offset, size, fill); 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; + /* copy provided samples */ + if (unroll && loop_end <= data_end) { + size = loop_end << shift; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; + offset += size; + + data += loop_start << shift; + while (--unroll > 0) { + size = loop_size << shift; + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; + offset += size; } - /* 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; + size = (data_end - loop_start) << shift; + } else { + size = data_end << shift; } + if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size, xor)) + goto faulty; 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; + snd_emu10k1_synth_memset(emu, sp->block, offset, blocksize - offset, fill); return 0; + +faulty: + snd_emu10k1_synth_free(emu, sp->block); + sp->block = NULL; + return -EFAULT; } /* |
