18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * synth callback 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#include <linux/export.h> 118c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * prototypes 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_cistatic struct snd_emux_voice *get_voice(struct snd_emux *emu, 178c2ecf20Sopenharmony_ci struct snd_emux_port *port); 188c2ecf20Sopenharmony_cistatic int start_voice(struct snd_emux_voice *vp); 198c2ecf20Sopenharmony_cistatic void trigger_voice(struct snd_emux_voice *vp); 208c2ecf20Sopenharmony_cistatic void release_voice(struct snd_emux_voice *vp); 218c2ecf20Sopenharmony_cistatic void update_voice(struct snd_emux_voice *vp, int update); 228c2ecf20Sopenharmony_cistatic void reset_voice(struct snd_emux *emu, int ch); 238c2ecf20Sopenharmony_cistatic void terminate_voice(struct snd_emux_voice *vp); 248c2ecf20Sopenharmony_cistatic void sysex(struct snd_emux *emu, char *buf, int len, int parsed, 258c2ecf20Sopenharmony_ci struct snd_midi_channel_set *chset); 268c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) 278c2ecf20Sopenharmony_cistatic int oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2); 288c2ecf20Sopenharmony_ci#endif 298c2ecf20Sopenharmony_cistatic int load_fx(struct snd_emux *emu, int type, int mode, 308c2ecf20Sopenharmony_ci const void __user *buf, long len); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void set_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 338c2ecf20Sopenharmony_cistatic void set_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 348c2ecf20Sopenharmony_cistatic void set_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 358c2ecf20Sopenharmony_cistatic void set_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 368c2ecf20Sopenharmony_cistatic void set_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 378c2ecf20Sopenharmony_cistatic void set_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 388c2ecf20Sopenharmony_cistatic void set_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 398c2ecf20Sopenharmony_cistatic void snd_emu8000_tweak_voice(struct snd_emu8000 *emu, int ch); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * Ensure a value is between two points 438c2ecf20Sopenharmony_ci * macro evaluates its args more than once, so changed to upper-case. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) 468c2ecf20Sopenharmony_ci#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* 508c2ecf20Sopenharmony_ci * set up operators 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_cistatic const struct snd_emux_operators emu8000_ops = { 538c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 548c2ecf20Sopenharmony_ci .get_voice = get_voice, 558c2ecf20Sopenharmony_ci .prepare = start_voice, 568c2ecf20Sopenharmony_ci .trigger = trigger_voice, 578c2ecf20Sopenharmony_ci .release = release_voice, 588c2ecf20Sopenharmony_ci .update = update_voice, 598c2ecf20Sopenharmony_ci .terminate = terminate_voice, 608c2ecf20Sopenharmony_ci .reset = reset_voice, 618c2ecf20Sopenharmony_ci .sample_new = snd_emu8000_sample_new, 628c2ecf20Sopenharmony_ci .sample_free = snd_emu8000_sample_free, 638c2ecf20Sopenharmony_ci .sample_reset = snd_emu8000_sample_reset, 648c2ecf20Sopenharmony_ci .load_fx = load_fx, 658c2ecf20Sopenharmony_ci .sysex = sysex, 668c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) 678c2ecf20Sopenharmony_ci .oss_ioctl = oss_ioctl, 688c2ecf20Sopenharmony_ci#endif 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_civoid 728c2ecf20Sopenharmony_cisnd_emu8000_ops_setup(struct snd_emu8000 *hw) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci hw->emu->ops = emu8000_ops; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* 808c2ecf20Sopenharmony_ci * Terminate a voice 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cistatic void 838c2ecf20Sopenharmony_cirelease_voice(struct snd_emux_voice *vp) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int dcysusv; 868c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci hw = vp->hw; 898c2ecf20Sopenharmony_ci dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; 908c2ecf20Sopenharmony_ci EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv); 918c2ecf20Sopenharmony_ci dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease; 928c2ecf20Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_cistatic void 998c2ecf20Sopenharmony_citerminate_voice(struct snd_emux_voice *vp) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci hw = vp->hw; 1048c2ecf20Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_cistatic void 1118c2ecf20Sopenharmony_ciupdate_voice(struct snd_emux_voice *vp, int update) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci hw = vp->hw; 1168c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_VOLUME) 1178c2ecf20Sopenharmony_ci set_volume(hw, vp); 1188c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_PITCH) 1198c2ecf20Sopenharmony_ci set_pitch(hw, vp); 1208c2ecf20Sopenharmony_ci if ((update & SNDRV_EMUX_UPDATE_PAN) && 1218c2ecf20Sopenharmony_ci vp->port->ctrls[EMUX_MD_REALTIME_PAN]) 1228c2ecf20Sopenharmony_ci set_pan(hw, vp); 1238c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_FMMOD) 1248c2ecf20Sopenharmony_ci set_fmmod(hw, vp); 1258c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_TREMFREQ) 1268c2ecf20Sopenharmony_ci set_tremfreq(hw, vp); 1278c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) 1288c2ecf20Sopenharmony_ci set_fm2frq2(hw, vp); 1298c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_Q) 1308c2ecf20Sopenharmony_ci set_filterQ(hw, vp); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* 1358c2ecf20Sopenharmony_ci * Find a channel (voice) within the EMU that is not in use or at least 1368c2ecf20Sopenharmony_ci * less in use than other channels. Always returns a valid pointer 1378c2ecf20Sopenharmony_ci * no matter what. If there is a real shortage of voices then one 1388c2ecf20Sopenharmony_ci * will be cut. Such is life. 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * The channel index (vp->ch) must be initialized in this routine. 1418c2ecf20Sopenharmony_ci * In Emu8k, it is identical with the array index. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_cistatic struct snd_emux_voice * 1448c2ecf20Sopenharmony_ciget_voice(struct snd_emux *emu, struct snd_emux_port *port) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci int i; 1478c2ecf20Sopenharmony_ci struct snd_emux_voice *vp; 1488c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* what we are looking for, in order of preference */ 1518c2ecf20Sopenharmony_ci enum { 1528c2ecf20Sopenharmony_ci OFF=0, RELEASED, PLAYING, END 1538c2ecf20Sopenharmony_ci }; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* Keeps track of what we are finding */ 1568c2ecf20Sopenharmony_ci struct best { 1578c2ecf20Sopenharmony_ci unsigned int time; 1588c2ecf20Sopenharmony_ci int voice; 1598c2ecf20Sopenharmony_ci } best[END]; 1608c2ecf20Sopenharmony_ci struct best *bp; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci hw = emu->hw; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci for (i = 0; i < END; i++) { 1658c2ecf20Sopenharmony_ci best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */ 1668c2ecf20Sopenharmony_ci best[i].voice = -1; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * Go through them all and get a best one to use. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci for (i = 0; i < emu->max_voices; i++) { 1738c2ecf20Sopenharmony_ci int state, val; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci vp = &emu->voices[i]; 1768c2ecf20Sopenharmony_ci state = vp->state; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (state == SNDRV_EMUX_ST_OFF) 1798c2ecf20Sopenharmony_ci bp = best + OFF; 1808c2ecf20Sopenharmony_ci else if (state == SNDRV_EMUX_ST_RELEASED || 1818c2ecf20Sopenharmony_ci state == SNDRV_EMUX_ST_PENDING) { 1828c2ecf20Sopenharmony_ci bp = best + RELEASED; 1838c2ecf20Sopenharmony_ci val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff; 1848c2ecf20Sopenharmony_ci if (! val) 1858c2ecf20Sopenharmony_ci bp = best + OFF; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci else if (state & SNDRV_EMUX_ST_ON) 1888c2ecf20Sopenharmony_ci bp = best + PLAYING; 1898c2ecf20Sopenharmony_ci else 1908c2ecf20Sopenharmony_ci continue; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* check if sample is finished playing (non-looping only) */ 1938c2ecf20Sopenharmony_ci if (state != SNDRV_EMUX_ST_OFF && 1948c2ecf20Sopenharmony_ci (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { 1958c2ecf20Sopenharmony_ci val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; 1968c2ecf20Sopenharmony_ci if (val >= vp->reg.loopstart) 1978c2ecf20Sopenharmony_ci bp = best + OFF; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (vp->time < bp->time) { 2018c2ecf20Sopenharmony_ci bp->time = vp->time; 2028c2ecf20Sopenharmony_ci bp->voice = i; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci for (i = 0; i < END; i++) { 2078c2ecf20Sopenharmony_ci if (best[i].voice >= 0) { 2088c2ecf20Sopenharmony_ci vp = &emu->voices[best[i].voice]; 2098c2ecf20Sopenharmony_ci vp->ch = best[i].voice; 2108c2ecf20Sopenharmony_ci return vp; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* not found */ 2158c2ecf20Sopenharmony_ci return NULL; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic int 2218c2ecf20Sopenharmony_cistart_voice(struct snd_emux_voice *vp) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci unsigned int temp; 2248c2ecf20Sopenharmony_ci int ch; 2258c2ecf20Sopenharmony_ci int addr; 2268c2ecf20Sopenharmony_ci struct snd_midi_channel *chan; 2278c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci hw = vp->hw; 2308c2ecf20Sopenharmony_ci ch = vp->ch; 2318c2ecf20Sopenharmony_ci chan = vp->chan; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* channel to be silent and idle */ 2348c2ecf20Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080); 2358c2ecf20Sopenharmony_ci EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF); 2368c2ecf20Sopenharmony_ci EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF); 2378c2ecf20Sopenharmony_ci EMU8000_PTRX_WRITE(hw, ch, 0); 2388c2ecf20Sopenharmony_ci EMU8000_CPF_WRITE(hw, ch, 0); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* set pitch offset */ 2418c2ecf20Sopenharmony_ci set_pitch(hw, vp); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* set envelope parameters */ 2448c2ecf20Sopenharmony_ci EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay); 2458c2ecf20Sopenharmony_ci EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld); 2468c2ecf20Sopenharmony_ci EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus); 2478c2ecf20Sopenharmony_ci EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay); 2488c2ecf20Sopenharmony_ci EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld); 2498c2ecf20Sopenharmony_ci /* decay/sustain parameter for volume envelope is used 2508c2ecf20Sopenharmony_ci for triggerg the voice */ 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* cutoff and volume */ 2538c2ecf20Sopenharmony_ci set_volume(hw, vp); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* modulation envelope heights */ 2568c2ecf20Sopenharmony_ci EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* lfo1/2 delay */ 2598c2ecf20Sopenharmony_ci EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay); 2608c2ecf20Sopenharmony_ci EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* lfo1 pitch & cutoff shift */ 2638c2ecf20Sopenharmony_ci set_fmmod(hw, vp); 2648c2ecf20Sopenharmony_ci /* lfo1 volume & freq */ 2658c2ecf20Sopenharmony_ci set_tremfreq(hw, vp); 2668c2ecf20Sopenharmony_ci /* lfo2 pitch & freq */ 2678c2ecf20Sopenharmony_ci set_fm2frq2(hw, vp); 2688c2ecf20Sopenharmony_ci /* pan & loop start */ 2698c2ecf20Sopenharmony_ci set_pan(hw, vp); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* chorus & loop end (chorus 8bit, MSB) */ 2728c2ecf20Sopenharmony_ci addr = vp->reg.loopend - 1; 2738c2ecf20Sopenharmony_ci temp = vp->reg.parm.chorus; 2748c2ecf20Sopenharmony_ci temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; 2758c2ecf20Sopenharmony_ci LIMITMAX(temp, 255); 2768c2ecf20Sopenharmony_ci temp = (temp <<24) | (unsigned int)addr; 2778c2ecf20Sopenharmony_ci EMU8000_CSL_WRITE(hw, ch, temp); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Q & current address (Q 4bit value, MSB) */ 2808c2ecf20Sopenharmony_ci addr = vp->reg.start - 1; 2818c2ecf20Sopenharmony_ci temp = vp->reg.parm.filterQ; 2828c2ecf20Sopenharmony_ci temp = (temp<<28) | (unsigned int)addr; 2838c2ecf20Sopenharmony_ci EMU8000_CCCA_WRITE(hw, ch, temp); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* clear unknown registers */ 2868c2ecf20Sopenharmony_ci EMU8000_00A0_WRITE(hw, ch, 0); 2878c2ecf20Sopenharmony_ci EMU8000_0080_WRITE(hw, ch, 0); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* reset volume */ 2908c2ecf20Sopenharmony_ci temp = vp->vtarget << 16; 2918c2ecf20Sopenharmony_ci EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget); 2928c2ecf20Sopenharmony_ci EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* 2988c2ecf20Sopenharmony_ci * Start envelope 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_cistatic void 3018c2ecf20Sopenharmony_citrigger_voice(struct snd_emux_voice *vp) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci int ch = vp->ch; 3048c2ecf20Sopenharmony_ci unsigned int temp; 3058c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci hw = vp->hw; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* set reverb and pitch target */ 3108c2ecf20Sopenharmony_ci temp = vp->reg.parm.reverb; 3118c2ecf20Sopenharmony_ci temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; 3128c2ecf20Sopenharmony_ci LIMITMAX(temp, 255); 3138c2ecf20Sopenharmony_ci temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux; 3148c2ecf20Sopenharmony_ci EMU8000_PTRX_WRITE(hw, ch, temp); 3158c2ecf20Sopenharmony_ci EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16); 3168c2ecf20Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* 3208c2ecf20Sopenharmony_ci * reset voice parameters 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_cistatic void 3238c2ecf20Sopenharmony_cireset_voice(struct snd_emux *emu, int ch) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci hw = emu->hw; 3288c2ecf20Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); 3298c2ecf20Sopenharmony_ci snd_emu8000_tweak_voice(hw, ch); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* 3338c2ecf20Sopenharmony_ci * Set the pitch of a possibly playing note. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_cistatic void 3368c2ecf20Sopenharmony_ciset_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci EMU8000_IP_WRITE(hw, vp->ch, vp->apitch); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* 3428c2ecf20Sopenharmony_ci * Set the volume of a possibly already playing note 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_cistatic void 3458c2ecf20Sopenharmony_ciset_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci int ifatn; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ifatn = (unsigned char)vp->acutoff; 3508c2ecf20Sopenharmony_ci ifatn = (ifatn << 8); 3518c2ecf20Sopenharmony_ci ifatn |= (unsigned char)vp->avol; 3528c2ecf20Sopenharmony_ci EMU8000_IFATN_WRITE(hw, vp->ch, ifatn); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* 3568c2ecf20Sopenharmony_ci * Set pan and loop start address. 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_cistatic void 3598c2ecf20Sopenharmony_ciset_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci unsigned int temp; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1); 3648c2ecf20Sopenharmony_ci EMU8000_PSST_WRITE(hw, vp->ch, temp); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci#define MOD_SENSE 18 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void 3708c2ecf20Sopenharmony_ciset_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci unsigned short fmmod; 3738c2ecf20Sopenharmony_ci short pitch; 3748c2ecf20Sopenharmony_ci unsigned char cutoff; 3758c2ecf20Sopenharmony_ci int modulation; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci pitch = (char)(vp->reg.parm.fmmod>>8); 3788c2ecf20Sopenharmony_ci cutoff = (vp->reg.parm.fmmod & 0xff); 3798c2ecf20Sopenharmony_ci modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; 3808c2ecf20Sopenharmony_ci pitch += (MOD_SENSE * modulation) / 1200; 3818c2ecf20Sopenharmony_ci LIMITVALUE(pitch, -128, 127); 3828c2ecf20Sopenharmony_ci fmmod = ((unsigned char)pitch<<8) | cutoff; 3838c2ecf20Sopenharmony_ci EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* set tremolo (lfo1) volume & frequency */ 3878c2ecf20Sopenharmony_cistatic void 3888c2ecf20Sopenharmony_ciset_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/* set lfo2 pitch & frequency */ 3948c2ecf20Sopenharmony_cistatic void 3958c2ecf20Sopenharmony_ciset_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci unsigned short fm2frq2; 3988c2ecf20Sopenharmony_ci short pitch; 3998c2ecf20Sopenharmony_ci unsigned char freq; 4008c2ecf20Sopenharmony_ci int modulation; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci pitch = (char)(vp->reg.parm.fm2frq2>>8); 4038c2ecf20Sopenharmony_ci freq = vp->reg.parm.fm2frq2 & 0xff; 4048c2ecf20Sopenharmony_ci modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; 4058c2ecf20Sopenharmony_ci pitch += (MOD_SENSE * modulation) / 1200; 4068c2ecf20Sopenharmony_ci LIMITVALUE(pitch, -128, 127); 4078c2ecf20Sopenharmony_ci fm2frq2 = ((unsigned char)pitch<<8) | freq; 4088c2ecf20Sopenharmony_ci EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/* set filterQ */ 4128c2ecf20Sopenharmony_cistatic void 4138c2ecf20Sopenharmony_ciset_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci unsigned int addr; 4168c2ecf20Sopenharmony_ci addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; 4178c2ecf20Sopenharmony_ci addr |= (vp->reg.parm.filterQ << 28); 4188c2ecf20Sopenharmony_ci EMU8000_CCCA_WRITE(hw, vp->ch, addr); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* 4228c2ecf20Sopenharmony_ci * set the envelope & LFO parameters to the default values 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_cistatic void 4258c2ecf20Sopenharmony_cisnd_emu8000_tweak_voice(struct snd_emu8000 *emu, int i) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci /* set all mod/vol envelope shape to minimum */ 4288c2ecf20Sopenharmony_ci EMU8000_ENVVOL_WRITE(emu, i, 0x8000); 4298c2ecf20Sopenharmony_ci EMU8000_ENVVAL_WRITE(emu, i, 0x8000); 4308c2ecf20Sopenharmony_ci EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F); 4318c2ecf20Sopenharmony_ci EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F); 4328c2ecf20Sopenharmony_ci EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F); 4338c2ecf20Sopenharmony_ci EMU8000_PEFE_WRITE(emu, i, 0); /* mod envelope height to zero */ 4348c2ecf20Sopenharmony_ci EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */ 4358c2ecf20Sopenharmony_ci EMU8000_LFO2VAL_WRITE(emu, i, 0x8000); 4368c2ecf20Sopenharmony_ci EMU8000_IP_WRITE(emu, i, 0xE000); /* no pitch shift */ 4378c2ecf20Sopenharmony_ci EMU8000_IFATN_WRITE(emu, i, 0xFF00); /* volume to minimum */ 4388c2ecf20Sopenharmony_ci EMU8000_FMMOD_WRITE(emu, i, 0); 4398c2ecf20Sopenharmony_ci EMU8000_TREMFRQ_WRITE(emu, i, 0); 4408c2ecf20Sopenharmony_ci EMU8000_FM2FRQ2_WRITE(emu, i, 0); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* 4448c2ecf20Sopenharmony_ci * sysex callback 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_cistatic void 4478c2ecf20Sopenharmony_cisysex(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_channel_set *chset) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci hw = emu->hw; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci switch (parsed) { 4548c2ecf20Sopenharmony_ci case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE: 4558c2ecf20Sopenharmony_ci hw->chorus_mode = chset->gs_chorus_mode; 4568c2ecf20Sopenharmony_ci snd_emu8000_update_chorus_mode(hw); 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci case SNDRV_MIDI_SYSEX_GS_REVERB_MODE: 4608c2ecf20Sopenharmony_ci hw->reverb_mode = chset->gs_reverb_mode; 4618c2ecf20Sopenharmony_ci snd_emu8000_update_reverb_mode(hw); 4628c2ecf20Sopenharmony_ci break; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) 4688c2ecf20Sopenharmony_ci/* 4698c2ecf20Sopenharmony_ci * OSS ioctl callback 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_cistatic int 4728c2ecf20Sopenharmony_cioss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci hw = emu->hw; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci switch (cmd) { 4798c2ecf20Sopenharmony_ci case _EMUX_OSS_REVERB_MODE: 4808c2ecf20Sopenharmony_ci hw->reverb_mode = p1; 4818c2ecf20Sopenharmony_ci snd_emu8000_update_reverb_mode(hw); 4828c2ecf20Sopenharmony_ci break; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci case _EMUX_OSS_CHORUS_MODE: 4858c2ecf20Sopenharmony_ci hw->chorus_mode = p1; 4868c2ecf20Sopenharmony_ci snd_emu8000_update_chorus_mode(hw); 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci case _EMUX_OSS_INITIALIZE_CHIP: 4908c2ecf20Sopenharmony_ci /* snd_emu8000_init(hw); */ /*ignored*/ 4918c2ecf20Sopenharmony_ci break; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci case _EMUX_OSS_EQUALIZER: 4948c2ecf20Sopenharmony_ci hw->bass_level = p1; 4958c2ecf20Sopenharmony_ci hw->treble_level = p2; 4968c2ecf20Sopenharmony_ci snd_emu8000_update_equalizer(hw); 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci#endif 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci/* 5058c2ecf20Sopenharmony_ci * additional patch keys 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci#define SNDRV_EMU8000_LOAD_CHORUS_FX 0x10 /* optarg=mode */ 5098c2ecf20Sopenharmony_ci#define SNDRV_EMU8000_LOAD_REVERB_FX 0x11 /* optarg=mode */ 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci/* 5138c2ecf20Sopenharmony_ci * callback routine 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int 5178c2ecf20Sopenharmony_ciload_fx(struct snd_emux *emu, int type, int mode, const void __user *buf, long len) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct snd_emu8000 *hw; 5208c2ecf20Sopenharmony_ci hw = emu->hw; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* skip header */ 5238c2ecf20Sopenharmony_ci buf += 16; 5248c2ecf20Sopenharmony_ci len -= 16; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci switch (type) { 5278c2ecf20Sopenharmony_ci case SNDRV_EMU8000_LOAD_CHORUS_FX: 5288c2ecf20Sopenharmony_ci return snd_emu8000_load_chorus_fx(hw, mode, buf, len); 5298c2ecf20Sopenharmony_ci case SNDRV_EMU8000_LOAD_REVERB_FX: 5308c2ecf20Sopenharmony_ci return snd_emu8000_load_reverb_fx(hw, mode, buf, len); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci return -EINVAL; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 535