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