xref: /openbmc/linux/sound/isa/azt2320.c (revision d5cb9783536a41df9f9cba5b0a1d78047ed787f7)
1 /*
2     card-azt2320.c - driver for Aztech Systems AZT2320 based soundcards.
3     Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 */
19 
20 /*
21     This driver should provide support for most Aztech AZT2320 based cards.
22     Several AZT2316 chips are also supported/tested, but autoprobe doesn't
23     work: all module option have to be set.
24 
25     No docs available for us at Aztech headquarters !!!   Unbelievable ...
26     No other help obtained.
27 
28     Thanks to Rainer Wiesner <rainer.wiesner@01019freenet.de> for the WSS
29     activation method (full-duplex audio!).
30 */
31 
32 #include <sound/driver.h>
33 #include <asm/io.h>
34 #include <linux/delay.h>
35 #include <linux/init.h>
36 #include <linux/time.h>
37 #include <linux/wait.h>
38 #include <linux/pnp.h>
39 #include <linux/moduleparam.h>
40 #include <sound/core.h>
41 #include <sound/initval.h>
42 #include <sound/cs4231.h>
43 #include <sound/mpu401.h>
44 #include <sound/opl3.h>
45 
46 #define PFX "azt2320: "
47 
48 MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
49 MODULE_DESCRIPTION("Aztech Systems AZT2320");
50 MODULE_LICENSE("GPL");
51 MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V},"
52 		"{Aztech Systems,AZT2320},"
53 		"{Aztech Systems,AZT3300},"
54 		"{Aztech Systems,AZT2320},"
55 		"{Aztech Systems,AZT3000}}");
56 
57 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
58 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
59 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
60 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
61 static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
62 static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
63 static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* PnP setup */
64 static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
65 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* Pnp setup */
66 static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
67 static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* PnP setup */
68 
69 module_param_array(index, int, NULL, 0444);
70 MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard.");
71 module_param_array(id, charp, NULL, 0444);
72 MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
73 module_param_array(enable, bool, NULL, 0444);
74 MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
75 module_param_array(port, long, NULL, 0444);
76 MODULE_PARM_DESC(port, "Port # for azt2320 driver.");
77 module_param_array(wss_port, long, NULL, 0444);
78 MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver.");
79 module_param_array(mpu_port, long, NULL, 0444);
80 MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver.");
81 module_param_array(fm_port, long, NULL, 0444);
82 MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver.");
83 module_param_array(irq, int, NULL, 0444);
84 MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver.");
85 module_param_array(mpu_irq, int, NULL, 0444);
86 MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver.");
87 module_param_array(dma1, int, NULL, 0444);
88 MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver.");
89 module_param_array(dma2, int, NULL, 0444);
90 MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver.");
91 
92 struct snd_card_azt2320 {
93 	int dev_no;
94 	struct pnp_dev *dev;
95 	struct pnp_dev *devmpu;
96 };
97 
98 static struct pnp_card_device_id snd_azt2320_pnpids[] = {
99 	/* PRO16V */
100 	{ .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
101 	/* Aztech Sound Galaxy 16 */
102 	{ .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },
103 	/* Packard Bell Sound III 336 AM/SP */
104 	{ .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },
105 	/* AT3300 */
106 	{ .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },
107 	/* --- */
108 	{ .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },
109 	/* --- */
110 	{ .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },
111 	{ .id = "" }	/* end */
112 };
113 
114 MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
115 
116 #define	DRIVER_NAME	"snd-card-azt2320"
117 
118 static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
119 					  struct pnp_card_link *card,
120 					  const struct pnp_card_device_id *id)
121 {
122 	struct pnp_dev *pdev;
123 	struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
124 	int err;
125 
126 	if (!cfg)
127 		return -ENOMEM;
128 
129 	acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
130 	if (acard->dev == NULL) {
131 		kfree(cfg);
132 		return -ENODEV;
133 	}
134 
135 	acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
136 
137 	pdev = acard->dev;
138 	pnp_init_resource_table(cfg);
139 
140 	/* override resources */
141 	if (port[dev] != SNDRV_AUTO_PORT)
142 		pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
143 	if (fm_port[dev] != SNDRV_AUTO_PORT)
144 		pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
145 	if (wss_port[dev] != SNDRV_AUTO_PORT)
146 		pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4);
147 	if (dma1[dev] != SNDRV_AUTO_DMA)
148 		pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
149 	if (dma2[dev] != SNDRV_AUTO_DMA)
150 		pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
151 	if (irq[dev] != SNDRV_AUTO_IRQ)
152 		pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
153 	if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
154 		snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
155 
156 	err = pnp_activate_dev(pdev);
157 	if (err < 0) {
158 		snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
159 		kfree(cfg);
160 		return err;
161 	}
162 	port[dev] = pnp_port_start(pdev, 0);
163 	fm_port[dev] = pnp_port_start(pdev, 1);
164 	wss_port[dev] = pnp_port_start(pdev, 2);
165 	dma1[dev] = pnp_dma(pdev, 0);
166 	dma2[dev] = pnp_dma(pdev, 1);
167 	irq[dev] = pnp_irq(pdev, 0);
168 
169 	pdev = acard->devmpu;
170 	if (pdev != NULL) {
171 		pnp_init_resource_table(cfg);
172 		if (mpu_port[dev] != SNDRV_AUTO_PORT)
173 			pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
174 		if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
175 			pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
176 		if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
177 			snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n");
178 		err = pnp_activate_dev(pdev);
179 		if (err < 0)
180 			goto __mpu_error;
181 		mpu_port[dev] = pnp_port_start(pdev, 0);
182 		mpu_irq[dev] = pnp_irq(pdev, 0);
183 	} else {
184 	     __mpu_error:
185 	     	if (pdev) {
186 		     	pnp_release_card_device(pdev);
187 	     		snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
188 	     	}
189 	     	acard->devmpu = NULL;
190 	     	mpu_port[dev] = -1;
191 	}
192 
193 	kfree (cfg);
194 	return 0;
195 }
196 
197 /* same of snd_sbdsp_command by Jaroslav Kysela */
198 static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val)
199 {
200 	int i;
201 	unsigned long limit;
202 
203 	limit = jiffies + HZ / 10;
204 	for (i = 50000; i && time_after(limit, jiffies); i--)
205 		if (!(inb(port + 0x0c) & 0x80)) {
206 			outb(val, port + 0x0c);
207 			return 0;
208 		}
209 	return -EBUSY;
210 }
211 
212 static int __devinit snd_card_azt2320_enable_wss(unsigned long port)
213 {
214 	int error;
215 
216 	if ((error = snd_card_azt2320_command(port, 0x09)))
217 		return error;
218 	if ((error = snd_card_azt2320_command(port, 0x00)))
219 		return error;
220 
221 	mdelay(5);
222 	return 0;
223 }
224 
225 static int __devinit snd_card_azt2320_probe(int dev,
226 					    struct pnp_card_link *pcard,
227 					    const struct pnp_card_device_id *pid)
228 {
229 	int error;
230 	snd_card_t *card;
231 	struct snd_card_azt2320 *acard;
232 	cs4231_t *chip;
233 	opl3_t *opl3;
234 
235 	if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
236 				 sizeof(struct snd_card_azt2320))) == NULL)
237 		return -ENOMEM;
238 	acard = (struct snd_card_azt2320 *)card->private_data;
239 
240 	if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
241 		snd_card_free(card);
242 		return error;
243 	}
244 	snd_card_set_dev(card, &pcard->card->dev);
245 
246 	if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
247 		snd_card_free(card);
248 		return error;
249 	}
250 
251 	if ((error = snd_cs4231_create(card, wss_port[dev], -1,
252 				       irq[dev],
253 				       dma1[dev],
254 				       dma2[dev],
255 				       CS4231_HW_DETECT, 0, &chip)) < 0) {
256 		snd_card_free(card);
257 		return error;
258 	}
259 
260 	strcpy(card->driver, "AZT2320");
261 	strcpy(card->shortname, "Aztech AZT2320");
262 	sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
263 		card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
264 
265 	if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
266 		snd_card_free(card);
267 		return error;
268 	}
269 	if ((error = snd_cs4231_mixer(chip)) < 0) {
270 		snd_card_free(card);
271 		return error;
272 	}
273 	if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
274 		snd_card_free(card);
275 		return error;
276 	}
277 
278 	if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
279 		if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
280 				mpu_port[dev], 0,
281 				mpu_irq[dev], SA_INTERRUPT,
282 				NULL) < 0)
283 			snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
284 	}
285 
286 	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
287 		if (snd_opl3_create(card,
288 				    fm_port[dev], fm_port[dev] + 2,
289 				    OPL3_HW_AUTO, 0, &opl3) < 0) {
290 			snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
291 				   fm_port[dev], fm_port[dev] + 2);
292 		} else {
293 			if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
294 				snd_card_free(card);
295 				return error;
296 			}
297 			if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
298 				snd_card_free(card);
299 				return error;
300 			}
301 		}
302 	}
303 
304 	if ((error = snd_card_register(card)) < 0) {
305 		snd_card_free(card);
306 		return error;
307 	}
308 	pnp_set_card_drvdata(pcard, card);
309 	return 0;
310 }
311 
312 static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
313 					    const struct pnp_card_device_id *id)
314 {
315 	static int dev;
316 	int res;
317 
318 	for ( ; dev < SNDRV_CARDS; dev++) {
319 		if (!enable[dev])
320 			continue;
321 		res = snd_card_azt2320_probe(dev, card, id);
322 		if (res < 0)
323 			return res;
324 		dev++;
325 		return 0;
326 	}
327         return -ENODEV;
328 }
329 
330 static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
331 {
332 	snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard);
333 
334 	snd_card_disconnect(card);
335 	snd_card_free_in_thread(card);
336 }
337 
338 static struct pnp_card_driver azt2320_pnpc_driver = {
339 	.flags          = PNP_DRIVER_RES_DISABLE,
340 	.name           = "azt2320",
341 	.id_table       = snd_azt2320_pnpids,
342 	.probe          = snd_azt2320_pnp_detect,
343 	.remove         = __devexit_p(snd_azt2320_pnp_remove),
344 };
345 
346 static int __init alsa_card_azt2320_init(void)
347 {
348 	int cards = 0;
349 
350 	cards += pnp_register_card_driver(&azt2320_pnpc_driver);
351 #ifdef MODULE
352 	if (!cards) {
353 		pnp_unregister_card_driver(&azt2320_pnpc_driver);
354 		snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
355 	}
356 #endif
357 	return cards ? 0 : -ENODEV;
358 }
359 
360 static void __exit alsa_card_azt2320_exit(void)
361 {
362 	pnp_unregister_card_driver(&azt2320_pnpc_driver);
363 }
364 
365 module_init(alsa_card_azt2320_init)
366 module_exit(alsa_card_azt2320_exit)
367