blob: 41c047e665ec7d62c6f85c25dbf45ce1441e901f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
3 Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>
4
5 Part of this code was developed at the Italian Ministry of Air Defence,
6 Sixth Division (oh, che pace ...), Rome.
7
8 Thanks to Maria Grazia Pollarini, Salvatore Vassallo.
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23*/
24
25
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <linux/init.h>
Takashi Iwai99a0b762005-11-17 17:13:59 +010027#include <linux/err.h>
Takashi Iwai5e24c1c2007-02-22 12:50:54 +010028#include <linux/isa.h>
Takashi Iwai99a0b762005-11-17 17:13:59 +010029#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/slab.h>
31#include <linux/pnp.h>
32#include <linux/moduleparam.h>
Takashi Iwai99a0b762005-11-17 17:13:59 +010033#include <asm/io.h>
34#include <asm/dma.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <sound/core.h>
Krzysztof Helt9f240a52008-06-11 12:26:32 +020036#if defined(CS4231) || defined(OPTi93X)
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <sound/cs4231.h>
38#else
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <sound/ad1848.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#endif /* CS4231 */
41#include <sound/mpu401.h>
42#include <sound/opl3.h>
43#ifndef OPTi93X
44#include <sound/opl4.h>
45#endif
46#define SNDRV_LEGACY_FIND_FREE_IRQ
47#define SNDRV_LEGACY_FIND_FREE_DMA
48#include <sound/initval.h>
49
50MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
51MODULE_LICENSE("GPL");
52#ifdef OPTi93X
53MODULE_DESCRIPTION("OPTi93X");
54MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}");
55#else /* OPTi93X */
56#ifdef CS4231
57MODULE_DESCRIPTION("OPTi92X - CS4231");
58MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)},"
59 "{OPTi,82C925 (CS4231)}}");
60#else /* CS4231 */
61MODULE_DESCRIPTION("OPTi92X - AD1848");
62MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"
63 "{OPTi,82C925 (AD1848)},"
64 "{OAK,Mozart}}");
65#endif /* CS4231 */
66#endif /* OPTi93X */
67
68static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */
69static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */
70//static int enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */
71static int isapnp = 1; /* Enable ISA PnP detection */
72static long port = SNDRV_DEFAULT_PORT1; /* 0x530,0xe80,0xf40,0x604 */
73static long mpu_port = SNDRV_DEFAULT_PORT1; /* 0x300,0x310,0x320,0x330 */
74static long fm_port = SNDRV_DEFAULT_PORT1; /* 0x388 */
75static int irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10,11 */
76static int mpu_irq = SNDRV_DEFAULT_IRQ1; /* 5,7,9,10 */
77static int dma1 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
78#if defined(CS4231) || defined(OPTi93X)
79static int dma2 = SNDRV_DEFAULT_DMA1; /* 0,1,3 */
80#endif /* CS4231 || OPTi93X */
81
82module_param(index, int, 0444);
83MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard.");
84module_param(id, charp, 0444);
85MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
86//module_param(enable, bool, 0444);
87//MODULE_PARM_DESC(enable, "Enable opti9xx soundcard.");
88module_param(isapnp, bool, 0444);
89MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
90module_param(port, long, 0444);
91MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
92module_param(mpu_port, long, 0444);
93MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
94module_param(fm_port, long, 0444);
95MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
96module_param(irq, int, 0444);
97MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
98module_param(mpu_irq, int, 0444);
99MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
100module_param(dma1, int, 0444);
101MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
102#if defined(CS4231) || defined(OPTi93X)
103module_param(dma2, int, 0444);
104MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
105#endif /* CS4231 || OPTi93X */
106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107#define OPTi9XX_HW_82C928 1
108#define OPTi9XX_HW_82C929 2
109#define OPTi9XX_HW_82C924 3
110#define OPTi9XX_HW_82C925 4
111#define OPTi9XX_HW_82C930 5
112#define OPTi9XX_HW_82C931 6
113#define OPTi9XX_HW_82C933 7
114#define OPTi9XX_HW_LAST OPTi9XX_HW_82C933
115
116#define OPTi9XX_MC_REG(n) n
117
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118#ifdef OPTi93X
119
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120#define OPTi93X_STATUS 0x02
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121#define OPTi93X_PORT(chip, r) ((chip)->port + OPTi93X_##r)
122
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123#define OPTi93X_IRQ_PLAYBACK 0x04
124#define OPTi93X_IRQ_CAPTURE 0x08
125
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126#endif /* OPTi93X */
127
Takashi Iwai346c7a62005-11-17 14:37:56 +0100128struct snd_opti9xx {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 unsigned short hardware;
130 unsigned char password;
131 char name[7];
132
133 unsigned long mc_base;
134 struct resource *res_mc_base;
135 unsigned long mc_base_size;
136#ifdef OPTi93X
137 unsigned long mc_indir_index;
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200138 struct snd_cs4231 *codec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139#endif /* OPTi93X */
140 unsigned long pwd_reg;
141
142 spinlock_t lock;
143
144 long wss_base;
145 int irq;
146 int dma1;
147#if defined(CS4231) || defined(OPTi93X)
148 int dma2;
149#endif /* CS4231 || OPTi93X */
150
151 long fm_port;
152
153 long mpu_port;
154 int mpu_irq;
155
156#ifdef CONFIG_PNP
157 struct pnp_dev *dev;
158 struct pnp_dev *devmpu;
159#endif /* CONFIG_PNP */
160};
161
Takashi Iwai99a0b762005-11-17 17:13:59 +0100162static int snd_opti9xx_pnp_is_probed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
164#ifdef CONFIG_PNP
165
166static struct pnp_card_device_id snd_opti9xx_pnpids[] = {
167#ifndef OPTi93X
168 /* OPTi 82C924 */
169 { .id = "OPT0924", .devs = { { "OPT0000" }, { "OPT0002" } }, .driver_data = 0x0924 },
170 /* OPTi 82C925 */
171 { .id = "OPT0925", .devs = { { "OPT9250" }, { "OPT0002" } }, .driver_data = 0x0925 },
172#else
173 /* OPTi 82C931/3 */
174 { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, .driver_data = 0x0931 },
175#endif /* OPTi93X */
176 { .id = "" }
177};
178
179MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);
180
181#endif /* CONFIG_PNP */
182
183#ifdef OPTi93X
Rene Herman83c51c02007-03-20 11:33:46 +0100184#define DEV_NAME "opti93x"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185#else
Rene Herman83c51c02007-03-20 11:33:46 +0100186#define DEV_NAME "opti92x"
187#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
189static char * snd_opti9xx_names[] = {
190 "unkown",
191 "82C928", "82C929",
192 "82C924", "82C925",
193 "82C930", "82C931", "82C933"
194};
195
196
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100197static long __devinit snd_legacy_find_free_ioport(long *port_table, long size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198{
199 while (*port_table != -1) {
Takashi Iwaib1d57762005-10-10 11:56:31 +0200200 if (request_region(*port_table, size, "ALSA test")) {
201 release_region(*port_table, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 return *port_table;
203 }
204 port_table++;
205 }
206 return -1;
207}
208
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100209static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
210 unsigned short hardware)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
212 static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};
213
214 chip->hardware = hardware;
215 strcpy(chip->name, snd_opti9xx_names[hardware]);
216
217 chip->mc_base_size = opti9xx_mc_size[hardware];
218
219 spin_lock_init(&chip->lock);
220
221 chip->wss_base = -1;
222 chip->irq = -1;
223 chip->dma1 = -1;
224#if defined(CS4231) || defined (OPTi93X)
225 chip->dma2 = -1;
226#endif /* CS4231 || OPTi93X */
227 chip->fm_port = -1;
228 chip->mpu_port = -1;
229 chip->mpu_irq = -1;
230
231 switch (hardware) {
232#ifndef OPTi93X
233 case OPTi9XX_HW_82C928:
234 case OPTi9XX_HW_82C929:
235 chip->mc_base = 0xf8c;
236 chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
237 chip->pwd_reg = 3;
238 break;
239
240 case OPTi9XX_HW_82C924:
241 case OPTi9XX_HW_82C925:
242 chip->mc_base = 0xf8c;
243 chip->password = 0xe5;
244 chip->pwd_reg = 3;
245 break;
246#else /* OPTi93X */
247
248 case OPTi9XX_HW_82C930:
249 case OPTi9XX_HW_82C931:
250 case OPTi9XX_HW_82C933:
251 chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
252 chip->mc_indir_index = 0xe0e;
253 chip->password = 0xe4;
254 chip->pwd_reg = 0;
255 break;
256#endif /* OPTi93X */
257
258 default:
259 snd_printk("chip %d not supported\n", hardware);
260 return -ENODEV;
261 }
262 return 0;
263}
264
Takashi Iwai346c7a62005-11-17 14:37:56 +0100265static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 unsigned char reg)
267{
268 unsigned long flags;
269 unsigned char retval = 0xff;
270
271 spin_lock_irqsave(&chip->lock, flags);
272 outb(chip->password, chip->mc_base + chip->pwd_reg);
273
274 switch (chip->hardware) {
275#ifndef OPTi93X
276 case OPTi9XX_HW_82C924:
277 case OPTi9XX_HW_82C925:
278 if (reg > 7) {
279 outb(reg, chip->mc_base + 8);
280 outb(chip->password, chip->mc_base + chip->pwd_reg);
281 retval = inb(chip->mc_base + 9);
282 break;
283 }
284
285 case OPTi9XX_HW_82C928:
286 case OPTi9XX_HW_82C929:
287 retval = inb(chip->mc_base + reg);
288 break;
289#else /* OPTi93X */
290
291 case OPTi9XX_HW_82C930:
292 case OPTi9XX_HW_82C931:
293 case OPTi9XX_HW_82C933:
294 outb(reg, chip->mc_indir_index);
295 outb(chip->password, chip->mc_base + chip->pwd_reg);
296 retval = inb(chip->mc_indir_index + 1);
297 break;
298#endif /* OPTi93X */
299
300 default:
301 snd_printk("chip %d not supported\n", chip->hardware);
302 }
303
304 spin_unlock_irqrestore(&chip->lock, flags);
305 return retval;
306}
307
Takashi Iwai346c7a62005-11-17 14:37:56 +0100308static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 unsigned char value)
310{
311 unsigned long flags;
312
313 spin_lock_irqsave(&chip->lock, flags);
314 outb(chip->password, chip->mc_base + chip->pwd_reg);
315
316 switch (chip->hardware) {
317#ifndef OPTi93X
318 case OPTi9XX_HW_82C924:
319 case OPTi9XX_HW_82C925:
320 if (reg > 7) {
321 outb(reg, chip->mc_base + 8);
322 outb(chip->password, chip->mc_base + chip->pwd_reg);
323 outb(value, chip->mc_base + 9);
324 break;
325 }
326
327 case OPTi9XX_HW_82C928:
328 case OPTi9XX_HW_82C929:
329 outb(value, chip->mc_base + reg);
330 break;
331#else /* OPTi93X */
332
333 case OPTi9XX_HW_82C930:
334 case OPTi9XX_HW_82C931:
335 case OPTi9XX_HW_82C933:
336 outb(reg, chip->mc_indir_index);
337 outb(chip->password, chip->mc_base + chip->pwd_reg);
338 outb(value, chip->mc_indir_index + 1);
339 break;
340#endif /* OPTi93X */
341
342 default:
343 snd_printk("chip %d not supported\n", chip->hardware);
344 }
345
346 spin_unlock_irqrestore(&chip->lock, flags);
347}
348
349
350#define snd_opti9xx_write_mask(chip, reg, value, mask) \
351 snd_opti9xx_write(chip, reg, \
352 (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))
353
354
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100355static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356{
357 unsigned char wss_base_bits;
358 unsigned char irq_bits;
359 unsigned char dma_bits;
360 unsigned char mpu_port_bits = 0;
361 unsigned char mpu_irq_bits;
362
363 switch (chip->hardware) {
364#ifndef OPTi93X
365 case OPTi9XX_HW_82C924:
366 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
367 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
368
369 case OPTi9XX_HW_82C925:
370 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
371 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
372 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
373#ifdef CS4231
374 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
375#else
376 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
377#endif /* CS4231 */
378 break;
379
380 case OPTi9XX_HW_82C928:
381 case OPTi9XX_HW_82C929:
382 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
383 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
384 /*
385 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);
386 */
387 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
388#ifdef CS4231
389 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
390#else
391 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
392#endif /* CS4231 */
393 break;
394
395#else /* OPTi93X */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 case OPTi9XX_HW_82C931:
397 case OPTi9XX_HW_82C933:
Krzysztof Helt3ae5f362008-06-08 07:57:53 +0200398 /*
Krzysztof Heltf81b9532007-10-16 14:54:37 +0200399 * The BTC 1817DW has QS1000 wavetable which is connected
400 * to the serial digital input of the OPTI931.
401 */
402 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff);
403 /*
404 * This bit sets OPTI931 to automaticaly select FM
405 * or digital input signal.
406 */
407 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);
Krzysztof Helt3ae5f362008-06-08 07:57:53 +0200408 case OPTi9XX_HW_82C930: /* FALL THROUGH */
409 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
410 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
411 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
412 (chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
413 0x34);
414 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 break;
416#endif /* OPTi93X */
417
418 default:
419 snd_printk("chip %d not supported\n", chip->hardware);
420 return -EINVAL;
421 }
422
423 switch (chip->wss_base) {
424 case 0x530:
425 wss_base_bits = 0x00;
426 break;
427 case 0x604:
428 wss_base_bits = 0x03;
429 break;
430 case 0xe80:
431 wss_base_bits = 0x01;
432 break;
433 case 0xf40:
434 wss_base_bits = 0x02;
435 break;
436 default:
437 snd_printk("WSS port 0x%lx not valid\n", chip->wss_base);
438 goto __skip_base;
439 }
440 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
441
442__skip_base:
443 switch (chip->irq) {
444//#ifdef OPTi93X
445 case 5:
446 irq_bits = 0x05;
447 break;
448//#endif /* OPTi93X */
449 case 7:
450 irq_bits = 0x01;
451 break;
452 case 9:
453 irq_bits = 0x02;
454 break;
455 case 10:
456 irq_bits = 0x03;
457 break;
458 case 11:
459 irq_bits = 0x04;
460 break;
461 default:
462 snd_printk("WSS irq # %d not valid\n", chip->irq);
463 goto __skip_resources;
464 }
465
466 switch (chip->dma1) {
467 case 0:
468 dma_bits = 0x01;
469 break;
470 case 1:
471 dma_bits = 0x02;
472 break;
473 case 3:
474 dma_bits = 0x03;
475 break;
476 default:
477 snd_printk("WSS dma1 # %d not valid\n", chip->dma1);
478 goto __skip_resources;
479 }
480
481#if defined(CS4231) || defined(OPTi93X)
482 if (chip->dma1 == chip->dma2) {
483 snd_printk("don't want to share dmas\n");
484 return -EBUSY;
485 }
486
487 switch (chip->dma2) {
488 case 0:
489 case 1:
490 break;
491 default:
492 snd_printk("WSS dma2 # %d not valid\n", chip->dma2);
493 goto __skip_resources;
494 }
495 dma_bits |= 0x04;
496#endif /* CS4231 || OPTi93X */
497
498#ifndef OPTi93X
499 outb(irq_bits << 3 | dma_bits, chip->wss_base);
500#else /* OPTi93X */
501 snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
502#endif /* OPTi93X */
503
504__skip_resources:
505 if (chip->hardware > OPTi9XX_HW_82C928) {
506 switch (chip->mpu_port) {
507 case 0:
508 case -1:
509 break;
510 case 0x300:
511 mpu_port_bits = 0x03;
512 break;
513 case 0x310:
514 mpu_port_bits = 0x02;
515 break;
516 case 0x320:
517 mpu_port_bits = 0x01;
518 break;
519 case 0x330:
520 mpu_port_bits = 0x00;
521 break;
522 default:
523 snd_printk("MPU-401 port 0x%lx not valid\n",
524 chip->mpu_port);
525 goto __skip_mpu;
526 }
527
528 switch (chip->mpu_irq) {
529 case 5:
530 mpu_irq_bits = 0x02;
531 break;
532 case 7:
533 mpu_irq_bits = 0x03;
534 break;
535 case 9:
536 mpu_irq_bits = 0x00;
537 break;
538 case 10:
539 mpu_irq_bits = 0x01;
540 break;
541 default:
542 snd_printk("MPU-401 irq # %d not valid\n",
543 chip->mpu_irq);
544 goto __skip_mpu;
545 }
546
547 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
548 (chip->mpu_port <= 0) ? 0x00 :
549 0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
550 0xf8);
551 }
552__skip_mpu:
553
554 return 0;
555}
556
557#ifdef OPTi93X
558
David Howells7d12e782006-10-05 14:55:46 +0100559static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200561 struct snd_cs4231 *codec = dev_id;
562 struct snd_opti9xx *chip = codec->card->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 unsigned char status;
564
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200565 status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
567 snd_pcm_period_elapsed(codec->playback_substream);
568 if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200569 snd_cs4231_overrange(codec);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 snd_pcm_period_elapsed(codec->capture_substream);
571 }
572 outb(0x00, OPTi93X_PORT(codec, STATUS));
573 return IRQ_HANDLED;
574}
575
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576#endif /* OPTi93X */
577
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100578static int __devinit snd_card_opti9xx_detect(struct snd_card *card,
579 struct snd_opti9xx *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580{
581 int i, err;
582
583#ifndef OPTi93X
584 for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
585 unsigned char value;
586
587 if ((err = snd_opti9xx_init(chip, i)) < 0)
588 return err;
589
590 if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
591 continue;
592
593 value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
594 if ((value != 0xff) && (value != inb(chip->mc_base + 1)))
595 if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
596 return 1;
597
Takashi Iwaib1d57762005-10-10 11:56:31 +0200598 release_and_free_resource(chip->res_mc_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 chip->res_mc_base = NULL;
600
601 }
602#else /* OPTi93X */
603 for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
604 unsigned long flags;
605 unsigned char value;
606
607 if ((err = snd_opti9xx_init(chip, i)) < 0)
608 return err;
609
610 if ((chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size, "OPTi9xx MC")) == NULL)
611 continue;
612
613 spin_lock_irqsave(&chip->lock, flags);
614 outb(chip->password, chip->mc_base + chip->pwd_reg);
615 outb(((chip->mc_indir_index & (1 << 8)) >> 4) |
616 ((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base);
617 spin_unlock_irqrestore(&chip->lock, flags);
618
619 value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
620 snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
621 if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
622 return 1;
623
Takashi Iwaib1d57762005-10-10 11:56:31 +0200624 release_and_free_resource(chip->res_mc_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 chip->res_mc_base = NULL;
626 }
627#endif /* OPTi93X */
628
629 return -ENODEV;
630}
631
632#ifdef CONFIG_PNP
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100633static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
634 struct pnp_card_link *card,
635 const struct pnp_card_device_id *pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636{
637 struct pnp_dev *pdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 int err;
639
640 chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL);
Rene Herman109c53f842007-11-30 17:59:25 +0100641 if (chip->dev == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 return -EBUSY;
Rene Herman109c53f842007-11-30 17:59:25 +0100643
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
645
646 pdev = chip->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 err = pnp_activate_dev(pdev);
649 if (err < 0) {
650 snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 return err;
652 }
653
654#ifdef OPTi93X
655 port = pnp_port_start(pdev, 0) - 4;
Takashi Iwai1ea73412007-09-17 16:44:06 +0200656 fm_port = pnp_port_start(pdev, 1) + 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657#else
658 if (pid->driver_data != 0x0924)
659 port = pnp_port_start(pdev, 1);
Takashi Iwai1ea73412007-09-17 16:44:06 +0200660 fm_port = pnp_port_start(pdev, 2) + 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661#endif /* OPTi93X */
662 irq = pnp_irq(pdev, 0);
663 dma1 = pnp_dma(pdev, 0);
664#if defined(CS4231) || defined(OPTi93X)
665 dma2 = pnp_dma(pdev, 1);
666#endif /* CS4231 || OPTi93X */
667
668 pdev = chip->devmpu;
669 if (pdev && mpu_port > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 err = pnp_activate_dev(pdev);
671 if (err < 0) {
672 snd_printk(KERN_ERR "AUDIO pnp configure failure\n");
673 mpu_port = -1;
674 chip->devmpu = NULL;
675 } else {
676 mpu_port = pnp_port_start(pdev, 0);
677 mpu_irq = pnp_irq(pdev, 0);
678 }
679 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 return pid->driver_data;
681}
682#endif /* CONFIG_PNP */
683
Takashi Iwai346c7a62005-11-17 14:37:56 +0100684static void snd_card_opti9xx_free(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685{
Takashi Iwai99a0b762005-11-17 17:13:59 +0100686 struct snd_opti9xx *chip = card->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200688 if (chip) {
689#ifdef OPTi93X
690 struct snd_cs4231 *codec = chip->codec;
691 if (codec->irq > 0) {
692 disable_irq(codec->irq);
693 free_irq(codec->irq, codec);
694 }
695#endif
Takashi Iwaib1d57762005-10-10 11:56:31 +0200696 release_and_free_resource(chip->res_mc_base);
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200697 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698}
699
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100700static int __devinit snd_opti9xx_probe(struct snd_card *card)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701{
702 static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 int error;
Takashi Iwai99a0b762005-11-17 17:13:59 +0100704 struct snd_opti9xx *chip = card->private_data;
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200705#if defined(CS4231) || defined(OPTi93X)
Takashi Iwai346c7a62005-11-17 14:37:56 +0100706 struct snd_cs4231 *codec;
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200707#ifdef CS4231
Takashi Iwai346c7a62005-11-17 14:37:56 +0100708 struct snd_timer *timer;
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200709#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710#else
Takashi Iwai346c7a62005-11-17 14:37:56 +0100711 struct snd_ad1848 *codec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712#endif
Takashi Iwai346c7a62005-11-17 14:37:56 +0100713 struct snd_pcm *pcm;
714 struct snd_rawmidi *rmidi;
715 struct snd_hwdep *synth;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
717 if (! chip->res_mc_base &&
Takashi Iwai99a0b762005-11-17 17:13:59 +0100718 (chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
719 "OPTi9xx MC")) == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721
722 chip->wss_base = port;
723 chip->fm_port = fm_port;
724 chip->mpu_port = mpu_port;
725 chip->irq = irq;
726 chip->mpu_irq = mpu_irq;
727 chip->dma1 = dma1;
728#if defined(CS4231) || defined(OPTi93X)
729 chip->dma2 = dma2;
730#endif
731
732 if (chip->wss_base == SNDRV_AUTO_PORT) {
733 if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 snd_printk("unable to find a free WSS port\n");
735 return -EBUSY;
736 }
737 }
Takashi Iwai99a0b762005-11-17 17:13:59 +0100738 if ((error = snd_opti9xx_configure(chip)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200741#if defined(CS4231) || defined(OPTi93X)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1,
743 chip->irq, chip->dma1, chip->dma2,
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200744#ifdef CS4231
745 CS4231_HW_DETECT, 0,
746#else /* OPTi93x */
747 CS4231_HW_OPTI93X, CS4231_HWSHARE_IRQ,
748#endif
Takashi Iwai99a0b762005-11-17 17:13:59 +0100749 &codec)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 return error;
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200751#ifdef OPTi93X
752 chip->codec = codec;
753#endif
Takashi Iwai99a0b762005-11-17 17:13:59 +0100754 if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 return error;
Takashi Iwai99a0b762005-11-17 17:13:59 +0100756 if ((error = snd_cs4231_mixer(codec)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 return error;
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200758#ifdef CS4231
Takashi Iwai99a0b762005-11-17 17:13:59 +0100759 if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 return error;
Krzysztof Helt9f240a52008-06-11 12:26:32 +0200761#else /* OPTI93X */
762 error = request_irq(chip->irq, snd_opti93x_interrupt,
763 IRQF_DISABLED, DEV_NAME" - WSS", codec);
764 if (error < 0) {
765 snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq);
766 return error;
767 }
768#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769#else
770 if ((error = snd_ad1848_create(card, chip->wss_base + 4,
771 chip->irq, chip->dma1,
Takashi Iwai99a0b762005-11-17 17:13:59 +0100772 AD1848_HW_DETECT, &codec)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 return error;
Takashi Iwai99a0b762005-11-17 17:13:59 +0100774 if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 return error;
Takashi Iwai99a0b762005-11-17 17:13:59 +0100776 if ((error = snd_ad1848_mixer(codec)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778#endif
779 strcpy(card->driver, chip->name);
780 sprintf(card->shortname, "OPTi %s", card->driver);
781#if defined(CS4231) || defined(OPTi93X)
782 sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
783 card->shortname, pcm->name, chip->wss_base + 4,
784 chip->irq, chip->dma1, chip->dma2);
785#else
786 sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
787 card->shortname, pcm->name, chip->wss_base + 4,
788 chip->irq, chip->dma1);
789#endif /* CS4231 || OPTi93X */
790
791 if (chip->mpu_port <= 0 || chip->mpu_port == SNDRV_AUTO_PORT)
792 rmidi = NULL;
793 else
794 if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
Thomas Gleixner65ca68b2006-07-01 19:29:46 -0700795 chip->mpu_port, 0, chip->mpu_irq, IRQF_DISABLED,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 &rmidi)))
Takashi Iwai99a0b762005-11-17 17:13:59 +0100797 snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
798 chip->mpu_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {
Takashi Iwai346c7a62005-11-17 14:37:56 +0100801 struct snd_opl3 *opl3 = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802#ifndef OPTi93X
803 if (chip->hardware == OPTi9XX_HW_82C928 ||
804 chip->hardware == OPTi9XX_HW_82C929 ||
805 chip->hardware == OPTi9XX_HW_82C924) {
Takashi Iwai346c7a62005-11-17 14:37:56 +0100806 struct snd_opl4 *opl4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 /* assume we have an OPL4 */
808 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
809 0x20, 0x20);
810 if (snd_opl4_create(card,
811 chip->fm_port,
812 chip->fm_port - 8,
813 2, &opl3, &opl4) < 0) {
814 /* no luck, use OPL3 instead */
815 snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
816 0x00, 0x20);
817 }
818 }
819#endif /* !OPTi93X */
820 if (!opl3 && snd_opl3_create(card,
821 chip->fm_port,
822 chip->fm_port + 2,
823 OPL3_HW_AUTO, 0, &opl3) < 0) {
Takashi Iwai99a0b762005-11-17 17:13:59 +0100824 snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 chip->fm_port, chip->fm_port + 4 - 1);
826 }
827 if (opl3) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828#ifdef CS4231
Takashi Iwai99a0b762005-11-17 17:13:59 +0100829 const int t1dev = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830#else
Takashi Iwai99a0b762005-11-17 17:13:59 +0100831 const int t1dev = 0;
832#endif
833 if ((error = snd_opl3_timer_new(opl3, t1dev, t1dev+1)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 return error;
Takashi Iwai99a0b762005-11-17 17:13:59 +0100835 if ((error = snd_opl3_hwdep_new(opl3, 0, 1, &synth)) < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 }
838 }
839
Takashi Iwai99a0b762005-11-17 17:13:59 +0100840 return snd_card_register(card);
841}
842
843static struct snd_card *snd_opti9xx_card_new(void)
844{
845 struct snd_card *card;
846
847 card = snd_card_new(index, id, THIS_MODULE, sizeof(struct snd_opti9xx));
848 if (! card)
849 return NULL;
850 card->private_free = snd_card_opti9xx_free;
851 return card;
852}
853
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100854static int __devinit snd_opti9xx_isa_match(struct device *devptr,
855 unsigned int dev)
856{
Takashi Iwai101f6f42007-06-20 12:03:09 +0200857#ifdef CONFIG_PNP
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100858 if (snd_opti9xx_pnp_is_probed)
859 return 0;
860 if (isapnp)
861 return 0;
Takashi Iwai101f6f42007-06-20 12:03:09 +0200862#endif
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100863 return 1;
864}
865
866static int __devinit snd_opti9xx_isa_probe(struct device *devptr,
867 unsigned int dev)
Takashi Iwai99a0b762005-11-17 17:13:59 +0100868{
869 struct snd_card *card;
870 int error;
871 static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
872#ifdef OPTi93X
873 static int possible_irqs[] = {5, 9, 10, 11, 7, -1};
874#else
875 static int possible_irqs[] = {9, 10, 11, 7, -1};
876#endif /* OPTi93X */
877 static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
878 static int possible_dma1s[] = {3, 1, 0, -1};
879#if defined(CS4231) || defined(OPTi93X)
880 static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
881#endif /* CS4231 || OPTi93X */
882
Takashi Iwai99a0b762005-11-17 17:13:59 +0100883 if (mpu_port == SNDRV_AUTO_PORT) {
884 if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
885 snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
886 return -EBUSY;
887 }
888 }
889 if (irq == SNDRV_AUTO_IRQ) {
890 if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
891 snd_printk(KERN_ERR "unable to find a free IRQ\n");
892 return -EBUSY;
893 }
894 }
895 if (mpu_irq == SNDRV_AUTO_IRQ) {
896 if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
897 snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
898 return -EBUSY;
899 }
900 }
901 if (dma1 == SNDRV_AUTO_DMA) {
902 if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
903 snd_printk(KERN_ERR "unable to find a free DMA1\n");
904 return -EBUSY;
905 }
906 }
907#if defined(CS4231) || defined(OPTi93X)
908 if (dma2 == SNDRV_AUTO_DMA) {
909 if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) {
910 snd_printk("unable to find a free DMA2\n");
911 return -EBUSY;
912 }
913 }
914#endif
915
916 card = snd_opti9xx_card_new();
917 if (! card)
918 return -ENOMEM;
919
920 if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 snd_card_free(card);
922 return error;
923 }
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100924 snd_card_set_dev(card, devptr);
Takashi Iwai99a0b762005-11-17 17:13:59 +0100925 if ((error = snd_opti9xx_probe(card)) < 0) {
926 snd_card_free(card);
927 return error;
928 }
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100929 dev_set_drvdata(devptr, card);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 return 0;
931}
932
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100933static int __devexit snd_opti9xx_isa_remove(struct device *devptr,
934 unsigned int dev)
Takashi Iwai99a0b762005-11-17 17:13:59 +0100935{
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100936 snd_card_free(dev_get_drvdata(devptr));
937 dev_set_drvdata(devptr, NULL);
Takashi Iwai99a0b762005-11-17 17:13:59 +0100938 return 0;
939}
940
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100941static struct isa_driver snd_opti9xx_driver = {
942 .match = snd_opti9xx_isa_match,
943 .probe = snd_opti9xx_isa_probe,
944 .remove = __devexit_p(snd_opti9xx_isa_remove),
Takashi Iwai99a0b762005-11-17 17:13:59 +0100945 /* FIXME: suspend/resume */
946 .driver = {
Rene Herman83c51c02007-03-20 11:33:46 +0100947 .name = DEV_NAME
Takashi Iwai99a0b762005-11-17 17:13:59 +0100948 },
949};
950
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951#ifdef CONFIG_PNP
Takashi Iwai5e24c1c2007-02-22 12:50:54 +0100952static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
953 const struct pnp_card_device_id *pid)
Takashi Iwai99a0b762005-11-17 17:13:59 +0100954{
955 struct snd_card *card;
956 int error, hw;
957 struct snd_opti9xx *chip;
958
959 if (snd_opti9xx_pnp_is_probed)
960 return -EBUSY;
961 if (! isapnp)
962 return -ENODEV;
963 card = snd_opti9xx_card_new();
964 if (! card)
965 return -ENOMEM;
966 chip = card->private_data;
967
968 hw = snd_card_opti9xx_pnp(chip, pcard, pid);
969 switch (hw) {
970 case 0x0924:
971 hw = OPTi9XX_HW_82C924;
972 break;
973 case 0x0925:
974 hw = OPTi9XX_HW_82C925;
975 break;
976 case 0x0931:
977 hw = OPTi9XX_HW_82C931;
978 break;
979 default:
980 snd_card_free(card);
981 return -ENODEV;
982 }
983
984 if ((error = snd_opti9xx_init(chip, hw))) {
985 snd_card_free(card);
986 return error;
987 }
988 if (hw <= OPTi9XX_HW_82C930)
989 chip->mc_base -= 0x80;
990 snd_card_set_dev(card, &pcard->card->dev);
991 if ((error = snd_opti9xx_probe(card)) < 0) {
992 snd_card_free(card);
993 return error;
994 }
995 pnp_set_card_drvdata(pcard, card);
996 snd_opti9xx_pnp_is_probed = 1;
997 return 0;
998}
999
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000static void __devexit snd_opti9xx_pnp_remove(struct pnp_card_link * pcard)
1001{
Takashi Iwai99a0b762005-11-17 17:13:59 +01001002 snd_card_free(pnp_get_card_drvdata(pcard));
1003 pnp_set_card_drvdata(pcard, NULL);
1004 snd_opti9xx_pnp_is_probed = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005}
1006
1007static struct pnp_card_driver opti9xx_pnpc_driver = {
1008 .flags = PNP_DRIVER_RES_DISABLE,
1009 .name = "opti9xx",
1010 .id_table = snd_opti9xx_pnpids,
Takashi Iwai99a0b762005-11-17 17:13:59 +01001011 .probe = snd_opti9xx_pnp_probe,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 .remove = __devexit_p(snd_opti9xx_pnp_remove),
1013};
1014#endif
1015
Takashi Iwai99a0b762005-11-17 17:13:59 +01001016#ifdef OPTi93X
1017#define CHIP_NAME "82C93x"
1018#else
1019#define CHIP_NAME "82C92x"
1020#endif
1021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022static int __init alsa_card_opti9xx_init(void)
1023{
Takashi Iwai0bbbc4c2006-02-27 17:23:46 +01001024#ifdef CONFIG_PNP
Takashi Iwai99a0b762005-11-17 17:13:59 +01001025 pnp_register_card_driver(&opti9xx_pnpc_driver);
1026 if (snd_opti9xx_pnp_is_probed)
1027 return 0;
Takashi Iwai101f6f42007-06-20 12:03:09 +02001028 pnp_unregister_card_driver(&opti9xx_pnpc_driver);
Takashi Iwai0bbbc4c2006-02-27 17:23:46 +01001029#endif
Takashi Iwai5e24c1c2007-02-22 12:50:54 +01001030 return isa_register_driver(&snd_opti9xx_driver, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031}
1032
1033static void __exit alsa_card_opti9xx_exit(void)
1034{
Clemens Ladischf7a92752005-12-07 09:13:42 +01001035 if (!snd_opti9xx_pnp_is_probed) {
Takashi Iwai5e24c1c2007-02-22 12:50:54 +01001036 isa_unregister_driver(&snd_opti9xx_driver);
1037 return;
Clemens Ladischf7a92752005-12-07 09:13:42 +01001038 }
Takashi Iwai0bbbc4c2006-02-27 17:23:46 +01001039#ifdef CONFIG_PNP
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 pnp_unregister_card_driver(&opti9xx_pnpc_driver);
Takashi Iwai0bbbc4c2006-02-27 17:23:46 +01001041#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042}
1043
1044module_init(alsa_card_opti9xx_init)
1045module_exit(alsa_card_opti9xx_exit)