xref: /kernel/linux/linux-5.10/sound/isa/opti9xx/miro.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *   ALSA soundcard driver for Miro miroSOUND PCM1 pro
48c2ecf20Sopenharmony_ci *                                  miroSOUND PCM12
58c2ecf20Sopenharmony_ci *                                  miroSOUND PCM20 Radio
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *   Copyright (C) 2004-2005 Martin Langer <martin-langer@gmx.de>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *   Based on OSS ACI and ALSA OPTi9xx drivers
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/isa.h>
158c2ecf20Sopenharmony_ci#include <linux/pnp.h>
168c2ecf20Sopenharmony_ci#include <linux/delay.h>
178c2ecf20Sopenharmony_ci#include <linux/ioport.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <asm/dma.h>
218c2ecf20Sopenharmony_ci#include <sound/core.h>
228c2ecf20Sopenharmony_ci#include <sound/wss.h>
238c2ecf20Sopenharmony_ci#include <sound/mpu401.h>
248c2ecf20Sopenharmony_ci#include <sound/opl4.h>
258c2ecf20Sopenharmony_ci#include <sound/control.h>
268c2ecf20Sopenharmony_ci#include <sound/info.h>
278c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IOPORT
288c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ
298c2ecf20Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA
308c2ecf20Sopenharmony_ci#include <sound/initval.h>
318c2ecf20Sopenharmony_ci#include <sound/aci.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Martin Langer <martin-langer@gmx.de>");
348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Miro miroSOUND PCM1 pro, PCM12, PCM20 Radio");
368c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Miro,miroSOUND PCM1 pro}, "
378c2ecf20Sopenharmony_ci			"{Miro,miroSOUND PCM12}, "
388c2ecf20Sopenharmony_ci			"{Miro,miroSOUND PCM20 Radio}}");
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1;		/* Index 0-MAX */
418c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1;		/* ID for this card */
428c2ecf20Sopenharmony_cistatic long port = SNDRV_DEFAULT_PORT1; 	/* 0x530,0xe80,0xf40,0x604 */
438c2ecf20Sopenharmony_cistatic long mpu_port = SNDRV_DEFAULT_PORT1;	/* 0x300,0x310,0x320,0x330 */
448c2ecf20Sopenharmony_cistatic long fm_port = SNDRV_DEFAULT_PORT1;	/* 0x388 */
458c2ecf20Sopenharmony_cistatic int irq = SNDRV_DEFAULT_IRQ1;		/* 5,7,9,10,11 */
468c2ecf20Sopenharmony_cistatic int mpu_irq = SNDRV_DEFAULT_IRQ1;	/* 5,7,9,10 */
478c2ecf20Sopenharmony_cistatic int dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
488c2ecf20Sopenharmony_cistatic int dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
498c2ecf20Sopenharmony_cistatic int wss;
508c2ecf20Sopenharmony_cistatic int ide;
518c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
528c2ecf20Sopenharmony_cistatic bool isapnp = 1;				/* Enable ISA PnP detection */
538c2ecf20Sopenharmony_ci#endif
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cimodule_param(index, int, 0444);
568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for miro soundcard.");
578c2ecf20Sopenharmony_cimodule_param(id, charp, 0444);
588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for miro soundcard.");
598c2ecf20Sopenharmony_cimodule_param_hw(port, long, ioport, 0444);
608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(port, "WSS port # for miro driver.");
618c2ecf20Sopenharmony_cimodule_param_hw(mpu_port, long, ioport, 0444);
628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver.");
638c2ecf20Sopenharmony_cimodule_param_hw(fm_port, long, ioport, 0444);
648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM Port # for miro driver.");
658c2ecf20Sopenharmony_cimodule_param_hw(irq, int, irq, 0444);
668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "WSS irq # for miro driver.");
678c2ecf20Sopenharmony_cimodule_param_hw(mpu_irq, int, irq, 0444);
688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver.");
698c2ecf20Sopenharmony_cimodule_param_hw(dma1, int, dma, 0444);
708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma1, "1st dma # for miro driver.");
718c2ecf20Sopenharmony_cimodule_param_hw(dma2, int, dma, 0444);
728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dma2, "2nd dma # for miro driver.");
738c2ecf20Sopenharmony_cimodule_param(wss, int, 0444);
748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(wss, "wss mode");
758c2ecf20Sopenharmony_cimodule_param(ide, int, 0444);
768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ide, "enable ide port");
778c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
788c2ecf20Sopenharmony_cimodule_param(isapnp, bool, 0444);
798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
808c2ecf20Sopenharmony_ci#endif
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define OPTi9XX_HW_DETECT	0
838c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C928	1
848c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C929	2
858c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C924	3
868c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C925	4
878c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C930	5
888c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C931	6
898c2ecf20Sopenharmony_ci#define OPTi9XX_HW_82C933	7
908c2ecf20Sopenharmony_ci#define OPTi9XX_HW_LAST		OPTi9XX_HW_82C933
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define OPTi9XX_MC_REG(n)	n
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistruct snd_miro {
958c2ecf20Sopenharmony_ci	unsigned short hardware;
968c2ecf20Sopenharmony_ci	unsigned char password;
978c2ecf20Sopenharmony_ci	char name[7];
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	struct resource *res_mc_base;
1008c2ecf20Sopenharmony_ci	struct resource *res_aci_port;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	unsigned long mc_base;
1038c2ecf20Sopenharmony_ci	unsigned long mc_base_size;
1048c2ecf20Sopenharmony_ci	unsigned long pwd_reg;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	spinlock_t lock;
1078c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	long wss_base;
1108c2ecf20Sopenharmony_ci	int irq;
1118c2ecf20Sopenharmony_ci	int dma1;
1128c2ecf20Sopenharmony_ci	int dma2;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	long mpu_port;
1158c2ecf20Sopenharmony_ci	int mpu_irq;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	struct snd_miro_aci *aci;
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic struct snd_miro_aci aci_device;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic const char * const snd_opti9xx_names[] = {
1238c2ecf20Sopenharmony_ci	"unknown",
1248c2ecf20Sopenharmony_ci	"82C928", "82C929",
1258c2ecf20Sopenharmony_ci	"82C924", "82C925",
1268c2ecf20Sopenharmony_ci	"82C930", "82C931", "82C933"
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int snd_miro_pnp_is_probed;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic const struct pnp_card_device_id snd_miro_pnpids[] = {
1348c2ecf20Sopenharmony_ci	/* PCM20 and PCM12 in PnP mode */
1358c2ecf20Sopenharmony_ci	{ .id = "MIR0924",
1368c2ecf20Sopenharmony_ci	  .devs = { { "MIR0000" }, { "MIR0002" }, { "MIR0005" } }, },
1378c2ecf20Sopenharmony_ci	{ .id = "" }
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_miro_pnpids);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci#endif	/* CONFIG_PNP */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/*
1458c2ecf20Sopenharmony_ci *  ACI control
1468c2ecf20Sopenharmony_ci */
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int aci_busy_wait(struct snd_miro_aci *aci)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	long timeout;
1518c2ecf20Sopenharmony_ci	unsigned char byte;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	for (timeout = 1; timeout <= ACI_MINTIME + 30; timeout++) {
1548c2ecf20Sopenharmony_ci		byte = inb(aci->aci_port + ACI_REG_BUSY);
1558c2ecf20Sopenharmony_ci		if ((byte & 1) == 0) {
1568c2ecf20Sopenharmony_ci			if (timeout >= ACI_MINTIME)
1578c2ecf20Sopenharmony_ci				snd_printd("aci ready in round %ld.\n",
1588c2ecf20Sopenharmony_ci					   timeout-ACI_MINTIME);
1598c2ecf20Sopenharmony_ci			return byte;
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci		if (timeout >= ACI_MINTIME) {
1628c2ecf20Sopenharmony_ci			long out=10*HZ;
1638c2ecf20Sopenharmony_ci			switch (timeout-ACI_MINTIME) {
1648c2ecf20Sopenharmony_ci			case 0 ... 9:
1658c2ecf20Sopenharmony_ci				out /= 10;
1668c2ecf20Sopenharmony_ci				fallthrough;
1678c2ecf20Sopenharmony_ci			case 10 ... 19:
1688c2ecf20Sopenharmony_ci				out /= 10;
1698c2ecf20Sopenharmony_ci				fallthrough;
1708c2ecf20Sopenharmony_ci			case 20 ... 30:
1718c2ecf20Sopenharmony_ci				out /= 10;
1728c2ecf20Sopenharmony_ci				fallthrough;
1738c2ecf20Sopenharmony_ci			default:
1748c2ecf20Sopenharmony_ci				set_current_state(TASK_UNINTERRUPTIBLE);
1758c2ecf20Sopenharmony_ci				schedule_timeout(out);
1768c2ecf20Sopenharmony_ci				break;
1778c2ecf20Sopenharmony_ci			}
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci	snd_printk(KERN_ERR "aci_busy_wait() time out\n");
1818c2ecf20Sopenharmony_ci	return -EBUSY;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic inline int aci_write(struct snd_miro_aci *aci, unsigned char byte)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	if (aci_busy_wait(aci) >= 0) {
1878c2ecf20Sopenharmony_ci		outb(byte, aci->aci_port + ACI_REG_COMMAND);
1888c2ecf20Sopenharmony_ci		return 0;
1898c2ecf20Sopenharmony_ci	} else {
1908c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "aci busy, aci_write(0x%x) stopped.\n", byte);
1918c2ecf20Sopenharmony_ci		return -EBUSY;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic inline int aci_read(struct snd_miro_aci *aci)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	unsigned char byte;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (aci_busy_wait(aci) >= 0) {
2008c2ecf20Sopenharmony_ci		byte = inb(aci->aci_port + ACI_REG_STATUS);
2018c2ecf20Sopenharmony_ci		return byte;
2028c2ecf20Sopenharmony_ci	} else {
2038c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "aci busy, aci_read() stopped.\n");
2048c2ecf20Sopenharmony_ci		return -EBUSY;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ciint snd_aci_cmd(struct snd_miro_aci *aci, int write1, int write2, int write3)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	int write[] = {write1, write2, write3};
2118c2ecf20Sopenharmony_ci	int value, i;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&aci->aci_mutex))
2148c2ecf20Sopenharmony_ci		return -EINTR;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	for (i=0; i<3; i++) {
2178c2ecf20Sopenharmony_ci		if (write[i]< 0 || write[i] > 255)
2188c2ecf20Sopenharmony_ci			break;
2198c2ecf20Sopenharmony_ci		else {
2208c2ecf20Sopenharmony_ci			value = aci_write(aci, write[i]);
2218c2ecf20Sopenharmony_ci			if (value < 0)
2228c2ecf20Sopenharmony_ci				goto out;
2238c2ecf20Sopenharmony_ci		}
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	value = aci_read(aci);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ciout:	mutex_unlock(&aci->aci_mutex);
2298c2ecf20Sopenharmony_ci	return value;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_aci_cmd);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic int aci_getvalue(struct snd_miro_aci *aci, unsigned char index)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	return snd_aci_cmd(aci, ACI_STATUS, index, -1);
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int aci_setvalue(struct snd_miro_aci *aci, unsigned char index,
2398c2ecf20Sopenharmony_ci			int value)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	return snd_aci_cmd(aci, index, value, -1);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistruct snd_miro_aci *snd_aci_get_aci(void)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	if (aci_device.aci_port == 0)
2478c2ecf20Sopenharmony_ci		return NULL;
2488c2ecf20Sopenharmony_ci	return &aci_device;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_aci_get_aci);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci/*
2538c2ecf20Sopenharmony_ci *  MIXER part
2548c2ecf20Sopenharmony_ci */
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci#define snd_miro_info_capture	snd_ctl_boolean_mono_info
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int snd_miro_get_capture(struct snd_kcontrol *kcontrol,
2598c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
2628c2ecf20Sopenharmony_ci	int value;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	value = aci_getvalue(miro->aci, ACI_S_GENERAL);
2658c2ecf20Sopenharmony_ci	if (value < 0) {
2668c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_get_capture() failed: %d\n",
2678c2ecf20Sopenharmony_ci			   value);
2688c2ecf20Sopenharmony_ci		return value;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = value & 0x20;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return 0;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int snd_miro_put_capture(struct snd_kcontrol *kcontrol,
2778c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *ucontrol)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
2808c2ecf20Sopenharmony_ci	int change, value, error;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	value = !(ucontrol->value.integer.value[0]);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	error = aci_setvalue(miro->aci, ACI_SET_SOLOMODE, value);
2858c2ecf20Sopenharmony_ci	if (error < 0) {
2868c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_put_capture() failed: %d\n",
2878c2ecf20Sopenharmony_ci			   error);
2888c2ecf20Sopenharmony_ci		return error;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	change = (value != miro->aci->aci_solomode);
2928c2ecf20Sopenharmony_ci	miro->aci->aci_solomode = value;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return change;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int snd_miro_info_preamp(struct snd_kcontrol *kcontrol,
2988c2ecf20Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
3018c2ecf20Sopenharmony_ci	uinfo->count = 1;
3028c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
3038c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 3;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	return 0;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic int snd_miro_get_preamp(struct snd_kcontrol *kcontrol,
3098c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
3128c2ecf20Sopenharmony_ci	int value;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (miro->aci->aci_version <= 176) {
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		/*
3178c2ecf20Sopenharmony_ci		   OSS says it's not readable with versions < 176.
3188c2ecf20Sopenharmony_ci		   But it doesn't work on my card,
3198c2ecf20Sopenharmony_ci		   which is a PCM12 with aci_version = 176.
3208c2ecf20Sopenharmony_ci		*/
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = miro->aci->aci_preamp;
3238c2ecf20Sopenharmony_ci		return 0;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	value = aci_getvalue(miro->aci, ACI_GET_PREAMP);
3278c2ecf20Sopenharmony_ci	if (value < 0) {
3288c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_get_preamp() failed: %d\n",
3298c2ecf20Sopenharmony_ci			   value);
3308c2ecf20Sopenharmony_ci		return value;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = value;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	return 0;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic int snd_miro_put_preamp(struct snd_kcontrol *kcontrol,
3398c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
3428c2ecf20Sopenharmony_ci	int error, value, change;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	value = ucontrol->value.integer.value[0];
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	error = aci_setvalue(miro->aci, ACI_SET_PREAMP, value);
3478c2ecf20Sopenharmony_ci	if (error < 0) {
3488c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_put_preamp() failed: %d\n",
3498c2ecf20Sopenharmony_ci			   error);
3508c2ecf20Sopenharmony_ci		return error;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	change = (value != miro->aci->aci_preamp);
3548c2ecf20Sopenharmony_ci	miro->aci->aci_preamp = value;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return change;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci#define snd_miro_info_amp	snd_ctl_boolean_mono_info
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int snd_miro_get_amp(struct snd_kcontrol *kcontrol,
3628c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
3658c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = miro->aci->aci_amp;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic int snd_miro_put_amp(struct snd_kcontrol *kcontrol,
3718c2ecf20Sopenharmony_ci			    struct snd_ctl_elem_value *ucontrol)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
3748c2ecf20Sopenharmony_ci	int error, value, change;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	value = ucontrol->value.integer.value[0];
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	error = aci_setvalue(miro->aci, ACI_SET_POWERAMP, value);
3798c2ecf20Sopenharmony_ci	if (error < 0) {
3808c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "snd_miro_put_amp() to %d failed: %d\n", value, error);
3818c2ecf20Sopenharmony_ci		return error;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	change = (value != miro->aci->aci_amp);
3858c2ecf20Sopenharmony_ci	miro->aci->aci_amp = value;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	return change;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci#define MIRO_DOUBLE(ctl_name, ctl_index, get_right_reg, set_right_reg) \
3918c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
3928c2ecf20Sopenharmony_ci  .name = ctl_name, \
3938c2ecf20Sopenharmony_ci  .index = ctl_index, \
3948c2ecf20Sopenharmony_ci  .info = snd_miro_info_double, \
3958c2ecf20Sopenharmony_ci  .get = snd_miro_get_double, \
3968c2ecf20Sopenharmony_ci  .put = snd_miro_put_double, \
3978c2ecf20Sopenharmony_ci  .private_value = get_right_reg | (set_right_reg << 8) \
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic int snd_miro_info_double(struct snd_kcontrol *kcontrol,
4018c2ecf20Sopenharmony_ci				struct snd_ctl_elem_info *uinfo)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
4068c2ecf20Sopenharmony_ci	uinfo->count = 2;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if ((reg >= ACI_GET_EQ1) && (reg <= ACI_GET_EQ7)) {
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci		/* equalizer elements */
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		uinfo->value.integer.min = - 0x7f;
4138c2ecf20Sopenharmony_ci		uinfo->value.integer.max = 0x7f;
4148c2ecf20Sopenharmony_ci	} else {
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		/* non-equalizer elements */
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		uinfo->value.integer.min = 0;
4198c2ecf20Sopenharmony_ci		uinfo->value.integer.max = 0x20;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	return 0;
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic int snd_miro_get_double(struct snd_kcontrol *kcontrol,
4268c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *uinfo)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
4298c2ecf20Sopenharmony_ci	int left_val, right_val;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	int right_reg = kcontrol->private_value & 0xff;
4328c2ecf20Sopenharmony_ci	int left_reg = right_reg + 1;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	right_val = aci_getvalue(miro->aci, right_reg);
4358c2ecf20Sopenharmony_ci	if (right_val < 0) {
4368c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", right_reg, right_val);
4378c2ecf20Sopenharmony_ci		return right_val;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	left_val = aci_getvalue(miro->aci, left_reg);
4418c2ecf20Sopenharmony_ci	if (left_val < 0) {
4428c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", left_reg, left_val);
4438c2ecf20Sopenharmony_ci		return left_val;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if ((right_reg >= ACI_GET_EQ1) && (right_reg <= ACI_GET_EQ7)) {
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		/* equalizer elements */
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		if (left_val < 0x80) {
4518c2ecf20Sopenharmony_ci			uinfo->value.integer.value[0] = left_val;
4528c2ecf20Sopenharmony_ci		} else {
4538c2ecf20Sopenharmony_ci			uinfo->value.integer.value[0] = 0x80 - left_val;
4548c2ecf20Sopenharmony_ci		}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		if (right_val < 0x80) {
4578c2ecf20Sopenharmony_ci			uinfo->value.integer.value[1] = right_val;
4588c2ecf20Sopenharmony_ci		} else {
4598c2ecf20Sopenharmony_ci			uinfo->value.integer.value[1] = 0x80 - right_val;
4608c2ecf20Sopenharmony_ci		}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	} else {
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		/* non-equalizer elements */
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		uinfo->value.integer.value[0] = 0x20 - left_val;
4678c2ecf20Sopenharmony_ci		uinfo->value.integer.value[1] = 0x20 - right_val;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	return 0;
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic int snd_miro_put_double(struct snd_kcontrol *kcontrol,
4748c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	struct snd_miro *miro = snd_kcontrol_chip(kcontrol);
4778c2ecf20Sopenharmony_ci	struct snd_miro_aci *aci = miro->aci;
4788c2ecf20Sopenharmony_ci	int left, right, left_old, right_old;
4798c2ecf20Sopenharmony_ci	int setreg_left, setreg_right, getreg_left, getreg_right;
4808c2ecf20Sopenharmony_ci	int change, error;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	left = ucontrol->value.integer.value[0];
4838c2ecf20Sopenharmony_ci	right = ucontrol->value.integer.value[1];
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	setreg_right = (kcontrol->private_value >> 8) & 0xff;
4868c2ecf20Sopenharmony_ci	setreg_left = setreg_right + 8;
4878c2ecf20Sopenharmony_ci	if (setreg_right == ACI_SET_MASTER)
4888c2ecf20Sopenharmony_ci		setreg_left -= 7;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	getreg_right = kcontrol->private_value & 0xff;
4918c2ecf20Sopenharmony_ci	getreg_left = getreg_right + 1;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	left_old = aci_getvalue(aci, getreg_left);
4948c2ecf20Sopenharmony_ci	if (left_old < 0) {
4958c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_left, left_old);
4968c2ecf20Sopenharmony_ci		return left_old;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	right_old = aci_getvalue(aci, getreg_right);
5008c2ecf20Sopenharmony_ci	if (right_old < 0) {
5018c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "aci_getvalue(%d) failed: %d\n", getreg_right, right_old);
5028c2ecf20Sopenharmony_ci		return right_old;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if ((getreg_right >= ACI_GET_EQ1) && (getreg_right <= ACI_GET_EQ7)) {
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci		/* equalizer elements */
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		if (left < -0x7f || left > 0x7f ||
5108c2ecf20Sopenharmony_ci		    right < -0x7f || right > 0x7f)
5118c2ecf20Sopenharmony_ci			return -EINVAL;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci		if (left_old > 0x80)
5148c2ecf20Sopenharmony_ci			left_old = 0x80 - left_old;
5158c2ecf20Sopenharmony_ci		if (right_old > 0x80)
5168c2ecf20Sopenharmony_ci			right_old = 0x80 - right_old;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		if (left >= 0) {
5198c2ecf20Sopenharmony_ci			error = aci_setvalue(aci, setreg_left, left);
5208c2ecf20Sopenharmony_ci			if (error < 0) {
5218c2ecf20Sopenharmony_ci				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
5228c2ecf20Sopenharmony_ci					   left, error);
5238c2ecf20Sopenharmony_ci				return error;
5248c2ecf20Sopenharmony_ci			}
5258c2ecf20Sopenharmony_ci		} else {
5268c2ecf20Sopenharmony_ci			error = aci_setvalue(aci, setreg_left, 0x80 - left);
5278c2ecf20Sopenharmony_ci			if (error < 0) {
5288c2ecf20Sopenharmony_ci				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
5298c2ecf20Sopenharmony_ci					   0x80 - left, error);
5308c2ecf20Sopenharmony_ci				return error;
5318c2ecf20Sopenharmony_ci			}
5328c2ecf20Sopenharmony_ci		}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci		if (right >= 0) {
5358c2ecf20Sopenharmony_ci			error = aci_setvalue(aci, setreg_right, right);
5368c2ecf20Sopenharmony_ci			if (error < 0) {
5378c2ecf20Sopenharmony_ci				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
5388c2ecf20Sopenharmony_ci					   right, error);
5398c2ecf20Sopenharmony_ci				return error;
5408c2ecf20Sopenharmony_ci			}
5418c2ecf20Sopenharmony_ci		} else {
5428c2ecf20Sopenharmony_ci			error = aci_setvalue(aci, setreg_right, 0x80 - right);
5438c2ecf20Sopenharmony_ci			if (error < 0) {
5448c2ecf20Sopenharmony_ci				snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
5458c2ecf20Sopenharmony_ci					   0x80 - right, error);
5468c2ecf20Sopenharmony_ci				return error;
5478c2ecf20Sopenharmony_ci			}
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	} else {
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		/* non-equalizer elements */
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		if (left < 0 || left > 0x20 ||
5558c2ecf20Sopenharmony_ci		    right < 0 || right > 0x20)
5568c2ecf20Sopenharmony_ci			return -EINVAL;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		left_old = 0x20 - left_old;
5598c2ecf20Sopenharmony_ci		right_old = 0x20 - right_old;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		error = aci_setvalue(aci, setreg_left, 0x20 - left);
5628c2ecf20Sopenharmony_ci		if (error < 0) {
5638c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
5648c2ecf20Sopenharmony_ci				   0x20 - left, error);
5658c2ecf20Sopenharmony_ci			return error;
5668c2ecf20Sopenharmony_ci		}
5678c2ecf20Sopenharmony_ci		error = aci_setvalue(aci, setreg_right, 0x20 - right);
5688c2ecf20Sopenharmony_ci		if (error < 0) {
5698c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
5708c2ecf20Sopenharmony_ci				   0x20 - right, error);
5718c2ecf20Sopenharmony_ci			return error;
5728c2ecf20Sopenharmony_ci		}
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	change = (left != left_old) || (right != right_old);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return change;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_controls[] = {
5818c2ecf20Sopenharmony_ciMIRO_DOUBLE("Master Playback Volume", 0, ACI_GET_MASTER, ACI_SET_MASTER),
5828c2ecf20Sopenharmony_ciMIRO_DOUBLE("Mic Playback Volume", 1, ACI_GET_MIC, ACI_SET_MIC),
5838c2ecf20Sopenharmony_ciMIRO_DOUBLE("Line Playback Volume", 1, ACI_GET_LINE, ACI_SET_LINE),
5848c2ecf20Sopenharmony_ciMIRO_DOUBLE("CD Playback Volume", 0, ACI_GET_CD, ACI_SET_CD),
5858c2ecf20Sopenharmony_ciMIRO_DOUBLE("Synth Playback Volume", 0, ACI_GET_SYNTH, ACI_SET_SYNTH),
5868c2ecf20Sopenharmony_ciMIRO_DOUBLE("PCM Playback Volume", 1, ACI_GET_PCM, ACI_SET_PCM),
5878c2ecf20Sopenharmony_ciMIRO_DOUBLE("Aux Playback Volume", 2, ACI_GET_LINE2, ACI_SET_LINE2),
5888c2ecf20Sopenharmony_ci};
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci/* Equalizer with seven bands (only PCM20)
5918c2ecf20Sopenharmony_ci   from -12dB up to +12dB on each band */
5928c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_eq_controls[] = {
5938c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 28 Hz", 0, ACI_GET_EQ1, ACI_SET_EQ1),
5948c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 160 Hz", 0, ACI_GET_EQ2, ACI_SET_EQ2),
5958c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 400 Hz", 0, ACI_GET_EQ3, ACI_SET_EQ3),
5968c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 1 kHz", 0, ACI_GET_EQ4, ACI_SET_EQ4),
5978c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 2.5 kHz", 0, ACI_GET_EQ5, ACI_SET_EQ5),
5988c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 6.3 kHz", 0, ACI_GET_EQ6, ACI_SET_EQ6),
5998c2ecf20Sopenharmony_ciMIRO_DOUBLE("Tone Control - 16 kHz", 0, ACI_GET_EQ7, ACI_SET_EQ7),
6008c2ecf20Sopenharmony_ci};
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_radio_control[] = {
6038c2ecf20Sopenharmony_ciMIRO_DOUBLE("Radio Playback Volume", 0, ACI_GET_LINE1, ACI_SET_LINE1),
6048c2ecf20Sopenharmony_ci};
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_line_control[] = {
6078c2ecf20Sopenharmony_ciMIRO_DOUBLE("Line Playback Volume", 2, ACI_GET_LINE1, ACI_SET_LINE1),
6088c2ecf20Sopenharmony_ci};
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_preamp_control[] = {
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
6138c2ecf20Sopenharmony_ci	.name = "Mic Boost",
6148c2ecf20Sopenharmony_ci	.index = 1,
6158c2ecf20Sopenharmony_ci	.info = snd_miro_info_preamp,
6168c2ecf20Sopenharmony_ci	.get = snd_miro_get_preamp,
6178c2ecf20Sopenharmony_ci	.put = snd_miro_put_preamp,
6188c2ecf20Sopenharmony_ci}};
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_amp_control[] = {
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
6238c2ecf20Sopenharmony_ci	.name = "Line Boost",
6248c2ecf20Sopenharmony_ci	.index = 0,
6258c2ecf20Sopenharmony_ci	.info = snd_miro_info_amp,
6268c2ecf20Sopenharmony_ci	.get = snd_miro_get_amp,
6278c2ecf20Sopenharmony_ci	.put = snd_miro_put_amp,
6288c2ecf20Sopenharmony_ci}};
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_miro_capture_control[] = {
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
6338c2ecf20Sopenharmony_ci	.name = "PCM Capture Switch",
6348c2ecf20Sopenharmony_ci	.index = 0,
6358c2ecf20Sopenharmony_ci	.info = snd_miro_info_capture,
6368c2ecf20Sopenharmony_ci	.get = snd_miro_get_capture,
6378c2ecf20Sopenharmony_ci	.put = snd_miro_put_capture,
6388c2ecf20Sopenharmony_ci}};
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cistatic const unsigned char aci_init_values[][2] = {
6418c2ecf20Sopenharmony_ci	{ ACI_SET_MUTE, 0x00 },
6428c2ecf20Sopenharmony_ci	{ ACI_SET_POWERAMP, 0x00 },
6438c2ecf20Sopenharmony_ci	{ ACI_SET_PREAMP, 0x00 },
6448c2ecf20Sopenharmony_ci	{ ACI_SET_SOLOMODE, 0x00 },
6458c2ecf20Sopenharmony_ci	{ ACI_SET_MIC + 0, 0x20 },
6468c2ecf20Sopenharmony_ci	{ ACI_SET_MIC + 8, 0x20 },
6478c2ecf20Sopenharmony_ci	{ ACI_SET_LINE + 0, 0x20 },
6488c2ecf20Sopenharmony_ci	{ ACI_SET_LINE + 8, 0x20 },
6498c2ecf20Sopenharmony_ci	{ ACI_SET_CD + 0, 0x20 },
6508c2ecf20Sopenharmony_ci	{ ACI_SET_CD + 8, 0x20 },
6518c2ecf20Sopenharmony_ci	{ ACI_SET_PCM + 0, 0x20 },
6528c2ecf20Sopenharmony_ci	{ ACI_SET_PCM + 8, 0x20 },
6538c2ecf20Sopenharmony_ci	{ ACI_SET_LINE1 + 0, 0x20 },
6548c2ecf20Sopenharmony_ci	{ ACI_SET_LINE1 + 8, 0x20 },
6558c2ecf20Sopenharmony_ci	{ ACI_SET_LINE2 + 0, 0x20 },
6568c2ecf20Sopenharmony_ci	{ ACI_SET_LINE2 + 8, 0x20 },
6578c2ecf20Sopenharmony_ci	{ ACI_SET_SYNTH + 0, 0x20 },
6588c2ecf20Sopenharmony_ci	{ ACI_SET_SYNTH + 8, 0x20 },
6598c2ecf20Sopenharmony_ci	{ ACI_SET_MASTER + 0, 0x20 },
6608c2ecf20Sopenharmony_ci	{ ACI_SET_MASTER + 1, 0x20 },
6618c2ecf20Sopenharmony_ci};
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic int snd_set_aci_init_values(struct snd_miro *miro)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	int idx, error;
6668c2ecf20Sopenharmony_ci	struct snd_miro_aci *aci = miro->aci;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* enable WSS on PCM1 */
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if ((aci->aci_product == 'A') && wss) {
6718c2ecf20Sopenharmony_ci		error = aci_setvalue(aci, ACI_SET_WSS, wss);
6728c2ecf20Sopenharmony_ci		if (error < 0) {
6738c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "enabling WSS mode failed\n");
6748c2ecf20Sopenharmony_ci			return error;
6758c2ecf20Sopenharmony_ci		}
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/* enable IDE port */
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (ide) {
6818c2ecf20Sopenharmony_ci		error = aci_setvalue(aci, ACI_SET_IDE, ide);
6828c2ecf20Sopenharmony_ci		if (error < 0) {
6838c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "enabling IDE port failed\n");
6848c2ecf20Sopenharmony_ci			return error;
6858c2ecf20Sopenharmony_ci		}
6868c2ecf20Sopenharmony_ci	}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/* set common aci values */
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(aci_init_values); idx++) {
6918c2ecf20Sopenharmony_ci		error = aci_setvalue(aci, aci_init_values[idx][0],
6928c2ecf20Sopenharmony_ci				     aci_init_values[idx][1]);
6938c2ecf20Sopenharmony_ci		if (error < 0) {
6948c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "aci_setvalue(%d) failed: %d\n",
6958c2ecf20Sopenharmony_ci				   aci_init_values[idx][0], error);
6968c2ecf20Sopenharmony_ci                        return error;
6978c2ecf20Sopenharmony_ci                }
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci	aci->aci_amp = 0;
7008c2ecf20Sopenharmony_ci	aci->aci_preamp = 0;
7018c2ecf20Sopenharmony_ci	aci->aci_solomode = 1;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	return 0;
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic int snd_miro_mixer(struct snd_card *card,
7078c2ecf20Sopenharmony_ci			  struct snd_miro *miro)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	unsigned int idx;
7108c2ecf20Sopenharmony_ci	int err;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!miro || !card))
7138c2ecf20Sopenharmony_ci		return -EINVAL;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	switch (miro->hardware) {
7168c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C924:
7178c2ecf20Sopenharmony_ci		strcpy(card->mixername, "ACI & OPTi924");
7188c2ecf20Sopenharmony_ci		break;
7198c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C929:
7208c2ecf20Sopenharmony_ci		strcpy(card->mixername, "ACI & OPTi929");
7218c2ecf20Sopenharmony_ci		break;
7228c2ecf20Sopenharmony_ci	default:
7238c2ecf20Sopenharmony_ci		snd_BUG();
7248c2ecf20Sopenharmony_ci		break;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_miro_controls); idx++) {
7288c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_controls[idx], miro))) < 0)
7298c2ecf20Sopenharmony_ci			return err;
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	if ((miro->aci->aci_product == 'A') ||
7338c2ecf20Sopenharmony_ci	    (miro->aci->aci_product == 'B')) {
7348c2ecf20Sopenharmony_ci		/* PCM1/PCM12 with power-amp and Line 2 */
7358c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_line_control[0], miro))) < 0)
7368c2ecf20Sopenharmony_ci			return err;
7378c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_amp_control[0], miro))) < 0)
7388c2ecf20Sopenharmony_ci			return err;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	if ((miro->aci->aci_product == 'B') ||
7428c2ecf20Sopenharmony_ci	    (miro->aci->aci_product == 'C')) {
7438c2ecf20Sopenharmony_ci		/* PCM12/PCM20 with mic-preamp */
7448c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_preamp_control[0], miro))) < 0)
7458c2ecf20Sopenharmony_ci			return err;
7468c2ecf20Sopenharmony_ci		if (miro->aci->aci_version >= 176)
7478c2ecf20Sopenharmony_ci			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_capture_control[0], miro))) < 0)
7488c2ecf20Sopenharmony_ci				return err;
7498c2ecf20Sopenharmony_ci	}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	if (miro->aci->aci_product == 'C') {
7528c2ecf20Sopenharmony_ci		/* PCM20 with radio and 7 band equalizer */
7538c2ecf20Sopenharmony_ci		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_radio_control[0], miro))) < 0)
7548c2ecf20Sopenharmony_ci			return err;
7558c2ecf20Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_miro_eq_controls); idx++) {
7568c2ecf20Sopenharmony_ci			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_miro_eq_controls[idx], miro))) < 0)
7578c2ecf20Sopenharmony_ci				return err;
7588c2ecf20Sopenharmony_ci		}
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	return 0;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic int snd_miro_init(struct snd_miro *chip,
7658c2ecf20Sopenharmony_ci			 unsigned short hardware)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	static const int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	chip->hardware = hardware;
7708c2ecf20Sopenharmony_ci	strcpy(chip->name, snd_opti9xx_names[hardware]);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	chip->mc_base_size = opti9xx_mc_size[hardware];
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	spin_lock_init(&chip->lock);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	chip->wss_base = -1;
7778c2ecf20Sopenharmony_ci	chip->irq = -1;
7788c2ecf20Sopenharmony_ci	chip->dma1 = -1;
7798c2ecf20Sopenharmony_ci	chip->dma2 = -1;
7808c2ecf20Sopenharmony_ci	chip->mpu_port = -1;
7818c2ecf20Sopenharmony_ci	chip->mpu_irq = -1;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	chip->pwd_reg = 3;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
7868c2ecf20Sopenharmony_ci	if (isapnp && chip->mc_base)
7878c2ecf20Sopenharmony_ci		/* PnP resource gives the least 10 bits */
7888c2ecf20Sopenharmony_ci		chip->mc_base |= 0xc00;
7898c2ecf20Sopenharmony_ci	else
7908c2ecf20Sopenharmony_ci#endif
7918c2ecf20Sopenharmony_ci		chip->mc_base = 0xf8c;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	switch (hardware) {
7948c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C929:
7958c2ecf20Sopenharmony_ci		chip->password = 0xe3;
7968c2ecf20Sopenharmony_ci		break;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C924:
7998c2ecf20Sopenharmony_ci		chip->password = 0xe5;
8008c2ecf20Sopenharmony_ci		break;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	default:
8038c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "sorry, no support for %d\n", hardware);
8048c2ecf20Sopenharmony_ci		return -ENODEV;
8058c2ecf20Sopenharmony_ci	}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	return 0;
8088c2ecf20Sopenharmony_ci}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_cistatic unsigned char snd_miro_read(struct snd_miro *chip,
8118c2ecf20Sopenharmony_ci				   unsigned char reg)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	unsigned long flags;
8148c2ecf20Sopenharmony_ci	unsigned char retval = 0xff;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
8178c2ecf20Sopenharmony_ci	outb(chip->password, chip->mc_base + chip->pwd_reg);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	switch (chip->hardware) {
8208c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C924:
8218c2ecf20Sopenharmony_ci		if (reg > 7) {
8228c2ecf20Sopenharmony_ci			outb(reg, chip->mc_base + 8);
8238c2ecf20Sopenharmony_ci			outb(chip->password, chip->mc_base + chip->pwd_reg);
8248c2ecf20Sopenharmony_ci			retval = inb(chip->mc_base + 9);
8258c2ecf20Sopenharmony_ci			break;
8268c2ecf20Sopenharmony_ci		}
8278c2ecf20Sopenharmony_ci		fallthrough;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C929:
8308c2ecf20Sopenharmony_ci		retval = inb(chip->mc_base + reg);
8318c2ecf20Sopenharmony_ci		break;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	default:
8348c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
8388c2ecf20Sopenharmony_ci	return retval;
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_cistatic void snd_miro_write(struct snd_miro *chip, unsigned char reg,
8428c2ecf20Sopenharmony_ci			   unsigned char value)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	unsigned long flags;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
8478c2ecf20Sopenharmony_ci	outb(chip->password, chip->mc_base + chip->pwd_reg);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	switch (chip->hardware) {
8508c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C924:
8518c2ecf20Sopenharmony_ci		if (reg > 7) {
8528c2ecf20Sopenharmony_ci			outb(reg, chip->mc_base + 8);
8538c2ecf20Sopenharmony_ci			outb(chip->password, chip->mc_base + chip->pwd_reg);
8548c2ecf20Sopenharmony_ci			outb(value, chip->mc_base + 9);
8558c2ecf20Sopenharmony_ci			break;
8568c2ecf20Sopenharmony_ci		}
8578c2ecf20Sopenharmony_ci		fallthrough;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C929:
8608c2ecf20Sopenharmony_ci		outb(value, chip->mc_base + reg);
8618c2ecf20Sopenharmony_ci		break;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	default:
8648c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "sorry, no support for %d\n", chip->hardware);
8658c2ecf20Sopenharmony_ci	}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
8688c2ecf20Sopenharmony_ci}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_cistatic inline void snd_miro_write_mask(struct snd_miro *chip,
8718c2ecf20Sopenharmony_ci		unsigned char reg, unsigned char value, unsigned char mask)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	unsigned char oldval = snd_miro_read(chip, reg);
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask));
8768c2ecf20Sopenharmony_ci}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci/*
8798c2ecf20Sopenharmony_ci *  Proc Interface
8808c2ecf20Sopenharmony_ci */
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cistatic void snd_miro_proc_read(struct snd_info_entry * entry,
8838c2ecf20Sopenharmony_ci			       struct snd_info_buffer *buffer)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	struct snd_miro *miro = (struct snd_miro *) entry->private_data;
8868c2ecf20Sopenharmony_ci	struct snd_miro_aci *aci = miro->aci;
8878c2ecf20Sopenharmony_ci	char* model = "unknown";
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	/* miroSOUND PCM1 pro, early PCM12 */
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	if ((miro->hardware == OPTi9XX_HW_82C929) &&
8928c2ecf20Sopenharmony_ci	    (aci->aci_vendor == 'm') &&
8938c2ecf20Sopenharmony_ci	    (aci->aci_product == 'A')) {
8948c2ecf20Sopenharmony_ci		switch (aci->aci_version) {
8958c2ecf20Sopenharmony_ci		case 3:
8968c2ecf20Sopenharmony_ci			model = "miroSOUND PCM1 pro";
8978c2ecf20Sopenharmony_ci			break;
8988c2ecf20Sopenharmony_ci		default:
8998c2ecf20Sopenharmony_ci			model = "miroSOUND PCM1 pro / (early) PCM12";
9008c2ecf20Sopenharmony_ci			break;
9018c2ecf20Sopenharmony_ci		}
9028c2ecf20Sopenharmony_ci	}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	/* miroSOUND PCM12, PCM12 (Rev. E), PCM12 pnp */
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	if ((miro->hardware == OPTi9XX_HW_82C924) &&
9078c2ecf20Sopenharmony_ci	    (aci->aci_vendor == 'm') &&
9088c2ecf20Sopenharmony_ci	    (aci->aci_product == 'B')) {
9098c2ecf20Sopenharmony_ci		switch (aci->aci_version) {
9108c2ecf20Sopenharmony_ci		case 4:
9118c2ecf20Sopenharmony_ci			model = "miroSOUND PCM12";
9128c2ecf20Sopenharmony_ci			break;
9138c2ecf20Sopenharmony_ci		case 176:
9148c2ecf20Sopenharmony_ci			model = "miroSOUND PCM12 (Rev. E)";
9158c2ecf20Sopenharmony_ci			break;
9168c2ecf20Sopenharmony_ci		default:
9178c2ecf20Sopenharmony_ci			model = "miroSOUND PCM12 / PCM12 pnp";
9188c2ecf20Sopenharmony_ci			break;
9198c2ecf20Sopenharmony_ci		}
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	/* miroSOUND PCM20 radio */
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if ((miro->hardware == OPTi9XX_HW_82C924) &&
9258c2ecf20Sopenharmony_ci	    (aci->aci_vendor == 'm') &&
9268c2ecf20Sopenharmony_ci	    (aci->aci_product == 'C')) {
9278c2ecf20Sopenharmony_ci		switch (aci->aci_version) {
9288c2ecf20Sopenharmony_ci		case 7:
9298c2ecf20Sopenharmony_ci			model = "miroSOUND PCM20 radio (Rev. E)";
9308c2ecf20Sopenharmony_ci			break;
9318c2ecf20Sopenharmony_ci		default:
9328c2ecf20Sopenharmony_ci			model = "miroSOUND PCM20 radio";
9338c2ecf20Sopenharmony_ci			break;
9348c2ecf20Sopenharmony_ci		}
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "\nGeneral information:\n");
9388c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  model   : %s\n", model);
9398c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  opti    : %s\n", miro->name);
9408c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  codec   : %s\n", miro->pcm->name);
9418c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  port    : 0x%lx\n", miro->wss_base);
9428c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  irq     : %d\n", miro->irq);
9438c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  dma     : %d,%d\n\n", miro->dma1, miro->dma2);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "MPU-401:\n");
9468c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  port    : 0x%lx\n", miro->mpu_port);
9478c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  irq     : %d\n\n", miro->mpu_irq);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "ACI information:\n");
9508c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  vendor  : ");
9518c2ecf20Sopenharmony_ci	switch (aci->aci_vendor) {
9528c2ecf20Sopenharmony_ci	case 'm':
9538c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "Miro\n");
9548c2ecf20Sopenharmony_ci		break;
9558c2ecf20Sopenharmony_ci	default:
9568c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_vendor);
9578c2ecf20Sopenharmony_ci		break;
9588c2ecf20Sopenharmony_ci	}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  product : ");
9618c2ecf20Sopenharmony_ci	switch (aci->aci_product) {
9628c2ecf20Sopenharmony_ci	case 'A':
9638c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "miroSOUND PCM1 pro / (early) PCM12\n");
9648c2ecf20Sopenharmony_ci		break;
9658c2ecf20Sopenharmony_ci	case 'B':
9668c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "miroSOUND PCM12\n");
9678c2ecf20Sopenharmony_ci		break;
9688c2ecf20Sopenharmony_ci	case 'C':
9698c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "miroSOUND PCM20 radio\n");
9708c2ecf20Sopenharmony_ci		break;
9718c2ecf20Sopenharmony_ci	default:
9728c2ecf20Sopenharmony_ci		snd_iprintf(buffer, "unknown (0x%x)\n", aci->aci_product);
9738c2ecf20Sopenharmony_ci		break;
9748c2ecf20Sopenharmony_ci	}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  firmware: %d (0x%x)\n",
9778c2ecf20Sopenharmony_ci		    aci->aci_version, aci->aci_version);
9788c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  port    : 0x%lx-0x%lx\n",
9798c2ecf20Sopenharmony_ci		    aci->aci_port, aci->aci_port+2);
9808c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  wss     : 0x%x\n", wss);
9818c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  ide     : 0x%x\n", ide);
9828c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  solomode: 0x%x\n", aci->aci_solomode);
9838c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  amp     : 0x%x\n", aci->aci_amp);
9848c2ecf20Sopenharmony_ci	snd_iprintf(buffer, "  preamp  : 0x%x\n", aci->aci_preamp);
9858c2ecf20Sopenharmony_ci}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_cistatic void snd_miro_proc_init(struct snd_card *card,
9888c2ecf20Sopenharmony_ci			       struct snd_miro *miro)
9898c2ecf20Sopenharmony_ci{
9908c2ecf20Sopenharmony_ci	snd_card_ro_proc_new(card, "miro", miro, snd_miro_proc_read);
9918c2ecf20Sopenharmony_ci}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci/*
9948c2ecf20Sopenharmony_ci *  Init
9958c2ecf20Sopenharmony_ci */
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_cistatic int snd_miro_configure(struct snd_miro *chip)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	unsigned char wss_base_bits;
10008c2ecf20Sopenharmony_ci	unsigned char irq_bits;
10018c2ecf20Sopenharmony_ci	unsigned char dma_bits;
10028c2ecf20Sopenharmony_ci	unsigned char mpu_port_bits = 0;
10038c2ecf20Sopenharmony_ci	unsigned char mpu_irq_bits;
10048c2ecf20Sopenharmony_ci	unsigned long flags;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
10078c2ecf20Sopenharmony_ci	snd_miro_write_mask(chip, OPTi9XX_MC_REG(2), 0x20, 0x20); /* OPL4 */
10088c2ecf20Sopenharmony_ci	snd_miro_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	switch (chip->hardware) {
10118c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C924:
10128c2ecf20Sopenharmony_ci		snd_miro_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
10138c2ecf20Sopenharmony_ci		snd_miro_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
10148c2ecf20Sopenharmony_ci		break;
10158c2ecf20Sopenharmony_ci	case OPTi9XX_HW_82C929:
10168c2ecf20Sopenharmony_ci		/* untested init commands for OPTi929 */
10178c2ecf20Sopenharmony_ci		snd_miro_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
10188c2ecf20Sopenharmony_ci		break;
10198c2ecf20Sopenharmony_ci	default:
10208c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
10218c2ecf20Sopenharmony_ci		return -EINVAL;
10228c2ecf20Sopenharmony_ci	}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	/* PnP resource says it decodes only 10 bits of address */
10258c2ecf20Sopenharmony_ci	switch (chip->wss_base & 0x3ff) {
10268c2ecf20Sopenharmony_ci	case 0x130:
10278c2ecf20Sopenharmony_ci		chip->wss_base = 0x530;
10288c2ecf20Sopenharmony_ci		wss_base_bits = 0x00;
10298c2ecf20Sopenharmony_ci		break;
10308c2ecf20Sopenharmony_ci	case 0x204:
10318c2ecf20Sopenharmony_ci		chip->wss_base = 0x604;
10328c2ecf20Sopenharmony_ci		wss_base_bits = 0x03;
10338c2ecf20Sopenharmony_ci		break;
10348c2ecf20Sopenharmony_ci	case 0x280:
10358c2ecf20Sopenharmony_ci		chip->wss_base = 0xe80;
10368c2ecf20Sopenharmony_ci		wss_base_bits = 0x01;
10378c2ecf20Sopenharmony_ci		break;
10388c2ecf20Sopenharmony_ci	case 0x340:
10398c2ecf20Sopenharmony_ci		chip->wss_base = 0xf40;
10408c2ecf20Sopenharmony_ci		wss_base_bits = 0x02;
10418c2ecf20Sopenharmony_ci		break;
10428c2ecf20Sopenharmony_ci	default:
10438c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "WSS port 0x%lx not valid\n", chip->wss_base);
10448c2ecf20Sopenharmony_ci		goto __skip_base;
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci	snd_miro_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci__skip_base:
10498c2ecf20Sopenharmony_ci	switch (chip->irq) {
10508c2ecf20Sopenharmony_ci	case 5:
10518c2ecf20Sopenharmony_ci		irq_bits = 0x05;
10528c2ecf20Sopenharmony_ci		break;
10538c2ecf20Sopenharmony_ci	case 7:
10548c2ecf20Sopenharmony_ci		irq_bits = 0x01;
10558c2ecf20Sopenharmony_ci		break;
10568c2ecf20Sopenharmony_ci	case 9:
10578c2ecf20Sopenharmony_ci		irq_bits = 0x02;
10588c2ecf20Sopenharmony_ci		break;
10598c2ecf20Sopenharmony_ci	case 10:
10608c2ecf20Sopenharmony_ci		irq_bits = 0x03;
10618c2ecf20Sopenharmony_ci		break;
10628c2ecf20Sopenharmony_ci	case 11:
10638c2ecf20Sopenharmony_ci		irq_bits = 0x04;
10648c2ecf20Sopenharmony_ci		break;
10658c2ecf20Sopenharmony_ci	default:
10668c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "WSS irq # %d not valid\n", chip->irq);
10678c2ecf20Sopenharmony_ci		goto __skip_resources;
10688c2ecf20Sopenharmony_ci	}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	switch (chip->dma1) {
10718c2ecf20Sopenharmony_ci	case 0:
10728c2ecf20Sopenharmony_ci		dma_bits = 0x01;
10738c2ecf20Sopenharmony_ci		break;
10748c2ecf20Sopenharmony_ci	case 1:
10758c2ecf20Sopenharmony_ci		dma_bits = 0x02;
10768c2ecf20Sopenharmony_ci		break;
10778c2ecf20Sopenharmony_ci	case 3:
10788c2ecf20Sopenharmony_ci		dma_bits = 0x03;
10798c2ecf20Sopenharmony_ci		break;
10808c2ecf20Sopenharmony_ci	default:
10818c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "WSS dma1 # %d not valid\n", chip->dma1);
10828c2ecf20Sopenharmony_ci		goto __skip_resources;
10838c2ecf20Sopenharmony_ci	}
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	if (chip->dma1 == chip->dma2) {
10868c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "don't want to share dmas\n");
10878c2ecf20Sopenharmony_ci		return -EBUSY;
10888c2ecf20Sopenharmony_ci	}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	switch (chip->dma2) {
10918c2ecf20Sopenharmony_ci	case 0:
10928c2ecf20Sopenharmony_ci	case 1:
10938c2ecf20Sopenharmony_ci		break;
10948c2ecf20Sopenharmony_ci	default:
10958c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "WSS dma2 # %d not valid\n", chip->dma2);
10968c2ecf20Sopenharmony_ci		goto __skip_resources;
10978c2ecf20Sopenharmony_ci	}
10988c2ecf20Sopenharmony_ci	dma_bits |= 0x04;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
11018c2ecf20Sopenharmony_ci	outb(irq_bits << 3 | dma_bits, chip->wss_base);
11028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci__skip_resources:
11058c2ecf20Sopenharmony_ci	if (chip->hardware > OPTi9XX_HW_82C928) {
11068c2ecf20Sopenharmony_ci		switch (chip->mpu_port) {
11078c2ecf20Sopenharmony_ci		case 0:
11088c2ecf20Sopenharmony_ci		case -1:
11098c2ecf20Sopenharmony_ci			break;
11108c2ecf20Sopenharmony_ci		case 0x300:
11118c2ecf20Sopenharmony_ci			mpu_port_bits = 0x03;
11128c2ecf20Sopenharmony_ci			break;
11138c2ecf20Sopenharmony_ci		case 0x310:
11148c2ecf20Sopenharmony_ci			mpu_port_bits = 0x02;
11158c2ecf20Sopenharmony_ci			break;
11168c2ecf20Sopenharmony_ci		case 0x320:
11178c2ecf20Sopenharmony_ci			mpu_port_bits = 0x01;
11188c2ecf20Sopenharmony_ci			break;
11198c2ecf20Sopenharmony_ci		case 0x330:
11208c2ecf20Sopenharmony_ci			mpu_port_bits = 0x00;
11218c2ecf20Sopenharmony_ci			break;
11228c2ecf20Sopenharmony_ci		default:
11238c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "MPU-401 port 0x%lx not valid\n",
11248c2ecf20Sopenharmony_ci				   chip->mpu_port);
11258c2ecf20Sopenharmony_ci			goto __skip_mpu;
11268c2ecf20Sopenharmony_ci		}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci		switch (chip->mpu_irq) {
11298c2ecf20Sopenharmony_ci		case 5:
11308c2ecf20Sopenharmony_ci			mpu_irq_bits = 0x02;
11318c2ecf20Sopenharmony_ci			break;
11328c2ecf20Sopenharmony_ci		case 7:
11338c2ecf20Sopenharmony_ci			mpu_irq_bits = 0x03;
11348c2ecf20Sopenharmony_ci			break;
11358c2ecf20Sopenharmony_ci		case 9:
11368c2ecf20Sopenharmony_ci			mpu_irq_bits = 0x00;
11378c2ecf20Sopenharmony_ci			break;
11388c2ecf20Sopenharmony_ci		case 10:
11398c2ecf20Sopenharmony_ci			mpu_irq_bits = 0x01;
11408c2ecf20Sopenharmony_ci			break;
11418c2ecf20Sopenharmony_ci		default:
11428c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "MPU-401 irq # %d not valid\n",
11438c2ecf20Sopenharmony_ci				   chip->mpu_irq);
11448c2ecf20Sopenharmony_ci			goto __skip_mpu;
11458c2ecf20Sopenharmony_ci		}
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci		snd_miro_write_mask(chip, OPTi9XX_MC_REG(6),
11488c2ecf20Sopenharmony_ci			(chip->mpu_port <= 0) ? 0x00 :
11498c2ecf20Sopenharmony_ci				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
11508c2ecf20Sopenharmony_ci			0xf8);
11518c2ecf20Sopenharmony_ci	}
11528c2ecf20Sopenharmony_ci__skip_mpu:
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	return 0;
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic int snd_miro_opti_check(struct snd_miro *chip)
11588c2ecf20Sopenharmony_ci{
11598c2ecf20Sopenharmony_ci	unsigned char value;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
11628c2ecf20Sopenharmony_ci					   "OPTi9xx MC");
11638c2ecf20Sopenharmony_ci	if (chip->res_mc_base == NULL)
11648c2ecf20Sopenharmony_ci		return -ENOMEM;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	value = snd_miro_read(chip, OPTi9XX_MC_REG(1));
11678c2ecf20Sopenharmony_ci	if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
11688c2ecf20Sopenharmony_ci		if (value == snd_miro_read(chip, OPTi9XX_MC_REG(1)))
11698c2ecf20Sopenharmony_ci			return 0;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	release_and_free_resource(chip->res_mc_base);
11728c2ecf20Sopenharmony_ci	chip->res_mc_base = NULL;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	return -ENODEV;
11758c2ecf20Sopenharmony_ci}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_cistatic int snd_card_miro_detect(struct snd_card *card,
11788c2ecf20Sopenharmony_ci				struct snd_miro *chip)
11798c2ecf20Sopenharmony_ci{
11808c2ecf20Sopenharmony_ci	int i, err;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	for (i = OPTi9XX_HW_82C929; i <= OPTi9XX_HW_82C924; i++) {
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci		if ((err = snd_miro_init(chip, i)) < 0)
11858c2ecf20Sopenharmony_ci			return err;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		err = snd_miro_opti_check(chip);
11888c2ecf20Sopenharmony_ci		if (err == 0)
11898c2ecf20Sopenharmony_ci			return 1;
11908c2ecf20Sopenharmony_ci	}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	return -ENODEV;
11938c2ecf20Sopenharmony_ci}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_cistatic int snd_card_miro_aci_detect(struct snd_card *card,
11968c2ecf20Sopenharmony_ci				    struct snd_miro *miro)
11978c2ecf20Sopenharmony_ci{
11988c2ecf20Sopenharmony_ci	unsigned char regval;
11998c2ecf20Sopenharmony_ci	int i;
12008c2ecf20Sopenharmony_ci	struct snd_miro_aci *aci = &aci_device;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	miro->aci = aci;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	mutex_init(&aci->aci_mutex);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	/* get ACI port from OPTi9xx MC 4 */
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	regval=inb(miro->mc_base + 4);
12098c2ecf20Sopenharmony_ci	aci->aci_port = (regval & 0x10) ? 0x344 : 0x354;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	miro->res_aci_port = request_region(aci->aci_port, 3, "miro aci");
12128c2ecf20Sopenharmony_ci	if (miro->res_aci_port == NULL) {
12138c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "aci i/o area 0x%lx-0x%lx already used.\n",
12148c2ecf20Sopenharmony_ci			   aci->aci_port, aci->aci_port+2);
12158c2ecf20Sopenharmony_ci		return -ENOMEM;
12168c2ecf20Sopenharmony_ci	}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci        /* force ACI into a known state */
12198c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++)
12208c2ecf20Sopenharmony_ci		if (snd_aci_cmd(aci, ACI_ERROR_OP, -1, -1) < 0) {
12218c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "can't force aci into known state.\n");
12228c2ecf20Sopenharmony_ci			return -ENXIO;
12238c2ecf20Sopenharmony_ci		}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	aci->aci_vendor = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
12268c2ecf20Sopenharmony_ci	aci->aci_product = snd_aci_cmd(aci, ACI_READ_IDCODE, -1, -1);
12278c2ecf20Sopenharmony_ci	if (aci->aci_vendor < 0 || aci->aci_product < 0) {
12288c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "can't read aci id on 0x%lx.\n",
12298c2ecf20Sopenharmony_ci			   aci->aci_port);
12308c2ecf20Sopenharmony_ci		return -ENXIO;
12318c2ecf20Sopenharmony_ci	}
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	aci->aci_version = snd_aci_cmd(aci, ACI_READ_VERSION, -1, -1);
12348c2ecf20Sopenharmony_ci	if (aci->aci_version < 0) {
12358c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "can't read aci version on 0x%lx.\n",
12368c2ecf20Sopenharmony_ci			   aci->aci_port);
12378c2ecf20Sopenharmony_ci		return -ENXIO;
12388c2ecf20Sopenharmony_ci	}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	if (snd_aci_cmd(aci, ACI_INIT, -1, -1) < 0 ||
12418c2ecf20Sopenharmony_ci	    snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0 ||
12428c2ecf20Sopenharmony_ci	    snd_aci_cmd(aci, ACI_ERROR_OP, ACI_ERROR_OP, ACI_ERROR_OP) < 0) {
12438c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "can't initialize aci.\n");
12448c2ecf20Sopenharmony_ci		return -ENXIO;
12458c2ecf20Sopenharmony_ci	}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	return 0;
12488c2ecf20Sopenharmony_ci}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_cistatic void snd_card_miro_free(struct snd_card *card)
12518c2ecf20Sopenharmony_ci{
12528c2ecf20Sopenharmony_ci	struct snd_miro *miro = card->private_data;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	release_and_free_resource(miro->res_aci_port);
12558c2ecf20Sopenharmony_ci	if (miro->aci)
12568c2ecf20Sopenharmony_ci		miro->aci->aci_port = 0;
12578c2ecf20Sopenharmony_ci	release_and_free_resource(miro->res_mc_base);
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_cistatic int snd_miro_probe(struct snd_card *card)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	int error;
12638c2ecf20Sopenharmony_ci	struct snd_miro *miro = card->private_data;
12648c2ecf20Sopenharmony_ci	struct snd_wss *codec;
12658c2ecf20Sopenharmony_ci	struct snd_rawmidi *rmidi;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	if (!miro->res_mc_base) {
12688c2ecf20Sopenharmony_ci		miro->res_mc_base = request_region(miro->mc_base,
12698c2ecf20Sopenharmony_ci						miro->mc_base_size,
12708c2ecf20Sopenharmony_ci						"miro (OPTi9xx MC)");
12718c2ecf20Sopenharmony_ci		if (miro->res_mc_base == NULL) {
12728c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "request for OPTI9xx MC failed\n");
12738c2ecf20Sopenharmony_ci			return -ENOMEM;
12748c2ecf20Sopenharmony_ci		}
12758c2ecf20Sopenharmony_ci	}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	error = snd_card_miro_aci_detect(card, miro);
12788c2ecf20Sopenharmony_ci	if (error < 0) {
12798c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "unable to detect aci chip\n");
12808c2ecf20Sopenharmony_ci		return -ENODEV;
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	miro->wss_base = port;
12848c2ecf20Sopenharmony_ci	miro->mpu_port = mpu_port;
12858c2ecf20Sopenharmony_ci	miro->irq = irq;
12868c2ecf20Sopenharmony_ci	miro->mpu_irq = mpu_irq;
12878c2ecf20Sopenharmony_ci	miro->dma1 = dma1;
12888c2ecf20Sopenharmony_ci	miro->dma2 = dma2;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	/* init proc interface */
12918c2ecf20Sopenharmony_ci	snd_miro_proc_init(card, miro);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	error = snd_miro_configure(miro);
12948c2ecf20Sopenharmony_ci	if (error)
12958c2ecf20Sopenharmony_ci		return error;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	error = snd_wss_create(card, miro->wss_base + 4, -1,
12988c2ecf20Sopenharmony_ci			       miro->irq, miro->dma1, miro->dma2,
12998c2ecf20Sopenharmony_ci			       WSS_HW_DETECT, 0, &codec);
13008c2ecf20Sopenharmony_ci	if (error < 0)
13018c2ecf20Sopenharmony_ci		return error;
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	error = snd_wss_pcm(codec, 0);
13048c2ecf20Sopenharmony_ci	if (error < 0)
13058c2ecf20Sopenharmony_ci		return error;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	error = snd_wss_mixer(codec);
13088c2ecf20Sopenharmony_ci	if (error < 0)
13098c2ecf20Sopenharmony_ci		return error;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	error = snd_wss_timer(codec, 0);
13128c2ecf20Sopenharmony_ci	if (error < 0)
13138c2ecf20Sopenharmony_ci		return error;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	miro->pcm = codec->pcm;
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	error = snd_miro_mixer(card, miro);
13188c2ecf20Sopenharmony_ci	if (error < 0)
13198c2ecf20Sopenharmony_ci		return error;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	if (miro->aci->aci_vendor == 'm') {
13228c2ecf20Sopenharmony_ci		/* It looks like a miro sound card. */
13238c2ecf20Sopenharmony_ci		switch (miro->aci->aci_product) {
13248c2ecf20Sopenharmony_ci		case 'A':
13258c2ecf20Sopenharmony_ci			sprintf(card->shortname,
13268c2ecf20Sopenharmony_ci				"miroSOUND PCM1 pro / PCM12");
13278c2ecf20Sopenharmony_ci			break;
13288c2ecf20Sopenharmony_ci		case 'B':
13298c2ecf20Sopenharmony_ci			sprintf(card->shortname,
13308c2ecf20Sopenharmony_ci				"miroSOUND PCM12");
13318c2ecf20Sopenharmony_ci			break;
13328c2ecf20Sopenharmony_ci		case 'C':
13338c2ecf20Sopenharmony_ci			sprintf(card->shortname,
13348c2ecf20Sopenharmony_ci				"miroSOUND PCM20 radio");
13358c2ecf20Sopenharmony_ci			break;
13368c2ecf20Sopenharmony_ci		default:
13378c2ecf20Sopenharmony_ci			sprintf(card->shortname,
13388c2ecf20Sopenharmony_ci				"unknown miro");
13398c2ecf20Sopenharmony_ci			snd_printk(KERN_INFO "unknown miro aci id\n");
13408c2ecf20Sopenharmony_ci			break;
13418c2ecf20Sopenharmony_ci		}
13428c2ecf20Sopenharmony_ci	} else {
13438c2ecf20Sopenharmony_ci		snd_printk(KERN_INFO "found unsupported aci card\n");
13448c2ecf20Sopenharmony_ci		sprintf(card->shortname, "unknown Cardinal Technologies");
13458c2ecf20Sopenharmony_ci	}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	strcpy(card->driver, "miro");
13488c2ecf20Sopenharmony_ci	snprintf(card->longname, sizeof(card->longname),
13498c2ecf20Sopenharmony_ci		 "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d",
13508c2ecf20Sopenharmony_ci		 card->shortname, miro->name, codec->pcm->name,
13518c2ecf20Sopenharmony_ci		 miro->wss_base + 4, miro->irq, miro->dma1, miro->dma2);
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
13548c2ecf20Sopenharmony_ci		rmidi = NULL;
13558c2ecf20Sopenharmony_ci	else {
13568c2ecf20Sopenharmony_ci		error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
13578c2ecf20Sopenharmony_ci				mpu_port, 0, miro->mpu_irq, &rmidi);
13588c2ecf20Sopenharmony_ci		if (error < 0)
13598c2ecf20Sopenharmony_ci			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
13608c2ecf20Sopenharmony_ci				   mpu_port);
13618c2ecf20Sopenharmony_ci	}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
13648c2ecf20Sopenharmony_ci		struct snd_opl3 *opl3 = NULL;
13658c2ecf20Sopenharmony_ci		struct snd_opl4 *opl4;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci		if (snd_opl4_create(card, fm_port, fm_port - 8,
13688c2ecf20Sopenharmony_ci				    2, &opl3, &opl4) < 0)
13698c2ecf20Sopenharmony_ci			snd_printk(KERN_WARNING "no OPL4 device at 0x%lx\n",
13708c2ecf20Sopenharmony_ci				   fm_port);
13718c2ecf20Sopenharmony_ci	}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	error = snd_set_aci_init_values(miro);
13748c2ecf20Sopenharmony_ci	if (error < 0)
13758c2ecf20Sopenharmony_ci                return error;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	return snd_card_register(card);
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_cistatic int snd_miro_isa_match(struct device *devptr, unsigned int n)
13818c2ecf20Sopenharmony_ci{
13828c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
13838c2ecf20Sopenharmony_ci	if (snd_miro_pnp_is_probed)
13848c2ecf20Sopenharmony_ci		return 0;
13858c2ecf20Sopenharmony_ci	if (isapnp)
13868c2ecf20Sopenharmony_ci		return 0;
13878c2ecf20Sopenharmony_ci#endif
13888c2ecf20Sopenharmony_ci	return 1;
13898c2ecf20Sopenharmony_ci}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_cistatic int snd_miro_isa_probe(struct device *devptr, unsigned int n)
13928c2ecf20Sopenharmony_ci{
13938c2ecf20Sopenharmony_ci	static const long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
13948c2ecf20Sopenharmony_ci	static const long possible_mpu_ports[] = {0x330, 0x300, 0x310, 0x320, -1};
13958c2ecf20Sopenharmony_ci	static const int possible_irqs[] = {11, 9, 10, 7, -1};
13968c2ecf20Sopenharmony_ci	static const int possible_mpu_irqs[] = {10, 5, 9, 7, -1};
13978c2ecf20Sopenharmony_ci	static const int possible_dma1s[] = {3, 1, 0, -1};
13988c2ecf20Sopenharmony_ci	static const int possible_dma2s[][2] = { {1, -1}, {0, -1}, {-1, -1},
13998c2ecf20Sopenharmony_ci					   {0, -1} };
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	int error;
14028c2ecf20Sopenharmony_ci	struct snd_miro *miro;
14038c2ecf20Sopenharmony_ci	struct snd_card *card;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	error = snd_card_new(devptr, index, id, THIS_MODULE,
14068c2ecf20Sopenharmony_ci			     sizeof(struct snd_miro), &card);
14078c2ecf20Sopenharmony_ci	if (error < 0)
14088c2ecf20Sopenharmony_ci		return error;
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	card->private_free = snd_card_miro_free;
14118c2ecf20Sopenharmony_ci	miro = card->private_data;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	error = snd_card_miro_detect(card, miro);
14148c2ecf20Sopenharmony_ci	if (error < 0) {
14158c2ecf20Sopenharmony_ci		snd_card_free(card);
14168c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "unable to detect OPTi9xx chip\n");
14178c2ecf20Sopenharmony_ci		return -ENODEV;
14188c2ecf20Sopenharmony_ci	}
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	if (port == SNDRV_AUTO_PORT) {
14218c2ecf20Sopenharmony_ci		port = snd_legacy_find_free_ioport(possible_ports, 4);
14228c2ecf20Sopenharmony_ci		if (port < 0) {
14238c2ecf20Sopenharmony_ci			snd_card_free(card);
14248c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "unable to find a free WSS port\n");
14258c2ecf20Sopenharmony_ci			return -EBUSY;
14268c2ecf20Sopenharmony_ci		}
14278c2ecf20Sopenharmony_ci	}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	if (mpu_port == SNDRV_AUTO_PORT) {
14308c2ecf20Sopenharmony_ci		mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2);
14318c2ecf20Sopenharmony_ci		if (mpu_port < 0) {
14328c2ecf20Sopenharmony_ci			snd_card_free(card);
14338c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR
14348c2ecf20Sopenharmony_ci				   "unable to find a free MPU401 port\n");
14358c2ecf20Sopenharmony_ci			return -EBUSY;
14368c2ecf20Sopenharmony_ci		}
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	if (irq == SNDRV_AUTO_IRQ) {
14408c2ecf20Sopenharmony_ci		irq = snd_legacy_find_free_irq(possible_irqs);
14418c2ecf20Sopenharmony_ci		if (irq < 0) {
14428c2ecf20Sopenharmony_ci			snd_card_free(card);
14438c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "unable to find a free IRQ\n");
14448c2ecf20Sopenharmony_ci			return -EBUSY;
14458c2ecf20Sopenharmony_ci		}
14468c2ecf20Sopenharmony_ci	}
14478c2ecf20Sopenharmony_ci	if (mpu_irq == SNDRV_AUTO_IRQ) {
14488c2ecf20Sopenharmony_ci		mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs);
14498c2ecf20Sopenharmony_ci		if (mpu_irq < 0) {
14508c2ecf20Sopenharmony_ci			snd_card_free(card);
14518c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR
14528c2ecf20Sopenharmony_ci				   "unable to find a free MPU401 IRQ\n");
14538c2ecf20Sopenharmony_ci			return -EBUSY;
14548c2ecf20Sopenharmony_ci		}
14558c2ecf20Sopenharmony_ci	}
14568c2ecf20Sopenharmony_ci	if (dma1 == SNDRV_AUTO_DMA) {
14578c2ecf20Sopenharmony_ci		dma1 = snd_legacy_find_free_dma(possible_dma1s);
14588c2ecf20Sopenharmony_ci		if (dma1 < 0) {
14598c2ecf20Sopenharmony_ci			snd_card_free(card);
14608c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "unable to find a free DMA1\n");
14618c2ecf20Sopenharmony_ci			return -EBUSY;
14628c2ecf20Sopenharmony_ci		}
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci	if (dma2 == SNDRV_AUTO_DMA) {
14658c2ecf20Sopenharmony_ci		dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4]);
14668c2ecf20Sopenharmony_ci		if (dma2 < 0) {
14678c2ecf20Sopenharmony_ci			snd_card_free(card);
14688c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "unable to find a free DMA2\n");
14698c2ecf20Sopenharmony_ci			return -EBUSY;
14708c2ecf20Sopenharmony_ci		}
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	error = snd_miro_probe(card);
14748c2ecf20Sopenharmony_ci	if (error < 0) {
14758c2ecf20Sopenharmony_ci		snd_card_free(card);
14768c2ecf20Sopenharmony_ci		return error;
14778c2ecf20Sopenharmony_ci	}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	dev_set_drvdata(devptr, card);
14808c2ecf20Sopenharmony_ci	return 0;
14818c2ecf20Sopenharmony_ci}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_cistatic int snd_miro_isa_remove(struct device *devptr,
14848c2ecf20Sopenharmony_ci			       unsigned int dev)
14858c2ecf20Sopenharmony_ci{
14868c2ecf20Sopenharmony_ci	snd_card_free(dev_get_drvdata(devptr));
14878c2ecf20Sopenharmony_ci	return 0;
14888c2ecf20Sopenharmony_ci}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci#define DEV_NAME "miro"
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_cistatic struct isa_driver snd_miro_driver = {
14938c2ecf20Sopenharmony_ci	.match		= snd_miro_isa_match,
14948c2ecf20Sopenharmony_ci	.probe		= snd_miro_isa_probe,
14958c2ecf20Sopenharmony_ci	.remove		= snd_miro_isa_remove,
14968c2ecf20Sopenharmony_ci	/* FIXME: suspend/resume */
14978c2ecf20Sopenharmony_ci	.driver		= {
14988c2ecf20Sopenharmony_ci		.name	= DEV_NAME
14998c2ecf20Sopenharmony_ci	},
15008c2ecf20Sopenharmony_ci};
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_cistatic int snd_card_miro_pnp(struct snd_miro *chip,
15058c2ecf20Sopenharmony_ci			     struct pnp_card_link *card,
15068c2ecf20Sopenharmony_ci			     const struct pnp_card_device_id *pid)
15078c2ecf20Sopenharmony_ci{
15088c2ecf20Sopenharmony_ci	struct pnp_dev *pdev;
15098c2ecf20Sopenharmony_ci	int err;
15108c2ecf20Sopenharmony_ci	struct pnp_dev *devmpu;
15118c2ecf20Sopenharmony_ci	struct pnp_dev *devmc;
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
15148c2ecf20Sopenharmony_ci	if (pdev == NULL)
15158c2ecf20Sopenharmony_ci		return -EBUSY;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
15188c2ecf20Sopenharmony_ci	if (devmpu == NULL)
15198c2ecf20Sopenharmony_ci		return -EBUSY;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
15228c2ecf20Sopenharmony_ci	if (devmc == NULL)
15238c2ecf20Sopenharmony_ci		return -EBUSY;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	err = pnp_activate_dev(pdev);
15268c2ecf20Sopenharmony_ci	if (err < 0) {
15278c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
15288c2ecf20Sopenharmony_ci		return err;
15298c2ecf20Sopenharmony_ci	}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	err = pnp_activate_dev(devmc);
15328c2ecf20Sopenharmony_ci	if (err < 0) {
15338c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "MC pnp configure failure: %d\n",
15348c2ecf20Sopenharmony_ci				    err);
15358c2ecf20Sopenharmony_ci		return err;
15368c2ecf20Sopenharmony_ci	}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	port = pnp_port_start(pdev, 1);
15398c2ecf20Sopenharmony_ci	fm_port = pnp_port_start(pdev, 2) + 8;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	/*
15428c2ecf20Sopenharmony_ci	 * The MC(0) is never accessed and the miroSOUND PCM20 card does not
15438c2ecf20Sopenharmony_ci	 * include it in the PnP resource range. OPTI93x include it.
15448c2ecf20Sopenharmony_ci	 */
15458c2ecf20Sopenharmony_ci	chip->mc_base = pnp_port_start(devmc, 0) - 1;
15468c2ecf20Sopenharmony_ci	chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	irq = pnp_irq(pdev, 0);
15498c2ecf20Sopenharmony_ci	dma1 = pnp_dma(pdev, 0);
15508c2ecf20Sopenharmony_ci	dma2 = pnp_dma(pdev, 1);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	if (mpu_port > 0) {
15538c2ecf20Sopenharmony_ci		err = pnp_activate_dev(devmpu);
15548c2ecf20Sopenharmony_ci		if (err < 0) {
15558c2ecf20Sopenharmony_ci			snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
15568c2ecf20Sopenharmony_ci			mpu_port = -1;
15578c2ecf20Sopenharmony_ci			return err;
15588c2ecf20Sopenharmony_ci		}
15598c2ecf20Sopenharmony_ci		mpu_port = pnp_port_start(devmpu, 0);
15608c2ecf20Sopenharmony_ci		mpu_irq = pnp_irq(devmpu, 0);
15618c2ecf20Sopenharmony_ci	}
15628c2ecf20Sopenharmony_ci	return 0;
15638c2ecf20Sopenharmony_ci}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_cistatic int snd_miro_pnp_probe(struct pnp_card_link *pcard,
15668c2ecf20Sopenharmony_ci			      const struct pnp_card_device_id *pid)
15678c2ecf20Sopenharmony_ci{
15688c2ecf20Sopenharmony_ci	struct snd_card *card;
15698c2ecf20Sopenharmony_ci	int err;
15708c2ecf20Sopenharmony_ci	struct snd_miro *miro;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	if (snd_miro_pnp_is_probed)
15738c2ecf20Sopenharmony_ci		return -EBUSY;
15748c2ecf20Sopenharmony_ci	if (!isapnp)
15758c2ecf20Sopenharmony_ci		return -ENODEV;
15768c2ecf20Sopenharmony_ci	err = snd_card_new(&pcard->card->dev, index, id, THIS_MODULE,
15778c2ecf20Sopenharmony_ci			   sizeof(struct snd_miro), &card);
15788c2ecf20Sopenharmony_ci	if (err < 0)
15798c2ecf20Sopenharmony_ci		return err;
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	card->private_free = snd_card_miro_free;
15828c2ecf20Sopenharmony_ci	miro = card->private_data;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	err = snd_card_miro_pnp(miro, pcard, pid);
15858c2ecf20Sopenharmony_ci	if (err) {
15868c2ecf20Sopenharmony_ci		snd_card_free(card);
15878c2ecf20Sopenharmony_ci		return err;
15888c2ecf20Sopenharmony_ci	}
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	/* only miroSOUND PCM20 and PCM12 == OPTi924 */
15918c2ecf20Sopenharmony_ci	err = snd_miro_init(miro, OPTi9XX_HW_82C924);
15928c2ecf20Sopenharmony_ci	if (err) {
15938c2ecf20Sopenharmony_ci		snd_card_free(card);
15948c2ecf20Sopenharmony_ci		return err;
15958c2ecf20Sopenharmony_ci	}
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	err = snd_miro_opti_check(miro);
15988c2ecf20Sopenharmony_ci	if (err) {
15998c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR "OPTI chip not found\n");
16008c2ecf20Sopenharmony_ci		snd_card_free(card);
16018c2ecf20Sopenharmony_ci		return err;
16028c2ecf20Sopenharmony_ci	}
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	err = snd_miro_probe(card);
16058c2ecf20Sopenharmony_ci	if (err < 0) {
16068c2ecf20Sopenharmony_ci		snd_card_free(card);
16078c2ecf20Sopenharmony_ci		return err;
16088c2ecf20Sopenharmony_ci	}
16098c2ecf20Sopenharmony_ci	pnp_set_card_drvdata(pcard, card);
16108c2ecf20Sopenharmony_ci	snd_miro_pnp_is_probed = 1;
16118c2ecf20Sopenharmony_ci	return 0;
16128c2ecf20Sopenharmony_ci}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_cistatic void snd_miro_pnp_remove(struct pnp_card_link *pcard)
16158c2ecf20Sopenharmony_ci{
16168c2ecf20Sopenharmony_ci	snd_card_free(pnp_get_card_drvdata(pcard));
16178c2ecf20Sopenharmony_ci	pnp_set_card_drvdata(pcard, NULL);
16188c2ecf20Sopenharmony_ci	snd_miro_pnp_is_probed = 0;
16198c2ecf20Sopenharmony_ci}
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_cistatic struct pnp_card_driver miro_pnpc_driver = {
16228c2ecf20Sopenharmony_ci	.flags		= PNP_DRIVER_RES_DISABLE,
16238c2ecf20Sopenharmony_ci	.name		= "miro",
16248c2ecf20Sopenharmony_ci	.id_table	= snd_miro_pnpids,
16258c2ecf20Sopenharmony_ci	.probe		= snd_miro_pnp_probe,
16268c2ecf20Sopenharmony_ci	.remove		= snd_miro_pnp_remove,
16278c2ecf20Sopenharmony_ci};
16288c2ecf20Sopenharmony_ci#endif
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_cistatic int __init alsa_card_miro_init(void)
16318c2ecf20Sopenharmony_ci{
16328c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
16338c2ecf20Sopenharmony_ci	pnp_register_card_driver(&miro_pnpc_driver);
16348c2ecf20Sopenharmony_ci	if (snd_miro_pnp_is_probed)
16358c2ecf20Sopenharmony_ci		return 0;
16368c2ecf20Sopenharmony_ci	pnp_unregister_card_driver(&miro_pnpc_driver);
16378c2ecf20Sopenharmony_ci#endif
16388c2ecf20Sopenharmony_ci	return isa_register_driver(&snd_miro_driver, 1);
16398c2ecf20Sopenharmony_ci}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_cistatic void __exit alsa_card_miro_exit(void)
16428c2ecf20Sopenharmony_ci{
16438c2ecf20Sopenharmony_ci	if (!snd_miro_pnp_is_probed) {
16448c2ecf20Sopenharmony_ci		isa_unregister_driver(&snd_miro_driver);
16458c2ecf20Sopenharmony_ci		return;
16468c2ecf20Sopenharmony_ci	}
16478c2ecf20Sopenharmony_ci#ifdef CONFIG_PNP
16488c2ecf20Sopenharmony_ci	pnp_unregister_card_driver(&miro_pnpc_driver);
16498c2ecf20Sopenharmony_ci#endif
16508c2ecf20Sopenharmony_ci}
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_cimodule_init(alsa_card_miro_init)
16538c2ecf20Sopenharmony_cimodule_exit(alsa_card_miro_exit)
1654