xref: /OK3568_Linux_fs/kernel/sound/isa/galaxy/galaxy.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Aztech AZT1605/AZT2316 Driver
4*4882a593Smuzhiyun  * Copyright (C) 2007,2010  Rene Herman
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/kernel.h>
8*4882a593Smuzhiyun #include <linux/module.h>
9*4882a593Smuzhiyun #include <linux/isa.h>
10*4882a593Smuzhiyun #include <linux/delay.h>
11*4882a593Smuzhiyun #include <linux/io.h>
12*4882a593Smuzhiyun #include <asm/processor.h>
13*4882a593Smuzhiyun #include <sound/core.h>
14*4882a593Smuzhiyun #include <sound/initval.h>
15*4882a593Smuzhiyun #include <sound/wss.h>
16*4882a593Smuzhiyun #include <sound/mpu401.h>
17*4882a593Smuzhiyun #include <sound/opl3.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun MODULE_DESCRIPTION(CRD_NAME);
20*4882a593Smuzhiyun MODULE_AUTHOR("Rene Herman");
21*4882a593Smuzhiyun MODULE_LICENSE("GPL");
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
24*4882a593Smuzhiyun static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
25*4882a593Smuzhiyun static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun module_param_array(index, int, NULL, 0444);
28*4882a593Smuzhiyun MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
29*4882a593Smuzhiyun module_param_array(id, charp, NULL, 0444);
30*4882a593Smuzhiyun MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
31*4882a593Smuzhiyun module_param_array(enable, bool, NULL, 0444);
32*4882a593Smuzhiyun MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
35*4882a593Smuzhiyun static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
36*4882a593Smuzhiyun static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
37*4882a593Smuzhiyun static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
38*4882a593Smuzhiyun static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
39*4882a593Smuzhiyun static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
40*4882a593Smuzhiyun static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
41*4882a593Smuzhiyun static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun module_param_hw_array(port, long, ioport, NULL, 0444);
44*4882a593Smuzhiyun MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
45*4882a593Smuzhiyun module_param_hw_array(wss_port, long, ioport, NULL, 0444);
46*4882a593Smuzhiyun MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");
47*4882a593Smuzhiyun module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
48*4882a593Smuzhiyun MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
49*4882a593Smuzhiyun module_param_hw_array(fm_port, long, ioport, NULL, 0444);
50*4882a593Smuzhiyun MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");
51*4882a593Smuzhiyun module_param_hw_array(irq, int, irq, NULL, 0444);
52*4882a593Smuzhiyun MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
53*4882a593Smuzhiyun module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
54*4882a593Smuzhiyun MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
55*4882a593Smuzhiyun module_param_hw_array(dma1, int, dma, NULL, 0444);
56*4882a593Smuzhiyun MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");
57*4882a593Smuzhiyun module_param_hw_array(dma2, int, dma, NULL, 0444);
58*4882a593Smuzhiyun MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun /*
61*4882a593Smuzhiyun  * Generic SB DSP support routines
62*4882a593Smuzhiyun  */
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun #define DSP_PORT_RESET		0x6
65*4882a593Smuzhiyun #define DSP_PORT_READ		0xa
66*4882a593Smuzhiyun #define DSP_PORT_COMMAND	0xc
67*4882a593Smuzhiyun #define DSP_PORT_STATUS		0xc
68*4882a593Smuzhiyun #define DSP_PORT_DATA_AVAIL	0xe
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun #define DSP_SIGNATURE		0xaa
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun #define DSP_COMMAND_GET_VERSION	0xe1
73*4882a593Smuzhiyun 
dsp_get_byte(void __iomem * port,u8 * val)74*4882a593Smuzhiyun static int dsp_get_byte(void __iomem *port, u8 *val)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	int loops = 1000;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) {
79*4882a593Smuzhiyun 		if (!loops--)
80*4882a593Smuzhiyun 			return -EIO;
81*4882a593Smuzhiyun 		cpu_relax();
82*4882a593Smuzhiyun 	}
83*4882a593Smuzhiyun 	*val = ioread8(port + DSP_PORT_READ);
84*4882a593Smuzhiyun 	return 0;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
dsp_reset(void __iomem * port)87*4882a593Smuzhiyun static int dsp_reset(void __iomem *port)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	u8 val;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	iowrite8(1, port + DSP_PORT_RESET);
92*4882a593Smuzhiyun 	udelay(10);
93*4882a593Smuzhiyun 	iowrite8(0, port + DSP_PORT_RESET);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE)
96*4882a593Smuzhiyun 		return -ENODEV;
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	return 0;
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun 
dsp_command(void __iomem * port,u8 cmd)101*4882a593Smuzhiyun static int dsp_command(void __iomem *port, u8 cmd)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	int loops = 1000;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	while (ioread8(port + DSP_PORT_STATUS) & 0x80) {
106*4882a593Smuzhiyun 		if (!loops--)
107*4882a593Smuzhiyun 			return -EIO;
108*4882a593Smuzhiyun 		cpu_relax();
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 	iowrite8(cmd, port + DSP_PORT_COMMAND);
111*4882a593Smuzhiyun 	return 0;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
dsp_get_version(void __iomem * port,u8 * major,u8 * minor)114*4882a593Smuzhiyun static int dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	int err;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	err = dsp_command(port, DSP_COMMAND_GET_VERSION);
119*4882a593Smuzhiyun 	if (err < 0)
120*4882a593Smuzhiyun 		return err;
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	err = dsp_get_byte(port, major);
123*4882a593Smuzhiyun 	if (err < 0)
124*4882a593Smuzhiyun 		return err;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	err = dsp_get_byte(port, minor);
127*4882a593Smuzhiyun 	if (err < 0)
128*4882a593Smuzhiyun 		return err;
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	return 0;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun /*
134*4882a593Smuzhiyun  * Generic WSS support routines
135*4882a593Smuzhiyun  */
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun #define WSS_CONFIG_DMA_0	(1 << 0)
138*4882a593Smuzhiyun #define WSS_CONFIG_DMA_1	(2 << 0)
139*4882a593Smuzhiyun #define WSS_CONFIG_DMA_3	(3 << 0)
140*4882a593Smuzhiyun #define WSS_CONFIG_DUPLEX	(1 << 2)
141*4882a593Smuzhiyun #define WSS_CONFIG_IRQ_7	(1 << 3)
142*4882a593Smuzhiyun #define WSS_CONFIG_IRQ_9	(2 << 3)
143*4882a593Smuzhiyun #define WSS_CONFIG_IRQ_10	(3 << 3)
144*4882a593Smuzhiyun #define WSS_CONFIG_IRQ_11	(4 << 3)
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun #define WSS_PORT_CONFIG		0
147*4882a593Smuzhiyun #define WSS_PORT_SIGNATURE	3
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun #define WSS_SIGNATURE		4
150*4882a593Smuzhiyun 
wss_detect(void __iomem * wss_port)151*4882a593Smuzhiyun static int wss_detect(void __iomem *wss_port)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun 	if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE)
154*4882a593Smuzhiyun 		return -ENODEV;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	return 0;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun 
wss_set_config(void __iomem * wss_port,u8 wss_config)159*4882a593Smuzhiyun static void wss_set_config(void __iomem *wss_port, u8 wss_config)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	iowrite8(wss_config, wss_port + WSS_PORT_CONFIG);
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun /*
165*4882a593Smuzhiyun  * Aztech Sound Galaxy specifics
166*4882a593Smuzhiyun  */
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun #define GALAXY_PORT_CONFIG	1024
169*4882a593Smuzhiyun #define CONFIG_PORT_SET		4
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun #define DSP_COMMAND_GALAXY_8	8
172*4882a593Smuzhiyun #define GALAXY_COMMAND_GET_TYPE	5
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun #define DSP_COMMAND_GALAXY_9	9
175*4882a593Smuzhiyun #define GALAXY_COMMAND_WSSMODE	0
176*4882a593Smuzhiyun #define GALAXY_COMMAND_SB8MODE	1
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun #define GALAXY_MODE_WSS		GALAXY_COMMAND_WSSMODE
179*4882a593Smuzhiyun #define GALAXY_MODE_SB8		GALAXY_COMMAND_SB8MODE
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun struct snd_galaxy {
182*4882a593Smuzhiyun 	void __iomem *port;
183*4882a593Smuzhiyun 	void __iomem *config_port;
184*4882a593Smuzhiyun 	void __iomem *wss_port;
185*4882a593Smuzhiyun 	u32 config;
186*4882a593Smuzhiyun 	struct resource *res_port;
187*4882a593Smuzhiyun 	struct resource *res_config_port;
188*4882a593Smuzhiyun 	struct resource *res_wss_port;
189*4882a593Smuzhiyun };
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun static u32 config[SNDRV_CARDS];
192*4882a593Smuzhiyun static u8 wss_config[SNDRV_CARDS];
193*4882a593Smuzhiyun 
snd_galaxy_match(struct device * dev,unsigned int n)194*4882a593Smuzhiyun static int snd_galaxy_match(struct device *dev, unsigned int n)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun 	if (!enable[n])
197*4882a593Smuzhiyun 		return 0;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	switch (port[n]) {
200*4882a593Smuzhiyun 	case SNDRV_AUTO_PORT:
201*4882a593Smuzhiyun 		dev_err(dev, "please specify port\n");
202*4882a593Smuzhiyun 		return 0;
203*4882a593Smuzhiyun 	case 0x220:
204*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_SBA_220;
205*4882a593Smuzhiyun 		break;
206*4882a593Smuzhiyun 	case 0x240:
207*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_SBA_240;
208*4882a593Smuzhiyun 		break;
209*4882a593Smuzhiyun 	case 0x260:
210*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_SBA_260;
211*4882a593Smuzhiyun 		break;
212*4882a593Smuzhiyun 	case 0x280:
213*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_SBA_280;
214*4882a593Smuzhiyun 		break;
215*4882a593Smuzhiyun 	default:
216*4882a593Smuzhiyun 		dev_err(dev, "invalid port %#lx\n", port[n]);
217*4882a593Smuzhiyun 		return 0;
218*4882a593Smuzhiyun 	}
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	switch (wss_port[n]) {
221*4882a593Smuzhiyun 	case SNDRV_AUTO_PORT:
222*4882a593Smuzhiyun 		dev_err(dev,  "please specify wss_port\n");
223*4882a593Smuzhiyun 		return 0;
224*4882a593Smuzhiyun 	case 0x530:
225*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530;
226*4882a593Smuzhiyun 		break;
227*4882a593Smuzhiyun 	case 0x604:
228*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604;
229*4882a593Smuzhiyun 		break;
230*4882a593Smuzhiyun 	case 0xe80:
231*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80;
232*4882a593Smuzhiyun 		break;
233*4882a593Smuzhiyun 	case 0xf40:
234*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40;
235*4882a593Smuzhiyun 		break;
236*4882a593Smuzhiyun 	default:
237*4882a593Smuzhiyun 		dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]);
238*4882a593Smuzhiyun 		return 0;
239*4882a593Smuzhiyun 	}
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	switch (irq[n]) {
242*4882a593Smuzhiyun 	case SNDRV_AUTO_IRQ:
243*4882a593Smuzhiyun 		dev_err(dev,  "please specify irq\n");
244*4882a593Smuzhiyun 		return 0;
245*4882a593Smuzhiyun 	case 7:
246*4882a593Smuzhiyun 		wss_config[n] |= WSS_CONFIG_IRQ_7;
247*4882a593Smuzhiyun 		break;
248*4882a593Smuzhiyun 	case 2:
249*4882a593Smuzhiyun 		irq[n] = 9;
250*4882a593Smuzhiyun 		fallthrough;
251*4882a593Smuzhiyun 	case 9:
252*4882a593Smuzhiyun 		wss_config[n] |= WSS_CONFIG_IRQ_9;
253*4882a593Smuzhiyun 		break;
254*4882a593Smuzhiyun 	case 10:
255*4882a593Smuzhiyun 		wss_config[n] |= WSS_CONFIG_IRQ_10;
256*4882a593Smuzhiyun 		break;
257*4882a593Smuzhiyun 	case 11:
258*4882a593Smuzhiyun 		wss_config[n] |= WSS_CONFIG_IRQ_11;
259*4882a593Smuzhiyun 		break;
260*4882a593Smuzhiyun 	default:
261*4882a593Smuzhiyun 		dev_err(dev, "invalid IRQ %d\n", irq[n]);
262*4882a593Smuzhiyun 		return 0;
263*4882a593Smuzhiyun 	}
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	switch (dma1[n]) {
266*4882a593Smuzhiyun 	case SNDRV_AUTO_DMA:
267*4882a593Smuzhiyun 		dev_err(dev,  "please specify dma1\n");
268*4882a593Smuzhiyun 		return 0;
269*4882a593Smuzhiyun 	case 0:
270*4882a593Smuzhiyun 		wss_config[n] |= WSS_CONFIG_DMA_0;
271*4882a593Smuzhiyun 		break;
272*4882a593Smuzhiyun 	case 1:
273*4882a593Smuzhiyun 		wss_config[n] |= WSS_CONFIG_DMA_1;
274*4882a593Smuzhiyun 		break;
275*4882a593Smuzhiyun 	case 3:
276*4882a593Smuzhiyun 		wss_config[n] |= WSS_CONFIG_DMA_3;
277*4882a593Smuzhiyun 		break;
278*4882a593Smuzhiyun 	default:
279*4882a593Smuzhiyun 		dev_err(dev, "invalid playback DMA %d\n", dma1[n]);
280*4882a593Smuzhiyun 		return 0;
281*4882a593Smuzhiyun 	}
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) {
284*4882a593Smuzhiyun 		dma2[n] = -1;
285*4882a593Smuzhiyun 		goto mpu;
286*4882a593Smuzhiyun 	}
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	wss_config[n] |= WSS_CONFIG_DUPLEX;
289*4882a593Smuzhiyun 	switch (dma2[n]) {
290*4882a593Smuzhiyun 	case 0:
291*4882a593Smuzhiyun 		break;
292*4882a593Smuzhiyun 	case 1:
293*4882a593Smuzhiyun 		if (dma1[n] == 0)
294*4882a593Smuzhiyun 			break;
295*4882a593Smuzhiyun 		fallthrough;
296*4882a593Smuzhiyun 	default:
297*4882a593Smuzhiyun 		dev_err(dev, "invalid capture DMA %d\n", dma2[n]);
298*4882a593Smuzhiyun 		return 0;
299*4882a593Smuzhiyun 	}
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun mpu:
302*4882a593Smuzhiyun 	switch (mpu_port[n]) {
303*4882a593Smuzhiyun 	case SNDRV_AUTO_PORT:
304*4882a593Smuzhiyun 		dev_warn(dev, "mpu_port not specified; not using MPU-401\n");
305*4882a593Smuzhiyun 		mpu_port[n] = -1;
306*4882a593Smuzhiyun 		goto fm;
307*4882a593Smuzhiyun 	case 0x300:
308*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300;
309*4882a593Smuzhiyun 		break;
310*4882a593Smuzhiyun 	case 0x330:
311*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330;
312*4882a593Smuzhiyun 		break;
313*4882a593Smuzhiyun 	default:
314*4882a593Smuzhiyun 		dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]);
315*4882a593Smuzhiyun 		return 0;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	switch (mpu_irq[n]) {
319*4882a593Smuzhiyun 	case SNDRV_AUTO_IRQ:
320*4882a593Smuzhiyun 		dev_warn(dev, "mpu_irq not specified: using polling mode\n");
321*4882a593Smuzhiyun 		mpu_irq[n] = -1;
322*4882a593Smuzhiyun 		break;
323*4882a593Smuzhiyun 	case 2:
324*4882a593Smuzhiyun 		mpu_irq[n] = 9;
325*4882a593Smuzhiyun 		fallthrough;
326*4882a593Smuzhiyun 	case 9:
327*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_MPUIRQ_2;
328*4882a593Smuzhiyun 		break;
329*4882a593Smuzhiyun #ifdef AZT1605
330*4882a593Smuzhiyun 	case 3:
331*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_MPUIRQ_3;
332*4882a593Smuzhiyun 		break;
333*4882a593Smuzhiyun #endif
334*4882a593Smuzhiyun 	case 5:
335*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_MPUIRQ_5;
336*4882a593Smuzhiyun 		break;
337*4882a593Smuzhiyun 	case 7:
338*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_MPUIRQ_7;
339*4882a593Smuzhiyun 		break;
340*4882a593Smuzhiyun #ifdef AZT2316
341*4882a593Smuzhiyun 	case 10:
342*4882a593Smuzhiyun 		config[n] |= GALAXY_CONFIG_MPUIRQ_10;
343*4882a593Smuzhiyun 		break;
344*4882a593Smuzhiyun #endif
345*4882a593Smuzhiyun 	default:
346*4882a593Smuzhiyun 		dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]);
347*4882a593Smuzhiyun 		return 0;
348*4882a593Smuzhiyun 	}
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	if (mpu_irq[n] == irq[n]) {
351*4882a593Smuzhiyun 		dev_err(dev, "cannot share IRQ between WSS and MPU-401\n");
352*4882a593Smuzhiyun 		return 0;
353*4882a593Smuzhiyun 	}
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun fm:
356*4882a593Smuzhiyun 	switch (fm_port[n]) {
357*4882a593Smuzhiyun 	case SNDRV_AUTO_PORT:
358*4882a593Smuzhiyun 		dev_warn(dev, "fm_port not specified: not using OPL3\n");
359*4882a593Smuzhiyun 		fm_port[n] = -1;
360*4882a593Smuzhiyun 		break;
361*4882a593Smuzhiyun 	case 0x388:
362*4882a593Smuzhiyun 		break;
363*4882a593Smuzhiyun 	default:
364*4882a593Smuzhiyun 		dev_err(dev, "illegal FM port %#lx\n", fm_port[n]);
365*4882a593Smuzhiyun 		return 0;
366*4882a593Smuzhiyun 	}
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	config[n] |= GALAXY_CONFIG_GAME_ENABLE;
369*4882a593Smuzhiyun 	return 1;
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun 
galaxy_init(struct snd_galaxy * galaxy,u8 * type)372*4882a593Smuzhiyun static int galaxy_init(struct snd_galaxy *galaxy, u8 *type)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 	u8 major;
375*4882a593Smuzhiyun 	u8 minor;
376*4882a593Smuzhiyun 	int err;
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	err = dsp_reset(galaxy->port);
379*4882a593Smuzhiyun 	if (err < 0)
380*4882a593Smuzhiyun 		return err;
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	err = dsp_get_version(galaxy->port, &major, &minor);
383*4882a593Smuzhiyun 	if (err < 0)
384*4882a593Smuzhiyun 		return err;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR)
387*4882a593Smuzhiyun 		return -ENODEV;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8);
390*4882a593Smuzhiyun 	if (err < 0)
391*4882a593Smuzhiyun 		return err;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE);
394*4882a593Smuzhiyun 	if (err < 0)
395*4882a593Smuzhiyun 		return err;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	err = dsp_get_byte(galaxy->port, type);
398*4882a593Smuzhiyun 	if (err < 0)
399*4882a593Smuzhiyun 		return err;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	return 0;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun 
galaxy_set_mode(struct snd_galaxy * galaxy,u8 mode)404*4882a593Smuzhiyun static int galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
405*4882a593Smuzhiyun {
406*4882a593Smuzhiyun 	int err;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9);
409*4882a593Smuzhiyun 	if (err < 0)
410*4882a593Smuzhiyun 		return err;
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	err = dsp_command(galaxy->port, mode);
413*4882a593Smuzhiyun 	if (err < 0)
414*4882a593Smuzhiyun 		return err;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun #ifdef AZT1605
417*4882a593Smuzhiyun 	/*
418*4882a593Smuzhiyun 	 * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again
419*4882a593Smuzhiyun 	 */
420*4882a593Smuzhiyun 	err = dsp_reset(galaxy->port);
421*4882a593Smuzhiyun 	if (err < 0)
422*4882a593Smuzhiyun 		return err;
423*4882a593Smuzhiyun #endif
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	return 0;
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun 
galaxy_set_config(struct snd_galaxy * galaxy,u32 config)428*4882a593Smuzhiyun static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun 	u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET);
431*4882a593Smuzhiyun 	int i;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET);
434*4882a593Smuzhiyun 	for (i = 0; i < GALAXY_CONFIG_SIZE; i++) {
435*4882a593Smuzhiyun 		iowrite8(config, galaxy->config_port + i);
436*4882a593Smuzhiyun 		config >>= 8;
437*4882a593Smuzhiyun 	}
438*4882a593Smuzhiyun 	iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET);
439*4882a593Smuzhiyun 	msleep(10);
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun 
galaxy_config(struct snd_galaxy * galaxy,u32 config)442*4882a593Smuzhiyun static void galaxy_config(struct snd_galaxy *galaxy, u32 config)
443*4882a593Smuzhiyun {
444*4882a593Smuzhiyun 	int i;
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	for (i = GALAXY_CONFIG_SIZE; i; i--) {
447*4882a593Smuzhiyun 		u8 tmp = ioread8(galaxy->config_port + i - 1);
448*4882a593Smuzhiyun 		galaxy->config = (galaxy->config << 8) | tmp;
449*4882a593Smuzhiyun 	}
450*4882a593Smuzhiyun 	config |= galaxy->config & GALAXY_CONFIG_MASK;
451*4882a593Smuzhiyun 	galaxy_set_config(galaxy, config);
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun 
galaxy_wss_config(struct snd_galaxy * galaxy,u8 wss_config)454*4882a593Smuzhiyun static int galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun 	int err;
457*4882a593Smuzhiyun 
458*4882a593Smuzhiyun 	err = wss_detect(galaxy->wss_port);
459*4882a593Smuzhiyun 	if (err < 0)
460*4882a593Smuzhiyun 		return err;
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	wss_set_config(galaxy->wss_port, wss_config);
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS);
465*4882a593Smuzhiyun 	if (err < 0)
466*4882a593Smuzhiyun 		return err;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	return 0;
469*4882a593Smuzhiyun }
470*4882a593Smuzhiyun 
snd_galaxy_free(struct snd_card * card)471*4882a593Smuzhiyun static void snd_galaxy_free(struct snd_card *card)
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun 	struct snd_galaxy *galaxy = card->private_data;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	if (galaxy->wss_port) {
476*4882a593Smuzhiyun 		wss_set_config(galaxy->wss_port, 0);
477*4882a593Smuzhiyun 		ioport_unmap(galaxy->wss_port);
478*4882a593Smuzhiyun 		release_and_free_resource(galaxy->res_wss_port);
479*4882a593Smuzhiyun 	}
480*4882a593Smuzhiyun 	if (galaxy->config_port) {
481*4882a593Smuzhiyun 		galaxy_set_config(galaxy, galaxy->config);
482*4882a593Smuzhiyun 		ioport_unmap(galaxy->config_port);
483*4882a593Smuzhiyun 		release_and_free_resource(galaxy->res_config_port);
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun 	if (galaxy->port) {
486*4882a593Smuzhiyun 		ioport_unmap(galaxy->port);
487*4882a593Smuzhiyun 		release_and_free_resource(galaxy->res_port);
488*4882a593Smuzhiyun 	}
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun 
snd_galaxy_probe(struct device * dev,unsigned int n)491*4882a593Smuzhiyun static int snd_galaxy_probe(struct device *dev, unsigned int n)
492*4882a593Smuzhiyun {
493*4882a593Smuzhiyun 	struct snd_galaxy *galaxy;
494*4882a593Smuzhiyun 	struct snd_wss *chip;
495*4882a593Smuzhiyun 	struct snd_card *card;
496*4882a593Smuzhiyun 	u8 type;
497*4882a593Smuzhiyun 	int err;
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	err = snd_card_new(dev, index[n], id[n], THIS_MODULE,
500*4882a593Smuzhiyun 			   sizeof(*galaxy), &card);
501*4882a593Smuzhiyun 	if (err < 0)
502*4882a593Smuzhiyun 		return err;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	card->private_free = snd_galaxy_free;
505*4882a593Smuzhiyun 	galaxy = card->private_data;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	galaxy->res_port = request_region(port[n], 16, DRV_NAME);
508*4882a593Smuzhiyun 	if (!galaxy->res_port) {
509*4882a593Smuzhiyun 		dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],
510*4882a593Smuzhiyun 			port[n] + 15);
511*4882a593Smuzhiyun 		err = -EBUSY;
512*4882a593Smuzhiyun 		goto error;
513*4882a593Smuzhiyun 	}
514*4882a593Smuzhiyun 	galaxy->port = ioport_map(port[n], 16);
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	err = galaxy_init(galaxy, &type);
517*4882a593Smuzhiyun 	if (err < 0) {
518*4882a593Smuzhiyun 		dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);
519*4882a593Smuzhiyun 		goto error;
520*4882a593Smuzhiyun 	}
521*4882a593Smuzhiyun 	dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG,
524*4882a593Smuzhiyun 						 16, DRV_NAME);
525*4882a593Smuzhiyun 	if (!galaxy->res_config_port) {
526*4882a593Smuzhiyun 		dev_err(dev, "could not grab ports %#lx-%#lx\n",
527*4882a593Smuzhiyun 			port[n] + GALAXY_PORT_CONFIG,
528*4882a593Smuzhiyun 			port[n] + GALAXY_PORT_CONFIG + 15);
529*4882a593Smuzhiyun 		err = -EBUSY;
530*4882a593Smuzhiyun 		goto error;
531*4882a593Smuzhiyun 	}
532*4882a593Smuzhiyun 	galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16);
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun 	galaxy_config(galaxy, config[n]);
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun 	galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME);
537*4882a593Smuzhiyun 	if (!galaxy->res_wss_port)  {
538*4882a593Smuzhiyun 		dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],
539*4882a593Smuzhiyun 			wss_port[n] + 3);
540*4882a593Smuzhiyun 		err = -EBUSY;
541*4882a593Smuzhiyun 		goto error;
542*4882a593Smuzhiyun 	}
543*4882a593Smuzhiyun 	galaxy->wss_port = ioport_map(wss_port[n], 4);
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun 	err = galaxy_wss_config(galaxy, wss_config[n]);
546*4882a593Smuzhiyun 	if (err < 0) {
547*4882a593Smuzhiyun 		dev_err(dev, "could not configure WSS\n");
548*4882a593Smuzhiyun 		goto error;
549*4882a593Smuzhiyun 	}
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	strcpy(card->driver, DRV_NAME);
552*4882a593Smuzhiyun 	strcpy(card->shortname, DRV_NAME);
553*4882a593Smuzhiyun 	sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",
554*4882a593Smuzhiyun 		card->shortname, port[n], wss_port[n], irq[n], dma1[n],
555*4882a593Smuzhiyun 		dma2[n]);
556*4882a593Smuzhiyun 
557*4882a593Smuzhiyun 	err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],
558*4882a593Smuzhiyun 			     dma2[n], WSS_HW_DETECT, 0, &chip);
559*4882a593Smuzhiyun 	if (err < 0)
560*4882a593Smuzhiyun 		goto error;
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	err = snd_wss_pcm(chip, 0);
563*4882a593Smuzhiyun 	if (err < 0)
564*4882a593Smuzhiyun 		goto error;
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	err = snd_wss_mixer(chip);
567*4882a593Smuzhiyun 	if (err < 0)
568*4882a593Smuzhiyun 		goto error;
569*4882a593Smuzhiyun 
570*4882a593Smuzhiyun 	err = snd_wss_timer(chip, 0);
571*4882a593Smuzhiyun 	if (err < 0)
572*4882a593Smuzhiyun 		goto error;
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 	if (mpu_port[n] >= 0) {
575*4882a593Smuzhiyun 		err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
576*4882a593Smuzhiyun 					  mpu_port[n], 0, mpu_irq[n], NULL);
577*4882a593Smuzhiyun 		if (err < 0)
578*4882a593Smuzhiyun 			goto error;
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	if (fm_port[n] >= 0) {
582*4882a593Smuzhiyun 		struct snd_opl3 *opl3;
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 		err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
585*4882a593Smuzhiyun 				      OPL3_HW_AUTO, 0, &opl3);
586*4882a593Smuzhiyun 		if (err < 0) {
587*4882a593Smuzhiyun 			dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);
588*4882a593Smuzhiyun 			goto error;
589*4882a593Smuzhiyun 		}
590*4882a593Smuzhiyun 		err = snd_opl3_timer_new(opl3, 1, 2);
591*4882a593Smuzhiyun 		if (err < 0)
592*4882a593Smuzhiyun 			goto error;
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 		err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
595*4882a593Smuzhiyun 		if (err < 0)
596*4882a593Smuzhiyun 			goto error;
597*4882a593Smuzhiyun 	}
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 	err = snd_card_register(card);
600*4882a593Smuzhiyun 	if (err < 0)
601*4882a593Smuzhiyun 		goto error;
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	dev_set_drvdata(dev, card);
604*4882a593Smuzhiyun 	return 0;
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun error:
607*4882a593Smuzhiyun 	snd_card_free(card);
608*4882a593Smuzhiyun 	return err;
609*4882a593Smuzhiyun }
610*4882a593Smuzhiyun 
snd_galaxy_remove(struct device * dev,unsigned int n)611*4882a593Smuzhiyun static int snd_galaxy_remove(struct device *dev, unsigned int n)
612*4882a593Smuzhiyun {
613*4882a593Smuzhiyun 	snd_card_free(dev_get_drvdata(dev));
614*4882a593Smuzhiyun 	return 0;
615*4882a593Smuzhiyun }
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun static struct isa_driver snd_galaxy_driver = {
618*4882a593Smuzhiyun 	.match		= snd_galaxy_match,
619*4882a593Smuzhiyun 	.probe		= snd_galaxy_probe,
620*4882a593Smuzhiyun 	.remove		= snd_galaxy_remove,
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	.driver		= {
623*4882a593Smuzhiyun 		.name	= DEV_NAME
624*4882a593Smuzhiyun 	}
625*4882a593Smuzhiyun };
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun module_isa_driver(snd_galaxy_driver, SNDRV_CARDS);
628