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