xref: /openbmc/linux/sound/pci/emu10k1/emu10k1_patch.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
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 
224