blob: bfe3a255815ca6c2963a0794d89a6e1de3090054 [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.");
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
76struct snd_card_azt2320 {
77 int dev_no;
78 struct pnp_dev *dev;
79 struct pnp_dev *devmpu;
Takashi Iwaib6cc25c2005-11-17 17:04:12 +010080 struct snd_cs4231 *chip;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081};
82
83static struct pnp_card_device_id snd_azt2320_pnpids[] = {
84 /* PRO16V */
85 { .id = "AZT1008", .devs = { { "AZT1008" }, { "AZT2001" }, } },
86 /* Aztech Sound Galaxy 16 */
87 { .id = "AZT2320", .devs = { { "AZT0001" }, { "AZT0002" }, } },
88 /* Packard Bell Sound III 336 AM/SP */
89 { .id = "AZT3000", .devs = { { "AZT1003" }, { "AZT2001" }, } },
90 /* AT3300 */
91 { .id = "AZT3002", .devs = { { "AZT1004" }, { "AZT2001" }, } },
92 /* --- */
93 { .id = "AZT3005", .devs = { { "AZT1003" }, { "AZT2001" }, } },
94 /* --- */
95 { .id = "AZT3011", .devs = { { "AZT1003" }, { "AZT2001" }, } },
96 { .id = "" } /* end */
97};
98
99MODULE_DEVICE_TABLE(pnp_card, snd_azt2320_pnpids);
100
101#define DRIVER_NAME "snd-card-azt2320"
102
103static int __devinit snd_card_azt2320_pnp(int dev, struct snd_card_azt2320 *acard,
104 struct pnp_card_link *card,
105 const struct pnp_card_device_id *id)
106{
107 struct pnp_dev *pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 int err;
109
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
Rene Herman109c53f842007-11-30 17:59:25 +0100111 if (acard->dev == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
114 acard->devmpu = pnp_request_card_device(card, id->devs[1].id, NULL);
115
116 pdev = acard->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118 err = pnp_activate_dev(pdev);
119 if (err < 0) {
120 snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 return err;
122 }
123 port[dev] = pnp_port_start(pdev, 0);
124 fm_port[dev] = pnp_port_start(pdev, 1);
125 wss_port[dev] = pnp_port_start(pdev, 2);
126 dma1[dev] = pnp_dma(pdev, 0);
127 dma2[dev] = pnp_dma(pdev, 1);
128 irq[dev] = pnp_irq(pdev, 0);
129
130 pdev = acard->devmpu;
131 if (pdev != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 err = pnp_activate_dev(pdev);
133 if (err < 0)
134 goto __mpu_error;
135 mpu_port[dev] = pnp_port_start(pdev, 0);
136 mpu_irq[dev] = pnp_irq(pdev, 0);
137 } else {
138 __mpu_error:
139 if (pdev) {
140 pnp_release_card_device(pdev);
141 snd_printk(KERN_ERR PFX "MPU401 pnp configure failure, skipping\n");
142 }
143 acard->devmpu = NULL;
144 mpu_port[dev] = -1;
145 }
146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 return 0;
148}
149
150/* same of snd_sbdsp_command by Jaroslav Kysela */
151static int __devinit snd_card_azt2320_command(unsigned long port, unsigned char val)
152{
153 int i;
154 unsigned long limit;
155
156 limit = jiffies + HZ / 10;
157 for (i = 50000; i && time_after(limit, jiffies); i--)
158 if (!(inb(port + 0x0c) & 0x80)) {
159 outb(val, port + 0x0c);
160 return 0;
161 }
162 return -EBUSY;
163}
164
165static int __devinit snd_card_azt2320_enable_wss(unsigned long port)
166{
167 int error;
168
169 if ((error = snd_card_azt2320_command(port, 0x09)))
170 return error;
171 if ((error = snd_card_azt2320_command(port, 0x00)))
172 return error;
173
174 mdelay(5);
175 return 0;
176}
177
178static int __devinit snd_card_azt2320_probe(int dev,
179 struct pnp_card_link *pcard,
180 const struct pnp_card_device_id *pid)
181{
182 int error;
Takashi Iwai11ff5c62005-11-17 14:42:36 +0100183 struct snd_card *card;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 struct snd_card_azt2320 *acard;
Takashi Iwai11ff5c62005-11-17 14:42:36 +0100185 struct snd_cs4231 *chip;
186 struct snd_opl3 *opl3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
188 if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
189 sizeof(struct snd_card_azt2320))) == NULL)
190 return -ENOMEM;
191 acard = (struct snd_card_azt2320 *)card->private_data;
192
193 if ((error = snd_card_azt2320_pnp(dev, acard, pcard, pid))) {
194 snd_card_free(card);
195 return error;
196 }
197 snd_card_set_dev(card, &pcard->card->dev);
198
199 if ((error = snd_card_azt2320_enable_wss(port[dev]))) {
200 snd_card_free(card);
201 return error;
202 }
203
204 if ((error = snd_cs4231_create(card, wss_port[dev], -1,
205 irq[dev],
206 dma1[dev],
207 dma2[dev],
208 CS4231_HW_DETECT, 0, &chip)) < 0) {
209 snd_card_free(card);
210 return error;
211 }
212
213 strcpy(card->driver, "AZT2320");
214 strcpy(card->shortname, "Aztech AZT2320");
215 sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",
216 card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]);
217
218 if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) {
219 snd_card_free(card);
220 return error;
221 }
222 if ((error = snd_cs4231_mixer(chip)) < 0) {
223 snd_card_free(card);
224 return error;
225 }
226 if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) {
227 snd_card_free(card);
228 return error;
229 }
230
231 if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
232 if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
233 mpu_port[dev], 0,
Thomas Gleixner65ca68b2006-07-01 19:29:46 -0700234 mpu_irq[dev], IRQF_DISABLED,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 NULL) < 0)
236 snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
237 }
238
239 if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
240 if (snd_opl3_create(card,
241 fm_port[dev], fm_port[dev] + 2,
242 OPL3_HW_AUTO, 0, &opl3) < 0) {
243 snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
244 fm_port[dev], fm_port[dev] + 2);
245 } else {
246 if ((error = snd_opl3_timer_new(opl3, 1, 2)) < 0) {
247 snd_card_free(card);
248 return error;
249 }
250 if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
251 snd_card_free(card);
252 return error;
253 }
254 }
255 }
256
257 if ((error = snd_card_register(card)) < 0) {
258 snd_card_free(card);
259 return error;
260 }
261 pnp_set_card_drvdata(pcard, card);
262 return 0;
263}
264
Bjorn Helgaasdb2735e2006-03-27 01:17:10 -0800265static unsigned int __devinitdata azt2320_devices;
266
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267static int __devinit snd_azt2320_pnp_detect(struct pnp_card_link *card,
268 const struct pnp_card_device_id *id)
269{
270 static int dev;
271 int res;
272
273 for ( ; dev < SNDRV_CARDS; dev++) {
274 if (!enable[dev])
275 continue;
276 res = snd_card_azt2320_probe(dev, card, id);
277 if (res < 0)
278 return res;
279 dev++;
Bjorn Helgaasdb2735e2006-03-27 01:17:10 -0800280 azt2320_devices++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 return 0;
282 }
283 return -ENODEV;
284}
285
286static void __devexit snd_azt2320_pnp_remove(struct pnp_card_link * pcard)
287{
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100288 snd_card_free(pnp_get_card_drvdata(pcard));
289 pnp_set_card_drvdata(pcard, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290}
291
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100292#ifdef CONFIG_PM
293static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
294{
295 struct snd_card *card = pnp_get_card_drvdata(pcard);
296 struct snd_card_azt2320 *acard = card->private_data;
297 struct snd_cs4231 *chip = acard->chip;
298
299 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
300 chip->suspend(chip);
301 return 0;
302}
303
304static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)
305{
306 struct snd_card *card = pnp_get_card_drvdata(pcard);
307 struct snd_card_azt2320 *acard = card->private_data;
308 struct snd_cs4231 *chip = acard->chip;
309
310 chip->resume(chip);
311 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
312 return 0;
313}
314#endif
315
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316static struct pnp_card_driver azt2320_pnpc_driver = {
317 .flags = PNP_DRIVER_RES_DISABLE,
318 .name = "azt2320",
319 .id_table = snd_azt2320_pnpids,
320 .probe = snd_azt2320_pnp_detect,
321 .remove = __devexit_p(snd_azt2320_pnp_remove),
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100322#ifdef CONFIG_PM
323 .suspend = snd_azt2320_pnp_suspend,
324 .resume = snd_azt2320_pnp_resume,
325#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326};
327
328static int __init alsa_card_azt2320_init(void)
329{
Bjorn Helgaasdb2735e2006-03-27 01:17:10 -0800330 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
Bjorn Helgaasdb2735e2006-03-27 01:17:10 -0800332 err = pnp_register_card_driver(&azt2320_pnpc_driver);
333 if (err)
334 return err;
335
336 if (!azt2320_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 pnp_unregister_card_driver(&azt2320_pnpc_driver);
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100338#ifdef MODULE
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 snd_printk(KERN_ERR "no AZT2320 based soundcards found\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340#endif
Takashi Iwaib6cc25c2005-11-17 17:04:12 +0100341 return -ENODEV;
342 }
343 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344}
345
346static void __exit alsa_card_azt2320_exit(void)
347{
348 pnp_unregister_card_driver(&azt2320_pnpc_driver);
349}
350
351module_init(alsa_card_azt2320_init)
352module_exit(alsa_card_azt2320_exit)