blob: 0e069aeab86d1075112daf01fe6384332017eb63 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Patch transfer callback for Emu10k1
3 *
4 * Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20/*
21 * All the code for loading in a patch. There is very little that is
22 * chip specific here. Just the actual writing to the board.
23 */
24
25#include "emu10k1_synth_local.h"
26
27/*
28 */
29#define BLANK_LOOP_START 4
30#define BLANK_LOOP_END 8
31#define BLANK_LOOP_SIZE 12
32#define BLANK_HEAD_SIZE 32
33
34/*
35 * allocate a sample block and copy data from userspace
36 */
37int
Takashi Iwaieb4698f2005-11-17 14:50:13 +010038snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
39 struct snd_util_memhdr *hdr,
40 const void __user *data, long count)
Linus Torvalds1da177e2005-04-16 15:20:36 -070041{
42 int offset;
43 int truesize, size, loopsize, blocksize;
44 int loopend, sampleend;
45 unsigned int start_addr;
Takashi Iwaieb4698f2005-11-17 14:50:13 +010046 struct snd_emu10k1 *emu;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
48 emu = rec->hw;
Takashi Iwaida3cec32008-08-08 17:12:14 +020049 if (snd_BUG_ON(!sp || !hdr))
50 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52 if (sp->v.size == 0) {
Takashi Iwai6f002b02014-02-25 17:02:09 +010053 dev_dbg(emu->card->dev,
54 "emu: rom font for sample %d\n", sp->v.sample);
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 return 0;
56 }
57
58 /* recalculate address offset */
59 sp->v.end -= sp->v.start;
60 sp->v.loopstart -= sp->v.start;
61 sp->v.loopend -= sp->v.start;
62 sp->v.start = 0;
63
64 /* some samples have invalid data. the addresses are corrected in voice info */
65 sampleend = sp->v.end;
66 if (sampleend > sp->v.size)
67 sampleend = sp->v.size;
68 loopend = sp->v.loopend;
69 if (loopend > sampleend)
70 loopend = sampleend;
71
72 /* be sure loop points start < end */
73 if (sp->v.loopstart >= sp->v.loopend) {
74 int tmp = sp->v.loopstart;
75 sp->v.loopstart = sp->v.loopend;
76 sp->v.loopend = tmp;
77 }
78
79 /* compute true data size to be loaded */
80 truesize = sp->v.size + BLANK_HEAD_SIZE;
81 loopsize = 0;
82#if 0 /* not supported */
83 if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
84 loopsize = sp->v.loopend - sp->v.loopstart;
85 truesize += loopsize;
86#endif
87 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
88 truesize += BLANK_LOOP_SIZE;
89
90 /* try to allocate a memory block */
91 blocksize = truesize;
92 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
93 blocksize *= 2;
94 sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
95 if (sp->block == NULL) {
Takashi Iwai6f002b02014-02-25 17:02:09 +010096 dev_dbg(emu->card->dev,
97 "synth malloc failed (size=%d)\n", blocksize);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 /* not ENOMEM (for compatibility with OSS) */
99 return -ENOSPC;
100 }
101 /* set the total size */
102 sp->v.truesize = blocksize;
103
104 /* write blank samples at head */
105 offset = 0;
106 size = BLANK_HEAD_SIZE;
107 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
108 size *= 2;
Takashi Iwaida3cec32008-08-08 17:12:14 +0200109 if (offset + size > blocksize)
110 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
112 offset += size;
113
114 /* copy start->loopend */
115 size = loopend;
116 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
117 size *= 2;
Takashi Iwaida3cec32008-08-08 17:12:14 +0200118 if (offset + size > blocksize)
119 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
121 snd_emu10k1_synth_free(emu, sp->block);
122 sp->block = NULL;
123 return -EFAULT;
124 }
125 offset += size;
126 data += size;
127
Masanari Iidad7558142012-08-22 18:40:21 +0900128#if 0 /* not supported yet */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 /* handle reverse (or bidirectional) loop */
130 if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
131 /* copy loop in reverse */
132 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
133 int woffset;
134 unsigned short *wblock = (unsigned short*)block;
135 woffset = offset / 2;
Takashi Iwaida3cec32008-08-08 17:12:14 +0200136 if (offset + loopsize * 2 > blocksize)
137 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 for (i = 0; i < loopsize; i++)
139 wblock[woffset + i] = wblock[woffset - i -1];
140 offset += loopsize * 2;
141 } else {
Takashi Iwaida3cec32008-08-08 17:12:14 +0200142 if (offset + loopsize > blocksize)
143 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 for (i = 0; i < loopsize; i++)
145 block[offset + i] = block[offset - i -1];
146 offset += loopsize;
147 }
148
149 /* modify loop pointers */
150 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
151 sp->v.loopend += loopsize;
152 } else {
153 sp->v.loopstart += loopsize;
154 sp->v.loopend += loopsize;
155 }
156 /* add sample pointer */
157 sp->v.end += loopsize;
158 }
159#endif
160
161 /* loopend -> sample end */
162 size = sp->v.size - loopend;
Takashi Iwaida3cec32008-08-08 17:12:14 +0200163 if (size < 0)
164 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
166 size *= 2;
167 if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
168 snd_emu10k1_synth_free(emu, sp->block);
169 sp->block = NULL;
170 return -EFAULT;
171 }
172 offset += size;
173
174 /* clear rest of samples (if any) */
175 if (offset < blocksize)
176 snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
177
178 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
179 /* if no blank loop is attached in the sample, add it */
180 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
181 sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
182 sp->v.loopend = sp->v.end + BLANK_LOOP_END;
183 }
184 }
185
186#if 0 /* not supported yet */
187 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
188 /* unsigned -> signed */
189 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
190 unsigned short *wblock = (unsigned short*)block;
191 for (i = 0; i < truesize; i++)
192 wblock[i] ^= 0x8000;
193 } else {
194 for (i = 0; i < truesize; i++)
195 block[i] ^= 0x80;
196 }
197 }
198#endif
199
200 /* recalculate offset */
201 start_addr = BLANK_HEAD_SIZE * 2;
202 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
203 start_addr >>= 1;
204 sp->v.start += start_addr;
205 sp->v.end += start_addr;
206 sp->v.loopstart += start_addr;
207 sp->v.loopend += start_addr;
208
209 return 0;
210}
211
212/*
213 * free a sample block
214 */
215int
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100216snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
217 struct snd_util_memhdr *hdr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100219 struct snd_emu10k1 *emu;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
221 emu = rec->hw;
Takashi Iwaida3cec32008-08-08 17:12:14 +0200222 if (snd_BUG_ON(!sp || !hdr))
223 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
225 if (sp->block) {
226 snd_emu10k1_synth_free(emu, sp->block);
227 sp->block = NULL;
228 }
229 return 0;
230}
231