ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / sound / isa / azt2320.c
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 <sound/core.h>
40 #define SNDRV_GET_ID
41 #include <sound/initval.h>
42 #include <sound/cs4231.h>
43 #include <sound/mpu401.h>
44 #include <sound/opl3.h>
45
46 #define chip_t cs4231_t
47
48 #define PFX "azt2320: "
49
50 MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
51 MODULE_DESCRIPTION("Aztech Systems AZT2320");
52 MODULE_LICENSE("GPL");
53 MODULE_CLASSES("{sound}");
54 MODULE_DEVICES("{{Aztech Systems,PRO16V},"
55                 "{Aztech Systems,AZT2320},"
56                 "{Aztech Systems,AZT3300},"
57                 "{Aztech Systems,AZT2320},"
58                 "{Aztech Systems,AZT3000}}");
59
60 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;      /* Index 0-MAX */
61 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;       /* ID for this card */
62 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
63 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;     /* PnP setup */
64 static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
65 static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
66 static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;  /* PnP setup */
67 static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;        /* Pnp setup */
68 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;    /* Pnp setup */
69 static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* PnP setup */
70 static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;       /* PnP setup */
71
72 MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
73 MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard.");
74 MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
75 MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
76 MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard.");
77 MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
78 MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
79 MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard.");
80 MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
81 MODULE_PARM(port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
82 MODULE_PARM_DESC(port, "Port # for azt2320 driver.");
83 MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC);
84 MODULE_PARM(wss_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
85 MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver.");
86 MODULE_PARM_SYNTAX(wss_port, SNDRV_PORT12_DESC);
87 MODULE_PARM(mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
88 MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver.");
89 MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC);
90 MODULE_PARM(fm_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
91 MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver.");
92 MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC);
93 MODULE_PARM(irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
94 MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver.");
95 MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC);
96 MODULE_PARM(mpu_irq, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
97 MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver.");
98 MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC);
99 MODULE_PARM(dma1, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
100 MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver.");
101 MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC);
102 MODULE_PARM(dma2, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
103 MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver.");
104 MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC);
105
106 struct snd_card_azt2320 {
107         int dev_no;
108         struct pnp_dev *dev;
109         struct pnp_dev *devmpu;
110 };
111
112 static struct pnp_card_device_id snd_azt2320_pnpids[] = {
113         /* PRO16V */
114         { .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
115         /* Aztech Sound Galaxy 16 */
116         { .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },
117         /* Packard Bell Sound III 336 AM/SP */
118         { .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },
119         /* AT3300 */
120         { .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },
121         /* --- */
122         { .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },
123         /* --- */
124         { .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },
125         { .id = "" }    /* end */
126 };
127
128 MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
129
130 #define DRIVER_NAME     "snd-card-azt2320"
131
132 static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
133                                           struct pnp_card_link *card,
134                                           const struct pnp_card_device_id *id)
135 {
136         struct pnp_dev *pdev;
137         struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
138         int err;
139
140         if (!cfg)
141                 return -ENOMEM;
142
143         acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
144         if (acard->dev == NULL) {
145                 kfree(cfg);
146                 return -ENODEV;
147         }
148
149         acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
150
151         pdev = acard->dev;
152         pnp_init_resource_table(cfg);
153
154         /* override resources */
155         if (port[dev] != SNDRV_AUTO_PORT)
156                 pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
157         if (fm_port[dev] != SNDRV_AUTO_PORT)
158                 pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
159         if (wss_port[dev] != SNDRV_AUTO_PORT)
160                 pnp_resource_change(&cfg->port_resource[2], wss_port[dev], 4);
161         if (dma1[dev] != SNDRV_AUTO_DMA)
162                 pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
163         if (dma2[dev] != SNDRV_AUTO_DMA)
164                 pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
165         if (irq[dev] != SNDRV_AUTO_IRQ)
166                 pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
167         if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
168                 snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
169
170         err = pnp_activate_dev(pdev);
171         if (err < 0) {
172                 snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
173                 kfree(cfg);
174                 return err;
175         }
176         port[dev] = pnp_port_start(pdev, 0);
177         fm_port[dev] = pnp_port_start(pdev, 1);
178         wss_port[dev] = pnp_port_start(pdev, 2);
179         dma1[dev] = pnp_dma(pdev, 0);
180         dma2[dev] = pnp_dma(pdev, 1);
181         irq[dev] = pnp_irq(pdev, 0);
182
183         pdev = acard->devmpu;
184         if (pdev != NULL) {
185                 pnp_init_resource_table(cfg);
186                 if (mpu_port[dev] != SNDRV_AUTO_PORT)
187                         pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
188                 if (mpu_irq[dev] != SNDRV_AUTO_IRQ)
189                         pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
190                 if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
191                         snd_printk(KERN_ERR PFX "MPU401 the requested resources are invalid, using auto config\n");
192                 err = pnp_activate_dev(pdev);
193                 if (err < 0)
194                         goto __mpu_error;
195                 mpu_port[dev] = pnp_port_start(pdev, 0);
196                 mpu_irq[dev] = pnp_irq(pdev, 0);
197         } else {
198              __mpu_error:
199                 if (pdev) {
200                         pnp_release_card_device(pdev);
201                         snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
202                 }
203                 acard->devmpu = NULL;
204                 mpu_port[dev] = -1;
205         }
206
207         kfree (cfg);
208         return 0;
209 }
210
211 /* same of snd_sbdsp_command by Jaroslav Kysela */
212 static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val)
213 {
214         int i;
215         unsigned long limit;
216
217         limit = jiffies + HZ / 10;
218         for (i = 50000; i && time_after(limit, jiffies); i--)
219                 if (!(inb(port + 0x0c) & 0x80)) {
220                         outb(val, port + 0x0c);
221                         return 0;
222                 }
223         return -EBUSY;
224 }
225
226 static int __devinit snd_card_azt2320_enable_wss(unsigned long port)
227 {
228         int error;
229
230         if ((error = snd_card_azt2320_command(port, 0x09)))
231                 return error;
232         if ((error = snd_card_azt2320_command(port, 0x00)))
233                 return error;
234
235         mdelay(5);
236         return 0;
237 }
238
239 static int __devinit snd_card_azt2320_probe(int dev,
240                                             struct pnp_card_link *pcard,
241                                             const struct pnp_card_device_id *pid)
242 {
243         int error;
244         snd_card_t *card;
245         struct snd_card_azt2320 *acard;
246         cs4231_t *chip;
247         opl3_t *opl3;
248
249         if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
250                                  sizeof(struct snd_card_azt2320))) == NULL)
251                 return -ENOMEM;
252         acard = (struct snd_card_azt2320 *)card->private_data;
253
254         if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
255                 snd_card_free(card);
256                 return error;
257         }
258         snd_card_set_dev(card, &pcard->card->dev);
259
260         if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
261                 snd_card_free(card);
262                 return error;
263         }
264
265         if ((error = snd_cs4231_create(card, wss_port[dev], -1,
266                                        irq[dev],
267                                        dma1[dev],
268                                        dma2[dev],
269                                        CS4231_HW_DETECT, 0, &chip)) < 0) {
270                 snd_card_free(card);
271                 return error;
272         }
273
274         strcpy(card->driver, "AZT2320");
275         strcpy(card->shortname, "Aztech AZT2320");
276         sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
277                 card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
278
279         if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
280                 snd_card_free(card);
281                 return error;
282         }
283         if ((error = snd_cs4231_mixer(chip)) < 0) {
284                 snd_card_free(card);
285                 return error;
286         }
287         if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
288                 snd_card_free(card);
289                 return error;
290         }
291
292         if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
293                 if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
294                                 mpu_port[dev], 0,
295                                 mpu_irq[dev], SA_INTERRUPT,
296                                 NULL) < 0)
297                         snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
298         }
299
300         if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
301                 if (snd_opl3_create(card,
302                                     fm_port[dev], fm_port[dev] + 2,
303                                     OPL3_HW_AUTO, 0, &opl3) < 0) {
304                         snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
305                                    fm_port[dev], fm_port[dev] + 2);
306                 } else {
307                         if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
308                                 snd_card_free(card);
309                                 return error;
310                         }
311                         if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
312                                 snd_card_free(card);
313                                 return error;
314                         }
315                 }
316         }
317
318         if ((error = snd_card_register(card)) < 0) {
319                 snd_card_free(card);
320                 return error;
321         }
322         pnp_set_card_drvdata(pcard, card);
323         return 0;
324 }
325
326 static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
327                                             const struct pnp_card_device_id *id)
328 {
329         static int dev;
330         int res;
331
332         for ( ; dev < SNDRV_CARDS; dev++) {
333                 if (!enable[dev])
334                         continue;
335                 res = snd_card_azt2320_probe(dev, card, id);
336                 if (res < 0)
337                         return res;
338                 dev++;
339                 return 0;
340         }
341         return -ENODEV;
342 }
343
344 static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
345 {
346         snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard);
347
348         snd_card_disconnect(card);
349         snd_card_free_in_thread(card);
350 }
351
352 static struct pnp_card_driver azt2320_pnpc_driver = {
353         .flags          = PNP_DRIVER_RES_DISABLE,
354         .name           = "azt2320",
355         .id_table       = snd_azt2320_pnpids,
356         .probe          = snd_azt2320_pnp_detect,
357         .remove         = __devexit_p(snd_azt2320_pnp_remove),
358 };
359
360 static int __init alsa_card_azt2320_init(void)
361 {
362         int cards = 0;
363
364         cards += pnp_register_card_driver(&azt2320_pnpc_driver);
365 #ifdef MODULE
366         if (!cards) {
367                 pnp_unregister_card_driver(&azt2320_pnpc_driver);
368                 snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
369         }
370 #endif
371         return cards ? 0 : -ENODEV;
372 }
373
374 static void __exit alsa_card_azt2320_exit(void)
375 {
376         pnp_unregister_card_driver(&azt2320_pnpc_driver);
377 }
378
379 module_init(alsa_card_azt2320_init)
380 module_exit(alsa_card_azt2320_exit)
381
382 #ifndef MODULE
383
384 /* format is: snd-azt2320=enable,index,id,port,
385                           wss_port,mpu_port,fm_port,
386                           irq,mpu_irq,dma1,dma2 */
387
388 static int __init alsa_card_azt2320_setup(char *str)
389 {
390         static unsigned __initdata nr_dev = 0;
391
392         if (nr_dev >= SNDRV_CARDS)
393                 return 0;
394         (void)(get_option(&str,&enable[nr_dev]) == 2 &&
395                get_option(&str,&index[nr_dev]) == 2 &&
396                get_id(&str,&id[nr_dev]) == 2 &&
397                get_option_long(&str,&port[nr_dev]) == 2 &&
398                get_option_long(&str,&wss_port[nr_dev]) == 2 &&
399                get_option_long(&str,&mpu_port[nr_dev]) == 2 &&
400                get_option(&str,&irq[nr_dev]) == 2 &&
401                get_option(&str,&mpu_irq[nr_dev]) == 2 &&
402                get_option(&str,&dma1[nr_dev]) == 2 &&
403                get_option(&str,&dma2[nr_dev]) == 2);
404         nr_dev++;
405         return 1;
406 }
407
408 __setup("snd-azt2320=", alsa_card_azt2320_setup);
409
410 #endif /* ifndef MODULE */