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