
/*
    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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#define __SND_OSS_COMPAT__
#define SND_MAIN_OBJECT_FILE
#include "../include/driver.h"
#include "../include/initval.h"
#include "../include/opti9xx.h"
#ifdef OPTi93X
#include "../include/opti93x.h"
#else	/* OPTi93X */
#ifdef CS4231
#include "../include/cs4231.h"
#else	/* CS4231 */
#include "../include/ad1848.h"
#endif	/* CS4231 */
#endif	/* OPTi93X */
#include "../include/mpu401.h"
#include "../include/opl3.h"

#ifdef OPTi93X
MODULE_DESCRIPTION("\
Driver: OPTi93X\n\
ISAPNP: OPT0931=OPT9310,OPT0002\n\
");
#else	/* OPTi93X */
#ifdef CS4231
MODULE_DESCRIPTION("\
Driver: OPTi92X - CS4231\n\
ISAPNP: OPT0924=OPT0000,OPT0002\n\
ISAPNP: OPT0925=OPT9250,OPT0002\n\
");
#else	/* CS4231 */
MODULE_DESCRIPTION("\
Driver: OPTi92X - AD1848\n\
ISAPNP: OPT0924=OPT0000,OPT0002\n\
ISAPNP: OPT0925=OPT9250,OPT0002\n\
");
#endif	/* CS4231 */
#endif	/* OPTi93X */

int snd_index = SND_DEFAULT_IDX1;		/* Index 0-MAX */
char *snd_id = SND_DEFAULT_STR1;		/* ID for this card */
int snd_port = SND_DEFAULT_PORT1; 		/* 0x530,0xe80,0xf40,0x604 */
int snd_mpu_port = SND_DEFAULT_PORT1;		/* 0x300,0x310,0x320,0x330 */
int snd_fm_port = SND_DEFAULT_PORT1;		/* 0x388 */
int snd_irq = SND_DEFAULT_IRQ1;			/* 5,7,9,10,11 */
int snd_mpu_irq = SND_DEFAULT_IRQ1;		/* 5,7,9,10 */
int snd_dma1 = SND_DEFAULT_DMA1;		/* 0,1,3 */
int snd_dma1_size = SND_DEFAULT_DMA_SIZE1;	/* 8,16,32,64,128 */
#if defined(CS4231) || defined(OPTi93X)
int snd_dma2 = SND_DEFAULT_DMA1;		/* 0,1,3 */
int snd_dma2_size = SND_DEFAULT_DMA_SIZE1;	/* 8,16,32,64,128 */
#endif	/* CS4231 || OPTi93X */

MODULE_PARM(snd_index, "i");
MODULE_PARM_DESC(snd_index, "Index value for opti9xx based soundcard.");
MODULE_PARM(snd_id, "s");
MODULE_PARM_DESC(snd_id, "ID string for opti9xx based soundcard.");
MODULE_PARM(snd_port, "i");
MODULE_PARM_DESC(snd_port, "WSS port # for opti9xx driver. [list=0x530,0xe80,0xf40,0x604]");
MODULE_PARM(snd_mpu_port, "i");
MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port # for opti9xx driver. [list=0x300,0x310,0x320,0x330]");
MODULE_PARM(snd_fm_port, "i");
MODULE_PARM_DESC(snd_fm_port, "FM port # for opti9xx driver. [list=0x388]");
MODULE_PARM(snd_irq, "i");
MODULE_PARM_DESC(snd_irq, "WSS irq # for opti9xx driver. [IRQ]");
MODULE_PARM(snd_mpu_irq, "i");
MODULE_PARM_DESC(snd_mpu_irq, "MPU-401 irq # for opti9xx driver. [IRQ]");
MODULE_PARM(snd_dma1, "i");
MODULE_PARM_DESC(snd_dma1, "1st dma # for opti9xx driver. [DMA8]");
MODULE_PARM(snd_dma1_size, "i");
MODULE_PARM_DESC(snd_dma1_size, "Size of 1st dma for opti9xx driver. [DMA8_SIZE]");
#if defined(CS4231) || defined(OPTi93X)
MODULE_PARM(snd_dma2, "i");
MODULE_PARM_DESC(snd_dma2, "2nd dma # for opti9xx driver. [DMA8]");
MODULE_PARM(snd_dma2_size, "i");
MODULE_PARM_DESC(snd_dma2_size, "Size of 2nd dma for opti9xx driver. [DMA8_SIZE]");
#endif	/* CS4231 || OPTi93X */


static struct snd_card_opti9xx {
	snd_irq_t *irqptr;
	snd_irq_t *mpuirqptr;
	snd_dma_t *dma1ptr;
#if defined(CS4231) || defined(OPTi93X)
	snd_dma_t *dma2ptr;
#endif	/* CS4231 || OPTi93X */
	opti9xx_t *chip;
	snd_card_t *card;
	snd_pcm_t *pcm;
	snd_kmixer_t *mixer;
	snd_rawmidi_t *rmidi;
	snd_hwdep_t *synth;
#ifdef __ISAPNP__
	struct isapnp_dev *dev;
	struct isapnp_dev *devmpu;
#endif	/* __ISAPNP__ */
} *snd_card_opti9xx_card = SND_DEFAULT_PTR1;

#ifdef __ISAPNP__
static unsigned int snd_card_opti9xx_pnpids[] = {
#ifndef OPTi93X
	/* OPTi 82C924 */
	(ISAPNP_VENDOR('O','P','T')<<16|ISAPNP_DEVICE(0x0924)),    /* device */
	(ISAPNP_VENDOR('O','P','T')<<16|ISAPNP_FUNCTION(0x0000)),  /* audio */
	(ISAPNP_VENDOR('O','P','T')<<16|ISAPNP_FUNCTION(0x0002)),  /* mpu401 */
	/* OPTi 82C925 */
	(ISAPNP_VENDOR('O','P','T')<<16|ISAPNP_DEVICE(0x0925)),    /* device */
	(ISAPNP_VENDOR('O','P','T')<<16|ISAPNP_FUNCTION(0x9250)),  /* audio */
	(ISAPNP_VENDOR('O','P','T')<<16|ISAPNP_FUNCTION(0x0002)),  /* mpu401 */
#else
	/* OPTi 82C931/3 */
	(ISAPNP_VENDOR('O','P','T')<<16|ISAPNP_DEVICE(0x0931)),    /* device */
	(ISAPNP_VENDOR('O','P','T')<<16|ISAPNP_FUNCTION(0x9310)),  /* audio */
	(ISAPNP_VENDOR('O','P','T')<<16|ISAPNP_FUNCTION(0x0002)),  /* mpu401 */
#endif	/* OPTi93X */
	0	/* end */
};
#endif	/* __ISAPNP__ */

#ifdef OPTi93X
#define DRIVER_NAME	"snd-card-opti93x"
#else
#define DRIVER_NAME	"snd-card-opti92x"
#endif	/* OPTi93X */

#define snd_card_opti9xx_printk(args...)	snd_printk(__FILE__": " ##args)


static void snd_card_opti9xx_use_inc(snd_card_t *card)
{
	MOD_INC_USE_COUNT;
}

static void snd_card_opti9xx_use_dec(snd_card_t *card)
{
	MOD_DEC_USE_COUNT;
}

static void snd_card_opti9xx_interrupt(int irq, void *dev_id,
				       struct pt_regs *regs)
{
	struct snd_card_opti9xx *acard;
	unsigned char status;

	if (!(acard = (struct snd_card_opti9xx *) dev_id))
		return;
	if (acard->chip == NULL || acard->pcm == NULL)
		return;

	if ((status = inb(acard->chip->wss_base + 4 + 2)) & 0x01)
#ifdef OPTi93X
		snd_opti93x_interrupt(acard->pcm, status);
#else
#ifdef CS4231
		snd_cs4231_interrupt(acard->pcm, status);
#else
		snd_ad1848_interrupt(acard->pcm, status);
#endif	/* CS4231 */
#endif	/* OPTi93X */
	else
		snd_mpu401_uart_interrupt(acard->rmidi);
}

static void snd_card_opti9xx_mpu_interrupt(int irq, void *dev_id,
					   struct pt_regs *regs)
{
	struct snd_card_opti9xx *acard;

	if (!(acard = (struct snd_card_opti9xx *) dev_id))
		return;

	snd_mpu401_uart_interrupt(acard->rmidi);
}

static opti9xx_t * __init snd_card_opti9xx_detect(snd_card_t *card)
{
	int i;
	opti9xx_t *chip = NULL;
	static int opti9xx_mc_size[] = {7, 7, 10, 10, 2, 2, 2};

#ifndef OPTi93X
	for (i = OPTi9XX_HW_82C928; i < OPTi9XX_HW_82C930; i++) {
		unsigned char value;

		if (!(chip = snd_opti9xx_new_device(i)))
			return NULL;
		if (snd_register_ioport(card,
				chip->mc_base, opti9xx_mc_size[i],
				DRIVER_NAME" - detect", NULL)) {
			snd_kfree(chip);
			continue;
		}

		value = snd_opti9xx_read(chip, OPTi9XX_MC_REG(1));
		if (value != 0xff) {
			if (value != inb(chip->mc_base + 1))
				snd_printk("warning: opti92x: port didn't close after read\n");
			if (value == snd_opti9xx_read(chip, OPTi9XX_MC_REG(1)))
				break;
		}

		snd_unregister_ioports(card);
		snd_kfree(chip);
		chip = NULL;
	}
#else	/* OPTi93X */
	for (i = OPTi9XX_HW_82C931; i >= OPTi9XX_HW_82C930; i--) {
		unsigned long flags;
		unsigned char value;

		if (!(chip = snd_opti9xx_new_device(i)))
			return NULL;
		if (snd_register_ioport(card,
				chip->mc_base, opti9xx_mc_size[i],
				DRIVER_NAME" - detect", NULL)) {
			snd_kfree(chip);
			continue;
		}
		if (snd_register_ioport(card,
				chip->mc_indir_index, 2,
				DRIVER_NAME" - detect", NULL)) {
			snd_kfree(chip);
			continue;
		}

		spin_lock_irqsave(&chip->lock, flags);
		outb(chip->password, chip->mc_base + chip->pwd_reg);
		outb(((chip->mc_indir_index & (1 << 8)) >> 4) |
			((chip->mc_indir_index & 0xf0) >> 4), chip->mc_base);
		spin_unlock_irqrestore(&chip->lock, flags);

		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)
			break;

		snd_unregister_ioports(card);
		snd_kfree(chip);
		chip = NULL;
	}
#endif	/* OPTi93X */

	snd_unregister_ioports(card);
	return chip;
}

#ifdef __ISAPNP__
static int __init snd_card_opti9xx_isapnp(struct snd_card_opti9xx *acard)
{
	unsigned short hw;
	int idx = 0;
	unsigned int tmp;
	struct isapnp_dev *pdev = NULL;
	static struct isapnp_card *card = NULL;

__again:
	while ((tmp = snd_card_opti9xx_pnpids[idx]) != 0) {
		if ((card = isapnp_find_card(tmp >> 16, tmp & 0xffff, card)))
			break;
		idx += 3;
	}
	if (card == NULL)
		return -ENODEV;

	hw = tmp & 0xffff;

	tmp = snd_card_opti9xx_pnpids[idx+1];
	pdev = isapnp_find_dev(card, tmp >> 16, tmp & 0xffff, NULL);
	if (pdev == NULL)
		goto __again;

	acard->dev = pdev;
	tmp = snd_card_opti9xx_pnpids[idx+2];
	acard->devmpu = isapnp_find_dev(card, tmp >> 16, tmp & 0xffff, NULL);

	pdev = acard->dev;
	if (pdev->prepare(pdev) < 0)
		return -EAGAIN;

#ifdef OPTi93X
	if (snd_port != SND_AUTO_PORT)
		isapnp_resource_change(&pdev->resource[0], snd_port + 4, 4);
#else
	if ((hw != ISAPNP_DEVICE(0x0924)) && (snd_port != SND_AUTO_PORT))
		isapnp_resource_change(&pdev->resource[1], snd_port, 4);
#endif	/* OPTi93X */
	if (snd_irq != SND_AUTO_IRQ)
		isapnp_resource_change(&pdev->irq_resource[0], snd_irq, 1);
	if (snd_dma1 != SND_AUTO_DMA)
		isapnp_resource_change(&pdev->dma_resource[0], snd_dma1, 1);
#if defined(CS4231) || defined(OPTi93X)
	if (snd_dma2 != SND_AUTO_DMA)
		isapnp_resource_change(&pdev->dma_resource[1], snd_dma2, 1);
#endif	/* CS4231 || OPTi93X */
	if (snd_fm_port != SND_AUTO_PORT)
		isapnp_resource_change(&pdev->resource[1], snd_fm_port, 4);

	if (pdev->activate(pdev) < 0) {
		snd_card_opti9xx_printk("AUDIO isapnp configure failure\n");
		return -EBUSY;
	}

#ifdef OPTi93X
	snd_port = pdev->resource[0].start - 4;
	snd_fm_port = pdev->resource[1].start;
#else
	if (hw != ISAPNP_DEVICE(0x0924))
		snd_port = pdev->resource[1].start;
	snd_fm_port = pdev->resource[2].start;
#endif	/* OPTi93X */
	snd_irq = pdev->irq_resource[0].start;
	snd_dma1 = pdev->dma_resource[0].start;
#if defined(CS4231) || defined(OPTi93X)
	snd_dma2 = pdev->dma_resource[1].start;
#endif	/* CS4231 || OPTi93X */

	pdev = acard->devmpu;
	if (pdev == NULL || pdev->prepare(pdev) < 0) {
		snd_mpu_port = -1;
		acard->devmpu = NULL;
		return hw;
	}

	if (snd_mpu_port != SND_AUTO_PORT)
		isapnp_resource_change(&pdev->resource[0], snd_mpu_port, 2);
	if (snd_mpu_irq != SND_AUTO_IRQ)
		isapnp_resource_change(&pdev->irq_resource[0], snd_mpu_irq, 1);

	if (pdev->activate(pdev) < 0) {
		snd_card_opti9xx_printk("MPU-401 isapnp configure failure\n");
		snd_mpu_port = -1;
		acard->devmpu = NULL;
	} else {
		snd_mpu_port = pdev->resource[0].start;
		snd_mpu_irq = pdev->irq_resource[0].start;
	}
	return hw;
}

static void snd_card_opti9xx_deactivate(struct snd_card_opti9xx *acard)
{
	if (acard->dev)
		acard->dev->deactivate(acard->dev);
	if (acard->devmpu)
		acard->devmpu->deactivate(acard->devmpu);
}
#endif	/* __ISAPNP__ */

static int __init snd_card_opti9xx_resources(struct snd_card_opti9xx *acard,
					     snd_card_t *card)
{
	int error, i, pnp = 0;
	static long no_alternatives[] = {-1};
	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};
	static long possible_mpu_ports[] = {0x300, 0x310, 0x320, 0x330, -1};
#ifdef OPTi93X
	static long possible_irqs[] = {5, 9, 10, 11, 7, -1};
#else
	static long possible_irqs[] = {9, 10, 11, 7, -1};
#endif	/* OPTi93X */
	static long possible_mpu_irqs[] = {5, 9, 10, 7, -1};
	static long possible_dma1s[] = {3, 1, 0, -1};
#if defined(CS4231) || defined(OPTi93X)
	static long possible_dma2s[][2] = {{1,-1}, {0,-1}, {-1,-1}, {0,-1}};
#endif	/* CS4231 || OPTi93X */

#ifdef __ISAPNP__
	pnp = acard->dev != NULL;
#endif	/* __ISAPNP__ */

#ifndef OPTi93X
	if (acard->chip->hardware == OPTi9XX_HW_82C928)
		snd_mpu_port = -1;
#endif	/* OPTi93X */
	error = 0;
	if (!pnp && (snd_mpu_port == SND_DEFAULT_PORT1)) {
		for (i = 0; possible_mpu_ports[i] != -1; i++)
			if (!snd_register_ioport(card, possible_mpu_ports[i], 2,
					DRIVER_NAME" - MPU-401", NULL)) {
				snd_mpu_port = possible_mpu_ports[i];
				break;
			}
		if (snd_mpu_port == SND_DEFAULT_PORT1)
			error = -EBUSY;
	}
	else
		error = (snd_mpu_port == -1) ? -ENODEV :
			snd_register_ioport(card, snd_mpu_port, 2,
			DRIVER_NAME" - MPU-401", NULL);
	if (error)
		acard->chip->mpu_port = -1;
	else if (pnp && (snd_irq == snd_mpu_irq))
		acard->chip->mpu_irq = snd_mpu_irq;
	else if (!snd_register_interrupt(card,
			DRIVER_NAME" - MPU-401",
			snd_mpu_irq, SND_IRQ_TYPE_ISA,
			snd_card_opti9xx_mpu_interrupt, acard,
			pnp ? no_alternatives : possible_mpu_irqs,
			&acard->mpuirqptr)) {
		acard->chip->mpu_port = snd_mpu_port;
		acard->chip->mpu_irq = acard->mpuirqptr->irq;
	}
	else
		acard->chip->mpu_port = -1;

	if (!pnp && (snd_port == SND_DEFAULT_PORT1)) {
		for (i = 0; possible_ports[i] != -1; i++)
			if (!snd_register_ioport(card, possible_ports[i], 8,
					DRIVER_NAME" - WSS", NULL)) {
				snd_port = possible_ports[i];
				break;
			}
		if (snd_port == SND_DEFAULT_PORT1)
			return -EBUSY;
	}
	else if ((error = snd_register_ioport(card, snd_port, 8,
			DRIVER_NAME" - WSS", NULL)) < 0)
		return error;
	acard->chip->wss_base = snd_port;
	if ((error = snd_register_interrupt(card, DRIVER_NAME" - WSS",
			snd_irq, SND_IRQ_TYPE_ISA,
			snd_card_opti9xx_interrupt, acard,
			pnp ? no_alternatives : possible_irqs,
			&acard->irqptr)) < 0)
		return error;
	acard->chip->irq = acard->irqptr->irq;
	if ((error = snd_register_dma_channel(card,
#if defined(CS4231) || defined(OPTi93X)
			DRIVER_NAME" - WSS playback",
#else
			DRIVER_NAME" - WSS",
#endif	/* CS4231 || OPTi93X */
			snd_dma1, SND_DMA_TYPE_ISA, snd_dma1_size,
			pnp ? no_alternatives : possible_dma1s,
			&acard->dma1ptr)) < 0)
		return error;
	acard->chip->dma1 = acard->dma1ptr->dma;
#if defined(CS4231) || defined(OPTi93X)
	if ((error = snd_register_dma_channel(card, DRIVER_NAME" - WSS capture",
			snd_dma2, SND_DMA_TYPE_ISA, snd_dma2_size,
			pnp ? no_alternatives :
				possible_dma2s[acard->dma1ptr->dma],
			&acard->dma2ptr)) < 0)
		return error;
	acard->chip->dma2 = acard->dma2ptr->dma;
#endif	/* CS4231 || OPTi93X */

	if (snd_register_ioport(card,
			pnp ? snd_fm_port : snd_fm_port = 0x388, 4,
			DRIVER_NAME" - OPL", NULL) < 0)
		snd_fm_port = -1;
	acard->chip->fm_port = snd_fm_port;

	return 0;
}

static int __init snd_card_opti9xx_probe(struct snd_card_opti9xx *acard)
{
	int error;
	opti9xx_t *chip;
	snd_card_t *card;
	snd_pcm_t *pcm;
	snd_kmixer_t *mixer;
	snd_rawmidi_t *rmidi;
	snd_hwdep_t *synth;
#ifdef __ISAPNP__
	int hw;
#endif	/* __ISAPNP__ */

	if (!(card = snd_card_new(snd_index, snd_id,
			snd_card_opti9xx_use_inc, snd_card_opti9xx_use_dec)))
		return -ENOMEM;
#ifdef OPTi93X
	card->type = SND_CARD_TYPE_OPTI93X;
#else
	card->type = SND_CARD_TYPE_OPTI92X;
#endif	/* OPTi93X */

#ifdef __ISAPNP__
	if ((hw = snd_card_opti9xx_isapnp(acard)) > 0) {
		switch (hw) {
		case ISAPNP_DEVICE(0x0924):
			hw = OPTi9XX_HW_82C924;
			break;
		case ISAPNP_DEVICE(0x0925):
			hw = OPTi9XX_HW_82C925;
			break;
		case ISAPNP_DEVICE(0x0931):
			hw = OPTi9XX_HW_82C931;
			break;
		default:
			error = -ENODEV;
			goto __on_error;
		}

		if (!(chip = snd_opti9xx_new_device(hw))) {
			error = -ENOMEM;
			goto __on_error;
		}
		if (hw <= OPTi9XX_HW_82C930)
			chip->mc_base -= 0x80;
	}
	else
#endif	/* __ISAPNP__ */
		if (!(chip = snd_card_opti9xx_detect(card))) {
			error = -ENODEV;
			goto __on_error;
		}
	acard->chip = chip;

	if ((error = snd_card_opti9xx_resources(acard, card)))
		goto __on_error;

	if ((error = snd_opti9xx_configure(acard->chip)))
		goto __on_error;

#ifdef OPTi93X
	if ((error = snd_opti93x_new_pcm(card, 0, acard->chip,
		acard->dma1ptr, acard->dma2ptr, &pcm)))
#else
#ifdef CS4231
	if ((error = snd_cs4231_new_pcm(card, 0, acard->chip->wss_base + 4,
		acard->irqptr, acard->dma1ptr, acard->dma2ptr,
		CS4231_HW_DETECT, 0, &pcm)))
#else
	if ((error = snd_ad1848_new_pcm(card, 0, acard->chip->wss_base + 4,
		acard->irqptr, acard->dma1ptr, AD1848_HW_DETECT, &pcm)))
#endif	/* CS4231 */
#endif	/* OPTi93X */
		goto __on_error;

#ifdef OPTi93X
	if ((error = snd_opti93x_new_mixer(pcm, 0, &mixer)))
#else
#ifdef CS4231
	if ((error = snd_cs4231_new_mixer(pcm, 0, &mixer)))
#else
	if ((error = snd_ad1848_new_mixer(pcm, 0, &mixer)))
#endif	/* CS4231 */
#endif	/* OPTi93X */
		goto __on_error;

	if (acard->chip->mpu_port <= 0)
		rmidi = NULL;
	else
		if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
				acard->chip->mpu_port, acard->chip->mpu_irq,
				&rmidi)))
			snd_card_opti9xx_printk("no MPU-401 device at 0x%lx?\n",
				acard->chip->mpu_port);

	if (acard->chip->fm_port <= 0)
		synth = NULL;
	else
		if ((error = snd_opl3_new(card, 0,
				acard->chip->fm_port, acard->chip->fm_port + 2,
#ifdef CS4231
				OPL3_HW_AUTO, 1, &synth)))
#else
				OPL3_HW_AUTO, 0, &synth)))
#endif	/* CS4231 */
			snd_card_opti9xx_printk("no OPL device at 0x%lx-0x%lx?\n",
				acard->chip->fm_port, acard->chip->fm_port + 2);

	strcpy(card->abbreviation, acard->chip->name);
	sprintf(card->shortname, "OPTi %s", card->abbreviation);
#if defined(CS4231) || defined(OPTi93X)
	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %li, dma %li&%li",
		card->shortname, pcm->name, acard->chip->wss_base + 4,
		acard->irqptr->irq, acard->dma1ptr->dma, acard->dma2ptr->dma);
#else
	sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %li, dma %li",
		card->shortname, pcm->name, acard->chip->wss_base + 4,
		acard->irqptr->irq, acard->dma1ptr->dma);
#endif	/* CS4231 || OPTi93X */
	if (!snd_card_register(card)) {
		acard->card = card;
		acard->pcm = pcm;
		acard->mixer = mixer;
		acard->rmidi = rmidi;
		acard->synth = synth;
		return 0;
	}

__on_error:
	if (acard->chip)
		snd_opti9xx_free(acard->chip);
	snd_card_free(card);

	return error ? error : -ENXIO;
}

static void snd_card_opti9xx_free(struct snd_card_opti9xx *acard)
{
	if (acard->chip)
		snd_opti9xx_free(acard->chip);

	snd_card_unregister(acard->card);

#ifdef __ISAPNP__
	snd_card_opti9xx_deactivate(acard);
#endif	/* __ISAPNP__ */

	snd_kfree(acard);
}

static int __init alsa_card_opti9xx_init(void)
{
	int error;
	struct snd_card_opti9xx *acard;

	if (!(acard = (struct snd_card_opti9xx *) snd_kcalloc(
			sizeof(struct snd_card_opti9xx), GFP_KERNEL)))
		return -ENOMEM;

	if ((error = snd_card_opti9xx_probe(acard))) {
#ifdef MODULE
#ifdef OPTi93X
		snd_card_opti9xx_printk("no OPTi 82C93x soundcard found\n");
#else
		snd_card_opti9xx_printk("no OPTi 82C92x soundcard found\n");
#endif	/* OPTi93X */
#endif
#ifdef __ISAPNP__
		snd_card_opti9xx_deactivate(acard);
#endif	/* __ISAPNP__ */
		snd_kfree(acard);
		return error;
	}
	else snd_card_opti9xx_card = acard;
	return 0;
}

static void __exit alsa_card_opti9xx_exit(void)
{
	snd_card_opti9xx_free(snd_card_opti9xx_card);
}

module_init(alsa_card_opti9xx_init)
module_exit(alsa_card_opti9xx_exit)
