162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
462306a36Sopenharmony_ci *     and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
562306a36Sopenharmony_ci *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Routines for control of EMU8000 chip
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/wait.h>
1162306a36Sopenharmony_ci#include <linux/sched/signal.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/ioport.h>
1462306a36Sopenharmony_ci#include <linux/export.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <sound/core.h>
1862306a36Sopenharmony_ci#include <sound/emu8000.h>
1962306a36Sopenharmony_ci#include <sound/emu8000_reg.h>
2062306a36Sopenharmony_ci#include <linux/uaccess.h>
2162306a36Sopenharmony_ci#include <linux/init.h>
2262306a36Sopenharmony_ci#include <sound/control.h>
2362306a36Sopenharmony_ci#include <sound/initval.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * emu8000 register controls
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * The following routines read and write registers on the emu8000.  They
3162306a36Sopenharmony_ci * should always be called via the EMU8000*READ/WRITE macros and never
3262306a36Sopenharmony_ci * directly.  The macros handle the port number and command word.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci/* Write a word */
3562306a36Sopenharmony_civoid snd_emu8000_poke(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	unsigned long flags;
3862306a36Sopenharmony_ci	spin_lock_irqsave(&emu->reg_lock, flags);
3962306a36Sopenharmony_ci	if (reg != emu->last_reg) {
4062306a36Sopenharmony_ci		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
4162306a36Sopenharmony_ci		emu->last_reg = reg;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci	outw((unsigned short)val, port); /* Send data */
4462306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->reg_lock, flags);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Read a word */
4862306a36Sopenharmony_ciunsigned short snd_emu8000_peek(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	unsigned short res;
5162306a36Sopenharmony_ci	unsigned long flags;
5262306a36Sopenharmony_ci	spin_lock_irqsave(&emu->reg_lock, flags);
5362306a36Sopenharmony_ci	if (reg != emu->last_reg) {
5462306a36Sopenharmony_ci		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
5562306a36Sopenharmony_ci		emu->last_reg = reg;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci	res = inw(port);	/* Read data */
5862306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->reg_lock, flags);
5962306a36Sopenharmony_ci	return res;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Write a double word */
6362306a36Sopenharmony_civoid snd_emu8000_poke_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	unsigned long flags;
6662306a36Sopenharmony_ci	spin_lock_irqsave(&emu->reg_lock, flags);
6762306a36Sopenharmony_ci	if (reg != emu->last_reg) {
6862306a36Sopenharmony_ci		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
6962306a36Sopenharmony_ci		emu->last_reg = reg;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci	outw((unsigned short)val, port); /* Send low word of data */
7262306a36Sopenharmony_ci	outw((unsigned short)(val>>16), port+2); /* Send high word of data */
7362306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->reg_lock, flags);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* Read a double word */
7762306a36Sopenharmony_ciunsigned int snd_emu8000_peek_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	unsigned short low;
8062306a36Sopenharmony_ci	unsigned int res;
8162306a36Sopenharmony_ci	unsigned long flags;
8262306a36Sopenharmony_ci	spin_lock_irqsave(&emu->reg_lock, flags);
8362306a36Sopenharmony_ci	if (reg != emu->last_reg) {
8462306a36Sopenharmony_ci		outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
8562306a36Sopenharmony_ci		emu->last_reg = reg;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	low = inw(port);	/* Read low word of data */
8862306a36Sopenharmony_ci	res = low + (inw(port+2) << 16);
8962306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->reg_lock, flags);
9062306a36Sopenharmony_ci	return res;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*
9462306a36Sopenharmony_ci * Set up / close a channel to be used for DMA.
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_ci/*exported*/ void
9762306a36Sopenharmony_cisnd_emu8000_dma_chan(struct snd_emu8000 *emu, int ch, int mode)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	unsigned right_bit = (mode & EMU8000_RAM_RIGHT) ? 0x01000000 : 0;
10062306a36Sopenharmony_ci	mode &= EMU8000_RAM_MODE_MASK;
10162306a36Sopenharmony_ci	if (mode == EMU8000_RAM_CLOSE) {
10262306a36Sopenharmony_ci		EMU8000_CCCA_WRITE(emu, ch, 0);
10362306a36Sopenharmony_ci		EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F);
10462306a36Sopenharmony_ci		return;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci	EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
10762306a36Sopenharmony_ci	EMU8000_VTFT_WRITE(emu, ch, 0);
10862306a36Sopenharmony_ci	EMU8000_CVCF_WRITE(emu, ch, 0);
10962306a36Sopenharmony_ci	EMU8000_PTRX_WRITE(emu, ch, 0x40000000);
11062306a36Sopenharmony_ci	EMU8000_CPF_WRITE(emu, ch, 0x40000000);
11162306a36Sopenharmony_ci	EMU8000_PSST_WRITE(emu, ch, 0);
11262306a36Sopenharmony_ci	EMU8000_CSL_WRITE(emu, ch, 0);
11362306a36Sopenharmony_ci	if (mode == EMU8000_RAM_WRITE) /* DMA write */
11462306a36Sopenharmony_ci		EMU8000_CCCA_WRITE(emu, ch, 0x06000000 | right_bit);
11562306a36Sopenharmony_ci	else	   /* DMA read */
11662306a36Sopenharmony_ci		EMU8000_CCCA_WRITE(emu, ch, 0x04000000 | right_bit);
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/*
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_cistatic void
12262306a36Sopenharmony_cisnd_emu8000_read_wait(struct snd_emu8000 *emu)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
12562306a36Sopenharmony_ci		schedule_timeout_interruptible(1);
12662306a36Sopenharmony_ci		if (signal_pending(current))
12762306a36Sopenharmony_ci			break;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_cistatic void
13462306a36Sopenharmony_cisnd_emu8000_write_wait(struct snd_emu8000 *emu)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
13762306a36Sopenharmony_ci		schedule_timeout_interruptible(1);
13862306a36Sopenharmony_ci		if (signal_pending(current))
13962306a36Sopenharmony_ci			break;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * detect a card at the given port
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_cistatic int
14762306a36Sopenharmony_cisnd_emu8000_detect(struct snd_emu8000 *emu)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	/* Initialise */
15062306a36Sopenharmony_ci	EMU8000_HWCF1_WRITE(emu, 0x0059);
15162306a36Sopenharmony_ci	EMU8000_HWCF2_WRITE(emu, 0x0020);
15262306a36Sopenharmony_ci	EMU8000_HWCF3_WRITE(emu, 0x0000);
15362306a36Sopenharmony_ci	/* Check for a recognisable emu8000 */
15462306a36Sopenharmony_ci	/*
15562306a36Sopenharmony_ci	if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c)
15662306a36Sopenharmony_ci		return -ENODEV;
15762306a36Sopenharmony_ci		*/
15862306a36Sopenharmony_ci	if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058)
15962306a36Sopenharmony_ci		return -ENODEV;
16062306a36Sopenharmony_ci	if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003)
16162306a36Sopenharmony_ci		return -ENODEV;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	snd_printdd("EMU8000 [0x%lx]: Synth chip found\n",
16462306a36Sopenharmony_ci                    emu->port1);
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/*
17062306a36Sopenharmony_ci * intiailize audio channels
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistatic void
17362306a36Sopenharmony_ciinit_audio(struct snd_emu8000 *emu)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	int ch;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* turn off envelope engines */
17862306a36Sopenharmony_ci	for (ch = 0; ch < EMU8000_CHANNELS; ch++)
17962306a36Sopenharmony_ci		EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* reset all other parameters to zero */
18262306a36Sopenharmony_ci	for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
18362306a36Sopenharmony_ci		EMU8000_ENVVOL_WRITE(emu, ch, 0);
18462306a36Sopenharmony_ci		EMU8000_ENVVAL_WRITE(emu, ch, 0);
18562306a36Sopenharmony_ci		EMU8000_DCYSUS_WRITE(emu, ch, 0);
18662306a36Sopenharmony_ci		EMU8000_ATKHLDV_WRITE(emu, ch, 0);
18762306a36Sopenharmony_ci		EMU8000_LFO1VAL_WRITE(emu, ch, 0);
18862306a36Sopenharmony_ci		EMU8000_ATKHLD_WRITE(emu, ch, 0);
18962306a36Sopenharmony_ci		EMU8000_LFO2VAL_WRITE(emu, ch, 0);
19062306a36Sopenharmony_ci		EMU8000_IP_WRITE(emu, ch, 0);
19162306a36Sopenharmony_ci		EMU8000_IFATN_WRITE(emu, ch, 0);
19262306a36Sopenharmony_ci		EMU8000_PEFE_WRITE(emu, ch, 0);
19362306a36Sopenharmony_ci		EMU8000_FMMOD_WRITE(emu, ch, 0);
19462306a36Sopenharmony_ci		EMU8000_TREMFRQ_WRITE(emu, ch, 0);
19562306a36Sopenharmony_ci		EMU8000_FM2FRQ2_WRITE(emu, ch, 0);
19662306a36Sopenharmony_ci		EMU8000_PTRX_WRITE(emu, ch, 0);
19762306a36Sopenharmony_ci		EMU8000_VTFT_WRITE(emu, ch, 0);
19862306a36Sopenharmony_ci		EMU8000_PSST_WRITE(emu, ch, 0);
19962306a36Sopenharmony_ci		EMU8000_CSL_WRITE(emu, ch, 0);
20062306a36Sopenharmony_ci		EMU8000_CCCA_WRITE(emu, ch, 0);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
20462306a36Sopenharmony_ci		EMU8000_CPF_WRITE(emu, ch, 0);
20562306a36Sopenharmony_ci		EMU8000_CVCF_WRITE(emu, ch, 0);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci/*
21162306a36Sopenharmony_ci * initialize DMA address
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_cistatic void
21462306a36Sopenharmony_ciinit_dma(struct snd_emu8000 *emu)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	EMU8000_SMALR_WRITE(emu, 0);
21762306a36Sopenharmony_ci	EMU8000_SMARR_WRITE(emu, 0);
21862306a36Sopenharmony_ci	EMU8000_SMALW_WRITE(emu, 0);
21962306a36Sopenharmony_ci	EMU8000_SMARW_WRITE(emu, 0);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/*
22362306a36Sopenharmony_ci * initialization arrays; from ADIP
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_cistatic const unsigned short init1[128] = {
22662306a36Sopenharmony_ci	0x03ff, 0x0030,  0x07ff, 0x0130, 0x0bff, 0x0230,  0x0fff, 0x0330,
22762306a36Sopenharmony_ci	0x13ff, 0x0430,  0x17ff, 0x0530, 0x1bff, 0x0630,  0x1fff, 0x0730,
22862306a36Sopenharmony_ci	0x23ff, 0x0830,  0x27ff, 0x0930, 0x2bff, 0x0a30,  0x2fff, 0x0b30,
22962306a36Sopenharmony_ci	0x33ff, 0x0c30,  0x37ff, 0x0d30, 0x3bff, 0x0e30,  0x3fff, 0x0f30,
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	0x43ff, 0x0030,  0x47ff, 0x0130, 0x4bff, 0x0230,  0x4fff, 0x0330,
23262306a36Sopenharmony_ci	0x53ff, 0x0430,  0x57ff, 0x0530, 0x5bff, 0x0630,  0x5fff, 0x0730,
23362306a36Sopenharmony_ci	0x63ff, 0x0830,  0x67ff, 0x0930, 0x6bff, 0x0a30,  0x6fff, 0x0b30,
23462306a36Sopenharmony_ci	0x73ff, 0x0c30,  0x77ff, 0x0d30, 0x7bff, 0x0e30,  0x7fff, 0x0f30,
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	0x83ff, 0x0030,  0x87ff, 0x0130, 0x8bff, 0x0230,  0x8fff, 0x0330,
23762306a36Sopenharmony_ci	0x93ff, 0x0430,  0x97ff, 0x0530, 0x9bff, 0x0630,  0x9fff, 0x0730,
23862306a36Sopenharmony_ci	0xa3ff, 0x0830,  0xa7ff, 0x0930, 0xabff, 0x0a30,  0xafff, 0x0b30,
23962306a36Sopenharmony_ci	0xb3ff, 0x0c30,  0xb7ff, 0x0d30, 0xbbff, 0x0e30,  0xbfff, 0x0f30,
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	0xc3ff, 0x0030,  0xc7ff, 0x0130, 0xcbff, 0x0230,  0xcfff, 0x0330,
24262306a36Sopenharmony_ci	0xd3ff, 0x0430,  0xd7ff, 0x0530, 0xdbff, 0x0630,  0xdfff, 0x0730,
24362306a36Sopenharmony_ci	0xe3ff, 0x0830,  0xe7ff, 0x0930, 0xebff, 0x0a30,  0xefff, 0x0b30,
24462306a36Sopenharmony_ci	0xf3ff, 0x0c30,  0xf7ff, 0x0d30, 0xfbff, 0x0e30,  0xffff, 0x0f30,
24562306a36Sopenharmony_ci};
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic const unsigned short init2[128] = {
24862306a36Sopenharmony_ci	0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
24962306a36Sopenharmony_ci	0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
25062306a36Sopenharmony_ci	0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
25162306a36Sopenharmony_ci	0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
25462306a36Sopenharmony_ci	0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
25562306a36Sopenharmony_ci	0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
25662306a36Sopenharmony_ci	0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
25962306a36Sopenharmony_ci	0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
26062306a36Sopenharmony_ci	0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
26162306a36Sopenharmony_ci	0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
26462306a36Sopenharmony_ci	0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
26562306a36Sopenharmony_ci	0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
26662306a36Sopenharmony_ci	0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic const unsigned short init3[128] = {
27062306a36Sopenharmony_ci	0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
27162306a36Sopenharmony_ci	0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
27262306a36Sopenharmony_ci	0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
27362306a36Sopenharmony_ci	0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
27662306a36Sopenharmony_ci	0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
27762306a36Sopenharmony_ci	0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
27862306a36Sopenharmony_ci	0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
28162306a36Sopenharmony_ci	0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
28262306a36Sopenharmony_ci	0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
28362306a36Sopenharmony_ci	0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
28662306a36Sopenharmony_ci	0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
28762306a36Sopenharmony_ci	0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
28862306a36Sopenharmony_ci	0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
28962306a36Sopenharmony_ci};
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic const unsigned short init4[128] = {
29262306a36Sopenharmony_ci	0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
29362306a36Sopenharmony_ci	0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
29462306a36Sopenharmony_ci	0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
29562306a36Sopenharmony_ci	0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
29862306a36Sopenharmony_ci	0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
29962306a36Sopenharmony_ci	0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
30062306a36Sopenharmony_ci	0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
30362306a36Sopenharmony_ci	0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
30462306a36Sopenharmony_ci	0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
30562306a36Sopenharmony_ci	0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
30862306a36Sopenharmony_ci	0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
30962306a36Sopenharmony_ci	0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
31062306a36Sopenharmony_ci	0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/* send an initialization array
31462306a36Sopenharmony_ci * Taken from the oss driver, not obvious from the doc how this
31562306a36Sopenharmony_ci * is meant to work
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_cistatic void
31862306a36Sopenharmony_cisend_array(struct snd_emu8000 *emu, const unsigned short *data, int size)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	int i;
32162306a36Sopenharmony_ci	const unsigned short *p;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	p = data;
32462306a36Sopenharmony_ci	for (i = 0; i < size; i++, p++)
32562306a36Sopenharmony_ci		EMU8000_INIT1_WRITE(emu, i, *p);
32662306a36Sopenharmony_ci	for (i = 0; i < size; i++, p++)
32762306a36Sopenharmony_ci		EMU8000_INIT2_WRITE(emu, i, *p);
32862306a36Sopenharmony_ci	for (i = 0; i < size; i++, p++)
32962306a36Sopenharmony_ci		EMU8000_INIT3_WRITE(emu, i, *p);
33062306a36Sopenharmony_ci	for (i = 0; i < size; i++, p++)
33162306a36Sopenharmony_ci		EMU8000_INIT4_WRITE(emu, i, *p);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci/*
33662306a36Sopenharmony_ci * Send initialization arrays to start up, this just follows the
33762306a36Sopenharmony_ci * initialisation sequence in the adip.
33862306a36Sopenharmony_ci */
33962306a36Sopenharmony_cistatic void
34062306a36Sopenharmony_ciinit_arrays(struct snd_emu8000 *emu)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	send_array(emu, init1, ARRAY_SIZE(init1)/4);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	msleep((1024 * 1000) / 44100); /* wait for 1024 clocks */
34562306a36Sopenharmony_ci	send_array(emu, init2, ARRAY_SIZE(init2)/4);
34662306a36Sopenharmony_ci	send_array(emu, init3, ARRAY_SIZE(init3)/4);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	EMU8000_HWCF4_WRITE(emu, 0);
34962306a36Sopenharmony_ci	EMU8000_HWCF5_WRITE(emu, 0x83);
35062306a36Sopenharmony_ci	EMU8000_HWCF6_WRITE(emu, 0x8000);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	send_array(emu, init4, ARRAY_SIZE(init4)/4);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci#define UNIQUE_ID1	0xa5b9
35762306a36Sopenharmony_ci#define UNIQUE_ID2	0x9d53
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/*
36062306a36Sopenharmony_ci * Size the onboard memory.
36162306a36Sopenharmony_ci * This is written so as not to need arbitrary delays after the write. It
36262306a36Sopenharmony_ci * seems that the only way to do this is to use the one channel and keep
36362306a36Sopenharmony_ci * reallocating between read and write.
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_cistatic void
36662306a36Sopenharmony_cisize_dram(struct snd_emu8000 *emu)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	int i, size;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (emu->dram_checked)
37162306a36Sopenharmony_ci		return;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	size = 0;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* write out a magic number */
37662306a36Sopenharmony_ci	snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);
37762306a36Sopenharmony_ci	snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ);
37862306a36Sopenharmony_ci	EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
37962306a36Sopenharmony_ci	EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
38062306a36Sopenharmony_ci	snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
38162306a36Sopenharmony_ci	snd_emu8000_write_wait(emu);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/*
38462306a36Sopenharmony_ci	 * Detect first 512 KiB.  If a write succeeds at the beginning of a
38562306a36Sopenharmony_ci	 * 512 KiB page we assume that the whole page is there.
38662306a36Sopenharmony_ci	 */
38762306a36Sopenharmony_ci	EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
38862306a36Sopenharmony_ci	EMU8000_SMLD_READ(emu); /* discard stale data  */
38962306a36Sopenharmony_ci	if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
39062306a36Sopenharmony_ci		goto skip_detect;   /* No RAM */
39162306a36Sopenharmony_ci	snd_emu8000_read_wait(emu);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	for (size = 512 * 1024; size < EMU8000_MAX_DRAM; size += 512 * 1024) {
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		/* Write a unique data on the test address.
39662306a36Sopenharmony_ci		 * if the address is out of range, the data is written on
39762306a36Sopenharmony_ci		 * 0x200000(=EMU8000_DRAM_OFFSET).  Then the id word is
39862306a36Sopenharmony_ci		 * changed by this data.
39962306a36Sopenharmony_ci		 */
40062306a36Sopenharmony_ci		/*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/
40162306a36Sopenharmony_ci		EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
40262306a36Sopenharmony_ci		EMU8000_SMLD_WRITE(emu, UNIQUE_ID2);
40362306a36Sopenharmony_ci		snd_emu8000_write_wait(emu);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		/*
40662306a36Sopenharmony_ci		 * read the data on the just written DRAM address
40762306a36Sopenharmony_ci		 * if not the same then we have reached the end of ram.
40862306a36Sopenharmony_ci		 */
40962306a36Sopenharmony_ci		/*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/
41062306a36Sopenharmony_ci		EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
41162306a36Sopenharmony_ci		/*snd_emu8000_read_wait(emu);*/
41262306a36Sopenharmony_ci		EMU8000_SMLD_READ(emu); /* discard stale data  */
41362306a36Sopenharmony_ci		if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
41462306a36Sopenharmony_ci			break; /* no memory at this address */
41562306a36Sopenharmony_ci		snd_emu8000_read_wait(emu);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		/*
41862306a36Sopenharmony_ci		 * If it is the same it could be that the address just
41962306a36Sopenharmony_ci		 * wraps back to the beginning; so check to see if the
42062306a36Sopenharmony_ci		 * initial value has been overwritten.
42162306a36Sopenharmony_ci		 */
42262306a36Sopenharmony_ci		EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
42362306a36Sopenharmony_ci		EMU8000_SMLD_READ(emu); /* discard stale data  */
42462306a36Sopenharmony_ci		if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
42562306a36Sopenharmony_ci			break; /* we must have wrapped around */
42662306a36Sopenharmony_ci		snd_emu8000_read_wait(emu);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		/* Otherwise, it's valid memory. */
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ciskip_detect:
43262306a36Sopenharmony_ci	/* wait until FULL bit in SMAxW register is false */
43362306a36Sopenharmony_ci	for (i = 0; i < 10000; i++) {
43462306a36Sopenharmony_ci		if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0)
43562306a36Sopenharmony_ci			break;
43662306a36Sopenharmony_ci		schedule_timeout_interruptible(1);
43762306a36Sopenharmony_ci		if (signal_pending(current))
43862306a36Sopenharmony_ci			break;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci	snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE);
44162306a36Sopenharmony_ci	snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	pr_info("EMU8000 [0x%lx]: %d KiB on-board DRAM detected\n",
44462306a36Sopenharmony_ci		    emu->port1, size/1024);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	emu->mem_size = size;
44762306a36Sopenharmony_ci	emu->dram_checked = 1;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci/*
45262306a36Sopenharmony_ci * Initiailise the FM section.  You have to do this to use sample RAM
45362306a36Sopenharmony_ci * and therefore lose 2 voices.
45462306a36Sopenharmony_ci */
45562306a36Sopenharmony_ci/*exported*/ void
45662306a36Sopenharmony_cisnd_emu8000_init_fm(struct snd_emu8000 *emu)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	unsigned long flags;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* Initialize the last two channels for DRAM refresh and producing
46162306a36Sopenharmony_ci	   the reverb and chorus effects for Yamaha OPL-3 synthesizer */
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* 31: FM left channel, 0xffffe0-0xffffe8 */
46462306a36Sopenharmony_ci	EMU8000_DCYSUSV_WRITE(emu, 30, 0x80);
46562306a36Sopenharmony_ci	EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */
46662306a36Sopenharmony_ci	EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24));
46762306a36Sopenharmony_ci	EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8));
46862306a36Sopenharmony_ci	EMU8000_CPF_WRITE(emu, 30, 0);
46962306a36Sopenharmony_ci	EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* 32: FM right channel, 0xfffff0-0xfffff8 */
47262306a36Sopenharmony_ci	EMU8000_DCYSUSV_WRITE(emu, 31, 0x80);
47362306a36Sopenharmony_ci	EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */
47462306a36Sopenharmony_ci	EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24));
47562306a36Sopenharmony_ci	EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8));
47662306a36Sopenharmony_ci	EMU8000_CPF_WRITE(emu, 31, 0x8000);
47762306a36Sopenharmony_ci	EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	spin_lock_irqsave(&emu->reg_lock, flags);
48262306a36Sopenharmony_ci	while (!(inw(EMU8000_PTR(emu)) & 0x1000))
48362306a36Sopenharmony_ci		;
48462306a36Sopenharmony_ci	while ((inw(EMU8000_PTR(emu)) & 0x1000))
48562306a36Sopenharmony_ci		;
48662306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->reg_lock, flags);
48762306a36Sopenharmony_ci	snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828);
48862306a36Sopenharmony_ci	/* this is really odd part.. */
48962306a36Sopenharmony_ci	outb(0x3C, EMU8000_PTR(emu));
49062306a36Sopenharmony_ci	outb(0, EMU8000_DATA1(emu));
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/* skew volume & cutoff */
49362306a36Sopenharmony_ci	EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF);
49462306a36Sopenharmony_ci	EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF);
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/*
49962306a36Sopenharmony_ci * The main initialization routine.
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_cistatic void
50262306a36Sopenharmony_cisnd_emu8000_init_hw(struct snd_emu8000 *emu)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	int i;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	emu->last_reg = 0xffff; /* reset the last register index */
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* initialize hardware configuration */
50962306a36Sopenharmony_ci	EMU8000_HWCF1_WRITE(emu, 0x0059);
51062306a36Sopenharmony_ci	EMU8000_HWCF2_WRITE(emu, 0x0020);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* disable audio; this seems to reduce a clicking noise a bit.. */
51362306a36Sopenharmony_ci	EMU8000_HWCF3_WRITE(emu, 0);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* initialize audio channels */
51662306a36Sopenharmony_ci	init_audio(emu);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* initialize DMA */
51962306a36Sopenharmony_ci	init_dma(emu);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* initialize init arrays */
52262306a36Sopenharmony_ci	init_arrays(emu);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/*
52562306a36Sopenharmony_ci	 * Initialize the FM section of the AWE32, this is needed
52662306a36Sopenharmony_ci	 * for DRAM refresh as well
52762306a36Sopenharmony_ci	 */
52862306a36Sopenharmony_ci	snd_emu8000_init_fm(emu);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* terminate all voices */
53162306a36Sopenharmony_ci	for (i = 0; i < EMU8000_DRAM_VOICES; i++)
53262306a36Sopenharmony_ci		EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* check DRAM memory size */
53562306a36Sopenharmony_ci	size_dram(emu);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* enable audio */
53862306a36Sopenharmony_ci	EMU8000_HWCF3_WRITE(emu, 0x4);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* set equzlier, chorus and reverb modes */
54162306a36Sopenharmony_ci	snd_emu8000_update_equalizer(emu);
54262306a36Sopenharmony_ci	snd_emu8000_update_chorus_mode(emu);
54362306a36Sopenharmony_ci	snd_emu8000_update_reverb_mode(emu);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/*----------------------------------------------------------------
54862306a36Sopenharmony_ci * Bass/Treble Equalizer
54962306a36Sopenharmony_ci *----------------------------------------------------------------*/
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic const unsigned short bass_parm[12][3] = {
55262306a36Sopenharmony_ci	{0xD26A, 0xD36A, 0x0000}, /* -12 dB */
55362306a36Sopenharmony_ci	{0xD25B, 0xD35B, 0x0000}, /*  -8 */
55462306a36Sopenharmony_ci	{0xD24C, 0xD34C, 0x0000}, /*  -6 */
55562306a36Sopenharmony_ci	{0xD23D, 0xD33D, 0x0000}, /*  -4 */
55662306a36Sopenharmony_ci	{0xD21F, 0xD31F, 0x0000}, /*  -2 */
55762306a36Sopenharmony_ci	{0xC208, 0xC308, 0x0001}, /*   0 (HW default) */
55862306a36Sopenharmony_ci	{0xC219, 0xC319, 0x0001}, /*  +2 */
55962306a36Sopenharmony_ci	{0xC22A, 0xC32A, 0x0001}, /*  +4 */
56062306a36Sopenharmony_ci	{0xC24C, 0xC34C, 0x0001}, /*  +6 */
56162306a36Sopenharmony_ci	{0xC26E, 0xC36E, 0x0001}, /*  +8 */
56262306a36Sopenharmony_ci	{0xC248, 0xC384, 0x0002}, /* +10 */
56362306a36Sopenharmony_ci	{0xC26A, 0xC36A, 0x0002}, /* +12 dB */
56462306a36Sopenharmony_ci};
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic const unsigned short treble_parm[12][9] = {
56762306a36Sopenharmony_ci	{0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
56862306a36Sopenharmony_ci	{0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
56962306a36Sopenharmony_ci	{0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
57062306a36Sopenharmony_ci	{0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
57162306a36Sopenharmony_ci	{0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
57262306a36Sopenharmony_ci	{0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
57362306a36Sopenharmony_ci	{0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
57462306a36Sopenharmony_ci	{0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
57562306a36Sopenharmony_ci	{0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
57662306a36Sopenharmony_ci	{0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
57762306a36Sopenharmony_ci	{0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
57862306a36Sopenharmony_ci	{0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}  /* +12 dB */
57962306a36Sopenharmony_ci};
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci/*
58362306a36Sopenharmony_ci * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
58462306a36Sopenharmony_ci */
58562306a36Sopenharmony_ci/*exported*/ void
58662306a36Sopenharmony_cisnd_emu8000_update_equalizer(struct snd_emu8000 *emu)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	unsigned short w;
58962306a36Sopenharmony_ci	int bass = emu->bass_level;
59062306a36Sopenharmony_ci	int treble = emu->treble_level;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
59362306a36Sopenharmony_ci		return;
59462306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]);
59562306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]);
59662306a36Sopenharmony_ci	EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]);
59762306a36Sopenharmony_ci	EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]);
59862306a36Sopenharmony_ci	EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]);
59962306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]);
60062306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]);
60162306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]);
60262306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]);
60362306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]);
60462306a36Sopenharmony_ci	w = bass_parm[bass][2] + treble_parm[treble][8];
60562306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262));
60662306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362));
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci/*----------------------------------------------------------------
61162306a36Sopenharmony_ci * Chorus mode control
61262306a36Sopenharmony_ci *----------------------------------------------------------------*/
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci/*
61562306a36Sopenharmony_ci * chorus mode parameters
61662306a36Sopenharmony_ci */
61762306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_1		0
61862306a36Sopenharmony_ci#define	SNDRV_EMU8000_CHORUS_2		1
61962306a36Sopenharmony_ci#define	SNDRV_EMU8000_CHORUS_3		2
62062306a36Sopenharmony_ci#define	SNDRV_EMU8000_CHORUS_4		3
62162306a36Sopenharmony_ci#define	SNDRV_EMU8000_CHORUS_FEEDBACK	4
62262306a36Sopenharmony_ci#define	SNDRV_EMU8000_CHORUS_FLANGER	5
62362306a36Sopenharmony_ci#define	SNDRV_EMU8000_CHORUS_SHORTDELAY	6
62462306a36Sopenharmony_ci#define	SNDRV_EMU8000_CHORUS_SHORTDELAY2	7
62562306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_PREDEFINED	8
62662306a36Sopenharmony_ci/* user can define chorus modes up to 32 */
62762306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_NUMBERS	32
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistruct soundfont_chorus_fx {
63062306a36Sopenharmony_ci	unsigned short feedback;	/* feedback level (0xE600-0xE6FF) */
63162306a36Sopenharmony_ci	unsigned short delay_offset;	/* delay (0-0x0DA3) [1/44100 sec] */
63262306a36Sopenharmony_ci	unsigned short lfo_depth;	/* LFO depth (0xBC00-0xBCFF) */
63362306a36Sopenharmony_ci	unsigned int delay;	/* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
63462306a36Sopenharmony_ci	unsigned int lfo_freq;		/* LFO freq LFO freq (0-0xFFFFFFFF) */
63562306a36Sopenharmony_ci};
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
63862306a36Sopenharmony_cistatic char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
63962306a36Sopenharmony_cistatic struct soundfont_chorus_fx chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = {
64062306a36Sopenharmony_ci	{0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
64162306a36Sopenharmony_ci	{0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
64262306a36Sopenharmony_ci	{0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
64362306a36Sopenharmony_ci	{0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
64462306a36Sopenharmony_ci	{0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
64562306a36Sopenharmony_ci	{0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
64662306a36Sopenharmony_ci	{0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */
64762306a36Sopenharmony_ci	{0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */
64862306a36Sopenharmony_ci};
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci/*exported*/ int
65162306a36Sopenharmony_cisnd_emu8000_load_chorus_fx(struct snd_emu8000 *emu, int mode, const void __user *buf, long len)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	struct soundfont_chorus_fx rec;
65462306a36Sopenharmony_ci	if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) {
65562306a36Sopenharmony_ci		snd_printk(KERN_WARNING "invalid chorus mode %d for uploading\n", mode);
65662306a36Sopenharmony_ci		return -EINVAL;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci	if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
65962306a36Sopenharmony_ci		return -EFAULT;
66062306a36Sopenharmony_ci	chorus_parm[mode] = rec;
66162306a36Sopenharmony_ci	chorus_defined[mode] = 1;
66262306a36Sopenharmony_ci	return 0;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci/*exported*/ void
66662306a36Sopenharmony_cisnd_emu8000_update_chorus_mode(struct snd_emu8000 *emu)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	int effect = emu->chorus_mode;
66962306a36Sopenharmony_ci	if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS ||
67062306a36Sopenharmony_ci	    (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect]))
67162306a36Sopenharmony_ci		return;
67262306a36Sopenharmony_ci	EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback);
67362306a36Sopenharmony_ci	EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset);
67462306a36Sopenharmony_ci	EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth);
67562306a36Sopenharmony_ci	EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay);
67662306a36Sopenharmony_ci	EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq);
67762306a36Sopenharmony_ci	EMU8000_HWCF6_WRITE(emu, 0x8000);
67862306a36Sopenharmony_ci	EMU8000_HWCF7_WRITE(emu, 0x0000);
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/*----------------------------------------------------------------
68262306a36Sopenharmony_ci * Reverb mode control
68362306a36Sopenharmony_ci *----------------------------------------------------------------*/
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci/*
68662306a36Sopenharmony_ci * reverb mode parameters
68762306a36Sopenharmony_ci */
68862306a36Sopenharmony_ci#define	SNDRV_EMU8000_REVERB_ROOM1	0
68962306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_ROOM2	1
69062306a36Sopenharmony_ci#define	SNDRV_EMU8000_REVERB_ROOM3	2
69162306a36Sopenharmony_ci#define	SNDRV_EMU8000_REVERB_HALL1	3
69262306a36Sopenharmony_ci#define	SNDRV_EMU8000_REVERB_HALL2	4
69362306a36Sopenharmony_ci#define	SNDRV_EMU8000_REVERB_PLATE	5
69462306a36Sopenharmony_ci#define	SNDRV_EMU8000_REVERB_DELAY	6
69562306a36Sopenharmony_ci#define	SNDRV_EMU8000_REVERB_PANNINGDELAY 7
69662306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_PREDEFINED	8
69762306a36Sopenharmony_ci/* user can define reverb modes up to 32 */
69862306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_NUMBERS	32
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistruct soundfont_reverb_fx {
70162306a36Sopenharmony_ci	unsigned short parms[28];
70262306a36Sopenharmony_ci};
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci/* reverb mode settings; write the following 28 data of 16 bit length
70562306a36Sopenharmony_ci *   on the corresponding ports in the reverb_cmds array
70662306a36Sopenharmony_ci */
70762306a36Sopenharmony_cistatic char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
70862306a36Sopenharmony_cistatic struct soundfont_reverb_fx reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = {
70962306a36Sopenharmony_ci{{  /* room 1 */
71062306a36Sopenharmony_ci	0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
71162306a36Sopenharmony_ci	0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
71262306a36Sopenharmony_ci	0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
71362306a36Sopenharmony_ci	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
71462306a36Sopenharmony_ci}},
71562306a36Sopenharmony_ci{{  /* room 2 */
71662306a36Sopenharmony_ci	0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
71762306a36Sopenharmony_ci	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
71862306a36Sopenharmony_ci	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
71962306a36Sopenharmony_ci	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
72062306a36Sopenharmony_ci}},
72162306a36Sopenharmony_ci{{  /* room 3 */
72262306a36Sopenharmony_ci	0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
72362306a36Sopenharmony_ci	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
72462306a36Sopenharmony_ci	0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
72562306a36Sopenharmony_ci	0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
72662306a36Sopenharmony_ci}},
72762306a36Sopenharmony_ci{{  /* hall 1 */
72862306a36Sopenharmony_ci	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
72962306a36Sopenharmony_ci	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
73062306a36Sopenharmony_ci	0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
73162306a36Sopenharmony_ci	0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
73262306a36Sopenharmony_ci}},
73362306a36Sopenharmony_ci{{  /* hall 2 */
73462306a36Sopenharmony_ci	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
73562306a36Sopenharmony_ci	0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
73662306a36Sopenharmony_ci	0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
73762306a36Sopenharmony_ci	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
73862306a36Sopenharmony_ci}},
73962306a36Sopenharmony_ci{{  /* plate */
74062306a36Sopenharmony_ci	0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
74162306a36Sopenharmony_ci	0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
74262306a36Sopenharmony_ci	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
74362306a36Sopenharmony_ci	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
74462306a36Sopenharmony_ci}},
74562306a36Sopenharmony_ci{{  /* delay */
74662306a36Sopenharmony_ci	0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
74762306a36Sopenharmony_ci	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
74862306a36Sopenharmony_ci	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
74962306a36Sopenharmony_ci	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
75062306a36Sopenharmony_ci}},
75162306a36Sopenharmony_ci{{  /* panning delay */
75262306a36Sopenharmony_ci	0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
75362306a36Sopenharmony_ci	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
75462306a36Sopenharmony_ci	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
75562306a36Sopenharmony_ci	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
75662306a36Sopenharmony_ci}},
75762306a36Sopenharmony_ci};
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cienum { DATA1, DATA2 };
76062306a36Sopenharmony_ci#define AWE_INIT1(c)	EMU8000_CMD(2,c), DATA1
76162306a36Sopenharmony_ci#define AWE_INIT2(c)	EMU8000_CMD(2,c), DATA2
76262306a36Sopenharmony_ci#define AWE_INIT3(c)	EMU8000_CMD(3,c), DATA1
76362306a36Sopenharmony_ci#define AWE_INIT4(c)	EMU8000_CMD(3,c), DATA2
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic struct reverb_cmd_pair {
76662306a36Sopenharmony_ci	unsigned short cmd, port;
76762306a36Sopenharmony_ci} reverb_cmds[28] = {
76862306a36Sopenharmony_ci  {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
76962306a36Sopenharmony_ci  {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
77062306a36Sopenharmony_ci  {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
77162306a36Sopenharmony_ci  {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
77262306a36Sopenharmony_ci  {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
77362306a36Sopenharmony_ci  {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
77462306a36Sopenharmony_ci  {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
77562306a36Sopenharmony_ci};
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci/*exported*/ int
77862306a36Sopenharmony_cisnd_emu8000_load_reverb_fx(struct snd_emu8000 *emu, int mode, const void __user *buf, long len)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct soundfont_reverb_fx rec;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) {
78362306a36Sopenharmony_ci		snd_printk(KERN_WARNING "invalid reverb mode %d for uploading\n", mode);
78462306a36Sopenharmony_ci		return -EINVAL;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci	if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
78762306a36Sopenharmony_ci		return -EFAULT;
78862306a36Sopenharmony_ci	reverb_parm[mode] = rec;
78962306a36Sopenharmony_ci	reverb_defined[mode] = 1;
79062306a36Sopenharmony_ci	return 0;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci/*exported*/ void
79462306a36Sopenharmony_cisnd_emu8000_update_reverb_mode(struct snd_emu8000 *emu)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	int effect = emu->reverb_mode;
79762306a36Sopenharmony_ci	int i;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS ||
80062306a36Sopenharmony_ci	    (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect]))
80162306a36Sopenharmony_ci		return;
80262306a36Sopenharmony_ci	for (i = 0; i < 28; i++) {
80362306a36Sopenharmony_ci		int port;
80462306a36Sopenharmony_ci		if (reverb_cmds[i].port == DATA1)
80562306a36Sopenharmony_ci			port = EMU8000_DATA1(emu);
80662306a36Sopenharmony_ci		else
80762306a36Sopenharmony_ci			port = EMU8000_DATA2(emu);
80862306a36Sopenharmony_ci		snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]);
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci/*----------------------------------------------------------------
81462306a36Sopenharmony_ci * mixer interface
81562306a36Sopenharmony_ci *----------------------------------------------------------------*/
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci/*
81862306a36Sopenharmony_ci * bass/treble
81962306a36Sopenharmony_ci */
82062306a36Sopenharmony_cistatic int mixer_bass_treble_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
82362306a36Sopenharmony_ci	uinfo->count = 1;
82462306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
82562306a36Sopenharmony_ci	uinfo->value.integer.max = 11;
82662306a36Sopenharmony_ci	return 0;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic int mixer_bass_treble_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level;
83462306a36Sopenharmony_ci	return 0;
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_cistatic int mixer_bass_treble_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
84062306a36Sopenharmony_ci	unsigned long flags;
84162306a36Sopenharmony_ci	int change;
84262306a36Sopenharmony_ci	unsigned short val1;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	val1 = ucontrol->value.integer.value[0] % 12;
84562306a36Sopenharmony_ci	spin_lock_irqsave(&emu->control_lock, flags);
84662306a36Sopenharmony_ci	if (kcontrol->private_value) {
84762306a36Sopenharmony_ci		change = val1 != emu->treble_level;
84862306a36Sopenharmony_ci		emu->treble_level = val1;
84962306a36Sopenharmony_ci	} else {
85062306a36Sopenharmony_ci		change = val1 != emu->bass_level;
85162306a36Sopenharmony_ci		emu->bass_level = val1;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->control_lock, flags);
85462306a36Sopenharmony_ci	snd_emu8000_update_equalizer(emu);
85562306a36Sopenharmony_ci	return change;
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_bass_control =
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
86162306a36Sopenharmony_ci	.name = "Synth Tone Control - Bass",
86262306a36Sopenharmony_ci	.info = mixer_bass_treble_info,
86362306a36Sopenharmony_ci	.get = mixer_bass_treble_get,
86462306a36Sopenharmony_ci	.put = mixer_bass_treble_put,
86562306a36Sopenharmony_ci	.private_value = 0,
86662306a36Sopenharmony_ci};
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_treble_control =
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
87162306a36Sopenharmony_ci	.name = "Synth Tone Control - Treble",
87262306a36Sopenharmony_ci	.info = mixer_bass_treble_info,
87362306a36Sopenharmony_ci	.get = mixer_bass_treble_get,
87462306a36Sopenharmony_ci	.put = mixer_bass_treble_put,
87562306a36Sopenharmony_ci	.private_value = 1,
87662306a36Sopenharmony_ci};
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci/*
87962306a36Sopenharmony_ci * chorus/reverb mode
88062306a36Sopenharmony_ci */
88162306a36Sopenharmony_cistatic int mixer_chorus_reverb_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
88462306a36Sopenharmony_ci	uinfo->count = 1;
88562306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
88662306a36Sopenharmony_ci	uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1);
88762306a36Sopenharmony_ci	return 0;
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic int mixer_chorus_reverb_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
89162306a36Sopenharmony_ci{
89262306a36Sopenharmony_ci	struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode;
89562306a36Sopenharmony_ci	return 0;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic int mixer_chorus_reverb_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
90162306a36Sopenharmony_ci	unsigned long flags;
90262306a36Sopenharmony_ci	int change;
90362306a36Sopenharmony_ci	unsigned short val1;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	spin_lock_irqsave(&emu->control_lock, flags);
90662306a36Sopenharmony_ci	if (kcontrol->private_value) {
90762306a36Sopenharmony_ci		val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
90862306a36Sopenharmony_ci		change = val1 != emu->chorus_mode;
90962306a36Sopenharmony_ci		emu->chorus_mode = val1;
91062306a36Sopenharmony_ci	} else {
91162306a36Sopenharmony_ci		val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
91262306a36Sopenharmony_ci		change = val1 != emu->reverb_mode;
91362306a36Sopenharmony_ci		emu->reverb_mode = val1;
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->control_lock, flags);
91662306a36Sopenharmony_ci	if (change) {
91762306a36Sopenharmony_ci		if (kcontrol->private_value)
91862306a36Sopenharmony_ci			snd_emu8000_update_chorus_mode(emu);
91962306a36Sopenharmony_ci		else
92062306a36Sopenharmony_ci			snd_emu8000_update_reverb_mode(emu);
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci	return change;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_chorus_mode_control =
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
92862306a36Sopenharmony_ci	.name = "Chorus Mode",
92962306a36Sopenharmony_ci	.info = mixer_chorus_reverb_info,
93062306a36Sopenharmony_ci	.get = mixer_chorus_reverb_get,
93162306a36Sopenharmony_ci	.put = mixer_chorus_reverb_put,
93262306a36Sopenharmony_ci	.private_value = 1,
93362306a36Sopenharmony_ci};
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_reverb_mode_control =
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
93862306a36Sopenharmony_ci	.name = "Reverb Mode",
93962306a36Sopenharmony_ci	.info = mixer_chorus_reverb_info,
94062306a36Sopenharmony_ci	.get = mixer_chorus_reverb_get,
94162306a36Sopenharmony_ci	.put = mixer_chorus_reverb_put,
94262306a36Sopenharmony_ci	.private_value = 0,
94362306a36Sopenharmony_ci};
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci/*
94662306a36Sopenharmony_ci * FM OPL3 chorus/reverb depth
94762306a36Sopenharmony_ci */
94862306a36Sopenharmony_cistatic int mixer_fm_depth_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
95162306a36Sopenharmony_ci	uinfo->count = 1;
95262306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
95362306a36Sopenharmony_ci	uinfo->value.integer.max = 255;
95462306a36Sopenharmony_ci	return 0;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_cistatic int mixer_fm_depth_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth;
96262306a36Sopenharmony_ci	return 0;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic int mixer_fm_depth_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol);
96862306a36Sopenharmony_ci	unsigned long flags;
96962306a36Sopenharmony_ci	int change;
97062306a36Sopenharmony_ci	unsigned short val1;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	val1 = ucontrol->value.integer.value[0] % 256;
97362306a36Sopenharmony_ci	spin_lock_irqsave(&emu->control_lock, flags);
97462306a36Sopenharmony_ci	if (kcontrol->private_value) {
97562306a36Sopenharmony_ci		change = val1 != emu->fm_chorus_depth;
97662306a36Sopenharmony_ci		emu->fm_chorus_depth = val1;
97762306a36Sopenharmony_ci	} else {
97862306a36Sopenharmony_ci		change = val1 != emu->fm_reverb_depth;
97962306a36Sopenharmony_ci		emu->fm_reverb_depth = val1;
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci	spin_unlock_irqrestore(&emu->control_lock, flags);
98262306a36Sopenharmony_ci	if (change)
98362306a36Sopenharmony_ci		snd_emu8000_init_fm(emu);
98462306a36Sopenharmony_ci	return change;
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_fm_chorus_depth_control =
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
99062306a36Sopenharmony_ci	.name = "FM Chorus Depth",
99162306a36Sopenharmony_ci	.info = mixer_fm_depth_info,
99262306a36Sopenharmony_ci	.get = mixer_fm_depth_get,
99362306a36Sopenharmony_ci	.put = mixer_fm_depth_put,
99462306a36Sopenharmony_ci	.private_value = 1,
99562306a36Sopenharmony_ci};
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_fm_reverb_depth_control =
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
100062306a36Sopenharmony_ci	.name = "FM Reverb Depth",
100162306a36Sopenharmony_ci	.info = mixer_fm_depth_info,
100262306a36Sopenharmony_ci	.get = mixer_fm_depth_get,
100362306a36Sopenharmony_ci	.put = mixer_fm_depth_put,
100462306a36Sopenharmony_ci	.private_value = 0,
100562306a36Sopenharmony_ci};
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic const struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = {
100962306a36Sopenharmony_ci	&mixer_bass_control,
101062306a36Sopenharmony_ci	&mixer_treble_control,
101162306a36Sopenharmony_ci	&mixer_chorus_mode_control,
101262306a36Sopenharmony_ci	&mixer_reverb_mode_control,
101362306a36Sopenharmony_ci	&mixer_fm_chorus_depth_control,
101462306a36Sopenharmony_ci	&mixer_fm_reverb_depth_control,
101562306a36Sopenharmony_ci};
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci/*
101862306a36Sopenharmony_ci * create and attach mixer elements for WaveTable treble/bass controls
101962306a36Sopenharmony_ci */
102062306a36Sopenharmony_cistatic int
102162306a36Sopenharmony_cisnd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	struct snd_kcontrol *kctl;
102462306a36Sopenharmony_ci	int i, err = 0;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	if (snd_BUG_ON(!emu || !card))
102762306a36Sopenharmony_ci		return -EINVAL;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	spin_lock_init(&emu->control_lock);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	memset(emu->controls, 0, sizeof(emu->controls));
103262306a36Sopenharmony_ci	for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
103362306a36Sopenharmony_ci		kctl = snd_ctl_new1(mixer_defs[i], emu);
103462306a36Sopenharmony_ci		err = snd_ctl_add(card, kctl);
103562306a36Sopenharmony_ci		if (err < 0)
103662306a36Sopenharmony_ci			goto __error;
103762306a36Sopenharmony_ci		emu->controls[i] = kctl;
103862306a36Sopenharmony_ci	}
103962306a36Sopenharmony_ci	return 0;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci__error:
104262306a36Sopenharmony_ci	for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
104362306a36Sopenharmony_ci		if (emu->controls[i])
104462306a36Sopenharmony_ci			snd_ctl_remove(card, emu->controls[i]);
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci	return err;
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci/*
105062306a36Sopenharmony_ci * initialize and register emu8000 synth device.
105162306a36Sopenharmony_ci */
105262306a36Sopenharmony_ciint
105362306a36Sopenharmony_cisnd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports,
105462306a36Sopenharmony_ci		struct snd_seq_device **awe_ret)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	struct snd_seq_device *awe;
105762306a36Sopenharmony_ci	struct snd_emu8000 *hw;
105862306a36Sopenharmony_ci	int err;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (awe_ret)
106162306a36Sopenharmony_ci		*awe_ret = NULL;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (seq_ports <= 0)
106462306a36Sopenharmony_ci		return 0;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	hw = devm_kzalloc(card->dev, sizeof(*hw), GFP_KERNEL);
106762306a36Sopenharmony_ci	if (hw == NULL)
106862306a36Sopenharmony_ci		return -ENOMEM;
106962306a36Sopenharmony_ci	spin_lock_init(&hw->reg_lock);
107062306a36Sopenharmony_ci	hw->index = index;
107162306a36Sopenharmony_ci	hw->port1 = port;
107262306a36Sopenharmony_ci	hw->port2 = port + 0x400;
107362306a36Sopenharmony_ci	hw->port3 = port + 0x800;
107462306a36Sopenharmony_ci	if (!devm_request_region(card->dev, hw->port1, 4, "Emu8000-1") ||
107562306a36Sopenharmony_ci	    !devm_request_region(card->dev, hw->port2, 4, "Emu8000-2") ||
107662306a36Sopenharmony_ci	    !devm_request_region(card->dev, hw->port3, 4, "Emu8000-3")) {
107762306a36Sopenharmony_ci		snd_printk(KERN_ERR "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n", hw->port1, hw->port2, hw->port3);
107862306a36Sopenharmony_ci		return -EBUSY;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci	hw->mem_size = 0;
108162306a36Sopenharmony_ci	hw->card = card;
108262306a36Sopenharmony_ci	hw->seq_ports = seq_ports;
108362306a36Sopenharmony_ci	hw->bass_level = 5;
108462306a36Sopenharmony_ci	hw->treble_level = 9;
108562306a36Sopenharmony_ci	hw->chorus_mode = 2;
108662306a36Sopenharmony_ci	hw->reverb_mode = 4;
108762306a36Sopenharmony_ci	hw->fm_chorus_depth = 0;
108862306a36Sopenharmony_ci	hw->fm_reverb_depth = 0;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	if (snd_emu8000_detect(hw) < 0)
109162306a36Sopenharmony_ci		return -ENODEV;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	snd_emu8000_init_hw(hw);
109462306a36Sopenharmony_ci	err = snd_emu8000_create_mixer(card, hw);
109562306a36Sopenharmony_ci	if (err < 0)
109662306a36Sopenharmony_ci		return err;
109762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER)
109862306a36Sopenharmony_ci	if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000,
109962306a36Sopenharmony_ci			       sizeof(struct snd_emu8000*), &awe) >= 0) {
110062306a36Sopenharmony_ci		strcpy(awe->name, "EMU-8000");
110162306a36Sopenharmony_ci		*(struct snd_emu8000 **)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci#else
110462306a36Sopenharmony_ci	awe = NULL;
110562306a36Sopenharmony_ci#endif
110662306a36Sopenharmony_ci	if (awe_ret)
110762306a36Sopenharmony_ci		*awe_ret = awe;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return 0;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci/*
111462306a36Sopenharmony_ci * exported stuff
111562306a36Sopenharmony_ci */
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_poke);
111862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_peek);
111962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_poke_dw);
112062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_peek_dw);
112162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_dma_chan);
112262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_init_fm);
112362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_load_chorus_fx);
112462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_load_reverb_fx);
112562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_update_chorus_mode);
112662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_update_reverb_mode);
112762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_update_equalizer);
1128