18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  synth callback routines for the emu8000 (AWE32/64)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1999 Steve Ratcliffe
68c2ecf20Sopenharmony_ci *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "emu8000_local.h"
108c2ecf20Sopenharmony_ci#include <linux/export.h>
118c2ecf20Sopenharmony_ci#include <sound/asoundef.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * prototypes
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_cistatic struct snd_emux_voice *get_voice(struct snd_emux *emu,
178c2ecf20Sopenharmony_ci					struct snd_emux_port *port);
188c2ecf20Sopenharmony_cistatic int start_voice(struct snd_emux_voice *vp);
198c2ecf20Sopenharmony_cistatic void trigger_voice(struct snd_emux_voice *vp);
208c2ecf20Sopenharmony_cistatic void release_voice(struct snd_emux_voice *vp);
218c2ecf20Sopenharmony_cistatic void update_voice(struct snd_emux_voice *vp, int update);
228c2ecf20Sopenharmony_cistatic void reset_voice(struct snd_emux *emu, int ch);
238c2ecf20Sopenharmony_cistatic void terminate_voice(struct snd_emux_voice *vp);
248c2ecf20Sopenharmony_cistatic void sysex(struct snd_emux *emu, char *buf, int len, int parsed,
258c2ecf20Sopenharmony_ci		  struct snd_midi_channel_set *chset);
268c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
278c2ecf20Sopenharmony_cistatic int oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2);
288c2ecf20Sopenharmony_ci#endif
298c2ecf20Sopenharmony_cistatic int load_fx(struct snd_emux *emu, int type, int mode,
308c2ecf20Sopenharmony_ci		   const void __user *buf, long len);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic void set_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
338c2ecf20Sopenharmony_cistatic void set_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
348c2ecf20Sopenharmony_cistatic void set_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
358c2ecf20Sopenharmony_cistatic void set_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
368c2ecf20Sopenharmony_cistatic void set_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
378c2ecf20Sopenharmony_cistatic void set_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
388c2ecf20Sopenharmony_cistatic void set_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
398c2ecf20Sopenharmony_cistatic void snd_emu8000_tweak_voice(struct snd_emu8000 *emu, int ch);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci * Ensure a value is between two points
438c2ecf20Sopenharmony_ci * macro evaluates its args more than once, so changed to upper-case.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_ci#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
468c2ecf20Sopenharmony_ci#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/*
508c2ecf20Sopenharmony_ci * set up operators
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistatic const struct snd_emux_operators emu8000_ops = {
538c2ecf20Sopenharmony_ci	.owner =	THIS_MODULE,
548c2ecf20Sopenharmony_ci	.get_voice =	get_voice,
558c2ecf20Sopenharmony_ci	.prepare =	start_voice,
568c2ecf20Sopenharmony_ci	.trigger =	trigger_voice,
578c2ecf20Sopenharmony_ci	.release =	release_voice,
588c2ecf20Sopenharmony_ci	.update =	update_voice,
598c2ecf20Sopenharmony_ci	.terminate =	terminate_voice,
608c2ecf20Sopenharmony_ci	.reset =	reset_voice,
618c2ecf20Sopenharmony_ci	.sample_new =	snd_emu8000_sample_new,
628c2ecf20Sopenharmony_ci	.sample_free =	snd_emu8000_sample_free,
638c2ecf20Sopenharmony_ci	.sample_reset = snd_emu8000_sample_reset,
648c2ecf20Sopenharmony_ci	.load_fx =	load_fx,
658c2ecf20Sopenharmony_ci	.sysex =	sysex,
668c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
678c2ecf20Sopenharmony_ci	.oss_ioctl =	oss_ioctl,
688c2ecf20Sopenharmony_ci#endif
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_civoid
728c2ecf20Sopenharmony_cisnd_emu8000_ops_setup(struct snd_emu8000 *hw)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	hw->emu->ops = emu8000_ops;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/*
808c2ecf20Sopenharmony_ci * Terminate a voice
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_cistatic void
838c2ecf20Sopenharmony_cirelease_voice(struct snd_emux_voice *vp)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int dcysusv;
868c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	hw = vp->hw;
898c2ecf20Sopenharmony_ci	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
908c2ecf20Sopenharmony_ci	EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv);
918c2ecf20Sopenharmony_ci	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease;
928c2ecf20Sopenharmony_ci	EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/*
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_cistatic void
998c2ecf20Sopenharmony_citerminate_voice(struct snd_emux_voice *vp)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	hw = vp->hw;
1048c2ecf20Sopenharmony_ci	EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F);
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/*
1098c2ecf20Sopenharmony_ci */
1108c2ecf20Sopenharmony_cistatic void
1118c2ecf20Sopenharmony_ciupdate_voice(struct snd_emux_voice *vp, int update)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	hw = vp->hw;
1168c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_VOLUME)
1178c2ecf20Sopenharmony_ci		set_volume(hw, vp);
1188c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_PITCH)
1198c2ecf20Sopenharmony_ci		set_pitch(hw, vp);
1208c2ecf20Sopenharmony_ci	if ((update & SNDRV_EMUX_UPDATE_PAN) &&
1218c2ecf20Sopenharmony_ci	    vp->port->ctrls[EMUX_MD_REALTIME_PAN])
1228c2ecf20Sopenharmony_ci		set_pan(hw, vp);
1238c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_FMMOD)
1248c2ecf20Sopenharmony_ci		set_fmmod(hw, vp);
1258c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
1268c2ecf20Sopenharmony_ci		set_tremfreq(hw, vp);
1278c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
1288c2ecf20Sopenharmony_ci		set_fm2frq2(hw, vp);
1298c2ecf20Sopenharmony_ci	if (update & SNDRV_EMUX_UPDATE_Q)
1308c2ecf20Sopenharmony_ci		set_filterQ(hw, vp);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/*
1358c2ecf20Sopenharmony_ci * Find a channel (voice) within the EMU that is not in use or at least
1368c2ecf20Sopenharmony_ci * less in use than other channels.  Always returns a valid pointer
1378c2ecf20Sopenharmony_ci * no matter what.  If there is a real shortage of voices then one
1388c2ecf20Sopenharmony_ci * will be cut. Such is life.
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * The channel index (vp->ch) must be initialized in this routine.
1418c2ecf20Sopenharmony_ci * In Emu8k, it is identical with the array index.
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_cistatic struct snd_emux_voice *
1448c2ecf20Sopenharmony_ciget_voice(struct snd_emux *emu, struct snd_emux_port *port)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	int  i;
1478c2ecf20Sopenharmony_ci	struct snd_emux_voice *vp;
1488c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* what we are looking for, in order of preference */
1518c2ecf20Sopenharmony_ci	enum {
1528c2ecf20Sopenharmony_ci		OFF=0, RELEASED, PLAYING, END
1538c2ecf20Sopenharmony_ci	};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* Keeps track of what we are finding */
1568c2ecf20Sopenharmony_ci	struct best {
1578c2ecf20Sopenharmony_ci		unsigned int  time;
1588c2ecf20Sopenharmony_ci		int voice;
1598c2ecf20Sopenharmony_ci	} best[END];
1608c2ecf20Sopenharmony_ci	struct best *bp;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	hw = emu->hw;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	for (i = 0; i < END; i++) {
1658c2ecf20Sopenharmony_ci		best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */
1668c2ecf20Sopenharmony_ci		best[i].voice = -1;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * Go through them all and get a best one to use.
1718c2ecf20Sopenharmony_ci	 */
1728c2ecf20Sopenharmony_ci	for (i = 0; i < emu->max_voices; i++) {
1738c2ecf20Sopenharmony_ci		int state, val;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		vp = &emu->voices[i];
1768c2ecf20Sopenharmony_ci		state = vp->state;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		if (state == SNDRV_EMUX_ST_OFF)
1798c2ecf20Sopenharmony_ci			bp = best + OFF;
1808c2ecf20Sopenharmony_ci		else if (state == SNDRV_EMUX_ST_RELEASED ||
1818c2ecf20Sopenharmony_ci			 state == SNDRV_EMUX_ST_PENDING) {
1828c2ecf20Sopenharmony_ci			bp = best + RELEASED;
1838c2ecf20Sopenharmony_ci			val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff;
1848c2ecf20Sopenharmony_ci			if (! val)
1858c2ecf20Sopenharmony_ci				bp = best + OFF;
1868c2ecf20Sopenharmony_ci		}
1878c2ecf20Sopenharmony_ci		else if (state & SNDRV_EMUX_ST_ON)
1888c2ecf20Sopenharmony_ci			bp = best + PLAYING;
1898c2ecf20Sopenharmony_ci		else
1908c2ecf20Sopenharmony_ci			continue;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		/* check if sample is finished playing (non-looping only) */
1938c2ecf20Sopenharmony_ci		if (state != SNDRV_EMUX_ST_OFF &&
1948c2ecf20Sopenharmony_ci		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
1958c2ecf20Sopenharmony_ci			val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
1968c2ecf20Sopenharmony_ci			if (val >= vp->reg.loopstart)
1978c2ecf20Sopenharmony_ci				bp = best + OFF;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		if (vp->time < bp->time) {
2018c2ecf20Sopenharmony_ci			bp->time = vp->time;
2028c2ecf20Sopenharmony_ci			bp->voice = i;
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	for (i = 0; i < END; i++) {
2078c2ecf20Sopenharmony_ci		if (best[i].voice >= 0) {
2088c2ecf20Sopenharmony_ci			vp = &emu->voices[best[i].voice];
2098c2ecf20Sopenharmony_ci			vp->ch = best[i].voice;
2108c2ecf20Sopenharmony_ci			return vp;
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* not found */
2158c2ecf20Sopenharmony_ci	return NULL;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/*
2198c2ecf20Sopenharmony_ci */
2208c2ecf20Sopenharmony_cistatic int
2218c2ecf20Sopenharmony_cistart_voice(struct snd_emux_voice *vp)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	unsigned int temp;
2248c2ecf20Sopenharmony_ci	int ch;
2258c2ecf20Sopenharmony_ci	int addr;
2268c2ecf20Sopenharmony_ci	struct snd_midi_channel *chan;
2278c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	hw = vp->hw;
2308c2ecf20Sopenharmony_ci	ch = vp->ch;
2318c2ecf20Sopenharmony_ci	chan = vp->chan;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* channel to be silent and idle */
2348c2ecf20Sopenharmony_ci	EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);
2358c2ecf20Sopenharmony_ci	EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);
2368c2ecf20Sopenharmony_ci	EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);
2378c2ecf20Sopenharmony_ci	EMU8000_PTRX_WRITE(hw, ch, 0);
2388c2ecf20Sopenharmony_ci	EMU8000_CPF_WRITE(hw, ch, 0);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* set pitch offset */
2418c2ecf20Sopenharmony_ci	set_pitch(hw, vp);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* set envelope parameters */
2448c2ecf20Sopenharmony_ci	EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay);
2458c2ecf20Sopenharmony_ci	EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld);
2468c2ecf20Sopenharmony_ci	EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus);
2478c2ecf20Sopenharmony_ci	EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay);
2488c2ecf20Sopenharmony_ci	EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld);
2498c2ecf20Sopenharmony_ci	/* decay/sustain parameter for volume envelope is used
2508c2ecf20Sopenharmony_ci	   for triggerg the voice */
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* cutoff and volume */
2538c2ecf20Sopenharmony_ci	set_volume(hw, vp);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* modulation envelope heights */
2568c2ecf20Sopenharmony_ci	EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	/* lfo1/2 delay */
2598c2ecf20Sopenharmony_ci	EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay);
2608c2ecf20Sopenharmony_ci	EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* lfo1 pitch & cutoff shift */
2638c2ecf20Sopenharmony_ci	set_fmmod(hw, vp);
2648c2ecf20Sopenharmony_ci	/* lfo1 volume & freq */
2658c2ecf20Sopenharmony_ci	set_tremfreq(hw, vp);
2668c2ecf20Sopenharmony_ci	/* lfo2 pitch & freq */
2678c2ecf20Sopenharmony_ci	set_fm2frq2(hw, vp);
2688c2ecf20Sopenharmony_ci	/* pan & loop start */
2698c2ecf20Sopenharmony_ci	set_pan(hw, vp);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* chorus & loop end (chorus 8bit, MSB) */
2728c2ecf20Sopenharmony_ci	addr = vp->reg.loopend - 1;
2738c2ecf20Sopenharmony_ci	temp = vp->reg.parm.chorus;
2748c2ecf20Sopenharmony_ci	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
2758c2ecf20Sopenharmony_ci	LIMITMAX(temp, 255);
2768c2ecf20Sopenharmony_ci	temp = (temp <<24) | (unsigned int)addr;
2778c2ecf20Sopenharmony_ci	EMU8000_CSL_WRITE(hw, ch, temp);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* Q & current address (Q 4bit value, MSB) */
2808c2ecf20Sopenharmony_ci	addr = vp->reg.start - 1;
2818c2ecf20Sopenharmony_ci	temp = vp->reg.parm.filterQ;
2828c2ecf20Sopenharmony_ci	temp = (temp<<28) | (unsigned int)addr;
2838c2ecf20Sopenharmony_ci	EMU8000_CCCA_WRITE(hw, ch, temp);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	/* clear unknown registers */
2868c2ecf20Sopenharmony_ci	EMU8000_00A0_WRITE(hw, ch, 0);
2878c2ecf20Sopenharmony_ci	EMU8000_0080_WRITE(hw, ch, 0);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* reset volume */
2908c2ecf20Sopenharmony_ci	temp = vp->vtarget << 16;
2918c2ecf20Sopenharmony_ci	EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget);
2928c2ecf20Sopenharmony_ci	EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return 0;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci/*
2988c2ecf20Sopenharmony_ci * Start envelope
2998c2ecf20Sopenharmony_ci */
3008c2ecf20Sopenharmony_cistatic void
3018c2ecf20Sopenharmony_citrigger_voice(struct snd_emux_voice *vp)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	int ch = vp->ch;
3048c2ecf20Sopenharmony_ci	unsigned int temp;
3058c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	hw = vp->hw;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* set reverb and pitch target */
3108c2ecf20Sopenharmony_ci	temp = vp->reg.parm.reverb;
3118c2ecf20Sopenharmony_ci	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
3128c2ecf20Sopenharmony_ci	LIMITMAX(temp, 255);
3138c2ecf20Sopenharmony_ci	temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux;
3148c2ecf20Sopenharmony_ci	EMU8000_PTRX_WRITE(hw, ch, temp);
3158c2ecf20Sopenharmony_ci	EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16);
3168c2ecf20Sopenharmony_ci	EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus);
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/*
3208c2ecf20Sopenharmony_ci * reset voice parameters
3218c2ecf20Sopenharmony_ci */
3228c2ecf20Sopenharmony_cistatic void
3238c2ecf20Sopenharmony_cireset_voice(struct snd_emux *emu, int ch)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	hw = emu->hw;
3288c2ecf20Sopenharmony_ci	EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
3298c2ecf20Sopenharmony_ci	snd_emu8000_tweak_voice(hw, ch);
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci/*
3338c2ecf20Sopenharmony_ci * Set the pitch of a possibly playing note.
3348c2ecf20Sopenharmony_ci */
3358c2ecf20Sopenharmony_cistatic void
3368c2ecf20Sopenharmony_ciset_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	EMU8000_IP_WRITE(hw, vp->ch, vp->apitch);
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/*
3428c2ecf20Sopenharmony_ci * Set the volume of a possibly already playing note
3438c2ecf20Sopenharmony_ci */
3448c2ecf20Sopenharmony_cistatic void
3458c2ecf20Sopenharmony_ciset_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	int  ifatn;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	ifatn = (unsigned char)vp->acutoff;
3508c2ecf20Sopenharmony_ci	ifatn = (ifatn << 8);
3518c2ecf20Sopenharmony_ci	ifatn |= (unsigned char)vp->avol;
3528c2ecf20Sopenharmony_ci	EMU8000_IFATN_WRITE(hw, vp->ch, ifatn);
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/*
3568c2ecf20Sopenharmony_ci * Set pan and loop start address.
3578c2ecf20Sopenharmony_ci */
3588c2ecf20Sopenharmony_cistatic void
3598c2ecf20Sopenharmony_ciset_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	unsigned int temp;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1);
3648c2ecf20Sopenharmony_ci	EMU8000_PSST_WRITE(hw, vp->ch, temp);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci#define MOD_SENSE 18
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void
3708c2ecf20Sopenharmony_ciset_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	unsigned short fmmod;
3738c2ecf20Sopenharmony_ci	short pitch;
3748c2ecf20Sopenharmony_ci	unsigned char cutoff;
3758c2ecf20Sopenharmony_ci	int modulation;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	pitch = (char)(vp->reg.parm.fmmod>>8);
3788c2ecf20Sopenharmony_ci	cutoff = (vp->reg.parm.fmmod & 0xff);
3798c2ecf20Sopenharmony_ci	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
3808c2ecf20Sopenharmony_ci	pitch += (MOD_SENSE * modulation) / 1200;
3818c2ecf20Sopenharmony_ci	LIMITVALUE(pitch, -128, 127);
3828c2ecf20Sopenharmony_ci	fmmod = ((unsigned char)pitch<<8) | cutoff;
3838c2ecf20Sopenharmony_ci	EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/* set tremolo (lfo1) volume & frequency */
3878c2ecf20Sopenharmony_cistatic void
3888c2ecf20Sopenharmony_ciset_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci/* set lfo2 pitch & frequency */
3948c2ecf20Sopenharmony_cistatic void
3958c2ecf20Sopenharmony_ciset_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	unsigned short fm2frq2;
3988c2ecf20Sopenharmony_ci	short pitch;
3998c2ecf20Sopenharmony_ci	unsigned char freq;
4008c2ecf20Sopenharmony_ci	int modulation;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	pitch = (char)(vp->reg.parm.fm2frq2>>8);
4038c2ecf20Sopenharmony_ci	freq = vp->reg.parm.fm2frq2 & 0xff;
4048c2ecf20Sopenharmony_ci	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
4058c2ecf20Sopenharmony_ci	pitch += (MOD_SENSE * modulation) / 1200;
4068c2ecf20Sopenharmony_ci	LIMITVALUE(pitch, -128, 127);
4078c2ecf20Sopenharmony_ci	fm2frq2 = ((unsigned char)pitch<<8) | freq;
4088c2ecf20Sopenharmony_ci	EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci/* set filterQ */
4128c2ecf20Sopenharmony_cistatic void
4138c2ecf20Sopenharmony_ciset_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	unsigned int addr;
4168c2ecf20Sopenharmony_ci	addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
4178c2ecf20Sopenharmony_ci	addr |= (vp->reg.parm.filterQ << 28);
4188c2ecf20Sopenharmony_ci	EMU8000_CCCA_WRITE(hw, vp->ch, addr);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci/*
4228c2ecf20Sopenharmony_ci * set the envelope & LFO parameters to the default values
4238c2ecf20Sopenharmony_ci */
4248c2ecf20Sopenharmony_cistatic void
4258c2ecf20Sopenharmony_cisnd_emu8000_tweak_voice(struct snd_emu8000 *emu, int i)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	/* set all mod/vol envelope shape to minimum */
4288c2ecf20Sopenharmony_ci	EMU8000_ENVVOL_WRITE(emu, i, 0x8000);
4298c2ecf20Sopenharmony_ci	EMU8000_ENVVAL_WRITE(emu, i, 0x8000);
4308c2ecf20Sopenharmony_ci	EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F);
4318c2ecf20Sopenharmony_ci	EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F);
4328c2ecf20Sopenharmony_ci	EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F);
4338c2ecf20Sopenharmony_ci	EMU8000_PEFE_WRITE(emu, i, 0);  /* mod envelope height to zero */
4348c2ecf20Sopenharmony_ci	EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */
4358c2ecf20Sopenharmony_ci	EMU8000_LFO2VAL_WRITE(emu, i, 0x8000);
4368c2ecf20Sopenharmony_ci	EMU8000_IP_WRITE(emu, i, 0xE000);	/* no pitch shift */
4378c2ecf20Sopenharmony_ci	EMU8000_IFATN_WRITE(emu, i, 0xFF00);	/* volume to minimum */
4388c2ecf20Sopenharmony_ci	EMU8000_FMMOD_WRITE(emu, i, 0);
4398c2ecf20Sopenharmony_ci	EMU8000_TREMFRQ_WRITE(emu, i, 0);
4408c2ecf20Sopenharmony_ci	EMU8000_FM2FRQ2_WRITE(emu, i, 0);
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci/*
4448c2ecf20Sopenharmony_ci * sysex callback
4458c2ecf20Sopenharmony_ci */
4468c2ecf20Sopenharmony_cistatic void
4478c2ecf20Sopenharmony_cisysex(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_channel_set *chset)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	hw = emu->hw;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	switch (parsed) {
4548c2ecf20Sopenharmony_ci	case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE:
4558c2ecf20Sopenharmony_ci		hw->chorus_mode = chset->gs_chorus_mode;
4568c2ecf20Sopenharmony_ci		snd_emu8000_update_chorus_mode(hw);
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	case SNDRV_MIDI_SYSEX_GS_REVERB_MODE:
4608c2ecf20Sopenharmony_ci		hw->reverb_mode = chset->gs_reverb_mode;
4618c2ecf20Sopenharmony_ci		snd_emu8000_update_reverb_mode(hw);
4628c2ecf20Sopenharmony_ci		break;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER_OSS)
4688c2ecf20Sopenharmony_ci/*
4698c2ecf20Sopenharmony_ci * OSS ioctl callback
4708c2ecf20Sopenharmony_ci */
4718c2ecf20Sopenharmony_cistatic int
4728c2ecf20Sopenharmony_cioss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	hw = emu->hw;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	switch (cmd) {
4798c2ecf20Sopenharmony_ci	case _EMUX_OSS_REVERB_MODE:
4808c2ecf20Sopenharmony_ci		hw->reverb_mode = p1;
4818c2ecf20Sopenharmony_ci		snd_emu8000_update_reverb_mode(hw);
4828c2ecf20Sopenharmony_ci		break;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	case _EMUX_OSS_CHORUS_MODE:
4858c2ecf20Sopenharmony_ci		hw->chorus_mode = p1;
4868c2ecf20Sopenharmony_ci		snd_emu8000_update_chorus_mode(hw);
4878c2ecf20Sopenharmony_ci		break;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	case _EMUX_OSS_INITIALIZE_CHIP:
4908c2ecf20Sopenharmony_ci		/* snd_emu8000_init(hw); */ /*ignored*/
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	case _EMUX_OSS_EQUALIZER:
4948c2ecf20Sopenharmony_ci		hw->bass_level = p1;
4958c2ecf20Sopenharmony_ci		hw->treble_level = p2;
4968c2ecf20Sopenharmony_ci		snd_emu8000_update_equalizer(hw);
4978c2ecf20Sopenharmony_ci		break;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci#endif
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/*
5058c2ecf20Sopenharmony_ci * additional patch keys
5068c2ecf20Sopenharmony_ci */
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci#define SNDRV_EMU8000_LOAD_CHORUS_FX	0x10	/* optarg=mode */
5098c2ecf20Sopenharmony_ci#define SNDRV_EMU8000_LOAD_REVERB_FX	0x11	/* optarg=mode */
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci/*
5138c2ecf20Sopenharmony_ci * callback routine
5148c2ecf20Sopenharmony_ci */
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic int
5178c2ecf20Sopenharmony_ciload_fx(struct snd_emux *emu, int type, int mode, const void __user *buf, long len)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	struct snd_emu8000 *hw;
5208c2ecf20Sopenharmony_ci	hw = emu->hw;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	/* skip header */
5238c2ecf20Sopenharmony_ci	buf += 16;
5248c2ecf20Sopenharmony_ci	len -= 16;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	switch (type) {
5278c2ecf20Sopenharmony_ci	case SNDRV_EMU8000_LOAD_CHORUS_FX:
5288c2ecf20Sopenharmony_ci		return snd_emu8000_load_chorus_fx(hw, mode, buf, len);
5298c2ecf20Sopenharmony_ci	case SNDRV_EMU8000_LOAD_REVERB_FX:
5308c2ecf20Sopenharmony_ci		return snd_emu8000_load_reverb_fx(hw, mode, buf, len);
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci	return -EINVAL;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
535