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