blob: a530691bf4f7232acb9e5fb6c0a2e12953591ab3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
48MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
49MODULE_DESCRIPTION("Aztech Systems AZT2320");
50MODULE_LICENSE("GPL");
51MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V},"
52 "{Aztech Systems,AZT2320},"
53 "{Aztech Systems,AZT3300},"
54 "{Aztech Systems,AZT2320},"
55 "{Aztech Systems,AZT3000}}");
56
57static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
58static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
59static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
60static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
61static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
62static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
63static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
64static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */
65static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */
66static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */
67static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */
68
69module_param_array(index, int, NULL, 0444);
70MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard.");
71module_param_array(id, charp, NULL, 0444);
72MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
73module_param_array(enable, bool, NULL, 0444);
74MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
75module_param_array(port, long, NULL, 0444);
76MODULE_PARM_DESC(port, "Port # for azt2320 driver.");
77module_param_array(wss_port, long, NULL, 0444);
78MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver.");
79module_param_array(mpu_port, long, NULL, 0444);
80MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver.");
81module_param_array(fm_port, long, NULL, 0444);
82MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver.");
83module_param_array(irq, int, NULL, 0444);
84MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver.");
85module_param_array(mpu_irq, int, NULL, 0444);
86MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver.");
87module_param_array(dma1, int, NULL, 0444);
88MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver.");
89module_param_array(dma2, int, NULL, 0444);
90MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver.");
91
92struct snd_card_azt2320 {
93 int dev_no;
94 struct pnp_dev *dev;
95 struct pnp_dev *devmpu;
Takashi Iwaib6cc25c2005-11-17 17:04:12 +010096 struct snd_cs4231 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097};
98
99static struct pnp_card_device_id snd_azt2320_pnpids[] = {
100 /* PRO16V */
101 { .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
102 /* Aztech Sound Galaxy 16 */
103 { .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },
104 /* Packard Bell Sound III 336 AM/SP */
105 { .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },
106 /* AT3300 */
107 { .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },
108 /* --- */
109 { .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },
110 /* --- */
111 { .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },
112 { .id = "" } /* end */
113};
114
115MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
116
117#define DRIVER_NAME "snd-card-azt2320"
118
119static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
120 struct pnp_card_link *card,
121 const struct pnp_card_device_id *id)
122{
123 struct pnp_dev *pdev;
124 struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
125 int err;
126
127 if (!cfg)
128 return -ENOMEM;
129
130 acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
131 if (acard->dev == NULL) {
132 kfree(cfg);
133 return -ENODEV;
134 }
135
136 acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
137
138 pdev = acard->dev;
139 pnp_init_resource_table(cfg);
140
141 /* override resources */
142 if (port[dev] != SNDRV_AUTO_PORT)
143 pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
144 if (fm_port[dev] != SNDRV_AUTO_PORT)
145 pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
146 if (wss_port[dev] != SNDRV_AUTO_PORT)
147 pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4);
148 if (dma1[dev] != SNDRV_AUTO_DMA)
149 pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
150 if (dma2[dev] != SNDRV_AUTO_DMA)
151 pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
152 if (irq[dev] != SNDRV_AUTO_IRQ)
153 pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
154 if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
155 snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
156
157 err = pnp_activate_dev(pdev);
158 if (err < 0) {
159 snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
160 kfree(cfg);
161 return err;
162 }
163 port[dev] = pnp_port_start(pdev, 0);
164 fm_port[dev] = pnp_port_start(pdev, 1);
165 wss_port[dev] = pnp_port_start(pdev, 2);
166 dma1[dev] = pnp_dma(pdev, 0);
167 dma2[dev] = pnp_dma(pdev, 1);
168 irq[dev] = pnp_irq(pdev, 0);
169
170 pdev = acard->devmpu;
171 if (pdev != NULL) {
172 pnp_init_resource_table(cfg);
173 if (mpu_port[dev] != SNDRV_AUTO_PORT)
174 pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
175 if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
176 pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
177 if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
178 snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n");
179 err = pnp_activate_dev(pdev);
180 if (err < 0)
181 goto __mpu_error;
182 mpu_port[dev] = pnp_port_start(pdev, 0);
183 mpu_irq[dev] = pnp_irq(pdev, 0);
184 } else {
185 __mpu_error:
186 if (pdev) {
187 pnp_release_card_device(pdev);
188 snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
189 }
190 acard->devmpu = NULL;
191 mpu_port[dev] = -1;
192 }
193
194 kfree (cfg);
195 return 0;
196}
197
198/* same of snd_sbdsp_command by Jaroslav Kysela */
199static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val)
200{
201 int i;
202 unsigned long limit;
203
204 limit = jiffies + HZ / 10;
205 for (i = 50000; i && time_after(limit, jiffies); i--)
206 if (!(inb(port + 0x0c) & 0x80)) {
207 outb(val, port + 0x0c);
208 return 0;
209 }
210 return -EBUSY;
211}
212
213static int __devinit snd_card_azt2320_enable_wss(unsigned long port)
214{
215 int error;
216
217 if ((error = snd_card_azt2320_command(port, 0x09)))
218 return error;
219 if ((error = snd_card_azt2320_command(port, 0x00)))
220 return error;
221
222 mdelay(5);
223 return 0;
224}
225
226static int __devinit snd_card_azt2320_probe(int dev,
227 struct pnp_card_link *pcard,
228 const struct pnp_card_device_id *pid)
229{
230 int error;
Takashi Iwai11ff5c62005-11-17 14:42:36 +0100231 struct snd_card *card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 struct snd_card_azt2320 *acard;
Takashi Iwai11ff5c62005-11-17 14:42:36 +0100233 struct snd_cs4231 *chip;
234 struct snd_opl3 *opl3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
236 if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
237 sizeof(struct snd_card_azt2320))) == NULL)
238 return -ENOMEM;
239 acard = (struct snd_card_azt2320 *)card->private_data;
240
241 if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
242 snd_card_free(card);
243 return error;
244 }
245 snd_card_set_dev(card, &pcard->card->dev);
246
247 if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
248 snd_card_free(card);
249 return error;
250 }
251
252 if ((error = snd_cs4231_create(card, wss_port[dev], -1,
253 irq[dev],
254 dma1[dev],
255 dma2[dev],
256 CS4231_HW_DETECT, 0, &chip)) < 0) {
257 snd_card_free(card);
258 return error;
259 }
260
261 strcpy(card->driver, "AZT2320");
262 strcpy(card->shortname, "Aztech AZT2320");
263 sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
264 card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
265
266 if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
267 snd_card_free(card);
268 return error;
269 }
270 if ((error = snd_cs4231_mixer(chip)) < 0) {
271 snd_card_free(card);
272 return error;
273 }
274 if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
275 snd_card_free(card);
276 return error;
277 }
278
279 if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
280 if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
281 mpu_port[dev], 0,
282 mpu_irq[dev], SA_INTERRUPT,
283 NULL) < 0)
284 snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
285 }
286
287 if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
288 if (snd_opl3_create(card,
289 fm_port[dev], fm_port[dev] + 2,
290 OPL3_HW_AUTO, 0, &opl3) < 0) {
291 snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
292 fm_port[dev], fm_port[dev] + 2);
293 } else {
294 if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
295 snd_card_free(card);
296 return error;
297 }
298 if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
299 snd_card_free(card);
300 return error;
301 }
302 }
303 }
304
305 if ((error = snd_card_register(card)) < 0) {
306 snd_card_free(card);
307 return error;
308 }
309 pnp_set_card_drvdata(pcard, card);
310 return 0;
311}
312
313static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
314 const struct pnp_card_device_id *id)
315{
316 static int dev;
317 int res;
318
319 for ( ; dev < SNDRV_CARDS; dev++) {
320 if (!enable[dev])
321 continue;
322 res = snd_card_azt2320_probe(dev, card, id);
323 if (res < 0)
324 return res;
325 dev++;
326 return 0;
327 }
328 return -ENODEV;
329}
330
331static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
332{
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100333 snd_card_free(pnp_get_card_drvdata(pcard));
334 pnp_set_card_drvdata(pcard, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335}
336
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100337#ifdef CONFIG_PM
338static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
339{
340 struct snd_card *card = pnp_get_card_drvdata(pcard);
341 struct snd_card_azt2320 *acard = card->private_data;
342 struct snd_cs4231 *chip = acard->chip;
343
344 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
345 chip->suspend(chip);
346 return 0;
347}
348
349static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
350{
351 struct snd_card *card = pnp_get_card_drvdata(pcard);
352 struct snd_card_azt2320 *acard = card->private_data;
353 struct snd_cs4231 *chip = acard->chip;
354
355 chip->resume(chip);
356 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
357 return 0;
358}
359#endif
360
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361static struct pnp_card_driver azt2320_pnpc_driver = {
362 .flags = PNP_DRIVER_RES_DISABLE,
363 .name = "azt2320",
364 .id_table = snd_azt2320_pnpids,
365 .probe = snd_azt2320_pnp_detect,
366 .remove = __devexit_p(snd_azt2320_pnp_remove),
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100367#ifdef CONFIG_PM
368 .suspend = snd_azt2320_pnp_suspend,
369 .resume = snd_azt2320_pnp_resume,
370#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371};
372
373static int __init alsa_card_azt2320_init(void)
374{
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100375 int cards;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100377 cards = pnp_register_card_driver(&azt2320_pnpc_driver);
378 if (cards <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 pnp_unregister_card_driver(&azt2320_pnpc_driver);
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100380#ifdef MODULE
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382#endif
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100383 return -ENODEV;
384 }
385 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386}
387
388static void __exit alsa_card_azt2320_exit(void)
389{
390 pnp_unregister_card_driver(&azt2320_pnpc_driver);
391}
392
393module_init(alsa_card_azt2320_init)
394module_exit(alsa_card_azt2320_exit)