162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *   Low-level ALSA driver for the ENSONIQ SoundScape
462306a36Sopenharmony_ci *   Copyright (c) by Chris Rankin
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *   This driver was written in part using information obtained from
762306a36Sopenharmony_ci *   the OSS/Free SoundScape driver, written by Hannu Savolainen.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/io.h>
1362306a36Sopenharmony_ci#include <linux/isa.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/firmware.h>
1662306a36Sopenharmony_ci#include <linux/pnp.h>
1762306a36Sopenharmony_ci#include <linux/spinlock.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <asm/dma.h>
2062306a36Sopenharmony_ci#include <sound/core.h>
2162306a36Sopenharmony_ci#include <sound/wss.h>
2262306a36Sopenharmony_ci#include <sound/mpu401.h>
2362306a36Sopenharmony_ci#include <sound/initval.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciMODULE_AUTHOR("Chris Rankin");
2762306a36Sopenharmony_ciMODULE_DESCRIPTION("ENSONIQ SoundScape driver");
2862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2962306a36Sopenharmony_ciMODULE_FIRMWARE("sndscape.co0");
3062306a36Sopenharmony_ciMODULE_FIRMWARE("sndscape.co1");
3162306a36Sopenharmony_ciMODULE_FIRMWARE("sndscape.co2");
3262306a36Sopenharmony_ciMODULE_FIRMWARE("sndscape.co3");
3362306a36Sopenharmony_ciMODULE_FIRMWARE("sndscape.co4");
3462306a36Sopenharmony_ciMODULE_FIRMWARE("scope.cod");
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
3762306a36Sopenharmony_cistatic char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
3862306a36Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
3962306a36Sopenharmony_cistatic long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
4062306a36Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
4162306a36Sopenharmony_cistatic int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
4262306a36Sopenharmony_cistatic int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
4362306a36Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
4462306a36Sopenharmony_cistatic bool joystick[SNDRV_CARDS];
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
4762306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
5062306a36Sopenharmony_ciMODULE_PARM_DESC(id, "Description for SoundScape card");
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444);
5362306a36Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for SoundScape driver.");
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cimodule_param_hw_array(wss_port, long, ioport, NULL, 0444);
5662306a36Sopenharmony_ciMODULE_PARM_DESC(wss_port, "WSS Port # for SoundScape driver.");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444);
5962306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for SoundScape driver.");
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cimodule_param_hw_array(mpu_irq, int, irq, NULL, 0444);
6262306a36Sopenharmony_ciMODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver.");
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cimodule_param_hw_array(dma, int, dma, NULL, 0444);
6562306a36Sopenharmony_ciMODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cimodule_param_hw_array(dma2, int, dma, NULL, 0444);
6862306a36Sopenharmony_ciMODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cimodule_param_array(joystick, bool, NULL, 0444);
7162306a36Sopenharmony_ciMODULE_PARM_DESC(joystick, "Enable gameport.");
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#ifdef CONFIG_PNP
7462306a36Sopenharmony_cistatic int isa_registered;
7562306a36Sopenharmony_cistatic int pnp_registered;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic const struct pnp_card_device_id sscape_pnpids[] = {
7862306a36Sopenharmony_ci	{ .id = "ENS3081", .devs = { { "ENS0000" } } }, /* Soundscape PnP */
7962306a36Sopenharmony_ci	{ .id = "ENS4081", .devs = { { "ENS1011" } } },	/* VIVO90 */
8062306a36Sopenharmony_ci	{ .id = "" }	/* end */
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, sscape_pnpids);
8462306a36Sopenharmony_ci#endif
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define HOST_CTRL_IO(i)  ((i) + 2)
8862306a36Sopenharmony_ci#define HOST_DATA_IO(i)  ((i) + 3)
8962306a36Sopenharmony_ci#define ODIE_ADDR_IO(i)  ((i) + 4)
9062306a36Sopenharmony_ci#define ODIE_DATA_IO(i)  ((i) + 5)
9162306a36Sopenharmony_ci#define CODEC_IO(i)      ((i) + 8)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define IC_ODIE  1
9462306a36Sopenharmony_ci#define IC_OPUS  2
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define RX_READY 0x01
9762306a36Sopenharmony_ci#define TX_READY 0x02
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define CMD_ACK			0x80
10062306a36Sopenharmony_ci#define CMD_SET_MIDI_VOL	0x84
10162306a36Sopenharmony_ci#define CMD_GET_MIDI_VOL	0x85
10262306a36Sopenharmony_ci#define CMD_XXX_MIDI_VOL	0x86
10362306a36Sopenharmony_ci#define CMD_SET_EXTMIDI		0x8a
10462306a36Sopenharmony_ci#define CMD_GET_EXTMIDI		0x8b
10562306a36Sopenharmony_ci#define CMD_SET_MT32		0x8c
10662306a36Sopenharmony_ci#define CMD_GET_MT32		0x8d
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cienum GA_REG {
10962306a36Sopenharmony_ci	GA_INTSTAT_REG = 0,
11062306a36Sopenharmony_ci	GA_INTENA_REG,
11162306a36Sopenharmony_ci	GA_DMAA_REG,
11262306a36Sopenharmony_ci	GA_DMAB_REG,
11362306a36Sopenharmony_ci	GA_INTCFG_REG,
11462306a36Sopenharmony_ci	GA_DMACFG_REG,
11562306a36Sopenharmony_ci	GA_CDCFG_REG,
11662306a36Sopenharmony_ci	GA_SMCFGA_REG,
11762306a36Sopenharmony_ci	GA_SMCFGB_REG,
11862306a36Sopenharmony_ci	GA_HMCTL_REG
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci#define DMA_8BIT  0x80
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cienum card_type {
12562306a36Sopenharmony_ci	MEDIA_FX,	/* Sequoia S-1000 */
12662306a36Sopenharmony_ci	SSCAPE,		/* Sequoia S-2000 */
12762306a36Sopenharmony_ci	SSCAPE_PNP,
12862306a36Sopenharmony_ci	SSCAPE_VIVO,
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistruct soundscape {
13262306a36Sopenharmony_ci	spinlock_t lock;
13362306a36Sopenharmony_ci	unsigned io_base;
13462306a36Sopenharmony_ci	int ic_type;
13562306a36Sopenharmony_ci	enum card_type type;
13662306a36Sopenharmony_ci	struct resource *io_res;
13762306a36Sopenharmony_ci	struct resource *wss_res;
13862306a36Sopenharmony_ci	struct snd_wss *chip;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	unsigned char midi_vol;
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci#define INVALID_IRQ  ((unsigned)-1)
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic inline struct soundscape *get_card_soundscape(struct snd_card *c)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	return (struct soundscape *) (c->private_data);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/*
15262306a36Sopenharmony_ci * Allocates some kernel memory that we can use for DMA.
15362306a36Sopenharmony_ci * I think this means that the memory has to map to
15462306a36Sopenharmony_ci * contiguous pages of physical memory.
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_cistatic struct snd_dma_buffer *get_dmabuf(struct soundscape *s,
15762306a36Sopenharmony_ci					 struct snd_dma_buffer *buf,
15862306a36Sopenharmony_ci					 unsigned long size)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	if (buf) {
16162306a36Sopenharmony_ci		if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV,
16262306a36Sopenharmony_ci						 s->chip->card->dev,
16362306a36Sopenharmony_ci						 size, buf) < 0) {
16462306a36Sopenharmony_ci			snd_printk(KERN_ERR "sscape: Failed to allocate "
16562306a36Sopenharmony_ci					    "%lu bytes for DMA\n",
16662306a36Sopenharmony_ci					    size);
16762306a36Sopenharmony_ci			return NULL;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return buf;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/*
17562306a36Sopenharmony_ci * Release the DMA-able kernel memory ...
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_cistatic void free_dmabuf(struct snd_dma_buffer *buf)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	if (buf && buf->area)
18062306a36Sopenharmony_ci		snd_dma_free_pages(buf);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/*
18462306a36Sopenharmony_ci * This function writes to the SoundScape's control registers,
18562306a36Sopenharmony_ci * but doesn't do any locking. It's up to the caller to do that.
18662306a36Sopenharmony_ci * This is why this function is "unsafe" ...
18762306a36Sopenharmony_ci */
18862306a36Sopenharmony_cistatic inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg,
18962306a36Sopenharmony_ci				       unsigned char val)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	outb(reg, ODIE_ADDR_IO(io_base));
19262306a36Sopenharmony_ci	outb(val, ODIE_DATA_IO(io_base));
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/*
19662306a36Sopenharmony_ci * Write to the SoundScape's control registers, and do the
19762306a36Sopenharmony_ci * necessary locking ...
19862306a36Sopenharmony_ci */
19962306a36Sopenharmony_cistatic void sscape_write(struct soundscape *s, enum GA_REG reg,
20062306a36Sopenharmony_ci			 unsigned char val)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	unsigned long flags;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
20562306a36Sopenharmony_ci	sscape_write_unsafe(s->io_base, reg, val);
20662306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/*
21062306a36Sopenharmony_ci * Read from the SoundScape's control registers, but leave any
21162306a36Sopenharmony_ci * locking to the caller. This is why the function is "unsafe" ...
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_cistatic inline unsigned char sscape_read_unsafe(unsigned io_base,
21462306a36Sopenharmony_ci					       enum GA_REG reg)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	outb(reg, ODIE_ADDR_IO(io_base));
21762306a36Sopenharmony_ci	return inb(ODIE_DATA_IO(io_base));
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/*
22162306a36Sopenharmony_ci * Puts the SoundScape into "host" mode, as compared to "MIDI" mode
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_cistatic inline void set_host_mode_unsafe(unsigned io_base)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	outb(0x0, HOST_CTRL_IO(io_base));
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/*
22962306a36Sopenharmony_ci * Puts the SoundScape into "MIDI" mode, as compared to "host" mode
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_cistatic inline void set_midi_mode_unsafe(unsigned io_base)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	outb(0x3, HOST_CTRL_IO(io_base));
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/*
23762306a36Sopenharmony_ci * Read the SoundScape's host-mode control register, but leave
23862306a36Sopenharmony_ci * any locking issues to the caller ...
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_cistatic inline int host_read_unsafe(unsigned io_base)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	int data = -1;
24362306a36Sopenharmony_ci	if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0)
24462306a36Sopenharmony_ci		data = inb(HOST_DATA_IO(io_base));
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return data;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/*
25062306a36Sopenharmony_ci * Read the SoundScape's host-mode control register, performing
25162306a36Sopenharmony_ci * a limited amount of busy-waiting if the register isn't ready.
25262306a36Sopenharmony_ci * Also leaves all locking-issues to the caller ...
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_cistatic int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	int data;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	while (((data = host_read_unsafe(io_base)) < 0) && (timeout != 0)) {
25962306a36Sopenharmony_ci		udelay(100);
26062306a36Sopenharmony_ci		--timeout;
26162306a36Sopenharmony_ci	} /* while */
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return data;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/*
26762306a36Sopenharmony_ci * Write to the SoundScape's host-mode control registers, but
26862306a36Sopenharmony_ci * leave any locking issues to the caller ...
26962306a36Sopenharmony_ci */
27062306a36Sopenharmony_cistatic inline int host_write_unsafe(unsigned io_base, unsigned char data)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) {
27362306a36Sopenharmony_ci		outb(data, HOST_DATA_IO(io_base));
27462306a36Sopenharmony_ci		return 1;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/*
28162306a36Sopenharmony_ci * Write to the SoundScape's host-mode control registers, performing
28262306a36Sopenharmony_ci * a limited amount of busy-waiting if the register isn't ready.
28362306a36Sopenharmony_ci * Also leaves all locking-issues to the caller ...
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_cistatic int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,
28662306a36Sopenharmony_ci				  unsigned timeout)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	int err;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) {
29162306a36Sopenharmony_ci		udelay(100);
29262306a36Sopenharmony_ci		--timeout;
29362306a36Sopenharmony_ci	} /* while */
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return err;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci/*
30062306a36Sopenharmony_ci * Check that the MIDI subsystem is operational. If it isn't,
30162306a36Sopenharmony_ci * then we will hang the computer if we try to use it ...
30262306a36Sopenharmony_ci *
30362306a36Sopenharmony_ci * NOTE: This check is based upon observation, not documentation.
30462306a36Sopenharmony_ci */
30562306a36Sopenharmony_cistatic inline int verify_mpu401(const struct snd_mpu401 *mpu)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	return ((inb(MPU401C(mpu)) & 0xc0) == 0x80);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/*
31162306a36Sopenharmony_ci * This is apparently the standard way to initialise an MPU-401
31262306a36Sopenharmony_ci */
31362306a36Sopenharmony_cistatic inline void initialise_mpu401(const struct snd_mpu401 *mpu)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	outb(0, MPU401D(mpu));
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/*
31962306a36Sopenharmony_ci * Tell the SoundScape to activate the AD1845 chip (I think).
32062306a36Sopenharmony_ci * The AD1845 detection fails if we *don't* do this, so I
32162306a36Sopenharmony_ci * think that this is a good idea ...
32262306a36Sopenharmony_ci */
32362306a36Sopenharmony_cistatic void activate_ad1845_unsafe(unsigned io_base)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	unsigned char val = sscape_read_unsafe(io_base, GA_HMCTL_REG);
32662306a36Sopenharmony_ci	sscape_write_unsafe(io_base, GA_HMCTL_REG, (val & 0xcf) | 0x10);
32762306a36Sopenharmony_ci	sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci/*
33162306a36Sopenharmony_ci * Tell the SoundScape to begin a DMA transfer using the given channel.
33262306a36Sopenharmony_ci * All locking issues are left to the caller.
33362306a36Sopenharmony_ci */
33462306a36Sopenharmony_cistatic void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	sscape_write_unsafe(io_base, reg,
33762306a36Sopenharmony_ci			    sscape_read_unsafe(io_base, reg) | 0x01);
33862306a36Sopenharmony_ci	sscape_write_unsafe(io_base, reg,
33962306a36Sopenharmony_ci			    sscape_read_unsafe(io_base, reg) & 0xfe);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/*
34362306a36Sopenharmony_ci * Wait for a DMA transfer to complete. This is a "limited busy-wait",
34462306a36Sopenharmony_ci * and all locking issues are left to the caller.
34562306a36Sopenharmony_ci */
34662306a36Sopenharmony_cistatic int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg,
34762306a36Sopenharmony_ci				  unsigned timeout)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) {
35062306a36Sopenharmony_ci		udelay(100);
35162306a36Sopenharmony_ci		--timeout;
35262306a36Sopenharmony_ci	} /* while */
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return sscape_read_unsafe(io_base, reg) & 0x01;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci/*
35862306a36Sopenharmony_ci * Wait for the On-Board Processor to return its start-up
35962306a36Sopenharmony_ci * acknowledgement sequence. This wait is too long for
36062306a36Sopenharmony_ci * us to perform "busy-waiting", and so we must sleep.
36162306a36Sopenharmony_ci * This in turn means that we must not be holding any
36262306a36Sopenharmony_ci * spinlocks when we call this function.
36362306a36Sopenharmony_ci */
36462306a36Sopenharmony_cistatic int obp_startup_ack(struct soundscape *s, unsigned timeout)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	do {
36962306a36Sopenharmony_ci		unsigned long flags;
37062306a36Sopenharmony_ci		int x;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		spin_lock_irqsave(&s->lock, flags);
37362306a36Sopenharmony_ci		x = host_read_unsafe(s->io_base);
37462306a36Sopenharmony_ci		spin_unlock_irqrestore(&s->lock, flags);
37562306a36Sopenharmony_ci		if (x == 0xfe || x == 0xff)
37662306a36Sopenharmony_ci			return 1;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		msleep(10);
37962306a36Sopenharmony_ci	} while (time_before(jiffies, end_time));
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci/*
38562306a36Sopenharmony_ci * Wait for the host to return its start-up acknowledgement
38662306a36Sopenharmony_ci * sequence. This wait is too long for us to perform
38762306a36Sopenharmony_ci * "busy-waiting", and so we must sleep. This in turn means
38862306a36Sopenharmony_ci * that we must not be holding any spinlocks when we call
38962306a36Sopenharmony_ci * this function.
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_cistatic int host_startup_ack(struct soundscape *s, unsigned timeout)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	unsigned long end_time = jiffies + msecs_to_jiffies(timeout);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	do {
39662306a36Sopenharmony_ci		unsigned long flags;
39762306a36Sopenharmony_ci		int x;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		spin_lock_irqsave(&s->lock, flags);
40062306a36Sopenharmony_ci		x = host_read_unsafe(s->io_base);
40162306a36Sopenharmony_ci		spin_unlock_irqrestore(&s->lock, flags);
40262306a36Sopenharmony_ci		if (x == 0xfe)
40362306a36Sopenharmony_ci			return 1;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		msleep(10);
40662306a36Sopenharmony_ci	} while (time_before(jiffies, end_time));
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return 0;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/*
41262306a36Sopenharmony_ci * Upload a byte-stream into the SoundScape using DMA channel A.
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_cistatic int upload_dma_data(struct soundscape *s, const unsigned char *data,
41562306a36Sopenharmony_ci			   size_t size)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	unsigned long flags;
41862306a36Sopenharmony_ci	struct snd_dma_buffer dma;
41962306a36Sopenharmony_ci	int ret;
42062306a36Sopenharmony_ci	unsigned char val;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (!get_dmabuf(s, &dma, PAGE_ALIGN(32 * 1024)))
42362306a36Sopenharmony_ci		return -ENOMEM;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/*
42862306a36Sopenharmony_ci	 * Reset the board ...
42962306a36Sopenharmony_ci	 */
43062306a36Sopenharmony_ci	val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
43162306a36Sopenharmony_ci	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val & 0x3f);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	/*
43462306a36Sopenharmony_ci	 * Enable the DMA channels and configure them ...
43562306a36Sopenharmony_ci	 */
43662306a36Sopenharmony_ci	val = (s->chip->dma1 << 4) | DMA_8BIT;
43762306a36Sopenharmony_ci	sscape_write_unsafe(s->io_base, GA_DMAA_REG, val);
43862306a36Sopenharmony_ci	sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/*
44162306a36Sopenharmony_ci	 * Take the board out of reset ...
44262306a36Sopenharmony_ci	 */
44362306a36Sopenharmony_ci	val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
44462306a36Sopenharmony_ci	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x80);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/*
44762306a36Sopenharmony_ci	 * Upload the firmware to the SoundScape
44862306a36Sopenharmony_ci	 * board through the DMA channel ...
44962306a36Sopenharmony_ci	 */
45062306a36Sopenharmony_ci	while (size != 0) {
45162306a36Sopenharmony_ci		unsigned long len;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		len = min(size, dma.bytes);
45462306a36Sopenharmony_ci		memcpy(dma.area, data, len);
45562306a36Sopenharmony_ci		data += len;
45662306a36Sopenharmony_ci		size -= len;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);
45962306a36Sopenharmony_ci		sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);
46062306a36Sopenharmony_ci		if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {
46162306a36Sopenharmony_ci			/*
46262306a36Sopenharmony_ci			 * Don't forget to release this spinlock we're holding
46362306a36Sopenharmony_ci			 */
46462306a36Sopenharmony_ci			spin_unlock_irqrestore(&s->lock, flags);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci			snd_printk(KERN_ERR
46762306a36Sopenharmony_ci					"sscape: DMA upload has timed out\n");
46862306a36Sopenharmony_ci			ret = -EAGAIN;
46962306a36Sopenharmony_ci			goto _release_dma;
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci	} /* while */
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	set_host_mode_unsafe(s->io_base);
47462306a36Sopenharmony_ci	outb(0x0, s->io_base);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/*
47762306a36Sopenharmony_ci	 * Boot the board ... (I think)
47862306a36Sopenharmony_ci	 */
47962306a36Sopenharmony_ci	val = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
48062306a36Sopenharmony_ci	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, val | 0x40);
48162306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/*
48462306a36Sopenharmony_ci	 * If all has gone well, then the board should acknowledge
48562306a36Sopenharmony_ci	 * the new upload and tell us that it has rebooted OK. We
48662306a36Sopenharmony_ci	 * give it 5 seconds (max) ...
48762306a36Sopenharmony_ci	 */
48862306a36Sopenharmony_ci	ret = 0;
48962306a36Sopenharmony_ci	if (!obp_startup_ack(s, 5000)) {
49062306a36Sopenharmony_ci		snd_printk(KERN_ERR "sscape: No response "
49162306a36Sopenharmony_ci				    "from on-board processor after upload\n");
49262306a36Sopenharmony_ci		ret = -EAGAIN;
49362306a36Sopenharmony_ci	} else if (!host_startup_ack(s, 5000)) {
49462306a36Sopenharmony_ci		snd_printk(KERN_ERR
49562306a36Sopenharmony_ci				"sscape: SoundScape failed to initialise\n");
49662306a36Sopenharmony_ci		ret = -EAGAIN;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci_release_dma:
50062306a36Sopenharmony_ci	/*
50162306a36Sopenharmony_ci	 * NOTE!!! We are NOT holding any spinlocks at this point !!!
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci	sscape_write(s, GA_DMAA_REG, (s->ic_type == IC_OPUS ? 0x40 : 0x70));
50462306a36Sopenharmony_ci	free_dmabuf(&dma);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return ret;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci/*
51062306a36Sopenharmony_ci * Upload the bootblock(?) into the SoundScape. The only
51162306a36Sopenharmony_ci * purpose of this block of code seems to be to tell
51262306a36Sopenharmony_ci * us which version of the microcode we should be using.
51362306a36Sopenharmony_ci */
51462306a36Sopenharmony_cistatic int sscape_upload_bootblock(struct snd_card *card)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	struct soundscape *sscape = get_card_soundscape(card);
51762306a36Sopenharmony_ci	unsigned long flags;
51862306a36Sopenharmony_ci	const struct firmware *init_fw = NULL;
51962306a36Sopenharmony_ci	int data = 0;
52062306a36Sopenharmony_ci	int ret;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	ret = request_firmware(&init_fw, "scope.cod", card->dev);
52362306a36Sopenharmony_ci	if (ret < 0) {
52462306a36Sopenharmony_ci		snd_printk(KERN_ERR "sscape: Error loading scope.cod");
52562306a36Sopenharmony_ci		return ret;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci	ret = upload_dma_data(sscape, init_fw->data, init_fw->size);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	release_firmware(init_fw);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	spin_lock_irqsave(&sscape->lock, flags);
53262306a36Sopenharmony_ci	if (ret == 0)
53362306a36Sopenharmony_ci		data = host_read_ctrl_unsafe(sscape->io_base, 100);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (data & 0x10)
53662306a36Sopenharmony_ci		sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2f);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	spin_unlock_irqrestore(&sscape->lock, flags);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	data &= 0xf;
54162306a36Sopenharmony_ci	if (ret == 0 && data > 7) {
54262306a36Sopenharmony_ci		snd_printk(KERN_ERR
54362306a36Sopenharmony_ci				"sscape: timeout reading firmware version\n");
54462306a36Sopenharmony_ci		ret = -EAGAIN;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return (ret == 0) ? data : ret;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/*
55162306a36Sopenharmony_ci * Upload the microcode into the SoundScape.
55262306a36Sopenharmony_ci */
55362306a36Sopenharmony_cistatic int sscape_upload_microcode(struct snd_card *card, int version)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct soundscape *sscape = get_card_soundscape(card);
55662306a36Sopenharmony_ci	const struct firmware *init_fw = NULL;
55762306a36Sopenharmony_ci	char name[14];
55862306a36Sopenharmony_ci	int err;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	scnprintf(name, sizeof(name), "sndscape.co%d", version);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	err = request_firmware(&init_fw, name, card->dev);
56362306a36Sopenharmony_ci	if (err < 0) {
56462306a36Sopenharmony_ci		snd_printk(KERN_ERR "sscape: Error loading sndscape.co%d",
56562306a36Sopenharmony_ci				version);
56662306a36Sopenharmony_ci		return err;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci	err = upload_dma_data(sscape, init_fw->data, init_fw->size);
56962306a36Sopenharmony_ci	if (err == 0)
57062306a36Sopenharmony_ci		snd_printk(KERN_INFO "sscape: MIDI firmware loaded %zu KBs\n",
57162306a36Sopenharmony_ci				init_fw->size >> 10);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	release_firmware(init_fw);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return err;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci/*
57962306a36Sopenharmony_ci * Mixer control for the SoundScape's MIDI device.
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_cistatic int sscape_midi_info(struct snd_kcontrol *ctl,
58262306a36Sopenharmony_ci			    struct snd_ctl_elem_info *uinfo)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
58562306a36Sopenharmony_ci	uinfo->count = 1;
58662306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
58762306a36Sopenharmony_ci	uinfo->value.integer.max = 127;
58862306a36Sopenharmony_ci	return 0;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic int sscape_midi_get(struct snd_kcontrol *kctl,
59262306a36Sopenharmony_ci			   struct snd_ctl_elem_value *uctl)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	struct snd_wss *chip = snd_kcontrol_chip(kctl);
59562306a36Sopenharmony_ci	struct snd_card *card = chip->card;
59662306a36Sopenharmony_ci	register struct soundscape *s = get_card_soundscape(card);
59762306a36Sopenharmony_ci	unsigned long flags;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
60062306a36Sopenharmony_ci	uctl->value.integer.value[0] = s->midi_vol;
60162306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
60262306a36Sopenharmony_ci	return 0;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic int sscape_midi_put(struct snd_kcontrol *kctl,
60662306a36Sopenharmony_ci			   struct snd_ctl_elem_value *uctl)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct snd_wss *chip = snd_kcontrol_chip(kctl);
60962306a36Sopenharmony_ci	struct snd_card *card = chip->card;
61062306a36Sopenharmony_ci	struct soundscape *s = get_card_soundscape(card);
61162306a36Sopenharmony_ci	unsigned long flags;
61262306a36Sopenharmony_ci	int change;
61362306a36Sopenharmony_ci	unsigned char new_val;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	new_val = uctl->value.integer.value[0] & 127;
61862306a36Sopenharmony_ci	/*
61962306a36Sopenharmony_ci	 * We need to put the board into HOST mode before we
62062306a36Sopenharmony_ci	 * can send any volume-changing HOST commands ...
62162306a36Sopenharmony_ci	 */
62262306a36Sopenharmony_ci	set_host_mode_unsafe(s->io_base);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/*
62562306a36Sopenharmony_ci	 * To successfully change the MIDI volume setting, you seem to
62662306a36Sopenharmony_ci	 * have to write a volume command, write the new volume value,
62762306a36Sopenharmony_ci	 * and then perform another volume-related command. Perhaps the
62862306a36Sopenharmony_ci	 * first command is an "open" and the second command is a "close"?
62962306a36Sopenharmony_ci	 */
63062306a36Sopenharmony_ci	if (s->midi_vol == new_val) {
63162306a36Sopenharmony_ci		change = 0;
63262306a36Sopenharmony_ci		goto __skip_change;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci	change = host_write_ctrl_unsafe(s->io_base, CMD_SET_MIDI_VOL, 100)
63562306a36Sopenharmony_ci		 && host_write_ctrl_unsafe(s->io_base, new_val, 100)
63662306a36Sopenharmony_ci		 && host_write_ctrl_unsafe(s->io_base, CMD_XXX_MIDI_VOL, 100)
63762306a36Sopenharmony_ci		 && host_write_ctrl_unsafe(s->io_base, new_val, 100);
63862306a36Sopenharmony_ci	s->midi_vol = new_val;
63962306a36Sopenharmony_ci__skip_change:
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/*
64262306a36Sopenharmony_ci	 * Take the board out of HOST mode and back into MIDI mode ...
64362306a36Sopenharmony_ci	 */
64462306a36Sopenharmony_ci	set_midi_mode_unsafe(s->io_base);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
64762306a36Sopenharmony_ci	return change;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic const struct snd_kcontrol_new midi_mixer_ctl = {
65162306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
65262306a36Sopenharmony_ci	.name = "MIDI",
65362306a36Sopenharmony_ci	.info = sscape_midi_info,
65462306a36Sopenharmony_ci	.get = sscape_midi_get,
65562306a36Sopenharmony_ci	.put = sscape_midi_put
65662306a36Sopenharmony_ci};
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci/*
65962306a36Sopenharmony_ci * The SoundScape can use two IRQs from a possible set of four.
66062306a36Sopenharmony_ci * These IRQs are encoded as bit patterns so that they can be
66162306a36Sopenharmony_ci * written to the control registers.
66262306a36Sopenharmony_ci */
66362306a36Sopenharmony_cistatic unsigned get_irq_config(int sscape_type, int irq)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	static const int valid_irq[] = { 9, 5, 7, 10 };
66662306a36Sopenharmony_ci	static const int old_irq[] = { 9, 7, 5, 15 };
66762306a36Sopenharmony_ci	unsigned cfg;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (sscape_type == MEDIA_FX) {
67062306a36Sopenharmony_ci		for (cfg = 0; cfg < ARRAY_SIZE(old_irq); ++cfg)
67162306a36Sopenharmony_ci			if (irq == old_irq[cfg])
67262306a36Sopenharmony_ci				return cfg;
67362306a36Sopenharmony_ci	} else {
67462306a36Sopenharmony_ci		for (cfg = 0; cfg < ARRAY_SIZE(valid_irq); ++cfg)
67562306a36Sopenharmony_ci			if (irq == valid_irq[cfg])
67662306a36Sopenharmony_ci				return cfg;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return INVALID_IRQ;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/*
68362306a36Sopenharmony_ci * Perform certain arcane port-checks to see whether there
68462306a36Sopenharmony_ci * is a SoundScape board lurking behind the given ports.
68562306a36Sopenharmony_ci */
68662306a36Sopenharmony_cistatic int detect_sscape(struct soundscape *s, long wss_io)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	unsigned long flags;
68962306a36Sopenharmony_ci	unsigned d;
69062306a36Sopenharmony_ci	int retval = 0;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	spin_lock_irqsave(&s->lock, flags);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	/*
69562306a36Sopenharmony_ci	 * The following code is lifted from the original OSS driver,
69662306a36Sopenharmony_ci	 * and as I don't have a datasheet I cannot really comment
69762306a36Sopenharmony_ci	 * on what it is doing...
69862306a36Sopenharmony_ci	 */
69962306a36Sopenharmony_ci	if ((inb(HOST_CTRL_IO(s->io_base)) & 0x78) != 0)
70062306a36Sopenharmony_ci		goto _done;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	d = inb(ODIE_ADDR_IO(s->io_base)) & 0xf0;
70362306a36Sopenharmony_ci	if ((d & 0x80) != 0)
70462306a36Sopenharmony_ci		goto _done;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (d == 0)
70762306a36Sopenharmony_ci		s->ic_type = IC_ODIE;
70862306a36Sopenharmony_ci	else if ((d & 0x60) != 0)
70962306a36Sopenharmony_ci		s->ic_type = IC_OPUS;
71062306a36Sopenharmony_ci	else
71162306a36Sopenharmony_ci		goto _done;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	outb(0xfa, ODIE_ADDR_IO(s->io_base));
71462306a36Sopenharmony_ci	if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0a)
71562306a36Sopenharmony_ci		goto _done;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	outb(0xfe, ODIE_ADDR_IO(s->io_base));
71862306a36Sopenharmony_ci	if ((inb(ODIE_ADDR_IO(s->io_base)) & 0x9f) != 0x0e)
71962306a36Sopenharmony_ci		goto _done;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	outb(0xfe, ODIE_ADDR_IO(s->io_base));
72262306a36Sopenharmony_ci	d = inb(ODIE_DATA_IO(s->io_base));
72362306a36Sopenharmony_ci	if (s->type != SSCAPE_VIVO && (d & 0x9f) != 0x0e)
72462306a36Sopenharmony_ci		goto _done;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (s->ic_type == IC_OPUS)
72762306a36Sopenharmony_ci		activate_ad1845_unsafe(s->io_base);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (s->type == SSCAPE_VIVO)
73062306a36Sopenharmony_ci		wss_io += 4;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	d  = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
73362306a36Sopenharmony_ci	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	/* wait for WSS codec */
73662306a36Sopenharmony_ci	for (d = 0; d < 500; d++) {
73762306a36Sopenharmony_ci		if ((inb(wss_io) & 0x80) == 0)
73862306a36Sopenharmony_ci			break;
73962306a36Sopenharmony_ci		spin_unlock_irqrestore(&s->lock, flags);
74062306a36Sopenharmony_ci		msleep(1);
74162306a36Sopenharmony_ci		spin_lock_irqsave(&s->lock, flags);
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if ((inb(wss_io) & 0x80) != 0)
74562306a36Sopenharmony_ci		goto _done;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	if (inb(wss_io + 2) == 0xff)
74862306a36Sopenharmony_ci		goto _done;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	d  = sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f;
75162306a36Sopenharmony_ci	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	if ((inb(wss_io) & 0x80) != 0)
75462306a36Sopenharmony_ci		s->type = MEDIA_FX;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	d = sscape_read_unsafe(s->io_base, GA_HMCTL_REG);
75762306a36Sopenharmony_ci	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, d | 0xc0);
75862306a36Sopenharmony_ci	/* wait for WSS codec */
75962306a36Sopenharmony_ci	for (d = 0; d < 500; d++) {
76062306a36Sopenharmony_ci		if ((inb(wss_io) & 0x80) == 0)
76162306a36Sopenharmony_ci			break;
76262306a36Sopenharmony_ci		spin_unlock_irqrestore(&s->lock, flags);
76362306a36Sopenharmony_ci		msleep(1);
76462306a36Sopenharmony_ci		spin_lock_irqsave(&s->lock, flags);
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/*
76862306a36Sopenharmony_ci	 * SoundScape successfully detected!
76962306a36Sopenharmony_ci	 */
77062306a36Sopenharmony_ci	retval = 1;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci_done:
77362306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->lock, flags);
77462306a36Sopenharmony_ci	return retval;
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci/*
77862306a36Sopenharmony_ci * ALSA callback function, called when attempting to open the MIDI device.
77962306a36Sopenharmony_ci * Check that the MIDI firmware has been loaded, because we don't want
78062306a36Sopenharmony_ci * to crash the machine. Also check that someone isn't using the hardware
78162306a36Sopenharmony_ci * IOCTL device.
78262306a36Sopenharmony_ci */
78362306a36Sopenharmony_cistatic int mpu401_open(struct snd_mpu401 *mpu)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	if (!verify_mpu401(mpu)) {
78662306a36Sopenharmony_ci		snd_printk(KERN_ERR "sscape: MIDI disabled, "
78762306a36Sopenharmony_ci				    "please load firmware\n");
78862306a36Sopenharmony_ci		return -ENODEV;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	return 0;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci/*
79562306a36Sopenharmony_ci * Initialise an MPU-401 subdevice for MIDI support on the SoundScape.
79662306a36Sopenharmony_ci */
79762306a36Sopenharmony_cistatic int create_mpu401(struct snd_card *card, int devnum,
79862306a36Sopenharmony_ci			 unsigned long port, int irq)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct soundscape *sscape = get_card_soundscape(card);
80162306a36Sopenharmony_ci	struct snd_rawmidi *rawmidi;
80262306a36Sopenharmony_ci	int err;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, port,
80562306a36Sopenharmony_ci				  MPU401_INFO_INTEGRATED, irq, &rawmidi);
80662306a36Sopenharmony_ci	if (err == 0) {
80762306a36Sopenharmony_ci		struct snd_mpu401 *mpu = rawmidi->private_data;
80862306a36Sopenharmony_ci		mpu->open_input = mpu401_open;
80962306a36Sopenharmony_ci		mpu->open_output = mpu401_open;
81062306a36Sopenharmony_ci		mpu->private_data = sscape;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		initialise_mpu401(mpu);
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	return err;
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci/*
82062306a36Sopenharmony_ci * Create an AD1845 PCM subdevice on the SoundScape. The AD1845
82162306a36Sopenharmony_ci * is very much like a CS4231, with a few extra bits. We will
82262306a36Sopenharmony_ci * try to support at least some of the extra bits by overriding
82362306a36Sopenharmony_ci * some of the CS4231 callback.
82462306a36Sopenharmony_ci */
82562306a36Sopenharmony_cistatic int create_ad1845(struct snd_card *card, unsigned port,
82662306a36Sopenharmony_ci			 int irq, int dma1, int dma2)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	register struct soundscape *sscape = get_card_soundscape(card);
82962306a36Sopenharmony_ci	struct snd_wss *chip;
83062306a36Sopenharmony_ci	int err;
83162306a36Sopenharmony_ci	int codec_type = WSS_HW_DETECT;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	switch (sscape->type) {
83462306a36Sopenharmony_ci	case MEDIA_FX:
83562306a36Sopenharmony_ci	case SSCAPE:
83662306a36Sopenharmony_ci		/*
83762306a36Sopenharmony_ci		 * There are some freak examples of early Soundscape cards
83862306a36Sopenharmony_ci		 * with CS4231 instead of AD1848/CS4248. Unfortunately, the
83962306a36Sopenharmony_ci		 * CS4231 works only in CS4248 compatibility mode on
84062306a36Sopenharmony_ci		 * these cards so force it.
84162306a36Sopenharmony_ci		 */
84262306a36Sopenharmony_ci		if (sscape->ic_type != IC_OPUS)
84362306a36Sopenharmony_ci			codec_type = WSS_HW_AD1848;
84462306a36Sopenharmony_ci		break;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	case SSCAPE_VIVO:
84762306a36Sopenharmony_ci		port += 4;
84862306a36Sopenharmony_ci		break;
84962306a36Sopenharmony_ci	default:
85062306a36Sopenharmony_ci		break;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	err = snd_wss_create(card, port, -1, irq, dma1, dma2,
85462306a36Sopenharmony_ci			     codec_type, WSS_HWSHARE_DMA1, &chip);
85562306a36Sopenharmony_ci	if (!err) {
85662306a36Sopenharmony_ci		unsigned long flags;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		if (sscape->type != SSCAPE_VIVO) {
85962306a36Sopenharmony_ci			/*
86062306a36Sopenharmony_ci			 * The input clock frequency on the SoundScape must
86162306a36Sopenharmony_ci			 * be 14.31818 MHz, because we must set this register
86262306a36Sopenharmony_ci			 * to get the playback to sound correct ...
86362306a36Sopenharmony_ci			 */
86462306a36Sopenharmony_ci			snd_wss_mce_up(chip);
86562306a36Sopenharmony_ci			spin_lock_irqsave(&chip->reg_lock, flags);
86662306a36Sopenharmony_ci			snd_wss_out(chip, AD1845_CLOCK, 0x20);
86762306a36Sopenharmony_ci			spin_unlock_irqrestore(&chip->reg_lock, flags);
86862306a36Sopenharmony_ci			snd_wss_mce_down(chip);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci		err = snd_wss_pcm(chip, 0);
87362306a36Sopenharmony_ci		if (err < 0) {
87462306a36Sopenharmony_ci			snd_printk(KERN_ERR "sscape: No PCM device "
87562306a36Sopenharmony_ci					    "for AD1845 chip\n");
87662306a36Sopenharmony_ci			goto _error;
87762306a36Sopenharmony_ci		}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci		err = snd_wss_mixer(chip);
88062306a36Sopenharmony_ci		if (err < 0) {
88162306a36Sopenharmony_ci			snd_printk(KERN_ERR "sscape: No mixer device "
88262306a36Sopenharmony_ci					    "for AD1845 chip\n");
88362306a36Sopenharmony_ci			goto _error;
88462306a36Sopenharmony_ci		}
88562306a36Sopenharmony_ci		if (chip->hardware != WSS_HW_AD1848) {
88662306a36Sopenharmony_ci			err = snd_wss_timer(chip, 0);
88762306a36Sopenharmony_ci			if (err < 0) {
88862306a36Sopenharmony_ci				snd_printk(KERN_ERR "sscape: No timer device "
88962306a36Sopenharmony_ci						    "for AD1845 chip\n");
89062306a36Sopenharmony_ci				goto _error;
89162306a36Sopenharmony_ci			}
89262306a36Sopenharmony_ci		}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci		if (sscape->type != SSCAPE_VIVO) {
89562306a36Sopenharmony_ci			err = snd_ctl_add(card,
89662306a36Sopenharmony_ci					  snd_ctl_new1(&midi_mixer_ctl, chip));
89762306a36Sopenharmony_ci			if (err < 0) {
89862306a36Sopenharmony_ci				snd_printk(KERN_ERR "sscape: Could not create "
89962306a36Sopenharmony_ci						    "MIDI mixer control\n");
90062306a36Sopenharmony_ci				goto _error;
90162306a36Sopenharmony_ci			}
90262306a36Sopenharmony_ci		}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		sscape->chip = chip;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci_error:
90862306a36Sopenharmony_ci	return err;
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci/*
91362306a36Sopenharmony_ci * Create an ALSA soundcard entry for the SoundScape, using
91462306a36Sopenharmony_ci * the given list of port, IRQ and DMA resources.
91562306a36Sopenharmony_ci */
91662306a36Sopenharmony_cistatic int create_sscape(int dev, struct snd_card *card)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	struct soundscape *sscape = get_card_soundscape(card);
91962306a36Sopenharmony_ci	unsigned dma_cfg;
92062306a36Sopenharmony_ci	unsigned irq_cfg;
92162306a36Sopenharmony_ci	unsigned mpu_irq_cfg;
92262306a36Sopenharmony_ci	struct resource *io_res;
92362306a36Sopenharmony_ci	struct resource *wss_res;
92462306a36Sopenharmony_ci	unsigned long flags;
92562306a36Sopenharmony_ci	int err;
92662306a36Sopenharmony_ci	int val;
92762306a36Sopenharmony_ci	const char *name;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/*
93062306a36Sopenharmony_ci	 * Grab IO ports that we will need to probe so that we
93162306a36Sopenharmony_ci	 * can detect and control this hardware ...
93262306a36Sopenharmony_ci	 */
93362306a36Sopenharmony_ci	io_res = devm_request_region(card->dev, port[dev], 8, "SoundScape");
93462306a36Sopenharmony_ci	if (!io_res) {
93562306a36Sopenharmony_ci		snd_printk(KERN_ERR
93662306a36Sopenharmony_ci			   "sscape: can't grab port 0x%lx\n", port[dev]);
93762306a36Sopenharmony_ci		return -EBUSY;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci	wss_res = NULL;
94062306a36Sopenharmony_ci	if (sscape->type == SSCAPE_VIVO) {
94162306a36Sopenharmony_ci		wss_res = devm_request_region(card->dev, wss_port[dev], 4,
94262306a36Sopenharmony_ci					      "SoundScape");
94362306a36Sopenharmony_ci		if (!wss_res) {
94462306a36Sopenharmony_ci			snd_printk(KERN_ERR "sscape: can't grab port 0x%lx\n",
94562306a36Sopenharmony_ci					    wss_port[dev]);
94662306a36Sopenharmony_ci			return -EBUSY;
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/*
95162306a36Sopenharmony_ci	 * Grab one DMA channel ...
95262306a36Sopenharmony_ci	 */
95362306a36Sopenharmony_ci	err = snd_devm_request_dma(card->dev, dma[dev], "SoundScape");
95462306a36Sopenharmony_ci	if (err < 0) {
95562306a36Sopenharmony_ci		snd_printk(KERN_ERR "sscape: can't grab DMA %d\n", dma[dev]);
95662306a36Sopenharmony_ci		return err;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	spin_lock_init(&sscape->lock);
96062306a36Sopenharmony_ci	sscape->io_res = io_res;
96162306a36Sopenharmony_ci	sscape->wss_res = wss_res;
96262306a36Sopenharmony_ci	sscape->io_base = port[dev];
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (!detect_sscape(sscape, wss_port[dev])) {
96562306a36Sopenharmony_ci		printk(KERN_ERR "sscape: hardware not detected at 0x%x\n",
96662306a36Sopenharmony_ci			sscape->io_base);
96762306a36Sopenharmony_ci		return -ENODEV;
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	switch (sscape->type) {
97162306a36Sopenharmony_ci	case MEDIA_FX:
97262306a36Sopenharmony_ci		name = "MediaFX/SoundFX";
97362306a36Sopenharmony_ci		break;
97462306a36Sopenharmony_ci	case SSCAPE:
97562306a36Sopenharmony_ci		name = "Soundscape";
97662306a36Sopenharmony_ci		break;
97762306a36Sopenharmony_ci	case SSCAPE_PNP:
97862306a36Sopenharmony_ci		name = "Soundscape PnP";
97962306a36Sopenharmony_ci		break;
98062306a36Sopenharmony_ci	case SSCAPE_VIVO:
98162306a36Sopenharmony_ci		name = "Soundscape VIVO";
98262306a36Sopenharmony_ci		break;
98362306a36Sopenharmony_ci	default:
98462306a36Sopenharmony_ci		name = "unknown Soundscape";
98562306a36Sopenharmony_ci		break;
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	printk(KERN_INFO "sscape: %s card detected at 0x%x, using IRQ %d, DMA %d\n",
98962306a36Sopenharmony_ci			 name, sscape->io_base, irq[dev], dma[dev]);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/*
99262306a36Sopenharmony_ci	 * Check that the user didn't pass us garbage data ...
99362306a36Sopenharmony_ci	 */
99462306a36Sopenharmony_ci	irq_cfg = get_irq_config(sscape->type, irq[dev]);
99562306a36Sopenharmony_ci	if (irq_cfg == INVALID_IRQ) {
99662306a36Sopenharmony_ci		snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", irq[dev]);
99762306a36Sopenharmony_ci		return -ENXIO;
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	mpu_irq_cfg = get_irq_config(sscape->type, mpu_irq[dev]);
100162306a36Sopenharmony_ci	if (mpu_irq_cfg == INVALID_IRQ) {
100262306a36Sopenharmony_ci		snd_printk(KERN_ERR "sscape: Invalid IRQ %d\n", mpu_irq[dev]);
100362306a36Sopenharmony_ci		return -ENXIO;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	/*
100762306a36Sopenharmony_ci	 * Tell the on-board devices where their resources are (I think -
100862306a36Sopenharmony_ci	 * I can't be sure without a datasheet ... So many magic values!)
100962306a36Sopenharmony_ci	 */
101062306a36Sopenharmony_ci	spin_lock_irqsave(&sscape->lock, flags);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	sscape_write_unsafe(sscape->io_base, GA_SMCFGA_REG, 0x2e);
101362306a36Sopenharmony_ci	sscape_write_unsafe(sscape->io_base, GA_SMCFGB_REG, 0x00);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/*
101662306a36Sopenharmony_ci	 * Enable and configure the DMA channels ...
101762306a36Sopenharmony_ci	 */
101862306a36Sopenharmony_ci	sscape_write_unsafe(sscape->io_base, GA_DMACFG_REG, 0x50);
101962306a36Sopenharmony_ci	dma_cfg = (sscape->ic_type == IC_OPUS ? 0x40 : 0x70);
102062306a36Sopenharmony_ci	sscape_write_unsafe(sscape->io_base, GA_DMAA_REG, dma_cfg);
102162306a36Sopenharmony_ci	sscape_write_unsafe(sscape->io_base, GA_DMAB_REG, 0x20);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	mpu_irq_cfg |= mpu_irq_cfg << 2;
102462306a36Sopenharmony_ci	val = sscape_read_unsafe(sscape->io_base, GA_HMCTL_REG) & 0xF7;
102562306a36Sopenharmony_ci	if (joystick[dev])
102662306a36Sopenharmony_ci		val |= 8;
102762306a36Sopenharmony_ci	sscape_write_unsafe(sscape->io_base, GA_HMCTL_REG, val | 0x10);
102862306a36Sopenharmony_ci	sscape_write_unsafe(sscape->io_base, GA_INTCFG_REG, 0xf0 | mpu_irq_cfg);
102962306a36Sopenharmony_ci	sscape_write_unsafe(sscape->io_base,
103062306a36Sopenharmony_ci			    GA_CDCFG_REG, 0x09 | DMA_8BIT
103162306a36Sopenharmony_ci			    | (dma[dev] << 4) | (irq_cfg << 1));
103262306a36Sopenharmony_ci	/*
103362306a36Sopenharmony_ci	 * Enable the master IRQ ...
103462306a36Sopenharmony_ci	 */
103562306a36Sopenharmony_ci	sscape_write_unsafe(sscape->io_base, GA_INTENA_REG, 0x80);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	spin_unlock_irqrestore(&sscape->lock, flags);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	/*
104062306a36Sopenharmony_ci	 * We have now enabled the codec chip, and so we should
104162306a36Sopenharmony_ci	 * detect the AD1845 device ...
104262306a36Sopenharmony_ci	 */
104362306a36Sopenharmony_ci	err = create_ad1845(card, wss_port[dev], irq[dev],
104462306a36Sopenharmony_ci			    dma[dev], dma2[dev]);
104562306a36Sopenharmony_ci	if (err < 0) {
104662306a36Sopenharmony_ci		snd_printk(KERN_ERR
104762306a36Sopenharmony_ci				"sscape: No AD1845 device at 0x%lx, IRQ %d\n",
104862306a36Sopenharmony_ci				wss_port[dev], irq[dev]);
104962306a36Sopenharmony_ci		return err;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci	strcpy(card->driver, "SoundScape");
105262306a36Sopenharmony_ci	strcpy(card->shortname, name);
105362306a36Sopenharmony_ci	snprintf(card->longname, sizeof(card->longname),
105462306a36Sopenharmony_ci		 "%s at 0x%lx, IRQ %d, DMA1 %d, DMA2 %d\n",
105562306a36Sopenharmony_ci		 name, sscape->chip->port, sscape->chip->irq,
105662306a36Sopenharmony_ci		 sscape->chip->dma1, sscape->chip->dma2);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci#define MIDI_DEVNUM  0
105962306a36Sopenharmony_ci	if (sscape->type != SSCAPE_VIVO) {
106062306a36Sopenharmony_ci		err = sscape_upload_bootblock(card);
106162306a36Sopenharmony_ci		if (err >= 0)
106262306a36Sopenharmony_ci			err = sscape_upload_microcode(card, err);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		if (err == 0) {
106562306a36Sopenharmony_ci			err = create_mpu401(card, MIDI_DEVNUM, port[dev],
106662306a36Sopenharmony_ci					    mpu_irq[dev]);
106762306a36Sopenharmony_ci			if (err < 0) {
106862306a36Sopenharmony_ci				snd_printk(KERN_ERR "sscape: Failed to create "
106962306a36Sopenharmony_ci						"MPU-401 device at 0x%lx\n",
107062306a36Sopenharmony_ci						port[dev]);
107162306a36Sopenharmony_ci				return err;
107262306a36Sopenharmony_ci			}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci			/*
107562306a36Sopenharmony_ci			 * Initialize mixer
107662306a36Sopenharmony_ci			 */
107762306a36Sopenharmony_ci			spin_lock_irqsave(&sscape->lock, flags);
107862306a36Sopenharmony_ci			sscape->midi_vol = 0;
107962306a36Sopenharmony_ci			host_write_ctrl_unsafe(sscape->io_base,
108062306a36Sopenharmony_ci						CMD_SET_MIDI_VOL, 100);
108162306a36Sopenharmony_ci			host_write_ctrl_unsafe(sscape->io_base,
108262306a36Sopenharmony_ci						sscape->midi_vol, 100);
108362306a36Sopenharmony_ci			host_write_ctrl_unsafe(sscape->io_base,
108462306a36Sopenharmony_ci						CMD_XXX_MIDI_VOL, 100);
108562306a36Sopenharmony_ci			host_write_ctrl_unsafe(sscape->io_base,
108662306a36Sopenharmony_ci						sscape->midi_vol, 100);
108762306a36Sopenharmony_ci			host_write_ctrl_unsafe(sscape->io_base,
108862306a36Sopenharmony_ci						CMD_SET_EXTMIDI, 100);
108962306a36Sopenharmony_ci			host_write_ctrl_unsafe(sscape->io_base,
109062306a36Sopenharmony_ci						0, 100);
109162306a36Sopenharmony_ci			host_write_ctrl_unsafe(sscape->io_base, CMD_ACK, 100);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci			set_midi_mode_unsafe(sscape->io_base);
109462306a36Sopenharmony_ci			spin_unlock_irqrestore(&sscape->lock, flags);
109562306a36Sopenharmony_ci		}
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	return 0;
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic int snd_sscape_match(struct device *pdev, unsigned int i)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	/*
110562306a36Sopenharmony_ci	 * Make sure we were given ALL of the other parameters.
110662306a36Sopenharmony_ci	 */
110762306a36Sopenharmony_ci	if (port[i] == SNDRV_AUTO_PORT)
110862306a36Sopenharmony_ci		return 0;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	if (irq[i] == SNDRV_AUTO_IRQ ||
111162306a36Sopenharmony_ci	    mpu_irq[i] == SNDRV_AUTO_IRQ ||
111262306a36Sopenharmony_ci	    dma[i] == SNDRV_AUTO_DMA) {
111362306a36Sopenharmony_ci		printk(KERN_INFO
111462306a36Sopenharmony_ci		       "sscape: insufficient parameters, "
111562306a36Sopenharmony_ci		       "need IO, IRQ, MPU-IRQ and DMA\n");
111662306a36Sopenharmony_ci		return 0;
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	return 1;
112062306a36Sopenharmony_ci}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cistatic int snd_sscape_probe(struct device *pdev, unsigned int dev)
112362306a36Sopenharmony_ci{
112462306a36Sopenharmony_ci	struct snd_card *card;
112562306a36Sopenharmony_ci	struct soundscape *sscape;
112662306a36Sopenharmony_ci	int ret;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	ret = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
112962306a36Sopenharmony_ci				sizeof(struct soundscape), &card);
113062306a36Sopenharmony_ci	if (ret < 0)
113162306a36Sopenharmony_ci		return ret;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	sscape = get_card_soundscape(card);
113462306a36Sopenharmony_ci	sscape->type = SSCAPE;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	dma[dev] &= 0x03;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	ret = create_sscape(dev, card);
113962306a36Sopenharmony_ci	if (ret < 0)
114062306a36Sopenharmony_ci		return ret;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	ret = snd_card_register(card);
114362306a36Sopenharmony_ci	if (ret < 0) {
114462306a36Sopenharmony_ci		snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
114562306a36Sopenharmony_ci		return ret;
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci	dev_set_drvdata(pdev, card);
114862306a36Sopenharmony_ci	return 0;
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci#define DEV_NAME "sscape"
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic struct isa_driver snd_sscape_driver = {
115462306a36Sopenharmony_ci	.match		= snd_sscape_match,
115562306a36Sopenharmony_ci	.probe		= snd_sscape_probe,
115662306a36Sopenharmony_ci	/* FIXME: suspend/resume */
115762306a36Sopenharmony_ci	.driver		= {
115862306a36Sopenharmony_ci		.name	= DEV_NAME
115962306a36Sopenharmony_ci	},
116062306a36Sopenharmony_ci};
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci#ifdef CONFIG_PNP
116362306a36Sopenharmony_cistatic inline int get_next_autoindex(int i)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	while (i < SNDRV_CARDS && port[i] != SNDRV_AUTO_PORT)
116662306a36Sopenharmony_ci		++i;
116762306a36Sopenharmony_ci	return i;
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_cistatic int sscape_pnp_detect(struct pnp_card_link *pcard,
117262306a36Sopenharmony_ci			     const struct pnp_card_device_id *pid)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	static int idx = 0;
117562306a36Sopenharmony_ci	struct pnp_dev *dev;
117662306a36Sopenharmony_ci	struct snd_card *card;
117762306a36Sopenharmony_ci	struct soundscape *sscape;
117862306a36Sopenharmony_ci	int ret;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	/*
118162306a36Sopenharmony_ci	 * Allow this function to fail *quietly* if all the ISA PnP
118262306a36Sopenharmony_ci	 * devices were configured using module parameters instead.
118362306a36Sopenharmony_ci	 */
118462306a36Sopenharmony_ci	idx = get_next_autoindex(idx);
118562306a36Sopenharmony_ci	if (idx >= SNDRV_CARDS)
118662306a36Sopenharmony_ci		return -ENOSPC;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/*
118962306a36Sopenharmony_ci	 * Check that we still have room for another sound card ...
119062306a36Sopenharmony_ci	 */
119162306a36Sopenharmony_ci	dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
119262306a36Sopenharmony_ci	if (!dev)
119362306a36Sopenharmony_ci		return -ENODEV;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	if (!pnp_is_active(dev)) {
119662306a36Sopenharmony_ci		if (pnp_activate_dev(dev) < 0) {
119762306a36Sopenharmony_ci			snd_printk(KERN_INFO "sscape: device is inactive\n");
119862306a36Sopenharmony_ci			return -EBUSY;
119962306a36Sopenharmony_ci		}
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	/*
120362306a36Sopenharmony_ci	 * Create a new ALSA sound card entry, in anticipation
120462306a36Sopenharmony_ci	 * of detecting our hardware ...
120562306a36Sopenharmony_ci	 */
120662306a36Sopenharmony_ci	ret = snd_devm_card_new(&pcard->card->dev,
120762306a36Sopenharmony_ci				index[idx], id[idx], THIS_MODULE,
120862306a36Sopenharmony_ci				sizeof(struct soundscape), &card);
120962306a36Sopenharmony_ci	if (ret < 0)
121062306a36Sopenharmony_ci		return ret;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	sscape = get_card_soundscape(card);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	/*
121562306a36Sopenharmony_ci	 * Identify card model ...
121662306a36Sopenharmony_ci	 */
121762306a36Sopenharmony_ci	if (!strncmp("ENS4081", pid->id, 7))
121862306a36Sopenharmony_ci		sscape->type = SSCAPE_VIVO;
121962306a36Sopenharmony_ci	else
122062306a36Sopenharmony_ci		sscape->type = SSCAPE_PNP;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/*
122362306a36Sopenharmony_ci	 * Read the correct parameters off the ISA PnP bus ...
122462306a36Sopenharmony_ci	 */
122562306a36Sopenharmony_ci	port[idx] = pnp_port_start(dev, 0);
122662306a36Sopenharmony_ci	irq[idx] = pnp_irq(dev, 0);
122762306a36Sopenharmony_ci	mpu_irq[idx] = pnp_irq(dev, 1);
122862306a36Sopenharmony_ci	dma[idx] = pnp_dma(dev, 0) & 0x03;
122962306a36Sopenharmony_ci	if (sscape->type == SSCAPE_PNP) {
123062306a36Sopenharmony_ci		dma2[idx] = dma[idx];
123162306a36Sopenharmony_ci		wss_port[idx] = CODEC_IO(port[idx]);
123262306a36Sopenharmony_ci	} else {
123362306a36Sopenharmony_ci		wss_port[idx] = pnp_port_start(dev, 1);
123462306a36Sopenharmony_ci		dma2[idx] = pnp_dma(dev, 1);
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	ret = create_sscape(idx, card);
123862306a36Sopenharmony_ci	if (ret < 0)
123962306a36Sopenharmony_ci		return ret;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	ret = snd_card_register(card);
124262306a36Sopenharmony_ci	if (ret < 0) {
124362306a36Sopenharmony_ci		snd_printk(KERN_ERR "sscape: Failed to register sound card\n");
124462306a36Sopenharmony_ci		return ret;
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	pnp_set_card_drvdata(pcard, card);
124862306a36Sopenharmony_ci	++idx;
124962306a36Sopenharmony_ci	return 0;
125062306a36Sopenharmony_ci}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic struct pnp_card_driver sscape_pnpc_driver = {
125362306a36Sopenharmony_ci	.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
125462306a36Sopenharmony_ci	.name = "sscape",
125562306a36Sopenharmony_ci	.id_table = sscape_pnpids,
125662306a36Sopenharmony_ci	.probe = sscape_pnp_detect,
125762306a36Sopenharmony_ci};
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci#endif /* CONFIG_PNP */
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic int __init sscape_init(void)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	int err;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	err = isa_register_driver(&snd_sscape_driver, SNDRV_CARDS);
126662306a36Sopenharmony_ci#ifdef CONFIG_PNP
126762306a36Sopenharmony_ci	if (!err)
126862306a36Sopenharmony_ci		isa_registered = 1;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	err = pnp_register_card_driver(&sscape_pnpc_driver);
127162306a36Sopenharmony_ci	if (!err)
127262306a36Sopenharmony_ci		pnp_registered = 1;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if (isa_registered)
127562306a36Sopenharmony_ci		err = 0;
127662306a36Sopenharmony_ci#endif
127762306a36Sopenharmony_ci	return err;
127862306a36Sopenharmony_ci}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic void __exit sscape_exit(void)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci#ifdef CONFIG_PNP
128362306a36Sopenharmony_ci	if (pnp_registered)
128462306a36Sopenharmony_ci		pnp_unregister_card_driver(&sscape_pnpc_driver);
128562306a36Sopenharmony_ci	if (isa_registered)
128662306a36Sopenharmony_ci#endif
128762306a36Sopenharmony_ci		isa_unregister_driver(&snd_sscape_driver);
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cimodule_init(sscape_init);
129162306a36Sopenharmony_cimodule_exit(sscape_exit);
1292