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 dev_dbg(emu->card->dev, 54 "emu: rom font for sample %d\n", sp->v.sample); 55 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 swap(sp->v.loopstart, sp->v.loopend); 75 76 /* compute true data size to be loaded */ 77 truesize = sp->v.size + BLANK_HEAD_SIZE; 78 loopsize = 0; 79 #if 0 /* not supported */ 80 if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) 81 loopsize = sp->v.loopend - sp->v.loopstart; 82 truesize += loopsize; 83 #endif 84 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) 85 truesize += BLANK_LOOP_SIZE; 86 87 /* try to allocate a memory block */ 88 blocksize = truesize; 89 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 90 blocksize *= 2; 91 sp->block = snd_emu10k1_synth_alloc(emu, blocksize); 92 if (sp->block == NULL) { 93 dev_dbg(emu->card->dev, 94 "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 if (offset + size > blocksize) 107 return -EINVAL; 108 snd_emu10k1_synth_bzero(emu, sp->block, offset, size); 109 offset += size; 110 111 /* copy start->loopend */ 112 size = loopend; 113 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 114 size *= 2; 115 if (offset + size > blocksize) 116 return -EINVAL; 117 if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { 118 snd_emu10k1_synth_free(emu, sp->block); 119 sp->block = NULL; 120 return -EFAULT; 121 } 122 offset += size; 123 data += size; 124 125 #if 0 /* not supported yet */ 126 /* handle reverse (or bidirectional) loop */ 127 if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { 128 /* copy loop in reverse */ 129 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { 130 int woffset; 131 unsigned short *wblock = (unsigned short*)block; 132 woffset = offset / 2; 133 if (offset + loopsize * 2 > blocksize) 134 return -EINVAL; 135 for (i = 0; i < loopsize; i++) 136 wblock[woffset + i] = wblock[woffset - i -1]; 137 offset += loopsize * 2; 138 } else { 139 if (offset + loopsize > blocksize) 140 return -EINVAL; 141 for (i = 0; i < loopsize; i++) 142 block[offset + i] = block[offset - i -1]; 143 offset += loopsize; 144 } 145 146 /* modify loop pointers */ 147 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { 148 sp->v.loopend += loopsize; 149 } else { 150 sp->v.loopstart += loopsize; 151 sp->v.loopend += loopsize; 152 } 153 /* add sample pointer */ 154 sp->v.end += loopsize; 155 } 156 #endif 157 158 /* loopend -> sample end */ 159 size = sp->v.size - loopend; 160 if (size < 0) 161 return -EINVAL; 162 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 163 size *= 2; 164 if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { 165 snd_emu10k1_synth_free(emu, sp->block); 166 sp->block = NULL; 167 return -EFAULT; 168 } 169 offset += size; 170 171 /* clear rest of samples (if any) */ 172 if (offset < blocksize) 173 snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset); 174 175 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { 176 /* if no blank loop is attached in the sample, add it */ 177 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { 178 sp->v.loopstart = sp->v.end + BLANK_LOOP_START; 179 sp->v.loopend = sp->v.end + BLANK_LOOP_END; 180 } 181 } 182 183 #if 0 /* not supported yet */ 184 if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) { 185 /* unsigned -> signed */ 186 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { 187 unsigned short *wblock = (unsigned short*)block; 188 for (i = 0; i < truesize; i++) 189 wblock[i] ^= 0x8000; 190 } else { 191 for (i = 0; i < truesize; i++) 192 block[i] ^= 0x80; 193 } 194 } 195 #endif 196 197 /* recalculate offset */ 198 start_addr = BLANK_HEAD_SIZE * 2; 199 if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 200 start_addr >>= 1; 201 sp->v.start += start_addr; 202 sp->v.end += start_addr; 203 sp->v.loopstart += start_addr; 204 sp->v.loopend += start_addr; 205 206 return 0; 207 } 208 209 /* 210 * free a sample block 211 */ 212 int 213 snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp, 214 struct snd_util_memhdr *hdr) 215 { 216 struct snd_emu10k1 *emu; 217 218 emu = rec->hw; 219 if (snd_BUG_ON(!sp || !hdr)) 220 return -EINVAL; 221 222 if (sp->block) { 223 snd_emu10k1_synth_free(emu, sp->block); 224 sp->block = NULL; 225 } 226 return 0; 227 } 228 229