162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Patch transfer callback for Emu10k1 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2000 Takashi iwai <tiwai@suse.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci/* 862306a36Sopenharmony_ci * All the code for loading in a patch. There is very little that is 962306a36Sopenharmony_ci * chip specific here. Just the actual writing to the board. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "emu10k1_synth_local.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci#define BLANK_LOOP_START 4 1762306a36Sopenharmony_ci#define BLANK_LOOP_END 8 1862306a36Sopenharmony_ci#define BLANK_LOOP_SIZE 12 1962306a36Sopenharmony_ci#define BLANK_HEAD_SIZE 32 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * allocate a sample block and copy data from userspace 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ciint 2562306a36Sopenharmony_cisnd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, 2662306a36Sopenharmony_ci struct snd_util_memhdr *hdr, 2762306a36Sopenharmony_ci const void __user *data, long count) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci int offset; 3062306a36Sopenharmony_ci int truesize, size, blocksize; 3162306a36Sopenharmony_ci __maybe_unused int loopsize; 3262306a36Sopenharmony_ci int loopend, sampleend; 3362306a36Sopenharmony_ci unsigned int start_addr; 3462306a36Sopenharmony_ci struct snd_emu10k1 *emu; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci emu = rec->hw; 3762306a36Sopenharmony_ci if (snd_BUG_ON(!sp || !hdr)) 3862306a36Sopenharmony_ci return -EINVAL; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (sp->v.size == 0) { 4162306a36Sopenharmony_ci dev_dbg(emu->card->dev, 4262306a36Sopenharmony_ci "emu: rom font for sample %d\n", sp->v.sample); 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* recalculate address offset */ 4762306a36Sopenharmony_ci sp->v.end -= sp->v.start; 4862306a36Sopenharmony_ci sp->v.loopstart -= sp->v.start; 4962306a36Sopenharmony_ci sp->v.loopend -= sp->v.start; 5062306a36Sopenharmony_ci sp->v.start = 0; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci /* some samples have invalid data. the addresses are corrected in voice info */ 5362306a36Sopenharmony_ci sampleend = sp->v.end; 5462306a36Sopenharmony_ci if (sampleend > sp->v.size) 5562306a36Sopenharmony_ci sampleend = sp->v.size; 5662306a36Sopenharmony_ci loopend = sp->v.loopend; 5762306a36Sopenharmony_ci if (loopend > sampleend) 5862306a36Sopenharmony_ci loopend = sampleend; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* be sure loop points start < end */ 6162306a36Sopenharmony_ci if (sp->v.loopstart >= sp->v.loopend) 6262306a36Sopenharmony_ci swap(sp->v.loopstart, sp->v.loopend); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* compute true data size to be loaded */ 6562306a36Sopenharmony_ci truesize = sp->v.size + BLANK_HEAD_SIZE; 6662306a36Sopenharmony_ci loopsize = 0; 6762306a36Sopenharmony_ci#if 0 /* not supported */ 6862306a36Sopenharmony_ci if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) 6962306a36Sopenharmony_ci loopsize = sp->v.loopend - sp->v.loopstart; 7062306a36Sopenharmony_ci truesize += loopsize; 7162306a36Sopenharmony_ci#endif 7262306a36Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) 7362306a36Sopenharmony_ci truesize += BLANK_LOOP_SIZE; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* try to allocate a memory block */ 7662306a36Sopenharmony_ci blocksize = truesize; 7762306a36Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 7862306a36Sopenharmony_ci blocksize *= 2; 7962306a36Sopenharmony_ci sp->block = snd_emu10k1_synth_alloc(emu, blocksize); 8062306a36Sopenharmony_ci if (sp->block == NULL) { 8162306a36Sopenharmony_ci dev_dbg(emu->card->dev, 8262306a36Sopenharmony_ci "synth malloc failed (size=%d)\n", blocksize); 8362306a36Sopenharmony_ci /* not ENOMEM (for compatibility with OSS) */ 8462306a36Sopenharmony_ci return -ENOSPC; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci /* set the total size */ 8762306a36Sopenharmony_ci sp->v.truesize = blocksize; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* write blank samples at head */ 9062306a36Sopenharmony_ci offset = 0; 9162306a36Sopenharmony_ci size = BLANK_HEAD_SIZE; 9262306a36Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 9362306a36Sopenharmony_ci size *= 2; 9462306a36Sopenharmony_ci if (offset + size > blocksize) 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci snd_emu10k1_synth_bzero(emu, sp->block, offset, size); 9762306a36Sopenharmony_ci offset += size; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* copy start->loopend */ 10062306a36Sopenharmony_ci size = loopend; 10162306a36Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 10262306a36Sopenharmony_ci size *= 2; 10362306a36Sopenharmony_ci if (offset + size > blocksize) 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { 10662306a36Sopenharmony_ci snd_emu10k1_synth_free(emu, sp->block); 10762306a36Sopenharmony_ci sp->block = NULL; 10862306a36Sopenharmony_ci return -EFAULT; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci offset += size; 11162306a36Sopenharmony_ci data += size; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#if 0 /* not supported yet */ 11462306a36Sopenharmony_ci /* handle reverse (or bidirectional) loop */ 11562306a36Sopenharmony_ci if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { 11662306a36Sopenharmony_ci /* copy loop in reverse */ 11762306a36Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { 11862306a36Sopenharmony_ci int woffset; 11962306a36Sopenharmony_ci unsigned short *wblock = (unsigned short*)block; 12062306a36Sopenharmony_ci woffset = offset / 2; 12162306a36Sopenharmony_ci if (offset + loopsize * 2 > blocksize) 12262306a36Sopenharmony_ci return -EINVAL; 12362306a36Sopenharmony_ci for (i = 0; i < loopsize; i++) 12462306a36Sopenharmony_ci wblock[woffset + i] = wblock[woffset - i -1]; 12562306a36Sopenharmony_ci offset += loopsize * 2; 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci if (offset + loopsize > blocksize) 12862306a36Sopenharmony_ci return -EINVAL; 12962306a36Sopenharmony_ci for (i = 0; i < loopsize; i++) 13062306a36Sopenharmony_ci block[offset + i] = block[offset - i -1]; 13162306a36Sopenharmony_ci offset += loopsize; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* modify loop pointers */ 13562306a36Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { 13662306a36Sopenharmony_ci sp->v.loopend += loopsize; 13762306a36Sopenharmony_ci } else { 13862306a36Sopenharmony_ci sp->v.loopstart += loopsize; 13962306a36Sopenharmony_ci sp->v.loopend += loopsize; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci /* add sample pointer */ 14262306a36Sopenharmony_ci sp->v.end += loopsize; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci#endif 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* loopend -> sample end */ 14762306a36Sopenharmony_ci size = sp->v.size - loopend; 14862306a36Sopenharmony_ci if (size < 0) 14962306a36Sopenharmony_ci return -EINVAL; 15062306a36Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 15162306a36Sopenharmony_ci size *= 2; 15262306a36Sopenharmony_ci if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { 15362306a36Sopenharmony_ci snd_emu10k1_synth_free(emu, sp->block); 15462306a36Sopenharmony_ci sp->block = NULL; 15562306a36Sopenharmony_ci return -EFAULT; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci offset += size; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* clear rest of samples (if any) */ 16062306a36Sopenharmony_ci if (offset < blocksize) 16162306a36Sopenharmony_ci snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { 16462306a36Sopenharmony_ci /* if no blank loop is attached in the sample, add it */ 16562306a36Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { 16662306a36Sopenharmony_ci sp->v.loopstart = sp->v.end + BLANK_LOOP_START; 16762306a36Sopenharmony_ci sp->v.loopend = sp->v.end + BLANK_LOOP_END; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci#if 0 /* not supported yet */ 17262306a36Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) { 17362306a36Sopenharmony_ci /* unsigned -> signed */ 17462306a36Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { 17562306a36Sopenharmony_ci unsigned short *wblock = (unsigned short*)block; 17662306a36Sopenharmony_ci for (i = 0; i < truesize; i++) 17762306a36Sopenharmony_ci wblock[i] ^= 0x8000; 17862306a36Sopenharmony_ci } else { 17962306a36Sopenharmony_ci for (i = 0; i < truesize; i++) 18062306a36Sopenharmony_ci block[i] ^= 0x80; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci#endif 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* recalculate offset */ 18662306a36Sopenharmony_ci start_addr = BLANK_HEAD_SIZE * 2; 18762306a36Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 18862306a36Sopenharmony_ci start_addr >>= 1; 18962306a36Sopenharmony_ci sp->v.start += start_addr; 19062306a36Sopenharmony_ci sp->v.end += start_addr; 19162306a36Sopenharmony_ci sp->v.loopstart += start_addr; 19262306a36Sopenharmony_ci sp->v.loopend += start_addr; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* 19862306a36Sopenharmony_ci * free a sample block 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ciint 20162306a36Sopenharmony_cisnd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp, 20262306a36Sopenharmony_ci struct snd_util_memhdr *hdr) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct snd_emu10k1 *emu; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci emu = rec->hw; 20762306a36Sopenharmony_ci if (snd_BUG_ON(!sp || !hdr)) 20862306a36Sopenharmony_ci return -EINVAL; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (sp->block) { 21162306a36Sopenharmony_ci snd_emu10k1_synth_free(emu, sp->block); 21262306a36Sopenharmony_ci sp->block = NULL; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 217