Merge /spare/repo/linux-2.6/
[linux-2.6] / 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