18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Patch routines for the emu8000 (AWE32/64) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1999 Steve Ratcliffe 68c2ecf20Sopenharmony_ci * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "emu8000_local.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 128c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic int emu8000_reset_addr; 168c2ecf20Sopenharmony_cimodule_param(emu8000_reset_addr, int, 0444); 178c2ecf20Sopenharmony_ciMODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)"); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * Open up channels. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_cistatic int 248c2ecf20Sopenharmony_cisnd_emu8000_open_dma(struct snd_emu8000 *emu, int write) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci int i; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci /* reserve all 30 voices for loading */ 298c2ecf20Sopenharmony_ci for (i = 0; i < EMU8000_DRAM_VOICES; i++) { 308c2ecf20Sopenharmony_ci snd_emux_lock_voice(emu->emu, i); 318c2ecf20Sopenharmony_ci snd_emu8000_dma_chan(emu, i, write); 328c2ecf20Sopenharmony_ci } 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* assign voice 31 and 32 to ROM */ 358c2ecf20Sopenharmony_ci EMU8000_VTFT_WRITE(emu, 30, 0); 368c2ecf20Sopenharmony_ci EMU8000_PSST_WRITE(emu, 30, 0x1d8); 378c2ecf20Sopenharmony_ci EMU8000_CSL_WRITE(emu, 30, 0x1e0); 388c2ecf20Sopenharmony_ci EMU8000_CCCA_WRITE(emu, 30, 0x1d8); 398c2ecf20Sopenharmony_ci EMU8000_VTFT_WRITE(emu, 31, 0); 408c2ecf20Sopenharmony_ci EMU8000_PSST_WRITE(emu, 31, 0x1d8); 418c2ecf20Sopenharmony_ci EMU8000_CSL_WRITE(emu, 31, 0x1e0); 428c2ecf20Sopenharmony_ci EMU8000_CCCA_WRITE(emu, 31, 0x1d8); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * Close all dram channels. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic void 518c2ecf20Sopenharmony_cisnd_emu8000_close_dma(struct snd_emu8000 *emu) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci int i; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for (i = 0; i < EMU8000_DRAM_VOICES; i++) { 568c2ecf20Sopenharmony_ci snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE); 578c2ecf20Sopenharmony_ci snd_emux_unlock_voice(emu->emu, i); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define BLANK_LOOP_START 4 658c2ecf20Sopenharmony_ci#define BLANK_LOOP_END 8 668c2ecf20Sopenharmony_ci#define BLANK_LOOP_SIZE 12 678c2ecf20Sopenharmony_ci#define BLANK_HEAD_SIZE 48 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Read a word from userland, taking care of conversions from 718c2ecf20Sopenharmony_ci * 8bit samples etc. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic unsigned short 748c2ecf20Sopenharmony_ciread_word(const void __user *buf, int offset, int mode) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned short c; 778c2ecf20Sopenharmony_ci if (mode & SNDRV_SFNT_SAMPLE_8BITS) { 788c2ecf20Sopenharmony_ci unsigned char cc; 798c2ecf20Sopenharmony_ci get_user(cc, (unsigned char __user *)buf + offset); 808c2ecf20Sopenharmony_ci c = cc << 8; /* convert 8bit -> 16bit */ 818c2ecf20Sopenharmony_ci } else { 828c2ecf20Sopenharmony_ci#ifdef SNDRV_LITTLE_ENDIAN 838c2ecf20Sopenharmony_ci get_user(c, (unsigned short __user *)buf + offset); 848c2ecf20Sopenharmony_ci#else 858c2ecf20Sopenharmony_ci unsigned short cc; 868c2ecf20Sopenharmony_ci get_user(cc, (unsigned short __user *)buf + offset); 878c2ecf20Sopenharmony_ci c = swab16(cc); 888c2ecf20Sopenharmony_ci#endif 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED) 918c2ecf20Sopenharmony_ci c ^= 0x8000; /* unsigned -> signed */ 928c2ecf20Sopenharmony_ci return c; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic void 988c2ecf20Sopenharmony_cisnd_emu8000_write_wait(struct snd_emu8000 *emu) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { 1018c2ecf20Sopenharmony_ci schedule_timeout_interruptible(1); 1028c2ecf20Sopenharmony_ci if (signal_pending(current)) 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* 1088c2ecf20Sopenharmony_ci * write sample word data 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci * You should not have to keep resetting the address each time 1118c2ecf20Sopenharmony_ci * as the chip is supposed to step on the next address automatically. 1128c2ecf20Sopenharmony_ci * It mostly does, but during writes of some samples at random it 1138c2ecf20Sopenharmony_ci * completely loses words (every one in 16 roughly but with no 1148c2ecf20Sopenharmony_ci * obvious pattern). 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * This is therefore much slower than need be, but is at least 1178c2ecf20Sopenharmony_ci * working. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic inline void 1208c2ecf20Sopenharmony_ciwrite_word(struct snd_emu8000 *emu, int *offset, unsigned short data) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci if (emu8000_reset_addr) { 1238c2ecf20Sopenharmony_ci if (emu8000_reset_addr > 1) 1248c2ecf20Sopenharmony_ci snd_emu8000_write_wait(emu); 1258c2ecf20Sopenharmony_ci EMU8000_SMALW_WRITE(emu, *offset); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci EMU8000_SMLD_WRITE(emu, data); 1288c2ecf20Sopenharmony_ci *offset += 1; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * Write the sample to EMU800 memory. This routine is invoked out of 1338c2ecf20Sopenharmony_ci * the generic soundfont routines as a callback. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ciint 1368c2ecf20Sopenharmony_cisnd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp, 1378c2ecf20Sopenharmony_ci struct snd_util_memhdr *hdr, 1388c2ecf20Sopenharmony_ci const void __user *data, long count) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int i; 1418c2ecf20Sopenharmony_ci int rc; 1428c2ecf20Sopenharmony_ci int offset; 1438c2ecf20Sopenharmony_ci int truesize; 1448c2ecf20Sopenharmony_ci int dram_offset, dram_start; 1458c2ecf20Sopenharmony_ci struct snd_emu8000 *emu; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci emu = rec->hw; 1488c2ecf20Sopenharmony_ci if (snd_BUG_ON(!sp)) 1498c2ecf20Sopenharmony_ci return -EINVAL; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (sp->v.size == 0) 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* be sure loop points start < end */ 1558c2ecf20Sopenharmony_ci if (sp->v.loopstart > sp->v.loopend) 1568c2ecf20Sopenharmony_ci swap(sp->v.loopstart, sp->v.loopend); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* compute true data size to be loaded */ 1598c2ecf20Sopenharmony_ci truesize = sp->v.size; 1608c2ecf20Sopenharmony_ci if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) 1618c2ecf20Sopenharmony_ci truesize += sp->v.loopend - sp->v.loopstart; 1628c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) 1638c2ecf20Sopenharmony_ci truesize += BLANK_LOOP_SIZE; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci sp->block = snd_util_mem_alloc(hdr, truesize * 2); 1668c2ecf20Sopenharmony_ci if (sp->block == NULL) { 1678c2ecf20Sopenharmony_ci /*snd_printd("EMU8000: out of memory\n");*/ 1688c2ecf20Sopenharmony_ci /* not ENOMEM (for compatibility) */ 1698c2ecf20Sopenharmony_ci return -ENOSPC; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) { 1738c2ecf20Sopenharmony_ci if (!access_ok(data, sp->v.size)) 1748c2ecf20Sopenharmony_ci return -EFAULT; 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci if (!access_ok(data, sp->v.size * 2)) 1778c2ecf20Sopenharmony_ci return -EFAULT; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* recalculate address offset */ 1818c2ecf20Sopenharmony_ci sp->v.end -= sp->v.start; 1828c2ecf20Sopenharmony_ci sp->v.loopstart -= sp->v.start; 1838c2ecf20Sopenharmony_ci sp->v.loopend -= sp->v.start; 1848c2ecf20Sopenharmony_ci sp->v.start = 0; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* dram position (in word) -- mem_offset is byte */ 1878c2ecf20Sopenharmony_ci dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1); 1888c2ecf20Sopenharmony_ci dram_start = dram_offset; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* set the total size (store onto obsolete checksum value) */ 1918c2ecf20Sopenharmony_ci sp->v.truesize = truesize * 2; /* in bytes */ 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci snd_emux_terminate_all(emu->emu); 1948c2ecf20Sopenharmony_ci if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0) 1958c2ecf20Sopenharmony_ci return rc; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Set the address to start writing at */ 1988c2ecf20Sopenharmony_ci snd_emu8000_write_wait(emu); 1998c2ecf20Sopenharmony_ci EMU8000_SMALW_WRITE(emu, dram_offset); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /*snd_emu8000_init_fm(emu);*/ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci#if 0 2048c2ecf20Sopenharmony_ci /* first block - write 48 samples for silence */ 2058c2ecf20Sopenharmony_ci if (! sp->block->offset) { 2068c2ecf20Sopenharmony_ci for (i = 0; i < BLANK_HEAD_SIZE; i++) { 2078c2ecf20Sopenharmony_ci write_word(emu, &dram_offset, 0); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci#endif 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci offset = 0; 2138c2ecf20Sopenharmony_ci for (i = 0; i < sp->v.size; i++) { 2148c2ecf20Sopenharmony_ci unsigned short s; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci s = read_word(data, offset, sp->v.mode_flags); 2178c2ecf20Sopenharmony_ci offset++; 2188c2ecf20Sopenharmony_ci write_word(emu, &dram_offset, s); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* we may take too long time in this loop. 2218c2ecf20Sopenharmony_ci * so give controls back to kernel if needed. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci cond_resched(); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (i == sp->v.loopend && 2268c2ecf20Sopenharmony_ci (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))) 2278c2ecf20Sopenharmony_ci { 2288c2ecf20Sopenharmony_ci int looplen = sp->v.loopend - sp->v.loopstart; 2298c2ecf20Sopenharmony_ci int k; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* copy reverse loop */ 2328c2ecf20Sopenharmony_ci for (k = 1; k <= looplen; k++) { 2338c2ecf20Sopenharmony_ci s = read_word(data, offset - k, sp->v.mode_flags); 2348c2ecf20Sopenharmony_ci write_word(emu, &dram_offset, s); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) { 2378c2ecf20Sopenharmony_ci sp->v.loopend += looplen; 2388c2ecf20Sopenharmony_ci } else { 2398c2ecf20Sopenharmony_ci sp->v.loopstart += looplen; 2408c2ecf20Sopenharmony_ci sp->v.loopend += looplen; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci sp->v.end += looplen; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* if no blank loop is attached in the sample, add it */ 2478c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) { 2488c2ecf20Sopenharmony_ci for (i = 0; i < BLANK_LOOP_SIZE; i++) { 2498c2ecf20Sopenharmony_ci write_word(emu, &dram_offset, 0); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) { 2528c2ecf20Sopenharmony_ci sp->v.loopstart = sp->v.end + BLANK_LOOP_START; 2538c2ecf20Sopenharmony_ci sp->v.loopend = sp->v.end + BLANK_LOOP_END; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* add dram offset */ 2588c2ecf20Sopenharmony_ci sp->v.start += dram_start; 2598c2ecf20Sopenharmony_ci sp->v.end += dram_start; 2608c2ecf20Sopenharmony_ci sp->v.loopstart += dram_start; 2618c2ecf20Sopenharmony_ci sp->v.loopend += dram_start; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci snd_emu8000_close_dma(emu); 2648c2ecf20Sopenharmony_ci snd_emu8000_init_fm(emu); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * free a sample block 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ciint 2738c2ecf20Sopenharmony_cisnd_emu8000_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp, 2748c2ecf20Sopenharmony_ci struct snd_util_memhdr *hdr) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci if (sp->block) { 2778c2ecf20Sopenharmony_ci snd_util_mem_free(hdr, sp->block); 2788c2ecf20Sopenharmony_ci sp->block = NULL; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* 2858c2ecf20Sopenharmony_ci * sample_reset callback - terminate voices 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_civoid 2888c2ecf20Sopenharmony_cisnd_emu8000_sample_reset(struct snd_emux *rec) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci snd_emux_terminate_all(rec); 2918c2ecf20Sopenharmony_ci} 292