162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *   ALSA soundcard driver for Miro miroSOUND PCM1 pro
462306a36Sopenharmony_ci *                                  miroSOUND PCM12
562306a36Sopenharmony_ci *                                  miroSOUND PCM20 Radio
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *   Copyright (C) 2004-2005 Martin Langer <martin-langer@gmx.de>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *   Based on OSS ACI and ALSA OPTi9xx drivers
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/isa.h>
1562306a36Sopenharmony_ci#include <linux/pnp.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci#include <linux/ioport.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <asm/dma.h>
2162306a36Sopenharmony_ci#include <sound/core.h>
2262306a36Sopenharmony_ci#include <sound/wss.h>
2362306a36Sopenharmony_ci#include <sound/mpu401.h>
2462306a36Sopenharmony_ci#include <sound/opl4.h>
2562306a36Sopenharmony_ci#include <sound/control.h>
2662306a36Sopenharmony_ci#include <sound/info.h>
2762306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IOPORT
2862306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ
2962306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA
3062306a36Sopenharmony_ci#include <sound/initval.h>
3162306a36Sopenharmony_ci#include <sound/aci.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciMODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
3462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3562306a36Sopenharmony_ciMODULE_DESCRIPTION("Miro miroSOUND PCM1 pro, PCM12, PCM20 Radio");
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1;		/* Index 0-MAX */
3862306a36Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1;		/* ID for this card */
3962306a36Sopenharmony_cistatic long port = SNDRV_DEFAULT_PORT1; 	/* 0x530,0xe80,0xf40,0x604 */
4062306a36Sopenharmony_cistatic long mpu_port = SNDRV_DEFAULT_PORT1;	/* 0x300,0x310,0x320,0x330 */
4162306a36Sopenharmony_cistatic long fm_port = SNDRV_DEFAULT_PORT1;	/* 0x388 */
4262306a36Sopenharmony_cistatic int irq = SNDRV_DEFAULT_IRQ1;		/* 5,7,9,10,11 */
4362306a36Sopenharmony_cistatic int mpu_irq = SNDRV_DEFAULT_IRQ1;	/* 5,7,9,10 */
4462306a36Sopenharmony_cistatic int dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
4562306a36Sopenharmony_cistatic int dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
4662306a36Sopenharmony_cistatic int wss;
4762306a36Sopenharmony_cistatic int ide;
4862306a36Sopenharmony_ci#ifdef CONFIG_PNP
4962306a36Sopenharmony_cistatic bool isapnp = 1;				/* Enable ISA PnP detection */
5062306a36Sopenharmony_ci#endif
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cimodule_param(index, int, 0444);
5362306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for miro soundcard.");
5462306a36Sopenharmony_cimodule_param(id, charp, 0444);
5562306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for miro soundcard.");
5662306a36Sopenharmony_cimodule_param_hw(port, long, ioport, 0444);
5762306a36Sopenharmony_ciMODULE_PARM_DESC(port, "WSS port # for miro driver.");
5862306a36Sopenharmony_cimodule_param_hw(mpu_port, long, ioport, 0444);
5962306a36Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver.");
6062306a36Sopenharmony_cimodule_param_hw(fm_port, long, ioport, 0444);
6162306a36Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM Port # for miro driver.");
6262306a36Sopenharmony_cimodule_param_hw(irq, int, irq, 0444);
6362306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "WSS irq # for miro driver.");
6462306a36Sopenharmony_cimodule_param_hw(mpu_irq, int, irq, 0444);
6562306a36Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver.");
6662306a36Sopenharmony_cimodule_param_hw(dma1, int, dma, 0444);
6762306a36Sopenharmony_ciMODULE_PARM_DESC(dma1, "1st dma # for miro driver.");
6862306a36Sopenharmony_cimodule_param_hw(dma2, int, dma, 0444);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(dma2, "2nd dma # for miro driver.");
7062306a36Sopenharmony_cimodule_param(wss, int, 0444);
7162306a36Sopenharmony_ciMODULE_PARM_DESC(wss, "wss mode");
7262306a36Sopenharmony_cimodule_param(ide, int, 0444);
7362306a36Sopenharmony_ciMODULE_PARM_DESC(ide, "enable ide port");
7462306a36Sopenharmony_ci#ifdef CONFIG_PNP
7562306a36Sopenharmony_cimodule_param(isapnp, bool, 0444);
7662306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
7762306a36Sopenharmony_ci#endif
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define OPTi9XX_HW_DETECT	0
8062306a36Sopenharmony_ci#define OPTi9XX_HW_82C928	1
8162306a36Sopenharmony_ci#define OPTi9XX_HW_82C929	2
8262306a36Sopenharmony_ci#define OPTi9XX_HW_82C924	3
8362306a36Sopenharmony_ci#define OPTi9XX_HW_82C925	4
8462306a36Sopenharmony_ci#define OPTi9XX_HW_82C930	5
8562306a36Sopenharmony_ci#define OPTi9XX_HW_82C931	6
8662306a36Sopenharmony_ci#define OPTi9XX_HW_82C933	7
8762306a36Sopenharmony_ci#define OPTi9XX_HW_LAST		OPTi9XX_HW_82C933
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define OPTi9XX_MC_REG(n)	n
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistruct snd_miro {
9262306a36Sopenharmony_ci	unsigned short hardware;
9362306a36Sopenharmony_ci	unsigned char password;
9462306a36Sopenharmony_ci	char name[7];
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	struct resource *res_mc_base;
9762306a36Sopenharmony_ci	struct resource *res_aci_port;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	unsigned long mc_base;
10062306a36Sopenharmony_ci	unsigned long mc_base_size;
10162306a36Sopenharmony_ci	unsigned long pwd_reg;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	spinlock_t lock;
10462306a36Sopenharmony_ci	struct snd_pcm *pcm;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	long wss_base;
10762306a36Sopenharmony_ci	int irq;
10862306a36Sopenharmony_ci	int dma1;
10962306a36Sopenharmony_ci	int dma2;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	long mpu_port;
11262306a36Sopenharmony_ci	int mpu_irq;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	struct snd_miro_aci *aci;
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct snd_miro_aci aci_device;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic const char * const snd_opti9xx_names[] = {
12062306a36Sopenharmony_ci	"unknown",
12162306a36Sopenharmony_ci	"82C928", "82C929",
12262306a36Sopenharmony_ci	"82C924", "82C925",
12362306a36Sopenharmony_ci	"82C930", "82C931", "82C933"
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int snd_miro_pnp_is_probed;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci#ifdef CONFIG_PNP
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_miro_pnpids[] = {
13162306a36Sopenharmony_ci	/* PCM20 and PCM12 in PnP mode */
13262306a36Sopenharmony_ci	{ .id = "MIR0924",
13362306a36Sopenharmony_ci	  .devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, },
13462306a36Sopenharmony_ci	{ .id = "" }
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_miro_pnpids);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#endif	/* CONFIG_PNP */
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/*
14262306a36Sopenharmony_ci *  ACI control
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int aci_busy_wait(struct snd_miro_aci *aci)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	long timeout;
14862306a36Sopenharmony_ci	unsigned char byte;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	for (timeout = 1; timeout <= ACI_MINTIME + 30; timeout++) {
15162306a36Sopenharmony_ci		byte = inb(aci->aci_port + ACI_REG_BUSY);
15262306a36Sopenharmony_ci		if ((byte & 1) == 0) {
15362306a36Sopenharmony_ci			if (timeout >= ACI_MINTIME)
15462306a36Sopenharmony_ci				snd_printd("aci ready in round %ld.\n",
15562306a36Sopenharmony_ci					   timeout-ACI_MINTIME);
15662306a36Sopenharmony_ci			return byte;
15762306a36Sopenharmony_ci		}
15862306a36Sopenharmony_ci		if (timeout >= ACI_MINTIME) {
15962306a36Sopenharmony_ci			long out=10*HZ;
16062306a36Sopenharmony_ci			switch (timeout-ACI_MINTIME) {
16162306a36Sopenharmony_ci			case 0 ... 9:
16262306a36Sopenharmony_ci				out /= 10;
16362306a36Sopenharmony_ci				fallthrough;
16462306a36Sopenharmony_ci			case 10 ... 19:
16562306a36Sopenharmony_ci				out /= 10;
16662306a36Sopenharmony_ci				fallthrough;
16762306a36Sopenharmony_ci			case 20 ... 30:
16862306a36Sopenharmony_ci				out /= 10;
16962306a36Sopenharmony_ci				fallthrough;
17062306a36Sopenharmony_ci			default:
17162306a36Sopenharmony_ci				set_current_state(TASK_UNINTERRUPTIBLE);
17262306a36Sopenharmony_ci				schedule_timeout(out);
17362306a36Sopenharmony_ci				break;
17462306a36Sopenharmony_ci			}
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci	snd_printk(KERN_ERR "aci_busy_wait() time out\n");
17862306a36Sopenharmony_ci	return -EBUSY;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic inline int aci_write(struct snd_miro_aci *aci, unsigned char byte)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	if (aci_busy_wait(aci) >= 0) {
18462306a36Sopenharmony_ci		outb(byte, aci->aci_port + ACI_REG_COMMAND);
18562306a36Sopenharmony_ci		return 0;
18662306a36Sopenharmony_ci	} else {
18762306a36Sopenharmony_ci		snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
18862306a36Sopenharmony_ci		return -EBUSY;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic inline int aci_read(struct snd_miro_aci *aci)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	unsigned char byte;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (aci_busy_wait(aci) >= 0) {
19762306a36Sopenharmony_ci		byte = inb(aci->aci_port + ACI_REG_STATUS);
19862306a36Sopenharmony_ci		return byte;
19962306a36Sopenharmony_ci	} else {
20062306a36Sopenharmony_ci		snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
20162306a36Sopenharmony_ci		return -EBUSY;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ciint snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	int write[] = {write1, write2, write3};
20862306a36Sopenharmony_ci	int value, i;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (mutex_lock_interruptible(&aci->aci_mutex))
21162306a36Sopenharmony_ci		return -EINTR;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	for (i=0; i<3; i++) {
21462306a36Sopenharmony_ci		if (write[i]< 0 || write[i] > 255)
21562306a36Sopenharmony_ci			break;
21662306a36Sopenharmony_ci		else {
21762306a36Sopenharmony_ci			value = aci_write(aci, write[i]);
21862306a36Sopenharmony_ci			if (value < 0)
21962306a36Sopenharmony_ci				goto out;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	value = aci_read(aci);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ciout:	mutex_unlock(&aci->aci_mutex);
22662306a36Sopenharmony_ci	return value;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_aci_cmd);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int aci_getvalue(struct snd_miro_aci *aci, unsigned char index)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	return snd_aci_cmd(aci, ACI_STATUS, index, -1);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int aci_setvalue(struct snd_miro_aci *aci, unsigned char index,
23662306a36Sopenharmony_ci			int value)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	return snd_aci_cmd(aci, index, value, -1);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistruct snd_miro_aci *snd_aci_get_aci(void)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	if (aci_device.aci_port == 0)
24462306a36Sopenharmony_ci		return NULL;
24562306a36Sopenharmony_ci	return &aci_device;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_aci_get_aci);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/*
25062306a36Sopenharmony_ci *  MIXER part
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci#define snd_miro_info_capture	snd_ctl_boolean_mono_info
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic int snd_miro_get_capture(struct snd_kcontrol *kcontrol,
25662306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
25962306a36Sopenharmony_ci	int value;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	value = aci_getvalue(miro->aci, ACI_S_GENERAL);
26262306a36Sopenharmony_ci	if (value < 0) {
26362306a36Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n",
26462306a36Sopenharmony_ci			   value);
26562306a36Sopenharmony_ci		return value;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = value & 0x20;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return 0;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic int snd_miro_put_capture(struct snd_kcontrol *kcontrol,
27462306a36Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
27762306a36Sopenharmony_ci	int change, value, error;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	value = !(ucontrol->value.integer.value[0]);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value);
28262306a36Sopenharmony_ci	if (error < 0) {
28362306a36Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n",
28462306a36Sopenharmony_ci			   error);
28562306a36Sopenharmony_ci		return error;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	change = (value != miro->aci->aci_solomode);
28962306a36Sopenharmony_ci	miro->aci->aci_solomode = value;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return change;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic int snd_miro_info_preamp(struct snd_kcontrol *kcontrol,
29562306a36Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
29862306a36Sopenharmony_ci	uinfo->count = 1;
29962306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
30062306a36Sopenharmony_ci	uinfo->value.integer.max = 3;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
30662306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
30962306a36Sopenharmony_ci	int value;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (miro->aci->aci_version <= 176) {
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		/*
31462306a36Sopenharmony_ci		   OSS says it's not readable with versions < 176.
31562306a36Sopenharmony_ci		   But it doesn't work on my card,
31662306a36Sopenharmony_ci		   which is a PCM12 with aci_version = 176.
31762306a36Sopenharmony_ci		*/
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = miro->aci->aci_preamp;
32062306a36Sopenharmony_ci		return 0;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	value = aci_getvalue(miro->aci, ACI_GET_PREAMP);
32462306a36Sopenharmony_ci	if (value < 0) {
32562306a36Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n",
32662306a36Sopenharmony_ci			   value);
32762306a36Sopenharmony_ci		return value;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = value;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return 0;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int snd_miro_put_preamp(struct snd_kcontrol *kcontrol,
33662306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
33962306a36Sopenharmony_ci	int error, value, change;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	value = ucontrol->value.integer.value[0];
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value);
34462306a36Sopenharmony_ci	if (error < 0) {
34562306a36Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n",
34662306a36Sopenharmony_ci			   error);
34762306a36Sopenharmony_ci		return error;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	change = (value != miro->aci->aci_preamp);
35162306a36Sopenharmony_ci	miro->aci->aci_preamp = value;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return change;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci#define snd_miro_info_amp	snd_ctl_boolean_mono_info
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int snd_miro_get_amp(struct snd_kcontrol *kcontrol,
35962306a36Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
36262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = miro->aci->aci_amp;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return 0;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int snd_miro_put_amp(struct snd_kcontrol *kcontrol,
36862306a36Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
37162306a36Sopenharmony_ci	int error, value, change;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	value = ucontrol->value.integer.value[0];
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value);
37662306a36Sopenharmony_ci	if (error < 0) {
37762306a36Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
37862306a36Sopenharmony_ci		return error;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	change = (value != miro->aci->aci_amp);
38262306a36Sopenharmony_ci	miro->aci->aci_amp = value;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	return change;
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci#define MIRO_DOUBLE(ctl_name, ctl_index, get_right_reg, set_right_reg) \
38862306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
38962306a36Sopenharmony_ci  .name = ctl_name, \
39062306a36Sopenharmony_ci  .index = ctl_index, \
39162306a36Sopenharmony_ci  .info = snd_miro_info_double, \
39262306a36Sopenharmony_ci  .get = snd_miro_get_double, \
39362306a36Sopenharmony_ci  .put = snd_miro_put_double, \
39462306a36Sopenharmony_ci  .private_value = get_right_reg | (set_right_reg << 8) \
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int snd_miro_info_double(struct snd_kcontrol *kcontrol,
39862306a36Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
40362306a36Sopenharmony_ci	uinfo->count = 2;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if ((reg >= ACI_GET_EQ1) && (reg <= ACI_GET_EQ7)) {
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		/* equalizer elements */
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		uinfo->value.integer.min = - 0x7f;
41062306a36Sopenharmony_ci		uinfo->value.integer.max = 0x7f;
41162306a36Sopenharmony_ci	} else {
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		/* non-equalizer elements */
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		uinfo->value.integer.min = 0;
41662306a36Sopenharmony_ci		uinfo->value.integer.max = 0x20;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	return 0;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int snd_miro_get_double(struct snd_kcontrol *kcontrol,
42362306a36Sopenharmony_ci			       struct snd_ctl_elem_value *uinfo)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
42662306a36Sopenharmony_ci	int left_val, right_val;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	int right_reg = kcontrol->private_value & 0xff;
42962306a36Sopenharmony_ci	int left_reg = right_reg + 1;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	right_val = aci_getvalue(miro->aci, right_reg);
43262306a36Sopenharmony_ci	if (right_val < 0) {
43362306a36Sopenharmony_ci		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
43462306a36Sopenharmony_ci		return right_val;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	left_val = aci_getvalue(miro->aci, left_reg);
43862306a36Sopenharmony_ci	if (left_val < 0) {
43962306a36Sopenharmony_ci		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
44062306a36Sopenharmony_ci		return left_val;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if ((right_reg >= ACI_GET_EQ1) && (right_reg <= ACI_GET_EQ7)) {
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		/* equalizer elements */
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		if (left_val < 0x80) {
44862306a36Sopenharmony_ci			uinfo->value.integer.value[0] = left_val;
44962306a36Sopenharmony_ci		} else {
45062306a36Sopenharmony_ci			uinfo->value.integer.value[0] = 0x80 - left_val;
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		if (right_val < 0x80) {
45462306a36Sopenharmony_ci			uinfo->value.integer.value[1] = right_val;
45562306a36Sopenharmony_ci		} else {
45662306a36Sopenharmony_ci			uinfo->value.integer.value[1] = 0x80 - right_val;
45762306a36Sopenharmony_ci		}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	} else {
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/* non-equalizer elements */
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci		uinfo->value.integer.value[0] = 0x20 - left_val;
46462306a36Sopenharmony_ci		uinfo->value.integer.value[1] = 0x20 - right_val;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	return 0;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int snd_miro_put_double(struct snd_kcontrol *kcontrol,
47162306a36Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
47462306a36Sopenharmony_ci	struct snd_miro_aci *aci = miro->aci;
47562306a36Sopenharmony_ci	int left, right, left_old, right_old;
47662306a36Sopenharmony_ci	int setreg_left, setreg_right, getreg_left, getreg_right;
47762306a36Sopenharmony_ci	int change, error;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	left = ucontrol->value.integer.value[0];
48062306a36Sopenharmony_ci	right = ucontrol->value.integer.value[1];
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	setreg_right = (kcontrol->private_value >> 8) & 0xff;
48362306a36Sopenharmony_ci	setreg_left = setreg_right + 8;
48462306a36Sopenharmony_ci	if (setreg_right == ACI_SET_MASTER)
48562306a36Sopenharmony_ci		setreg_left -= 7;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	getreg_right = kcontrol->private_value & 0xff;
48862306a36Sopenharmony_ci	getreg_left = getreg_right + 1;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	left_old = aci_getvalue(aci, getreg_left);
49162306a36Sopenharmony_ci	if (left_old < 0) {
49262306a36Sopenharmony_ci		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
49362306a36Sopenharmony_ci		return left_old;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	right_old = aci_getvalue(aci, getreg_right);
49762306a36Sopenharmony_ci	if (right_old < 0) {
49862306a36Sopenharmony_ci		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
49962306a36Sopenharmony_ci		return right_old;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if ((getreg_right >= ACI_GET_EQ1) && (getreg_right <= ACI_GET_EQ7)) {
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		/* equalizer elements */
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		if (left < -0x7f || left > 0x7f ||
50762306a36Sopenharmony_ci		    right < -0x7f || right > 0x7f)
50862306a36Sopenharmony_ci			return -EINVAL;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		if (left_old > 0x80)
51162306a36Sopenharmony_ci			left_old = 0x80 - left_old;
51262306a36Sopenharmony_ci		if (right_old > 0x80)
51362306a36Sopenharmony_ci			right_old = 0x80 - right_old;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		if (left >= 0) {
51662306a36Sopenharmony_ci			error = aci_setvalue(aci, setreg_left, left);
51762306a36Sopenharmony_ci			if (error < 0) {
51862306a36Sopenharmony_ci				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
51962306a36Sopenharmony_ci					   left, error);
52062306a36Sopenharmony_ci				return error;
52162306a36Sopenharmony_ci			}
52262306a36Sopenharmony_ci		} else {
52362306a36Sopenharmony_ci			error = aci_setvalue(aci, setreg_left, 0x80 - left);
52462306a36Sopenharmony_ci			if (error < 0) {
52562306a36Sopenharmony_ci				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
52662306a36Sopenharmony_ci					   0x80 - left, error);
52762306a36Sopenharmony_ci				return error;
52862306a36Sopenharmony_ci			}
52962306a36Sopenharmony_ci		}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		if (right >= 0) {
53262306a36Sopenharmony_ci			error = aci_setvalue(aci, setreg_right, right);
53362306a36Sopenharmony_ci			if (error < 0) {
53462306a36Sopenharmony_ci				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
53562306a36Sopenharmony_ci					   right, error);
53662306a36Sopenharmony_ci				return error;
53762306a36Sopenharmony_ci			}
53862306a36Sopenharmony_ci		} else {
53962306a36Sopenharmony_ci			error = aci_setvalue(aci, setreg_right, 0x80 - right);
54062306a36Sopenharmony_ci			if (error < 0) {
54162306a36Sopenharmony_ci				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
54262306a36Sopenharmony_ci					   0x80 - right, error);
54362306a36Sopenharmony_ci				return error;
54462306a36Sopenharmony_ci			}
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	} else {
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		/* non-equalizer elements */
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		if (left < 0 || left > 0x20 ||
55262306a36Sopenharmony_ci		    right < 0 || right > 0x20)
55362306a36Sopenharmony_ci			return -EINVAL;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		left_old = 0x20 - left_old;
55662306a36Sopenharmony_ci		right_old = 0x20 - right_old;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		error = aci_setvalue(aci, setreg_left, 0x20 - left);
55962306a36Sopenharmony_ci		if (error < 0) {
56062306a36Sopenharmony_ci			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
56162306a36Sopenharmony_ci				   0x20 - left, error);
56262306a36Sopenharmony_ci			return error;
56362306a36Sopenharmony_ci		}
56462306a36Sopenharmony_ci		error = aci_setvalue(aci, setreg_right, 0x20 - right);
56562306a36Sopenharmony_ci		if (error < 0) {
56662306a36Sopenharmony_ci			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
56762306a36Sopenharmony_ci				   0x20 - right, error);
56862306a36Sopenharmony_ci			return error;
56962306a36Sopenharmony_ci		}
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	change = (left != left_old) || (right != right_old);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	return change;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_controls[] = {
57862306a36Sopenharmony_ciMIRO_DOUBLE("Master Playback Volume", 0, ACI_GET_MASTER, ACI_SET_MASTER),
57962306a36Sopenharmony_ciMIRO_DOUBLE("Mic Playback Volume", 1, ACI_GET_MIC, ACI_SET_MIC),
58062306a36Sopenharmony_ciMIRO_DOUBLE("Line Playback Volume", 1, ACI_GET_LINE, ACI_SET_LINE),
58162306a36Sopenharmony_ciMIRO_DOUBLE("CD Playback Volume", 0, ACI_GET_CD, ACI_SET_CD),
58262306a36Sopenharmony_ciMIRO_DOUBLE("Synth Playback Volume", 0, ACI_GET_SYNTH, ACI_SET_SYNTH),
58362306a36Sopenharmony_ciMIRO_DOUBLE("PCM Playback Volume", 1, ACI_GET_PCM, ACI_SET_PCM),
58462306a36Sopenharmony_ciMIRO_DOUBLE("Aux Playback Volume", 2, ACI_GET_LINE2, ACI_SET_LINE2),
58562306a36Sopenharmony_ci};
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci/* Equalizer with seven bands (only PCM20)
58862306a36Sopenharmony_ci   from -12dB up to +12dB on each band */
58962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_eq_controls[] = {
59062306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 28 Hz", 0, ACI_GET_EQ1, ACI_SET_EQ1),
59162306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 160 Hz", 0, ACI_GET_EQ2, ACI_SET_EQ2),
59262306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 400 Hz", 0, ACI_GET_EQ3, ACI_SET_EQ3),
59362306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 1 kHz", 0, ACI_GET_EQ4, ACI_SET_EQ4),
59462306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 2.5 kHz", 0, ACI_GET_EQ5, ACI_SET_EQ5),
59562306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 6.3 kHz", 0, ACI_GET_EQ6, ACI_SET_EQ6),
59662306a36Sopenharmony_ciMIRO_DOUBLE("Tone Control - 16 kHz", 0, ACI_GET_EQ7, ACI_SET_EQ7),
59762306a36Sopenharmony_ci};
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_radio_control[] = {
60062306a36Sopenharmony_ciMIRO_DOUBLE("Radio Playback Volume", 0, ACI_GET_LINE1, ACI_SET_LINE1),
60162306a36Sopenharmony_ci};
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_line_control[] = {
60462306a36Sopenharmony_ciMIRO_DOUBLE("Line Playback Volume", 2, ACI_GET_LINE1, ACI_SET_LINE1),
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_preamp_control[] = {
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
61062306a36Sopenharmony_ci	.name = "Mic Boost",
61162306a36Sopenharmony_ci	.index = 1,
61262306a36Sopenharmony_ci	.info = snd_miro_info_preamp,
61362306a36Sopenharmony_ci	.get = snd_miro_get_preamp,
61462306a36Sopenharmony_ci	.put = snd_miro_put_preamp,
61562306a36Sopenharmony_ci}};
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_amp_control[] = {
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
62062306a36Sopenharmony_ci	.name = "Line Boost",
62162306a36Sopenharmony_ci	.index = 0,
62262306a36Sopenharmony_ci	.info = snd_miro_info_amp,
62362306a36Sopenharmony_ci	.get = snd_miro_get_amp,
62462306a36Sopenharmony_ci	.put = snd_miro_put_amp,
62562306a36Sopenharmony_ci}};
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_capture_control[] = {
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
63062306a36Sopenharmony_ci	.name = "PCM Capture Switch",
63162306a36Sopenharmony_ci	.index = 0,
63262306a36Sopenharmony_ci	.info = snd_miro_info_capture,
63362306a36Sopenharmony_ci	.get = snd_miro_get_capture,
63462306a36Sopenharmony_ci	.put = snd_miro_put_capture,
63562306a36Sopenharmony_ci}};
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic const unsigned char aci_init_values[][2] = {
63862306a36Sopenharmony_ci	{ ACI_SET_MUTE, 0x00 },
63962306a36Sopenharmony_ci	{ ACI_SET_POWERAMP, 0x00 },
64062306a36Sopenharmony_ci	{ ACI_SET_PREAMP, 0x00 },
64162306a36Sopenharmony_ci	{ ACI_SET_SOLOMODE, 0x00 },
64262306a36Sopenharmony_ci	{ ACI_SET_MIC + 0, 0x20 },
64362306a36Sopenharmony_ci	{ ACI_SET_MIC + 8, 0x20 },
64462306a36Sopenharmony_ci	{ ACI_SET_LINE + 0, 0x20 },
64562306a36Sopenharmony_ci	{ ACI_SET_LINE + 8, 0x20 },
64662306a36Sopenharmony_ci	{ ACI_SET_CD + 0, 0x20 },
64762306a36Sopenharmony_ci	{ ACI_SET_CD + 8, 0x20 },
64862306a36Sopenharmony_ci	{ ACI_SET_PCM + 0, 0x20 },
64962306a36Sopenharmony_ci	{ ACI_SET_PCM + 8, 0x20 },
65062306a36Sopenharmony_ci	{ ACI_SET_LINE1 + 0, 0x20 },
65162306a36Sopenharmony_ci	{ ACI_SET_LINE1 + 8, 0x20 },
65262306a36Sopenharmony_ci	{ ACI_SET_LINE2 + 0, 0x20 },
65362306a36Sopenharmony_ci	{ ACI_SET_LINE2 + 8, 0x20 },
65462306a36Sopenharmony_ci	{ ACI_SET_SYNTH + 0, 0x20 },
65562306a36Sopenharmony_ci	{ ACI_SET_SYNTH + 8, 0x20 },
65662306a36Sopenharmony_ci	{ ACI_SET_MASTER + 0, 0x20 },
65762306a36Sopenharmony_ci	{ ACI_SET_MASTER + 1, 0x20 },
65862306a36Sopenharmony_ci};
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_cistatic int snd_set_aci_init_values(struct snd_miro *miro)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	int idx, error;
66362306a36Sopenharmony_ci	struct snd_miro_aci *aci = miro->aci;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	/* enable WSS on PCM1 */
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if ((aci->aci_product == 'A') && wss) {
66862306a36Sopenharmony_ci		error = aci_setvalue(aci, ACI_SET_WSS, wss);
66962306a36Sopenharmony_ci		if (error < 0) {
67062306a36Sopenharmony_ci			snd_printk(KERN_ERR "enabling WSS mode failed\n");
67162306a36Sopenharmony_ci			return error;
67262306a36Sopenharmony_ci		}
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* enable IDE port */
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (ide) {
67862306a36Sopenharmony_ci		error = aci_setvalue(aci, ACI_SET_IDE, ide);
67962306a36Sopenharmony_ci		if (error < 0) {
68062306a36Sopenharmony_ci			snd_printk(KERN_ERR "enabling IDE port failed\n");
68162306a36Sopenharmony_ci			return error;
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* set common aci values */
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++) {
68862306a36Sopenharmony_ci		error = aci_setvalue(aci, aci_init_values[idx][0],
68962306a36Sopenharmony_ci				     aci_init_values[idx][1]);
69062306a36Sopenharmony_ci		if (error < 0) {
69162306a36Sopenharmony_ci			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
69262306a36Sopenharmony_ci				   aci_init_values[idx][0], error);
69362306a36Sopenharmony_ci                        return error;
69462306a36Sopenharmony_ci                }
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci	aci->aci_amp = 0;
69762306a36Sopenharmony_ci	aci->aci_preamp = 0;
69862306a36Sopenharmony_ci	aci->aci_solomode = 1;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	return 0;
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cistatic int snd_miro_mixer(struct snd_card *card,
70462306a36Sopenharmony_ci			  struct snd_miro *miro)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	unsigned int idx;
70762306a36Sopenharmony_ci	int err;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (snd_BUG_ON(!miro || !card))
71062306a36Sopenharmony_ci		return -EINVAL;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	switch (miro->hardware) {
71362306a36Sopenharmony_ci	case OPTi9XX_HW_82C924:
71462306a36Sopenharmony_ci		strcpy(card->mixername, "ACI & OPTi924");
71562306a36Sopenharmony_ci		break;
71662306a36Sopenharmony_ci	case OPTi9XX_HW_82C929:
71762306a36Sopenharmony_ci		strcpy(card->mixername, "ACI & OPTi929");
71862306a36Sopenharmony_ci		break;
71962306a36Sopenharmony_ci	default:
72062306a36Sopenharmony_ci		snd_BUG();
72162306a36Sopenharmony_ci		break;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_miro_controls); idx++) {
72562306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro));
72662306a36Sopenharmony_ci		if (err < 0)
72762306a36Sopenharmony_ci			return err;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if ((miro->aci->aci_product == 'A') ||
73162306a36Sopenharmony_ci	    (miro->aci->aci_product == 'B')) {
73262306a36Sopenharmony_ci		/* PCM1/PCM12 with power-amp and Line 2 */
73362306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro));
73462306a36Sopenharmony_ci		if (err < 0)
73562306a36Sopenharmony_ci			return err;
73662306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro));
73762306a36Sopenharmony_ci		if (err < 0)
73862306a36Sopenharmony_ci			return err;
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if ((miro->aci->aci_product == 'B') ||
74262306a36Sopenharmony_ci	    (miro->aci->aci_product == 'C')) {
74362306a36Sopenharmony_ci		/* PCM12/PCM20 with mic-preamp */
74462306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro));
74562306a36Sopenharmony_ci		if (err < 0)
74662306a36Sopenharmony_ci			return err;
74762306a36Sopenharmony_ci		if (miro->aci->aci_version >= 176) {
74862306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro));
74962306a36Sopenharmony_ci			if (err < 0)
75062306a36Sopenharmony_ci				return err;
75162306a36Sopenharmony_ci		}
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (miro->aci->aci_product == 'C') {
75562306a36Sopenharmony_ci		/* PCM20 with radio and 7 band equalizer */
75662306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro));
75762306a36Sopenharmony_ci		if (err < 0)
75862306a36Sopenharmony_ci			return err;
75962306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_miro_eq_controls); idx++) {
76062306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro));
76162306a36Sopenharmony_ci			if (err < 0)
76262306a36Sopenharmony_ci				return err;
76362306a36Sopenharmony_ci		}
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	return 0;
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic int snd_miro_init(struct snd_miro *chip,
77062306a36Sopenharmony_ci			 unsigned short hardware)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	chip->hardware = hardware;
77562306a36Sopenharmony_ci	strcpy(chip->name, snd_opti9xx_names[hardware]);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	chip->mc_base_size = opti9xx_mc_size[hardware];
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	chip->wss_base = -1;
78262306a36Sopenharmony_ci	chip->irq = -1;
78362306a36Sopenharmony_ci	chip->dma1 = -1;
78462306a36Sopenharmony_ci	chip->dma2 = -1;
78562306a36Sopenharmony_ci	chip->mpu_port = -1;
78662306a36Sopenharmony_ci	chip->mpu_irq = -1;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	chip->pwd_reg = 3;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci#ifdef CONFIG_PNP
79162306a36Sopenharmony_ci	if (isapnp && chip->mc_base)
79262306a36Sopenharmony_ci		/* PnP resource gives the least 10 bits */
79362306a36Sopenharmony_ci		chip->mc_base |= 0xc00;
79462306a36Sopenharmony_ci	else
79562306a36Sopenharmony_ci#endif
79662306a36Sopenharmony_ci		chip->mc_base = 0xf8c;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	switch (hardware) {
79962306a36Sopenharmony_ci	case OPTi9XX_HW_82C929:
80062306a36Sopenharmony_ci		chip->password = 0xe3;
80162306a36Sopenharmony_ci		break;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	case OPTi9XX_HW_82C924:
80462306a36Sopenharmony_ci		chip->password = 0xe5;
80562306a36Sopenharmony_ci		break;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	default:
80862306a36Sopenharmony_ci		snd_printk(KERN_ERR "sorry, no support for %d\n", hardware);
80962306a36Sopenharmony_ci		return -ENODEV;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return 0;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic unsigned char snd_miro_read(struct snd_miro *chip,
81662306a36Sopenharmony_ci				   unsigned char reg)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	unsigned long flags;
81962306a36Sopenharmony_ci	unsigned char retval = 0xff;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
82262306a36Sopenharmony_ci	outb(chip->password, chip->mc_base + chip->pwd_reg);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	switch (chip->hardware) {
82562306a36Sopenharmony_ci	case OPTi9XX_HW_82C924:
82662306a36Sopenharmony_ci		if (reg > 7) {
82762306a36Sopenharmony_ci			outb(reg, chip->mc_base + 8);
82862306a36Sopenharmony_ci			outb(chip->password, chip->mc_base + chip->pwd_reg);
82962306a36Sopenharmony_ci			retval = inb(chip->mc_base + 9);
83062306a36Sopenharmony_ci			break;
83162306a36Sopenharmony_ci		}
83262306a36Sopenharmony_ci		fallthrough;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	case OPTi9XX_HW_82C929:
83562306a36Sopenharmony_ci		retval = inb(chip->mc_base + reg);
83662306a36Sopenharmony_ci		break;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	default:
83962306a36Sopenharmony_ci		snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
84362306a36Sopenharmony_ci	return retval;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void snd_miro_write(struct snd_miro *chip, unsigned char reg,
84762306a36Sopenharmony_ci			   unsigned char value)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	unsigned long flags;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
85262306a36Sopenharmony_ci	outb(chip->password, chip->mc_base + chip->pwd_reg);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	switch (chip->hardware) {
85562306a36Sopenharmony_ci	case OPTi9XX_HW_82C924:
85662306a36Sopenharmony_ci		if (reg > 7) {
85762306a36Sopenharmony_ci			outb(reg, chip->mc_base + 8);
85862306a36Sopenharmony_ci			outb(chip->password, chip->mc_base + chip->pwd_reg);
85962306a36Sopenharmony_ci			outb(value, chip->mc_base + 9);
86062306a36Sopenharmony_ci			break;
86162306a36Sopenharmony_ci		}
86262306a36Sopenharmony_ci		fallthrough;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	case OPTi9XX_HW_82C929:
86562306a36Sopenharmony_ci		outb(value, chip->mc_base + reg);
86662306a36Sopenharmony_ci		break;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	default:
86962306a36Sopenharmony_ci		snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic inline void snd_miro_write_mask(struct snd_miro *chip,
87662306a36Sopenharmony_ci		unsigned char reg, unsigned char value, unsigned char mask)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	unsigned char oldval = snd_miro_read(chip, reg);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask));
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci/*
88462306a36Sopenharmony_ci *  Proc Interface
88562306a36Sopenharmony_ci */
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic void snd_miro_proc_read(struct snd_info_entry * entry,
88862306a36Sopenharmony_ci			       struct snd_info_buffer *buffer)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct snd_miro *miro = (struct snd_miro *) entry->private_data;
89162306a36Sopenharmony_ci	struct snd_miro_aci *aci = miro->aci;
89262306a36Sopenharmony_ci	char* model = "unknown";
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* miroSOUND PCM1 pro, early PCM12 */
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	if ((miro->hardware == OPTi9XX_HW_82C929) &&
89762306a36Sopenharmony_ci	    (aci->aci_vendor == 'm') &&
89862306a36Sopenharmony_ci	    (aci->aci_product == 'A')) {
89962306a36Sopenharmony_ci		switch (aci->aci_version) {
90062306a36Sopenharmony_ci		case 3:
90162306a36Sopenharmony_ci			model = "miroSOUND PCM1 pro";
90262306a36Sopenharmony_ci			break;
90362306a36Sopenharmony_ci		default:
90462306a36Sopenharmony_ci			model = "miroSOUND PCM1 pro / (early) PCM12";
90562306a36Sopenharmony_ci			break;
90662306a36Sopenharmony_ci		}
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	if ((miro->hardware == OPTi9XX_HW_82C924) &&
91262306a36Sopenharmony_ci	    (aci->aci_vendor == 'm') &&
91362306a36Sopenharmony_ci	    (aci->aci_product == 'B')) {
91462306a36Sopenharmony_ci		switch (aci->aci_version) {
91562306a36Sopenharmony_ci		case 4:
91662306a36Sopenharmony_ci			model = "miroSOUND PCM12";
91762306a36Sopenharmony_ci			break;
91862306a36Sopenharmony_ci		case 176:
91962306a36Sopenharmony_ci			model = "miroSOUND PCM12 (Rev. E)";
92062306a36Sopenharmony_ci			break;
92162306a36Sopenharmony_ci		default:
92262306a36Sopenharmony_ci			model = "miroSOUND PCM12 / PCM12 pnp";
92362306a36Sopenharmony_ci			break;
92462306a36Sopenharmony_ci		}
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	/* miroSOUND PCM20 radio */
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if ((miro->hardware == OPTi9XX_HW_82C924) &&
93062306a36Sopenharmony_ci	    (aci->aci_vendor == 'm') &&
93162306a36Sopenharmony_ci	    (aci->aci_product == 'C')) {
93262306a36Sopenharmony_ci		switch (aci->aci_version) {
93362306a36Sopenharmony_ci		case 7:
93462306a36Sopenharmony_ci			model = "miroSOUND PCM20 radio (Rev. E)";
93562306a36Sopenharmony_ci			break;
93662306a36Sopenharmony_ci		default:
93762306a36Sopenharmony_ci			model = "miroSOUND PCM20 radio";
93862306a36Sopenharmony_ci			break;
93962306a36Sopenharmony_ci		}
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	snd_iprintf(buffer, "\nGeneral information:\n");
94362306a36Sopenharmony_ci	snd_iprintf(buffer, "  model   : %s\n", model);
94462306a36Sopenharmony_ci	snd_iprintf(buffer, "  opti    : %s\n", miro->name);
94562306a36Sopenharmony_ci	snd_iprintf(buffer, "  codec   : %s\n", miro->pcm->name);
94662306a36Sopenharmony_ci	snd_iprintf(buffer, "  port    : 0x%lx\n", miro->wss_base);
94762306a36Sopenharmony_ci	snd_iprintf(buffer, "  irq     : %d\n", miro->irq);
94862306a36Sopenharmony_ci	snd_iprintf(buffer, "  dma     : %d,%d\n\n", miro->dma1, miro->dma2);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	snd_iprintf(buffer, "MPU-401:\n");
95162306a36Sopenharmony_ci	snd_iprintf(buffer, "  port    : 0x%lx\n", miro->mpu_port);
95262306a36Sopenharmony_ci	snd_iprintf(buffer, "  irq     : %d\n\n", miro->mpu_irq);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	snd_iprintf(buffer, "ACI information:\n");
95562306a36Sopenharmony_ci	snd_iprintf(buffer, "  vendor  : ");
95662306a36Sopenharmony_ci	switch (aci->aci_vendor) {
95762306a36Sopenharmony_ci	case 'm':
95862306a36Sopenharmony_ci		snd_iprintf(buffer, "Miro\n");
95962306a36Sopenharmony_ci		break;
96062306a36Sopenharmony_ci	default:
96162306a36Sopenharmony_ci		snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_vendor);
96262306a36Sopenharmony_ci		break;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	snd_iprintf(buffer, "  product : ");
96662306a36Sopenharmony_ci	switch (aci->aci_product) {
96762306a36Sopenharmony_ci	case 'A':
96862306a36Sopenharmony_ci		snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n");
96962306a36Sopenharmony_ci		break;
97062306a36Sopenharmony_ci	case 'B':
97162306a36Sopenharmony_ci		snd_iprintf(buffer, "miroSOUND PCM12\n");
97262306a36Sopenharmony_ci		break;
97362306a36Sopenharmony_ci	case 'C':
97462306a36Sopenharmony_ci		snd_iprintf(buffer, "miroSOUND PCM20 radio\n");
97562306a36Sopenharmony_ci		break;
97662306a36Sopenharmony_ci	default:
97762306a36Sopenharmony_ci		snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_product);
97862306a36Sopenharmony_ci		break;
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	snd_iprintf(buffer, "  firmware: %d (0x%x)\n",
98262306a36Sopenharmony_ci		    aci->aci_version, aci->aci_version);
98362306a36Sopenharmony_ci	snd_iprintf(buffer, "  port    : 0x%lx-0x%lx\n",
98462306a36Sopenharmony_ci		    aci->aci_port, aci->aci_port+2);
98562306a36Sopenharmony_ci	snd_iprintf(buffer, "  wss     : 0x%x\n", wss);
98662306a36Sopenharmony_ci	snd_iprintf(buffer, "  ide     : 0x%x\n", ide);
98762306a36Sopenharmony_ci	snd_iprintf(buffer, "  solomode: 0x%x\n", aci->aci_solomode);
98862306a36Sopenharmony_ci	snd_iprintf(buffer, "  amp     : 0x%x\n", aci->aci_amp);
98962306a36Sopenharmony_ci	snd_iprintf(buffer, "  preamp  : 0x%x\n", aci->aci_preamp);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_cistatic void snd_miro_proc_init(struct snd_card *card,
99362306a36Sopenharmony_ci			       struct snd_miro *miro)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	snd_card_ro_proc_new(card, "miro", miro, snd_miro_proc_read);
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci/*
99962306a36Sopenharmony_ci *  Init
100062306a36Sopenharmony_ci */
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cistatic int snd_miro_configure(struct snd_miro *chip)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	unsigned char wss_base_bits;
100562306a36Sopenharmony_ci	unsigned char irq_bits;
100662306a36Sopenharmony_ci	unsigned char dma_bits;
100762306a36Sopenharmony_ci	unsigned char mpu_port_bits = 0;
100862306a36Sopenharmony_ci	unsigned char mpu_irq_bits;
100962306a36Sopenharmony_ci	unsigned long flags;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
101262306a36Sopenharmony_ci	snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
101362306a36Sopenharmony_ci	snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	switch (chip->hardware) {
101662306a36Sopenharmony_ci	case OPTi9XX_HW_82C924:
101762306a36Sopenharmony_ci		snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
101862306a36Sopenharmony_ci		snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
101962306a36Sopenharmony_ci		break;
102062306a36Sopenharmony_ci	case OPTi9XX_HW_82C929:
102162306a36Sopenharmony_ci		/* untested init commands for OPTi929 */
102262306a36Sopenharmony_ci		snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
102362306a36Sopenharmony_ci		break;
102462306a36Sopenharmony_ci	default:
102562306a36Sopenharmony_ci		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
102662306a36Sopenharmony_ci		return -EINVAL;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	/* PnP resource says it decodes only 10 bits of address */
103062306a36Sopenharmony_ci	switch (chip->wss_base & 0x3ff) {
103162306a36Sopenharmony_ci	case 0x130:
103262306a36Sopenharmony_ci		chip->wss_base = 0x530;
103362306a36Sopenharmony_ci		wss_base_bits = 0x00;
103462306a36Sopenharmony_ci		break;
103562306a36Sopenharmony_ci	case 0x204:
103662306a36Sopenharmony_ci		chip->wss_base = 0x604;
103762306a36Sopenharmony_ci		wss_base_bits = 0x03;
103862306a36Sopenharmony_ci		break;
103962306a36Sopenharmony_ci	case 0x280:
104062306a36Sopenharmony_ci		chip->wss_base = 0xe80;
104162306a36Sopenharmony_ci		wss_base_bits = 0x01;
104262306a36Sopenharmony_ci		break;
104362306a36Sopenharmony_ci	case 0x340:
104462306a36Sopenharmony_ci		chip->wss_base = 0xf40;
104562306a36Sopenharmony_ci		wss_base_bits = 0x02;
104662306a36Sopenharmony_ci		break;
104762306a36Sopenharmony_ci	default:
104862306a36Sopenharmony_ci		snd_printk(KERN_ERR "WSS port 0x%lx not valid\n", chip->wss_base);
104962306a36Sopenharmony_ci		goto __skip_base;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci	snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci__skip_base:
105462306a36Sopenharmony_ci	switch (chip->irq) {
105562306a36Sopenharmony_ci	case 5:
105662306a36Sopenharmony_ci		irq_bits = 0x05;
105762306a36Sopenharmony_ci		break;
105862306a36Sopenharmony_ci	case 7:
105962306a36Sopenharmony_ci		irq_bits = 0x01;
106062306a36Sopenharmony_ci		break;
106162306a36Sopenharmony_ci	case 9:
106262306a36Sopenharmony_ci		irq_bits = 0x02;
106362306a36Sopenharmony_ci		break;
106462306a36Sopenharmony_ci	case 10:
106562306a36Sopenharmony_ci		irq_bits = 0x03;
106662306a36Sopenharmony_ci		break;
106762306a36Sopenharmony_ci	case 11:
106862306a36Sopenharmony_ci		irq_bits = 0x04;
106962306a36Sopenharmony_ci		break;
107062306a36Sopenharmony_ci	default:
107162306a36Sopenharmony_ci		snd_printk(KERN_ERR "WSS irq # %d not valid\n", chip->irq);
107262306a36Sopenharmony_ci		goto __skip_resources;
107362306a36Sopenharmony_ci	}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	switch (chip->dma1) {
107662306a36Sopenharmony_ci	case 0:
107762306a36Sopenharmony_ci		dma_bits = 0x01;
107862306a36Sopenharmony_ci		break;
107962306a36Sopenharmony_ci	case 1:
108062306a36Sopenharmony_ci		dma_bits = 0x02;
108162306a36Sopenharmony_ci		break;
108262306a36Sopenharmony_ci	case 3:
108362306a36Sopenharmony_ci		dma_bits = 0x03;
108462306a36Sopenharmony_ci		break;
108562306a36Sopenharmony_ci	default:
108662306a36Sopenharmony_ci		snd_printk(KERN_ERR "WSS dma1 # %d not valid\n", chip->dma1);
108762306a36Sopenharmony_ci		goto __skip_resources;
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	if (chip->dma1 == chip->dma2) {
109162306a36Sopenharmony_ci		snd_printk(KERN_ERR "don't want to share dmas\n");
109262306a36Sopenharmony_ci		return -EBUSY;
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	switch (chip->dma2) {
109662306a36Sopenharmony_ci	case 0:
109762306a36Sopenharmony_ci	case 1:
109862306a36Sopenharmony_ci		break;
109962306a36Sopenharmony_ci	default:
110062306a36Sopenharmony_ci		snd_printk(KERN_ERR "WSS dma2 # %d not valid\n", chip->dma2);
110162306a36Sopenharmony_ci		goto __skip_resources;
110262306a36Sopenharmony_ci	}
110362306a36Sopenharmony_ci	dma_bits |= 0x04;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
110662306a36Sopenharmony_ci	outb(irq_bits << 3 | dma_bits, chip->wss_base);
110762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci__skip_resources:
111062306a36Sopenharmony_ci	if (chip->hardware > OPTi9XX_HW_82C928) {
111162306a36Sopenharmony_ci		switch (chip->mpu_port) {
111262306a36Sopenharmony_ci		case 0:
111362306a36Sopenharmony_ci		case -1:
111462306a36Sopenharmony_ci			break;
111562306a36Sopenharmony_ci		case 0x300:
111662306a36Sopenharmony_ci			mpu_port_bits = 0x03;
111762306a36Sopenharmony_ci			break;
111862306a36Sopenharmony_ci		case 0x310:
111962306a36Sopenharmony_ci			mpu_port_bits = 0x02;
112062306a36Sopenharmony_ci			break;
112162306a36Sopenharmony_ci		case 0x320:
112262306a36Sopenharmony_ci			mpu_port_bits = 0x01;
112362306a36Sopenharmony_ci			break;
112462306a36Sopenharmony_ci		case 0x330:
112562306a36Sopenharmony_ci			mpu_port_bits = 0x00;
112662306a36Sopenharmony_ci			break;
112762306a36Sopenharmony_ci		default:
112862306a36Sopenharmony_ci			snd_printk(KERN_ERR "MPU-401 port 0x%lx not valid\n",
112962306a36Sopenharmony_ci				   chip->mpu_port);
113062306a36Sopenharmony_ci			goto __skip_mpu;
113162306a36Sopenharmony_ci		}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		switch (chip->mpu_irq) {
113462306a36Sopenharmony_ci		case 5:
113562306a36Sopenharmony_ci			mpu_irq_bits = 0x02;
113662306a36Sopenharmony_ci			break;
113762306a36Sopenharmony_ci		case 7:
113862306a36Sopenharmony_ci			mpu_irq_bits = 0x03;
113962306a36Sopenharmony_ci			break;
114062306a36Sopenharmony_ci		case 9:
114162306a36Sopenharmony_ci			mpu_irq_bits = 0x00;
114262306a36Sopenharmony_ci			break;
114362306a36Sopenharmony_ci		case 10:
114462306a36Sopenharmony_ci			mpu_irq_bits = 0x01;
114562306a36Sopenharmony_ci			break;
114662306a36Sopenharmony_ci		default:
114762306a36Sopenharmony_ci			snd_printk(KERN_ERR "MPU-401 irq # %d not valid\n",
114862306a36Sopenharmony_ci				   chip->mpu_irq);
114962306a36Sopenharmony_ci			goto __skip_mpu;
115062306a36Sopenharmony_ci		}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci		snd_miro_write_mask(chip, OPTi9XX_MC_REG(6),
115362306a36Sopenharmony_ci			(chip->mpu_port <= 0) ? 0x00 :
115462306a36Sopenharmony_ci				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
115562306a36Sopenharmony_ci			0xf8);
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci__skip_mpu:
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	return 0;
116062306a36Sopenharmony_ci}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_cistatic int snd_miro_opti_check(struct snd_card *card, struct snd_miro *chip)
116362306a36Sopenharmony_ci{
116462306a36Sopenharmony_ci	unsigned char value;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	chip->res_mc_base =
116762306a36Sopenharmony_ci		devm_request_region(card->dev, chip->mc_base,
116862306a36Sopenharmony_ci				    chip->mc_base_size, "OPTi9xx MC");
116962306a36Sopenharmony_ci	if (chip->res_mc_base == NULL)
117062306a36Sopenharmony_ci		return -ENOMEM;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	value = snd_miro_read(chip, OPTi9XX_MC_REG(1));
117362306a36Sopenharmony_ci	if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
117462306a36Sopenharmony_ci		if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
117562306a36Sopenharmony_ci			return 0;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	devm_release_resource(card->dev, chip->res_mc_base);
117862306a36Sopenharmony_ci	chip->res_mc_base = NULL;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	return -ENODEV;
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic int snd_card_miro_detect(struct snd_card *card,
118462306a36Sopenharmony_ci				struct snd_miro *chip)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	int i, err;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci		err = snd_miro_init(chip, i);
119162306a36Sopenharmony_ci		if (err < 0)
119262306a36Sopenharmony_ci			return err;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		err = snd_miro_opti_check(card, chip);
119562306a36Sopenharmony_ci		if (err == 0)
119662306a36Sopenharmony_ci			return 1;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	return -ENODEV;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic int snd_card_miro_aci_detect(struct snd_card *card,
120362306a36Sopenharmony_ci				    struct snd_miro *miro)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	unsigned char regval;
120662306a36Sopenharmony_ci	int i;
120762306a36Sopenharmony_ci	struct snd_miro_aci *aci = &aci_device;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	miro->aci = aci;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	mutex_init(&aci->aci_mutex);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	/* get ACI port from OPTi9xx MC 4 */
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	regval=inb(miro->mc_base + 4);
121662306a36Sopenharmony_ci	aci->aci_port = (regval & 0x10) ? 0x344 : 0x354;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	miro->res_aci_port =
121962306a36Sopenharmony_ci		devm_request_region(card->dev, aci->aci_port, 3, "miro aci");
122062306a36Sopenharmony_ci	if (miro->res_aci_port == NULL) {
122162306a36Sopenharmony_ci		snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n",
122262306a36Sopenharmony_ci			   aci->aci_port, aci->aci_port+2);
122362306a36Sopenharmony_ci		return -ENOMEM;
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci        /* force ACI into a known state */
122762306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
122862306a36Sopenharmony_ci		if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) {
122962306a36Sopenharmony_ci			snd_printk(KERN_ERR "can't force aci into known state.\n");
123062306a36Sopenharmony_ci			return -ENXIO;
123162306a36Sopenharmony_ci		}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
123462306a36Sopenharmony_ci	aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
123562306a36Sopenharmony_ci	if (aci->aci_vendor < 0 || aci->aci_product < 0) {
123662306a36Sopenharmony_ci		snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n",
123762306a36Sopenharmony_ci			   aci->aci_port);
123862306a36Sopenharmony_ci		return -ENXIO;
123962306a36Sopenharmony_ci	}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1);
124262306a36Sopenharmony_ci	if (aci->aci_version < 0) {
124362306a36Sopenharmony_ci		snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n",
124462306a36Sopenharmony_ci			   aci->aci_port);
124562306a36Sopenharmony_ci		return -ENXIO;
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 ||
124962306a36Sopenharmony_ci	    snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
125062306a36Sopenharmony_ci	    snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
125162306a36Sopenharmony_ci		snd_printk(KERN_ERR "can't initialize aci.\n");
125262306a36Sopenharmony_ci		return -ENXIO;
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	return 0;
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_cistatic int snd_miro_probe(struct snd_card *card)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	int error;
126162306a36Sopenharmony_ci	struct snd_miro *miro = card->private_data;
126262306a36Sopenharmony_ci	struct snd_wss *codec;
126362306a36Sopenharmony_ci	struct snd_rawmidi *rmidi;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	if (!miro->res_mc_base) {
126662306a36Sopenharmony_ci		miro->res_mc_base = devm_request_region(card->dev,
126762306a36Sopenharmony_ci							miro->mc_base,
126862306a36Sopenharmony_ci							miro->mc_base_size,
126962306a36Sopenharmony_ci							"miro (OPTi9xx MC)");
127062306a36Sopenharmony_ci		if (miro->res_mc_base == NULL) {
127162306a36Sopenharmony_ci			snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
127262306a36Sopenharmony_ci			return -ENOMEM;
127362306a36Sopenharmony_ci		}
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	error = snd_card_miro_aci_detect(card, miro);
127762306a36Sopenharmony_ci	if (error < 0) {
127862306a36Sopenharmony_ci		snd_printk(KERN_ERR "unable to detect aci chip\n");
127962306a36Sopenharmony_ci		return -ENODEV;
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	miro->wss_base = port;
128362306a36Sopenharmony_ci	miro->mpu_port = mpu_port;
128462306a36Sopenharmony_ci	miro->irq = irq;
128562306a36Sopenharmony_ci	miro->mpu_irq = mpu_irq;
128662306a36Sopenharmony_ci	miro->dma1 = dma1;
128762306a36Sopenharmony_ci	miro->dma2 = dma2;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	/* init proc interface */
129062306a36Sopenharmony_ci	snd_miro_proc_init(card, miro);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	error = snd_miro_configure(miro);
129362306a36Sopenharmony_ci	if (error)
129462306a36Sopenharmony_ci		return error;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	error = snd_wss_create(card, miro->wss_base + 4, -1,
129762306a36Sopenharmony_ci			       miro->irq, miro->dma1, miro->dma2,
129862306a36Sopenharmony_ci			       WSS_HW_DETECT, 0, &codec);
129962306a36Sopenharmony_ci	if (error < 0)
130062306a36Sopenharmony_ci		return error;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	error = snd_wss_pcm(codec, 0);
130362306a36Sopenharmony_ci	if (error < 0)
130462306a36Sopenharmony_ci		return error;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	error = snd_wss_mixer(codec);
130762306a36Sopenharmony_ci	if (error < 0)
130862306a36Sopenharmony_ci		return error;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	error = snd_wss_timer(codec, 0);
131162306a36Sopenharmony_ci	if (error < 0)
131262306a36Sopenharmony_ci		return error;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	miro->pcm = codec->pcm;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	error = snd_miro_mixer(card, miro);
131762306a36Sopenharmony_ci	if (error < 0)
131862306a36Sopenharmony_ci		return error;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	if (miro->aci->aci_vendor == 'm') {
132162306a36Sopenharmony_ci		/* It looks like a miro sound card. */
132262306a36Sopenharmony_ci		switch (miro->aci->aci_product) {
132362306a36Sopenharmony_ci		case 'A':
132462306a36Sopenharmony_ci			sprintf(card->shortname,
132562306a36Sopenharmony_ci				"miroSOUND PCM1 pro / PCM12");
132662306a36Sopenharmony_ci			break;
132762306a36Sopenharmony_ci		case 'B':
132862306a36Sopenharmony_ci			sprintf(card->shortname,
132962306a36Sopenharmony_ci				"miroSOUND PCM12");
133062306a36Sopenharmony_ci			break;
133162306a36Sopenharmony_ci		case 'C':
133262306a36Sopenharmony_ci			sprintf(card->shortname,
133362306a36Sopenharmony_ci				"miroSOUND PCM20 radio");
133462306a36Sopenharmony_ci			break;
133562306a36Sopenharmony_ci		default:
133662306a36Sopenharmony_ci			sprintf(card->shortname,
133762306a36Sopenharmony_ci				"unknown miro");
133862306a36Sopenharmony_ci			snd_printk(KERN_INFO "unknown miro aci id\n");
133962306a36Sopenharmony_ci			break;
134062306a36Sopenharmony_ci		}
134162306a36Sopenharmony_ci	} else {
134262306a36Sopenharmony_ci		snd_printk(KERN_INFO "found unsupported aci card\n");
134362306a36Sopenharmony_ci		sprintf(card->shortname, "unknown Cardinal Technologies");
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	strcpy(card->driver, "miro");
134762306a36Sopenharmony_ci	scnprintf(card->longname, sizeof(card->longname),
134862306a36Sopenharmony_ci		  "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d",
134962306a36Sopenharmony_ci		  card->shortname, miro->name, codec->pcm->name,
135062306a36Sopenharmony_ci		  miro->wss_base + 4, miro->irq, miro->dma1, miro->dma2);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
135362306a36Sopenharmony_ci		rmidi = NULL;
135462306a36Sopenharmony_ci	else {
135562306a36Sopenharmony_ci		error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
135662306a36Sopenharmony_ci				mpu_port, 0, miro->mpu_irq, &rmidi);
135762306a36Sopenharmony_ci		if (error < 0)
135862306a36Sopenharmony_ci			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
135962306a36Sopenharmony_ci				   mpu_port);
136062306a36Sopenharmony_ci	}
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
136362306a36Sopenharmony_ci		struct snd_opl3 *opl3 = NULL;
136462306a36Sopenharmony_ci		struct snd_opl4 *opl4;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci		if (snd_opl4_create(card, fm_port, fm_port - 8,
136762306a36Sopenharmony_ci				    2, &opl3, &opl4) < 0)
136862306a36Sopenharmony_ci			snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n",
136962306a36Sopenharmony_ci				   fm_port);
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	error = snd_set_aci_init_values(miro);
137362306a36Sopenharmony_ci	if (error < 0)
137462306a36Sopenharmony_ci                return error;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	return snd_card_register(card);
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic int snd_miro_isa_match(struct device *devptr, unsigned int n)
138062306a36Sopenharmony_ci{
138162306a36Sopenharmony_ci#ifdef CONFIG_PNP
138262306a36Sopenharmony_ci	if (snd_miro_pnp_is_probed)
138362306a36Sopenharmony_ci		return 0;
138462306a36Sopenharmony_ci	if (isapnp)
138562306a36Sopenharmony_ci		return 0;
138662306a36Sopenharmony_ci#endif
138762306a36Sopenharmony_ci	return 1;
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic int snd_miro_isa_probe(struct device *devptr, unsigned int n)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
139362306a36Sopenharmony_ci	static const long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
139462306a36Sopenharmony_ci	static const int possible_irqs[] = {11, 9, 10, 7, -1};
139562306a36Sopenharmony_ci	static const int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
139662306a36Sopenharmony_ci	static const int possible_dma1s[] = {3, 1, 0, -1};
139762306a36Sopenharmony_ci	static const int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1},
139862306a36Sopenharmony_ci					   {0, -1} };
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	int error;
140162306a36Sopenharmony_ci	struct snd_miro *miro;
140262306a36Sopenharmony_ci	struct snd_card *card;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	error = snd_devm_card_new(devptr, index, id, THIS_MODULE,
140562306a36Sopenharmony_ci				  sizeof(struct snd_miro), &card);
140662306a36Sopenharmony_ci	if (error < 0)
140762306a36Sopenharmony_ci		return error;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	miro = card->private_data;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	error = snd_card_miro_detect(card, miro);
141262306a36Sopenharmony_ci	if (error < 0) {
141362306a36Sopenharmony_ci		snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
141462306a36Sopenharmony_ci		return -ENODEV;
141562306a36Sopenharmony_ci	}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	if (port == SNDRV_AUTO_PORT) {
141862306a36Sopenharmony_ci		port = snd_legacy_find_free_ioport(possible_ports, 4);
141962306a36Sopenharmony_ci		if (port < 0) {
142062306a36Sopenharmony_ci			snd_printk(KERN_ERR "unable to find a free WSS port\n");
142162306a36Sopenharmony_ci			return -EBUSY;
142262306a36Sopenharmony_ci		}
142362306a36Sopenharmony_ci	}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (mpu_port == SNDRV_AUTO_PORT) {
142662306a36Sopenharmony_ci		mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
142762306a36Sopenharmony_ci		if (mpu_port < 0) {
142862306a36Sopenharmony_ci			snd_printk(KERN_ERR
142962306a36Sopenharmony_ci				   "unable to find a free MPU401 port\n");
143062306a36Sopenharmony_ci			return -EBUSY;
143162306a36Sopenharmony_ci		}
143262306a36Sopenharmony_ci	}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	if (irq == SNDRV_AUTO_IRQ) {
143562306a36Sopenharmony_ci		irq = snd_legacy_find_free_irq(possible_irqs);
143662306a36Sopenharmony_ci		if (irq < 0) {
143762306a36Sopenharmony_ci			snd_printk(KERN_ERR "unable to find a free IRQ\n");
143862306a36Sopenharmony_ci			return -EBUSY;
143962306a36Sopenharmony_ci		}
144062306a36Sopenharmony_ci	}
144162306a36Sopenharmony_ci	if (mpu_irq == SNDRV_AUTO_IRQ) {
144262306a36Sopenharmony_ci		mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
144362306a36Sopenharmony_ci		if (mpu_irq < 0) {
144462306a36Sopenharmony_ci			snd_printk(KERN_ERR
144562306a36Sopenharmony_ci				   "unable to find a free MPU401 IRQ\n");
144662306a36Sopenharmony_ci			return -EBUSY;
144762306a36Sopenharmony_ci		}
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci	if (dma1 == SNDRV_AUTO_DMA) {
145062306a36Sopenharmony_ci		dma1 = snd_legacy_find_free_dma(possible_dma1s);
145162306a36Sopenharmony_ci		if (dma1 < 0) {
145262306a36Sopenharmony_ci			snd_printk(KERN_ERR "unable to find a free DMA1\n");
145362306a36Sopenharmony_ci			return -EBUSY;
145462306a36Sopenharmony_ci		}
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci	if (dma2 == SNDRV_AUTO_DMA) {
145762306a36Sopenharmony_ci		dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
145862306a36Sopenharmony_ci		if (dma2 < 0) {
145962306a36Sopenharmony_ci			snd_printk(KERN_ERR "unable to find a free DMA2\n");
146062306a36Sopenharmony_ci			return -EBUSY;
146162306a36Sopenharmony_ci		}
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	error = snd_miro_probe(card);
146562306a36Sopenharmony_ci	if (error < 0)
146662306a36Sopenharmony_ci		return error;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	dev_set_drvdata(devptr, card);
146962306a36Sopenharmony_ci	return 0;
147062306a36Sopenharmony_ci}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci#define DEV_NAME "miro"
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_cistatic struct isa_driver snd_miro_driver = {
147562306a36Sopenharmony_ci	.match		= snd_miro_isa_match,
147662306a36Sopenharmony_ci	.probe		= snd_miro_isa_probe,
147762306a36Sopenharmony_ci	/* FIXME: suspend/resume */
147862306a36Sopenharmony_ci	.driver		= {
147962306a36Sopenharmony_ci		.name	= DEV_NAME
148062306a36Sopenharmony_ci	},
148162306a36Sopenharmony_ci};
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci#ifdef CONFIG_PNP
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistatic int snd_card_miro_pnp(struct snd_miro *chip,
148662306a36Sopenharmony_ci			     struct pnp_card_link *card,
148762306a36Sopenharmony_ci			     const struct pnp_card_device_id *pid)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	struct pnp_dev *pdev;
149062306a36Sopenharmony_ci	int err;
149162306a36Sopenharmony_ci	struct pnp_dev *devmpu;
149262306a36Sopenharmony_ci	struct pnp_dev *devmc;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
149562306a36Sopenharmony_ci	if (pdev == NULL)
149662306a36Sopenharmony_ci		return -EBUSY;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
149962306a36Sopenharmony_ci	if (devmpu == NULL)
150062306a36Sopenharmony_ci		return -EBUSY;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
150362306a36Sopenharmony_ci	if (devmc == NULL)
150462306a36Sopenharmony_ci		return -EBUSY;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	err = pnp_activate_dev(pdev);
150762306a36Sopenharmony_ci	if (err < 0) {
150862306a36Sopenharmony_ci		snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
150962306a36Sopenharmony_ci		return err;
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	err = pnp_activate_dev(devmc);
151362306a36Sopenharmony_ci	if (err < 0) {
151462306a36Sopenharmony_ci		snd_printk(KERN_ERR "MC pnp configure failure: %d\n",
151562306a36Sopenharmony_ci				    err);
151662306a36Sopenharmony_ci		return err;
151762306a36Sopenharmony_ci	}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	port = pnp_port_start(pdev, 1);
152062306a36Sopenharmony_ci	fm_port = pnp_port_start(pdev, 2) + 8;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	/*
152362306a36Sopenharmony_ci	 * The MC(0) is never accessed and the miroSOUND PCM20 card does not
152462306a36Sopenharmony_ci	 * include it in the PnP resource range. OPTI93x include it.
152562306a36Sopenharmony_ci	 */
152662306a36Sopenharmony_ci	chip->mc_base = pnp_port_start(devmc, 0) - 1;
152762306a36Sopenharmony_ci	chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	irq = pnp_irq(pdev, 0);
153062306a36Sopenharmony_ci	dma1 = pnp_dma(pdev, 0);
153162306a36Sopenharmony_ci	dma2 = pnp_dma(pdev, 1);
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	if (mpu_port > 0) {
153462306a36Sopenharmony_ci		err = pnp_activate_dev(devmpu);
153562306a36Sopenharmony_ci		if (err < 0) {
153662306a36Sopenharmony_ci			snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
153762306a36Sopenharmony_ci			mpu_port = -1;
153862306a36Sopenharmony_ci			return err;
153962306a36Sopenharmony_ci		}
154062306a36Sopenharmony_ci		mpu_port = pnp_port_start(devmpu, 0);
154162306a36Sopenharmony_ci		mpu_irq = pnp_irq(devmpu, 0);
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci	return 0;
154462306a36Sopenharmony_ci}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_cistatic int snd_miro_pnp_probe(struct pnp_card_link *pcard,
154762306a36Sopenharmony_ci			      const struct pnp_card_device_id *pid)
154862306a36Sopenharmony_ci{
154962306a36Sopenharmony_ci	struct snd_card *card;
155062306a36Sopenharmony_ci	int err;
155162306a36Sopenharmony_ci	struct snd_miro *miro;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	if (snd_miro_pnp_is_probed)
155462306a36Sopenharmony_ci		return -EBUSY;
155562306a36Sopenharmony_ci	if (!isapnp)
155662306a36Sopenharmony_ci		return -ENODEV;
155762306a36Sopenharmony_ci	err = snd_devm_card_new(&pcard->card->dev, index, id, THIS_MODULE,
155862306a36Sopenharmony_ci				sizeof(struct snd_miro), &card);
155962306a36Sopenharmony_ci	if (err < 0)
156062306a36Sopenharmony_ci		return err;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	miro = card->private_data;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	err = snd_card_miro_pnp(miro, pcard, pid);
156562306a36Sopenharmony_ci	if (err)
156662306a36Sopenharmony_ci		return err;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	/* only miroSOUND PCM20 and PCM12 == OPTi924 */
156962306a36Sopenharmony_ci	err = snd_miro_init(miro, OPTi9XX_HW_82C924);
157062306a36Sopenharmony_ci	if (err)
157162306a36Sopenharmony_ci		return err;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	err = snd_miro_opti_check(card, miro);
157462306a36Sopenharmony_ci	if (err) {
157562306a36Sopenharmony_ci		snd_printk(KERN_ERR "OPTI chip not found\n");
157662306a36Sopenharmony_ci		return err;
157762306a36Sopenharmony_ci	}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	err = snd_miro_probe(card);
158062306a36Sopenharmony_ci	if (err < 0)
158162306a36Sopenharmony_ci		return err;
158262306a36Sopenharmony_ci	pnp_set_card_drvdata(pcard, card);
158362306a36Sopenharmony_ci	snd_miro_pnp_is_probed = 1;
158462306a36Sopenharmony_ci	return 0;
158562306a36Sopenharmony_ci}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_cistatic void snd_miro_pnp_remove(struct pnp_card_link *pcard)
158862306a36Sopenharmony_ci{
158962306a36Sopenharmony_ci	snd_miro_pnp_is_probed = 0;
159062306a36Sopenharmony_ci}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_cistatic struct pnp_card_driver miro_pnpc_driver = {
159362306a36Sopenharmony_ci	.flags		= PNP_DRIVER_RES_DISABLE,
159462306a36Sopenharmony_ci	.name		= "miro",
159562306a36Sopenharmony_ci	.id_table	= snd_miro_pnpids,
159662306a36Sopenharmony_ci	.probe		= snd_miro_pnp_probe,
159762306a36Sopenharmony_ci	.remove		= snd_miro_pnp_remove,
159862306a36Sopenharmony_ci};
159962306a36Sopenharmony_ci#endif
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_cistatic int __init alsa_card_miro_init(void)
160262306a36Sopenharmony_ci{
160362306a36Sopenharmony_ci#ifdef CONFIG_PNP
160462306a36Sopenharmony_ci	pnp_register_card_driver(&miro_pnpc_driver);
160562306a36Sopenharmony_ci	if (snd_miro_pnp_is_probed)
160662306a36Sopenharmony_ci		return 0;
160762306a36Sopenharmony_ci	pnp_unregister_card_driver(&miro_pnpc_driver);
160862306a36Sopenharmony_ci#endif
160962306a36Sopenharmony_ci	return isa_register_driver(&snd_miro_driver, 1);
161062306a36Sopenharmony_ci}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_cistatic void __exit alsa_card_miro_exit(void)
161362306a36Sopenharmony_ci{
161462306a36Sopenharmony_ci	if (!snd_miro_pnp_is_probed) {
161562306a36Sopenharmony_ci		isa_unregister_driver(&snd_miro_driver);
161662306a36Sopenharmony_ci		return;
161762306a36Sopenharmony_ci	}
161862306a36Sopenharmony_ci#ifdef CONFIG_PNP
161962306a36Sopenharmony_ci	pnp_unregister_card_driver(&miro_pnpc_driver);
162062306a36Sopenharmony_ci#endif
162162306a36Sopenharmony_ci}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_cimodule_init(alsa_card_miro_init)
162462306a36Sopenharmony_cimodule_exit(alsa_card_miro_exit)
1625