162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * synth callback routines for Emu10k1 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/export.h> 962306a36Sopenharmony_ci#include "emu10k1_synth_local.h" 1062306a36Sopenharmony_ci#include <sound/asoundef.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* voice status */ 1362306a36Sopenharmony_cienum { 1462306a36Sopenharmony_ci V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END 1562306a36Sopenharmony_ci}; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* Keeps track of what we are finding */ 1862306a36Sopenharmony_cistruct best_voice { 1962306a36Sopenharmony_ci unsigned int time; 2062306a36Sopenharmony_ci int voice; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * prototypes 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_cistatic void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw, 2762306a36Sopenharmony_ci struct best_voice *best, int active_only); 2862306a36Sopenharmony_cistatic struct snd_emux_voice *get_voice(struct snd_emux *emux, 2962306a36Sopenharmony_ci struct snd_emux_port *port); 3062306a36Sopenharmony_cistatic int start_voice(struct snd_emux_voice *vp); 3162306a36Sopenharmony_cistatic void trigger_voice(struct snd_emux_voice *vp); 3262306a36Sopenharmony_cistatic void release_voice(struct snd_emux_voice *vp); 3362306a36Sopenharmony_cistatic void update_voice(struct snd_emux_voice *vp, int update); 3462306a36Sopenharmony_cistatic void terminate_voice(struct snd_emux_voice *vp); 3562306a36Sopenharmony_cistatic void free_voice(struct snd_emux_voice *vp); 3662306a36Sopenharmony_cistatic u32 make_fmmod(struct snd_emux_voice *vp); 3762306a36Sopenharmony_cistatic u32 make_fm2frq2(struct snd_emux_voice *vp); 3862306a36Sopenharmony_cistatic int get_pitch_shift(struct snd_emux *emu); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * Ensure a value is between two points 4262306a36Sopenharmony_ci * macro evaluates its args more than once, so changed to upper-case. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) 4562306a36Sopenharmony_ci#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* 4962306a36Sopenharmony_ci * set up operators 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_cistatic const struct snd_emux_operators emu10k1_ops = { 5262306a36Sopenharmony_ci .owner = THIS_MODULE, 5362306a36Sopenharmony_ci .get_voice = get_voice, 5462306a36Sopenharmony_ci .prepare = start_voice, 5562306a36Sopenharmony_ci .trigger = trigger_voice, 5662306a36Sopenharmony_ci .release = release_voice, 5762306a36Sopenharmony_ci .update = update_voice, 5862306a36Sopenharmony_ci .terminate = terminate_voice, 5962306a36Sopenharmony_ci .free_voice = free_voice, 6062306a36Sopenharmony_ci .sample_new = snd_emu10k1_sample_new, 6162306a36Sopenharmony_ci .sample_free = snd_emu10k1_sample_free, 6262306a36Sopenharmony_ci .get_pitch_shift = get_pitch_shift, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_civoid 6662306a36Sopenharmony_cisnd_emu10k1_ops_setup(struct snd_emux *emux) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci emux->ops = emu10k1_ops; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * get more voice for pcm 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * terminate most inactive voice and give it as a pcm voice. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * voice_lock is already held. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ciint 8062306a36Sopenharmony_cisnd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct snd_emux *emu; 8362306a36Sopenharmony_ci struct snd_emux_voice *vp; 8462306a36Sopenharmony_ci struct best_voice best[V_END]; 8562306a36Sopenharmony_ci int i; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci emu = hw->synth; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci lookup_voices(emu, hw, best, 1); /* no OFF voices */ 9062306a36Sopenharmony_ci for (i = 0; i < V_END; i++) { 9162306a36Sopenharmony_ci if (best[i].voice >= 0) { 9262306a36Sopenharmony_ci int ch; 9362306a36Sopenharmony_ci vp = &emu->voices[best[i].voice]; 9462306a36Sopenharmony_ci ch = vp->ch; 9562306a36Sopenharmony_ci if (ch < 0) { 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci dev_warn(emu->card->dev, 9862306a36Sopenharmony_ci "synth_get_voice: ch < 0 (%d) ??", i); 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci continue; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci vp->emu->num_voices--; 10362306a36Sopenharmony_ci vp->ch = -1; 10462306a36Sopenharmony_ci vp->state = SNDRV_EMUX_ST_OFF; 10562306a36Sopenharmony_ci return ch; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* not found */ 11062306a36Sopenharmony_ci return -ENOMEM; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * turn off the voice (not terminated) 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_cistatic void 11862306a36Sopenharmony_cirelease_voice(struct snd_emux_voice *vp) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct snd_emu10k1 *hw; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci hw = vp->hw; 12362306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(hw, vp->ch, 12462306a36Sopenharmony_ci DCYSUSM, (unsigned char)vp->reg.parm.modrelease | DCYSUSM_PHASE1_MASK, 12562306a36Sopenharmony_ci DCYSUSV, (unsigned char)vp->reg.parm.volrelease | DCYSUSV_PHASE1_MASK | DCYSUSV_CHANNELENABLE_MASK, 12662306a36Sopenharmony_ci REGLIST_END); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci * terminate the voice 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic void 13462306a36Sopenharmony_citerminate_voice(struct snd_emux_voice *vp) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct snd_emu10k1 *hw; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (snd_BUG_ON(!vp)) 13962306a36Sopenharmony_ci return; 14062306a36Sopenharmony_ci hw = vp->hw; 14162306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(hw, vp->ch, 14262306a36Sopenharmony_ci DCYSUSV, 0, 14362306a36Sopenharmony_ci VTFT, VTFT_FILTERTARGET_MASK, 14462306a36Sopenharmony_ci CVCF, CVCF_CURRENTFILTER_MASK, 14562306a36Sopenharmony_ci PTRX, 0, 14662306a36Sopenharmony_ci CPF, 0, 14762306a36Sopenharmony_ci REGLIST_END); 14862306a36Sopenharmony_ci if (vp->block) { 14962306a36Sopenharmony_ci struct snd_emu10k1_memblk *emem; 15062306a36Sopenharmony_ci emem = (struct snd_emu10k1_memblk *)vp->block; 15162306a36Sopenharmony_ci if (emem->map_locked > 0) 15262306a36Sopenharmony_ci emem->map_locked--; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* 15762306a36Sopenharmony_ci * release the voice to system 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cistatic void 16062306a36Sopenharmony_cifree_voice(struct snd_emux_voice *vp) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct snd_emu10k1 *hw; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci hw = vp->hw; 16562306a36Sopenharmony_ci /* FIXME: emu10k1_synth is broken. */ 16662306a36Sopenharmony_ci /* This can get called with hw == 0 */ 16762306a36Sopenharmony_ci /* Problem apparent on plug, unplug then plug */ 16862306a36Sopenharmony_ci /* on the Audigy 2 ZS Notebook. */ 16962306a36Sopenharmony_ci if (hw && (vp->ch >= 0)) { 17062306a36Sopenharmony_ci snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]); 17162306a36Sopenharmony_ci vp->emu->num_voices--; 17262306a36Sopenharmony_ci vp->ch = -1; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* 17862306a36Sopenharmony_ci * update registers 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_cistatic void 18162306a36Sopenharmony_ciupdate_voice(struct snd_emux_voice *vp, int update) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct snd_emu10k1 *hw; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci hw = vp->hw; 18662306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_VOLUME) 18762306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol); 18862306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_PITCH) 18962306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); 19062306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_PAN) { 19162306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan); 19262306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_FMMOD) 19562306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, make_fmmod(vp)); 19662306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_TREMFREQ) 19762306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); 19862306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) 19962306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, make_fm2frq2(vp)); 20062306a36Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_Q) 20162306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, CCCA_RESONANCE, vp->ch, vp->reg.parm.filterQ); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* 20662306a36Sopenharmony_ci * look up voice table - get the best voice in order of preference 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci/* spinlock held! */ 20962306a36Sopenharmony_cistatic void 21062306a36Sopenharmony_cilookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, 21162306a36Sopenharmony_ci struct best_voice *best, int active_only) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct snd_emux_voice *vp; 21462306a36Sopenharmony_ci struct best_voice *bp; 21562306a36Sopenharmony_ci int i; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci for (i = 0; i < V_END; i++) { 21862306a36Sopenharmony_ci best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */ 21962306a36Sopenharmony_ci best[i].voice = -1; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * Go through them all and get a best one to use. 22462306a36Sopenharmony_ci * NOTE: could also look at volume and pick the quietest one. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci for (i = 0; i < emu->max_voices; i++) { 22762306a36Sopenharmony_ci int state, val; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci vp = &emu->voices[i]; 23062306a36Sopenharmony_ci state = vp->state; 23162306a36Sopenharmony_ci if (state == SNDRV_EMUX_ST_OFF) { 23262306a36Sopenharmony_ci if (vp->ch < 0) { 23362306a36Sopenharmony_ci if (active_only) 23462306a36Sopenharmony_ci continue; 23562306a36Sopenharmony_ci bp = best + V_FREE; 23662306a36Sopenharmony_ci } else 23762306a36Sopenharmony_ci bp = best + V_OFF; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci else if (state == SNDRV_EMUX_ST_RELEASED || 24062306a36Sopenharmony_ci state == SNDRV_EMUX_ST_PENDING) { 24162306a36Sopenharmony_ci bp = best + V_RELEASED; 24262306a36Sopenharmony_ci#if 1 24362306a36Sopenharmony_ci val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch); 24462306a36Sopenharmony_ci if (! val) 24562306a36Sopenharmony_ci bp = best + V_OFF; 24662306a36Sopenharmony_ci#endif 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci else if (state == SNDRV_EMUX_ST_STANDBY) 24962306a36Sopenharmony_ci continue; 25062306a36Sopenharmony_ci else if (state & SNDRV_EMUX_ST_ON) 25162306a36Sopenharmony_ci bp = best + V_PLAYING; 25262306a36Sopenharmony_ci else 25362306a36Sopenharmony_ci continue; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* check if sample is finished playing (non-looping only) */ 25662306a36Sopenharmony_ci if (bp != best + V_OFF && bp != best + V_FREE && 25762306a36Sopenharmony_ci (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { 25862306a36Sopenharmony_ci val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch) - 64; 25962306a36Sopenharmony_ci if (val >= vp->reg.loopstart) 26062306a36Sopenharmony_ci bp = best + V_OFF; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (vp->time < bp->time) { 26462306a36Sopenharmony_ci bp->time = vp->time; 26562306a36Sopenharmony_ci bp->voice = i; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * get an empty voice 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * emu->voice_lock is already held. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_cistatic struct snd_emux_voice * 27662306a36Sopenharmony_ciget_voice(struct snd_emux *emu, struct snd_emux_port *port) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct snd_emu10k1 *hw; 27962306a36Sopenharmony_ci struct snd_emux_voice *vp; 28062306a36Sopenharmony_ci struct best_voice best[V_END]; 28162306a36Sopenharmony_ci int i; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci hw = emu->hw; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci lookup_voices(emu, hw, best, 0); 28662306a36Sopenharmony_ci for (i = 0; i < V_END; i++) { 28762306a36Sopenharmony_ci if (best[i].voice >= 0) { 28862306a36Sopenharmony_ci vp = &emu->voices[best[i].voice]; 28962306a36Sopenharmony_ci if (vp->ch < 0) { 29062306a36Sopenharmony_ci /* allocate a voice */ 29162306a36Sopenharmony_ci struct snd_emu10k1_voice *hwvoice; 29262306a36Sopenharmony_ci if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, 1, NULL, &hwvoice) < 0) 29362306a36Sopenharmony_ci continue; 29462306a36Sopenharmony_ci vp->ch = hwvoice->number; 29562306a36Sopenharmony_ci emu->num_voices++; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci return vp; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* not found */ 30262306a36Sopenharmony_ci return NULL; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/* 30662306a36Sopenharmony_ci * prepare envelopes and LFOs 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_cistatic int 30962306a36Sopenharmony_cistart_voice(struct snd_emux_voice *vp) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci unsigned int temp; 31262306a36Sopenharmony_ci int ch; 31362306a36Sopenharmony_ci u32 psst, dsl, map, ccca, vtarget; 31462306a36Sopenharmony_ci unsigned int addr, mapped_offset; 31562306a36Sopenharmony_ci struct snd_midi_channel *chan; 31662306a36Sopenharmony_ci struct snd_emu10k1 *hw; 31762306a36Sopenharmony_ci struct snd_emu10k1_memblk *emem; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci hw = vp->hw; 32062306a36Sopenharmony_ci ch = vp->ch; 32162306a36Sopenharmony_ci if (snd_BUG_ON(ch < 0)) 32262306a36Sopenharmony_ci return -EINVAL; 32362306a36Sopenharmony_ci chan = vp->chan; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci emem = (struct snd_emu10k1_memblk *)vp->block; 32662306a36Sopenharmony_ci if (emem == NULL) 32762306a36Sopenharmony_ci return -EINVAL; 32862306a36Sopenharmony_ci emem->map_locked++; 32962306a36Sopenharmony_ci if (snd_emu10k1_memblk_map(hw, emem) < 0) { 33062306a36Sopenharmony_ci /* dev_err(hw->card->devK, "emu: cannot map!\n"); */ 33162306a36Sopenharmony_ci return -ENOMEM; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; 33462306a36Sopenharmony_ci vp->reg.start += mapped_offset; 33562306a36Sopenharmony_ci vp->reg.end += mapped_offset; 33662306a36Sopenharmony_ci vp->reg.loopstart += mapped_offset; 33762306a36Sopenharmony_ci vp->reg.loopend += mapped_offset; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* set channel routing */ 34062306a36Sopenharmony_ci /* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */ 34162306a36Sopenharmony_ci if (hw->audigy) { 34262306a36Sopenharmony_ci temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | 34362306a36Sopenharmony_ci (FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24); 34462306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp); 34562306a36Sopenharmony_ci } else { 34662306a36Sopenharmony_ci temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | 34762306a36Sopenharmony_ci (FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28); 34862306a36Sopenharmony_ci snd_emu10k1_ptr_write(hw, FXRT, ch, temp); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci temp = vp->reg.parm.reverb; 35262306a36Sopenharmony_ci temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; 35362306a36Sopenharmony_ci LIMITMAX(temp, 255); 35462306a36Sopenharmony_ci addr = vp->reg.loopstart; 35562306a36Sopenharmony_ci psst = (temp << 24) | addr; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci addr = vp->reg.loopend; 35862306a36Sopenharmony_ci temp = vp->reg.parm.chorus; 35962306a36Sopenharmony_ci temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; 36062306a36Sopenharmony_ci LIMITMAX(temp, 255); 36162306a36Sopenharmony_ci dsl = (temp << 24) | addr; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci addr = vp->reg.start + 64; 36662306a36Sopenharmony_ci temp = vp->reg.parm.filterQ; 36762306a36Sopenharmony_ci ccca = (temp << 28) | addr; 36862306a36Sopenharmony_ci if (vp->apitch < 0xe400) 36962306a36Sopenharmony_ci ccca |= CCCA_INTERPROM_0; 37062306a36Sopenharmony_ci else { 37162306a36Sopenharmony_ci unsigned int shift = (vp->apitch - 0xe000) >> 10; 37262306a36Sopenharmony_ci ccca |= shift << 25; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) 37562306a36Sopenharmony_ci ccca |= CCCA_8BITSELECT; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci vtarget = (unsigned int)vp->vtarget << 16; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(hw, ch, 38062306a36Sopenharmony_ci /* channel to be silent and idle */ 38162306a36Sopenharmony_ci DCYSUSV, 0, 38262306a36Sopenharmony_ci VTFT, VTFT_FILTERTARGET_MASK, 38362306a36Sopenharmony_ci CVCF, CVCF_CURRENTFILTER_MASK, 38462306a36Sopenharmony_ci PTRX, 0, 38562306a36Sopenharmony_ci CPF, 0, 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* set pitch offset */ 38862306a36Sopenharmony_ci IP, vp->apitch, 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* set envelope parameters */ 39162306a36Sopenharmony_ci ENVVAL, vp->reg.parm.moddelay, 39262306a36Sopenharmony_ci ATKHLDM, vp->reg.parm.modatkhld, 39362306a36Sopenharmony_ci DCYSUSM, vp->reg.parm.moddcysus, 39462306a36Sopenharmony_ci ENVVOL, vp->reg.parm.voldelay, 39562306a36Sopenharmony_ci ATKHLDV, vp->reg.parm.volatkhld, 39662306a36Sopenharmony_ci /* decay/sustain parameter for volume envelope is used 39762306a36Sopenharmony_ci for triggerg the voice */ 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* cutoff and volume */ 40062306a36Sopenharmony_ci IFATN, (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol, 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* modulation envelope heights */ 40362306a36Sopenharmony_ci PEFE, vp->reg.parm.pefe, 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* lfo1/2 delay */ 40662306a36Sopenharmony_ci LFOVAL1, vp->reg.parm.lfo1delay, 40762306a36Sopenharmony_ci LFOVAL2, vp->reg.parm.lfo2delay, 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* lfo1 pitch & cutoff shift */ 41062306a36Sopenharmony_ci FMMOD, make_fmmod(vp), 41162306a36Sopenharmony_ci /* lfo1 volume & freq */ 41262306a36Sopenharmony_ci TREMFRQ, vp->reg.parm.tremfrq, 41362306a36Sopenharmony_ci /* lfo2 pitch & freq */ 41462306a36Sopenharmony_ci FM2FRQ2, make_fm2frq2(vp), 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* reverb and loop start (reverb 8bit, MSB) */ 41762306a36Sopenharmony_ci PSST, psst, 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* chorus & loop end (chorus 8bit, MSB) */ 42062306a36Sopenharmony_ci DSL, dsl, 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* clear filter delay memory */ 42362306a36Sopenharmony_ci Z1, 0, 42462306a36Sopenharmony_ci Z2, 0, 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* invalidate maps */ 42762306a36Sopenharmony_ci MAPA, map, 42862306a36Sopenharmony_ci MAPB, map, 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Q & current address (Q 4bit value, MSB) */ 43162306a36Sopenharmony_ci CCCA, ccca, 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* cache */ 43462306a36Sopenharmony_ci CCR, REG_VAL_PUT(CCR_CACHEINVALIDSIZE, 64), 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* reset volume */ 43762306a36Sopenharmony_ci VTFT, vtarget | vp->ftarget, 43862306a36Sopenharmony_ci CVCF, vtarget | CVCF_CURRENTFILTER_MASK, 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci REGLIST_END); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci hw->voices[ch].dirty = 1; 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* 44762306a36Sopenharmony_ci * Start envelope 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_cistatic void 45062306a36Sopenharmony_citrigger_voice(struct snd_emux_voice *vp) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci unsigned int ptarget; 45362306a36Sopenharmony_ci struct snd_emu10k1 *hw; 45462306a36Sopenharmony_ci struct snd_emu10k1_memblk *emem; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci hw = vp->hw; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci emem = (struct snd_emu10k1_memblk *)vp->block; 45962306a36Sopenharmony_ci if (! emem || emem->mapped_page < 0) 46062306a36Sopenharmony_ci return; /* not mapped */ 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci#if 0 46362306a36Sopenharmony_ci ptarget = (unsigned int)vp->ptarget << 16; 46462306a36Sopenharmony_ci#else 46562306a36Sopenharmony_ci ptarget = IP_TO_CP(vp->apitch); 46662306a36Sopenharmony_ci#endif 46762306a36Sopenharmony_ci snd_emu10k1_ptr_write_multiple(hw, vp->ch, 46862306a36Sopenharmony_ci /* set pitch target and pan (volume) */ 46962306a36Sopenharmony_ci PTRX, ptarget | (vp->apan << 8) | vp->aaux, 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* current pitch and fractional address */ 47262306a36Sopenharmony_ci CPF, ptarget, 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* enable envelope engine */ 47562306a36Sopenharmony_ci DCYSUSV, vp->reg.parm.voldcysus | DCYSUSV_CHANNELENABLE_MASK, 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci REGLIST_END); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci#define MOD_SENSE 18 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci/* calculate lfo1 modulation height and cutoff register */ 48362306a36Sopenharmony_cistatic u32 48462306a36Sopenharmony_cimake_fmmod(struct snd_emux_voice *vp) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci short pitch; 48762306a36Sopenharmony_ci unsigned char cutoff; 48862306a36Sopenharmony_ci int modulation; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci pitch = (char)(vp->reg.parm.fmmod>>8); 49162306a36Sopenharmony_ci cutoff = (vp->reg.parm.fmmod & 0xff); 49262306a36Sopenharmony_ci modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; 49362306a36Sopenharmony_ci pitch += (MOD_SENSE * modulation) / 1200; 49462306a36Sopenharmony_ci LIMITVALUE(pitch, -128, 127); 49562306a36Sopenharmony_ci return ((unsigned char)pitch << 8) | cutoff; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* calculate set lfo2 pitch & frequency register */ 49962306a36Sopenharmony_cistatic u32 50062306a36Sopenharmony_cimake_fm2frq2(struct snd_emux_voice *vp) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci short pitch; 50362306a36Sopenharmony_ci unsigned char freq; 50462306a36Sopenharmony_ci int modulation; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci pitch = (char)(vp->reg.parm.fm2frq2>>8); 50762306a36Sopenharmony_ci freq = vp->reg.parm.fm2frq2 & 0xff; 50862306a36Sopenharmony_ci modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; 50962306a36Sopenharmony_ci pitch += (MOD_SENSE * modulation) / 1200; 51062306a36Sopenharmony_ci LIMITVALUE(pitch, -128, 127); 51162306a36Sopenharmony_ci return ((unsigned char)pitch << 8) | freq; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int get_pitch_shift(struct snd_emux *emu) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct snd_emu10k1 *hw = emu->hw; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return (hw->card_capabilities->emu_model && 51962306a36Sopenharmony_ci hw->emu1010.word_clock == 44100) ? 0 : -501; 52062306a36Sopenharmony_ci} 521