18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Patch transfer callback for Emu10k1
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci/*
88c2ecf20Sopenharmony_ci * All the code for loading in a patch.  There is very little that is
98c2ecf20Sopenharmony_ci * chip specific here.  Just the actual writing to the board.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "emu10k1_synth_local.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci#define BLANK_LOOP_START	4
178c2ecf20Sopenharmony_ci#define BLANK_LOOP_END		8
188c2ecf20Sopenharmony_ci#define BLANK_LOOP_SIZE		12
198c2ecf20Sopenharmony_ci#define BLANK_HEAD_SIZE		32
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * allocate a sample block and copy data from userspace
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ciint
258c2ecf20Sopenharmony_cisnd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
268c2ecf20Sopenharmony_ci		       struct snd_util_memhdr *hdr,
278c2ecf20Sopenharmony_ci		       const void __user *data, long count)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	int offset;
308c2ecf20Sopenharmony_ci	int truesize, size, blocksize;
318c2ecf20Sopenharmony_ci	__maybe_unused int loopsize;
328c2ecf20Sopenharmony_ci	int loopend, sampleend;
338c2ecf20Sopenharmony_ci	unsigned int start_addr;
348c2ecf20Sopenharmony_ci	struct snd_emu10k1 *emu;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	emu = rec->hw;
378c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!sp || !hdr))
388c2ecf20Sopenharmony_ci		return -EINVAL;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (sp->v.size == 0) {
418c2ecf20Sopenharmony_ci		dev_dbg(emu->card->dev,
428c2ecf20Sopenharmony_ci			"emu: rom font for sample %d\n", sp->v.sample);
438c2ecf20Sopenharmony_ci		return 0;
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* recalculate address offset */
478c2ecf20Sopenharmony_ci	sp->v.end -= sp->v.start;
488c2ecf20Sopenharmony_ci	sp->v.loopstart -= sp->v.start;
498c2ecf20Sopenharmony_ci	sp->v.loopend -= sp->v.start;
508c2ecf20Sopenharmony_ci	sp->v.start = 0;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* some samples have invalid data.  the addresses are corrected in voice info */
538c2ecf20Sopenharmony_ci	sampleend = sp->v.end;
548c2ecf20Sopenharmony_ci	if (sampleend > sp->v.size)
558c2ecf20Sopenharmony_ci		sampleend = sp->v.size;
568c2ecf20Sopenharmony_ci	loopend = sp->v.loopend;
578c2ecf20Sopenharmony_ci	if (loopend > sampleend)
588c2ecf20Sopenharmony_ci		loopend = sampleend;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/* be sure loop points start < end */
618c2ecf20Sopenharmony_ci	if (sp->v.loopstart >= sp->v.loopend)
628c2ecf20Sopenharmony_ci		swap(sp->v.loopstart, sp->v.loopend);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* compute true data size to be loaded */
658c2ecf20Sopenharmony_ci	truesize = sp->v.size + BLANK_HEAD_SIZE;
668c2ecf20Sopenharmony_ci	loopsize = 0;
678c2ecf20Sopenharmony_ci#if 0 /* not supported */
688c2ecf20Sopenharmony_ci	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
698c2ecf20Sopenharmony_ci		loopsize = sp->v.loopend - sp->v.loopstart;
708c2ecf20Sopenharmony_ci	truesize += loopsize;
718c2ecf20Sopenharmony_ci#endif
728c2ecf20Sopenharmony_ci	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
738c2ecf20Sopenharmony_ci		truesize += BLANK_LOOP_SIZE;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* try to allocate a memory block */
768c2ecf20Sopenharmony_ci	blocksize = truesize;
778c2ecf20Sopenharmony_ci	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
788c2ecf20Sopenharmony_ci		blocksize *= 2;
798c2ecf20Sopenharmony_ci	sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
808c2ecf20Sopenharmony_ci	if (sp->block == NULL) {
818c2ecf20Sopenharmony_ci		dev_dbg(emu->card->dev,
828c2ecf20Sopenharmony_ci			"synth malloc failed (size=%d)\n", blocksize);
838c2ecf20Sopenharmony_ci		/* not ENOMEM (for compatibility with OSS) */
848c2ecf20Sopenharmony_ci		return -ENOSPC;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci	/* set the total size */
878c2ecf20Sopenharmony_ci	sp->v.truesize = blocksize;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* write blank samples at head */
908c2ecf20Sopenharmony_ci	offset = 0;
918c2ecf20Sopenharmony_ci	size = BLANK_HEAD_SIZE;
928c2ecf20Sopenharmony_ci	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
938c2ecf20Sopenharmony_ci		size *= 2;
948c2ecf20Sopenharmony_ci	if (offset + size > blocksize)
958c2ecf20Sopenharmony_ci		return -EINVAL;
968c2ecf20Sopenharmony_ci	snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
978c2ecf20Sopenharmony_ci	offset += size;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* copy start->loopend */
1008c2ecf20Sopenharmony_ci	size = loopend;
1018c2ecf20Sopenharmony_ci	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
1028c2ecf20Sopenharmony_ci		size *= 2;
1038c2ecf20Sopenharmony_ci	if (offset + size > blocksize)
1048c2ecf20Sopenharmony_ci		return -EINVAL;
1058c2ecf20Sopenharmony_ci	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
1068c2ecf20Sopenharmony_ci		snd_emu10k1_synth_free(emu, sp->block);
1078c2ecf20Sopenharmony_ci		sp->block = NULL;
1088c2ecf20Sopenharmony_ci		return -EFAULT;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	offset += size;
1118c2ecf20Sopenharmony_ci	data += size;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#if 0 /* not supported yet */
1148c2ecf20Sopenharmony_ci	/* handle reverse (or bidirectional) loop */
1158c2ecf20Sopenharmony_ci	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
1168c2ecf20Sopenharmony_ci		/* copy loop in reverse */
1178c2ecf20Sopenharmony_ci		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
1188c2ecf20Sopenharmony_ci			int woffset;
1198c2ecf20Sopenharmony_ci			unsigned short *wblock = (unsigned short*)block;
1208c2ecf20Sopenharmony_ci			woffset = offset / 2;
1218c2ecf20Sopenharmony_ci			if (offset + loopsize * 2 > blocksize)
1228c2ecf20Sopenharmony_ci				return -EINVAL;
1238c2ecf20Sopenharmony_ci			for (i = 0; i < loopsize; i++)
1248c2ecf20Sopenharmony_ci				wblock[woffset + i] = wblock[woffset - i -1];
1258c2ecf20Sopenharmony_ci			offset += loopsize * 2;
1268c2ecf20Sopenharmony_ci		} else {
1278c2ecf20Sopenharmony_ci			if (offset + loopsize > blocksize)
1288c2ecf20Sopenharmony_ci				return -EINVAL;
1298c2ecf20Sopenharmony_ci			for (i = 0; i < loopsize; i++)
1308c2ecf20Sopenharmony_ci				block[offset + i] = block[offset - i -1];
1318c2ecf20Sopenharmony_ci			offset += loopsize;
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		/* modify loop pointers */
1358c2ecf20Sopenharmony_ci		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
1368c2ecf20Sopenharmony_ci			sp->v.loopend += loopsize;
1378c2ecf20Sopenharmony_ci		} else {
1388c2ecf20Sopenharmony_ci			sp->v.loopstart += loopsize;
1398c2ecf20Sopenharmony_ci			sp->v.loopend += loopsize;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci		/* add sample pointer */
1428c2ecf20Sopenharmony_ci		sp->v.end += loopsize;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci#endif
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* loopend -> sample end */
1478c2ecf20Sopenharmony_ci	size = sp->v.size - loopend;
1488c2ecf20Sopenharmony_ci	if (size < 0)
1498c2ecf20Sopenharmony_ci		return -EINVAL;
1508c2ecf20Sopenharmony_ci	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
1518c2ecf20Sopenharmony_ci		size *= 2;
1528c2ecf20Sopenharmony_ci	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
1538c2ecf20Sopenharmony_ci		snd_emu10k1_synth_free(emu, sp->block);
1548c2ecf20Sopenharmony_ci		sp->block = NULL;
1558c2ecf20Sopenharmony_ci		return -EFAULT;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci	offset += size;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* clear rest of samples (if any) */
1608c2ecf20Sopenharmony_ci	if (offset < blocksize)
1618c2ecf20Sopenharmony_ci		snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
1648c2ecf20Sopenharmony_ci		/* if no blank loop is attached in the sample, add it */
1658c2ecf20Sopenharmony_ci		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
1668c2ecf20Sopenharmony_ci			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
1678c2ecf20Sopenharmony_ci			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci#if 0 /* not supported yet */
1728c2ecf20Sopenharmony_ci	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
1738c2ecf20Sopenharmony_ci		/* unsigned -> signed */
1748c2ecf20Sopenharmony_ci		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
1758c2ecf20Sopenharmony_ci			unsigned short *wblock = (unsigned short*)block;
1768c2ecf20Sopenharmony_ci			for (i = 0; i < truesize; i++)
1778c2ecf20Sopenharmony_ci				wblock[i] ^= 0x8000;
1788c2ecf20Sopenharmony_ci		} else {
1798c2ecf20Sopenharmony_ci			for (i = 0; i < truesize; i++)
1808c2ecf20Sopenharmony_ci				block[i] ^= 0x80;
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci#endif
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* recalculate offset */
1868c2ecf20Sopenharmony_ci	start_addr = BLANK_HEAD_SIZE * 2;
1878c2ecf20Sopenharmony_ci	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
1888c2ecf20Sopenharmony_ci		start_addr >>= 1;
1898c2ecf20Sopenharmony_ci	sp->v.start += start_addr;
1908c2ecf20Sopenharmony_ci	sp->v.end += start_addr;
1918c2ecf20Sopenharmony_ci	sp->v.loopstart += start_addr;
1928c2ecf20Sopenharmony_ci	sp->v.loopend += start_addr;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/*
1988c2ecf20Sopenharmony_ci * free a sample block
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_ciint
2018c2ecf20Sopenharmony_cisnd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
2028c2ecf20Sopenharmony_ci			struct snd_util_memhdr *hdr)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct snd_emu10k1 *emu;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	emu = rec->hw;
2078c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!sp || !hdr))
2088c2ecf20Sopenharmony_ci		return -EINVAL;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (sp->block) {
2118c2ecf20Sopenharmony_ci		snd_emu10k1_synth_free(emu, sp->block);
2128c2ecf20Sopenharmony_ci		sp->block = NULL;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
217