opti92x-ad1848.c 30.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
    card-opti92x-ad1848.c - driver for OPTi 82c92x based soundcards.
    Copyright (C) 1998-2000 by Massimo Piccioni <dafastidio@libero.it>

    Part of this code was developed at the Italian Ministry of Air Defence,
    Sixth Division (oh, che pace ...), Rome.

    Thanks to Maria Grazia Pollarini, Salvatore Vassallo.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
*/


#include <linux/init.h>
27
#include <linux/err.h>
28
#include <linux/isa.h>
29
#include <linux/delay.h>
L
Linus Torvalds 已提交
30
#include <linux/pnp.h>
31
#include <linux/module.h>
32 33
#include <asm/io.h>
#include <asm/dma.h>
L
Linus Torvalds 已提交
34
#include <sound/core.h>
35
#include <sound/tlv.h>
36
#include <sound/wss.h>
L
Linus Torvalds 已提交
37 38 39 40 41
#include <sound/mpu401.h>
#include <sound/opl3.h>
#ifndef OPTi93X
#include <sound/opl4.h>
#endif
42
#define SNDRV_LEGACY_FIND_FREE_IOPORT
L
Linus Torvalds 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>

MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
MODULE_LICENSE("GPL");
#ifdef OPTi93X
MODULE_DESCRIPTION("OPTi93X");
MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}");
#else	/* OPTi93X */
#ifdef CS4231
MODULE_DESCRIPTION("OPTi92X - CS4231");
MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)},"
		"{OPTi,82C925 (CS4231)}}");
#else	/* CS4231 */
MODULE_DESCRIPTION("OPTi92X - AD1848");
MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)},"
		"{OPTi,82C925 (AD1848)},"
	        "{OAK,Mozart}}");
#endif	/* CS4231 */
#endif	/* OPTi93X */

static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */
static char *id = SNDRV_DEFAULT_STR1;		/* ID for this card */
67
//static bool enable = SNDRV_DEFAULT_ENABLE1;	/* Enable this card */
68
#ifdef CONFIG_PNP
69
static bool isapnp = true;			/* Enable ISA PnP detection */
70
#endif
L
Linus Torvalds 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
static long port = SNDRV_DEFAULT_PORT1; 	/* 0x530,0xe80,0xf40,0x604 */
static long mpu_port = SNDRV_DEFAULT_PORT1;	/* 0x300,0x310,0x320,0x330 */
static long fm_port = SNDRV_DEFAULT_PORT1;	/* 0x388 */
static int irq = SNDRV_DEFAULT_IRQ1;		/* 5,7,9,10,11 */
static int mpu_irq = SNDRV_DEFAULT_IRQ1;	/* 5,7,9,10 */
static int dma1 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
#if defined(CS4231) || defined(OPTi93X)
static int dma2 = SNDRV_DEFAULT_DMA1;		/* 0,1,3 */
#endif	/* CS4231 || OPTi93X */

module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
//module_param(enable, bool, 0444);
//MODULE_PARM_DESC(enable, "Enable opti9xx soundcard.");
87
#ifdef CONFIG_PNP
L
Linus Torvalds 已提交
88 89
module_param(isapnp, bool, 0444);
MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
90
#endif
L
Linus Torvalds 已提交
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
module_param(port, long, 0444);
MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
module_param(mpu_port, long, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
module_param(fm_port, long, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
module_param(irq, int, 0444);
MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
module_param(mpu_irq, int, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
module_param(dma1, int, 0444);
MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
#if defined(CS4231) || defined(OPTi93X)
module_param(dma2, int, 0444);
MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
#endif	/* CS4231 || OPTi93X */

#define OPTi9XX_HW_82C928	1
#define OPTi9XX_HW_82C929	2
#define OPTi9XX_HW_82C924	3
#define OPTi9XX_HW_82C925	4
#define OPTi9XX_HW_82C930	5
#define OPTi9XX_HW_82C931	6
#define OPTi9XX_HW_82C933	7
#define OPTi9XX_HW_LAST		OPTi9XX_HW_82C933

#define OPTi9XX_MC_REG(n)	n

#ifdef OPTi93X

#define OPTi93X_STATUS			0x02
#define OPTi93X_PORT(chip, r)		((chip)->port + OPTi93X_##r)

#define OPTi93X_IRQ_PLAYBACK		0x04
#define OPTi93X_IRQ_CAPTURE		0x08

#endif /* OPTi93X */

129
struct snd_opti9xx {
L
Linus Torvalds 已提交
130 131 132 133 134 135 136 137 138
	unsigned short hardware;
	unsigned char password;
	char name[7];

	unsigned long mc_base;
	struct resource *res_mc_base;
	unsigned long mc_base_size;
#ifdef OPTi93X
	unsigned long mc_indir_index;
139
	struct resource *res_mc_indir;
L
Linus Torvalds 已提交
140
#endif	/* OPTi93X */
141
	struct snd_wss *codec;
L
Linus Torvalds 已提交
142 143 144 145
	unsigned long pwd_reg;

	spinlock_t lock;

146
	long wss_base;
L
Linus Torvalds 已提交
147 148 149
	int irq;
};

150
static int snd_opti9xx_pnp_is_probed;
L
Linus Torvalds 已提交
151 152 153 154 155 156

#ifdef CONFIG_PNP

static struct pnp_card_device_id snd_opti9xx_pnpids[] = {
#ifndef OPTi93X
	/* OPTi 82C924 */
157 158 159
	{ .id = "OPT0924",
	  .devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } },
	  .driver_data = 0x0924 },
L
Linus Torvalds 已提交
160
	/* OPTi 82C925 */
161 162 163
	{ .id = "OPT0925",
	  .devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } },
	  .driver_data = 0x0925 },
L
Linus Torvalds 已提交
164 165
#else
	/* OPTi 82C931/3 */
166 167
	{ .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } },
	  .driver_data = 0x0931 },
L
Linus Torvalds 已提交
168 169 170 171 172 173 174 175
#endif	/* OPTi93X */
	{ .id = "" }
};

MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids);

#endif	/* CONFIG_PNP */

176
#define DEV_NAME KBUILD_MODNAME
L
Linus Torvalds 已提交
177 178

static char * snd_opti9xx_names[] = {
179
	"unknown",
L
Linus Torvalds 已提交
180 181 182 183 184
	"82C928",	"82C929",
	"82C924",	"82C925",
	"82C930",	"82C931",	"82C933"
};

185 186
static int snd_opti9xx_init(struct snd_opti9xx *chip,
			    unsigned short hardware)
L
Linus Torvalds 已提交
187 188 189 190 191 192 193 194 195 196
{
	static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};

	chip->hardware = hardware;
	strcpy(chip->name, snd_opti9xx_names[hardware]);

	spin_lock_init(&chip->lock);

	chip->irq = -1;

197 198 199 200 201
#ifndef OPTi93X
#ifdef CONFIG_PNP
	if (isapnp && chip->mc_base)
		/* PnP resource gives the least 10 bits */
		chip->mc_base |= 0xc00;
R
Randy Dunlap 已提交
202
	else
203
#endif	/* CONFIG_PNP */
R
Randy Dunlap 已提交
204
	{
205 206 207 208 209 210 211
		chip->mc_base = 0xf8c;
		chip->mc_base_size = opti9xx_mc_size[hardware];
	}
#else
		chip->mc_base_size = opti9xx_mc_size[hardware];
#endif

L
Linus Torvalds 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
	switch (hardware) {
#ifndef OPTi93X
	case OPTi9XX_HW_82C928:
	case OPTi9XX_HW_82C929:
		chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
		chip->pwd_reg = 3;
		break;

	case OPTi9XX_HW_82C924:
	case OPTi9XX_HW_82C925:
		chip->password = 0xe5;
		chip->pwd_reg = 3;
		break;
#else	/* OPTi93X */

	case OPTi9XX_HW_82C930:
	case OPTi9XX_HW_82C931:
	case OPTi9XX_HW_82C933:
		chip->mc_base = (hardware == OPTi9XX_HW_82C930) ? 0xf8f : 0xf8d;
231
		if (!chip->mc_indir_index)
232
			chip->mc_indir_index = 0xe0e;
L
Linus Torvalds 已提交
233 234 235 236 237 238
		chip->password = 0xe4;
		chip->pwd_reg = 0;
		break;
#endif	/* OPTi93X */

	default:
239
		snd_printk(KERN_ERR "chip %d not supported\n", hardware);
L
Linus Torvalds 已提交
240 241 242 243 244
		return -ENODEV;
	}
	return 0;
}

245
static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
L
Linus Torvalds 已提交
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
				      unsigned char reg)
{
	unsigned long flags;
	unsigned char retval = 0xff;

	spin_lock_irqsave(&chip->lock, flags);
	outb(chip->password, chip->mc_base + chip->pwd_reg);

	switch (chip->hardware) {
#ifndef OPTi93X
	case OPTi9XX_HW_82C924:
	case OPTi9XX_HW_82C925:
		if (reg > 7) {
			outb(reg, chip->mc_base + 8);
			outb(chip->password, chip->mc_base + chip->pwd_reg);
			retval = inb(chip->mc_base + 9);
			break;
		}

	case OPTi9XX_HW_82C928:
	case OPTi9XX_HW_82C929:
		retval = inb(chip->mc_base + reg);
		break;
#else	/* OPTi93X */

	case OPTi9XX_HW_82C930:
	case OPTi9XX_HW_82C931:
	case OPTi9XX_HW_82C933:
		outb(reg, chip->mc_indir_index);
		outb(chip->password, chip->mc_base + chip->pwd_reg);
		retval = inb(chip->mc_indir_index + 1);
		break;
#endif	/* OPTi93X */

	default:
281
		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
L
Linus Torvalds 已提交
282 283 284 285 286
	}

	spin_unlock_irqrestore(&chip->lock, flags);
	return retval;
}
287

288
static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
L
Linus Torvalds 已提交
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
			      unsigned char value)
{
	unsigned long flags;

	spin_lock_irqsave(&chip->lock, flags);
	outb(chip->password, chip->mc_base + chip->pwd_reg);

	switch (chip->hardware) {
#ifndef OPTi93X
	case OPTi9XX_HW_82C924:
	case OPTi9XX_HW_82C925:
		if (reg > 7) {
			outb(reg, chip->mc_base + 8);
			outb(chip->password, chip->mc_base + chip->pwd_reg);
			outb(value, chip->mc_base + 9);
			break;
		}

	case OPTi9XX_HW_82C928:
	case OPTi9XX_HW_82C929:
		outb(value, chip->mc_base + reg);
		break;
#else	/* OPTi93X */

	case OPTi9XX_HW_82C930:
	case OPTi9XX_HW_82C931:
	case OPTi9XX_HW_82C933:
		outb(reg, chip->mc_indir_index);
		outb(chip->password, chip->mc_base + chip->pwd_reg);
		outb(value, chip->mc_indir_index + 1);
		break;
#endif	/* OPTi93X */

	default:
323
		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
L
Linus Torvalds 已提交
324 325 326 327 328 329 330 331 332 333 334
	}

	spin_unlock_irqrestore(&chip->lock, flags);
}


#define snd_opti9xx_write_mask(chip, reg, value, mask)	\
	snd_opti9xx_write(chip, reg,			\
		(snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask)))


335
static int snd_opti9xx_configure(struct snd_opti9xx *chip,
336
					   long port,
337 338
					   int irq, int dma1, int dma2,
					   long mpu_port, int mpu_irq)
L
Linus Torvalds 已提交
339 340 341 342 343 344 345 346 347 348
{
	unsigned char wss_base_bits;
	unsigned char irq_bits;
	unsigned char dma_bits;
	unsigned char mpu_port_bits = 0;
	unsigned char mpu_irq_bits;

	switch (chip->hardware) {
#ifndef OPTi93X
	case OPTi9XX_HW_82C924:
349
		/* opti 929 mode (?), OPL3 clock output, audio enable */
L
Linus Torvalds 已提交
350
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
351
		/* enable wave audio */
L
Linus Torvalds 已提交
352 353 354
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);

	case OPTi9XX_HW_82C925:
355
		/* enable WSS mode */
L
Linus Torvalds 已提交
356
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
357
		/* OPL3 FM synthesis */
L
Linus Torvalds 已提交
358
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
359
		/* disable Sound Blaster IRQ and DMA */
L
Linus Torvalds 已提交
360 361
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
#ifdef CS4231
362
		/* cs4231/4248 fix enabled */
L
Linus Torvalds 已提交
363 364
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
#else
365
		/* cs4231/4248 fix disabled */
L
Linus Torvalds 已提交
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
#endif	/* CS4231 */
		break;

	case OPTi9XX_HW_82C928:
	case OPTi9XX_HW_82C929:
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
		/*
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xa2, 0xae);
		*/
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x00, 0x0c);
#ifdef CS4231
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
#else
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
#endif	/* CS4231 */
		break;

#else	/* OPTi93X */
	case OPTi9XX_HW_82C931:
387 388 389
		/* disable 3D sound (set GPIO1 as output, low) */
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c);
	case OPTi9XX_HW_82C933: /* FALL THROUGH */
390
		/*
391 392 393 394 395 396 397 398 399
		 * The BTC 1817DW has QS1000 wavetable which is connected
		 * to the serial digital input of the OPTI931.
		 */
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(21), 0x82, 0xff);
		/* 
		 * This bit sets OPTI931 to automaticaly select FM
		 * or digital input signal.
		 */
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01);
400 401 402 403 404 405 406
	case OPTi9XX_HW_82C930: /* FALL THROUGH */
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03);
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff);
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 |
			(chip->hardware == OPTi9XX_HW_82C930 ? 0x00 : 0x04),
			0x34);
		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x20, 0xbf);
L
Linus Torvalds 已提交
407 408 409 410
		break;
#endif	/* OPTi93X */

	default:
411
		snd_printk(KERN_ERR "chip %d not supported\n", chip->hardware);
L
Linus Torvalds 已提交
412 413 414
		return -EINVAL;
	}

415 416 417 418
	/* PnP resource says it decodes only 10 bits of address */
	switch (port & 0x3ff) {
	case 0x130:
		chip->wss_base = 0x530;
L
Linus Torvalds 已提交
419 420
		wss_base_bits = 0x00;
		break;
421 422
	case 0x204:
		chip->wss_base = 0x604;
L
Linus Torvalds 已提交
423 424
		wss_base_bits = 0x03;
		break;
425 426
	case 0x280:
		chip->wss_base = 0xe80;
L
Linus Torvalds 已提交
427 428
		wss_base_bits = 0x01;
		break;
429 430
	case 0x340:
		chip->wss_base = 0xf40;
L
Linus Torvalds 已提交
431 432 433
		wss_base_bits = 0x02;
		break;
	default:
434
		snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port);
L
Linus Torvalds 已提交
435 436 437 438 439
		goto __skip_base;
	}
	snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);

__skip_base:
440
	switch (irq) {
L
Linus Torvalds 已提交
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
//#ifdef OPTi93X
	case 5:
		irq_bits = 0x05;
		break;
//#endif	/* OPTi93X */
	case 7:
		irq_bits = 0x01;
		break;
	case 9:
		irq_bits = 0x02;
		break;
	case 10:
		irq_bits = 0x03;
		break;
	case 11:
		irq_bits = 0x04;
		break;
	default:
459
		snd_printk(KERN_WARNING "WSS irq # %d not valid\n", irq);
L
Linus Torvalds 已提交
460 461 462
		goto __skip_resources;
	}

463
	switch (dma1) {
L
Linus Torvalds 已提交
464 465 466 467 468 469 470 471 472 473
	case 0:
		dma_bits = 0x01;
		break;
	case 1:
		dma_bits = 0x02;
		break;
	case 3:
		dma_bits = 0x03;
		break;
	default:
474
		snd_printk(KERN_WARNING "WSS dma1 # %d not valid\n", dma1);
L
Linus Torvalds 已提交
475 476 477 478
		goto __skip_resources;
	}

#if defined(CS4231) || defined(OPTi93X)
479
	if (dma1 == dma2) {
480
		snd_printk(KERN_ERR "don't want to share dmas\n");
L
Linus Torvalds 已提交
481 482 483
		return -EBUSY;
	}

484
	switch (dma2) {
L
Linus Torvalds 已提交
485 486 487 488
	case 0:
	case 1:
		break;
	default:
489
		snd_printk(KERN_WARNING "WSS dma2 # %d not valid\n", dma2);
L
Linus Torvalds 已提交
490 491 492 493 494 495
		goto __skip_resources;
	}
	dma_bits |= 0x04;
#endif	/* CS4231 || OPTi93X */

#ifndef OPTi93X
496
	 outb(irq_bits << 3 | dma_bits, chip->wss_base);
L
Linus Torvalds 已提交
497 498 499 500 501 502
#else /* OPTi93X */
	snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
#endif /* OPTi93X */

__skip_resources:
	if (chip->hardware > OPTi9XX_HW_82C928) {
503
		switch (mpu_port) {
L
Linus Torvalds 已提交
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
		case 0:
		case -1:
			break;
		case 0x300:
			mpu_port_bits = 0x03;
			break;
		case 0x310:
			mpu_port_bits = 0x02;
			break;
		case 0x320:
			mpu_port_bits = 0x01;
			break;
		case 0x330:
			mpu_port_bits = 0x00;
			break;
		default:
520
			snd_printk(KERN_WARNING
521
				   "MPU-401 port 0x%lx not valid\n", mpu_port);
L
Linus Torvalds 已提交
522 523 524
			goto __skip_mpu;
		}

525
		switch (mpu_irq) {
L
Linus Torvalds 已提交
526 527 528 529 530 531 532 533 534 535 536 537 538
		case 5:
			mpu_irq_bits = 0x02;
			break;
		case 7:
			mpu_irq_bits = 0x03;
			break;
		case 9:
			mpu_irq_bits = 0x00;
			break;
		case 10:
			mpu_irq_bits = 0x01;
			break;
		default:
539
			snd_printk(KERN_WARNING "MPU-401 irq # %d not valid\n",
540
				mpu_irq);
L
Linus Torvalds 已提交
541 542 543 544
			goto __skip_mpu;
		}

		snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6),
545
			(mpu_port <= 0) ? 0x00 :
L
Linus Torvalds 已提交
546 547 548 549 550 551 552 553 554 555
				0x80 | mpu_port_bits << 5 | mpu_irq_bits << 3,
			0xf8);
	}
__skip_mpu:

	return 0;
}

#ifdef OPTi93X

556 557 558
static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0);
559 560 561 562 563 564

static struct snd_kcontrol_new snd_opti93x_controls[] = {
WSS_DOUBLE("Master Playback Switch", 0,
		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
WSS_DOUBLE_TLV("Master Playback Volume", 0,
		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1,
565 566 567 568 569 570 571
		db_scale_5bit_3db_step),
WSS_DOUBLE_TLV("PCM Playback Volume", 0,
		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1,
		db_scale_5bit),
WSS_DOUBLE_TLV("FM Playback Volume", 0,
		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1,
		db_scale_4bit_12db_max),
572 573
WSS_DOUBLE("Line Playback Switch", 0,
		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
574 575 576
WSS_DOUBLE_TLV("Line Playback Volume", 0,
		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1,
		db_scale_4bit_12db_max),
577 578
WSS_DOUBLE("Mic Playback Switch", 0,
		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
579 580 581 582 583 584
WSS_DOUBLE_TLV("Mic Playback Volume", 0,
		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1,
		db_scale_4bit_12db_max),
WSS_DOUBLE_TLV("CD Playback Volume", 0,
		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1,
		db_scale_4bit_12db_max),
585 586
WSS_DOUBLE("Aux Playback Switch", 0,
		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
587 588 589
WSS_DOUBLE_TLV("Aux Playback Volume", 0,
		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1,
		db_scale_4bit_12db_max),
590 591
};

592
static int snd_opti93x_mixer(struct snd_wss *chip)
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
{
	struct snd_card *card;
	unsigned int idx;
	struct snd_ctl_elem_id id1, id2;
	int err;

	if (snd_BUG_ON(!chip || !chip->pcm))
		return -EINVAL;

	card = chip->card;

	strcpy(card->mixername, chip->pcm->name);

	memset(&id1, 0, sizeof(id1));
	memset(&id2, 0, sizeof(id2));
	id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
	/* reassign AUX0 switch to CD */
	strcpy(id1.name, "Aux Playback Switch");
	strcpy(id2.name, "CD Playback Switch");
	err = snd_ctl_rename_id(card, &id1, &id2);
	if (err < 0) {
		snd_printk(KERN_ERR "Cannot rename opti93x control\n");
		return err;
	}
	/* reassign AUX1 switch to FM */
	strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
	strcpy(id2.name, "FM Playback Switch");
	err = snd_ctl_rename_id(card, &id1, &id2);
	if (err < 0) {
		snd_printk(KERN_ERR "Cannot rename opti93x control\n");
		return err;
	}
	/* remove AUX1 volume */
	strcpy(id1.name, "Aux Playback Volume"); id1.index = 1;
	snd_ctl_remove_id(card, &id1);

	/* Replace WSS volume controls with OPTi93x volume controls */
	id1.index = 0;
	for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
		strcpy(id1.name, snd_opti93x_controls[idx].name);
		snd_ctl_remove_id(card, &id1);

		err = snd_ctl_add(card,
				snd_ctl_new1(&snd_opti93x_controls[idx], chip));
		if (err < 0)
			return err;
	}
	return 0;
}

643
static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
644
{
645 646
	struct snd_opti9xx *chip = dev_id;
	struct snd_wss *codec = chip->codec;
L
Linus Torvalds 已提交
647 648
	unsigned char status;

649 650 651
	if (!codec)
		return IRQ_HANDLED;

K
Krzysztof Helt 已提交
652
	status = snd_opti9xx_read(chip, OPTi9XX_MC_REG(11));
L
Linus Torvalds 已提交
653 654 655
	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)
		snd_pcm_period_elapsed(codec->playback_substream);
	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) {
656
		snd_wss_overrange(codec);
L
Linus Torvalds 已提交
657 658 659 660 661 662 663 664
		snd_pcm_period_elapsed(codec->capture_substream);
	}
	outb(0x00, OPTi93X_PORT(codec, STATUS));
	return IRQ_HANDLED;
}

#endif /* OPTi93X */

665
static int snd_opti9xx_read_check(struct snd_opti9xx *chip)
L
Linus Torvalds 已提交
666
{
667 668 669 670
	unsigned char value;
#ifdef OPTi93X
	unsigned long flags;
#endif
L
Linus Torvalds 已提交
671

672 673 674 675
	chip->res_mc_base = request_region(chip->mc_base, chip->mc_base_size,
					   "OPTi9xx MC");
	if (chip->res_mc_base == NULL)
		return -EBUSY;
L
Linus Torvalds 已提交
676
#ifndef OPTi93X
677 678 679 680 681
	value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
	if (value != 0xff && value != inb(chip->mc_base + OPTi9XX_MC_REG(1)))
		if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
			return 0;
#else	/* OPTi93X */
682
	chip->res_mc_indir = request_region(chip->mc_indir_index, 2,
683 684 685
					    "OPTi93x MC");
	if (chip->res_mc_indir == NULL)
		return -EBUSY;
L
Linus Torvalds 已提交
686

687 688 689 690
	spin_lock_irqsave(&chip->lock, flags);
	outb(chip->password, chip->mc_base + chip->pwd_reg);
	outb(((chip->mc_indir_index & 0x1f0) >> 4), chip->mc_base);
	spin_unlock_irqrestore(&chip->lock, flags);
L
Linus Torvalds 已提交
691

692 693 694 695
	value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(7));
	snd_opti9xx_write(chip, OPTi9XX_MC_REG(7), 0xff - value);
	if (snd_opti9xx_read(chip, OPTi9XX_MC_REG(7)) == 0xff - value)
		return 0;
L
Linus Torvalds 已提交
696

697 698 699 700 701
	release_and_free_resource(chip->res_mc_indir);
	chip->res_mc_indir = NULL;
#endif	/* OPTi93X */
	release_and_free_resource(chip->res_mc_base);
	chip->res_mc_base = NULL;
L
Linus Torvalds 已提交
702

703 704
	return -ENODEV;
}
L
Linus Torvalds 已提交
705

706 707
static int snd_card_opti9xx_detect(struct snd_card *card,
				   struct snd_opti9xx *chip)
708 709
{
	int i, err;
L
Linus Torvalds 已提交
710

711 712 713 714 715 716 717
#ifndef OPTi93X
	for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
#else
	for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
#endif
		err = snd_opti9xx_init(chip, i);
		if (err < 0)
L
Linus Torvalds 已提交
718 719
			return err;

720 721
		err = snd_opti9xx_read_check(chip);
		if (err == 0)
L
Linus Torvalds 已提交
722
			return 1;
723 724 725
#ifdef OPTi93X
		chip->mc_indir_index = 0;
#endif
L
Linus Torvalds 已提交
726 727 728 729 730
	}
	return -ENODEV;
}

#ifdef CONFIG_PNP
731 732 733
static int snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
				struct pnp_card_link *card,
				const struct pnp_card_device_id *pid)
L
Linus Torvalds 已提交
734 735 736
{
	struct pnp_dev *pdev;
	int err;
737 738 739 740
	struct pnp_dev *devmpu;
#ifndef OPTi93X
	struct pnp_dev *devmc;
#endif
L
Linus Torvalds 已提交
741

742 743
	pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
	if (pdev == NULL)
L
Linus Torvalds 已提交
744
		return -EBUSY;
745

L
Linus Torvalds 已提交
746 747 748 749 750 751 752 753
	err = pnp_activate_dev(pdev);
	if (err < 0) {
		snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
		return err;
	}

#ifdef OPTi93X
	port = pnp_port_start(pdev, 0) - 4;
754
	fm_port = pnp_port_start(pdev, 1) + 8;
755 756 757
	/* adjust mc_indir_index - some cards report it at 0xe?d,
	   other at 0xe?c but it really is always at 0xe?e */
	chip->mc_indir_index = (pnp_port_start(pdev, 3) & ~0xf) | 0xe;
L
Linus Torvalds 已提交
758
#else
759 760 761 762 763 764 765 766 767 768 769
	devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
	if (devmc == NULL)
		return -EBUSY;

	err = pnp_activate_dev(devmc);
	if (err < 0) {
		snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err);
		return err;
	}

	port = pnp_port_start(pdev, 1);
770
	fm_port = pnp_port_start(pdev, 2) + 8;
771 772 773 774 775 776
	/*
	 * The MC(0) is never accessed and card does not
	 * include it in the PnP resource range. OPTI93x include it.
	 */
	chip->mc_base = pnp_port_start(devmc, 0) - 1;
	chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
L
Linus Torvalds 已提交
777 778 779 780 781 782 783
#endif	/* OPTi93X */
	irq = pnp_irq(pdev, 0);
	dma1 = pnp_dma(pdev, 0);
#if defined(CS4231) || defined(OPTi93X)
	dma2 = pnp_dma(pdev, 1);
#endif	/* CS4231 || OPTi93X */

784 785 786 787
	devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);

	if (devmpu && mpu_port > 0) {
		err = pnp_activate_dev(devmpu);
L
Linus Torvalds 已提交
788
		if (err < 0) {
789
			snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
L
Linus Torvalds 已提交
790 791
			mpu_port = -1;
		} else {
792 793
			mpu_port = pnp_port_start(devmpu, 0);
			mpu_irq = pnp_irq(devmpu, 0);
L
Linus Torvalds 已提交
794 795 796 797 798 799
		}
	}
	return pid->driver_data;
}
#endif	/* CONFIG_PNP */

800
static void snd_card_opti9xx_free(struct snd_card *card)
L
Linus Torvalds 已提交
801
{
802
	struct snd_opti9xx *chip = card->private_data;
803

K
Krzysztof Helt 已提交
804 805
	if (chip) {
#ifdef OPTi93X
806 807 808
		if (chip->irq > 0) {
			disable_irq(chip->irq);
			free_irq(chip->irq, chip);
K
Krzysztof Helt 已提交
809
		}
810
		release_and_free_resource(chip->res_mc_indir);
K
Krzysztof Helt 已提交
811
#endif
812
		release_and_free_resource(chip->res_mc_base);
K
Krzysztof Helt 已提交
813
	}
L
Linus Torvalds 已提交
814 815
}

816
static int snd_opti9xx_probe(struct snd_card *card)
L
Linus Torvalds 已提交
817 818 819
{
	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
	int error;
820
	int xdma2;
821
	struct snd_opti9xx *chip = card->private_data;
822
	struct snd_wss *codec;
K
Krzysztof Helt 已提交
823
#ifdef CS4231
824
	struct snd_timer *timer;
L
Linus Torvalds 已提交
825
#endif
826 827 828
	struct snd_pcm *pcm;
	struct snd_rawmidi *rmidi;
	struct snd_hwdep *synth;
L
Linus Torvalds 已提交
829 830

#if defined(CS4231) || defined(OPTi93X)
831
	xdma2 = dma2;
832
#else
833
	xdma2 = -1;
L
Linus Torvalds 已提交
834 835
#endif

836 837 838
	if (port == SNDRV_AUTO_PORT) {
		port = snd_legacy_find_free_ioport(possible_ports, 4);
		if (port < 0) {
839
			snd_printk(KERN_ERR "unable to find a free WSS port\n");
L
Linus Torvalds 已提交
840 841 842
			return -EBUSY;
		}
	}
843 844
	error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
				      mpu_port, mpu_irq);
845
	if (error)
L
Linus Torvalds 已提交
846 847
		return error;

848
	error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2,
849
#ifdef OPTi93X
850
			       WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
851 852
#else
			       WSS_HW_DETECT, 0,
K
Krzysztof Helt 已提交
853
#endif
854 855
			       &codec);
	if (error < 0)
L
Linus Torvalds 已提交
856
		return error;
K
Krzysztof Helt 已提交
857
	chip->codec = codec;
858
	error = snd_wss_pcm(codec, 0, &pcm);
859 860
	if (error < 0)
		return error;
861 862
	error = snd_wss_mixer(codec);
	if (error < 0)
L
Linus Torvalds 已提交
863
		return error;
864 865 866 867 868
#ifdef OPTi93X
	error = snd_opti93x_mixer(codec);
	if (error < 0)
		return error;
#endif
K
Krzysztof Helt 已提交
869
#ifdef CS4231
870 871
	error = snd_wss_timer(codec, 0, &timer);
	if (error < 0)
L
Linus Torvalds 已提交
872
		return error;
873 874
#endif
#ifdef OPTi93X
875
	error = request_irq(irq, snd_opti93x_interrupt,
Y
Yong Zhang 已提交
876
			    0, DEV_NAME" - WSS", chip);
K
Krzysztof Helt 已提交
877
	if (error < 0) {
878
		snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq);
K
Krzysztof Helt 已提交
879 880
		return error;
	}
L
Linus Torvalds 已提交
881
#endif
882
	chip->irq = irq;
L
Linus Torvalds 已提交
883 884 885 886
	strcpy(card->driver, chip->name);
	sprintf(card->shortname, "OPTi %s", card->driver);
#if defined(CS4231) || defined(OPTi93X)
	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
887 888
		card->shortname, pcm->name,
		chip->wss_base + 4, irq, dma1, xdma2);
L
Linus Torvalds 已提交
889 890
#else
	sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
891
		card->shortname, pcm->name, chip->wss_base + 4, irq, dma1);
L
Linus Torvalds 已提交
892 893
#endif	/* CS4231 || OPTi93X */

894
	if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
L
Linus Torvalds 已提交
895
		rmidi = NULL;
896 897
	else {
		error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
898
				mpu_port, 0, mpu_irq, &rmidi);
899
		if (error)
900
			snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
901 902
				   mpu_port);
	}
L
Linus Torvalds 已提交
903

904
	if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
905
		struct snd_opl3 *opl3 = NULL;
L
Linus Torvalds 已提交
906 907 908 909
#ifndef OPTi93X
		if (chip->hardware == OPTi9XX_HW_82C928 ||
		    chip->hardware == OPTi9XX_HW_82C929 ||
		    chip->hardware == OPTi9XX_HW_82C924) {
910
			struct snd_opl4 *opl4;
L
Linus Torvalds 已提交
911 912 913
			/* assume we have an OPL4 */
			snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
					       0x20, 0x20);
914
			if (snd_opl4_create(card, fm_port, fm_port - 8,
L
Linus Torvalds 已提交
915 916 917 918 919 920 921
					    2, &opl3, &opl4) < 0) {
				/* no luck, use OPL3 instead */
				snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2),
						       0x00, 0x20);
			}
		}
#endif	/* !OPTi93X */
922
		if (!opl3 && snd_opl3_create(card, fm_port, fm_port + 2,
L
Linus Torvalds 已提交
923
					     OPL3_HW_AUTO, 0, &opl3) < 0) {
924
			snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
925
				   fm_port, fm_port + 4 - 1);
L
Linus Torvalds 已提交
926 927
		}
		if (opl3) {
928 929
			error = snd_opl3_hwdep_new(opl3, 0, 1, &synth);
			if (error < 0)
L
Linus Torvalds 已提交
930 931 932 933
				return error;
		}
	}

934 935 936
	return snd_card_register(card);
}

937
static int snd_opti9xx_card_new(struct snd_card **cardp)
938 939
{
	struct snd_card *card;
940
	int err;
941

942 943 944
	err = snd_card_create(index, id, THIS_MODULE,
			      sizeof(struct snd_opti9xx), &card);
	if (err < 0)
945
		return err;
946
	card->private_free = snd_card_opti9xx_free;
947 948
	*cardp = card;
	return 0;
949 950
}

951 952
static int snd_opti9xx_isa_match(struct device *devptr,
				 unsigned int dev)
953
{
954
#ifdef CONFIG_PNP
955 956 957 958
	if (snd_opti9xx_pnp_is_probed)
		return 0;
	if (isapnp)
		return 0;
959
#endif
960 961 962
	return 1;
}

963 964
static int snd_opti9xx_isa_probe(struct device *devptr,
				 unsigned int dev)
965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
{
	struct snd_card *card;
	int error;
	static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
#ifdef OPTi93X
	static int possible_irqs[] = {5, 9, 10, 11, 7, -1};
#else
	static int possible_irqs[] = {9, 10, 11, 7, -1};
#endif	/* OPTi93X */
	static int possible_mpu_irqs[] = {5, 9, 10, 7, -1};
	static int possible_dma1s[] = {3, 1, 0, -1};
#if defined(CS4231) || defined(OPTi93X)
	static int possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
#endif	/* CS4231 || OPTi93X */

	if (mpu_port == SNDRV_AUTO_PORT) {
		if ((mpu_port = snd_legacy_find_free_ioport(possible_mpu_ports, 2)) < 0) {
			snd_printk(KERN_ERR "unable to find a free MPU401 port\n");
			return -EBUSY;
		}
	}
	if (irq == SNDRV_AUTO_IRQ) {
		if ((irq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
			snd_printk(KERN_ERR "unable to find a free IRQ\n");
			return -EBUSY;
		}
	}
	if (mpu_irq == SNDRV_AUTO_IRQ) {
		if ((mpu_irq = snd_legacy_find_free_irq(possible_mpu_irqs)) < 0) {
			snd_printk(KERN_ERR "unable to find a free MPU401 IRQ\n");
			return -EBUSY;
		}
	}
	if (dma1 == SNDRV_AUTO_DMA) {
		if ((dma1 = snd_legacy_find_free_dma(possible_dma1s)) < 0) {
			snd_printk(KERN_ERR "unable to find a free DMA1\n");
			return -EBUSY;
		}
	}
#if defined(CS4231) || defined(OPTi93X)
	if (dma2 == SNDRV_AUTO_DMA) {
		if ((dma2 = snd_legacy_find_free_dma(possible_dma2s[dma1 % 4])) < 0) {
1007
			snd_printk(KERN_ERR "unable to find a free DMA2\n");
1008 1009 1010 1011 1012
			return -EBUSY;
		}
	}
#endif

1013 1014 1015
	error = snd_opti9xx_card_new(&card);
	if (error < 0)
		return error;
1016 1017

	if ((error = snd_card_opti9xx_detect(card, card->private_data)) < 0) {
L
Linus Torvalds 已提交
1018 1019 1020
		snd_card_free(card);
		return error;
	}
1021
	snd_card_set_dev(card, devptr);
1022 1023 1024 1025
	if ((error = snd_opti9xx_probe(card)) < 0) {
		snd_card_free(card);
		return error;
	}
1026
	dev_set_drvdata(devptr, card);
1027 1028 1029
	return 0;
}

1030 1031
static int snd_opti9xx_isa_remove(struct device *devptr,
				  unsigned int dev)
1032
{
1033
	snd_card_free(dev_get_drvdata(devptr));
L
Linus Torvalds 已提交
1034 1035 1036
	return 0;
}

1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
#ifdef CONFIG_PM
static int snd_opti9xx_suspend(struct snd_card *card)
{
	struct snd_opti9xx *chip = card->private_data;

	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
	chip->codec->suspend(chip->codec);
	return 0;
}

static int snd_opti9xx_resume(struct snd_card *card)
{
	struct snd_opti9xx *chip = card->private_data;
	int error, xdma2;
#if defined(CS4231) || defined(OPTi93X)
	xdma2 = dma2;
#else
	xdma2 = -1;
#endif

	error = snd_opti9xx_configure(chip, port, irq, dma1, xdma2,
				      mpu_port, mpu_irq);
	if (error)
		return error;
	chip->codec->resume(chip->codec);
	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
	return 0;
}

static int snd_opti9xx_isa_suspend(struct device *dev, unsigned int n,
				   pm_message_t state)
{
	return snd_opti9xx_suspend(dev_get_drvdata(dev));
}

static int snd_opti9xx_isa_resume(struct device *dev, unsigned int n)
{
	return snd_opti9xx_resume(dev_get_drvdata(dev));
}
#endif

1078 1079 1080
static struct isa_driver snd_opti9xx_driver = {
	.match		= snd_opti9xx_isa_match,
	.probe		= snd_opti9xx_isa_probe,
1081
	.remove		= snd_opti9xx_isa_remove,
1082 1083 1084 1085
#ifdef CONFIG_PM
	.suspend	= snd_opti9xx_isa_suspend,
	.resume		= snd_opti9xx_isa_resume,
#endif
1086
	.driver		= {
1087
		.name	= DEV_NAME
1088 1089 1090
	},
};

L
Linus Torvalds 已提交
1091
#ifdef CONFIG_PNP
1092 1093
static int snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
				 const struct pnp_card_device_id *pid)
L
Linus Torvalds 已提交
1094
{
1095 1096 1097 1098 1099 1100 1101 1102
	struct snd_card *card;
	int error, hw;
	struct snd_opti9xx *chip;

	if (snd_opti9xx_pnp_is_probed)
		return -EBUSY;
	if (! isapnp)
		return -ENODEV;
1103 1104 1105
	error = snd_opti9xx_card_new(&card);
	if (error < 0)
		return error;
1106
	chip = card->private_data;
L
Linus Torvalds 已提交
1107

1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
	hw = snd_card_opti9xx_pnp(chip, pcard, pid);
	switch (hw) {
	case 0x0924:
		hw = OPTi9XX_HW_82C924;
		break;
	case 0x0925:
		hw = OPTi9XX_HW_82C925;
		break;
	case 0x0931:
		hw = OPTi9XX_HW_82C931;
		break;
	default:
		snd_card_free(card);
		return -ENODEV;
	}

	if ((error = snd_opti9xx_init(chip, hw))) {
		snd_card_free(card);
		return error;
	}
1128 1129 1130 1131 1132 1133
	error = snd_opti9xx_read_check(chip);
	if (error) {
		snd_printk(KERN_ERR "OPTI chip not found\n");
		snd_card_free(card);
		return error;
	}
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
	snd_card_set_dev(card, &pcard->card->dev);
	if ((error = snd_opti9xx_probe(card)) < 0) {
		snd_card_free(card);
		return error;
	}
	pnp_set_card_drvdata(pcard, card);
	snd_opti9xx_pnp_is_probed = 1;
	return 0;
}

1144
static void snd_opti9xx_pnp_remove(struct pnp_card_link *pcard)
1145 1146 1147 1148
{
	snd_card_free(pnp_get_card_drvdata(pcard));
	pnp_set_card_drvdata(pcard, NULL);
	snd_opti9xx_pnp_is_probed = 0;
L
Linus Torvalds 已提交
1149 1150
}

1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163
#ifdef CONFIG_PM
static int snd_opti9xx_pnp_suspend(struct pnp_card_link *pcard,
				   pm_message_t state)
{
	return snd_opti9xx_suspend(pnp_get_card_drvdata(pcard));
}

static int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard)
{
	return snd_opti9xx_resume(pnp_get_card_drvdata(pcard));
}
#endif

L
Linus Torvalds 已提交
1164 1165
static struct pnp_card_driver opti9xx_pnpc_driver = {
	.flags		= PNP_DRIVER_RES_DISABLE,
1166
	.name		= DEV_NAME,
L
Linus Torvalds 已提交
1167
	.id_table	= snd_opti9xx_pnpids,
1168
	.probe		= snd_opti9xx_pnp_probe,
1169
	.remove		= snd_opti9xx_pnp_remove,
1170 1171 1172 1173
#ifdef CONFIG_PM
	.suspend	= snd_opti9xx_pnp_suspend,
	.resume		= snd_opti9xx_pnp_resume,
#endif
L
Linus Torvalds 已提交
1174 1175 1176 1177
};
#endif

#ifdef OPTi93X
1178
#define CHIP_NAME	"82C93x"
L
Linus Torvalds 已提交
1179
#else
1180
#define CHIP_NAME	"82C92x"
L
Linus Torvalds 已提交
1181
#endif
1182 1183 1184

static int __init alsa_card_opti9xx_init(void)
{
1185
#ifdef CONFIG_PNP
1186 1187 1188
	pnp_register_card_driver(&opti9xx_pnpc_driver);
	if (snd_opti9xx_pnp_is_probed)
		return 0;
1189
	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
1190
#endif
1191
	return isa_register_driver(&snd_opti9xx_driver, 1);
L
Linus Torvalds 已提交
1192 1193 1194 1195
}

static void __exit alsa_card_opti9xx_exit(void)
{
1196
	if (!snd_opti9xx_pnp_is_probed) {
1197 1198
		isa_unregister_driver(&snd_opti9xx_driver);
		return;
1199
	}
1200
#ifdef CONFIG_PNP
L
Linus Torvalds 已提交
1201
	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
1202
#endif
L
Linus Torvalds 已提交
1203 1204 1205 1206
}

module_init(alsa_card_opti9xx_init)
module_exit(alsa_card_opti9xx_exit)