blob: b615538a928d6e5570a480fd628e210c244bf044 [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,
Thomas Gleixner65ca68b2006-07-01 19:29:46 -0700282 mpu_irq[dev], IRQF_DISABLED,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 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
Bjorn Helgaasdb2735e2006-03-27 01:17:10 -0800313static unsigned int __devinitdata azt2320_devices;
314
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315static 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++;
Bjorn Helgaasdb2735e2006-03-27 01:17:10 -0800328 azt2320_devices++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 return 0;
330 }
331 return -ENODEV;
332}
333
334static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
335{
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100336 snd_card_free(pnp_get_card_drvdata(pcard));
337 pnp_set_card_drvdata(pcard, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338}
339
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100340#ifdef CONFIG_PM
341static 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
352static 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364static 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),
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100370#ifdef CONFIG_PM
371 .suspend = snd_azt2320_pnp_suspend,
372 .resume = snd_azt2320_pnp_resume,
373#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374};
375
376static int __init alsa_card_azt2320_init(void)
377{
Bjorn Helgaasdb2735e2006-03-27 01:17:10 -0800378 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379
Bjorn Helgaasdb2735e2006-03-27 01:17:10 -0800380 err = pnp_register_card_driver(&azt2320_pnpc_driver);
381 if (err)
382 return err;
383
384 if (!azt2320_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 pnp_unregister_card_driver(&azt2320_pnpc_driver);
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100386#ifdef MODULE
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388#endif
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100389 return -ENODEV;
390 }
391 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392}
393
394static void __exit alsa_card_azt2320_exit(void)
395{
396 pnp_unregister_card_driver(&azt2320_pnpc_driver);
397}
398
399module_init(alsa_card_azt2320_init)
400module_exit(alsa_card_azt2320_exit)