xref: /kernel/linux/linux-6.6/sound/isa/gus/interwave.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Driver for AMD InterWave soundcard
462306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *   1999/07/22		Erik Inge Bolso <knan@mo.himolde.no>
762306a36Sopenharmony_ci *			* mixer group handlers
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/isa.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/pnp.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <asm/dma.h>
1762306a36Sopenharmony_ci#include <sound/core.h>
1862306a36Sopenharmony_ci#include <sound/gus.h>
1962306a36Sopenharmony_ci#include <sound/wss.h>
2062306a36Sopenharmony_ci#ifdef SNDRV_STB
2162306a36Sopenharmony_ci#include <sound/tea6330t.h>
2262306a36Sopenharmony_ci#endif
2362306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ
2462306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA
2562306a36Sopenharmony_ci#include <sound/initval.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
2862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2962306a36Sopenharmony_ci#ifndef SNDRV_STB
3062306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD InterWave");
3162306a36Sopenharmony_ci#else
3262306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD InterWave STB with TEA6330T");
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
3662306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
3762306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
3862306a36Sopenharmony_ci#ifdef CONFIG_PNP
3962306a36Sopenharmony_cistatic bool isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
4062306a36Sopenharmony_ci#endif
4162306a36Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x210,0x220,0x230,0x240,0x250,0x260 */
4262306a36Sopenharmony_ci#ifdef SNDRV_STB
4362306a36Sopenharmony_cistatic long port_tc[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x350,0x360,0x370,0x380 */
4462306a36Sopenharmony_ci#endif
4562306a36Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 2,3,5,9,11,12,15 */
4662306a36Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
4762306a36Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3,5,6,7 */
4862306a36Sopenharmony_cistatic int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
4962306a36Sopenharmony_ci				/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
5062306a36Sopenharmony_cistatic int midi[SNDRV_CARDS];
5162306a36Sopenharmony_cistatic int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
5262306a36Sopenharmony_cistatic int effect[SNDRV_CARDS];
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#ifdef SNDRV_STB
5562306a36Sopenharmony_ci#define PFX "interwave-stb: "
5662306a36Sopenharmony_ci#define INTERWAVE_DRIVER	"snd_interwave_stb"
5762306a36Sopenharmony_ci#define INTERWAVE_PNP_DRIVER	"interwave-stb"
5862306a36Sopenharmony_ci#else
5962306a36Sopenharmony_ci#define PFX "interwave: "
6062306a36Sopenharmony_ci#define INTERWAVE_DRIVER	"snd_interwave"
6162306a36Sopenharmony_ci#define INTERWAVE_PNP_DRIVER	"interwave"
6262306a36Sopenharmony_ci#endif
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
6562306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for InterWave soundcard.");
6662306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
6762306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for InterWave soundcard.");
6862306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable InterWave soundcard.");
7062306a36Sopenharmony_ci#ifdef CONFIG_PNP
7162306a36Sopenharmony_cimodule_param_array(isapnp, bool, NULL, 0444);
7262306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
7362306a36Sopenharmony_ci#endif
7462306a36Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444);
7562306a36Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for InterWave driver.");
7662306a36Sopenharmony_ci#ifdef SNDRV_STB
7762306a36Sopenharmony_cimodule_param_hw_array(port_tc, long, ioport, NULL, 0444);
7862306a36Sopenharmony_ciMODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver.");
7962306a36Sopenharmony_ci#endif
8062306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444);
8162306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for InterWave driver.");
8262306a36Sopenharmony_cimodule_param_hw_array(dma1, int, dma, NULL, 0444);
8362306a36Sopenharmony_ciMODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver.");
8462306a36Sopenharmony_cimodule_param_hw_array(dma2, int, dma, NULL, 0444);
8562306a36Sopenharmony_ciMODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver.");
8662306a36Sopenharmony_cimodule_param_array(joystick_dac, int, NULL, 0444);
8762306a36Sopenharmony_ciMODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver.");
8862306a36Sopenharmony_cimodule_param_array(midi, int, NULL, 0444);
8962306a36Sopenharmony_ciMODULE_PARM_DESC(midi, "MIDI UART enable for InterWave driver.");
9062306a36Sopenharmony_cimodule_param_array(pcm_channels, int, NULL, 0444);
9162306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for InterWave driver.");
9262306a36Sopenharmony_cimodule_param_array(effect, int, NULL, 0444);
9362306a36Sopenharmony_ciMODULE_PARM_DESC(effect, "Effects enable for InterWave driver.");
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistruct snd_interwave {
9662306a36Sopenharmony_ci	int irq;
9762306a36Sopenharmony_ci	struct snd_card *card;
9862306a36Sopenharmony_ci	struct snd_gus_card *gus;
9962306a36Sopenharmony_ci	struct snd_wss *wss;
10062306a36Sopenharmony_ci#ifdef SNDRV_STB
10162306a36Sopenharmony_ci	struct resource *i2c_res;
10262306a36Sopenharmony_ci#endif
10362306a36Sopenharmony_ci	unsigned short gus_status_reg;
10462306a36Sopenharmony_ci	unsigned short pcm_status_reg;
10562306a36Sopenharmony_ci#ifdef CONFIG_PNP
10662306a36Sopenharmony_ci	struct pnp_dev *dev;
10762306a36Sopenharmony_ci#ifdef SNDRV_STB
10862306a36Sopenharmony_ci	struct pnp_dev *devtc;
10962306a36Sopenharmony_ci#endif
11062306a36Sopenharmony_ci#endif
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#ifdef CONFIG_PNP
11562306a36Sopenharmony_cistatic int isa_registered;
11662306a36Sopenharmony_cistatic int pnp_registered;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_interwave_pnpids[] = {
11962306a36Sopenharmony_ci#ifndef SNDRV_STB
12062306a36Sopenharmony_ci	/* Gravis UltraSound Plug & Play */
12162306a36Sopenharmony_ci	{ .id = "GRV0001", .devs = { { .id = "GRV0000" } } },
12262306a36Sopenharmony_ci	/* STB SoundRage32 */
12362306a36Sopenharmony_ci	{ .id = "STB011a", .devs = { { .id = "STB0010" } } },
12462306a36Sopenharmony_ci	/* MED3210 */
12562306a36Sopenharmony_ci	{ .id = "DXP3201", .devs = { { .id = "DXP0010" } } },
12662306a36Sopenharmony_ci	/* Dynasonic Pro */
12762306a36Sopenharmony_ci	/* This device also have CDC1117:DynaSonix Pro Audio Effects Processor */
12862306a36Sopenharmony_ci	{ .id = "CDC1111", .devs = { { .id = "CDC1112" } } },
12962306a36Sopenharmony_ci	/* Panasonic PCA761AW Audio Card */
13062306a36Sopenharmony_ci	{ .id = "ADV55ff", .devs = { { .id = "ADV0010" } } },
13162306a36Sopenharmony_ci	/* InterWave STB without TEA6330T */
13262306a36Sopenharmony_ci	{ .id = "ADV550a", .devs = { { .id = "ADV0010" } } },
13362306a36Sopenharmony_ci#else
13462306a36Sopenharmony_ci	/* InterWave STB with TEA6330T */
13562306a36Sopenharmony_ci	{ .id = "ADV550a", .devs = { { .id = "ADV0010" }, { .id = "ADV0015" } } },
13662306a36Sopenharmony_ci#endif
13762306a36Sopenharmony_ci	{ .id = "" }
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_interwave_pnpids);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#endif /* CONFIG_PNP */
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci#ifdef SNDRV_STB
14662306a36Sopenharmony_cistatic void snd_interwave_i2c_setlines(struct snd_i2c_bus *bus, int ctrl, int data)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	unsigned long port = bus->private_value;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci#if 0
15162306a36Sopenharmony_ci	printk(KERN_DEBUG "i2c_setlines - 0x%lx <- %i,%i\n", port, ctrl, data);
15262306a36Sopenharmony_ci#endif
15362306a36Sopenharmony_ci	outb((data << 1) | ctrl, port);
15462306a36Sopenharmony_ci	udelay(10);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int snd_interwave_i2c_getclockline(struct snd_i2c_bus *bus)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	unsigned long port = bus->private_value;
16062306a36Sopenharmony_ci	unsigned char res;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	res = inb(port) & 1;
16362306a36Sopenharmony_ci#if 0
16462306a36Sopenharmony_ci	printk(KERN_DEBUG "i2c_getclockline - 0x%lx -> %i\n", port, res);
16562306a36Sopenharmony_ci#endif
16662306a36Sopenharmony_ci	return res;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int snd_interwave_i2c_getdataline(struct snd_i2c_bus *bus, int ack)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	unsigned long port = bus->private_value;
17262306a36Sopenharmony_ci	unsigned char res;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (ack)
17562306a36Sopenharmony_ci		udelay(10);
17662306a36Sopenharmony_ci	res = (inb(port) & 2) >> 1;
17762306a36Sopenharmony_ci#if 0
17862306a36Sopenharmony_ci	printk(KERN_DEBUG "i2c_getdataline - 0x%lx -> %i\n", port, res);
17962306a36Sopenharmony_ci#endif
18062306a36Sopenharmony_ci	return res;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic struct snd_i2c_bit_ops snd_interwave_i2c_bit_ops = {
18462306a36Sopenharmony_ci	.setlines = snd_interwave_i2c_setlines,
18562306a36Sopenharmony_ci	.getclock = snd_interwave_i2c_getclockline,
18662306a36Sopenharmony_ci	.getdata  = snd_interwave_i2c_getdataline,
18762306a36Sopenharmony_ci};
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int snd_interwave_detect_stb(struct snd_interwave *iwcard,
19062306a36Sopenharmony_ci				    struct snd_gus_card *gus, int dev,
19162306a36Sopenharmony_ci				    struct snd_i2c_bus **rbus)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	unsigned long port;
19462306a36Sopenharmony_ci	struct snd_i2c_bus *bus;
19562306a36Sopenharmony_ci	struct snd_card *card = iwcard->card;
19662306a36Sopenharmony_ci	char name[32];
19762306a36Sopenharmony_ci	int err;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	*rbus = NULL;
20062306a36Sopenharmony_ci	port = port_tc[dev];
20162306a36Sopenharmony_ci	if (port == SNDRV_AUTO_PORT) {
20262306a36Sopenharmony_ci		port = 0x350;
20362306a36Sopenharmony_ci		if (gus->gf1.port == 0x250) {
20462306a36Sopenharmony_ci			port = 0x360;
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci		while (port <= 0x380) {
20762306a36Sopenharmony_ci			iwcard->i2c_res = devm_request_region(card->dev, port, 1,
20862306a36Sopenharmony_ci							      "InterWave (I2C bus)");
20962306a36Sopenharmony_ci			if (iwcard->i2c_res)
21062306a36Sopenharmony_ci				break;
21162306a36Sopenharmony_ci			port += 0x10;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci	} else {
21462306a36Sopenharmony_ci		iwcard->i2c_res = devm_request_region(card->dev, port, 1,
21562306a36Sopenharmony_ci						      "InterWave (I2C bus)");
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci	if (iwcard->i2c_res == NULL) {
21862306a36Sopenharmony_ci		snd_printk(KERN_ERR "interwave: can't grab i2c bus port\n");
21962306a36Sopenharmony_ci		return -ENODEV;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	sprintf(name, "InterWave-%i", card->number);
22362306a36Sopenharmony_ci	err = snd_i2c_bus_create(card, name, NULL, &bus);
22462306a36Sopenharmony_ci	if (err < 0)
22562306a36Sopenharmony_ci		return err;
22662306a36Sopenharmony_ci	bus->private_value = port;
22762306a36Sopenharmony_ci	bus->hw_ops.bit = &snd_interwave_i2c_bit_ops;
22862306a36Sopenharmony_ci	err = snd_tea6330t_detect(bus, 0);
22962306a36Sopenharmony_ci	if (err < 0)
23062306a36Sopenharmony_ci		return err;
23162306a36Sopenharmony_ci	*rbus = bus;
23262306a36Sopenharmony_ci	return 0;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci#endif
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int snd_interwave_detect(struct snd_interwave *iwcard,
23762306a36Sopenharmony_ci				struct snd_gus_card *gus,
23862306a36Sopenharmony_ci				int dev
23962306a36Sopenharmony_ci#ifdef SNDRV_STB
24062306a36Sopenharmony_ci				, struct snd_i2c_bus **rbus
24162306a36Sopenharmony_ci#endif
24262306a36Sopenharmony_ci				          )
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	unsigned long flags;
24562306a36Sopenharmony_ci	unsigned char rev1, rev2;
24662306a36Sopenharmony_ci	int d;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0);	/* reset GF1 */
24962306a36Sopenharmony_ci	d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
25062306a36Sopenharmony_ci	if ((d & 0x07) != 0) {
25162306a36Sopenharmony_ci		snd_printdd("[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
25262306a36Sopenharmony_ci		return -ENODEV;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci	udelay(160);
25562306a36Sopenharmony_ci	snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);	/* release reset */
25662306a36Sopenharmony_ci	udelay(160);
25762306a36Sopenharmony_ci	d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
25862306a36Sopenharmony_ci	if ((d & 0x07) != 1) {
25962306a36Sopenharmony_ci		snd_printdd("[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
26062306a36Sopenharmony_ci		return -ENODEV;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	spin_lock_irqsave(&gus->reg_lock, flags);
26362306a36Sopenharmony_ci	rev1 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
26462306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, ~rev1);
26562306a36Sopenharmony_ci	rev2 = snd_gf1_look8(gus, SNDRV_GF1_GB_VERSION_NUMBER);
26662306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, rev1);
26762306a36Sopenharmony_ci	spin_unlock_irqrestore(&gus->reg_lock, flags);
26862306a36Sopenharmony_ci	snd_printdd("[0x%lx] InterWave check - rev1=0x%x, rev2=0x%x\n", gus->gf1.port, rev1, rev2);
26962306a36Sopenharmony_ci	if ((rev1 & 0xf0) == (rev2 & 0xf0) &&
27062306a36Sopenharmony_ci	    (rev1 & 0x0f) != (rev2 & 0x0f)) {
27162306a36Sopenharmony_ci		snd_printdd("[0x%lx] InterWave check - passed\n", gus->gf1.port);
27262306a36Sopenharmony_ci		gus->interwave = 1;
27362306a36Sopenharmony_ci		strcpy(gus->card->shortname, "AMD InterWave");
27462306a36Sopenharmony_ci		gus->revision = rev1 >> 4;
27562306a36Sopenharmony_ci#ifndef SNDRV_STB
27662306a36Sopenharmony_ci		return 0;	/* ok.. We have an InterWave board */
27762306a36Sopenharmony_ci#else
27862306a36Sopenharmony_ci		return snd_interwave_detect_stb(iwcard, gus, dev, rbus);
27962306a36Sopenharmony_ci#endif
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci	snd_printdd("[0x%lx] InterWave check - failed\n", gus->gf1.port);
28262306a36Sopenharmony_ci	return -ENODEV;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic irqreturn_t snd_interwave_interrupt(int irq, void *dev_id)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct snd_interwave *iwcard = dev_id;
28862306a36Sopenharmony_ci	int loop, max = 5;
28962306a36Sopenharmony_ci	int handled = 0;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	do {
29262306a36Sopenharmony_ci		loop = 0;
29362306a36Sopenharmony_ci		if (inb(iwcard->gus_status_reg)) {
29462306a36Sopenharmony_ci			handled = 1;
29562306a36Sopenharmony_ci			snd_gus_interrupt(irq, iwcard->gus);
29662306a36Sopenharmony_ci			loop++;
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci		if (inb(iwcard->pcm_status_reg) & 0x01) {	/* IRQ bit is set? */
29962306a36Sopenharmony_ci			handled = 1;
30062306a36Sopenharmony_ci			snd_wss_interrupt(irq, iwcard->wss);
30162306a36Sopenharmony_ci			loop++;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci	} while (loop && --max > 0);
30462306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void snd_interwave_reset(struct snd_gus_card *gus)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x00);
31062306a36Sopenharmony_ci	udelay(160);
31162306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_RESET, 0x01);
31262306a36Sopenharmony_ci	udelay(160);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void snd_interwave_bank_sizes(struct snd_gus_card *gus, int *sizes)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	unsigned int idx;
31862306a36Sopenharmony_ci	unsigned int local;
31962306a36Sopenharmony_ci	unsigned char d;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	for (idx = 0; idx < 4; idx++) {
32262306a36Sopenharmony_ci		sizes[idx] = 0;
32362306a36Sopenharmony_ci		d = 0x55;
32462306a36Sopenharmony_ci		for (local = idx << 22;
32562306a36Sopenharmony_ci		     local < (idx << 22) + 0x400000;
32662306a36Sopenharmony_ci		     local += 0x40000, d++) {
32762306a36Sopenharmony_ci			snd_gf1_poke(gus, local, d);
32862306a36Sopenharmony_ci			snd_gf1_poke(gus, local + 1, d + 1);
32962306a36Sopenharmony_ci#if 0
33062306a36Sopenharmony_ci			printk(KERN_DEBUG "d = 0x%x, local = 0x%x, "
33162306a36Sopenharmony_ci			       "local + 1 = 0x%x, idx << 22 = 0x%x\n",
33262306a36Sopenharmony_ci			       d,
33362306a36Sopenharmony_ci			       snd_gf1_peek(gus, local),
33462306a36Sopenharmony_ci			       snd_gf1_peek(gus, local + 1),
33562306a36Sopenharmony_ci			       snd_gf1_peek(gus, idx << 22));
33662306a36Sopenharmony_ci#endif
33762306a36Sopenharmony_ci			if (snd_gf1_peek(gus, local) != d ||
33862306a36Sopenharmony_ci			    snd_gf1_peek(gus, local + 1) != d + 1 ||
33962306a36Sopenharmony_ci			    snd_gf1_peek(gus, idx << 22) != 0x55)
34062306a36Sopenharmony_ci				break;
34162306a36Sopenharmony_ci			sizes[idx]++;
34262306a36Sopenharmony_ci		}
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci#if 0
34562306a36Sopenharmony_ci	printk(KERN_DEBUG "sizes: %i %i %i %i\n",
34662306a36Sopenharmony_ci	       sizes[0], sizes[1], sizes[2], sizes[3]);
34762306a36Sopenharmony_ci#endif
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistruct rom_hdr {
35162306a36Sopenharmony_ci	/* 000 */ unsigned char iwave[8];
35262306a36Sopenharmony_ci	/* 008 */ unsigned char rom_hdr_revision;
35362306a36Sopenharmony_ci	/* 009 */ unsigned char series_number;
35462306a36Sopenharmony_ci	/* 010 */ unsigned char series_name[16];
35562306a36Sopenharmony_ci	/* 026 */ unsigned char date[10];
35662306a36Sopenharmony_ci	/* 036 */ unsigned short vendor_revision_major;
35762306a36Sopenharmony_ci	/* 038 */ unsigned short vendor_revision_minor;
35862306a36Sopenharmony_ci	/* 040 */ unsigned int rom_size;
35962306a36Sopenharmony_ci	/* 044 */ unsigned char copyright[128];
36062306a36Sopenharmony_ci	/* 172 */ unsigned char vendor_name[64];
36162306a36Sopenharmony_ci	/* 236 */ unsigned char rom_description[128];
36262306a36Sopenharmony_ci	/* 364 */ unsigned char pad[147];
36362306a36Sopenharmony_ci	/* 511 */ unsigned char csum;
36462306a36Sopenharmony_ci};
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void snd_interwave_detect_memory(struct snd_gus_card *gus)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	static const unsigned int lmc[13] =
36962306a36Sopenharmony_ci	{
37062306a36Sopenharmony_ci		0x00000001, 0x00000101, 0x01010101, 0x00000401,
37162306a36Sopenharmony_ci		0x04040401, 0x00040101, 0x04040101, 0x00000004,
37262306a36Sopenharmony_ci		0x00000404, 0x04040404, 0x00000010, 0x00001010,
37362306a36Sopenharmony_ci		0x10101010
37462306a36Sopenharmony_ci	};
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	int bank_pos, pages;
37762306a36Sopenharmony_ci	unsigned int i, lmct;
37862306a36Sopenharmony_ci	int psizes[4];
37962306a36Sopenharmony_ci	unsigned char iwave[8];
38062306a36Sopenharmony_ci	unsigned char csum;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	snd_interwave_reset(gus);
38362306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);		/* enhanced mode */
38462306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);	/* DRAM I/O cycles selected */
38562306a36Sopenharmony_ci	snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff10) | 0x004c);
38662306a36Sopenharmony_ci	/* ok.. simple test of memory size */
38762306a36Sopenharmony_ci	pages = 0;
38862306a36Sopenharmony_ci	snd_gf1_poke(gus, 0, 0x55);
38962306a36Sopenharmony_ci	snd_gf1_poke(gus, 1, 0xaa);
39062306a36Sopenharmony_ci#if 1
39162306a36Sopenharmony_ci	if (snd_gf1_peek(gus, 0) == 0x55 && snd_gf1_peek(gus, 1) == 0xaa)
39262306a36Sopenharmony_ci#else
39362306a36Sopenharmony_ci	if (0)			/* ok.. for testing of 0k RAM */
39462306a36Sopenharmony_ci#endif
39562306a36Sopenharmony_ci	{
39662306a36Sopenharmony_ci		snd_interwave_bank_sizes(gus, psizes);
39762306a36Sopenharmony_ci		lmct = (psizes[3] << 24) | (psizes[2] << 16) |
39862306a36Sopenharmony_ci		    (psizes[1] << 8) | psizes[0];
39962306a36Sopenharmony_ci#if 0
40062306a36Sopenharmony_ci		printk(KERN_DEBUG "lmct = 0x%08x\n", lmct);
40162306a36Sopenharmony_ci#endif
40262306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(lmc); i++)
40362306a36Sopenharmony_ci			if (lmct == lmc[i]) {
40462306a36Sopenharmony_ci#if 0
40562306a36Sopenharmony_ci				printk(KERN_DEBUG "found !!! %i\n", i);
40662306a36Sopenharmony_ci#endif
40762306a36Sopenharmony_ci				snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | i);
40862306a36Sopenharmony_ci				snd_interwave_bank_sizes(gus, psizes);
40962306a36Sopenharmony_ci				break;
41062306a36Sopenharmony_ci			}
41162306a36Sopenharmony_ci		if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode)
41262306a36Sopenharmony_ci			 snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2);
41362306a36Sopenharmony_ci		for (i = 0; i < 4; i++) {
41462306a36Sopenharmony_ci			gus->gf1.mem_alloc.banks_8[i].address =
41562306a36Sopenharmony_ci			    gus->gf1.mem_alloc.banks_16[i].address = i << 22;
41662306a36Sopenharmony_ci			gus->gf1.mem_alloc.banks_8[i].size =
41762306a36Sopenharmony_ci			    gus->gf1.mem_alloc.banks_16[i].size = psizes[i] << 18;
41862306a36Sopenharmony_ci			pages += psizes[i];
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci	pages <<= 18;
42262306a36Sopenharmony_ci	gus->gf1.memory = pages;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x03);	/* select ROM */
42562306a36Sopenharmony_ci	snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xff1f) | (4 << 5));
42662306a36Sopenharmony_ci	gus->gf1.rom_banks = 0;
42762306a36Sopenharmony_ci	gus->gf1.rom_memory = 0;
42862306a36Sopenharmony_ci	for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) {
42962306a36Sopenharmony_ci		for (i = 0; i < 8; ++i)
43062306a36Sopenharmony_ci			iwave[i] = snd_gf1_peek(gus, bank_pos + i);
43162306a36Sopenharmony_ci		if (strncmp(iwave, "INTRWAVE", 8))
43262306a36Sopenharmony_ci			continue;	/* first check */
43362306a36Sopenharmony_ci		csum = 0;
43462306a36Sopenharmony_ci		for (i = 0; i < sizeof(struct rom_hdr); i++)
43562306a36Sopenharmony_ci			csum += snd_gf1_peek(gus, bank_pos + i);
43662306a36Sopenharmony_ci		if (csum != 0)
43762306a36Sopenharmony_ci			continue;	/* not valid rom */
43862306a36Sopenharmony_ci		gus->gf1.rom_banks++;
43962306a36Sopenharmony_ci		gus->gf1.rom_present |= 1 << (bank_pos >> 22);
44062306a36Sopenharmony_ci		gus->gf1.rom_memory = snd_gf1_peek(gus, bank_pos + 40) |
44162306a36Sopenharmony_ci				     (snd_gf1_peek(gus, bank_pos + 41) << 8) |
44262306a36Sopenharmony_ci				     (snd_gf1_peek(gus, bank_pos + 42) << 16) |
44362306a36Sopenharmony_ci				     (snd_gf1_peek(gus, bank_pos + 43) << 24);
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci#if 0
44662306a36Sopenharmony_ci	if (gus->gf1.rom_memory > 0) {
44762306a36Sopenharmony_ci		if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8)
44862306a36Sopenharmony_ci			gus->card->type = SNDRV_CARD_TYPE_IW_DYNASONIC;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci#endif
45162306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x00);	/* select RAM */
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (!gus->gf1.enh_mode)
45462306a36Sopenharmony_ci		snd_interwave_reset(gus);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic void snd_interwave_init(int dev, struct snd_gus_card *gus)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	unsigned long flags;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* ok.. some InterWave specific initialization */
46262306a36Sopenharmony_ci	spin_lock_irqsave(&gus->reg_lock, flags);
46362306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0x00);
46462306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_COMPATIBILITY, 0x1f);
46562306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_DECODE_CONTROL, 0x49);
46662306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_VERSION_NUMBER, 0x11);
46762306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_A, 0x00);
46862306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_MPU401_CONTROL_B, 0x30);
46962306a36Sopenharmony_ci	snd_gf1_write8(gus, SNDRV_GF1_GB_EMULATION_IRQ, 0x00);
47062306a36Sopenharmony_ci	spin_unlock_irqrestore(&gus->reg_lock, flags);
47162306a36Sopenharmony_ci	gus->equal_irq = 1;
47262306a36Sopenharmony_ci	gus->codec_flag = 1;
47362306a36Sopenharmony_ci	gus->interwave = 1;
47462306a36Sopenharmony_ci	gus->max_flag = 1;
47562306a36Sopenharmony_ci	gus->joystick_dac = joystick_dac[dev];
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_interwave_controls[] = {
48062306a36Sopenharmony_ciWSS_DOUBLE("Master Playback Switch", 0,
48162306a36Sopenharmony_ci		CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1),
48262306a36Sopenharmony_ciWSS_DOUBLE("Master Playback Volume", 0,
48362306a36Sopenharmony_ci		CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1),
48462306a36Sopenharmony_ciWSS_DOUBLE("Mic Playback Switch", 0,
48562306a36Sopenharmony_ci		CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1),
48662306a36Sopenharmony_ciWSS_DOUBLE("Mic Playback Volume", 0,
48762306a36Sopenharmony_ci		CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1)
48862306a36Sopenharmony_ci};
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int snd_interwave_mixer(struct snd_wss *chip)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct snd_card *card = chip->card;
49362306a36Sopenharmony_ci	struct snd_ctl_elem_id id1, id2;
49462306a36Sopenharmony_ci	unsigned int idx;
49562306a36Sopenharmony_ci	int err;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	memset(&id1, 0, sizeof(id1));
49862306a36Sopenharmony_ci	memset(&id2, 0, sizeof(id2));
49962306a36Sopenharmony_ci	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
50062306a36Sopenharmony_ci#if 0
50162306a36Sopenharmony_ci	/* remove mono microphone controls */
50262306a36Sopenharmony_ci	strcpy(id1.name, "Mic Playback Switch");
50362306a36Sopenharmony_ci	err = snd_ctl_remove_id(card, &id1);
50462306a36Sopenharmony_ci	if (err < 0)
50562306a36Sopenharmony_ci		return err;
50662306a36Sopenharmony_ci	strcpy(id1.name, "Mic Playback Volume");
50762306a36Sopenharmony_ci	err = snd_ctl_remove_id(card, &id1);
50862306a36Sopenharmony_ci	if (err < 0)
50962306a36Sopenharmony_ci		return err;
51062306a36Sopenharmony_ci#endif
51162306a36Sopenharmony_ci	/* add new master and mic controls */
51262306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++) {
51362306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip));
51462306a36Sopenharmony_ci		if (err < 0)
51562306a36Sopenharmony_ci			return err;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci	snd_wss_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f);
51862306a36Sopenharmony_ci	snd_wss_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f);
51962306a36Sopenharmony_ci	snd_wss_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f);
52062306a36Sopenharmony_ci	snd_wss_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);
52162306a36Sopenharmony_ci	/* reassign AUXA to SYNTHESIZER */
52262306a36Sopenharmony_ci	strcpy(id1.name, "Aux Playback Switch");
52362306a36Sopenharmony_ci	strcpy(id2.name, "Synth Playback Switch");
52462306a36Sopenharmony_ci	err = snd_ctl_rename_id(card, &id1, &id2);
52562306a36Sopenharmony_ci	if (err < 0)
52662306a36Sopenharmony_ci		return err;
52762306a36Sopenharmony_ci	strcpy(id1.name, "Aux Playback Volume");
52862306a36Sopenharmony_ci	strcpy(id2.name, "Synth Playback Volume");
52962306a36Sopenharmony_ci	err = snd_ctl_rename_id(card, &id1, &id2);
53062306a36Sopenharmony_ci	if (err < 0)
53162306a36Sopenharmony_ci		return err;
53262306a36Sopenharmony_ci	/* reassign AUXB to CD */
53362306a36Sopenharmony_ci	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
53462306a36Sopenharmony_ci	strcpy(id2.name, "CD Playback Switch");
53562306a36Sopenharmony_ci	err = snd_ctl_rename_id(card, &id1, &id2);
53662306a36Sopenharmony_ci	if (err < 0)
53762306a36Sopenharmony_ci		return err;
53862306a36Sopenharmony_ci	strcpy(id1.name, "Aux Playback Volume");
53962306a36Sopenharmony_ci	strcpy(id2.name, "CD Playback Volume");
54062306a36Sopenharmony_ci	err = snd_ctl_rename_id(card, &id1, &id2);
54162306a36Sopenharmony_ci	if (err < 0)
54262306a36Sopenharmony_ci		return err;
54362306a36Sopenharmony_ci	return 0;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci#ifdef CONFIG_PNP
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int snd_interwave_pnp(int dev, struct snd_interwave *iwcard,
54962306a36Sopenharmony_ci			     struct pnp_card_link *card,
55062306a36Sopenharmony_ci			     const struct pnp_card_device_id *id)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct pnp_dev *pdev;
55362306a36Sopenharmony_ci	int err;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	iwcard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
55662306a36Sopenharmony_ci	if (iwcard->dev == NULL)
55762306a36Sopenharmony_ci		return -EBUSY;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci#ifdef SNDRV_STB
56062306a36Sopenharmony_ci	iwcard->devtc = pnp_request_card_device(card, id->devs[1].id, NULL);
56162306a36Sopenharmony_ci	if (iwcard->devtc == NULL)
56262306a36Sopenharmony_ci		return -EBUSY;
56362306a36Sopenharmony_ci#endif
56462306a36Sopenharmony_ci	/* Synth & Codec initialization */
56562306a36Sopenharmony_ci	pdev = iwcard->dev;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	err = pnp_activate_dev(pdev);
56862306a36Sopenharmony_ci	if (err < 0) {
56962306a36Sopenharmony_ci		snd_printk(KERN_ERR "InterWave PnP configure failure (out of resources?)\n");
57062306a36Sopenharmony_ci		return err;
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci	if (pnp_port_start(pdev, 0) + 0x100 != pnp_port_start(pdev, 1) ||
57362306a36Sopenharmony_ci	    pnp_port_start(pdev, 0) + 0x10c != pnp_port_start(pdev, 2)) {
57462306a36Sopenharmony_ci		snd_printk(KERN_ERR "PnP configure failure (wrong ports)\n");
57562306a36Sopenharmony_ci		return -ENOENT;
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci	port[dev] = pnp_port_start(pdev, 0);
57862306a36Sopenharmony_ci	dma1[dev] = pnp_dma(pdev, 0);
57962306a36Sopenharmony_ci	if (dma2[dev] >= 0)
58062306a36Sopenharmony_ci		dma2[dev] = pnp_dma(pdev, 1);
58162306a36Sopenharmony_ci	irq[dev] = pnp_irq(pdev, 0);
58262306a36Sopenharmony_ci	snd_printdd("isapnp IW: sb port=0x%llx, gf1 port=0x%llx, codec port=0x%llx\n",
58362306a36Sopenharmony_ci			(unsigned long long)pnp_port_start(pdev, 0),
58462306a36Sopenharmony_ci			(unsigned long long)pnp_port_start(pdev, 1),
58562306a36Sopenharmony_ci			(unsigned long long)pnp_port_start(pdev, 2));
58662306a36Sopenharmony_ci	snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
58762306a36Sopenharmony_ci#ifdef SNDRV_STB
58862306a36Sopenharmony_ci	/* Tone Control initialization */
58962306a36Sopenharmony_ci	pdev = iwcard->devtc;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	err = pnp_activate_dev(pdev);
59262306a36Sopenharmony_ci	if (err < 0) {
59362306a36Sopenharmony_ci		snd_printk(KERN_ERR "InterWave ToneControl PnP configure failure (out of resources?)\n");
59462306a36Sopenharmony_ci		return err;
59562306a36Sopenharmony_ci	}
59662306a36Sopenharmony_ci	port_tc[dev] = pnp_port_start(pdev, 0);
59762306a36Sopenharmony_ci	snd_printdd("isapnp IW: tone control port=0x%lx\n", port_tc[dev]);
59862306a36Sopenharmony_ci#endif
59962306a36Sopenharmony_ci	return 0;
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci#endif /* CONFIG_PNP */
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic int snd_interwave_card_new(struct device *pdev, int dev,
60462306a36Sopenharmony_ci				  struct snd_card **cardp)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct snd_card *card;
60762306a36Sopenharmony_ci	struct snd_interwave *iwcard;
60862306a36Sopenharmony_ci	int err;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
61162306a36Sopenharmony_ci				sizeof(struct snd_interwave), &card);
61262306a36Sopenharmony_ci	if (err < 0)
61362306a36Sopenharmony_ci		return err;
61462306a36Sopenharmony_ci	iwcard = card->private_data;
61562306a36Sopenharmony_ci	iwcard->card = card;
61662306a36Sopenharmony_ci	iwcard->irq = -1;
61762306a36Sopenharmony_ci	*cardp = card;
61862306a36Sopenharmony_ci	return 0;
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic int snd_interwave_probe_gus(struct snd_card *card, int dev,
62262306a36Sopenharmony_ci				   struct snd_gus_card **gusp)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	return snd_gus_create(card, port[dev], -irq[dev], dma1[dev], dma2[dev],
62562306a36Sopenharmony_ci			      0, 32, pcm_channels[dev], effect[dev], gusp);
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic int snd_interwave_probe(struct snd_card *card, int dev,
62962306a36Sopenharmony_ci			       struct snd_gus_card *gus)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	int xirq, xdma1, xdma2;
63262306a36Sopenharmony_ci	struct snd_interwave *iwcard = card->private_data;
63362306a36Sopenharmony_ci	struct snd_wss *wss;
63462306a36Sopenharmony_ci#ifdef SNDRV_STB
63562306a36Sopenharmony_ci	struct snd_i2c_bus *i2c_bus;
63662306a36Sopenharmony_ci#endif
63762306a36Sopenharmony_ci	char *str;
63862306a36Sopenharmony_ci	int err;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	xirq = irq[dev];
64162306a36Sopenharmony_ci	xdma1 = dma1[dev];
64262306a36Sopenharmony_ci	xdma2 = dma2[dev];
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	err = snd_interwave_detect(iwcard, gus, dev
64562306a36Sopenharmony_ci#ifdef SNDRV_STB
64662306a36Sopenharmony_ci				   , &i2c_bus
64762306a36Sopenharmony_ci#endif
64862306a36Sopenharmony_ci				   );
64962306a36Sopenharmony_ci	if (err < 0)
65062306a36Sopenharmony_ci		return err;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	iwcard->gus_status_reg = gus->gf1.reg_irqstat;
65362306a36Sopenharmony_ci	iwcard->pcm_status_reg = gus->gf1.port + 0x10c + 2;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	snd_interwave_init(dev, gus);
65662306a36Sopenharmony_ci	snd_interwave_detect_memory(gus);
65762306a36Sopenharmony_ci	err = snd_gus_initialize(gus);
65862306a36Sopenharmony_ci	if (err < 0)
65962306a36Sopenharmony_ci		return err;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	if (devm_request_irq(card->dev, xirq, snd_interwave_interrupt, 0,
66262306a36Sopenharmony_ci			     "InterWave", iwcard)) {
66362306a36Sopenharmony_ci		snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
66462306a36Sopenharmony_ci		return -EBUSY;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci	iwcard->irq = xirq;
66762306a36Sopenharmony_ci	card->sync_irq = iwcard->irq;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	err = snd_wss_create(card,
67062306a36Sopenharmony_ci			     gus->gf1.port + 0x10c, -1, xirq,
67162306a36Sopenharmony_ci			     xdma2 < 0 ? xdma1 : xdma2, xdma1,
67262306a36Sopenharmony_ci			     WSS_HW_INTERWAVE,
67362306a36Sopenharmony_ci			     WSS_HWSHARE_IRQ |
67462306a36Sopenharmony_ci			     WSS_HWSHARE_DMA1 |
67562306a36Sopenharmony_ci			     WSS_HWSHARE_DMA2,
67662306a36Sopenharmony_ci			     &wss);
67762306a36Sopenharmony_ci	if (err < 0)
67862306a36Sopenharmony_ci		return err;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	err = snd_wss_pcm(wss, 0);
68162306a36Sopenharmony_ci	if (err < 0)
68262306a36Sopenharmony_ci		return err;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	sprintf(wss->pcm->name + strlen(wss->pcm->name), " rev %c",
68562306a36Sopenharmony_ci		gus->revision + 'A');
68662306a36Sopenharmony_ci	strcat(wss->pcm->name, " (codec)");
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	err = snd_wss_timer(wss, 2);
68962306a36Sopenharmony_ci	if (err < 0)
69062306a36Sopenharmony_ci		return err;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	err = snd_wss_mixer(wss);
69362306a36Sopenharmony_ci	if (err < 0)
69462306a36Sopenharmony_ci		return err;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (pcm_channels[dev] > 0) {
69762306a36Sopenharmony_ci		err = snd_gf1_pcm_new(gus, 1, 1);
69862306a36Sopenharmony_ci		if (err < 0)
69962306a36Sopenharmony_ci			return err;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci	err = snd_interwave_mixer(wss);
70262306a36Sopenharmony_ci	if (err < 0)
70362306a36Sopenharmony_ci		return err;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci#ifdef SNDRV_STB
70662306a36Sopenharmony_ci	{
70762306a36Sopenharmony_ci		struct snd_ctl_elem_id id1, id2;
70862306a36Sopenharmony_ci		memset(&id1, 0, sizeof(id1));
70962306a36Sopenharmony_ci		memset(&id2, 0, sizeof(id2));
71062306a36Sopenharmony_ci		id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
71162306a36Sopenharmony_ci		strcpy(id1.name, "Master Playback Switch");
71262306a36Sopenharmony_ci		strcpy(id2.name, id1.name);
71362306a36Sopenharmony_ci		id2.index = 1;
71462306a36Sopenharmony_ci		err = snd_ctl_rename_id(card, &id1, &id2);
71562306a36Sopenharmony_ci		if (err < 0)
71662306a36Sopenharmony_ci			return err;
71762306a36Sopenharmony_ci		strcpy(id1.name, "Master Playback Volume");
71862306a36Sopenharmony_ci		strcpy(id2.name, id1.name);
71962306a36Sopenharmony_ci		err = snd_ctl_rename_id(card, &id1, &id2);
72062306a36Sopenharmony_ci		if (err < 0)
72162306a36Sopenharmony_ci			return err;
72262306a36Sopenharmony_ci		err = snd_tea6330t_update_mixer(card, i2c_bus, 0, 1);
72362306a36Sopenharmony_ci		if (err < 0)
72462306a36Sopenharmony_ci			return err;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci#endif
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	gus->uart_enable = midi[dev];
72962306a36Sopenharmony_ci	err = snd_gf1_rawmidi_new(gus, 0);
73062306a36Sopenharmony_ci	if (err < 0)
73162306a36Sopenharmony_ci		return err;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci#ifndef SNDRV_STB
73462306a36Sopenharmony_ci	str = "AMD InterWave";
73562306a36Sopenharmony_ci	if (gus->gf1.rom_banks == 1 && gus->gf1.rom_present == 8)
73662306a36Sopenharmony_ci		str = "Dynasonic 3-D";
73762306a36Sopenharmony_ci#else
73862306a36Sopenharmony_ci	str = "InterWave STB";
73962306a36Sopenharmony_ci#endif
74062306a36Sopenharmony_ci	strcpy(card->driver, str);
74162306a36Sopenharmony_ci	strcpy(card->shortname, str);
74262306a36Sopenharmony_ci	sprintf(card->longname, "%s at 0x%lx, irq %i, dma %d",
74362306a36Sopenharmony_ci		str,
74462306a36Sopenharmony_ci		gus->gf1.port,
74562306a36Sopenharmony_ci		xirq,
74662306a36Sopenharmony_ci		xdma1);
74762306a36Sopenharmony_ci	if (xdma2 >= 0)
74862306a36Sopenharmony_ci		sprintf(card->longname + strlen(card->longname), "&%d", xdma2);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	err = snd_card_register(card);
75162306a36Sopenharmony_ci	if (err < 0)
75262306a36Sopenharmony_ci		return err;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	iwcard->wss = wss;
75562306a36Sopenharmony_ci	iwcard->gus = gus;
75662306a36Sopenharmony_ci	return 0;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic int snd_interwave_isa_match(struct device *pdev,
76062306a36Sopenharmony_ci				   unsigned int dev)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	if (!enable[dev])
76362306a36Sopenharmony_ci		return 0;
76462306a36Sopenharmony_ci#ifdef CONFIG_PNP
76562306a36Sopenharmony_ci	if (isapnp[dev])
76662306a36Sopenharmony_ci		return 0;
76762306a36Sopenharmony_ci#endif
76862306a36Sopenharmony_ci	return 1;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_cistatic int snd_interwave_isa_probe(struct device *pdev,
77262306a36Sopenharmony_ci				   unsigned int dev)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct snd_card *card;
77562306a36Sopenharmony_ci	struct snd_gus_card *gus;
77662306a36Sopenharmony_ci	int err;
77762306a36Sopenharmony_ci	static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
77862306a36Sopenharmony_ci	static const int possible_dmas[] = {0, 1, 3, 5, 6, 7, -1};
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	if (irq[dev] == SNDRV_AUTO_IRQ) {
78162306a36Sopenharmony_ci		irq[dev] = snd_legacy_find_free_irq(possible_irqs);
78262306a36Sopenharmony_ci		if (irq[dev] < 0) {
78362306a36Sopenharmony_ci			snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
78462306a36Sopenharmony_ci			return -EBUSY;
78562306a36Sopenharmony_ci		}
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci	if (dma1[dev] == SNDRV_AUTO_DMA) {
78862306a36Sopenharmony_ci		dma1[dev] = snd_legacy_find_free_dma(possible_dmas);
78962306a36Sopenharmony_ci		if (dma1[dev] < 0) {
79062306a36Sopenharmony_ci			snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
79162306a36Sopenharmony_ci			return -EBUSY;
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci	if (dma2[dev] == SNDRV_AUTO_DMA) {
79562306a36Sopenharmony_ci		dma2[dev] = snd_legacy_find_free_dma(possible_dmas);
79662306a36Sopenharmony_ci		if (dma2[dev] < 0) {
79762306a36Sopenharmony_ci			snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
79862306a36Sopenharmony_ci			return -EBUSY;
79962306a36Sopenharmony_ci		}
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	err = snd_interwave_card_new(pdev, dev, &card);
80362306a36Sopenharmony_ci	if (err < 0)
80462306a36Sopenharmony_ci		return err;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if (port[dev] != SNDRV_AUTO_PORT)
80762306a36Sopenharmony_ci		err = snd_interwave_probe_gus(card, dev, &gus);
80862306a36Sopenharmony_ci	else {
80962306a36Sopenharmony_ci		static const long possible_ports[] = {0x210, 0x220, 0x230, 0x240, 0x250, 0x260};
81062306a36Sopenharmony_ci		int i;
81162306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
81262306a36Sopenharmony_ci			port[dev] = possible_ports[i];
81362306a36Sopenharmony_ci			err = snd_interwave_probe_gus(card, dev, &gus);
81462306a36Sopenharmony_ci			if (! err)
81562306a36Sopenharmony_ci				return 0;
81662306a36Sopenharmony_ci		}
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	if (err < 0)
81962306a36Sopenharmony_ci		return err;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	err = snd_interwave_probe(card, dev, gus);
82262306a36Sopenharmony_ci	if (err < 0)
82362306a36Sopenharmony_ci		return err;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	dev_set_drvdata(pdev, card);
82662306a36Sopenharmony_ci	return 0;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic struct isa_driver snd_interwave_driver = {
83062306a36Sopenharmony_ci	.match		= snd_interwave_isa_match,
83162306a36Sopenharmony_ci	.probe		= snd_interwave_isa_probe,
83262306a36Sopenharmony_ci	/* FIXME: suspend,resume */
83362306a36Sopenharmony_ci	.driver		= {
83462306a36Sopenharmony_ci		.name	= INTERWAVE_DRIVER
83562306a36Sopenharmony_ci	},
83662306a36Sopenharmony_ci};
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci#ifdef CONFIG_PNP
83962306a36Sopenharmony_cistatic int snd_interwave_pnp_detect(struct pnp_card_link *pcard,
84062306a36Sopenharmony_ci				    const struct pnp_card_device_id *pid)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	static int dev;
84362306a36Sopenharmony_ci	struct snd_card *card;
84462306a36Sopenharmony_ci	struct snd_gus_card *gus;
84562306a36Sopenharmony_ci	int res;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	for ( ; dev < SNDRV_CARDS; dev++) {
84862306a36Sopenharmony_ci		if (enable[dev] && isapnp[dev])
84962306a36Sopenharmony_ci			break;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
85262306a36Sopenharmony_ci		return -ENODEV;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	res = snd_interwave_card_new(&pcard->card->dev, dev, &card);
85562306a36Sopenharmony_ci	if (res < 0)
85662306a36Sopenharmony_ci		return res;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	res = snd_interwave_pnp(dev, card->private_data, pcard, pid);
85962306a36Sopenharmony_ci	if (res < 0)
86062306a36Sopenharmony_ci		return res;
86162306a36Sopenharmony_ci	res = snd_interwave_probe_gus(card, dev, &gus);
86262306a36Sopenharmony_ci	if (res < 0)
86362306a36Sopenharmony_ci		return res;
86462306a36Sopenharmony_ci	res = snd_interwave_probe(card, dev, gus);
86562306a36Sopenharmony_ci	if (res < 0)
86662306a36Sopenharmony_ci		return res;
86762306a36Sopenharmony_ci	pnp_set_card_drvdata(pcard, card);
86862306a36Sopenharmony_ci	dev++;
86962306a36Sopenharmony_ci	return 0;
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic struct pnp_card_driver interwave_pnpc_driver = {
87362306a36Sopenharmony_ci	.flags = PNP_DRIVER_RES_DISABLE,
87462306a36Sopenharmony_ci	.name = INTERWAVE_PNP_DRIVER,
87562306a36Sopenharmony_ci	.id_table = snd_interwave_pnpids,
87662306a36Sopenharmony_ci	.probe = snd_interwave_pnp_detect,
87762306a36Sopenharmony_ci	/* FIXME: suspend,resume */
87862306a36Sopenharmony_ci};
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci#endif /* CONFIG_PNP */
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic int __init alsa_card_interwave_init(void)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	int err;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	err = isa_register_driver(&snd_interwave_driver, SNDRV_CARDS);
88762306a36Sopenharmony_ci#ifdef CONFIG_PNP
88862306a36Sopenharmony_ci	if (!err)
88962306a36Sopenharmony_ci		isa_registered = 1;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	err = pnp_register_card_driver(&interwave_pnpc_driver);
89262306a36Sopenharmony_ci	if (!err)
89362306a36Sopenharmony_ci		pnp_registered = 1;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (isa_registered)
89662306a36Sopenharmony_ci		err = 0;
89762306a36Sopenharmony_ci#endif
89862306a36Sopenharmony_ci	return err;
89962306a36Sopenharmony_ci}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic void __exit alsa_card_interwave_exit(void)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci#ifdef CONFIG_PNP
90462306a36Sopenharmony_ci	if (pnp_registered)
90562306a36Sopenharmony_ci		pnp_unregister_card_driver(&interwave_pnpc_driver);
90662306a36Sopenharmony_ci	if (isa_registered)
90762306a36Sopenharmony_ci#endif
90862306a36Sopenharmony_ci		isa_unregister_driver(&snd_interwave_driver);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cimodule_init(alsa_card_interwave_init)
91262306a36Sopenharmony_cimodule_exit(alsa_card_interwave_exit)
913