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  */
24 int
25 snd_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  */
200 int
201 snd_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