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