18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  synth callback routines for Emu10k1
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/export.h>
98c2ecf20Sopenharmony_ci#include "emu10k1_synth_local.h"
108c2ecf20Sopenharmony_ci#include <sound/asoundef.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci/* voice status */
138c2ecf20Sopenharmony_cienum {
148c2ecf20Sopenharmony_ci	V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END
158c2ecf20Sopenharmony_ci};
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* Keeps track of what we are finding */
188c2ecf20Sopenharmony_cistruct best_voice {
198c2ecf20Sopenharmony_ci	unsigned int time;
208c2ecf20Sopenharmony_ci	int voice;
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci * prototypes
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_cistatic void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,
278c2ecf20Sopenharmony_ci			  struct best_voice *best, int active_only);
288c2ecf20Sopenharmony_cistatic struct snd_emux_voice *get_voice(struct snd_emux *emux,
298c2ecf20Sopenharmony_ci					struct snd_emux_port *port);
308c2ecf20Sopenharmony_cistatic int start_voice(struct snd_emux_voice *vp);
318c2ecf20Sopenharmony_cistatic void trigger_voice(struct snd_emux_voice *vp);
328c2ecf20Sopenharmony_cistatic void release_voice(struct snd_emux_voice *vp);
338c2ecf20Sopenharmony_cistatic void update_voice(struct snd_emux_voice *vp, int update);
348c2ecf20Sopenharmony_cistatic void terminate_voice(struct snd_emux_voice *vp);
358c2ecf20Sopenharmony_cistatic void free_voice(struct snd_emux_voice *vp);
368c2ecf20Sopenharmony_cistatic void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
378c2ecf20Sopenharmony_cistatic void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
388c2ecf20Sopenharmony_cistatic void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci * Ensure a value is between two points
428c2ecf20Sopenharmony_ci * macro evaluates its args more than once, so changed to upper-case.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
458c2ecf20Sopenharmony_ci#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * set up operators
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_cistatic const struct snd_emux_operators emu10k1_ops = {
528c2ecf20Sopenharmony_ci	.owner =	THIS_MODULE,
538c2ecf20Sopenharmony_ci	.get_voice =	get_voice,
548c2ecf20Sopenharmony_ci	.prepare =	start_voice,
558c2ecf20Sopenharmony_ci	.trigger =	trigger_voice,
568c2ecf20Sopenharmony_ci	.release =	release_voice,
578c2ecf20Sopenharmony_ci	.update =	update_voice,
588c2ecf20Sopenharmony_ci	.terminate =	terminate_voice,
598c2ecf20Sopenharmony_ci	.free_voice =	free_voice,
608c2ecf20Sopenharmony_ci	.sample_new =	snd_emu10k1_sample_new,
618c2ecf20Sopenharmony_ci	.sample_free =	snd_emu10k1_sample_free,
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_civoid
658c2ecf20Sopenharmony_cisnd_emu10k1_ops_setup(struct snd_emux *emux)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	emux->ops = emu10k1_ops;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/*
728c2ecf20Sopenharmony_ci * get more voice for pcm
738c2ecf20Sopenharmony_ci *
748c2ecf20Sopenharmony_ci * terminate most inactive voice and give it as a pcm voice.
758c2ecf20Sopenharmony_ci *
768c2ecf20Sopenharmony_ci * voice_lock is already held.
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_ciint
798c2ecf20Sopenharmony_cisnd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct snd_emux *emu;
828c2ecf20Sopenharmony_ci	struct snd_emux_voice *vp;
838c2ecf20Sopenharmony_ci	struct best_voice best[V_END];
848c2ecf20Sopenharmony_ci	int i;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	emu = hw->synth;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	lookup_voices(emu, hw, best, 1); /* no OFF voices */
898c2ecf20Sopenharmony_ci	for (i = 0; i < V_END; i++) {
908c2ecf20Sopenharmony_ci		if (best[i].voice >= 0) {
918c2ecf20Sopenharmony_ci			int ch;
928c2ecf20Sopenharmony_ci			vp = &emu->voices[best[i].voice];
938c2ecf20Sopenharmony_ci			if ((ch = vp->ch) < 0) {
948c2ecf20Sopenharmony_ci				/*
958c2ecf20Sopenharmony_ci				dev_warn(emu->card->dev,
968c2ecf20Sopenharmony_ci				       "synth_get_voice: ch < 0 (%d) ??", i);
978c2ecf20Sopenharmony_ci				*/
988c2ecf20Sopenharmony_ci				continue;
998c2ecf20Sopenharmony_ci			}
1008c2ecf20Sopenharmony_ci			vp->emu->num_voices--;
1018c2ecf20Sopenharmony_ci			vp->ch = -1;
1028c2ecf20Sopenharmony_ci			vp->state = SNDRV_EMUX_ST_OFF;
1038c2ecf20Sopenharmony_ci			return ch;
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* not found */
1088c2ecf20Sopenharmony_ci	return -ENOMEM;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/*
1138c2ecf20Sopenharmony_ci * turn off the voice (not terminated)
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic void
1168c2ecf20Sopenharmony_cirelease_voice(struct snd_emux_voice *vp)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int dcysusv;
1198c2ecf20Sopenharmony_ci	struct snd_emu10k1 *hw;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	hw = vp->hw;
1228c2ecf20Sopenharmony_ci	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
1238c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
1248c2ecf20Sopenharmony_ci	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
1258c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/*
1308c2ecf20Sopenharmony_ci * terminate the voice
1318c2ecf20Sopenharmony_ci */
1328c2ecf20Sopenharmony_cistatic void
1338c2ecf20Sopenharmony_citerminate_voice(struct snd_emux_voice *vp)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	struct snd_emu10k1 *hw;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!vp))
1388c2ecf20Sopenharmony_ci		return;
1398c2ecf20Sopenharmony_ci	hw = vp->hw;
1408c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
1418c2ecf20Sopenharmony_ci	if (vp->block) {
1428c2ecf20Sopenharmony_ci		struct snd_emu10k1_memblk *emem;
1438c2ecf20Sopenharmony_ci		emem = (struct snd_emu10k1_memblk *)vp->block;
1448c2ecf20Sopenharmony_ci		if (emem->map_locked > 0)
1458c2ecf20Sopenharmony_ci			emem->map_locked--;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * release the voice to system
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_cistatic void
1538c2ecf20Sopenharmony_cifree_voice(struct snd_emux_voice *vp)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct snd_emu10k1 *hw;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	hw = vp->hw;
1588c2ecf20Sopenharmony_ci	/* FIXME: emu10k1_synth is broken. */
1598c2ecf20Sopenharmony_ci	/* This can get called with hw == 0 */
1608c2ecf20Sopenharmony_ci	/* Problem apparent on plug, unplug then plug */
1618c2ecf20Sopenharmony_ci	/* on the Audigy 2 ZS Notebook. */
1628c2ecf20Sopenharmony_ci	if (hw && (vp->ch >= 0)) {
1638c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
1648c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
1658c2ecf20Sopenharmony_ci		// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
1668c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);
1678c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);
1688c2ecf20Sopenharmony_ci		snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
1698c2ecf20Sopenharmony_ci		vp->emu->num_voices--;
1708c2ecf20Sopenharmony_ci		vp->ch = -1;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci/*
1768c2ecf20Sopenharmony_ci * update registers
1778c2ecf20Sopenharmony_ci */
1788c2ecf20Sopenharmony_cistatic void
1798c2ecf20Sopenharmony_ciupdate_voice(struct snd_emux_voice *vp, int update)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct snd_emu10k1 *hw;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	hw = vp->hw;
1848c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_VOLUME)
1858c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);
1868c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_PITCH)
1878c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
1888c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_PAN) {
1898c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);
1908c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_FMMOD)
1938c2ecf20Sopenharmony_ci		set_fmmod(hw, vp);
1948c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
1958c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
1968c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
1978c2ecf20Sopenharmony_ci		set_fm2frq2(hw, vp);
1988c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_Q)
1998c2ecf20Sopenharmony_ci		set_filterQ(hw, vp);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/*
2048c2ecf20Sopenharmony_ci * look up voice table - get the best voice in order of preference
2058c2ecf20Sopenharmony_ci */
2068c2ecf20Sopenharmony_ci/* spinlock held! */
2078c2ecf20Sopenharmony_cistatic void
2088c2ecf20Sopenharmony_cilookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
2098c2ecf20Sopenharmony_ci	      struct best_voice *best, int active_only)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct snd_emux_voice *vp;
2128c2ecf20Sopenharmony_ci	struct best_voice *bp;
2138c2ecf20Sopenharmony_ci	int  i;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	for (i = 0; i < V_END; i++) {
2168c2ecf20Sopenharmony_ci		best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */
2178c2ecf20Sopenharmony_ci		best[i].voice = -1;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/*
2218c2ecf20Sopenharmony_ci	 * Go through them all and get a best one to use.
2228c2ecf20Sopenharmony_ci	 * NOTE: could also look at volume and pick the quietest one.
2238c2ecf20Sopenharmony_ci	 */
2248c2ecf20Sopenharmony_ci	for (i = 0; i < emu->max_voices; i++) {
2258c2ecf20Sopenharmony_ci		int state, val;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		vp = &emu->voices[i];
2288c2ecf20Sopenharmony_ci		state = vp->state;
2298c2ecf20Sopenharmony_ci		if (state == SNDRV_EMUX_ST_OFF) {
2308c2ecf20Sopenharmony_ci			if (vp->ch < 0) {
2318c2ecf20Sopenharmony_ci				if (active_only)
2328c2ecf20Sopenharmony_ci					continue;
2338c2ecf20Sopenharmony_ci				bp = best + V_FREE;
2348c2ecf20Sopenharmony_ci			} else
2358c2ecf20Sopenharmony_ci				bp = best + V_OFF;
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci		else if (state == SNDRV_EMUX_ST_RELEASED ||
2388c2ecf20Sopenharmony_ci			 state == SNDRV_EMUX_ST_PENDING) {
2398c2ecf20Sopenharmony_ci			bp = best + V_RELEASED;
2408c2ecf20Sopenharmony_ci#if 1
2418c2ecf20Sopenharmony_ci			val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);
2428c2ecf20Sopenharmony_ci			if (! val)
2438c2ecf20Sopenharmony_ci				bp = best + V_OFF;
2448c2ecf20Sopenharmony_ci#endif
2458c2ecf20Sopenharmony_ci		}
2468c2ecf20Sopenharmony_ci		else if (state == SNDRV_EMUX_ST_STANDBY)
2478c2ecf20Sopenharmony_ci			continue;
2488c2ecf20Sopenharmony_ci		else if (state & SNDRV_EMUX_ST_ON)
2498c2ecf20Sopenharmony_ci			bp = best + V_PLAYING;
2508c2ecf20Sopenharmony_ci		else
2518c2ecf20Sopenharmony_ci			continue;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		/* check if sample is finished playing (non-looping only) */
2548c2ecf20Sopenharmony_ci		if (bp != best + V_OFF && bp != best + V_FREE &&
2558c2ecf20Sopenharmony_ci		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
2568c2ecf20Sopenharmony_ci			val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
2578c2ecf20Sopenharmony_ci			if (val >= vp->reg.loopstart)
2588c2ecf20Sopenharmony_ci				bp = best + V_OFF;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		if (vp->time < bp->time) {
2628c2ecf20Sopenharmony_ci			bp->time = vp->time;
2638c2ecf20Sopenharmony_ci			bp->voice = i;
2648c2ecf20Sopenharmony_ci		}
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/*
2698c2ecf20Sopenharmony_ci * get an empty voice
2708c2ecf20Sopenharmony_ci *
2718c2ecf20Sopenharmony_ci * emu->voice_lock is already held.
2728c2ecf20Sopenharmony_ci */
2738c2ecf20Sopenharmony_cistatic struct snd_emux_voice *
2748c2ecf20Sopenharmony_ciget_voice(struct snd_emux *emu, struct snd_emux_port *port)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct snd_emu10k1 *hw;
2778c2ecf20Sopenharmony_ci	struct snd_emux_voice *vp;
2788c2ecf20Sopenharmony_ci	struct best_voice best[V_END];
2798c2ecf20Sopenharmony_ci	int i;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	hw = emu->hw;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	lookup_voices(emu, hw, best, 0);
2848c2ecf20Sopenharmony_ci	for (i = 0; i < V_END; i++) {
2858c2ecf20Sopenharmony_ci		if (best[i].voice >= 0) {
2868c2ecf20Sopenharmony_ci			vp = &emu->voices[best[i].voice];
2878c2ecf20Sopenharmony_ci			if (vp->ch < 0) {
2888c2ecf20Sopenharmony_ci				/* allocate a voice */
2898c2ecf20Sopenharmony_ci				struct snd_emu10k1_voice *hwvoice;
2908c2ecf20Sopenharmony_ci				if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)
2918c2ecf20Sopenharmony_ci					continue;
2928c2ecf20Sopenharmony_ci				vp->ch = hwvoice->number;
2938c2ecf20Sopenharmony_ci				emu->num_voices++;
2948c2ecf20Sopenharmony_ci			}
2958c2ecf20Sopenharmony_ci			return vp;
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* not found */
3008c2ecf20Sopenharmony_ci	return NULL;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/*
3048c2ecf20Sopenharmony_ci * prepare envelopes and LFOs
3058c2ecf20Sopenharmony_ci */
3068c2ecf20Sopenharmony_cistatic int
3078c2ecf20Sopenharmony_cistart_voice(struct snd_emux_voice *vp)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	unsigned int temp;
3108c2ecf20Sopenharmony_ci	int ch;
3118c2ecf20Sopenharmony_ci	unsigned int addr, mapped_offset;
3128c2ecf20Sopenharmony_ci	struct snd_midi_channel *chan;
3138c2ecf20Sopenharmony_ci	struct snd_emu10k1 *hw;
3148c2ecf20Sopenharmony_ci	struct snd_emu10k1_memblk *emem;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	hw = vp->hw;
3178c2ecf20Sopenharmony_ci	ch = vp->ch;
3188c2ecf20Sopenharmony_ci	if (snd_BUG_ON(ch < 0))
3198c2ecf20Sopenharmony_ci		return -EINVAL;
3208c2ecf20Sopenharmony_ci	chan = vp->chan;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	emem = (struct snd_emu10k1_memblk *)vp->block;
3238c2ecf20Sopenharmony_ci	if (emem == NULL)
3248c2ecf20Sopenharmony_ci		return -EINVAL;
3258c2ecf20Sopenharmony_ci	emem->map_locked++;
3268c2ecf20Sopenharmony_ci	if (snd_emu10k1_memblk_map(hw, emem) < 0) {
3278c2ecf20Sopenharmony_ci		/* dev_err(hw->card->devK, "emu: cannot map!\n"); */
3288c2ecf20Sopenharmony_ci		return -ENOMEM;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci	mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
3318c2ecf20Sopenharmony_ci	vp->reg.start += mapped_offset;
3328c2ecf20Sopenharmony_ci	vp->reg.end += mapped_offset;
3338c2ecf20Sopenharmony_ci	vp->reg.loopstart += mapped_offset;
3348c2ecf20Sopenharmony_ci	vp->reg.loopend += mapped_offset;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* set channel routing */
3378c2ecf20Sopenharmony_ci	/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */
3388c2ecf20Sopenharmony_ci	if (hw->audigy) {
3398c2ecf20Sopenharmony_ci		temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) |
3408c2ecf20Sopenharmony_ci			(FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);
3418c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);
3428c2ecf20Sopenharmony_ci	} else {
3438c2ecf20Sopenharmony_ci		temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) |
3448c2ecf20Sopenharmony_ci			(FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);
3458c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* channel to be silent and idle */
3498c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000);
3508c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
3518c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
3528c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
3538c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, CPF, ch, 0);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* set pitch offset */
3568c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/* set envelope parameters */
3598c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);
3608c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);
3618c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);
3628c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);
3638c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);
3648c2ecf20Sopenharmony_ci	/* decay/sustain parameter for volume envelope is used
3658c2ecf20Sopenharmony_ci	   for triggerg the voice */
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* cutoff and volume */
3688c2ecf20Sopenharmony_ci	temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;
3698c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* modulation envelope heights */
3728c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* lfo1/2 delay */
3758c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);
3768c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* lfo1 pitch & cutoff shift */
3798c2ecf20Sopenharmony_ci	set_fmmod(hw, vp);
3808c2ecf20Sopenharmony_ci	/* lfo1 volume & freq */
3818c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
3828c2ecf20Sopenharmony_ci	/* lfo2 pitch & freq */
3838c2ecf20Sopenharmony_ci	set_fm2frq2(hw, vp);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	/* reverb and loop start (reverb 8bit, MSB) */
3868c2ecf20Sopenharmony_ci	temp = vp->reg.parm.reverb;
3878c2ecf20Sopenharmony_ci	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
3888c2ecf20Sopenharmony_ci	LIMITMAX(temp, 255);
3898c2ecf20Sopenharmony_ci	addr = vp->reg.loopstart;
3908c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* chorus & loop end (chorus 8bit, MSB) */
3938c2ecf20Sopenharmony_ci	addr = vp->reg.loopend;
3948c2ecf20Sopenharmony_ci	temp = vp->reg.parm.chorus;
3958c2ecf20Sopenharmony_ci	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
3968c2ecf20Sopenharmony_ci	LIMITMAX(temp, 255);
3978c2ecf20Sopenharmony_ci	temp = (temp <<24) | addr;
3988c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, DSL, ch, temp);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* clear filter delay memory */
4018c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, Z1, ch, 0);
4028c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, Z2, ch, 0);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* invalidate maps */
4058c2ecf20Sopenharmony_ci	temp = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
4068c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
4078c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
4088c2ecf20Sopenharmony_ci#if 0
4098c2ecf20Sopenharmony_ci	/* cache */
4108c2ecf20Sopenharmony_ci	{
4118c2ecf20Sopenharmony_ci		unsigned int val, sample;
4128c2ecf20Sopenharmony_ci		val = 32;
4138c2ecf20Sopenharmony_ci		if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
4148c2ecf20Sopenharmony_ci			sample = 0x80808080;
4158c2ecf20Sopenharmony_ci		else {
4168c2ecf20Sopenharmony_ci			sample = 0;
4178c2ecf20Sopenharmony_ci			val *= 2;
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		/* cache */
4218c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);
4228c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, CDE, ch, sample);
4238c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, CDF, ch, sample);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		/* invalidate maps */
4268c2ecf20Sopenharmony_ci		temp = ((unsigned int)hw->silent_page.addr << hw_address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
4278c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
4288c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		/* fill cache */
4318c2ecf20Sopenharmony_ci		val -= 4;
4328c2ecf20Sopenharmony_ci		val <<= 25;
4338c2ecf20Sopenharmony_ci		val |= 0x1c << 16;
4348c2ecf20Sopenharmony_ci		snd_emu10k1_ptr_write(hw, CCR, ch, val);
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci#endif
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* Q & current address (Q 4bit value, MSB) */
4398c2ecf20Sopenharmony_ci	addr = vp->reg.start;
4408c2ecf20Sopenharmony_ci	temp = vp->reg.parm.filterQ;
4418c2ecf20Sopenharmony_ci	temp = (temp<<28) | addr;
4428c2ecf20Sopenharmony_ci	if (vp->apitch < 0xe400)
4438c2ecf20Sopenharmony_ci		temp |= CCCA_INTERPROM_0;
4448c2ecf20Sopenharmony_ci	else {
4458c2ecf20Sopenharmony_ci		unsigned int shift = (vp->apitch - 0xe000) >> 10;
4468c2ecf20Sopenharmony_ci		temp |= shift << 25;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
4498c2ecf20Sopenharmony_ci		temp |= CCCA_8BITSELECT;
4508c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, CCCA, ch, temp);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	/* reset volume */
4538c2ecf20Sopenharmony_ci	temp = (unsigned int)vp->vtarget << 16;
4548c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
4558c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
4568c2ecf20Sopenharmony_ci	return 0;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci/*
4608c2ecf20Sopenharmony_ci * Start envelope
4618c2ecf20Sopenharmony_ci */
4628c2ecf20Sopenharmony_cistatic void
4638c2ecf20Sopenharmony_citrigger_voice(struct snd_emux_voice *vp)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	unsigned int temp, ptarget;
4668c2ecf20Sopenharmony_ci	struct snd_emu10k1 *hw;
4678c2ecf20Sopenharmony_ci	struct snd_emu10k1_memblk *emem;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	hw = vp->hw;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	emem = (struct snd_emu10k1_memblk *)vp->block;
4728c2ecf20Sopenharmony_ci	if (! emem || emem->mapped_page < 0)
4738c2ecf20Sopenharmony_ci		return; /* not mapped */
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci#if 0
4768c2ecf20Sopenharmony_ci	ptarget = (unsigned int)vp->ptarget << 16;
4778c2ecf20Sopenharmony_ci#else
4788c2ecf20Sopenharmony_ci	ptarget = IP_TO_CP(vp->apitch);
4798c2ecf20Sopenharmony_ci#endif
4808c2ecf20Sopenharmony_ci	/* set pitch target and pan (volume) */
4818c2ecf20Sopenharmony_ci	temp = ptarget | (vp->apan << 8) | vp->aaux;
4828c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/* pitch target */
4858c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/* trigger voice */
4888c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci#define MOD_SENSE 18
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci/* set lfo1 modulation height and cutoff */
4948c2ecf20Sopenharmony_cistatic void
4958c2ecf20Sopenharmony_ciset_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	unsigned short fmmod;
4988c2ecf20Sopenharmony_ci	short pitch;
4998c2ecf20Sopenharmony_ci	unsigned char cutoff;
5008c2ecf20Sopenharmony_ci	int modulation;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	pitch = (char)(vp->reg.parm.fmmod>>8);
5038c2ecf20Sopenharmony_ci	cutoff = (vp->reg.parm.fmmod & 0xff);
5048c2ecf20Sopenharmony_ci	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
5058c2ecf20Sopenharmony_ci	pitch += (MOD_SENSE * modulation) / 1200;
5068c2ecf20Sopenharmony_ci	LIMITVALUE(pitch, -128, 127);
5078c2ecf20Sopenharmony_ci	fmmod = ((unsigned char)pitch<<8) | cutoff;
5088c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci/* set lfo2 pitch & frequency */
5128c2ecf20Sopenharmony_cistatic void
5138c2ecf20Sopenharmony_ciset_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	unsigned short fm2frq2;
5168c2ecf20Sopenharmony_ci	short pitch;
5178c2ecf20Sopenharmony_ci	unsigned char freq;
5188c2ecf20Sopenharmony_ci	int modulation;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	pitch = (char)(vp->reg.parm.fm2frq2>>8);
5218c2ecf20Sopenharmony_ci	freq = vp->reg.parm.fm2frq2 & 0xff;
5228c2ecf20Sopenharmony_ci	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
5238c2ecf20Sopenharmony_ci	pitch += (MOD_SENSE * modulation) / 1200;
5248c2ecf20Sopenharmony_ci	LIMITVALUE(pitch, -128, 127);
5258c2ecf20Sopenharmony_ci	fm2frq2 = ((unsigned char)pitch<<8) | freq;
5268c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci/* set filterQ */
5308c2ecf20Sopenharmony_cistatic void
5318c2ecf20Sopenharmony_ciset_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	unsigned int val;
5348c2ecf20Sopenharmony_ci	val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
5358c2ecf20Sopenharmony_ci	val |= (vp->reg.parm.filterQ << 28);
5368c2ecf20Sopenharmony_ci	snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
5378c2ecf20Sopenharmony_ci}
538