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
snd_emu10k1_sample_new(struct snd_emux * rec,struct snd_sf_sample * sp,struct snd_util_memhdr * hdr,const void __user * data,long count)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
snd_emu10k1_sample_free(struct snd_emux * rec,struct snd_sf_sample * sp,struct snd_util_memhdr * hdr)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