xref: /linux/sound/isa/galaxy/galaxy.c (revision 177bf8620cf4ed290ee170a6c5966adc0924b336)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
249531192SRené Herman /*
349531192SRené Herman  * Aztech AZT1605/AZT2316 Driver
449531192SRené Herman  * Copyright (C) 2007,2010  Rene Herman
549531192SRené Herman  */
649531192SRené Herman 
749531192SRené Herman #include <linux/kernel.h>
849531192SRené Herman #include <linux/module.h>
949531192SRené Herman #include <linux/isa.h>
1049531192SRené Herman #include <linux/delay.h>
1149531192SRené Herman #include <linux/io.h>
1249531192SRené Herman #include <asm/processor.h>
1349531192SRené Herman #include <sound/core.h>
1449531192SRené Herman #include <sound/initval.h>
1549531192SRené Herman #include <sound/wss.h>
1649531192SRené Herman #include <sound/mpu401.h>
1749531192SRené Herman #include <sound/opl3.h>
1849531192SRené Herman 
1949531192SRené Herman MODULE_DESCRIPTION(CRD_NAME);
2049531192SRené Herman MODULE_AUTHOR("Rene Herman");
2149531192SRené Herman MODULE_LICENSE("GPL");
2249531192SRené Herman 
2349531192SRené Herman static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
2449531192SRené Herman static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
25a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
2649531192SRené Herman 
2749531192SRené Herman module_param_array(index, int, NULL, 0444);
2849531192SRené Herman MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
2949531192SRené Herman module_param_array(id, charp, NULL, 0444);
3049531192SRené Herman MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
3149531192SRené Herman module_param_array(enable, bool, NULL, 0444);
3249531192SRené Herman MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
3349531192SRené Herman 
3449531192SRené Herman static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
3549531192SRené Herman static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
3649531192SRené Herman static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
3749531192SRené Herman static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
3849531192SRené Herman static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
3949531192SRené Herman static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
4049531192SRené Herman static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
4149531192SRené Herman static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
4249531192SRené Herman 
43e992ef57SDavid Howells module_param_hw_array(port, long, ioport, NULL, 0444);
4449531192SRené Herman MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
45e992ef57SDavid Howells module_param_hw_array(wss_port, long, ioport, NULL, 0444);
4649531192SRené Herman MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");
47e992ef57SDavid Howells module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
4849531192SRené Herman MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
49e992ef57SDavid Howells module_param_hw_array(fm_port, long, ioport, NULL, 0444);
5049531192SRené Herman MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");
51e992ef57SDavid Howells module_param_hw_array(irq, int, irq, NULL, 0444);
5249531192SRené Herman MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
53e992ef57SDavid Howells module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
5449531192SRené Herman MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
55e992ef57SDavid Howells module_param_hw_array(dma1, int, dma, NULL, 0444);
5649531192SRené Herman MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");
57e992ef57SDavid Howells module_param_hw_array(dma2, int, dma, NULL, 0444);
5849531192SRené Herman MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
5949531192SRené Herman 
6049531192SRené Herman /*
6149531192SRené Herman  * Generic SB DSP support routines
6249531192SRené Herman  */
6349531192SRené Herman 
6449531192SRené Herman #define DSP_PORT_RESET		0x6
6549531192SRené Herman #define DSP_PORT_READ		0xa
6649531192SRené Herman #define DSP_PORT_COMMAND	0xc
6749531192SRené Herman #define DSP_PORT_STATUS		0xc
6849531192SRené Herman #define DSP_PORT_DATA_AVAIL	0xe
6949531192SRené Herman 
7049531192SRené Herman #define DSP_SIGNATURE		0xaa
7149531192SRené Herman 
7249531192SRené Herman #define DSP_COMMAND_GET_VERSION	0xe1
7349531192SRené Herman 
dsp_get_byte(void __iomem * port,u8 * val)741bff292eSBill Pemberton static int dsp_get_byte(void __iomem *port, u8 *val)
7549531192SRené Herman {
7649531192SRené Herman 	int loops = 1000;
7749531192SRené Herman 
7849531192SRené Herman 	while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) {
7949531192SRené Herman 		if (!loops--)
8049531192SRené Herman 			return -EIO;
8149531192SRené Herman 		cpu_relax();
8249531192SRené Herman 	}
8349531192SRené Herman 	*val = ioread8(port + DSP_PORT_READ);
8449531192SRené Herman 	return 0;
8549531192SRené Herman }
8649531192SRené Herman 
dsp_reset(void __iomem * port)871bff292eSBill Pemberton static int dsp_reset(void __iomem *port)
8849531192SRené Herman {
8949531192SRené Herman 	u8 val;
9049531192SRené Herman 
9149531192SRené Herman 	iowrite8(1, port + DSP_PORT_RESET);
9249531192SRené Herman 	udelay(10);
9349531192SRené Herman 	iowrite8(0, port + DSP_PORT_RESET);
9449531192SRené Herman 
9549531192SRené Herman 	if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE)
9649531192SRené Herman 		return -ENODEV;
9749531192SRené Herman 
9849531192SRené Herman 	return 0;
9949531192SRené Herman }
10049531192SRené Herman 
dsp_command(void __iomem * port,u8 cmd)1011bff292eSBill Pemberton static int dsp_command(void __iomem *port, u8 cmd)
10249531192SRené Herman {
10349531192SRené Herman 	int loops = 1000;
10449531192SRené Herman 
10549531192SRené Herman 	while (ioread8(port + DSP_PORT_STATUS) & 0x80) {
10649531192SRené Herman 		if (!loops--)
10749531192SRené Herman 			return -EIO;
10849531192SRené Herman 		cpu_relax();
10949531192SRené Herman 	}
11049531192SRené Herman 	iowrite8(cmd, port + DSP_PORT_COMMAND);
11149531192SRené Herman 	return 0;
11249531192SRené Herman }
11349531192SRené Herman 
dsp_get_version(void __iomem * port,u8 * major,u8 * minor)1141bff292eSBill Pemberton static int dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
11549531192SRené Herman {
11649531192SRené Herman 	int err;
11749531192SRené Herman 
11849531192SRené Herman 	err = dsp_command(port, DSP_COMMAND_GET_VERSION);
11949531192SRené Herman 	if (err < 0)
12049531192SRené Herman 		return err;
12149531192SRené Herman 
12249531192SRené Herman 	err = dsp_get_byte(port, major);
12349531192SRené Herman 	if (err < 0)
12449531192SRené Herman 		return err;
12549531192SRené Herman 
12649531192SRené Herman 	err = dsp_get_byte(port, minor);
12749531192SRené Herman 	if (err < 0)
12849531192SRené Herman 		return err;
12949531192SRené Herman 
13049531192SRené Herman 	return 0;
13149531192SRené Herman }
13249531192SRené Herman 
13349531192SRené Herman /*
13449531192SRené Herman  * Generic WSS support routines
13549531192SRené Herman  */
13649531192SRené Herman 
13749531192SRené Herman #define WSS_CONFIG_DMA_0	(1 << 0)
13849531192SRené Herman #define WSS_CONFIG_DMA_1	(2 << 0)
13949531192SRené Herman #define WSS_CONFIG_DMA_3	(3 << 0)
14049531192SRené Herman #define WSS_CONFIG_DUPLEX	(1 << 2)
14149531192SRené Herman #define WSS_CONFIG_IRQ_7	(1 << 3)
14249531192SRené Herman #define WSS_CONFIG_IRQ_9	(2 << 3)
14349531192SRené Herman #define WSS_CONFIG_IRQ_10	(3 << 3)
14449531192SRené Herman #define WSS_CONFIG_IRQ_11	(4 << 3)
14549531192SRené Herman 
14649531192SRené Herman #define WSS_PORT_CONFIG		0
14749531192SRené Herman #define WSS_PORT_SIGNATURE	3
14849531192SRené Herman 
14949531192SRené Herman #define WSS_SIGNATURE		4
15049531192SRené Herman 
wss_detect(void __iomem * wss_port)1511bff292eSBill Pemberton static int wss_detect(void __iomem *wss_port)
15249531192SRené Herman {
15349531192SRené Herman 	if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE)
15449531192SRené Herman 		return -ENODEV;
15549531192SRené Herman 
15649531192SRené Herman 	return 0;
15749531192SRené Herman }
15849531192SRené Herman 
wss_set_config(void __iomem * wss_port,u8 wss_config)15949531192SRené Herman static void wss_set_config(void __iomem *wss_port, u8 wss_config)
16049531192SRené Herman {
16149531192SRené Herman 	iowrite8(wss_config, wss_port + WSS_PORT_CONFIG);
16249531192SRené Herman }
16349531192SRené Herman 
16449531192SRené Herman /*
16549531192SRené Herman  * Aztech Sound Galaxy specifics
16649531192SRené Herman  */
16749531192SRené Herman 
16849531192SRené Herman #define GALAXY_PORT_CONFIG	1024
16949531192SRené Herman #define CONFIG_PORT_SET		4
17049531192SRené Herman 
17149531192SRené Herman #define DSP_COMMAND_GALAXY_8	8
17249531192SRené Herman #define GALAXY_COMMAND_GET_TYPE	5
17349531192SRené Herman 
17449531192SRené Herman #define DSP_COMMAND_GALAXY_9	9
17549531192SRené Herman #define GALAXY_COMMAND_WSSMODE	0
17649531192SRené Herman #define GALAXY_COMMAND_SB8MODE	1
17749531192SRené Herman 
17849531192SRené Herman #define GALAXY_MODE_WSS		GALAXY_COMMAND_WSSMODE
17949531192SRené Herman #define GALAXY_MODE_SB8		GALAXY_COMMAND_SB8MODE
18049531192SRené Herman 
18149531192SRené Herman struct snd_galaxy {
18249531192SRené Herman 	void __iomem *port;
18349531192SRené Herman 	void __iomem *config_port;
18449531192SRené Herman 	void __iomem *wss_port;
18549531192SRené Herman 	u32 config;
18649531192SRené Herman 	struct resource *res_port;
18749531192SRené Herman 	struct resource *res_config_port;
18849531192SRené Herman 	struct resource *res_wss_port;
18949531192SRené Herman };
19049531192SRené Herman 
19149531192SRené Herman static u32 config[SNDRV_CARDS];
19249531192SRené Herman static u8 wss_config[SNDRV_CARDS];
19349531192SRené Herman 
snd_galaxy_match(struct device * dev,unsigned int n)1941bff292eSBill Pemberton static int snd_galaxy_match(struct device *dev, unsigned int n)
19549531192SRené Herman {
19649531192SRené Herman 	if (!enable[n])
19749531192SRené Herman 		return 0;
19849531192SRené Herman 
19949531192SRené Herman 	switch (port[n]) {
20049531192SRené Herman 	case SNDRV_AUTO_PORT:
20149531192SRené Herman 		dev_err(dev, "please specify port\n");
20249531192SRené Herman 		return 0;
20349531192SRené Herman 	case 0x220:
20449531192SRené Herman 		config[n] |= GALAXY_CONFIG_SBA_220;
20549531192SRené Herman 		break;
20649531192SRené Herman 	case 0x240:
20749531192SRené Herman 		config[n] |= GALAXY_CONFIG_SBA_240;
20849531192SRené Herman 		break;
20949531192SRené Herman 	case 0x260:
21049531192SRené Herman 		config[n] |= GALAXY_CONFIG_SBA_260;
21149531192SRené Herman 		break;
21249531192SRené Herman 	case 0x280:
21349531192SRené Herman 		config[n] |= GALAXY_CONFIG_SBA_280;
21449531192SRené Herman 		break;
21549531192SRené Herman 	default:
21649531192SRené Herman 		dev_err(dev, "invalid port %#lx\n", port[n]);
21749531192SRené Herman 		return 0;
21849531192SRené Herman 	}
21949531192SRené Herman 
22049531192SRené Herman 	switch (wss_port[n]) {
22149531192SRené Herman 	case SNDRV_AUTO_PORT:
22249531192SRené Herman 		dev_err(dev,  "please specify wss_port\n");
22349531192SRené Herman 		return 0;
22449531192SRené Herman 	case 0x530:
22549531192SRené Herman 		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530;
22649531192SRené Herman 		break;
22749531192SRené Herman 	case 0x604:
22849531192SRené Herman 		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604;
22949531192SRené Herman 		break;
23049531192SRené Herman 	case 0xe80:
23149531192SRené Herman 		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80;
23249531192SRené Herman 		break;
23349531192SRené Herman 	case 0xf40:
23449531192SRené Herman 		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40;
23549531192SRené Herman 		break;
23649531192SRené Herman 	default:
23749531192SRené Herman 		dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]);
23849531192SRené Herman 		return 0;
23949531192SRené Herman 	}
24049531192SRené Herman 
24149531192SRené Herman 	switch (irq[n]) {
24249531192SRené Herman 	case SNDRV_AUTO_IRQ:
24349531192SRené Herman 		dev_err(dev,  "please specify irq\n");
24449531192SRené Herman 		return 0;
24549531192SRené Herman 	case 7:
24649531192SRené Herman 		wss_config[n] |= WSS_CONFIG_IRQ_7;
24749531192SRené Herman 		break;
24849531192SRené Herman 	case 2:
24949531192SRené Herman 		irq[n] = 9;
250c0dbbdadSGustavo A. R. Silva 		fallthrough;
25149531192SRené Herman 	case 9:
25249531192SRené Herman 		wss_config[n] |= WSS_CONFIG_IRQ_9;
25349531192SRené Herman 		break;
25449531192SRené Herman 	case 10:
25549531192SRené Herman 		wss_config[n] |= WSS_CONFIG_IRQ_10;
25649531192SRené Herman 		break;
25749531192SRené Herman 	case 11:
25849531192SRené Herman 		wss_config[n] |= WSS_CONFIG_IRQ_11;
25949531192SRené Herman 		break;
26049531192SRené Herman 	default:
26149531192SRené Herman 		dev_err(dev, "invalid IRQ %d\n", irq[n]);
26249531192SRené Herman 		return 0;
26349531192SRené Herman 	}
26449531192SRené Herman 
26549531192SRené Herman 	switch (dma1[n]) {
26649531192SRené Herman 	case SNDRV_AUTO_DMA:
26749531192SRené Herman 		dev_err(dev,  "please specify dma1\n");
26849531192SRené Herman 		return 0;
26949531192SRené Herman 	case 0:
27049531192SRené Herman 		wss_config[n] |= WSS_CONFIG_DMA_0;
27149531192SRené Herman 		break;
27249531192SRené Herman 	case 1:
27349531192SRené Herman 		wss_config[n] |= WSS_CONFIG_DMA_1;
27449531192SRené Herman 		break;
27549531192SRené Herman 	case 3:
27649531192SRené Herman 		wss_config[n] |= WSS_CONFIG_DMA_3;
27749531192SRené Herman 		break;
27849531192SRené Herman 	default:
27949531192SRené Herman 		dev_err(dev, "invalid playback DMA %d\n", dma1[n]);
28049531192SRené Herman 		return 0;
28149531192SRené Herman 	}
28249531192SRené Herman 
28349531192SRené Herman 	if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) {
28449531192SRené Herman 		dma2[n] = -1;
28549531192SRené Herman 		goto mpu;
28649531192SRené Herman 	}
28749531192SRené Herman 
28849531192SRené Herman 	wss_config[n] |= WSS_CONFIG_DUPLEX;
28949531192SRené Herman 	switch (dma2[n]) {
29049531192SRené Herman 	case 0:
29149531192SRené Herman 		break;
29249531192SRené Herman 	case 1:
29349531192SRené Herman 		if (dma1[n] == 0)
29449531192SRené Herman 			break;
295c0dbbdadSGustavo A. R. Silva 		fallthrough;
29649531192SRené Herman 	default:
29749531192SRené Herman 		dev_err(dev, "invalid capture DMA %d\n", dma2[n]);
29849531192SRené Herman 		return 0;
29949531192SRené Herman 	}
30049531192SRené Herman 
30149531192SRené Herman mpu:
30249531192SRené Herman 	switch (mpu_port[n]) {
30349531192SRené Herman 	case SNDRV_AUTO_PORT:
30449531192SRené Herman 		dev_warn(dev, "mpu_port not specified; not using MPU-401\n");
30549531192SRené Herman 		mpu_port[n] = -1;
30649531192SRené Herman 		goto fm;
30749531192SRené Herman 	case 0x300:
30849531192SRené Herman 		config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300;
30949531192SRené Herman 		break;
31049531192SRené Herman 	case 0x330:
31149531192SRené Herman 		config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330;
31249531192SRené Herman 		break;
31349531192SRené Herman 	default:
31449531192SRené Herman 		dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]);
31549531192SRené Herman 		return 0;
31649531192SRené Herman 	}
31749531192SRené Herman 
31849531192SRené Herman 	switch (mpu_irq[n]) {
31949531192SRené Herman 	case SNDRV_AUTO_IRQ:
32049531192SRené Herman 		dev_warn(dev, "mpu_irq not specified: using polling mode\n");
32149531192SRené Herman 		mpu_irq[n] = -1;
32249531192SRené Herman 		break;
32349531192SRené Herman 	case 2:
32449531192SRené Herman 		mpu_irq[n] = 9;
325c0dbbdadSGustavo A. R. Silva 		fallthrough;
32649531192SRené Herman 	case 9:
32749531192SRené Herman 		config[n] |= GALAXY_CONFIG_MPUIRQ_2;
32849531192SRené Herman 		break;
32949531192SRené Herman #ifdef AZT1605
33049531192SRené Herman 	case 3:
33149531192SRené Herman 		config[n] |= GALAXY_CONFIG_MPUIRQ_3;
33249531192SRené Herman 		break;
33349531192SRené Herman #endif
33449531192SRené Herman 	case 5:
33549531192SRené Herman 		config[n] |= GALAXY_CONFIG_MPUIRQ_5;
33649531192SRené Herman 		break;
33749531192SRené Herman 	case 7:
33849531192SRené Herman 		config[n] |= GALAXY_CONFIG_MPUIRQ_7;
33949531192SRené Herman 		break;
34049531192SRené Herman #ifdef AZT2316
34149531192SRené Herman 	case 10:
34249531192SRené Herman 		config[n] |= GALAXY_CONFIG_MPUIRQ_10;
34349531192SRené Herman 		break;
34449531192SRené Herman #endif
34549531192SRené Herman 	default:
34649531192SRené Herman 		dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]);
34749531192SRené Herman 		return 0;
34849531192SRené Herman 	}
34949531192SRené Herman 
35049531192SRené Herman 	if (mpu_irq[n] == irq[n]) {
35149531192SRené Herman 		dev_err(dev, "cannot share IRQ between WSS and MPU-401\n");
35249531192SRené Herman 		return 0;
35349531192SRené Herman 	}
35449531192SRené Herman 
35549531192SRené Herman fm:
35649531192SRené Herman 	switch (fm_port[n]) {
35749531192SRené Herman 	case SNDRV_AUTO_PORT:
35849531192SRené Herman 		dev_warn(dev, "fm_port not specified: not using OPL3\n");
35949531192SRené Herman 		fm_port[n] = -1;
36049531192SRené Herman 		break;
36149531192SRené Herman 	case 0x388:
36249531192SRené Herman 		break;
36349531192SRené Herman 	default:
36449531192SRené Herman 		dev_err(dev, "illegal FM port %#lx\n", fm_port[n]);
36549531192SRené Herman 		return 0;
36649531192SRené Herman 	}
36749531192SRené Herman 
36849531192SRené Herman 	config[n] |= GALAXY_CONFIG_GAME_ENABLE;
36949531192SRené Herman 	return 1;
37049531192SRené Herman }
37149531192SRené Herman 
galaxy_init(struct snd_galaxy * galaxy,u8 * type)3721bff292eSBill Pemberton static int galaxy_init(struct snd_galaxy *galaxy, u8 *type)
37349531192SRené Herman {
37449531192SRené Herman 	u8 major;
37549531192SRené Herman 	u8 minor;
37649531192SRené Herman 	int err;
37749531192SRené Herman 
37849531192SRené Herman 	err = dsp_reset(galaxy->port);
37949531192SRené Herman 	if (err < 0)
38049531192SRené Herman 		return err;
38149531192SRené Herman 
38249531192SRené Herman 	err = dsp_get_version(galaxy->port, &major, &minor);
38349531192SRené Herman 	if (err < 0)
38449531192SRené Herman 		return err;
38549531192SRené Herman 
38649531192SRené Herman 	if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR)
38749531192SRené Herman 		return -ENODEV;
38849531192SRené Herman 
38949531192SRené Herman 	err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8);
39049531192SRené Herman 	if (err < 0)
39149531192SRené Herman 		return err;
39249531192SRené Herman 
39349531192SRené Herman 	err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE);
39449531192SRené Herman 	if (err < 0)
39549531192SRené Herman 		return err;
39649531192SRené Herman 
39749531192SRené Herman 	err = dsp_get_byte(galaxy->port, type);
39849531192SRené Herman 	if (err < 0)
39949531192SRené Herman 		return err;
40049531192SRené Herman 
40149531192SRené Herman 	return 0;
40249531192SRené Herman }
40349531192SRené Herman 
galaxy_set_mode(struct snd_galaxy * galaxy,u8 mode)4041bff292eSBill Pemberton static int galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
40549531192SRené Herman {
40649531192SRené Herman 	int err;
40749531192SRené Herman 
40849531192SRené Herman 	err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9);
40949531192SRené Herman 	if (err < 0)
41049531192SRené Herman 		return err;
41149531192SRené Herman 
41249531192SRené Herman 	err = dsp_command(galaxy->port, mode);
41349531192SRené Herman 	if (err < 0)
41449531192SRené Herman 		return err;
41549531192SRené Herman 
41649531192SRené Herman #ifdef AZT1605
41749531192SRené Herman 	/*
41849531192SRené Herman 	 * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again
41949531192SRené Herman 	 */
42049531192SRené Herman 	err = dsp_reset(galaxy->port);
42149531192SRené Herman 	if (err < 0)
42249531192SRené Herman 		return err;
42349531192SRené Herman #endif
42449531192SRené Herman 
42549531192SRené Herman 	return 0;
42649531192SRené Herman }
42749531192SRené Herman 
galaxy_set_config(struct snd_galaxy * galaxy,u32 config)42849531192SRené Herman static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config)
42949531192SRené Herman {
43049531192SRené Herman 	u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET);
43149531192SRené Herman 	int i;
43249531192SRené Herman 
43349531192SRené Herman 	iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET);
43449531192SRené Herman 	for (i = 0; i < GALAXY_CONFIG_SIZE; i++) {
43549531192SRené Herman 		iowrite8(config, galaxy->config_port + i);
43649531192SRené Herman 		config >>= 8;
43749531192SRené Herman 	}
43849531192SRené Herman 	iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET);
43949531192SRené Herman 	msleep(10);
44049531192SRené Herman }
44149531192SRené Herman 
galaxy_config(struct snd_galaxy * galaxy,u32 config)4421bff292eSBill Pemberton static void galaxy_config(struct snd_galaxy *galaxy, u32 config)
44349531192SRené Herman {
44449531192SRené Herman 	int i;
44549531192SRené Herman 
44649531192SRené Herman 	for (i = GALAXY_CONFIG_SIZE; i; i--) {
44749531192SRené Herman 		u8 tmp = ioread8(galaxy->config_port + i - 1);
44849531192SRené Herman 		galaxy->config = (galaxy->config << 8) | tmp;
44949531192SRené Herman 	}
45049531192SRené Herman 	config |= galaxy->config & GALAXY_CONFIG_MASK;
45149531192SRené Herman 	galaxy_set_config(galaxy, config);
45249531192SRené Herman }
45349531192SRené Herman 
galaxy_wss_config(struct snd_galaxy * galaxy,u8 wss_config)4541bff292eSBill Pemberton static int galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
45549531192SRené Herman {
45649531192SRené Herman 	int err;
45749531192SRené Herman 
45849531192SRené Herman 	err = wss_detect(galaxy->wss_port);
45949531192SRené Herman 	if (err < 0)
46049531192SRené Herman 		return err;
46149531192SRené Herman 
46249531192SRené Herman 	wss_set_config(galaxy->wss_port, wss_config);
46349531192SRené Herman 
46449531192SRené Herman 	err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS);
46549531192SRené Herman 	if (err < 0)
46649531192SRené Herman 		return err;
46749531192SRené Herman 
46849531192SRené Herman 	return 0;
46949531192SRené Herman }
47049531192SRené Herman 
snd_galaxy_free(struct snd_card * card)47149531192SRené Herman static void snd_galaxy_free(struct snd_card *card)
47249531192SRené Herman {
47349531192SRené Herman 	struct snd_galaxy *galaxy = card->private_data;
47449531192SRené Herman 
47535a245ecSTakashi Iwai 	if (galaxy->wss_port)
47649531192SRené Herman 		wss_set_config(galaxy->wss_port, 0);
47735a245ecSTakashi Iwai 	if (galaxy->config_port)
47849531192SRené Herman 		galaxy_set_config(galaxy, galaxy->config);
47949531192SRené Herman }
48049531192SRené Herman 
__snd_galaxy_probe(struct device * dev,unsigned int n)48110b1881aSTakashi Iwai static int __snd_galaxy_probe(struct device *dev, unsigned int n)
48249531192SRené Herman {
48349531192SRené Herman 	struct snd_galaxy *galaxy;
48449531192SRené Herman 	struct snd_wss *chip;
48549531192SRené Herman 	struct snd_card *card;
48649531192SRené Herman 	u8 type;
48749531192SRené Herman 	int err;
48849531192SRené Herman 
48935a245ecSTakashi Iwai 	err = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,
4904323cc4dSTakashi Iwai 				sizeof(*galaxy), &card);
49149531192SRené Herman 	if (err < 0)
49249531192SRené Herman 		return err;
49349531192SRené Herman 
49449531192SRené Herman 	card->private_free = snd_galaxy_free;
49549531192SRené Herman 	galaxy = card->private_data;
49649531192SRené Herman 
49735a245ecSTakashi Iwai 	galaxy->res_port = devm_request_region(dev, port[n], 16, DRV_NAME);
49849531192SRené Herman 	if (!galaxy->res_port) {
49949531192SRené Herman 		dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],
50049531192SRené Herman 			port[n] + 15);
50135a245ecSTakashi Iwai 		return -EBUSY;
50249531192SRené Herman 	}
50335a245ecSTakashi Iwai 	galaxy->port = devm_ioport_map(dev, port[n], 16);
50435a245ecSTakashi Iwai 	if (!galaxy->port)
50535a245ecSTakashi Iwai 		return -ENOMEM;
50649531192SRené Herman 
50749531192SRené Herman 	err = galaxy_init(galaxy, &type);
50849531192SRené Herman 	if (err < 0) {
50949531192SRené Herman 		dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);
51035a245ecSTakashi Iwai 		return err;
51149531192SRené Herman 	}
51249531192SRené Herman 	dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);
51349531192SRené Herman 
51435a245ecSTakashi Iwai 	galaxy->res_config_port =
51535a245ecSTakashi Iwai 		devm_request_region(dev, port[n] + GALAXY_PORT_CONFIG, 16,
51635a245ecSTakashi Iwai 				    DRV_NAME);
51749531192SRené Herman 	if (!galaxy->res_config_port) {
51849531192SRené Herman 		dev_err(dev, "could not grab ports %#lx-%#lx\n",
51949531192SRené Herman 			port[n] + GALAXY_PORT_CONFIG,
52049531192SRené Herman 			port[n] + GALAXY_PORT_CONFIG + 15);
52135a245ecSTakashi Iwai 		return -EBUSY;
52249531192SRené Herman 	}
52335a245ecSTakashi Iwai 	galaxy->config_port =
52435a245ecSTakashi Iwai 		devm_ioport_map(dev, port[n] + GALAXY_PORT_CONFIG, 16);
52535a245ecSTakashi Iwai 	if (!galaxy->config_port)
52635a245ecSTakashi Iwai 		return -ENOMEM;
52749531192SRené Herman 	galaxy_config(galaxy, config[n]);
52849531192SRené Herman 
52935a245ecSTakashi Iwai 	galaxy->res_wss_port = devm_request_region(dev, wss_port[n], 4, DRV_NAME);
53049531192SRené Herman 	if (!galaxy->res_wss_port)  {
53149531192SRené Herman 		dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],
53249531192SRené Herman 			wss_port[n] + 3);
53335a245ecSTakashi Iwai 		return -EBUSY;
53449531192SRené Herman 	}
53535a245ecSTakashi Iwai 	galaxy->wss_port = devm_ioport_map(dev, wss_port[n], 4);
53635a245ecSTakashi Iwai 	if (!galaxy->wss_port)
53735a245ecSTakashi Iwai 		return -ENOMEM;
53849531192SRené Herman 
53949531192SRené Herman 	err = galaxy_wss_config(galaxy, wss_config[n]);
54049531192SRené Herman 	if (err < 0) {
54149531192SRené Herman 		dev_err(dev, "could not configure WSS\n");
54235a245ecSTakashi Iwai 		return err;
54349531192SRené Herman 	}
54449531192SRené Herman 
545*74987a0cSTakashi Iwai 	strscpy(card->driver, DRV_NAME);
546*74987a0cSTakashi Iwai 	strscpy(card->shortname, DRV_NAME);
54749531192SRené Herman 	sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",
54849531192SRené Herman 		card->shortname, port[n], wss_port[n], irq[n], dma1[n],
54949531192SRené Herman 		dma2[n]);
55049531192SRené Herman 
55149531192SRené Herman 	err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],
55249531192SRené Herman 			     dma2[n], WSS_HW_DETECT, 0, &chip);
55349531192SRené Herman 	if (err < 0)
55435a245ecSTakashi Iwai 		return err;
55549531192SRené Herman 
556fa60c065SLars-Peter Clausen 	err = snd_wss_pcm(chip, 0);
55749531192SRené Herman 	if (err < 0)
55835a245ecSTakashi Iwai 		return err;
55949531192SRené Herman 
56049531192SRené Herman 	err = snd_wss_mixer(chip);
56149531192SRené Herman 	if (err < 0)
56235a245ecSTakashi Iwai 		return err;
56349531192SRené Herman 
564fa60c065SLars-Peter Clausen 	err = snd_wss_timer(chip, 0);
56549531192SRené Herman 	if (err < 0)
56635a245ecSTakashi Iwai 		return err;
56749531192SRené Herman 
56849531192SRené Herman 	if (mpu_port[n] >= 0) {
56949531192SRené Herman 		err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
570dba8b469SClemens Ladisch 					  mpu_port[n], 0, mpu_irq[n], NULL);
57149531192SRené Herman 		if (err < 0)
57235a245ecSTakashi Iwai 			return err;
57349531192SRené Herman 	}
57449531192SRené Herman 
57549531192SRené Herman 	if (fm_port[n] >= 0) {
57649531192SRené Herman 		struct snd_opl3 *opl3;
57749531192SRené Herman 
57849531192SRené Herman 		err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
57949531192SRené Herman 				      OPL3_HW_AUTO, 0, &opl3);
58049531192SRené Herman 		if (err < 0) {
58149531192SRené Herman 			dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);
58235a245ecSTakashi Iwai 			return err;
58349531192SRené Herman 		}
58449531192SRené Herman 		err = snd_opl3_timer_new(opl3, 1, 2);
58549531192SRené Herman 		if (err < 0)
58635a245ecSTakashi Iwai 			return err;
58749531192SRené Herman 
58849531192SRené Herman 		err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
58949531192SRené Herman 		if (err < 0)
59035a245ecSTakashi Iwai 			return err;
59149531192SRené Herman 	}
59249531192SRené Herman 
59349531192SRené Herman 	err = snd_card_register(card);
59449531192SRené Herman 	if (err < 0)
59535a245ecSTakashi Iwai 		return err;
59649531192SRené Herman 
59749531192SRené Herman 	dev_set_drvdata(dev, card);
59849531192SRené Herman 	return 0;
59949531192SRené Herman }
60049531192SRené Herman 
snd_galaxy_probe(struct device * dev,unsigned int n)60110b1881aSTakashi Iwai static int snd_galaxy_probe(struct device *dev, unsigned int n)
60210b1881aSTakashi Iwai {
60310b1881aSTakashi Iwai 	return snd_card_free_on_error(dev, __snd_galaxy_probe(dev, n));
60410b1881aSTakashi Iwai }
60510b1881aSTakashi Iwai 
60649531192SRené Herman static struct isa_driver snd_galaxy_driver = {
60749531192SRené Herman 	.match		= snd_galaxy_match,
60849531192SRené Herman 	.probe		= snd_galaxy_probe,
60949531192SRené Herman 
61049531192SRené Herman 	.driver		= {
61149531192SRené Herman 		.name	= DEV_NAME
61249531192SRené Herman 	}
61349531192SRené Herman };
61449531192SRené Herman 
615ab55c050SWilliam Breathitt Gray module_isa_driver(snd_galaxy_driver, SNDRV_CARDS);
616