1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *  Patch transfer callback for Emu10k1
4 *
5 *  Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
6 */
7/*
8 * All the code for loading in a patch.  There is very little that is
9 * chip specific here.  Just the actual writing to the board.
10 */
11
12#include "emu10k1_synth_local.h"
13
14/*
15 */
16#define BLANK_LOOP_START	4
17#define BLANK_LOOP_END		8
18#define BLANK_LOOP_SIZE		12
19#define BLANK_HEAD_SIZE		32
20
21/*
22 * allocate a sample block and copy data from userspace
23 */
24int
25snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
26		       struct snd_util_memhdr *hdr,
27		       const void __user *data, long count)
28{
29	int offset;
30	int truesize, size, blocksize;
31	__maybe_unused int loopsize;
32	int loopend, sampleend;
33	unsigned int start_addr;
34	struct snd_emu10k1 *emu;
35
36	emu = rec->hw;
37	if (snd_BUG_ON(!sp || !hdr))
38		return -EINVAL;
39
40	if (sp->v.size == 0) {
41		dev_dbg(emu->card->dev,
42			"emu: rom font for sample %d\n", sp->v.sample);
43		return 0;
44	}
45
46	/* recalculate address offset */
47	sp->v.end -= sp->v.start;
48	sp->v.loopstart -= sp->v.start;
49	sp->v.loopend -= sp->v.start;
50	sp->v.start = 0;
51
52	/* some samples have invalid data.  the addresses are corrected in voice info */
53	sampleend = sp->v.end;
54	if (sampleend > sp->v.size)
55		sampleend = sp->v.size;
56	loopend = sp->v.loopend;
57	if (loopend > sampleend)
58		loopend = sampleend;
59
60	/* be sure loop points start < end */
61	if (sp->v.loopstart >= sp->v.loopend)
62		swap(sp->v.loopstart, sp->v.loopend);
63
64	/* compute true data size to be loaded */
65	truesize = sp->v.size + BLANK_HEAD_SIZE;
66	loopsize = 0;
67#if 0 /* not supported */
68	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
69		loopsize = sp->v.loopend - sp->v.loopstart;
70	truesize += loopsize;
71#endif
72	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
73		truesize += BLANK_LOOP_SIZE;
74
75	/* try to allocate a memory block */
76	blocksize = truesize;
77	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
78		blocksize *= 2;
79	sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
80	if (sp->block == NULL) {
81		dev_dbg(emu->card->dev,
82			"synth malloc failed (size=%d)\n", blocksize);
83		/* not ENOMEM (for compatibility with OSS) */
84		return -ENOSPC;
85	}
86	/* set the total size */
87	sp->v.truesize = blocksize;
88
89	/* write blank samples at head */
90	offset = 0;
91	size = BLANK_HEAD_SIZE;
92	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
93		size *= 2;
94	if (offset + size > blocksize)
95		return -EINVAL;
96	snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
97	offset += size;
98
99	/* copy start->loopend */
100	size = loopend;
101	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
102		size *= 2;
103	if (offset + size > blocksize)
104		return -EINVAL;
105	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
106		snd_emu10k1_synth_free(emu, sp->block);
107		sp->block = NULL;
108		return -EFAULT;
109	}
110	offset += size;
111	data += size;
112
113#if 0 /* not supported yet */
114	/* handle reverse (or bidirectional) loop */
115	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
116		/* copy loop in reverse */
117		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
118			int woffset;
119			unsigned short *wblock = (unsigned short*)block;
120			woffset = offset / 2;
121			if (offset + loopsize * 2 > blocksize)
122				return -EINVAL;
123			for (i = 0; i < loopsize; i++)
124				wblock[woffset + i] = wblock[woffset - i -1];
125			offset += loopsize * 2;
126		} else {
127			if (offset + loopsize > blocksize)
128				return -EINVAL;
129			for (i = 0; i < loopsize; i++)
130				block[offset + i] = block[offset - i -1];
131			offset += loopsize;
132		}
133
134		/* modify loop pointers */
135		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
136			sp->v.loopend += loopsize;
137		} else {
138			sp->v.loopstart += loopsize;
139			sp->v.loopend += loopsize;
140		}
141		/* add sample pointer */
142		sp->v.end += loopsize;
143	}
144#endif
145
146	/* loopend -> sample end */
147	size = sp->v.size - loopend;
148	if (size < 0)
149		return -EINVAL;
150	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
151		size *= 2;
152	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
153		snd_emu10k1_synth_free(emu, sp->block);
154		sp->block = NULL;
155		return -EFAULT;
156	}
157	offset += size;
158
159	/* clear rest of samples (if any) */
160	if (offset < blocksize)
161		snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
162
163	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
164		/* if no blank loop is attached in the sample, add it */
165		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
166			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
167			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
168		}
169	}
170
171#if 0 /* not supported yet */
172	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
173		/* unsigned -> signed */
174		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
175			unsigned short *wblock = (unsigned short*)block;
176			for (i = 0; i < truesize; i++)
177				wblock[i] ^= 0x8000;
178		} else {
179			for (i = 0; i < truesize; i++)
180				block[i] ^= 0x80;
181		}
182	}
183#endif
184
185	/* recalculate offset */
186	start_addr = BLANK_HEAD_SIZE * 2;
187	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
188		start_addr >>= 1;
189	sp->v.start += start_addr;
190	sp->v.end += start_addr;
191	sp->v.loopstart += start_addr;
192	sp->v.loopend += start_addr;
193
194	return 0;
195}
196
197/*
198 * free a sample block
199 */
200int
201snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
202			struct snd_util_memhdr *hdr)
203{
204	struct snd_emu10k1 *emu;
205
206	emu = rec->hw;
207	if (snd_BUG_ON(!sp || !hdr))
208		return -EINVAL;
209
210	if (sp->block) {
211		snd_emu10k1_synth_free(emu, sp->block);
212		sp->block = NULL;
213	}
214	return 0;
215}
216
217