162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*********************************************************************
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Linux multisound pinnacle/fiji driver for ALSA.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * 2002/06/30 Karsten Wiese:
762306a36Sopenharmony_ci *	for now this is only used to build a pinnacle / fiji driver.
862306a36Sopenharmony_ci *	the OSS parent of this code is designed to also support
962306a36Sopenharmony_ci *	the multisound classic via the file msnd_classic.c.
1062306a36Sopenharmony_ci *	to make it easier for some brave heart to implemt classic
1162306a36Sopenharmony_ci *	support in alsa, i left all the MSND_CLASSIC tokens in this file.
1262306a36Sopenharmony_ci *	but for now this untested & undone.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * ripped from linux kernel 2.4.18 by Karsten Wiese.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * the following is a copy of the 2.4.18 OSS FREE file-heading comment:
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Turtle Beach MultiSound Sound Card Driver for Linux
1962306a36Sopenharmony_ci * msnd_pinnacle.c / msnd_classic.c
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * -- If MSND_CLASSIC is defined:
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *     -> driver for Turtle Beach Classic/Monterey/Tahiti
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * -- Else
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci *     -> driver for Turtle Beach Pinnacle/Fiji
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * 12-3-2000  Modified IO port validation  Steve Sycamore
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * Copyright (C) 1998 Andrew Veliath
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci ********************************************************************/
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/kernel.h>
3662306a36Sopenharmony_ci#include <linux/module.h>
3762306a36Sopenharmony_ci#include <linux/interrupt.h>
3862306a36Sopenharmony_ci#include <linux/types.h>
3962306a36Sopenharmony_ci#include <linux/delay.h>
4062306a36Sopenharmony_ci#include <linux/ioport.h>
4162306a36Sopenharmony_ci#include <linux/firmware.h>
4262306a36Sopenharmony_ci#include <linux/isa.h>
4362306a36Sopenharmony_ci#include <linux/isapnp.h>
4462306a36Sopenharmony_ci#include <linux/irq.h>
4562306a36Sopenharmony_ci#include <linux/io.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#include <sound/core.h>
4862306a36Sopenharmony_ci#include <sound/initval.h>
4962306a36Sopenharmony_ci#include <sound/asound.h>
5062306a36Sopenharmony_ci#include <sound/pcm.h>
5162306a36Sopenharmony_ci#include <sound/mpu401.h>
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#ifdef MSND_CLASSIC
5462306a36Sopenharmony_ci# ifndef __alpha__
5562306a36Sopenharmony_ci#  define SLOWIO
5662306a36Sopenharmony_ci# endif
5762306a36Sopenharmony_ci#endif
5862306a36Sopenharmony_ci#include "msnd.h"
5962306a36Sopenharmony_ci#ifdef MSND_CLASSIC
6062306a36Sopenharmony_ci#  include "msnd_classic.h"
6162306a36Sopenharmony_ci#  define LOGNAME			"msnd_classic"
6262306a36Sopenharmony_ci#  define DEV_NAME			"msnd-classic"
6362306a36Sopenharmony_ci#else
6462306a36Sopenharmony_ci#  include "msnd_pinnacle.h"
6562306a36Sopenharmony_ci#  define LOGNAME			"snd_msnd_pinnacle"
6662306a36Sopenharmony_ci#  define DEV_NAME			"msnd-pinnacle"
6762306a36Sopenharmony_ci#endif
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void set_default_audio_parameters(struct snd_msnd *chip)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	chip->play_sample_size = snd_pcm_format_width(DEFSAMPLESIZE);
7262306a36Sopenharmony_ci	chip->play_sample_rate = DEFSAMPLERATE;
7362306a36Sopenharmony_ci	chip->play_channels = DEFCHANNELS;
7462306a36Sopenharmony_ci	chip->capture_sample_size = snd_pcm_format_width(DEFSAMPLESIZE);
7562306a36Sopenharmony_ci	chip->capture_sample_rate = DEFSAMPLERATE;
7662306a36Sopenharmony_ci	chip->capture_channels = DEFCHANNELS;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	switch (HIBYTE(wMessage)) {
8262306a36Sopenharmony_ci	case HIMT_PLAY_DONE: {
8362306a36Sopenharmony_ci		if (chip->banksPlayed < 3)
8462306a36Sopenharmony_ci			snd_printdd("%08X: HIMT_PLAY_DONE: %i\n",
8562306a36Sopenharmony_ci				(unsigned)jiffies, LOBYTE(wMessage));
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		if (chip->last_playbank == LOBYTE(wMessage)) {
8862306a36Sopenharmony_ci			snd_printdd("chip.last_playbank == LOBYTE(wMessage)\n");
8962306a36Sopenharmony_ci			break;
9062306a36Sopenharmony_ci		}
9162306a36Sopenharmony_ci		chip->banksPlayed++;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		if (test_bit(F_WRITING, &chip->flags))
9462306a36Sopenharmony_ci			snd_msnd_DAPQ(chip, 0);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		chip->last_playbank = LOBYTE(wMessage);
9762306a36Sopenharmony_ci		chip->playDMAPos += chip->play_period_bytes;
9862306a36Sopenharmony_ci		if (chip->playDMAPos > chip->playLimit)
9962306a36Sopenharmony_ci			chip->playDMAPos = 0;
10062306a36Sopenharmony_ci		snd_pcm_period_elapsed(chip->playback_substream);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	case HIMT_RECORD_DONE:
10562306a36Sopenharmony_ci		if (chip->last_recbank == LOBYTE(wMessage))
10662306a36Sopenharmony_ci			break;
10762306a36Sopenharmony_ci		chip->last_recbank = LOBYTE(wMessage);
10862306a36Sopenharmony_ci		chip->captureDMAPos += chip->capturePeriodBytes;
10962306a36Sopenharmony_ci		if (chip->captureDMAPos > (chip->captureLimit))
11062306a36Sopenharmony_ci			chip->captureDMAPos = 0;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		if (test_bit(F_READING, &chip->flags))
11362306a36Sopenharmony_ci			snd_msnd_DARQ(chip, chip->last_recbank);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		snd_pcm_period_elapsed(chip->capture_substream);
11662306a36Sopenharmony_ci		break;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	case HIMT_DSP:
11962306a36Sopenharmony_ci		switch (LOBYTE(wMessage)) {
12062306a36Sopenharmony_ci#ifndef MSND_CLASSIC
12162306a36Sopenharmony_ci		case HIDSP_PLAY_UNDER:
12262306a36Sopenharmony_ci#endif
12362306a36Sopenharmony_ci		case HIDSP_INT_PLAY_UNDER:
12462306a36Sopenharmony_ci			snd_printd(KERN_WARNING LOGNAME ": Play underflow %i\n",
12562306a36Sopenharmony_ci				chip->banksPlayed);
12662306a36Sopenharmony_ci			if (chip->banksPlayed > 2)
12762306a36Sopenharmony_ci				clear_bit(F_WRITING, &chip->flags);
12862306a36Sopenharmony_ci			break;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		case HIDSP_INT_RECORD_OVER:
13162306a36Sopenharmony_ci			snd_printd(KERN_WARNING LOGNAME ": Record overflow\n");
13262306a36Sopenharmony_ci			clear_bit(F_READING, &chip->flags);
13362306a36Sopenharmony_ci			break;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		default:
13662306a36Sopenharmony_ci			snd_printd(KERN_WARNING LOGNAME
13762306a36Sopenharmony_ci				   ": DSP message %d 0x%02x\n",
13862306a36Sopenharmony_ci				   LOBYTE(wMessage), LOBYTE(wMessage));
13962306a36Sopenharmony_ci			break;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci		break;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	case HIMT_MIDI_IN_UCHAR:
14462306a36Sopenharmony_ci		if (chip->msndmidi_mpu)
14562306a36Sopenharmony_ci			snd_msndmidi_input_read(chip->msndmidi_mpu);
14662306a36Sopenharmony_ci		break;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	default:
14962306a36Sopenharmony_ci		snd_printd(KERN_WARNING LOGNAME ": HIMT message %d 0x%02x\n",
15062306a36Sopenharmony_ci			   HIBYTE(wMessage), HIBYTE(wMessage));
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic irqreturn_t snd_msnd_interrupt(int irq, void *dev_id)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct snd_msnd *chip = dev_id;
15862306a36Sopenharmony_ci	void __iomem *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF;
15962306a36Sopenharmony_ci	u16 head, tail, size;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Send ack to DSP */
16262306a36Sopenharmony_ci	/* inb(chip->io + HP_RXL); */
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Evaluate queued DSP messages */
16562306a36Sopenharmony_ci	head = readw(chip->DSPQ + JQS_wHead);
16662306a36Sopenharmony_ci	tail = readw(chip->DSPQ + JQS_wTail);
16762306a36Sopenharmony_ci	size = readw(chip->DSPQ + JQS_wSize);
16862306a36Sopenharmony_ci	if (head > size || tail > size)
16962306a36Sopenharmony_ci		goto out;
17062306a36Sopenharmony_ci	while (head != tail) {
17162306a36Sopenharmony_ci		snd_msnd_eval_dsp_msg(chip, readw(pwDSPQData + 2 * head));
17262306a36Sopenharmony_ci		if (++head > size)
17362306a36Sopenharmony_ci			head = 0;
17462306a36Sopenharmony_ci		writew(head, chip->DSPQ + JQS_wHead);
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci out:
17762306a36Sopenharmony_ci	/* Send ack to DSP */
17862306a36Sopenharmony_ci	inb(chip->io + HP_RXL);
17962306a36Sopenharmony_ci	return IRQ_HANDLED;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int snd_msnd_reset_dsp(long io, unsigned char *info)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	int timeout = 100;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	outb(HPDSPRESET_ON, io + HP_DSPR);
18862306a36Sopenharmony_ci	msleep(1);
18962306a36Sopenharmony_ci#ifndef MSND_CLASSIC
19062306a36Sopenharmony_ci	if (info)
19162306a36Sopenharmony_ci		*info = inb(io + HP_INFO);
19262306a36Sopenharmony_ci#endif
19362306a36Sopenharmony_ci	outb(HPDSPRESET_OFF, io + HP_DSPR);
19462306a36Sopenharmony_ci	msleep(1);
19562306a36Sopenharmony_ci	while (timeout-- > 0) {
19662306a36Sopenharmony_ci		if (inb(io + HP_CVR) == HP_CVR_DEF)
19762306a36Sopenharmony_ci			return 0;
19862306a36Sopenharmony_ci		msleep(1);
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	snd_printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return -EIO;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int snd_msnd_probe(struct snd_card *card)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct snd_msnd *chip = card->private_data;
20862306a36Sopenharmony_ci	unsigned char info;
20962306a36Sopenharmony_ci#ifndef MSND_CLASSIC
21062306a36Sopenharmony_ci	char *xv, *rev = NULL;
21162306a36Sopenharmony_ci	char *pin = "TB Pinnacle", *fiji = "TB Fiji";
21262306a36Sopenharmony_ci	char *pinfiji = "TB Pinnacle/Fiji";
21362306a36Sopenharmony_ci#endif
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (!request_region(chip->io, DSP_NUMIO, "probing")) {
21662306a36Sopenharmony_ci		snd_printk(KERN_ERR LOGNAME ": I/O port conflict\n");
21762306a36Sopenharmony_ci		return -ENODEV;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (snd_msnd_reset_dsp(chip->io, &info) < 0) {
22162306a36Sopenharmony_ci		release_region(chip->io, DSP_NUMIO);
22262306a36Sopenharmony_ci		return -ENODEV;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci#ifdef MSND_CLASSIC
22662306a36Sopenharmony_ci	strcpy(card->shortname, "Classic/Tahiti/Monterey");
22762306a36Sopenharmony_ci	strcpy(card->longname, "Turtle Beach Multisound");
22862306a36Sopenharmony_ci	printk(KERN_INFO LOGNAME ": %s, "
22962306a36Sopenharmony_ci	       "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
23062306a36Sopenharmony_ci	       card->shortname,
23162306a36Sopenharmony_ci	       chip->io, chip->io + DSP_NUMIO - 1,
23262306a36Sopenharmony_ci	       chip->irq,
23362306a36Sopenharmony_ci	       chip->base, chip->base + 0x7fff);
23462306a36Sopenharmony_ci#else
23562306a36Sopenharmony_ci	switch (info >> 4) {
23662306a36Sopenharmony_ci	case 0xf:
23762306a36Sopenharmony_ci		xv = "<= 1.15";
23862306a36Sopenharmony_ci		break;
23962306a36Sopenharmony_ci	case 0x1:
24062306a36Sopenharmony_ci		xv = "1.18/1.2";
24162306a36Sopenharmony_ci		break;
24262306a36Sopenharmony_ci	case 0x2:
24362306a36Sopenharmony_ci		xv = "1.3";
24462306a36Sopenharmony_ci		break;
24562306a36Sopenharmony_ci	case 0x3:
24662306a36Sopenharmony_ci		xv = "1.4";
24762306a36Sopenharmony_ci		break;
24862306a36Sopenharmony_ci	default:
24962306a36Sopenharmony_ci		xv = "unknown";
25062306a36Sopenharmony_ci		break;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	switch (info & 0x7) {
25462306a36Sopenharmony_ci	case 0x0:
25562306a36Sopenharmony_ci		rev = "I";
25662306a36Sopenharmony_ci		strcpy(card->shortname, pin);
25762306a36Sopenharmony_ci		break;
25862306a36Sopenharmony_ci	case 0x1:
25962306a36Sopenharmony_ci		rev = "F";
26062306a36Sopenharmony_ci		strcpy(card->shortname, pin);
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci	case 0x2:
26362306a36Sopenharmony_ci		rev = "G";
26462306a36Sopenharmony_ci		strcpy(card->shortname, pin);
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci	case 0x3:
26762306a36Sopenharmony_ci		rev = "H";
26862306a36Sopenharmony_ci		strcpy(card->shortname, pin);
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci	case 0x4:
27162306a36Sopenharmony_ci		rev = "E";
27262306a36Sopenharmony_ci		strcpy(card->shortname, fiji);
27362306a36Sopenharmony_ci		break;
27462306a36Sopenharmony_ci	case 0x5:
27562306a36Sopenharmony_ci		rev = "C";
27662306a36Sopenharmony_ci		strcpy(card->shortname, fiji);
27762306a36Sopenharmony_ci		break;
27862306a36Sopenharmony_ci	case 0x6:
27962306a36Sopenharmony_ci		rev = "D";
28062306a36Sopenharmony_ci		strcpy(card->shortname, fiji);
28162306a36Sopenharmony_ci		break;
28262306a36Sopenharmony_ci	case 0x7:
28362306a36Sopenharmony_ci		rev = "A-B (Fiji) or A-E (Pinnacle)";
28462306a36Sopenharmony_ci		strcpy(card->shortname, pinfiji);
28562306a36Sopenharmony_ci		break;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci	strcpy(card->longname, "Turtle Beach Multisound Pinnacle");
28862306a36Sopenharmony_ci	printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, "
28962306a36Sopenharmony_ci	       "I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
29062306a36Sopenharmony_ci	       card->shortname,
29162306a36Sopenharmony_ci	       rev, xv,
29262306a36Sopenharmony_ci	       chip->io, chip->io + DSP_NUMIO - 1,
29362306a36Sopenharmony_ci	       chip->irq,
29462306a36Sopenharmony_ci	       chip->base, chip->base + 0x7fff);
29562306a36Sopenharmony_ci#endif
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	release_region(chip->io, DSP_NUMIO);
29862306a36Sopenharmony_ci	return 0;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic int snd_msnd_init_sma(struct snd_msnd *chip)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	static int initted;
30462306a36Sopenharmony_ci	u16 mastVolLeft, mastVolRight;
30562306a36Sopenharmony_ci	unsigned long flags;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci#ifdef MSND_CLASSIC
30862306a36Sopenharmony_ci	outb(chip->memid, chip->io + HP_MEMM);
30962306a36Sopenharmony_ci#endif
31062306a36Sopenharmony_ci	outb(HPBLKSEL_0, chip->io + HP_BLKS);
31162306a36Sopenharmony_ci	/* Motorola 56k shared memory base */
31262306a36Sopenharmony_ci	chip->SMA = chip->mappedbase + SMA_STRUCT_START;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (initted) {
31562306a36Sopenharmony_ci		mastVolLeft = readw(chip->SMA + SMA_wCurrMastVolLeft);
31662306a36Sopenharmony_ci		mastVolRight = readw(chip->SMA + SMA_wCurrMastVolRight);
31762306a36Sopenharmony_ci	} else
31862306a36Sopenharmony_ci		mastVolLeft = mastVolRight = 0;
31962306a36Sopenharmony_ci	memset_io(chip->mappedbase, 0, 0x8000);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* Critical section: bank 1 access */
32262306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
32362306a36Sopenharmony_ci	outb(HPBLKSEL_1, chip->io + HP_BLKS);
32462306a36Sopenharmony_ci	memset_io(chip->mappedbase, 0, 0x8000);
32562306a36Sopenharmony_ci	outb(HPBLKSEL_0, chip->io + HP_BLKS);
32662306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* Digital audio play queue */
32962306a36Sopenharmony_ci	chip->DAPQ = chip->mappedbase + DAPQ_OFFSET;
33062306a36Sopenharmony_ci	snd_msnd_init_queue(chip->DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Digital audio record queue */
33362306a36Sopenharmony_ci	chip->DARQ = chip->mappedbase + DARQ_OFFSET;
33462306a36Sopenharmony_ci	snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* MIDI out queue */
33762306a36Sopenharmony_ci	chip->MODQ = chip->mappedbase + MODQ_OFFSET;
33862306a36Sopenharmony_ci	snd_msnd_init_queue(chip->MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* MIDI in queue */
34162306a36Sopenharmony_ci	chip->MIDQ = chip->mappedbase + MIDQ_OFFSET;
34262306a36Sopenharmony_ci	snd_msnd_init_queue(chip->MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* DSP -> host message queue */
34562306a36Sopenharmony_ci	chip->DSPQ = chip->mappedbase + DSPQ_OFFSET;
34662306a36Sopenharmony_ci	snd_msnd_init_queue(chip->DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* Setup some DSP values */
34962306a36Sopenharmony_ci#ifndef MSND_CLASSIC
35062306a36Sopenharmony_ci	writew(1, chip->SMA + SMA_wCurrPlayFormat);
35162306a36Sopenharmony_ci	writew(chip->play_sample_size, chip->SMA + SMA_wCurrPlaySampleSize);
35262306a36Sopenharmony_ci	writew(chip->play_channels, chip->SMA + SMA_wCurrPlayChannels);
35362306a36Sopenharmony_ci	writew(chip->play_sample_rate, chip->SMA + SMA_wCurrPlaySampleRate);
35462306a36Sopenharmony_ci#endif
35562306a36Sopenharmony_ci	writew(chip->play_sample_rate, chip->SMA + SMA_wCalFreqAtoD);
35662306a36Sopenharmony_ci	writew(mastVolLeft, chip->SMA + SMA_wCurrMastVolLeft);
35762306a36Sopenharmony_ci	writew(mastVolRight, chip->SMA + SMA_wCurrMastVolRight);
35862306a36Sopenharmony_ci#ifndef MSND_CLASSIC
35962306a36Sopenharmony_ci	writel(0x00010000, chip->SMA + SMA_dwCurrPlayPitch);
36062306a36Sopenharmony_ci	writel(0x00000001, chip->SMA + SMA_dwCurrPlayRate);
36162306a36Sopenharmony_ci#endif
36262306a36Sopenharmony_ci	writew(0x303, chip->SMA + SMA_wCurrInputTagBits);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	initted = 1;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int upload_dsp_code(struct snd_card *card)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct snd_msnd *chip = card->private_data;
37362306a36Sopenharmony_ci	const struct firmware *init_fw = NULL, *perm_fw = NULL;
37462306a36Sopenharmony_ci	int err;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	outb(HPBLKSEL_0, chip->io + HP_BLKS);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	err = request_firmware(&init_fw, INITCODEFILE, card->dev);
37962306a36Sopenharmony_ci	if (err < 0) {
38062306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE);
38162306a36Sopenharmony_ci		goto cleanup1;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci	err = request_firmware(&perm_fw, PERMCODEFILE, card->dev);
38462306a36Sopenharmony_ci	if (err < 0) {
38562306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE);
38662306a36Sopenharmony_ci		goto cleanup;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	memcpy_toio(chip->mappedbase, perm_fw->data, perm_fw->size);
39062306a36Sopenharmony_ci	if (snd_msnd_upload_host(chip, init_fw->data, init_fw->size) < 0) {
39162306a36Sopenharmony_ci		printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
39262306a36Sopenharmony_ci		err = -ENODEV;
39362306a36Sopenharmony_ci		goto cleanup;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci	printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n");
39662306a36Sopenharmony_ci	err = 0;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cicleanup:
39962306a36Sopenharmony_ci	release_firmware(perm_fw);
40062306a36Sopenharmony_cicleanup1:
40162306a36Sopenharmony_ci	release_firmware(init_fw);
40262306a36Sopenharmony_ci	return err;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci#ifdef MSND_CLASSIC
40662306a36Sopenharmony_cistatic void reset_proteus(struct snd_msnd *chip)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	outb(HPPRORESET_ON, chip->io + HP_PROR);
40962306a36Sopenharmony_ci	msleep(TIME_PRO_RESET);
41062306a36Sopenharmony_ci	outb(HPPRORESET_OFF, chip->io + HP_PROR);
41162306a36Sopenharmony_ci	msleep(TIME_PRO_RESET_DONE);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci#endif
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int snd_msnd_initialize(struct snd_card *card)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct snd_msnd *chip = card->private_data;
41862306a36Sopenharmony_ci	int err, timeout;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci#ifdef MSND_CLASSIC
42162306a36Sopenharmony_ci	outb(HPWAITSTATE_0, chip->io + HP_WAIT);
42262306a36Sopenharmony_ci	outb(HPBITMODE_16, chip->io + HP_BITM);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	reset_proteus(chip);
42562306a36Sopenharmony_ci#endif
42662306a36Sopenharmony_ci	err = snd_msnd_init_sma(chip);
42762306a36Sopenharmony_ci	if (err < 0) {
42862306a36Sopenharmony_ci		printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
42962306a36Sopenharmony_ci		return err;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	err = snd_msnd_reset_dsp(chip->io, NULL);
43362306a36Sopenharmony_ci	if (err < 0)
43462306a36Sopenharmony_ci		return err;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	err = upload_dsp_code(card);
43762306a36Sopenharmony_ci	if (err < 0) {
43862306a36Sopenharmony_ci		printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
43962306a36Sopenharmony_ci		return err;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	timeout = 200;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	while (readw(chip->mappedbase)) {
44562306a36Sopenharmony_ci		msleep(1);
44662306a36Sopenharmony_ci		if (!timeout--) {
44762306a36Sopenharmony_ci			snd_printd(KERN_ERR LOGNAME ": DSP reset timeout\n");
44862306a36Sopenharmony_ci			return -EIO;
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	snd_msndmix_setup(chip);
45362306a36Sopenharmony_ci	return 0;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic int snd_msnd_dsp_full_reset(struct snd_card *card)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct snd_msnd *chip = card->private_data;
45962306a36Sopenharmony_ci	int rv;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (test_bit(F_RESETTING, &chip->flags) || ++chip->nresets > 10)
46262306a36Sopenharmony_ci		return 0;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	set_bit(F_RESETTING, &chip->flags);
46562306a36Sopenharmony_ci	snd_msnd_dsp_halt(chip, NULL);	/* Unconditionally halt */
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	rv = snd_msnd_initialize(card);
46862306a36Sopenharmony_ci	if (rv)
46962306a36Sopenharmony_ci		printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
47062306a36Sopenharmony_ci	snd_msndmix_force_recsrc(chip, 0);
47162306a36Sopenharmony_ci	clear_bit(F_RESETTING, &chip->flags);
47262306a36Sopenharmony_ci	return rv;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int snd_msnd_send_dsp_cmd_chk(struct snd_msnd *chip, u8 cmd)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	if (snd_msnd_send_dsp_cmd(chip, cmd) == 0)
47962306a36Sopenharmony_ci		return 0;
48062306a36Sopenharmony_ci	snd_msnd_dsp_full_reset(chip->card);
48162306a36Sopenharmony_ci	return snd_msnd_send_dsp_cmd(chip, cmd);
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	snd_printdd("snd_msnd_calibrate_adc(%i)\n", srate);
48762306a36Sopenharmony_ci	writew(srate, chip->SMA + SMA_wCalFreqAtoD);
48862306a36Sopenharmony_ci	if (chip->calibrate_signal == 0)
48962306a36Sopenharmony_ci		writew(readw(chip->SMA + SMA_wCurrHostStatusFlags)
49062306a36Sopenharmony_ci		       | 0x0001, chip->SMA + SMA_wCurrHostStatusFlags);
49162306a36Sopenharmony_ci	else
49262306a36Sopenharmony_ci		writew(readw(chip->SMA + SMA_wCurrHostStatusFlags)
49362306a36Sopenharmony_ci		       & ~0x0001, chip->SMA + SMA_wCurrHostStatusFlags);
49462306a36Sopenharmony_ci	if (snd_msnd_send_word(chip, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
49562306a36Sopenharmony_ci	    snd_msnd_send_dsp_cmd_chk(chip, HDEX_AUX_REQ) == 0) {
49662306a36Sopenharmony_ci		schedule_timeout_interruptible(msecs_to_jiffies(333));
49762306a36Sopenharmony_ci		return 0;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci	printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
50062306a36Sopenharmony_ci	return -EIO;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci/*
50462306a36Sopenharmony_ci * ALSA callback function, called when attempting to open the MIDI device.
50562306a36Sopenharmony_ci */
50662306a36Sopenharmony_cistatic int snd_msnd_mpu401_open(struct snd_mpu401 *mpu)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	snd_msnd_enable_irq(mpu->private_data);
50962306a36Sopenharmony_ci	snd_msnd_send_dsp_cmd(mpu->private_data, HDEX_MIDI_IN_START);
51062306a36Sopenharmony_ci	return 0;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic void snd_msnd_mpu401_close(struct snd_mpu401 *mpu)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	snd_msnd_send_dsp_cmd(mpu->private_data, HDEX_MIDI_IN_STOP);
51662306a36Sopenharmony_ci	snd_msnd_disable_irq(mpu->private_data);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
52062306a36Sopenharmony_cistatic int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic int snd_msnd_attach(struct snd_card *card)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct snd_msnd *chip = card->private_data;
52562306a36Sopenharmony_ci	int err;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	err = devm_request_irq(card->dev, chip->irq, snd_msnd_interrupt, 0,
52862306a36Sopenharmony_ci			       card->shortname, chip);
52962306a36Sopenharmony_ci	if (err < 0) {
53062306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", chip->irq);
53162306a36Sopenharmony_ci		return err;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci	card->sync_irq = chip->irq;
53462306a36Sopenharmony_ci	if (!devm_request_region(card->dev, chip->io, DSP_NUMIO,
53562306a36Sopenharmony_ci				 card->shortname))
53662306a36Sopenharmony_ci		return -EBUSY;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (!devm_request_mem_region(card->dev, chip->base, BUFFSIZE,
53962306a36Sopenharmony_ci				     card->shortname)) {
54062306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME
54162306a36Sopenharmony_ci			": unable to grab memory region 0x%lx-0x%lx\n",
54262306a36Sopenharmony_ci			chip->base, chip->base + BUFFSIZE - 1);
54362306a36Sopenharmony_ci		return -EBUSY;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	chip->mappedbase = devm_ioremap(card->dev, chip->base, 0x8000);
54662306a36Sopenharmony_ci	if (!chip->mappedbase) {
54762306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME
54862306a36Sopenharmony_ci			": unable to map memory region 0x%lx-0x%lx\n",
54962306a36Sopenharmony_ci			chip->base, chip->base + BUFFSIZE - 1);
55062306a36Sopenharmony_ci		return -EIO;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	err = snd_msnd_dsp_full_reset(card);
55462306a36Sopenharmony_ci	if (err < 0)
55562306a36Sopenharmony_ci		return err;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	err = snd_msnd_pcm(card, 0);
55862306a36Sopenharmony_ci	if (err < 0) {
55962306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": error creating new PCM device\n");
56062306a36Sopenharmony_ci		return err;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	err = snd_msndmix_new(card);
56462306a36Sopenharmony_ci	if (err < 0) {
56562306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": error creating new Mixer device\n");
56662306a36Sopenharmony_ci		return err;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (mpu_io[0] != SNDRV_AUTO_PORT) {
57162306a36Sopenharmony_ci		struct snd_mpu401 *mpu;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
57462306a36Sopenharmony_ci					  mpu_io[0],
57562306a36Sopenharmony_ci					  MPU401_MODE_INPUT |
57662306a36Sopenharmony_ci					  MPU401_MODE_OUTPUT,
57762306a36Sopenharmony_ci					  mpu_irq[0],
57862306a36Sopenharmony_ci					  &chip->rmidi);
57962306a36Sopenharmony_ci		if (err < 0) {
58062306a36Sopenharmony_ci			printk(KERN_ERR LOGNAME
58162306a36Sopenharmony_ci				": error creating new Midi device\n");
58262306a36Sopenharmony_ci			return err;
58362306a36Sopenharmony_ci		}
58462306a36Sopenharmony_ci		mpu = chip->rmidi->private_data;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci		mpu->open_input = snd_msnd_mpu401_open;
58762306a36Sopenharmony_ci		mpu->close_input = snd_msnd_mpu401_close;
58862306a36Sopenharmony_ci		mpu->private_data = chip;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	disable_irq(chip->irq);
59262306a36Sopenharmony_ci	snd_msnd_calibrate_adc(chip, chip->play_sample_rate);
59362306a36Sopenharmony_ci	snd_msndmix_force_recsrc(chip, 0);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	err = snd_card_register(card);
59662306a36Sopenharmony_ci	if (err < 0)
59762306a36Sopenharmony_ci		return err;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	return 0;
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci#ifndef MSND_CLASSIC
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/* Pinnacle/Fiji Logical Device Configuration */
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int snd_msnd_write_cfg(int cfg, int reg, int value)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	outb(reg, cfg);
61062306a36Sopenharmony_ci	outb(value, cfg + 1);
61162306a36Sopenharmony_ci	if (value != inb(cfg + 1)) {
61262306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": snd_msnd_write_cfg: I/O error\n");
61362306a36Sopenharmony_ci		return -EIO;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci	return 0;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic int snd_msnd_write_cfg_io0(int cfg, int num, u16 io)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
62162306a36Sopenharmony_ci		return -EIO;
62262306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
62362306a36Sopenharmony_ci		return -EIO;
62462306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
62562306a36Sopenharmony_ci		return -EIO;
62662306a36Sopenharmony_ci	return 0;
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic int snd_msnd_write_cfg_io1(int cfg, int num, u16 io)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
63262306a36Sopenharmony_ci		return -EIO;
63362306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
63462306a36Sopenharmony_ci		return -EIO;
63562306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
63662306a36Sopenharmony_ci		return -EIO;
63762306a36Sopenharmony_ci	return 0;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int snd_msnd_write_cfg_irq(int cfg, int num, u16 irq)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
64362306a36Sopenharmony_ci		return -EIO;
64462306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
64562306a36Sopenharmony_ci		return -EIO;
64662306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
64762306a36Sopenharmony_ci		return -EIO;
64862306a36Sopenharmony_ci	return 0;
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic int snd_msnd_write_cfg_mem(int cfg, int num, int mem)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	u16 wmem;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	mem >>= 8;
65662306a36Sopenharmony_ci	wmem = (u16)(mem & 0xfff);
65762306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
65862306a36Sopenharmony_ci		return -EIO;
65962306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
66062306a36Sopenharmony_ci		return -EIO;
66162306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
66262306a36Sopenharmony_ci		return -EIO;
66362306a36Sopenharmony_ci	if (wmem && snd_msnd_write_cfg(cfg, IREG_MEMCONTROL,
66462306a36Sopenharmony_ci				       MEMTYPE_HIADDR | MEMTYPE_16BIT))
66562306a36Sopenharmony_ci		return -EIO;
66662306a36Sopenharmony_ci	return 0;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int snd_msnd_activate_logical(int cfg, int num)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
67262306a36Sopenharmony_ci		return -EIO;
67362306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
67462306a36Sopenharmony_ci		return -EIO;
67562306a36Sopenharmony_ci	return 0;
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_cistatic int snd_msnd_write_cfg_logical(int cfg, int num, u16 io0,
67962306a36Sopenharmony_ci				      u16 io1, u16 irq, int mem)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
68262306a36Sopenharmony_ci		return -EIO;
68362306a36Sopenharmony_ci	if (snd_msnd_write_cfg_io0(cfg, num, io0))
68462306a36Sopenharmony_ci		return -EIO;
68562306a36Sopenharmony_ci	if (snd_msnd_write_cfg_io1(cfg, num, io1))
68662306a36Sopenharmony_ci		return -EIO;
68762306a36Sopenharmony_ci	if (snd_msnd_write_cfg_irq(cfg, num, irq))
68862306a36Sopenharmony_ci		return -EIO;
68962306a36Sopenharmony_ci	if (snd_msnd_write_cfg_mem(cfg, num, mem))
69062306a36Sopenharmony_ci		return -EIO;
69162306a36Sopenharmony_ci	if (snd_msnd_activate_logical(cfg, num))
69262306a36Sopenharmony_ci		return -EIO;
69362306a36Sopenharmony_ci	return 0;
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic int snd_msnd_pinnacle_cfg_reset(int cfg)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	int i;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/* Reset devices if told to */
70162306a36Sopenharmony_ci	printk(KERN_INFO LOGNAME ": Resetting all devices\n");
70262306a36Sopenharmony_ci	for (i = 0; i < 4; ++i)
70362306a36Sopenharmony_ci		if (snd_msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
70462306a36Sopenharmony_ci			return -EIO;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return 0;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci#endif
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
71162306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
71462306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for msnd_pinnacle soundcard.");
71562306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
71662306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for msnd_pinnacle soundcard.");
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
71962306a36Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
72062306a36Sopenharmony_cistatic long mem[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci#ifndef MSND_CLASSIC
72362306a36Sopenharmony_cistatic long cfg[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci/* Extra Peripheral Configuration (Default: Disable) */
72662306a36Sopenharmony_cistatic long ide_io0[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
72762306a36Sopenharmony_cistatic long ide_io1[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
72862306a36Sopenharmony_cistatic int ide_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic long joystick_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
73162306a36Sopenharmony_ci/* If we have the digital daugherboard... */
73262306a36Sopenharmony_cistatic int digital[SNDRV_CARDS];
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci/* Extra Peripheral Configuration */
73562306a36Sopenharmony_cistatic int reset[SNDRV_CARDS];
73662306a36Sopenharmony_ci#endif
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic int write_ndelay[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 1 };
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic int calibrate_signal;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci#ifdef CONFIG_PNP
74362306a36Sopenharmony_cistatic bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
74462306a36Sopenharmony_cimodule_param_array(isapnp, bool, NULL, 0444);
74562306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
74662306a36Sopenharmony_ci#define has_isapnp(x) isapnp[x]
74762306a36Sopenharmony_ci#else
74862306a36Sopenharmony_ci#define has_isapnp(x) 0
74962306a36Sopenharmony_ci#endif
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ciMODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>");
75262306a36Sopenharmony_ciMODULE_DESCRIPTION("Turtle Beach " LONGNAME " Linux Driver");
75362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
75462306a36Sopenharmony_ciMODULE_FIRMWARE(INITCODEFILE);
75562306a36Sopenharmony_ciMODULE_FIRMWARE(PERMCODEFILE);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cimodule_param_hw_array(io, long, ioport, NULL, 0444);
75862306a36Sopenharmony_ciMODULE_PARM_DESC(io, "IO port #");
75962306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444);
76062306a36Sopenharmony_cimodule_param_hw_array(mem, long, iomem, NULL, 0444);
76162306a36Sopenharmony_cimodule_param_array(write_ndelay, int, NULL, 0444);
76262306a36Sopenharmony_cimodule_param(calibrate_signal, int, 0444);
76362306a36Sopenharmony_ci#ifndef MSND_CLASSIC
76462306a36Sopenharmony_cimodule_param_array(digital, int, NULL, 0444);
76562306a36Sopenharmony_cimodule_param_hw_array(cfg, long, ioport, NULL, 0444);
76662306a36Sopenharmony_cimodule_param_array(reset, int, NULL, 0444);
76762306a36Sopenharmony_cimodule_param_hw_array(mpu_io, long, ioport, NULL, 0444);
76862306a36Sopenharmony_cimodule_param_hw_array(mpu_irq, int, irq, NULL, 0444);
76962306a36Sopenharmony_cimodule_param_hw_array(ide_io0, long, ioport, NULL, 0444);
77062306a36Sopenharmony_cimodule_param_hw_array(ide_io1, long, ioport, NULL, 0444);
77162306a36Sopenharmony_cimodule_param_hw_array(ide_irq, int, irq, NULL, 0444);
77262306a36Sopenharmony_cimodule_param_hw_array(joystick_io, long, ioport, NULL, 0444);
77362306a36Sopenharmony_ci#endif
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic int snd_msnd_isa_match(struct device *pdev, unsigned int i)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	if (io[i] == SNDRV_AUTO_PORT)
77962306a36Sopenharmony_ci		return 0;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (irq[i] == SNDRV_AUTO_PORT || mem[i] == SNDRV_AUTO_PORT) {
78262306a36Sopenharmony_ci		printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
78362306a36Sopenharmony_ci		return 0;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci#ifdef MSND_CLASSIC
78762306a36Sopenharmony_ci	if (!(io[i] == 0x290 ||
78862306a36Sopenharmony_ci	      io[i] == 0x260 ||
78962306a36Sopenharmony_ci	      io[i] == 0x250 ||
79062306a36Sopenharmony_ci	      io[i] == 0x240 ||
79162306a36Sopenharmony_ci	      io[i] == 0x230 ||
79262306a36Sopenharmony_ci	      io[i] == 0x220 ||
79362306a36Sopenharmony_ci	      io[i] == 0x210 ||
79462306a36Sopenharmony_ci	      io[i] == 0x3e0)) {
79562306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set "
79662306a36Sopenharmony_ci			" to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, "
79762306a36Sopenharmony_ci			"or 0x3E0\n");
79862306a36Sopenharmony_ci		return 0;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci#else
80162306a36Sopenharmony_ci	if (io[i] < 0x100 || io[i] > 0x3e0 || (io[i] % 0x10) != 0) {
80262306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME
80362306a36Sopenharmony_ci			": \"io\" - DSP I/O base must within the range 0x100 "
80462306a36Sopenharmony_ci			"to 0x3E0 and must be evenly divisible by 0x10\n");
80562306a36Sopenharmony_ci		return 0;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci#endif /* MSND_CLASSIC */
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (!(irq[i] == 5 ||
81062306a36Sopenharmony_ci	      irq[i] == 7 ||
81162306a36Sopenharmony_ci	      irq[i] == 9 ||
81262306a36Sopenharmony_ci	      irq[i] == 10 ||
81362306a36Sopenharmony_ci	      irq[i] == 11 ||
81462306a36Sopenharmony_ci	      irq[i] == 12)) {
81562306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME
81662306a36Sopenharmony_ci			": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
81762306a36Sopenharmony_ci		return 0;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	if (!(mem[i] == 0xb0000 ||
82162306a36Sopenharmony_ci	      mem[i] == 0xc8000 ||
82262306a36Sopenharmony_ci	      mem[i] == 0xd0000 ||
82362306a36Sopenharmony_ci	      mem[i] == 0xd8000 ||
82462306a36Sopenharmony_ci	      mem[i] == 0xe0000 ||
82562306a36Sopenharmony_ci	      mem[i] == 0xe8000)) {
82662306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
82762306a36Sopenharmony_ci		       "0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or "
82862306a36Sopenharmony_ci		       "0xe8000\n");
82962306a36Sopenharmony_ci		return 0;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci#ifndef MSND_CLASSIC
83362306a36Sopenharmony_ci	if (cfg[i] == SNDRV_AUTO_PORT) {
83462306a36Sopenharmony_ci		printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
83562306a36Sopenharmony_ci	} else if (cfg[i] != 0x250 && cfg[i] != 0x260 && cfg[i] != 0x270) {
83662306a36Sopenharmony_ci		printk(KERN_INFO LOGNAME
83762306a36Sopenharmony_ci			": Config port must be 0x250, 0x260 or 0x270 "
83862306a36Sopenharmony_ci			"(or unspecified for PnP mode)\n");
83962306a36Sopenharmony_ci		return 0;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci#endif /* MSND_CLASSIC */
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	return 1;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic int snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	int err;
84962306a36Sopenharmony_ci	struct snd_card *card;
85062306a36Sopenharmony_ci	struct snd_msnd *chip;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (has_isapnp(idx)
85362306a36Sopenharmony_ci#ifndef MSND_CLASSIC
85462306a36Sopenharmony_ci	    || cfg[idx] == SNDRV_AUTO_PORT
85562306a36Sopenharmony_ci#endif
85662306a36Sopenharmony_ci	    ) {
85762306a36Sopenharmony_ci		printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
85862306a36Sopenharmony_ci		return -ENODEV;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	err = snd_devm_card_new(pdev, index[idx], id[idx], THIS_MODULE,
86262306a36Sopenharmony_ci				sizeof(struct snd_msnd), &card);
86362306a36Sopenharmony_ci	if (err < 0)
86462306a36Sopenharmony_ci		return err;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	chip = card->private_data;
86762306a36Sopenharmony_ci	chip->card = card;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci#ifdef MSND_CLASSIC
87062306a36Sopenharmony_ci	switch (irq[idx]) {
87162306a36Sopenharmony_ci	case 5:
87262306a36Sopenharmony_ci		chip->irqid = HPIRQ_5; break;
87362306a36Sopenharmony_ci	case 7:
87462306a36Sopenharmony_ci		chip->irqid = HPIRQ_7; break;
87562306a36Sopenharmony_ci	case 9:
87662306a36Sopenharmony_ci		chip->irqid = HPIRQ_9; break;
87762306a36Sopenharmony_ci	case 10:
87862306a36Sopenharmony_ci		chip->irqid = HPIRQ_10; break;
87962306a36Sopenharmony_ci	case 11:
88062306a36Sopenharmony_ci		chip->irqid = HPIRQ_11; break;
88162306a36Sopenharmony_ci	case 12:
88262306a36Sopenharmony_ci		chip->irqid = HPIRQ_12; break;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	switch (mem[idx]) {
88662306a36Sopenharmony_ci	case 0xb0000:
88762306a36Sopenharmony_ci		chip->memid = HPMEM_B000; break;
88862306a36Sopenharmony_ci	case 0xc8000:
88962306a36Sopenharmony_ci		chip->memid = HPMEM_C800; break;
89062306a36Sopenharmony_ci	case 0xd0000:
89162306a36Sopenharmony_ci		chip->memid = HPMEM_D000; break;
89262306a36Sopenharmony_ci	case 0xd8000:
89362306a36Sopenharmony_ci		chip->memid = HPMEM_D800; break;
89462306a36Sopenharmony_ci	case 0xe0000:
89562306a36Sopenharmony_ci		chip->memid = HPMEM_E000; break;
89662306a36Sopenharmony_ci	case 0xe8000:
89762306a36Sopenharmony_ci		chip->memid = HPMEM_E800; break;
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci#else
90062306a36Sopenharmony_ci	printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%lx\n",
90162306a36Sopenharmony_ci			cfg[idx]);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if (!devm_request_region(card->dev, cfg[idx], 2,
90462306a36Sopenharmony_ci				 "Pinnacle/Fiji Config")) {
90562306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": Config port 0x%lx conflict\n",
90662306a36Sopenharmony_ci			   cfg[idx]);
90762306a36Sopenharmony_ci		return -EIO;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci	if (reset[idx])
91062306a36Sopenharmony_ci		if (snd_msnd_pinnacle_cfg_reset(cfg[idx]))
91162306a36Sopenharmony_ci			return -EIO;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	/* DSP */
91462306a36Sopenharmony_ci	err = snd_msnd_write_cfg_logical(cfg[idx], 0,
91562306a36Sopenharmony_ci					 io[idx], 0,
91662306a36Sopenharmony_ci					 irq[idx], mem[idx]);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (err)
91962306a36Sopenharmony_ci		return err;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	/* The following are Pinnacle specific */
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	/* MPU */
92462306a36Sopenharmony_ci	if (mpu_io[idx] != SNDRV_AUTO_PORT
92562306a36Sopenharmony_ci	    && mpu_irq[idx] != SNDRV_AUTO_IRQ) {
92662306a36Sopenharmony_ci		printk(KERN_INFO LOGNAME
92762306a36Sopenharmony_ci		       ": Configuring MPU to I/O 0x%lx IRQ %d\n",
92862306a36Sopenharmony_ci		       mpu_io[idx], mpu_irq[idx]);
92962306a36Sopenharmony_ci		err = snd_msnd_write_cfg_logical(cfg[idx], 1,
93062306a36Sopenharmony_ci						 mpu_io[idx], 0,
93162306a36Sopenharmony_ci						 mpu_irq[idx], 0);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		if (err)
93462306a36Sopenharmony_ci			return err;
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	/* IDE */
93862306a36Sopenharmony_ci	if (ide_io0[idx] != SNDRV_AUTO_PORT
93962306a36Sopenharmony_ci	    && ide_io1[idx] != SNDRV_AUTO_PORT
94062306a36Sopenharmony_ci	    && ide_irq[idx] != SNDRV_AUTO_IRQ) {
94162306a36Sopenharmony_ci		printk(KERN_INFO LOGNAME
94262306a36Sopenharmony_ci		       ": Configuring IDE to I/O 0x%lx, 0x%lx IRQ %d\n",
94362306a36Sopenharmony_ci		       ide_io0[idx], ide_io1[idx], ide_irq[idx]);
94462306a36Sopenharmony_ci		err = snd_msnd_write_cfg_logical(cfg[idx], 2,
94562306a36Sopenharmony_ci						 ide_io0[idx], ide_io1[idx],
94662306a36Sopenharmony_ci						 ide_irq[idx], 0);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci		if (err)
94962306a36Sopenharmony_ci			return err;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* Joystick */
95362306a36Sopenharmony_ci	if (joystick_io[idx] != SNDRV_AUTO_PORT) {
95462306a36Sopenharmony_ci		printk(KERN_INFO LOGNAME
95562306a36Sopenharmony_ci		       ": Configuring joystick to I/O 0x%lx\n",
95662306a36Sopenharmony_ci		       joystick_io[idx]);
95762306a36Sopenharmony_ci		err = snd_msnd_write_cfg_logical(cfg[idx], 3,
95862306a36Sopenharmony_ci						 joystick_io[idx], 0,
95962306a36Sopenharmony_ci						 0, 0);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci		if (err)
96262306a36Sopenharmony_ci			return err;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci#endif /* MSND_CLASSIC */
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	set_default_audio_parameters(chip);
96862306a36Sopenharmony_ci#ifdef MSND_CLASSIC
96962306a36Sopenharmony_ci	chip->type = msndClassic;
97062306a36Sopenharmony_ci#else
97162306a36Sopenharmony_ci	chip->type = msndPinnacle;
97262306a36Sopenharmony_ci#endif
97362306a36Sopenharmony_ci	chip->io = io[idx];
97462306a36Sopenharmony_ci	chip->irq = irq[idx];
97562306a36Sopenharmony_ci	chip->base = mem[idx];
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	chip->calibrate_signal = calibrate_signal ? 1 : 0;
97862306a36Sopenharmony_ci	chip->recsrc = 0;
97962306a36Sopenharmony_ci	chip->dspq_data_buff = DSPQ_DATA_BUFF;
98062306a36Sopenharmony_ci	chip->dspq_buff_size = DSPQ_BUFF_SIZE;
98162306a36Sopenharmony_ci	if (write_ndelay[idx])
98262306a36Sopenharmony_ci		clear_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
98362306a36Sopenharmony_ci	else
98462306a36Sopenharmony_ci		set_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
98562306a36Sopenharmony_ci#ifndef MSND_CLASSIC
98662306a36Sopenharmony_ci	if (digital[idx])
98762306a36Sopenharmony_ci		set_bit(F_HAVEDIGITAL, &chip->flags);
98862306a36Sopenharmony_ci#endif
98962306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
99062306a36Sopenharmony_ci	err = snd_msnd_probe(card);
99162306a36Sopenharmony_ci	if (err < 0) {
99262306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": Probe failed\n");
99362306a36Sopenharmony_ci		return err;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	err = snd_msnd_attach(card);
99762306a36Sopenharmony_ci	if (err < 0) {
99862306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": Attach failed\n");
99962306a36Sopenharmony_ci		return err;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci	dev_set_drvdata(pdev, card);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	return 0;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic struct isa_driver snd_msnd_driver = {
100762306a36Sopenharmony_ci	.match		= snd_msnd_isa_match,
100862306a36Sopenharmony_ci	.probe		= snd_msnd_isa_probe,
100962306a36Sopenharmony_ci	/* FIXME: suspend, resume */
101062306a36Sopenharmony_ci	.driver		= {
101162306a36Sopenharmony_ci		.name	= DEV_NAME
101262306a36Sopenharmony_ci	},
101362306a36Sopenharmony_ci};
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci#ifdef CONFIG_PNP
101662306a36Sopenharmony_cistatic int snd_msnd_pnp_detect(struct pnp_card_link *pcard,
101762306a36Sopenharmony_ci			       const struct pnp_card_device_id *pid)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	static int idx;
102062306a36Sopenharmony_ci	struct pnp_dev *pnp_dev;
102162306a36Sopenharmony_ci	struct pnp_dev *mpu_dev;
102262306a36Sopenharmony_ci	struct snd_card *card;
102362306a36Sopenharmony_ci	struct snd_msnd *chip;
102462306a36Sopenharmony_ci	int ret;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	for ( ; idx < SNDRV_CARDS; idx++) {
102762306a36Sopenharmony_ci		if (has_isapnp(idx))
102862306a36Sopenharmony_ci			break;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci	if (idx >= SNDRV_CARDS)
103162306a36Sopenharmony_ci		return -ENODEV;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/*
103462306a36Sopenharmony_ci	 * Check that we still have room for another sound card ...
103562306a36Sopenharmony_ci	 */
103662306a36Sopenharmony_ci	pnp_dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
103762306a36Sopenharmony_ci	if (!pnp_dev)
103862306a36Sopenharmony_ci		return -ENODEV;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	mpu_dev = pnp_request_card_device(pcard, pid->devs[1].id, NULL);
104162306a36Sopenharmony_ci	if (!mpu_dev)
104262306a36Sopenharmony_ci		return -ENODEV;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	if (!pnp_is_active(pnp_dev) && pnp_activate_dev(pnp_dev) < 0) {
104562306a36Sopenharmony_ci		printk(KERN_INFO "msnd_pinnacle: device is inactive\n");
104662306a36Sopenharmony_ci		return -EBUSY;
104762306a36Sopenharmony_ci	}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (!pnp_is_active(mpu_dev) && pnp_activate_dev(mpu_dev) < 0) {
105062306a36Sopenharmony_ci		printk(KERN_INFO "msnd_pinnacle: MPU device is inactive\n");
105162306a36Sopenharmony_ci		return -EBUSY;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	/*
105562306a36Sopenharmony_ci	 * Create a new ALSA sound card entry, in anticipation
105662306a36Sopenharmony_ci	 * of detecting our hardware ...
105762306a36Sopenharmony_ci	 */
105862306a36Sopenharmony_ci	ret = snd_devm_card_new(&pcard->card->dev,
105962306a36Sopenharmony_ci				index[idx], id[idx], THIS_MODULE,
106062306a36Sopenharmony_ci				sizeof(struct snd_msnd), &card);
106162306a36Sopenharmony_ci	if (ret < 0)
106262306a36Sopenharmony_ci		return ret;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	chip = card->private_data;
106562306a36Sopenharmony_ci	chip->card = card;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	/*
106862306a36Sopenharmony_ci	 * Read the correct parameters off the ISA PnP bus ...
106962306a36Sopenharmony_ci	 */
107062306a36Sopenharmony_ci	io[idx] = pnp_port_start(pnp_dev, 0);
107162306a36Sopenharmony_ci	irq[idx] = pnp_irq(pnp_dev, 0);
107262306a36Sopenharmony_ci	mem[idx] = pnp_mem_start(pnp_dev, 0);
107362306a36Sopenharmony_ci	mpu_io[idx] = pnp_port_start(mpu_dev, 0);
107462306a36Sopenharmony_ci	mpu_irq[idx] = pnp_irq(mpu_dev, 0);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	set_default_audio_parameters(chip);
107762306a36Sopenharmony_ci#ifdef MSND_CLASSIC
107862306a36Sopenharmony_ci	chip->type = msndClassic;
107962306a36Sopenharmony_ci#else
108062306a36Sopenharmony_ci	chip->type = msndPinnacle;
108162306a36Sopenharmony_ci#endif
108262306a36Sopenharmony_ci	chip->io = io[idx];
108362306a36Sopenharmony_ci	chip->irq = irq[idx];
108462306a36Sopenharmony_ci	chip->base = mem[idx];
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	chip->calibrate_signal = calibrate_signal ? 1 : 0;
108762306a36Sopenharmony_ci	chip->recsrc = 0;
108862306a36Sopenharmony_ci	chip->dspq_data_buff = DSPQ_DATA_BUFF;
108962306a36Sopenharmony_ci	chip->dspq_buff_size = DSPQ_BUFF_SIZE;
109062306a36Sopenharmony_ci	if (write_ndelay[idx])
109162306a36Sopenharmony_ci		clear_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
109262306a36Sopenharmony_ci	else
109362306a36Sopenharmony_ci		set_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
109462306a36Sopenharmony_ci#ifndef MSND_CLASSIC
109562306a36Sopenharmony_ci	if (digital[idx])
109662306a36Sopenharmony_ci		set_bit(F_HAVEDIGITAL, &chip->flags);
109762306a36Sopenharmony_ci#endif
109862306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
109962306a36Sopenharmony_ci	ret = snd_msnd_probe(card);
110062306a36Sopenharmony_ci	if (ret < 0) {
110162306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": Probe failed\n");
110262306a36Sopenharmony_ci		return ret;
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	ret = snd_msnd_attach(card);
110662306a36Sopenharmony_ci	if (ret < 0) {
110762306a36Sopenharmony_ci		printk(KERN_ERR LOGNAME ": Attach failed\n");
110862306a36Sopenharmony_ci		return ret;
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	pnp_set_card_drvdata(pcard, card);
111262306a36Sopenharmony_ci	++idx;
111362306a36Sopenharmony_ci	return 0;
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_cistatic int isa_registered;
111762306a36Sopenharmony_cistatic int pnp_registered;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_cistatic const struct pnp_card_device_id msnd_pnpids[] = {
112062306a36Sopenharmony_ci	/* Pinnacle PnP */
112162306a36Sopenharmony_ci	{ .id = "BVJ0440", .devs = { { "TBS0000" }, { "TBS0001" } } },
112262306a36Sopenharmony_ci	{ .id = "" }	/* end */
112362306a36Sopenharmony_ci};
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, msnd_pnpids);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_cistatic struct pnp_card_driver msnd_pnpc_driver = {
112862306a36Sopenharmony_ci	.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
112962306a36Sopenharmony_ci	.name = "msnd_pinnacle",
113062306a36Sopenharmony_ci	.id_table = msnd_pnpids,
113162306a36Sopenharmony_ci	.probe = snd_msnd_pnp_detect,
113262306a36Sopenharmony_ci};
113362306a36Sopenharmony_ci#endif /* CONFIG_PNP */
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_cistatic int __init snd_msnd_init(void)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	int err;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	err = isa_register_driver(&snd_msnd_driver, SNDRV_CARDS);
114062306a36Sopenharmony_ci#ifdef CONFIG_PNP
114162306a36Sopenharmony_ci	if (!err)
114262306a36Sopenharmony_ci		isa_registered = 1;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	err = pnp_register_card_driver(&msnd_pnpc_driver);
114562306a36Sopenharmony_ci	if (!err)
114662306a36Sopenharmony_ci		pnp_registered = 1;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (isa_registered)
114962306a36Sopenharmony_ci		err = 0;
115062306a36Sopenharmony_ci#endif
115162306a36Sopenharmony_ci	return err;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic void __exit snd_msnd_exit(void)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci#ifdef CONFIG_PNP
115762306a36Sopenharmony_ci	if (pnp_registered)
115862306a36Sopenharmony_ci		pnp_unregister_card_driver(&msnd_pnpc_driver);
115962306a36Sopenharmony_ci	if (isa_registered)
116062306a36Sopenharmony_ci#endif
116162306a36Sopenharmony_ci		isa_unregister_driver(&snd_msnd_driver);
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_cimodule_init(snd_msnd_init);
116562306a36Sopenharmony_cimodule_exit(snd_msnd_exit);
116662306a36Sopenharmony_ci
1167