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, size: 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, blk: 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, blk: sp->block, offset, data, size)) { |
106 | snd_emu10k1_synth_free(emu, blk: 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, blk: sp->block, offset, data, size)) { |
153 | snd_emu10k1_synth_free(emu, blk: 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, blk: sp->block, offset, size: 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, blk: sp->block); |
212 | sp->block = NULL; |
213 | } |
214 | return 0; |
215 | } |
216 | |
217 | |