162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * synth callback routines for the emu8000 (AWE32/64) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1999 Steve Ratcliffe 662306a36Sopenharmony_ci * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "emu8000_local.h" 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <sound/asoundef.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * prototypes 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_cistatic struct snd_emux_voice *get_voice(struct snd_emux *emu, 1762306a36Sopenharmony_ci struct snd_emux_port *port); 1862306a36Sopenharmony_cistatic int start_voice(struct snd_emux_voice *vp); 1962306a36Sopenharmony_cistatic void trigger_voice(struct snd_emux_voice *vp); 2062306a36Sopenharmony_cistatic void release_voice(struct snd_emux_voice *vp); 2162306a36Sopenharmony_cistatic void update_voice(struct snd_emux_voice *vp, int update); 2262306a36Sopenharmony_cistatic void reset_voice(struct snd_emux *emu, int ch); 2362306a36Sopenharmony_cistatic void terminate_voice(struct snd_emux_voice *vp); 2462306a36Sopenharmony_cistatic void sysex(struct snd_emux *emu, char *buf, int len, int parsed, 2562306a36Sopenharmony_ci struct snd_midi_channel_set *chset); 2662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) 2762306a36Sopenharmony_cistatic int oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2); 2862306a36Sopenharmony_ci#endif 2962306a36Sopenharmony_cistatic int load_fx(struct snd_emux *emu, int type, int mode, 3062306a36Sopenharmony_ci const void __user *buf, long len); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void set_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 3362306a36Sopenharmony_cistatic void set_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 3462306a36Sopenharmony_cistatic void set_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 3562306a36Sopenharmony_cistatic void set_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 3662306a36Sopenharmony_cistatic void set_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 3762306a36Sopenharmony_cistatic void set_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 3862306a36Sopenharmony_cistatic void set_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp); 3962306a36Sopenharmony_cistatic void snd_emu8000_tweak_voice(struct snd_emu8000 *emu, int ch); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * Ensure a value is between two points 4362306a36Sopenharmony_ci * macro evaluates its args more than once, so changed to upper-case. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) 4662306a36Sopenharmony_ci#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * set up operators 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic const struct snd_emux_operators emu8000_ops = { 5362306a36Sopenharmony_ci .owner = THIS_MODULE, 5462306a36Sopenharmony_ci .get_voice = get_voice, 5562306a36Sopenharmony_ci .prepare = start_voice, 5662306a36Sopenharmony_ci .trigger = trigger_voice, 5762306a36Sopenharmony_ci .release = release_voice, 5862306a36Sopenharmony_ci .update = update_voice, 5962306a36Sopenharmony_ci .terminate = terminate_voice, 6062306a36Sopenharmony_ci .reset = reset_voice, 6162306a36Sopenharmony_ci .sample_new = snd_emu8000_sample_new, 6262306a36Sopenharmony_ci .sample_free = snd_emu8000_sample_free, 6362306a36Sopenharmony_ci .sample_reset = snd_emu8000_sample_reset, 6462306a36Sopenharmony_ci .load_fx = load_fx, 6562306a36Sopenharmony_ci .sysex = sysex, 6662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) 6762306a36Sopenharmony_ci .oss_ioctl = oss_ioctl, 6862306a36Sopenharmony_ci#endif 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_civoid 7262306a36Sopenharmony_cisnd_emu8000_ops_setup(struct snd_emu8000 *hw) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci hw->emu->ops = emu8000_ops; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * Terminate a voice 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistatic void 8362306a36Sopenharmony_cirelease_voice(struct snd_emux_voice *vp) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci int dcysusv; 8662306a36Sopenharmony_ci struct snd_emu8000 *hw; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci hw = vp->hw; 8962306a36Sopenharmony_ci dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; 9062306a36Sopenharmony_ci EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv); 9162306a36Sopenharmony_ci dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease; 9262306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistatic void 9962306a36Sopenharmony_citerminate_voice(struct snd_emux_voice *vp) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct snd_emu8000 *hw; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci hw = vp->hw; 10462306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistatic void 11162306a36Sopenharmony_ciupdate_voice(struct snd_emux_voice *vp, int update) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct snd_emu8000 *hw; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci hw = vp->hw; 11662306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_VOLUME) 11762306a36Sopenharmony_ci set_volume(hw, vp); 11862306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_PITCH) 11962306a36Sopenharmony_ci set_pitch(hw, vp); 12062306a36Sopenharmony_ci if ((update & SNDRV_EMUX_UPDATE_PAN) && 12162306a36Sopenharmony_ci vp->port->ctrls[EMUX_MD_REALTIME_PAN]) 12262306a36Sopenharmony_ci set_pan(hw, vp); 12362306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_FMMOD) 12462306a36Sopenharmony_ci set_fmmod(hw, vp); 12562306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_TREMFREQ) 12662306a36Sopenharmony_ci set_tremfreq(hw, vp); 12762306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) 12862306a36Sopenharmony_ci set_fm2frq2(hw, vp); 12962306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_Q) 13062306a36Sopenharmony_ci set_filterQ(hw, vp); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * Find a channel (voice) within the EMU that is not in use or at least 13662306a36Sopenharmony_ci * less in use than other channels. Always returns a valid pointer 13762306a36Sopenharmony_ci * no matter what. If there is a real shortage of voices then one 13862306a36Sopenharmony_ci * will be cut. Such is life. 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * The channel index (vp->ch) must be initialized in this routine. 14162306a36Sopenharmony_ci * In Emu8k, it is identical with the array index. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_cistatic struct snd_emux_voice * 14462306a36Sopenharmony_ciget_voice(struct snd_emux *emu, struct snd_emux_port *port) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci int i; 14762306a36Sopenharmony_ci struct snd_emux_voice *vp; 14862306a36Sopenharmony_ci struct snd_emu8000 *hw; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* what we are looking for, in order of preference */ 15162306a36Sopenharmony_ci enum { 15262306a36Sopenharmony_ci OFF=0, RELEASED, PLAYING, END 15362306a36Sopenharmony_ci }; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Keeps track of what we are finding */ 15662306a36Sopenharmony_ci struct best { 15762306a36Sopenharmony_ci unsigned int time; 15862306a36Sopenharmony_ci int voice; 15962306a36Sopenharmony_ci } best[END]; 16062306a36Sopenharmony_ci struct best *bp; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci hw = emu->hw; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci for (i = 0; i < END; i++) { 16562306a36Sopenharmony_ci best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */ 16662306a36Sopenharmony_ci best[i].voice = -1; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * Go through them all and get a best one to use. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci for (i = 0; i < emu->max_voices; i++) { 17362306a36Sopenharmony_ci int state, val; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci vp = &emu->voices[i]; 17662306a36Sopenharmony_ci state = vp->state; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (state == SNDRV_EMUX_ST_OFF) 17962306a36Sopenharmony_ci bp = best + OFF; 18062306a36Sopenharmony_ci else if (state == SNDRV_EMUX_ST_RELEASED || 18162306a36Sopenharmony_ci state == SNDRV_EMUX_ST_PENDING) { 18262306a36Sopenharmony_ci bp = best + RELEASED; 18362306a36Sopenharmony_ci val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff; 18462306a36Sopenharmony_ci if (! val) 18562306a36Sopenharmony_ci bp = best + OFF; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci else if (state & SNDRV_EMUX_ST_ON) 18862306a36Sopenharmony_ci bp = best + PLAYING; 18962306a36Sopenharmony_ci else 19062306a36Sopenharmony_ci continue; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* check if sample is finished playing (non-looping only) */ 19362306a36Sopenharmony_ci if (state != SNDRV_EMUX_ST_OFF && 19462306a36Sopenharmony_ci (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { 19562306a36Sopenharmony_ci val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; 19662306a36Sopenharmony_ci if (val >= vp->reg.loopstart) 19762306a36Sopenharmony_ci bp = best + OFF; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (vp->time < bp->time) { 20162306a36Sopenharmony_ci bp->time = vp->time; 20262306a36Sopenharmony_ci bp->voice = i; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for (i = 0; i < END; i++) { 20762306a36Sopenharmony_ci if (best[i].voice >= 0) { 20862306a36Sopenharmony_ci vp = &emu->voices[best[i].voice]; 20962306a36Sopenharmony_ci vp->ch = best[i].voice; 21062306a36Sopenharmony_ci return vp; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* not found */ 21562306a36Sopenharmony_ci return NULL; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_cistatic int 22162306a36Sopenharmony_cistart_voice(struct snd_emux_voice *vp) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci unsigned int temp; 22462306a36Sopenharmony_ci int ch; 22562306a36Sopenharmony_ci int addr; 22662306a36Sopenharmony_ci struct snd_midi_channel *chan; 22762306a36Sopenharmony_ci struct snd_emu8000 *hw; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci hw = vp->hw; 23062306a36Sopenharmony_ci ch = vp->ch; 23162306a36Sopenharmony_ci chan = vp->chan; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* channel to be silent and idle */ 23462306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080); 23562306a36Sopenharmony_ci EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF); 23662306a36Sopenharmony_ci EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF); 23762306a36Sopenharmony_ci EMU8000_PTRX_WRITE(hw, ch, 0); 23862306a36Sopenharmony_ci EMU8000_CPF_WRITE(hw, ch, 0); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* set pitch offset */ 24162306a36Sopenharmony_ci set_pitch(hw, vp); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* set envelope parameters */ 24462306a36Sopenharmony_ci EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay); 24562306a36Sopenharmony_ci EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld); 24662306a36Sopenharmony_ci EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus); 24762306a36Sopenharmony_ci EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay); 24862306a36Sopenharmony_ci EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld); 24962306a36Sopenharmony_ci /* decay/sustain parameter for volume envelope is used 25062306a36Sopenharmony_ci for triggerg the voice */ 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* cutoff and volume */ 25362306a36Sopenharmony_ci set_volume(hw, vp); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* modulation envelope heights */ 25662306a36Sopenharmony_ci EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* lfo1/2 delay */ 25962306a36Sopenharmony_ci EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay); 26062306a36Sopenharmony_ci EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* lfo1 pitch & cutoff shift */ 26362306a36Sopenharmony_ci set_fmmod(hw, vp); 26462306a36Sopenharmony_ci /* lfo1 volume & freq */ 26562306a36Sopenharmony_ci set_tremfreq(hw, vp); 26662306a36Sopenharmony_ci /* lfo2 pitch & freq */ 26762306a36Sopenharmony_ci set_fm2frq2(hw, vp); 26862306a36Sopenharmony_ci /* pan & loop start */ 26962306a36Sopenharmony_ci set_pan(hw, vp); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* chorus & loop end (chorus 8bit, MSB) */ 27262306a36Sopenharmony_ci addr = vp->reg.loopend - 1; 27362306a36Sopenharmony_ci temp = vp->reg.parm.chorus; 27462306a36Sopenharmony_ci temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; 27562306a36Sopenharmony_ci LIMITMAX(temp, 255); 27662306a36Sopenharmony_ci temp = (temp <<24) | (unsigned int)addr; 27762306a36Sopenharmony_ci EMU8000_CSL_WRITE(hw, ch, temp); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Q & current address (Q 4bit value, MSB) */ 28062306a36Sopenharmony_ci addr = vp->reg.start - 1; 28162306a36Sopenharmony_ci temp = vp->reg.parm.filterQ; 28262306a36Sopenharmony_ci temp = (temp<<28) | (unsigned int)addr; 28362306a36Sopenharmony_ci EMU8000_CCCA_WRITE(hw, ch, temp); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* clear unknown registers */ 28662306a36Sopenharmony_ci EMU8000_00A0_WRITE(hw, ch, 0); 28762306a36Sopenharmony_ci EMU8000_0080_WRITE(hw, ch, 0); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* reset volume */ 29062306a36Sopenharmony_ci temp = vp->vtarget << 16; 29162306a36Sopenharmony_ci EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget); 29262306a36Sopenharmony_ci EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* 29862306a36Sopenharmony_ci * Start envelope 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic void 30162306a36Sopenharmony_citrigger_voice(struct snd_emux_voice *vp) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci int ch = vp->ch; 30462306a36Sopenharmony_ci unsigned int temp; 30562306a36Sopenharmony_ci struct snd_emu8000 *hw; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci hw = vp->hw; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* set reverb and pitch target */ 31062306a36Sopenharmony_ci temp = vp->reg.parm.reverb; 31162306a36Sopenharmony_ci temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; 31262306a36Sopenharmony_ci LIMITMAX(temp, 255); 31362306a36Sopenharmony_ci temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux; 31462306a36Sopenharmony_ci EMU8000_PTRX_WRITE(hw, ch, temp); 31562306a36Sopenharmony_ci EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16); 31662306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* 32062306a36Sopenharmony_ci * reset voice parameters 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_cistatic void 32362306a36Sopenharmony_cireset_voice(struct snd_emux *emu, int ch) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct snd_emu8000 *hw; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci hw = emu->hw; 32862306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); 32962306a36Sopenharmony_ci snd_emu8000_tweak_voice(hw, ch); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* 33362306a36Sopenharmony_ci * Set the pitch of a possibly playing note. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_cistatic void 33662306a36Sopenharmony_ciset_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci EMU8000_IP_WRITE(hw, vp->ch, vp->apitch); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* 34262306a36Sopenharmony_ci * Set the volume of a possibly already playing note 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_cistatic void 34562306a36Sopenharmony_ciset_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci int ifatn; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ifatn = (unsigned char)vp->acutoff; 35062306a36Sopenharmony_ci ifatn = (ifatn << 8); 35162306a36Sopenharmony_ci ifatn |= (unsigned char)vp->avol; 35262306a36Sopenharmony_ci EMU8000_IFATN_WRITE(hw, vp->ch, ifatn); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/* 35662306a36Sopenharmony_ci * Set pan and loop start address. 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_cistatic void 35962306a36Sopenharmony_ciset_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci unsigned int temp; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1); 36462306a36Sopenharmony_ci EMU8000_PSST_WRITE(hw, vp->ch, temp); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci#define MOD_SENSE 18 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic void 37062306a36Sopenharmony_ciset_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci unsigned short fmmod; 37362306a36Sopenharmony_ci short pitch; 37462306a36Sopenharmony_ci unsigned char cutoff; 37562306a36Sopenharmony_ci int modulation; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci pitch = (char)(vp->reg.parm.fmmod>>8); 37862306a36Sopenharmony_ci cutoff = (vp->reg.parm.fmmod & 0xff); 37962306a36Sopenharmony_ci modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; 38062306a36Sopenharmony_ci pitch += (MOD_SENSE * modulation) / 1200; 38162306a36Sopenharmony_ci LIMITVALUE(pitch, -128, 127); 38262306a36Sopenharmony_ci fmmod = ((unsigned char)pitch<<8) | cutoff; 38362306a36Sopenharmony_ci EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* set tremolo (lfo1) volume & frequency */ 38762306a36Sopenharmony_cistatic void 38862306a36Sopenharmony_ciset_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci/* set lfo2 pitch & frequency */ 39462306a36Sopenharmony_cistatic void 39562306a36Sopenharmony_ciset_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci unsigned short fm2frq2; 39862306a36Sopenharmony_ci short pitch; 39962306a36Sopenharmony_ci unsigned char freq; 40062306a36Sopenharmony_ci int modulation; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci pitch = (char)(vp->reg.parm.fm2frq2>>8); 40362306a36Sopenharmony_ci freq = vp->reg.parm.fm2frq2 & 0xff; 40462306a36Sopenharmony_ci modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; 40562306a36Sopenharmony_ci pitch += (MOD_SENSE * modulation) / 1200; 40662306a36Sopenharmony_ci LIMITVALUE(pitch, -128, 127); 40762306a36Sopenharmony_ci fm2frq2 = ((unsigned char)pitch<<8) | freq; 40862306a36Sopenharmony_ci EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/* set filterQ */ 41262306a36Sopenharmony_cistatic void 41362306a36Sopenharmony_ciset_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci unsigned int addr; 41662306a36Sopenharmony_ci addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff; 41762306a36Sopenharmony_ci addr |= (vp->reg.parm.filterQ << 28); 41862306a36Sopenharmony_ci EMU8000_CCCA_WRITE(hw, vp->ch, addr); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* 42262306a36Sopenharmony_ci * set the envelope & LFO parameters to the default values 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_cistatic void 42562306a36Sopenharmony_cisnd_emu8000_tweak_voice(struct snd_emu8000 *emu, int i) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci /* set all mod/vol envelope shape to minimum */ 42862306a36Sopenharmony_ci EMU8000_ENVVOL_WRITE(emu, i, 0x8000); 42962306a36Sopenharmony_ci EMU8000_ENVVAL_WRITE(emu, i, 0x8000); 43062306a36Sopenharmony_ci EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F); 43162306a36Sopenharmony_ci EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F); 43262306a36Sopenharmony_ci EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F); 43362306a36Sopenharmony_ci EMU8000_PEFE_WRITE(emu, i, 0); /* mod envelope height to zero */ 43462306a36Sopenharmony_ci EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */ 43562306a36Sopenharmony_ci EMU8000_LFO2VAL_WRITE(emu, i, 0x8000); 43662306a36Sopenharmony_ci EMU8000_IP_WRITE(emu, i, 0xE000); /* no pitch shift */ 43762306a36Sopenharmony_ci EMU8000_IFATN_WRITE(emu, i, 0xFF00); /* volume to minimum */ 43862306a36Sopenharmony_ci EMU8000_FMMOD_WRITE(emu, i, 0); 43962306a36Sopenharmony_ci EMU8000_TREMFRQ_WRITE(emu, i, 0); 44062306a36Sopenharmony_ci EMU8000_FM2FRQ2_WRITE(emu, i, 0); 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci/* 44462306a36Sopenharmony_ci * sysex callback 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_cistatic void 44762306a36Sopenharmony_cisysex(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_channel_set *chset) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct snd_emu8000 *hw; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci hw = emu->hw; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci switch (parsed) { 45462306a36Sopenharmony_ci case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE: 45562306a36Sopenharmony_ci hw->chorus_mode = chset->gs_chorus_mode; 45662306a36Sopenharmony_ci snd_emu8000_update_chorus_mode(hw); 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci case SNDRV_MIDI_SYSEX_GS_REVERB_MODE: 46062306a36Sopenharmony_ci hw->reverb_mode = chset->gs_reverb_mode; 46162306a36Sopenharmony_ci snd_emu8000_update_reverb_mode(hw); 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS) 46862306a36Sopenharmony_ci/* 46962306a36Sopenharmony_ci * OSS ioctl callback 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_cistatic int 47262306a36Sopenharmony_cioss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct snd_emu8000 *hw; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci hw = emu->hw; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci switch (cmd) { 47962306a36Sopenharmony_ci case _EMUX_OSS_REVERB_MODE: 48062306a36Sopenharmony_ci hw->reverb_mode = p1; 48162306a36Sopenharmony_ci snd_emu8000_update_reverb_mode(hw); 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci case _EMUX_OSS_CHORUS_MODE: 48562306a36Sopenharmony_ci hw->chorus_mode = p1; 48662306a36Sopenharmony_ci snd_emu8000_update_chorus_mode(hw); 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci case _EMUX_OSS_INITIALIZE_CHIP: 49062306a36Sopenharmony_ci /* snd_emu8000_init(hw); */ /*ignored*/ 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci case _EMUX_OSS_EQUALIZER: 49462306a36Sopenharmony_ci hw->bass_level = p1; 49562306a36Sopenharmony_ci hw->treble_level = p2; 49662306a36Sopenharmony_ci snd_emu8000_update_equalizer(hw); 49762306a36Sopenharmony_ci break; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci#endif 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci/* 50562306a36Sopenharmony_ci * additional patch keys 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci#define SNDRV_EMU8000_LOAD_CHORUS_FX 0x10 /* optarg=mode */ 50962306a36Sopenharmony_ci#define SNDRV_EMU8000_LOAD_REVERB_FX 0x11 /* optarg=mode */ 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/* 51362306a36Sopenharmony_ci * callback routine 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int 51762306a36Sopenharmony_ciload_fx(struct snd_emux *emu, int type, int mode, const void __user *buf, long len) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct snd_emu8000 *hw; 52062306a36Sopenharmony_ci hw = emu->hw; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* skip header */ 52362306a36Sopenharmony_ci buf += 16; 52462306a36Sopenharmony_ci len -= 16; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci switch (type) { 52762306a36Sopenharmony_ci case SNDRV_EMU8000_LOAD_CHORUS_FX: 52862306a36Sopenharmony_ci return snd_emu8000_load_chorus_fx(hw, mode, buf, len); 52962306a36Sopenharmony_ci case SNDRV_EMU8000_LOAD_REVERB_FX: 53062306a36Sopenharmony_ci return snd_emu8000_load_reverb_fx(hw, mode, buf, len); 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci return -EINVAL; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 535