18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Patch transfer callback for Emu10k1 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2000 Takashi iwai <tiwai@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci/* 88c2ecf20Sopenharmony_ci * All the code for loading in a patch. There is very little that is 98c2ecf20Sopenharmony_ci * chip specific here. Just the actual writing to the board. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "emu10k1_synth_local.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci#define BLANK_LOOP_START 4 178c2ecf20Sopenharmony_ci#define BLANK_LOOP_END 8 188c2ecf20Sopenharmony_ci#define BLANK_LOOP_SIZE 12 198c2ecf20Sopenharmony_ci#define BLANK_HEAD_SIZE 32 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * allocate a sample block and copy data from userspace 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ciint 258c2ecf20Sopenharmony_cisnd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, 268c2ecf20Sopenharmony_ci struct snd_util_memhdr *hdr, 278c2ecf20Sopenharmony_ci const void __user *data, long count) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci int offset; 308c2ecf20Sopenharmony_ci int truesize, size, blocksize; 318c2ecf20Sopenharmony_ci __maybe_unused int loopsize; 328c2ecf20Sopenharmony_ci int loopend, sampleend; 338c2ecf20Sopenharmony_ci unsigned int start_addr; 348c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci emu = rec->hw; 378c2ecf20Sopenharmony_ci if (snd_BUG_ON(!sp || !hdr)) 388c2ecf20Sopenharmony_ci return -EINVAL; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (sp->v.size == 0) { 418c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, 428c2ecf20Sopenharmony_ci "emu: rom font for sample %d\n", sp->v.sample); 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* recalculate address offset */ 478c2ecf20Sopenharmony_ci sp->v.end -= sp->v.start; 488c2ecf20Sopenharmony_ci sp->v.loopstart -= sp->v.start; 498c2ecf20Sopenharmony_ci sp->v.loopend -= sp->v.start; 508c2ecf20Sopenharmony_ci sp->v.start = 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* some samples have invalid data. the addresses are corrected in voice info */ 538c2ecf20Sopenharmony_ci sampleend = sp->v.end; 548c2ecf20Sopenharmony_ci if (sampleend > sp->v.size) 558c2ecf20Sopenharmony_ci sampleend = sp->v.size; 568c2ecf20Sopenharmony_ci loopend = sp->v.loopend; 578c2ecf20Sopenharmony_ci if (loopend > sampleend) 588c2ecf20Sopenharmony_ci loopend = sampleend; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* be sure loop points start < end */ 618c2ecf20Sopenharmony_ci if (sp->v.loopstart >= sp->v.loopend) 628c2ecf20Sopenharmony_ci swap(sp->v.loopstart, sp->v.loopend); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* compute true data size to be loaded */ 658c2ecf20Sopenharmony_ci truesize = sp->v.size + BLANK_HEAD_SIZE; 668c2ecf20Sopenharmony_ci loopsize = 0; 678c2ecf20Sopenharmony_ci#if 0 /* not supported */ 688c2ecf20Sopenharmony_ci if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) 698c2ecf20Sopenharmony_ci loopsize = sp->v.loopend - sp->v.loopstart; 708c2ecf20Sopenharmony_ci truesize += loopsize; 718c2ecf20Sopenharmony_ci#endif 728c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) 738c2ecf20Sopenharmony_ci truesize += BLANK_LOOP_SIZE; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* try to allocate a memory block */ 768c2ecf20Sopenharmony_ci blocksize = truesize; 778c2ecf20Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 788c2ecf20Sopenharmony_ci blocksize *= 2; 798c2ecf20Sopenharmony_ci sp->block = snd_emu10k1_synth_alloc(emu, blocksize); 808c2ecf20Sopenharmony_ci if (sp->block == NULL) { 818c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, 828c2ecf20Sopenharmony_ci "synth malloc failed (size=%d)\n", blocksize); 838c2ecf20Sopenharmony_ci /* not ENOMEM (for compatibility with OSS) */ 848c2ecf20Sopenharmony_ci return -ENOSPC; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci /* set the total size */ 878c2ecf20Sopenharmony_ci sp->v.truesize = blocksize; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* write blank samples at head */ 908c2ecf20Sopenharmony_ci offset = 0; 918c2ecf20Sopenharmony_ci size = BLANK_HEAD_SIZE; 928c2ecf20Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 938c2ecf20Sopenharmony_ci size *= 2; 948c2ecf20Sopenharmony_ci if (offset + size > blocksize) 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci snd_emu10k1_synth_bzero(emu, sp->block, offset, size); 978c2ecf20Sopenharmony_ci offset += size; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* copy start->loopend */ 1008c2ecf20Sopenharmony_ci size = loopend; 1018c2ecf20Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 1028c2ecf20Sopenharmony_ci size *= 2; 1038c2ecf20Sopenharmony_ci if (offset + size > blocksize) 1048c2ecf20Sopenharmony_ci return -EINVAL; 1058c2ecf20Sopenharmony_ci if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { 1068c2ecf20Sopenharmony_ci snd_emu10k1_synth_free(emu, sp->block); 1078c2ecf20Sopenharmony_ci sp->block = NULL; 1088c2ecf20Sopenharmony_ci return -EFAULT; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci offset += size; 1118c2ecf20Sopenharmony_ci data += size; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#if 0 /* not supported yet */ 1148c2ecf20Sopenharmony_ci /* handle reverse (or bidirectional) loop */ 1158c2ecf20Sopenharmony_ci if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) { 1168c2ecf20Sopenharmony_ci /* copy loop in reverse */ 1178c2ecf20Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { 1188c2ecf20Sopenharmony_ci int woffset; 1198c2ecf20Sopenharmony_ci unsigned short *wblock = (unsigned short*)block; 1208c2ecf20Sopenharmony_ci woffset = offset / 2; 1218c2ecf20Sopenharmony_ci if (offset + loopsize * 2 > blocksize) 1228c2ecf20Sopenharmony_ci return -EINVAL; 1238c2ecf20Sopenharmony_ci for (i = 0; i < loopsize; i++) 1248c2ecf20Sopenharmony_ci wblock[woffset + i] = wblock[woffset - i -1]; 1258c2ecf20Sopenharmony_ci offset += loopsize * 2; 1268c2ecf20Sopenharmony_ci } else { 1278c2ecf20Sopenharmony_ci if (offset + loopsize > blocksize) 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci for (i = 0; i < loopsize; i++) 1308c2ecf20Sopenharmony_ci block[offset + i] = block[offset - i -1]; 1318c2ecf20Sopenharmony_ci offset += loopsize; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* modify loop pointers */ 1358c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { 1368c2ecf20Sopenharmony_ci sp->v.loopend += loopsize; 1378c2ecf20Sopenharmony_ci } else { 1388c2ecf20Sopenharmony_ci sp->v.loopstart += loopsize; 1398c2ecf20Sopenharmony_ci sp->v.loopend += loopsize; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci /* add sample pointer */ 1428c2ecf20Sopenharmony_ci sp->v.end += loopsize; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci#endif 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* loopend -> sample end */ 1478c2ecf20Sopenharmony_ci size = sp->v.size - loopend; 1488c2ecf20Sopenharmony_ci if (size < 0) 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 1518c2ecf20Sopenharmony_ci size *= 2; 1528c2ecf20Sopenharmony_ci if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { 1538c2ecf20Sopenharmony_ci snd_emu10k1_synth_free(emu, sp->block); 1548c2ecf20Sopenharmony_ci sp->block = NULL; 1558c2ecf20Sopenharmony_ci return -EFAULT; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci offset += size; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* clear rest of samples (if any) */ 1608c2ecf20Sopenharmony_ci if (offset < blocksize) 1618c2ecf20Sopenharmony_ci snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { 1648c2ecf20Sopenharmony_ci /* if no blank loop is attached in the sample, add it */ 1658c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { 1668c2ecf20Sopenharmony_ci sp->v.loopstart = sp->v.end + BLANK_LOOP_START; 1678c2ecf20Sopenharmony_ci sp->v.loopend = sp->v.end + BLANK_LOOP_END; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#if 0 /* not supported yet */ 1728c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) { 1738c2ecf20Sopenharmony_ci /* unsigned -> signed */ 1748c2ecf20Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) { 1758c2ecf20Sopenharmony_ci unsigned short *wblock = (unsigned short*)block; 1768c2ecf20Sopenharmony_ci for (i = 0; i < truesize; i++) 1778c2ecf20Sopenharmony_ci wblock[i] ^= 0x8000; 1788c2ecf20Sopenharmony_ci } else { 1798c2ecf20Sopenharmony_ci for (i = 0; i < truesize; i++) 1808c2ecf20Sopenharmony_ci block[i] ^= 0x80; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci#endif 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* recalculate offset */ 1868c2ecf20Sopenharmony_ci start_addr = BLANK_HEAD_SIZE * 2; 1878c2ecf20Sopenharmony_ci if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) 1888c2ecf20Sopenharmony_ci start_addr >>= 1; 1898c2ecf20Sopenharmony_ci sp->v.start += start_addr; 1908c2ecf20Sopenharmony_ci sp->v.end += start_addr; 1918c2ecf20Sopenharmony_ci sp->v.loopstart += start_addr; 1928c2ecf20Sopenharmony_ci sp->v.loopend += start_addr; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* 1988c2ecf20Sopenharmony_ci * free a sample block 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ciint 2018c2ecf20Sopenharmony_cisnd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp, 2028c2ecf20Sopenharmony_ci struct snd_util_memhdr *hdr) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct snd_emu10k1 *emu; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci emu = rec->hw; 2078c2ecf20Sopenharmony_ci if (snd_BUG_ON(!sp || !hdr)) 2088c2ecf20Sopenharmony_ci return -EINVAL; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (sp->block) { 2118c2ecf20Sopenharmony_ci snd_emu10k1_synth_free(emu, sp->block); 2128c2ecf20Sopenharmony_ci sp->block = NULL; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 217