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