11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  synth callback routines for Emu10k1
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
8d81a6d71SPaul Gortmaker #include <linux/export.h>
91da177e4SLinus Torvalds #include "emu10k1_synth_local.h"
101da177e4SLinus Torvalds #include <sound/asoundef.h>
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds /* voice status */
131da177e4SLinus Torvalds enum {
141da177e4SLinus Torvalds 	V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END
151da177e4SLinus Torvalds };
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /* Keeps track of what we are finding */
18eb4698f3STakashi Iwai struct best_voice {
191da177e4SLinus Torvalds 	unsigned int time;
201da177e4SLinus Torvalds 	int voice;
21eb4698f3STakashi Iwai };
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds /*
241da177e4SLinus Torvalds  * prototypes
251da177e4SLinus Torvalds  */
26c94fa4c9SJames Courtier-Dutton static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,
27eb4698f3STakashi Iwai 			  struct best_voice *best, int active_only);
28c94fa4c9SJames Courtier-Dutton static struct snd_emux_voice *get_voice(struct snd_emux *emux,
29eb4698f3STakashi Iwai 					struct snd_emux_port *port);
30eb4698f3STakashi Iwai static int start_voice(struct snd_emux_voice *vp);
31eb4698f3STakashi Iwai static void trigger_voice(struct snd_emux_voice *vp);
32eb4698f3STakashi Iwai static void release_voice(struct snd_emux_voice *vp);
33eb4698f3STakashi Iwai static void update_voice(struct snd_emux_voice *vp, int update);
34eb4698f3STakashi Iwai static void terminate_voice(struct snd_emux_voice *vp);
35eb4698f3STakashi Iwai static void free_voice(struct snd_emux_voice *vp);
3646055699SOswald Buddenhagen static u32 make_fmmod(struct snd_emux_voice *vp);
3746055699SOswald Buddenhagen static u32 make_fm2frq2(struct snd_emux_voice *vp);
38e68235c8SOswald Buddenhagen static int get_pitch_shift(struct snd_emux *emu);
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds /*
411da177e4SLinus Torvalds  * Ensure a value is between two points
421da177e4SLinus Torvalds  * macro evaluates its args more than once, so changed to upper-case.
431da177e4SLinus Torvalds  */
441da177e4SLinus Torvalds #define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
451da177e4SLinus Torvalds #define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds /*
491da177e4SLinus Torvalds  * set up operators
501da177e4SLinus Torvalds  */
513bb9eca9SBhumika Goyal static const struct snd_emux_operators emu10k1_ops = {
521da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
531da177e4SLinus Torvalds 	.get_voice =	get_voice,
541da177e4SLinus Torvalds 	.prepare =	start_voice,
551da177e4SLinus Torvalds 	.trigger =	trigger_voice,
561da177e4SLinus Torvalds 	.release =	release_voice,
571da177e4SLinus Torvalds 	.update =	update_voice,
581da177e4SLinus Torvalds 	.terminate =	terminate_voice,
591da177e4SLinus Torvalds 	.free_voice =	free_voice,
601da177e4SLinus Torvalds 	.sample_new =	snd_emu10k1_sample_new,
611da177e4SLinus Torvalds 	.sample_free =	snd_emu10k1_sample_free,
62e68235c8SOswald Buddenhagen 	.get_pitch_shift = get_pitch_shift,
631da177e4SLinus Torvalds };
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds void
snd_emu10k1_ops_setup(struct snd_emux * emux)66c94fa4c9SJames Courtier-Dutton snd_emu10k1_ops_setup(struct snd_emux *emux)
671da177e4SLinus Torvalds {
68c94fa4c9SJames Courtier-Dutton 	emux->ops = emu10k1_ops;
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds /*
731da177e4SLinus Torvalds  * get more voice for pcm
741da177e4SLinus Torvalds  *
751da177e4SLinus Torvalds  * terminate most inactive voice and give it as a pcm voice.
7695926035STakashi Iwai  *
7795926035STakashi Iwai  * voice_lock is already held.
781da177e4SLinus Torvalds  */
791da177e4SLinus Torvalds int
snd_emu10k1_synth_get_voice(struct snd_emu10k1 * hw)80eb4698f3STakashi Iwai snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
811da177e4SLinus Torvalds {
82eb4698f3STakashi Iwai 	struct snd_emux *emu;
83eb4698f3STakashi Iwai 	struct snd_emux_voice *vp;
84eb4698f3STakashi Iwai 	struct best_voice best[V_END];
851da177e4SLinus Torvalds 	int i;
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	emu = hw->synth;
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds 	lookup_voices(emu, hw, best, 1); /* no OFF voices */
901da177e4SLinus Torvalds 	for (i = 0; i < V_END; i++) {
911da177e4SLinus Torvalds 		if (best[i].voice >= 0) {
921da177e4SLinus Torvalds 			int ch;
931da177e4SLinus Torvalds 			vp = &emu->voices[best[i].voice];
9412bda107STakashi Iwai 			ch = vp->ch;
9512bda107STakashi Iwai 			if (ch < 0) {
9628a97c19STakashi Iwai 				/*
976f002b02STakashi Iwai 				dev_warn(emu->card->dev,
9828a97c19STakashi Iwai 				       "synth_get_voice: ch < 0 (%d) ??", i);
9928a97c19STakashi Iwai 				*/
1001da177e4SLinus Torvalds 				continue;
1011da177e4SLinus Torvalds 			}
1021da177e4SLinus Torvalds 			vp->emu->num_voices--;
1031da177e4SLinus Torvalds 			vp->ch = -1;
1041da177e4SLinus Torvalds 			vp->state = SNDRV_EMUX_ST_OFF;
1051da177e4SLinus Torvalds 			return ch;
1061da177e4SLinus Torvalds 		}
1071da177e4SLinus Torvalds 	}
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	/* not found */
1101da177e4SLinus Torvalds 	return -ENOMEM;
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds /*
1151da177e4SLinus Torvalds  * turn off the voice (not terminated)
1161da177e4SLinus Torvalds  */
1171da177e4SLinus Torvalds static void
release_voice(struct snd_emux_voice * vp)118eb4698f3STakashi Iwai release_voice(struct snd_emux_voice *vp)
1191da177e4SLinus Torvalds {
120eb4698f3STakashi Iwai 	struct snd_emu10k1 *hw;
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds 	hw = vp->hw;
12346055699SOswald Buddenhagen 	snd_emu10k1_ptr_write_multiple(hw, vp->ch,
12446055699SOswald Buddenhagen 		DCYSUSM, (unsigned char)vp->reg.parm.modrelease | DCYSUSM_PHASE1_MASK,
12546055699SOswald Buddenhagen 		DCYSUSV, (unsigned char)vp->reg.parm.volrelease | DCYSUSV_PHASE1_MASK | DCYSUSV_CHANNELENABLE_MASK,
12646055699SOswald Buddenhagen 		REGLIST_END);
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds /*
1311da177e4SLinus Torvalds  * terminate the voice
1321da177e4SLinus Torvalds  */
1331da177e4SLinus Torvalds static void
terminate_voice(struct snd_emux_voice * vp)134eb4698f3STakashi Iwai terminate_voice(struct snd_emux_voice *vp)
1351da177e4SLinus Torvalds {
136eb4698f3STakashi Iwai 	struct snd_emu10k1 *hw;
1371da177e4SLinus Torvalds 
138da3cec35STakashi Iwai 	if (snd_BUG_ON(!vp))
139da3cec35STakashi Iwai 		return;
1401da177e4SLinus Torvalds 	hw = vp->hw;
1415c2664ccSOswald Buddenhagen 	snd_emu10k1_ptr_write_multiple(hw, vp->ch,
1425c2664ccSOswald Buddenhagen 		DCYSUSV, 0,
1435c2664ccSOswald Buddenhagen 		VTFT, VTFT_FILTERTARGET_MASK,
1445c2664ccSOswald Buddenhagen 		CVCF, CVCF_CURRENTFILTER_MASK,
1455c2664ccSOswald Buddenhagen 		PTRX, 0,
1465c2664ccSOswald Buddenhagen 		CPF, 0,
1475c2664ccSOswald Buddenhagen 		REGLIST_END);
1481da177e4SLinus Torvalds 	if (vp->block) {
149eb4698f3STakashi Iwai 		struct snd_emu10k1_memblk *emem;
150eb4698f3STakashi Iwai 		emem = (struct snd_emu10k1_memblk *)vp->block;
1511da177e4SLinus Torvalds 		if (emem->map_locked > 0)
1521da177e4SLinus Torvalds 			emem->map_locked--;
1531da177e4SLinus Torvalds 	}
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds /*
1571da177e4SLinus Torvalds  * release the voice to system
1581da177e4SLinus Torvalds  */
1591da177e4SLinus Torvalds static void
free_voice(struct snd_emux_voice * vp)160eb4698f3STakashi Iwai free_voice(struct snd_emux_voice *vp)
1611da177e4SLinus Torvalds {
162eb4698f3STakashi Iwai 	struct snd_emu10k1 *hw;
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	hw = vp->hw;
165c94fa4c9SJames Courtier-Dutton 	/* FIXME: emu10k1_synth is broken. */
166c94fa4c9SJames Courtier-Dutton 	/* This can get called with hw == 0 */
167c94fa4c9SJames Courtier-Dutton 	/* Problem apparent on plug, unplug then plug */
168c94fa4c9SJames Courtier-Dutton 	/* on the Audigy 2 ZS Notebook. */
169c94fa4c9SJames Courtier-Dutton 	if (hw && (vp->ch >= 0)) {
1701da177e4SLinus Torvalds 		snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
1711da177e4SLinus Torvalds 		vp->emu->num_voices--;
1721da177e4SLinus Torvalds 		vp->ch = -1;
1731da177e4SLinus Torvalds 	}
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds /*
1781da177e4SLinus Torvalds  * update registers
1791da177e4SLinus Torvalds  */
1801da177e4SLinus Torvalds static void
update_voice(struct snd_emux_voice * vp,int update)181eb4698f3STakashi Iwai update_voice(struct snd_emux_voice *vp, int update)
1821da177e4SLinus Torvalds {
183eb4698f3STakashi Iwai 	struct snd_emu10k1 *hw;
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	hw = vp->hw;
1861da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_VOLUME)
1871da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);
1881da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_PITCH)
1891da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
1901da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_PAN) {
1911da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);
1921da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
1931da177e4SLinus Torvalds 	}
1941da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_FMMOD)
19546055699SOswald Buddenhagen 		snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, make_fmmod(vp));
1961da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
1971da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
1981da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
19946055699SOswald Buddenhagen 		snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, make_fm2frq2(vp));
2001da177e4SLinus Torvalds 	if (update & SNDRV_EMUX_UPDATE_Q)
20146055699SOswald Buddenhagen 		snd_emu10k1_ptr_write(hw, CCCA_RESONANCE, vp->ch, vp->reg.parm.filterQ);
2021da177e4SLinus Torvalds }
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds /*
2061da177e4SLinus Torvalds  * look up voice table - get the best voice in order of preference
2071da177e4SLinus Torvalds  */
2081da177e4SLinus Torvalds /* spinlock held! */
2091da177e4SLinus Torvalds static void
lookup_voices(struct snd_emux * emu,struct snd_emu10k1 * hw,struct best_voice * best,int active_only)210eb4698f3STakashi Iwai lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
211eb4698f3STakashi Iwai 	      struct best_voice *best, int active_only)
2121da177e4SLinus Torvalds {
213eb4698f3STakashi Iwai 	struct snd_emux_voice *vp;
214eb4698f3STakashi Iwai 	struct best_voice *bp;
2151da177e4SLinus Torvalds 	int  i;
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 	for (i = 0; i < V_END; i++) {
218395d9dd5SPeter Senna Tschudin 		best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */
2191da177e4SLinus Torvalds 		best[i].voice = -1;
2201da177e4SLinus Torvalds 	}
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds 	/*
2231da177e4SLinus Torvalds 	 * Go through them all and get a best one to use.
2241da177e4SLinus Torvalds 	 * NOTE: could also look at volume and pick the quietest one.
2251da177e4SLinus Torvalds 	 */
2261da177e4SLinus Torvalds 	for (i = 0; i < emu->max_voices; i++) {
2271da177e4SLinus Torvalds 		int state, val;
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 		vp = &emu->voices[i];
2301da177e4SLinus Torvalds 		state = vp->state;
2311da177e4SLinus Torvalds 		if (state == SNDRV_EMUX_ST_OFF) {
2321da177e4SLinus Torvalds 			if (vp->ch < 0) {
2331da177e4SLinus Torvalds 				if (active_only)
2341da177e4SLinus Torvalds 					continue;
2351da177e4SLinus Torvalds 				bp = best + V_FREE;
2361da177e4SLinus Torvalds 			} else
2371da177e4SLinus Torvalds 				bp = best + V_OFF;
2381da177e4SLinus Torvalds 		}
2391da177e4SLinus Torvalds 		else if (state == SNDRV_EMUX_ST_RELEASED ||
2401da177e4SLinus Torvalds 			 state == SNDRV_EMUX_ST_PENDING) {
2411da177e4SLinus Torvalds 			bp = best + V_RELEASED;
242fc207733STim #if 1
2431da177e4SLinus Torvalds 			val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);
2441da177e4SLinus Torvalds 			if (! val)
2451da177e4SLinus Torvalds 				bp = best + V_OFF;
2461da177e4SLinus Torvalds #endif
2471da177e4SLinus Torvalds 		}
2481da177e4SLinus Torvalds 		else if (state == SNDRV_EMUX_ST_STANDBY)
2491da177e4SLinus Torvalds 			continue;
2501da177e4SLinus Torvalds 		else if (state & SNDRV_EMUX_ST_ON)
2511da177e4SLinus Torvalds 			bp = best + V_PLAYING;
2521da177e4SLinus Torvalds 		else
2531da177e4SLinus Torvalds 			continue;
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 		/* check if sample is finished playing (non-looping only) */
2561da177e4SLinus Torvalds 		if (bp != best + V_OFF && bp != best + V_FREE &&
2571da177e4SLinus Torvalds 		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
258*0f28afedSOswald Buddenhagen 			val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
2591da177e4SLinus Torvalds 			if (val >= vp->reg.loopstart)
2601da177e4SLinus Torvalds 				bp = best + V_OFF;
2611da177e4SLinus Torvalds 		}
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 		if (vp->time < bp->time) {
2641da177e4SLinus Torvalds 			bp->time = vp->time;
2651da177e4SLinus Torvalds 			bp->voice = i;
2661da177e4SLinus Torvalds 		}
2671da177e4SLinus Torvalds 	}
2681da177e4SLinus Torvalds }
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds /*
2711da177e4SLinus Torvalds  * get an empty voice
2721da177e4SLinus Torvalds  *
2731da177e4SLinus Torvalds  * emu->voice_lock is already held.
2741da177e4SLinus Torvalds  */
275eb4698f3STakashi Iwai static struct snd_emux_voice *
get_voice(struct snd_emux * emu,struct snd_emux_port * port)276eb4698f3STakashi Iwai get_voice(struct snd_emux *emu, struct snd_emux_port *port)
2771da177e4SLinus Torvalds {
278eb4698f3STakashi Iwai 	struct snd_emu10k1 *hw;
279eb4698f3STakashi Iwai 	struct snd_emux_voice *vp;
280eb4698f3STakashi Iwai 	struct best_voice best[V_END];
2811da177e4SLinus Torvalds 	int i;
2821da177e4SLinus Torvalds 
2831da177e4SLinus Torvalds 	hw = emu->hw;
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	lookup_voices(emu, hw, best, 0);
2861da177e4SLinus Torvalds 	for (i = 0; i < V_END; i++) {
2871da177e4SLinus Torvalds 		if (best[i].voice >= 0) {
2881da177e4SLinus Torvalds 			vp = &emu->voices[best[i].voice];
2891da177e4SLinus Torvalds 			if (vp->ch < 0) {
2901da177e4SLinus Torvalds 				/* allocate a voice */
291eb4698f3STakashi Iwai 				struct snd_emu10k1_voice *hwvoice;
292a915d604SOswald Buddenhagen 				if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, 1, NULL, &hwvoice) < 0)
2931da177e4SLinus Torvalds 					continue;
2941da177e4SLinus Torvalds 				vp->ch = hwvoice->number;
2951da177e4SLinus Torvalds 				emu->num_voices++;
2961da177e4SLinus Torvalds 			}
2971da177e4SLinus Torvalds 			return vp;
2981da177e4SLinus Torvalds 		}
2991da177e4SLinus Torvalds 	}
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds 	/* not found */
3021da177e4SLinus Torvalds 	return NULL;
3031da177e4SLinus Torvalds }
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds /*
3061da177e4SLinus Torvalds  * prepare envelopes and LFOs
3071da177e4SLinus Torvalds  */
3081da177e4SLinus Torvalds static int
start_voice(struct snd_emux_voice * vp)309eb4698f3STakashi Iwai start_voice(struct snd_emux_voice *vp)
3101da177e4SLinus Torvalds {
3111da177e4SLinus Torvalds 	unsigned int temp;
3121da177e4SLinus Torvalds 	int ch;
31346055699SOswald Buddenhagen 	u32 psst, dsl, map, ccca, vtarget;
3141da177e4SLinus Torvalds 	unsigned int addr, mapped_offset;
315eb4698f3STakashi Iwai 	struct snd_midi_channel *chan;
316eb4698f3STakashi Iwai 	struct snd_emu10k1 *hw;
317eb4698f3STakashi Iwai 	struct snd_emu10k1_memblk *emem;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	hw = vp->hw;
3201da177e4SLinus Torvalds 	ch = vp->ch;
321da3cec35STakashi Iwai 	if (snd_BUG_ON(ch < 0))
322da3cec35STakashi Iwai 		return -EINVAL;
3231da177e4SLinus Torvalds 	chan = vp->chan;
3241da177e4SLinus Torvalds 
325eb4698f3STakashi Iwai 	emem = (struct snd_emu10k1_memblk *)vp->block;
3261da177e4SLinus Torvalds 	if (emem == NULL)
3271da177e4SLinus Torvalds 		return -EINVAL;
3281da177e4SLinus Torvalds 	emem->map_locked++;
3291da177e4SLinus Torvalds 	if (snd_emu10k1_memblk_map(hw, emem) < 0) {
3306f002b02STakashi Iwai 		/* dev_err(hw->card->devK, "emu: cannot map!\n"); */
3311da177e4SLinus Torvalds 		return -ENOMEM;
3321da177e4SLinus Torvalds 	}
3331da177e4SLinus Torvalds 	mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
3341da177e4SLinus Torvalds 	vp->reg.start += mapped_offset;
3351da177e4SLinus Torvalds 	vp->reg.end += mapped_offset;
3361da177e4SLinus Torvalds 	vp->reg.loopstart += mapped_offset;
3371da177e4SLinus Torvalds 	vp->reg.loopend += mapped_offset;
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 	/* set channel routing */
3401da177e4SLinus Torvalds 	/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */
3411da177e4SLinus Torvalds 	if (hw->audigy) {
3421da177e4SLinus Torvalds 		temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) |
3431da177e4SLinus Torvalds 			(FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);
3441da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);
3451da177e4SLinus Torvalds 	} else {
3461da177e4SLinus Torvalds 		temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) |
3471da177e4SLinus Torvalds 			(FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);
3481da177e4SLinus Torvalds 		snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
3491da177e4SLinus Torvalds 	}
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 	temp = vp->reg.parm.reverb;
3521da177e4SLinus Torvalds 	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
3531da177e4SLinus Torvalds 	LIMITMAX(temp, 255);
3541da177e4SLinus Torvalds 	addr = vp->reg.loopstart;
35546055699SOswald Buddenhagen 	psst = (temp << 24) | addr;
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 	addr = vp->reg.loopend;
3581da177e4SLinus Torvalds 	temp = vp->reg.parm.chorus;
3591da177e4SLinus Torvalds 	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
3601da177e4SLinus Torvalds 	LIMITMAX(temp, 255);
36146055699SOswald Buddenhagen 	dsl = (temp << 24) | addr;
36246055699SOswald Buddenhagen 
36346055699SOswald Buddenhagen 	map = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
36446055699SOswald Buddenhagen 
365*0f28afedSOswald Buddenhagen 	addr = vp->reg.start;
36646055699SOswald Buddenhagen 	temp = vp->reg.parm.filterQ;
36746055699SOswald Buddenhagen 	ccca = (temp << 28) | addr;
36846055699SOswald Buddenhagen 	if (vp->apitch < 0xe400)
36946055699SOswald Buddenhagen 		ccca |= CCCA_INTERPROM_0;
37046055699SOswald Buddenhagen 	else {
37146055699SOswald Buddenhagen 		unsigned int shift = (vp->apitch - 0xe000) >> 10;
37246055699SOswald Buddenhagen 		ccca |= shift << 25;
37346055699SOswald Buddenhagen 	}
37446055699SOswald Buddenhagen 	if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
37546055699SOswald Buddenhagen 		ccca |= CCCA_8BITSELECT;
37646055699SOswald Buddenhagen 
37746055699SOswald Buddenhagen 	vtarget = (unsigned int)vp->vtarget << 16;
37846055699SOswald Buddenhagen 
37946055699SOswald Buddenhagen 	snd_emu10k1_ptr_write_multiple(hw, ch,
38046055699SOswald Buddenhagen 		/* channel to be silent and idle */
38146055699SOswald Buddenhagen 		DCYSUSV, 0,
38246055699SOswald Buddenhagen 		VTFT, VTFT_FILTERTARGET_MASK,
38346055699SOswald Buddenhagen 		CVCF, CVCF_CURRENTFILTER_MASK,
38446055699SOswald Buddenhagen 		PTRX, 0,
38546055699SOswald Buddenhagen 		CPF, 0,
38646055699SOswald Buddenhagen 
38746055699SOswald Buddenhagen 		/* set pitch offset */
38846055699SOswald Buddenhagen 		IP, vp->apitch,
38946055699SOswald Buddenhagen 
39046055699SOswald Buddenhagen 		/* set envelope parameters */
39146055699SOswald Buddenhagen 		ENVVAL, vp->reg.parm.moddelay,
39246055699SOswald Buddenhagen 		ATKHLDM, vp->reg.parm.modatkhld,
39346055699SOswald Buddenhagen 		DCYSUSM, vp->reg.parm.moddcysus,
39446055699SOswald Buddenhagen 		ENVVOL, vp->reg.parm.voldelay,
39546055699SOswald Buddenhagen 		ATKHLDV, vp->reg.parm.volatkhld,
39646055699SOswald Buddenhagen 		/* decay/sustain parameter for volume envelope is used
39746055699SOswald Buddenhagen 		   for triggerg the voice */
39846055699SOswald Buddenhagen 
39946055699SOswald Buddenhagen 		/* cutoff and volume */
40046055699SOswald Buddenhagen 		IFATN, (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol,
40146055699SOswald Buddenhagen 
40246055699SOswald Buddenhagen 		/* modulation envelope heights */
40346055699SOswald Buddenhagen 		PEFE, vp->reg.parm.pefe,
40446055699SOswald Buddenhagen 
40546055699SOswald Buddenhagen 		/* lfo1/2 delay */
40646055699SOswald Buddenhagen 		LFOVAL1, vp->reg.parm.lfo1delay,
40746055699SOswald Buddenhagen 		LFOVAL2, vp->reg.parm.lfo2delay,
40846055699SOswald Buddenhagen 
40946055699SOswald Buddenhagen 		/* lfo1 pitch & cutoff shift */
41046055699SOswald Buddenhagen 		FMMOD, make_fmmod(vp),
41146055699SOswald Buddenhagen 		/* lfo1 volume & freq */
41246055699SOswald Buddenhagen 		TREMFRQ, vp->reg.parm.tremfrq,
41346055699SOswald Buddenhagen 		/* lfo2 pitch & freq */
41446055699SOswald Buddenhagen 		FM2FRQ2, make_fm2frq2(vp),
41546055699SOswald Buddenhagen 
41646055699SOswald Buddenhagen 		/* reverb and loop start (reverb 8bit, MSB) */
41746055699SOswald Buddenhagen 		PSST, psst,
41846055699SOswald Buddenhagen 
41946055699SOswald Buddenhagen 		/* chorus & loop end (chorus 8bit, MSB) */
42046055699SOswald Buddenhagen 		DSL, dsl,
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 		/* clear filter delay memory */
42346055699SOswald Buddenhagen 		Z1, 0,
42446055699SOswald Buddenhagen 		Z2, 0,
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 		/* invalidate maps */
42746055699SOswald Buddenhagen 		MAPA, map,
42846055699SOswald Buddenhagen 		MAPB, map,
42946055699SOswald Buddenhagen 
43046055699SOswald Buddenhagen 		/* Q & current address (Q 4bit value, MSB) */
43146055699SOswald Buddenhagen 		CCCA, ccca,
43246055699SOswald Buddenhagen 
43346055699SOswald Buddenhagen 		/* reset volume */
43446055699SOswald Buddenhagen 		VTFT, vtarget | vp->ftarget,
43546055699SOswald Buddenhagen 		CVCF, vtarget | CVCF_CURRENTFILTER_MASK,
43646055699SOswald Buddenhagen 
43746055699SOswald Buddenhagen 		REGLIST_END);
4381da177e4SLinus Torvalds 
43982a9fa6eSOswald Buddenhagen 	hw->voices[ch].dirty = 1;
4401da177e4SLinus Torvalds 	return 0;
4411da177e4SLinus Torvalds }
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds /*
4441da177e4SLinus Torvalds  * Start envelope
4451da177e4SLinus Torvalds  */
4461da177e4SLinus Torvalds static void
trigger_voice(struct snd_emux_voice * vp)447eb4698f3STakashi Iwai trigger_voice(struct snd_emux_voice *vp)
4481da177e4SLinus Torvalds {
44946055699SOswald Buddenhagen 	unsigned int ptarget;
450eb4698f3STakashi Iwai 	struct snd_emu10k1 *hw;
451eb4698f3STakashi Iwai 	struct snd_emu10k1_memblk *emem;
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds 	hw = vp->hw;
4541da177e4SLinus Torvalds 
455eb4698f3STakashi Iwai 	emem = (struct snd_emu10k1_memblk *)vp->block;
4561da177e4SLinus Torvalds 	if (! emem || emem->mapped_page < 0)
4571da177e4SLinus Torvalds 		return; /* not mapped */
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds #if 0
4601da177e4SLinus Torvalds 	ptarget = (unsigned int)vp->ptarget << 16;
4611da177e4SLinus Torvalds #else
4621da177e4SLinus Torvalds 	ptarget = IP_TO_CP(vp->apitch);
4631da177e4SLinus Torvalds #endif
46446055699SOswald Buddenhagen 	snd_emu10k1_ptr_write_multiple(hw, vp->ch,
4651da177e4SLinus Torvalds 		/* set pitch target and pan (volume) */
46646055699SOswald Buddenhagen 		PTRX, ptarget | (vp->apan << 8) | vp->aaux,
4671da177e4SLinus Torvalds 
46846055699SOswald Buddenhagen 		/* current pitch and fractional address */
46946055699SOswald Buddenhagen 		CPF, ptarget,
4701da177e4SLinus Torvalds 
47146055699SOswald Buddenhagen 		/* enable envelope engine */
47246055699SOswald Buddenhagen 		DCYSUSV, vp->reg.parm.voldcysus | DCYSUSV_CHANNELENABLE_MASK,
47346055699SOswald Buddenhagen 
47446055699SOswald Buddenhagen 		REGLIST_END);
4751da177e4SLinus Torvalds }
4761da177e4SLinus Torvalds 
4771da177e4SLinus Torvalds #define MOD_SENSE 18
4781da177e4SLinus Torvalds 
47946055699SOswald Buddenhagen /* calculate lfo1 modulation height and cutoff register */
48046055699SOswald Buddenhagen static u32
make_fmmod(struct snd_emux_voice * vp)48146055699SOswald Buddenhagen make_fmmod(struct snd_emux_voice *vp)
4821da177e4SLinus Torvalds {
4831da177e4SLinus Torvalds 	short pitch;
4841da177e4SLinus Torvalds 	unsigned char cutoff;
4851da177e4SLinus Torvalds 	int modulation;
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	pitch = (char)(vp->reg.parm.fmmod>>8);
4881da177e4SLinus Torvalds 	cutoff = (vp->reg.parm.fmmod & 0xff);
4891da177e4SLinus Torvalds 	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
4901da177e4SLinus Torvalds 	pitch += (MOD_SENSE * modulation) / 1200;
4911da177e4SLinus Torvalds 	LIMITVALUE(pitch, -128, 127);
49246055699SOswald Buddenhagen 	return ((unsigned char)pitch << 8) | cutoff;
4931da177e4SLinus Torvalds }
4941da177e4SLinus Torvalds 
49546055699SOswald Buddenhagen /* calculate set lfo2 pitch & frequency register */
49646055699SOswald Buddenhagen static u32
make_fm2frq2(struct snd_emux_voice * vp)49746055699SOswald Buddenhagen make_fm2frq2(struct snd_emux_voice *vp)
4981da177e4SLinus Torvalds {
4991da177e4SLinus Torvalds 	short pitch;
5001da177e4SLinus Torvalds 	unsigned char freq;
5011da177e4SLinus Torvalds 	int modulation;
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds 	pitch = (char)(vp->reg.parm.fm2frq2>>8);
5041da177e4SLinus Torvalds 	freq = vp->reg.parm.fm2frq2 & 0xff;
5051da177e4SLinus Torvalds 	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
5061da177e4SLinus Torvalds 	pitch += (MOD_SENSE * modulation) / 1200;
5071da177e4SLinus Torvalds 	LIMITVALUE(pitch, -128, 127);
50846055699SOswald Buddenhagen 	return ((unsigned char)pitch << 8) | freq;
5091da177e4SLinus Torvalds }
510e68235c8SOswald Buddenhagen 
get_pitch_shift(struct snd_emux * emu)511e68235c8SOswald Buddenhagen static int get_pitch_shift(struct snd_emux *emu)
512e68235c8SOswald Buddenhagen {
513e68235c8SOswald Buddenhagen 	struct snd_emu10k1 *hw = emu->hw;
514e68235c8SOswald Buddenhagen 
515e68235c8SOswald Buddenhagen 	return (hw->card_capabilities->emu_model &&
516e68235c8SOswald Buddenhagen 			hw->emu1010.word_clock == 44100) ? 0 : -501;
517e68235c8SOswald Buddenhagen }
518