blob: bb41c6ec2f43a8094de28d09830a382181e0b949 [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;
96};
97
98static 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
114MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
115
116#define DRIVER_NAME "snd-card-azt2320"
117
118static 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 */
198static 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
212static 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
225static 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
312static 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
330static 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
338static 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
346static 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
360static void __exit alsa_card_azt2320_exit(void)
361{
362 pnp_unregister_card_driver(&azt2320_pnpc_driver);
363}
364
365module_init(alsa_card_azt2320_init)
366module_exit(alsa_card_azt2320_exit)