Merge ../linux-2.6
[linux-2.6] / 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 <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         struct snd_cs4231 *chip;
97 };
98
99 static 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
115 MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
116
117 #define DRIVER_NAME     "snd-card-azt2320"
118
119 static 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 */
199 static 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
213 static 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
226 static 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;
231         struct snd_card *card;
232         struct snd_card_azt2320 *acard;
233         struct snd_cs4231 *chip;
234         struct snd_opl3 *opl3;
235
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
313 static unsigned int __devinitdata azt2320_devices;
314
315 static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
316                                             const struct pnp_card_device_id *id)
317 {
318         static int dev;
319         int res;
320
321         for ( ; dev < SNDRV_CARDS; dev++) {
322                 if (!enable[dev])
323                         continue;
324                 res = snd_card_azt2320_probe(dev, card, id);
325                 if (res < 0)
326                         return res;
327                 dev++;
328                 azt2320_devices++;
329                 return 0;
330         }
331         return -ENODEV;
332 }
333
334 static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
335 {
336         snd_card_free(pnp_get_card_drvdata(pcard));
337         pnp_set_card_drvdata(pcard, NULL);
338 }
339
340 #ifdef CONFIG_PM
341 static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
342 {
343         struct snd_card *card = pnp_get_card_drvdata(pcard);
344         struct snd_card_azt2320 *acard = card->private_data;
345         struct snd_cs4231 *chip = acard->chip;
346
347         snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
348         chip->suspend(chip);
349         return 0;
350 }
351
352 static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
353 {
354         struct snd_card *card = pnp_get_card_drvdata(pcard);
355         struct snd_card_azt2320 *acard = card->private_data;
356         struct snd_cs4231 *chip = acard->chip;
357
358         chip->resume(chip);
359         snd_power_change_state(card, SNDRV_CTL_POWER_D0);
360         return 0;
361 }
362 #endif
363
364 static struct pnp_card_driver azt2320_pnpc_driver = {
365         .flags          = PNP_DRIVER_RES_DISABLE,
366         .name           = "azt2320",
367         .id_table       = snd_azt2320_pnpids,
368         .probe          = snd_azt2320_pnp_detect,
369         .remove         = __devexit_p(snd_azt2320_pnp_remove),
370 #ifdef CONFIG_PM
371         .suspend        = snd_azt2320_pnp_suspend,
372         .resume         = snd_azt2320_pnp_resume,
373 #endif
374 };
375
376 static int __init alsa_card_azt2320_init(void)
377 {
378         int err;
379
380         err = pnp_register_card_driver(&azt2320_pnpc_driver);
381         if (err)
382                 return err;
383
384         if (!azt2320_devices) {
385                 pnp_unregister_card_driver(&azt2320_pnpc_driver);
386 #ifdef MODULE
387                 snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
388 #endif
389                 return -ENODEV;
390         }
391         return 0;
392 }
393
394 static void __exit alsa_card_azt2320_exit(void)
395 {
396         pnp_unregister_card_driver(&azt2320_pnpc_driver);
397 }
398
399 module_init(alsa_card_azt2320_init)
400 module_exit(alsa_card_azt2320_exit)