162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Driver for generic ESS AudioDrive ES18xx soundcards
462306a36Sopenharmony_ci *  Copyright (c) by Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>
562306a36Sopenharmony_ci *  Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci/* GENERAL NOTES:
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * BUGS:
1062306a36Sopenharmony_ci * - There are pops (we can't delay in trigger function, cause midlevel
1162306a36Sopenharmony_ci *   often need to trigger down and then up very quickly).
1262306a36Sopenharmony_ci *   Any ideas?
1362306a36Sopenharmony_ci * - Support for 16 bit DMA seems to be broken. I've no hardware to tune it.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * ES1868  NOTES:
1862306a36Sopenharmony_ci * - The chip has one half duplex pcm (with very limited full duplex support).
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * - Duplex stereophonic sound is impossible.
2162306a36Sopenharmony_ci * - Record and playback must share the same frequency rate.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * - The driver use dma2 for playback and dma1 for capture.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * ES1869 NOTES:
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * - there are a first full duplex pcm and a second playback only pcm
3062306a36Sopenharmony_ci *   (incompatible with first pcm capture)
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * - there is support for the capture volume and ESS Spatializer 3D effect.
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * - contrarily to some pages in DS_1869.PDF the rates can be set
3562306a36Sopenharmony_ci *   independently.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * - Zoom Video is implemented by sharing the FM DAC, thus the user can
3862306a36Sopenharmony_ci *   have either FM playback or Video playback but not both simultaneously.
3962306a36Sopenharmony_ci *   The Video Playback Switch mixer control toggles this choice.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * BUGS:
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * - There is a major trouble I noted:
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci *   using both channel for playback stereo 16 bit samples at 44100 Hz
4662306a36Sopenharmony_ci *   the second pcm (Audio1) DMA slows down irregularly and sound is garbled.
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci *   The same happens using Audio1 for captureing.
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci *   The Windows driver does not suffer of this (although it use Audio1
5162306a36Sopenharmony_ci *   only for captureing). I'm unable to discover why.
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/*
5662306a36Sopenharmony_ci * ES1879 NOTES:
5762306a36Sopenharmony_ci * - When Zoom Video is enabled (reg 0x71 bit 6 toggled on) the PCM playback
5862306a36Sopenharmony_ci *   seems to be effected (speaker_test plays a lower frequency). Can't find
5962306a36Sopenharmony_ci *   anything in the datasheet to account for this, so a Video Playback Switch
6062306a36Sopenharmony_ci *   control has been included to allow ZV to be enabled only when necessary.
6162306a36Sopenharmony_ci *   Then again on at least one test system the 0x71 bit 6 enable bit is not
6262306a36Sopenharmony_ci *   needed for ZV, so maybe the datasheet is entirely wrong here.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#include <linux/init.h>
6662306a36Sopenharmony_ci#include <linux/err.h>
6762306a36Sopenharmony_ci#include <linux/isa.h>
6862306a36Sopenharmony_ci#include <linux/pnp.h>
6962306a36Sopenharmony_ci#include <linux/isapnp.h>
7062306a36Sopenharmony_ci#include <linux/module.h>
7162306a36Sopenharmony_ci#include <linux/delay.h>
7262306a36Sopenharmony_ci#include <linux/io.h>
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#include <asm/dma.h>
7562306a36Sopenharmony_ci#include <sound/core.h>
7662306a36Sopenharmony_ci#include <sound/control.h>
7762306a36Sopenharmony_ci#include <sound/pcm.h>
7862306a36Sopenharmony_ci#include <sound/pcm_params.h>
7962306a36Sopenharmony_ci#include <sound/mpu401.h>
8062306a36Sopenharmony_ci#include <sound/opl3.h>
8162306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_IRQ
8262306a36Sopenharmony_ci#define SNDRV_LEGACY_FIND_FREE_DMA
8362306a36Sopenharmony_ci#include <sound/initval.h>
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define PFX "es18xx: "
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistruct snd_es18xx {
8862306a36Sopenharmony_ci	unsigned long port;		/* port of ESS chip */
8962306a36Sopenharmony_ci	unsigned long ctrl_port;	/* Control port of ESS chip */
9062306a36Sopenharmony_ci	int irq;			/* IRQ number of ESS chip */
9162306a36Sopenharmony_ci	int dma1;			/* DMA1 */
9262306a36Sopenharmony_ci	int dma2;			/* DMA2 */
9362306a36Sopenharmony_ci	unsigned short version;		/* version of ESS chip */
9462306a36Sopenharmony_ci	int caps;			/* Chip capabilities */
9562306a36Sopenharmony_ci	unsigned short audio2_vol;	/* volume level of audio2 */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	unsigned short active;		/* active channel mask */
9862306a36Sopenharmony_ci	unsigned int dma1_shift;
9962306a36Sopenharmony_ci	unsigned int dma2_shift;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	struct snd_pcm *pcm;
10262306a36Sopenharmony_ci	struct snd_pcm_substream *playback_a_substream;
10362306a36Sopenharmony_ci	struct snd_pcm_substream *capture_a_substream;
10462306a36Sopenharmony_ci	struct snd_pcm_substream *playback_b_substream;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	struct snd_rawmidi *rmidi;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	struct snd_kcontrol *hw_volume;
10962306a36Sopenharmony_ci	struct snd_kcontrol *hw_switch;
11062306a36Sopenharmony_ci	struct snd_kcontrol *master_volume;
11162306a36Sopenharmony_ci	struct snd_kcontrol *master_switch;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	spinlock_t reg_lock;
11462306a36Sopenharmony_ci	spinlock_t mixer_lock;
11562306a36Sopenharmony_ci#ifdef CONFIG_PM
11662306a36Sopenharmony_ci	unsigned char pm_reg;
11762306a36Sopenharmony_ci#endif
11862306a36Sopenharmony_ci#ifdef CONFIG_PNP
11962306a36Sopenharmony_ci	struct pnp_dev *dev;
12062306a36Sopenharmony_ci	struct pnp_dev *devc;
12162306a36Sopenharmony_ci#endif
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define AUDIO1_IRQ	0x01
12562306a36Sopenharmony_ci#define AUDIO2_IRQ	0x02
12662306a36Sopenharmony_ci#define HWV_IRQ		0x04
12762306a36Sopenharmony_ci#define MPU_IRQ		0x08
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define ES18XX_PCM2	0x0001	/* Has two useable PCM */
13062306a36Sopenharmony_ci#define ES18XX_SPATIALIZER 0x0002	/* Has 3D Spatializer */
13162306a36Sopenharmony_ci#define ES18XX_RECMIX	0x0004	/* Has record mixer */
13262306a36Sopenharmony_ci#define ES18XX_DUPLEX_MONO 0x0008	/* Has mono duplex only */
13362306a36Sopenharmony_ci#define ES18XX_DUPLEX_SAME 0x0010	/* Playback and record must share the same rate */
13462306a36Sopenharmony_ci#define ES18XX_NEW_RATE	0x0020	/* More precise rate setting */
13562306a36Sopenharmony_ci#define ES18XX_AUXB	0x0040	/* AuxB mixer control */
13662306a36Sopenharmony_ci#define ES18XX_HWV	0x0080	/* Has separate hardware volume mixer controls*/
13762306a36Sopenharmony_ci#define ES18XX_MONO	0x0100	/* Mono_in mixer control */
13862306a36Sopenharmony_ci#define ES18XX_I2S	0x0200	/* I2S mixer control */
13962306a36Sopenharmony_ci#define ES18XX_MUTEREC	0x0400	/* Record source can be muted */
14062306a36Sopenharmony_ci#define ES18XX_CONTROL	0x0800	/* Has control ports */
14162306a36Sopenharmony_ci#define ES18XX_GPO_2BIT	0x1000	/* GPO0,1 controlled by PM port */
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* Power Management */
14462306a36Sopenharmony_ci#define ES18XX_PM	0x07
14562306a36Sopenharmony_ci#define ES18XX_PM_GPO0	0x01
14662306a36Sopenharmony_ci#define ES18XX_PM_GPO1	0x02
14762306a36Sopenharmony_ci#define ES18XX_PM_PDR	0x04
14862306a36Sopenharmony_ci#define ES18XX_PM_ANA	0x08
14962306a36Sopenharmony_ci#define ES18XX_PM_FM	0x020
15062306a36Sopenharmony_ci#define ES18XX_PM_SUS	0x080
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/* Lowlevel */
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#define DAC1 0x01
15562306a36Sopenharmony_ci#define ADC1 0x02
15662306a36Sopenharmony_ci#define DAC2 0x04
15762306a36Sopenharmony_ci#define MILLISECOND 10000
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int snd_es18xx_dsp_command(struct snd_es18xx *chip, unsigned char val)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci        int i;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci        for(i = MILLISECOND; i; i--)
16462306a36Sopenharmony_ci                if ((inb(chip->port + 0x0C) & 0x80) == 0) {
16562306a36Sopenharmony_ci                        outb(val, chip->port + 0x0C);
16662306a36Sopenharmony_ci                        return 0;
16762306a36Sopenharmony_ci                }
16862306a36Sopenharmony_ci	snd_printk(KERN_ERR "dsp_command: timeout (0x%x)\n", val);
16962306a36Sopenharmony_ci        return -EINVAL;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int snd_es18xx_dsp_get_byte(struct snd_es18xx *chip)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci        int i;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci        for(i = MILLISECOND/10; i; i--)
17762306a36Sopenharmony_ci                if (inb(chip->port + 0x0C) & 0x40)
17862306a36Sopenharmony_ci                        return inb(chip->port + 0x0A);
17962306a36Sopenharmony_ci	snd_printk(KERN_ERR "dsp_get_byte failed: 0x%lx = 0x%x!!!\n",
18062306a36Sopenharmony_ci		   chip->port + 0x0A, inb(chip->port + 0x0A));
18162306a36Sopenharmony_ci        return -ENODEV;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci#undef REG_DEBUG
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int snd_es18xx_write(struct snd_es18xx *chip,
18762306a36Sopenharmony_ci			    unsigned char reg, unsigned char data)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	unsigned long flags;
19062306a36Sopenharmony_ci	int ret;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci        spin_lock_irqsave(&chip->reg_lock, flags);
19362306a36Sopenharmony_ci	ret = snd_es18xx_dsp_command(chip, reg);
19462306a36Sopenharmony_ci	if (ret < 0)
19562306a36Sopenharmony_ci		goto end;
19662306a36Sopenharmony_ci        ret = snd_es18xx_dsp_command(chip, data);
19762306a36Sopenharmony_ci end:
19862306a36Sopenharmony_ci        spin_unlock_irqrestore(&chip->reg_lock, flags);
19962306a36Sopenharmony_ci#ifdef REG_DEBUG
20062306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Reg %02x set to %02x\n", reg, data);
20162306a36Sopenharmony_ci#endif
20262306a36Sopenharmony_ci	return ret;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int snd_es18xx_read(struct snd_es18xx *chip, unsigned char reg)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	unsigned long flags;
20862306a36Sopenharmony_ci	int ret, data;
20962306a36Sopenharmony_ci        spin_lock_irqsave(&chip->reg_lock, flags);
21062306a36Sopenharmony_ci	ret = snd_es18xx_dsp_command(chip, 0xC0);
21162306a36Sopenharmony_ci	if (ret < 0)
21262306a36Sopenharmony_ci		goto end;
21362306a36Sopenharmony_ci        ret = snd_es18xx_dsp_command(chip, reg);
21462306a36Sopenharmony_ci	if (ret < 0)
21562306a36Sopenharmony_ci		goto end;
21662306a36Sopenharmony_ci	data = snd_es18xx_dsp_get_byte(chip);
21762306a36Sopenharmony_ci	ret = data;
21862306a36Sopenharmony_ci#ifdef REG_DEBUG
21962306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Reg %02x now is %02x (%d)\n", reg, data, ret);
22062306a36Sopenharmony_ci#endif
22162306a36Sopenharmony_ci end:
22262306a36Sopenharmony_ci        spin_unlock_irqrestore(&chip->reg_lock, flags);
22362306a36Sopenharmony_ci	return ret;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/* Return old value */
22762306a36Sopenharmony_cistatic int snd_es18xx_bits(struct snd_es18xx *chip, unsigned char reg,
22862306a36Sopenharmony_ci			   unsigned char mask, unsigned char val)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci        int ret;
23162306a36Sopenharmony_ci	unsigned char old, new, oval;
23262306a36Sopenharmony_ci	unsigned long flags;
23362306a36Sopenharmony_ci        spin_lock_irqsave(&chip->reg_lock, flags);
23462306a36Sopenharmony_ci        ret = snd_es18xx_dsp_command(chip, 0xC0);
23562306a36Sopenharmony_ci	if (ret < 0)
23662306a36Sopenharmony_ci		goto end;
23762306a36Sopenharmony_ci        ret = snd_es18xx_dsp_command(chip, reg);
23862306a36Sopenharmony_ci	if (ret < 0)
23962306a36Sopenharmony_ci		goto end;
24062306a36Sopenharmony_ci	ret = snd_es18xx_dsp_get_byte(chip);
24162306a36Sopenharmony_ci	if (ret < 0) {
24262306a36Sopenharmony_ci		goto end;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	old = ret;
24562306a36Sopenharmony_ci	oval = old & mask;
24662306a36Sopenharmony_ci	if (val != oval) {
24762306a36Sopenharmony_ci		ret = snd_es18xx_dsp_command(chip, reg);
24862306a36Sopenharmony_ci		if (ret < 0)
24962306a36Sopenharmony_ci			goto end;
25062306a36Sopenharmony_ci		new = (old & ~mask) | (val & mask);
25162306a36Sopenharmony_ci		ret = snd_es18xx_dsp_command(chip, new);
25262306a36Sopenharmony_ci		if (ret < 0)
25362306a36Sopenharmony_ci			goto end;
25462306a36Sopenharmony_ci#ifdef REG_DEBUG
25562306a36Sopenharmony_ci		snd_printk(KERN_DEBUG "Reg %02x was %02x, set to %02x (%d)\n",
25662306a36Sopenharmony_ci			   reg, old, new, ret);
25762306a36Sopenharmony_ci#endif
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	ret = oval;
26062306a36Sopenharmony_ci end:
26162306a36Sopenharmony_ci        spin_unlock_irqrestore(&chip->reg_lock, flags);
26262306a36Sopenharmony_ci	return ret;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic inline void snd_es18xx_mixer_write(struct snd_es18xx *chip,
26662306a36Sopenharmony_ci			    unsigned char reg, unsigned char data)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	unsigned long flags;
26962306a36Sopenharmony_ci        spin_lock_irqsave(&chip->mixer_lock, flags);
27062306a36Sopenharmony_ci        outb(reg, chip->port + 0x04);
27162306a36Sopenharmony_ci        outb(data, chip->port + 0x05);
27262306a36Sopenharmony_ci        spin_unlock_irqrestore(&chip->mixer_lock, flags);
27362306a36Sopenharmony_ci#ifdef REG_DEBUG
27462306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Mixer reg %02x set to %02x\n", reg, data);
27562306a36Sopenharmony_ci#endif
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic inline int snd_es18xx_mixer_read(struct snd_es18xx *chip, unsigned char reg)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	unsigned long flags;
28162306a36Sopenharmony_ci	int data;
28262306a36Sopenharmony_ci        spin_lock_irqsave(&chip->mixer_lock, flags);
28362306a36Sopenharmony_ci        outb(reg, chip->port + 0x04);
28462306a36Sopenharmony_ci	data = inb(chip->port + 0x05);
28562306a36Sopenharmony_ci        spin_unlock_irqrestore(&chip->mixer_lock, flags);
28662306a36Sopenharmony_ci#ifdef REG_DEBUG
28762306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Mixer reg %02x now is %02x\n", reg, data);
28862306a36Sopenharmony_ci#endif
28962306a36Sopenharmony_ci        return data;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci/* Return old value */
29362306a36Sopenharmony_cistatic inline int snd_es18xx_mixer_bits(struct snd_es18xx *chip, unsigned char reg,
29462306a36Sopenharmony_ci					unsigned char mask, unsigned char val)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	unsigned char old, new, oval;
29762306a36Sopenharmony_ci	unsigned long flags;
29862306a36Sopenharmony_ci        spin_lock_irqsave(&chip->mixer_lock, flags);
29962306a36Sopenharmony_ci        outb(reg, chip->port + 0x04);
30062306a36Sopenharmony_ci	old = inb(chip->port + 0x05);
30162306a36Sopenharmony_ci	oval = old & mask;
30262306a36Sopenharmony_ci	if (val != oval) {
30362306a36Sopenharmony_ci		new = (old & ~mask) | (val & mask);
30462306a36Sopenharmony_ci		outb(new, chip->port + 0x05);
30562306a36Sopenharmony_ci#ifdef REG_DEBUG
30662306a36Sopenharmony_ci		snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x\n",
30762306a36Sopenharmony_ci			   reg, old, new);
30862306a36Sopenharmony_ci#endif
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci        spin_unlock_irqrestore(&chip->mixer_lock, flags);
31162306a36Sopenharmony_ci	return oval;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic inline int snd_es18xx_mixer_writable(struct snd_es18xx *chip, unsigned char reg,
31562306a36Sopenharmony_ci					    unsigned char mask)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	int old, expected, new;
31862306a36Sopenharmony_ci	unsigned long flags;
31962306a36Sopenharmony_ci        spin_lock_irqsave(&chip->mixer_lock, flags);
32062306a36Sopenharmony_ci        outb(reg, chip->port + 0x04);
32162306a36Sopenharmony_ci	old = inb(chip->port + 0x05);
32262306a36Sopenharmony_ci	expected = old ^ mask;
32362306a36Sopenharmony_ci	outb(expected, chip->port + 0x05);
32462306a36Sopenharmony_ci	new = inb(chip->port + 0x05);
32562306a36Sopenharmony_ci        spin_unlock_irqrestore(&chip->mixer_lock, flags);
32662306a36Sopenharmony_ci#ifdef REG_DEBUG
32762306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x, now is %02x\n",
32862306a36Sopenharmony_ci		   reg, old, expected, new);
32962306a36Sopenharmony_ci#endif
33062306a36Sopenharmony_ci	return expected == new;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic int snd_es18xx_reset(struct snd_es18xx *chip)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	int i;
33762306a36Sopenharmony_ci        outb(0x03, chip->port + 0x06);
33862306a36Sopenharmony_ci        inb(chip->port + 0x06);
33962306a36Sopenharmony_ci        outb(0x00, chip->port + 0x06);
34062306a36Sopenharmony_ci        for(i = 0; i < MILLISECOND && !(inb(chip->port + 0x0E) & 0x80); i++);
34162306a36Sopenharmony_ci        if (inb(chip->port + 0x0A) != 0xAA)
34262306a36Sopenharmony_ci                return -1;
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic int snd_es18xx_reset_fifo(struct snd_es18xx *chip)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci        outb(0x02, chip->port + 0x06);
34962306a36Sopenharmony_ci        inb(chip->port + 0x06);
35062306a36Sopenharmony_ci        outb(0x00, chip->port + 0x06);
35162306a36Sopenharmony_ci	return 0;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic const struct snd_ratnum new_clocks[2] = {
35562306a36Sopenharmony_ci	{
35662306a36Sopenharmony_ci		.num = 793800,
35762306a36Sopenharmony_ci		.den_min = 1,
35862306a36Sopenharmony_ci		.den_max = 128,
35962306a36Sopenharmony_ci		.den_step = 1,
36062306a36Sopenharmony_ci	},
36162306a36Sopenharmony_ci	{
36262306a36Sopenharmony_ci		.num = 768000,
36362306a36Sopenharmony_ci		.den_min = 1,
36462306a36Sopenharmony_ci		.den_max = 128,
36562306a36Sopenharmony_ci		.den_step = 1,
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci};
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums new_hw_constraints_clocks = {
37062306a36Sopenharmony_ci	.nrats = 2,
37162306a36Sopenharmony_ci	.rats = new_clocks,
37262306a36Sopenharmony_ci};
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic const struct snd_ratnum old_clocks[2] = {
37562306a36Sopenharmony_ci	{
37662306a36Sopenharmony_ci		.num = 795444,
37762306a36Sopenharmony_ci		.den_min = 1,
37862306a36Sopenharmony_ci		.den_max = 128,
37962306a36Sopenharmony_ci		.den_step = 1,
38062306a36Sopenharmony_ci	},
38162306a36Sopenharmony_ci	{
38262306a36Sopenharmony_ci		.num = 397722,
38362306a36Sopenharmony_ci		.den_min = 1,
38462306a36Sopenharmony_ci		.den_max = 128,
38562306a36Sopenharmony_ci		.den_step = 1,
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci};
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums old_hw_constraints_clocks  = {
39062306a36Sopenharmony_ci	.nrats = 2,
39162306a36Sopenharmony_ci	.rats = old_clocks,
39262306a36Sopenharmony_ci};
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic void snd_es18xx_rate_set(struct snd_es18xx *chip,
39662306a36Sopenharmony_ci				struct snd_pcm_substream *substream,
39762306a36Sopenharmony_ci				int mode)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	unsigned int bits, div0;
40062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
40162306a36Sopenharmony_ci	if (chip->caps & ES18XX_NEW_RATE) {
40262306a36Sopenharmony_ci		if (runtime->rate_num == new_clocks[0].num)
40362306a36Sopenharmony_ci			bits = 128 - runtime->rate_den;
40462306a36Sopenharmony_ci		else
40562306a36Sopenharmony_ci			bits = 256 - runtime->rate_den;
40662306a36Sopenharmony_ci	} else {
40762306a36Sopenharmony_ci		if (runtime->rate_num == old_clocks[0].num)
40862306a36Sopenharmony_ci			bits = 256 - runtime->rate_den;
40962306a36Sopenharmony_ci		else
41062306a36Sopenharmony_ci			bits = 128 - runtime->rate_den;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	/* set filter register */
41462306a36Sopenharmony_ci	div0 = 256 - 7160000*20/(8*82*runtime->rate);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	if ((chip->caps & ES18XX_PCM2) && mode == DAC2) {
41762306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x70, bits);
41862306a36Sopenharmony_ci		/*
41962306a36Sopenharmony_ci		 * Comment from kernel oss driver:
42062306a36Sopenharmony_ci		 * FKS: fascinating: 0x72 doesn't seem to work.
42162306a36Sopenharmony_ci		 */
42262306a36Sopenharmony_ci		snd_es18xx_write(chip, 0xA2, div0);
42362306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x72, div0);
42462306a36Sopenharmony_ci	} else {
42562306a36Sopenharmony_ci		snd_es18xx_write(chip, 0xA1, bits);
42662306a36Sopenharmony_ci		snd_es18xx_write(chip, 0xA2, div0);
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic int snd_es18xx_playback_hw_params(struct snd_pcm_substream *substream,
43162306a36Sopenharmony_ci					 struct snd_pcm_hw_params *hw_params)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
43462306a36Sopenharmony_ci	int shift;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	shift = 0;
43762306a36Sopenharmony_ci	if (params_channels(hw_params) == 2)
43862306a36Sopenharmony_ci		shift++;
43962306a36Sopenharmony_ci	if (snd_pcm_format_width(params_format(hw_params)) == 16)
44062306a36Sopenharmony_ci		shift++;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) {
44362306a36Sopenharmony_ci		if ((chip->caps & ES18XX_DUPLEX_MONO) &&
44462306a36Sopenharmony_ci		    (chip->capture_a_substream) &&
44562306a36Sopenharmony_ci		    params_channels(hw_params) != 1) {
44662306a36Sopenharmony_ci			_snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS);
44762306a36Sopenharmony_ci			return -EBUSY;
44862306a36Sopenharmony_ci		}
44962306a36Sopenharmony_ci		chip->dma2_shift = shift;
45062306a36Sopenharmony_ci	} else {
45162306a36Sopenharmony_ci		chip->dma1_shift = shift;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci	return 0;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic int snd_es18xx_playback1_prepare(struct snd_es18xx *chip,
45762306a36Sopenharmony_ci					struct snd_pcm_substream *substream)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
46062306a36Sopenharmony_ci	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
46162306a36Sopenharmony_ci	unsigned int count = snd_pcm_lib_period_bytes(substream);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci        snd_es18xx_rate_set(chip, substream, DAC2);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci        /* Transfer Count Reload */
46662306a36Sopenharmony_ci        count = 0x10000 - count;
46762306a36Sopenharmony_ci        snd_es18xx_mixer_write(chip, 0x74, count & 0xff);
46862306a36Sopenharmony_ci        snd_es18xx_mixer_write(chip, 0x76, count >> 8);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* Set format */
47162306a36Sopenharmony_ci        snd_es18xx_mixer_bits(chip, 0x7A, 0x07,
47262306a36Sopenharmony_ci			      ((runtime->channels == 1) ? 0x00 : 0x02) |
47362306a36Sopenharmony_ci			      (snd_pcm_format_width(runtime->format) == 16 ? 0x01 : 0x00) |
47462306a36Sopenharmony_ci			      (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x04));
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci        /* Set DMA controller */
47762306a36Sopenharmony_ci        snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	return 0;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int snd_es18xx_playback1_trigger(struct snd_es18xx *chip,
48362306a36Sopenharmony_ci					struct snd_pcm_substream *substream,
48462306a36Sopenharmony_ci					int cmd)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	switch (cmd) {
48762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
48862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
48962306a36Sopenharmony_ci		if (chip->active & DAC2)
49062306a36Sopenharmony_ci			return 0;
49162306a36Sopenharmony_ci		chip->active |= DAC2;
49262306a36Sopenharmony_ci                /* Start DMA */
49362306a36Sopenharmony_ci		if (chip->dma2 >= 4)
49462306a36Sopenharmony_ci			snd_es18xx_mixer_write(chip, 0x78, 0xb3);
49562306a36Sopenharmony_ci		else
49662306a36Sopenharmony_ci			snd_es18xx_mixer_write(chip, 0x78, 0x93);
49762306a36Sopenharmony_ci#ifdef AVOID_POPS
49862306a36Sopenharmony_ci		/* Avoid pops */
49962306a36Sopenharmony_ci		mdelay(100);
50062306a36Sopenharmony_ci		if (chip->caps & ES18XX_PCM2)
50162306a36Sopenharmony_ci			/* Restore Audio 2 volume */
50262306a36Sopenharmony_ci			snd_es18xx_mixer_write(chip, 0x7C, chip->audio2_vol);
50362306a36Sopenharmony_ci		else
50462306a36Sopenharmony_ci			/* Enable PCM output */
50562306a36Sopenharmony_ci			snd_es18xx_dsp_command(chip, 0xD1);
50662306a36Sopenharmony_ci#endif
50762306a36Sopenharmony_ci		break;
50862306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
50962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
51062306a36Sopenharmony_ci		if (!(chip->active & DAC2))
51162306a36Sopenharmony_ci			return 0;
51262306a36Sopenharmony_ci		chip->active &= ~DAC2;
51362306a36Sopenharmony_ci                /* Stop DMA */
51462306a36Sopenharmony_ci                snd_es18xx_mixer_write(chip, 0x78, 0x00);
51562306a36Sopenharmony_ci#ifdef AVOID_POPS
51662306a36Sopenharmony_ci		mdelay(25);
51762306a36Sopenharmony_ci		if (chip->caps & ES18XX_PCM2)
51862306a36Sopenharmony_ci			/* Set Audio 2 volume to 0 */
51962306a36Sopenharmony_ci			snd_es18xx_mixer_write(chip, 0x7C, 0);
52062306a36Sopenharmony_ci		else
52162306a36Sopenharmony_ci			/* Disable PCM output */
52262306a36Sopenharmony_ci			snd_es18xx_dsp_command(chip, 0xD3);
52362306a36Sopenharmony_ci#endif
52462306a36Sopenharmony_ci		break;
52562306a36Sopenharmony_ci	default:
52662306a36Sopenharmony_ci		return -EINVAL;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	return 0;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic int snd_es18xx_capture_hw_params(struct snd_pcm_substream *substream,
53362306a36Sopenharmony_ci					struct snd_pcm_hw_params *hw_params)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
53662306a36Sopenharmony_ci	int shift;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	shift = 0;
53962306a36Sopenharmony_ci	if ((chip->caps & ES18XX_DUPLEX_MONO) &&
54062306a36Sopenharmony_ci	    chip->playback_a_substream &&
54162306a36Sopenharmony_ci	    params_channels(hw_params) != 1) {
54262306a36Sopenharmony_ci		_snd_pcm_hw_param_setempty(hw_params, SNDRV_PCM_HW_PARAM_CHANNELS);
54362306a36Sopenharmony_ci		return -EBUSY;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	if (params_channels(hw_params) == 2)
54662306a36Sopenharmony_ci		shift++;
54762306a36Sopenharmony_ci	if (snd_pcm_format_width(params_format(hw_params)) == 16)
54862306a36Sopenharmony_ci		shift++;
54962306a36Sopenharmony_ci	chip->dma1_shift = shift;
55062306a36Sopenharmony_ci	return 0;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic int snd_es18xx_capture_prepare(struct snd_pcm_substream *substream)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
55662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
55762306a36Sopenharmony_ci	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
55862306a36Sopenharmony_ci	unsigned int count = snd_pcm_lib_period_bytes(substream);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	snd_es18xx_reset_fifo(chip);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci        /* Set stereo/mono */
56362306a36Sopenharmony_ci        snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci        snd_es18xx_rate_set(chip, substream, ADC1);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci        /* Transfer Count Reload */
56862306a36Sopenharmony_ci	count = 0x10000 - count;
56962306a36Sopenharmony_ci	snd_es18xx_write(chip, 0xA4, count & 0xff);
57062306a36Sopenharmony_ci	snd_es18xx_write(chip, 0xA5, count >> 8);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci#ifdef AVOID_POPS
57362306a36Sopenharmony_ci	mdelay(100);
57462306a36Sopenharmony_ci#endif
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci        /* Set format */
57762306a36Sopenharmony_ci        snd_es18xx_write(chip, 0xB7,
57862306a36Sopenharmony_ci                         snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71);
57962306a36Sopenharmony_ci        snd_es18xx_write(chip, 0xB7, 0x90 |
58062306a36Sopenharmony_ci                         ((runtime->channels == 1) ? 0x40 : 0x08) |
58162306a36Sopenharmony_ci                         (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) |
58262306a36Sopenharmony_ci                         (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20));
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci        /* Set DMA controller */
58562306a36Sopenharmony_ci        snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	return 0;
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic int snd_es18xx_capture_trigger(struct snd_pcm_substream *substream,
59162306a36Sopenharmony_ci				      int cmd)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	switch (cmd) {
59662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
59762306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
59862306a36Sopenharmony_ci		if (chip->active & ADC1)
59962306a36Sopenharmony_ci			return 0;
60062306a36Sopenharmony_ci		chip->active |= ADC1;
60162306a36Sopenharmony_ci                /* Start DMA */
60262306a36Sopenharmony_ci                snd_es18xx_write(chip, 0xB8, 0x0f);
60362306a36Sopenharmony_ci		break;
60462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
60562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
60662306a36Sopenharmony_ci		if (!(chip->active & ADC1))
60762306a36Sopenharmony_ci			return 0;
60862306a36Sopenharmony_ci		chip->active &= ~ADC1;
60962306a36Sopenharmony_ci                /* Stop DMA */
61062306a36Sopenharmony_ci                snd_es18xx_write(chip, 0xB8, 0x00);
61162306a36Sopenharmony_ci		break;
61262306a36Sopenharmony_ci	default:
61362306a36Sopenharmony_ci		return -EINVAL;
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	return 0;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic int snd_es18xx_playback2_prepare(struct snd_es18xx *chip,
62062306a36Sopenharmony_ci					struct snd_pcm_substream *substream)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
62362306a36Sopenharmony_ci	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
62462306a36Sopenharmony_ci	unsigned int count = snd_pcm_lib_period_bytes(substream);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	snd_es18xx_reset_fifo(chip);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci        /* Set stereo/mono */
62962306a36Sopenharmony_ci        snd_es18xx_bits(chip, 0xA8, 0x03, runtime->channels == 1 ? 0x02 : 0x01);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci        snd_es18xx_rate_set(chip, substream, DAC1);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci        /* Transfer Count Reload */
63462306a36Sopenharmony_ci	count = 0x10000 - count;
63562306a36Sopenharmony_ci	snd_es18xx_write(chip, 0xA4, count & 0xff);
63662306a36Sopenharmony_ci	snd_es18xx_write(chip, 0xA5, count >> 8);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci        /* Set format */
63962306a36Sopenharmony_ci        snd_es18xx_write(chip, 0xB6,
64062306a36Sopenharmony_ci                         snd_pcm_format_unsigned(runtime->format) ? 0x80 : 0x00);
64162306a36Sopenharmony_ci        snd_es18xx_write(chip, 0xB7,
64262306a36Sopenharmony_ci                         snd_pcm_format_unsigned(runtime->format) ? 0x51 : 0x71);
64362306a36Sopenharmony_ci        snd_es18xx_write(chip, 0xB7, 0x90 |
64462306a36Sopenharmony_ci                         (runtime->channels == 1 ? 0x40 : 0x08) |
64562306a36Sopenharmony_ci                         (snd_pcm_format_width(runtime->format) == 16 ? 0x04 : 0x00) |
64662306a36Sopenharmony_ci                         (snd_pcm_format_unsigned(runtime->format) ? 0x00 : 0x20));
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci        /* Set DMA controller */
64962306a36Sopenharmony_ci        snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return 0;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic int snd_es18xx_playback2_trigger(struct snd_es18xx *chip,
65562306a36Sopenharmony_ci					struct snd_pcm_substream *substream,
65662306a36Sopenharmony_ci					int cmd)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	switch (cmd) {
65962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
66062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
66162306a36Sopenharmony_ci		if (chip->active & DAC1)
66262306a36Sopenharmony_ci			return 0;
66362306a36Sopenharmony_ci		chip->active |= DAC1;
66462306a36Sopenharmony_ci                /* Start DMA */
66562306a36Sopenharmony_ci                snd_es18xx_write(chip, 0xB8, 0x05);
66662306a36Sopenharmony_ci#ifdef AVOID_POPS
66762306a36Sopenharmony_ci		/* Avoid pops */
66862306a36Sopenharmony_ci		mdelay(100);
66962306a36Sopenharmony_ci                /* Enable Audio 1 */
67062306a36Sopenharmony_ci                snd_es18xx_dsp_command(chip, 0xD1);
67162306a36Sopenharmony_ci#endif
67262306a36Sopenharmony_ci		break;
67362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
67462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
67562306a36Sopenharmony_ci		if (!(chip->active & DAC1))
67662306a36Sopenharmony_ci			return 0;
67762306a36Sopenharmony_ci		chip->active &= ~DAC1;
67862306a36Sopenharmony_ci                /* Stop DMA */
67962306a36Sopenharmony_ci                snd_es18xx_write(chip, 0xB8, 0x00);
68062306a36Sopenharmony_ci#ifdef AVOID_POPS
68162306a36Sopenharmony_ci		/* Avoid pops */
68262306a36Sopenharmony_ci		mdelay(25);
68362306a36Sopenharmony_ci                /* Disable Audio 1 */
68462306a36Sopenharmony_ci                snd_es18xx_dsp_command(chip, 0xD3);
68562306a36Sopenharmony_ci#endif
68662306a36Sopenharmony_ci		break;
68762306a36Sopenharmony_ci	default:
68862306a36Sopenharmony_ci		return -EINVAL;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	return 0;
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic int snd_es18xx_playback_prepare(struct snd_pcm_substream *substream)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
69762306a36Sopenharmony_ci	if (substream->number == 0 && (chip->caps & ES18XX_PCM2))
69862306a36Sopenharmony_ci		return snd_es18xx_playback1_prepare(chip, substream);
69962306a36Sopenharmony_ci	else
70062306a36Sopenharmony_ci		return snd_es18xx_playback2_prepare(chip, substream);
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cistatic int snd_es18xx_playback_trigger(struct snd_pcm_substream *substream,
70462306a36Sopenharmony_ci				       int cmd)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
70762306a36Sopenharmony_ci	if (substream->number == 0 && (chip->caps & ES18XX_PCM2))
70862306a36Sopenharmony_ci		return snd_es18xx_playback1_trigger(chip, substream, cmd);
70962306a36Sopenharmony_ci	else
71062306a36Sopenharmony_ci		return snd_es18xx_playback2_trigger(chip, substream, cmd);
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct snd_card *card = dev_id;
71662306a36Sopenharmony_ci	struct snd_es18xx *chip = card->private_data;
71762306a36Sopenharmony_ci	unsigned char status;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (chip->caps & ES18XX_CONTROL) {
72062306a36Sopenharmony_ci		/* Read Interrupt status */
72162306a36Sopenharmony_ci		status = inb(chip->ctrl_port + 6);
72262306a36Sopenharmony_ci	} else {
72362306a36Sopenharmony_ci		/* Read Interrupt status */
72462306a36Sopenharmony_ci		status = snd_es18xx_mixer_read(chip, 0x7f) >> 4;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci#if 0
72762306a36Sopenharmony_ci	else {
72862306a36Sopenharmony_ci		status = 0;
72962306a36Sopenharmony_ci		if (inb(chip->port + 0x0C) & 0x01)
73062306a36Sopenharmony_ci			status |= AUDIO1_IRQ;
73162306a36Sopenharmony_ci		if (snd_es18xx_mixer_read(chip, 0x7A) & 0x80)
73262306a36Sopenharmony_ci			status |= AUDIO2_IRQ;
73362306a36Sopenharmony_ci		if ((chip->caps & ES18XX_HWV) &&
73462306a36Sopenharmony_ci		    snd_es18xx_mixer_read(chip, 0x64) & 0x10)
73562306a36Sopenharmony_ci			status |= HWV_IRQ;
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci#endif
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/* Audio 1 & Audio 2 */
74062306a36Sopenharmony_ci        if (status & AUDIO2_IRQ) {
74162306a36Sopenharmony_ci                if (chip->active & DAC2)
74262306a36Sopenharmony_ci                	snd_pcm_period_elapsed(chip->playback_a_substream);
74362306a36Sopenharmony_ci		/* ack interrupt */
74462306a36Sopenharmony_ci                snd_es18xx_mixer_bits(chip, 0x7A, 0x80, 0x00);
74562306a36Sopenharmony_ci        }
74662306a36Sopenharmony_ci        if (status & AUDIO1_IRQ) {
74762306a36Sopenharmony_ci                /* ok.. capture is active */
74862306a36Sopenharmony_ci                if (chip->active & ADC1)
74962306a36Sopenharmony_ci                	snd_pcm_period_elapsed(chip->capture_a_substream);
75062306a36Sopenharmony_ci                /* ok.. playback2 is active */
75162306a36Sopenharmony_ci                else if (chip->active & DAC1)
75262306a36Sopenharmony_ci                	snd_pcm_period_elapsed(chip->playback_b_substream);
75362306a36Sopenharmony_ci		/* ack interrupt */
75462306a36Sopenharmony_ci		inb(chip->port + 0x0E);
75562306a36Sopenharmony_ci        }
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* MPU */
75862306a36Sopenharmony_ci	if ((status & MPU_IRQ) && chip->rmidi)
75962306a36Sopenharmony_ci		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/* Hardware volume */
76262306a36Sopenharmony_ci	if (status & HWV_IRQ) {
76362306a36Sopenharmony_ci		int split = 0;
76462306a36Sopenharmony_ci		if (chip->caps & ES18XX_HWV) {
76562306a36Sopenharmony_ci			split = snd_es18xx_mixer_read(chip, 0x64) & 0x80;
76662306a36Sopenharmony_ci			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
76762306a36Sopenharmony_ci					&chip->hw_switch->id);
76862306a36Sopenharmony_ci			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
76962306a36Sopenharmony_ci					&chip->hw_volume->id);
77062306a36Sopenharmony_ci		}
77162306a36Sopenharmony_ci		if (!split) {
77262306a36Sopenharmony_ci			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
77362306a36Sopenharmony_ci					&chip->master_switch->id);
77462306a36Sopenharmony_ci			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
77562306a36Sopenharmony_ci					&chip->master_volume->id);
77662306a36Sopenharmony_ci		}
77762306a36Sopenharmony_ci		/* ack interrupt */
77862306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x66, 0x00);
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci	return IRQ_HANDLED;
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_es18xx_playback_pointer(struct snd_pcm_substream *substream)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
78662306a36Sopenharmony_ci	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
78762306a36Sopenharmony_ci	int pos;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) {
79062306a36Sopenharmony_ci		if (!(chip->active & DAC2))
79162306a36Sopenharmony_ci			return 0;
79262306a36Sopenharmony_ci		pos = snd_dma_pointer(chip->dma2, size);
79362306a36Sopenharmony_ci		return pos >> chip->dma2_shift;
79462306a36Sopenharmony_ci	} else {
79562306a36Sopenharmony_ci		if (!(chip->active & DAC1))
79662306a36Sopenharmony_ci			return 0;
79762306a36Sopenharmony_ci		pos = snd_dma_pointer(chip->dma1, size);
79862306a36Sopenharmony_ci		return pos >> chip->dma1_shift;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_es18xx_capture_pointer(struct snd_pcm_substream *substream)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
80562306a36Sopenharmony_ci	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
80662306a36Sopenharmony_ci	int pos;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci        if (!(chip->active & ADC1))
80962306a36Sopenharmony_ci                return 0;
81062306a36Sopenharmony_ci	pos = snd_dma_pointer(chip->dma1, size);
81162306a36Sopenharmony_ci	return pos >> chip->dma1_shift;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_es18xx_playback =
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
81762306a36Sopenharmony_ci				 SNDRV_PCM_INFO_RESUME |
81862306a36Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
81962306a36Sopenharmony_ci	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
82062306a36Sopenharmony_ci				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
82162306a36Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
82262306a36Sopenharmony_ci	.rate_min =		4000,
82362306a36Sopenharmony_ci	.rate_max =		48000,
82462306a36Sopenharmony_ci	.channels_min =		1,
82562306a36Sopenharmony_ci	.channels_max =		2,
82662306a36Sopenharmony_ci	.buffer_bytes_max =	65536,
82762306a36Sopenharmony_ci	.period_bytes_min =	64,
82862306a36Sopenharmony_ci	.period_bytes_max =	65536,
82962306a36Sopenharmony_ci	.periods_min =		1,
83062306a36Sopenharmony_ci	.periods_max =		1024,
83162306a36Sopenharmony_ci	.fifo_size =		0,
83262306a36Sopenharmony_ci};
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_es18xx_capture =
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
83762306a36Sopenharmony_ci				 SNDRV_PCM_INFO_RESUME |
83862306a36Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
83962306a36Sopenharmony_ci	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
84062306a36Sopenharmony_ci				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
84162306a36Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
84262306a36Sopenharmony_ci	.rate_min =		4000,
84362306a36Sopenharmony_ci	.rate_max =		48000,
84462306a36Sopenharmony_ci	.channels_min =		1,
84562306a36Sopenharmony_ci	.channels_max =		2,
84662306a36Sopenharmony_ci	.buffer_bytes_max =	65536,
84762306a36Sopenharmony_ci	.period_bytes_min =	64,
84862306a36Sopenharmony_ci	.period_bytes_max =	65536,
84962306a36Sopenharmony_ci	.periods_min =		1,
85062306a36Sopenharmony_ci	.periods_max =		1024,
85162306a36Sopenharmony_ci	.fifo_size =		0,
85262306a36Sopenharmony_ci};
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cistatic int snd_es18xx_playback_open(struct snd_pcm_substream *substream)
85562306a36Sopenharmony_ci{
85662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
85762306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (substream->number == 0 && (chip->caps & ES18XX_PCM2)) {
86062306a36Sopenharmony_ci		if ((chip->caps & ES18XX_DUPLEX_MONO) &&
86162306a36Sopenharmony_ci		    chip->capture_a_substream &&
86262306a36Sopenharmony_ci		    chip->capture_a_substream->runtime->channels != 1)
86362306a36Sopenharmony_ci			return -EAGAIN;
86462306a36Sopenharmony_ci		chip->playback_a_substream = substream;
86562306a36Sopenharmony_ci	} else if (substream->number <= 1) {
86662306a36Sopenharmony_ci		if (chip->capture_a_substream)
86762306a36Sopenharmony_ci			return -EAGAIN;
86862306a36Sopenharmony_ci		chip->playback_b_substream = substream;
86962306a36Sopenharmony_ci	} else {
87062306a36Sopenharmony_ci		snd_BUG();
87162306a36Sopenharmony_ci		return -EINVAL;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci	substream->runtime->hw = snd_es18xx_playback;
87462306a36Sopenharmony_ci	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
87562306a36Sopenharmony_ci				      (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks);
87662306a36Sopenharmony_ci        return 0;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic int snd_es18xx_capture_open(struct snd_pcm_substream *substream)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
88262306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci        if (chip->playback_b_substream)
88562306a36Sopenharmony_ci                return -EAGAIN;
88662306a36Sopenharmony_ci	if ((chip->caps & ES18XX_DUPLEX_MONO) &&
88762306a36Sopenharmony_ci	    chip->playback_a_substream &&
88862306a36Sopenharmony_ci	    chip->playback_a_substream->runtime->channels != 1)
88962306a36Sopenharmony_ci		return -EAGAIN;
89062306a36Sopenharmony_ci        chip->capture_a_substream = substream;
89162306a36Sopenharmony_ci	substream->runtime->hw = snd_es18xx_capture;
89262306a36Sopenharmony_ci	snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
89362306a36Sopenharmony_ci				      (chip->caps & ES18XX_NEW_RATE) ? &new_hw_constraints_clocks : &old_hw_constraints_clocks);
89462306a36Sopenharmony_ci        return 0;
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic int snd_es18xx_playback_close(struct snd_pcm_substream *substream)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (substream->number == 0 && (chip->caps & ES18XX_PCM2))
90262306a36Sopenharmony_ci		chip->playback_a_substream = NULL;
90362306a36Sopenharmony_ci	else
90462306a36Sopenharmony_ci		chip->playback_b_substream = NULL;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	return 0;
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic int snd_es18xx_capture_close(struct snd_pcm_substream *substream)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci        struct snd_es18xx *chip = snd_pcm_substream_chip(substream);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci        chip->capture_a_substream = NULL;
91462306a36Sopenharmony_ci        return 0;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci/*
91862306a36Sopenharmony_ci *  MIXER part
91962306a36Sopenharmony_ci */
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci/* Record source mux routines:
92262306a36Sopenharmony_ci * Depending on the chipset this mux switches between 4, 5, or 8 possible inputs.
92362306a36Sopenharmony_ci * bit table for the 4/5 source mux:
92462306a36Sopenharmony_ci * reg 1C:
92562306a36Sopenharmony_ci *  b2 b1 b0   muxSource
92662306a36Sopenharmony_ci *   x  0  x   microphone
92762306a36Sopenharmony_ci *   0  1  x   CD
92862306a36Sopenharmony_ci *   1  1  0   line
92962306a36Sopenharmony_ci *   1  1  1   mixer
93062306a36Sopenharmony_ci * if it's "mixer" and it's a 5 source mux chipset then reg 7A bit 3 determines
93162306a36Sopenharmony_ci * either the play mixer or the capture mixer.
93262306a36Sopenharmony_ci *
93362306a36Sopenharmony_ci * "map4Source" translates from source number to reg bit pattern
93462306a36Sopenharmony_ci * "invMap4Source" translates from reg bit pattern to source number
93562306a36Sopenharmony_ci */
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	static const char * const texts5Source[5] = {
94062306a36Sopenharmony_ci		"Mic", "CD", "Line", "Master", "Mix"
94162306a36Sopenharmony_ci	};
94262306a36Sopenharmony_ci	static const char * const texts8Source[8] = {
94362306a36Sopenharmony_ci		"Mic", "Mic Master", "CD", "AOUT",
94462306a36Sopenharmony_ci		"Mic1", "Mix", "Line", "Master"
94562306a36Sopenharmony_ci	};
94662306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	switch (chip->version) {
94962306a36Sopenharmony_ci	case 0x1868:
95062306a36Sopenharmony_ci	case 0x1878:
95162306a36Sopenharmony_ci		return snd_ctl_enum_info(uinfo, 1, 4, texts5Source);
95262306a36Sopenharmony_ci	case 0x1887:
95362306a36Sopenharmony_ci	case 0x1888:
95462306a36Sopenharmony_ci		return snd_ctl_enum_info(uinfo, 1, 5, texts5Source);
95562306a36Sopenharmony_ci	case 0x1869: /* DS somewhat contradictory for 1869: could be 5 or 8 */
95662306a36Sopenharmony_ci	case 0x1879:
95762306a36Sopenharmony_ci		return snd_ctl_enum_info(uinfo, 1, 8, texts8Source);
95862306a36Sopenharmony_ci	default:
95962306a36Sopenharmony_ci		return -EINVAL;
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	static const unsigned char invMap4Source[8] = {0, 0, 1, 1, 0, 0, 2, 3};
96662306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
96762306a36Sopenharmony_ci	int muxSource = snd_es18xx_mixer_read(chip, 0x1c) & 0x07;
96862306a36Sopenharmony_ci	if (!(chip->version == 0x1869 || chip->version == 0x1879)) {
96962306a36Sopenharmony_ci		muxSource = invMap4Source[muxSource];
97062306a36Sopenharmony_ci		if (muxSource==3 &&
97162306a36Sopenharmony_ci		    (chip->version == 0x1887 || chip->version == 0x1888) &&
97262306a36Sopenharmony_ci		    (snd_es18xx_mixer_read(chip, 0x7a) & 0x08)
97362306a36Sopenharmony_ci		)
97462306a36Sopenharmony_ci			muxSource = 4;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = muxSource;
97762306a36Sopenharmony_ci	return 0;
97862306a36Sopenharmony_ci}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cistatic int snd_es18xx_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
98162306a36Sopenharmony_ci{
98262306a36Sopenharmony_ci	static const unsigned char map4Source[4] = {0, 2, 6, 7};
98362306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
98462306a36Sopenharmony_ci	unsigned char val = ucontrol->value.enumerated.item[0];
98562306a36Sopenharmony_ci	unsigned char retVal = 0;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	switch (chip->version) {
98862306a36Sopenharmony_ci /* 5 source chips */
98962306a36Sopenharmony_ci	case 0x1887:
99062306a36Sopenharmony_ci	case 0x1888:
99162306a36Sopenharmony_ci		if (val > 4)
99262306a36Sopenharmony_ci			return -EINVAL;
99362306a36Sopenharmony_ci		if (val == 4) {
99462306a36Sopenharmony_ci			retVal = snd_es18xx_mixer_bits(chip, 0x7a, 0x08, 0x08) != 0x08;
99562306a36Sopenharmony_ci			val = 3;
99662306a36Sopenharmony_ci		} else
99762306a36Sopenharmony_ci			retVal = snd_es18xx_mixer_bits(chip, 0x7a, 0x08, 0x00) != 0x00;
99862306a36Sopenharmony_ci		fallthrough;
99962306a36Sopenharmony_ci /* 4 source chips */
100062306a36Sopenharmony_ci	case 0x1868:
100162306a36Sopenharmony_ci	case 0x1878:
100262306a36Sopenharmony_ci		if (val > 3)
100362306a36Sopenharmony_ci			return -EINVAL;
100462306a36Sopenharmony_ci		val = map4Source[val];
100562306a36Sopenharmony_ci		break;
100662306a36Sopenharmony_ci /* 8 source chips */
100762306a36Sopenharmony_ci	case 0x1869:
100862306a36Sopenharmony_ci	case 0x1879:
100962306a36Sopenharmony_ci		if (val > 7)
101062306a36Sopenharmony_ci			return -EINVAL;
101162306a36Sopenharmony_ci		break;
101262306a36Sopenharmony_ci	default:
101362306a36Sopenharmony_ci		return -EINVAL;
101462306a36Sopenharmony_ci	}
101562306a36Sopenharmony_ci	return (snd_es18xx_mixer_bits(chip, 0x1c, 0x07, val) != val) || retVal;
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci#define snd_es18xx_info_spatializer_enable	snd_ctl_boolean_mono_info
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_cistatic int snd_es18xx_get_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
102362306a36Sopenharmony_ci	unsigned char val = snd_es18xx_mixer_read(chip, 0x50);
102462306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = !!(val & 8);
102562306a36Sopenharmony_ci	return 0;
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic int snd_es18xx_put_spatializer_enable(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
103162306a36Sopenharmony_ci	unsigned char oval, nval;
103262306a36Sopenharmony_ci	int change;
103362306a36Sopenharmony_ci	nval = ucontrol->value.integer.value[0] ? 0x0c : 0x04;
103462306a36Sopenharmony_ci	oval = snd_es18xx_mixer_read(chip, 0x50) & 0x0c;
103562306a36Sopenharmony_ci	change = nval != oval;
103662306a36Sopenharmony_ci	if (change) {
103762306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x50, nval & ~0x04);
103862306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x50, nval);
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci	return change;
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_cistatic int snd_es18xx_info_hw_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
104662306a36Sopenharmony_ci	uinfo->count = 2;
104762306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
104862306a36Sopenharmony_ci	uinfo->value.integer.max = 63;
104962306a36Sopenharmony_ci	return 0;
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_cistatic int snd_es18xx_get_hw_volume(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
105562306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = snd_es18xx_mixer_read(chip, 0x61) & 0x3f;
105662306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = snd_es18xx_mixer_read(chip, 0x63) & 0x3f;
105762306a36Sopenharmony_ci	return 0;
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci#define snd_es18xx_info_hw_switch	snd_ctl_boolean_stereo_info
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic int snd_es18xx_get_hw_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
106562306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = !(snd_es18xx_mixer_read(chip, 0x61) & 0x40);
106662306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = !(snd_es18xx_mixer_read(chip, 0x63) & 0x40);
106762306a36Sopenharmony_ci	return 0;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_cistatic void snd_es18xx_hwv_free(struct snd_kcontrol *kcontrol)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
107362306a36Sopenharmony_ci	chip->master_volume = NULL;
107462306a36Sopenharmony_ci	chip->master_switch = NULL;
107562306a36Sopenharmony_ci	chip->hw_volume = NULL;
107662306a36Sopenharmony_ci	chip->hw_switch = NULL;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic int snd_es18xx_reg_bits(struct snd_es18xx *chip, unsigned char reg,
108062306a36Sopenharmony_ci			       unsigned char mask, unsigned char val)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	if (reg < 0xa0)
108362306a36Sopenharmony_ci		return snd_es18xx_mixer_bits(chip, reg, mask, val);
108462306a36Sopenharmony_ci	else
108562306a36Sopenharmony_ci		return snd_es18xx_bits(chip, reg, mask, val);
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cistatic int snd_es18xx_reg_read(struct snd_es18xx *chip, unsigned char reg)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci	if (reg < 0xa0)
109162306a36Sopenharmony_ci		return snd_es18xx_mixer_read(chip, reg);
109262306a36Sopenharmony_ci	else
109362306a36Sopenharmony_ci		return snd_es18xx_read(chip, reg);
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, flags) \
109762306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
109862306a36Sopenharmony_ci  .info = snd_es18xx_info_single, \
109962306a36Sopenharmony_ci  .get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \
110062306a36Sopenharmony_ci  .private_value = reg | (shift << 8) | (mask << 16) | (flags << 24) }
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci#define ES18XX_FL_INVERT	(1 << 0)
110362306a36Sopenharmony_ci#define ES18XX_FL_PMPORT	(1 << 1)
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cistatic int snd_es18xx_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
111062306a36Sopenharmony_ci	uinfo->count = 1;
111162306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
111262306a36Sopenharmony_ci	uinfo->value.integer.max = mask;
111362306a36Sopenharmony_ci	return 0;
111462306a36Sopenharmony_ci}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_cistatic int snd_es18xx_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
111962306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
112062306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
112162306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
112262306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT;
112362306a36Sopenharmony_ci	int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT;
112462306a36Sopenharmony_ci	int val;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	if (pm_port)
112762306a36Sopenharmony_ci		val = inb(chip->port + ES18XX_PM);
112862306a36Sopenharmony_ci	else
112962306a36Sopenharmony_ci		val = snd_es18xx_reg_read(chip, reg);
113062306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = (val >> shift) & mask;
113162306a36Sopenharmony_ci	if (invert)
113262306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
113362306a36Sopenharmony_ci	return 0;
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_cistatic int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
113762306a36Sopenharmony_ci{
113862306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
113962306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
114062306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
114162306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
114262306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT;
114362306a36Sopenharmony_ci	int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT;
114462306a36Sopenharmony_ci	unsigned char val;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	val = (ucontrol->value.integer.value[0] & mask);
114762306a36Sopenharmony_ci	if (invert)
114862306a36Sopenharmony_ci		val = mask - val;
114962306a36Sopenharmony_ci	mask <<= shift;
115062306a36Sopenharmony_ci	val <<= shift;
115162306a36Sopenharmony_ci	if (pm_port) {
115262306a36Sopenharmony_ci		unsigned char cur = inb(chip->port + ES18XX_PM);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci		if ((cur & mask) == val)
115562306a36Sopenharmony_ci			return 0;
115662306a36Sopenharmony_ci		outb((cur & ~mask) | val, chip->port + ES18XX_PM);
115762306a36Sopenharmony_ci		return 1;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return snd_es18xx_reg_bits(chip, reg, mask, val) != val;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci#define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
116462306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
116562306a36Sopenharmony_ci  .info = snd_es18xx_info_double, \
116662306a36Sopenharmony_ci  .get = snd_es18xx_get_double, .put = snd_es18xx_put_double, \
116762306a36Sopenharmony_ci  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_cistatic int snd_es18xx_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
117062306a36Sopenharmony_ci{
117162306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
117462306a36Sopenharmony_ci	uinfo->count = 2;
117562306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
117662306a36Sopenharmony_ci	uinfo->value.integer.max = mask;
117762306a36Sopenharmony_ci	return 0;
117862306a36Sopenharmony_ci}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_cistatic int snd_es18xx_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
118362306a36Sopenharmony_ci	int left_reg = kcontrol->private_value & 0xff;
118462306a36Sopenharmony_ci	int right_reg = (kcontrol->private_value >> 8) & 0xff;
118562306a36Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 16) & 0x07;
118662306a36Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 19) & 0x07;
118762306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
118862306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 22) & 1;
118962306a36Sopenharmony_ci	unsigned char left, right;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	left = snd_es18xx_reg_read(chip, left_reg);
119262306a36Sopenharmony_ci	if (left_reg != right_reg)
119362306a36Sopenharmony_ci		right = snd_es18xx_reg_read(chip, right_reg);
119462306a36Sopenharmony_ci	else
119562306a36Sopenharmony_ci		right = left;
119662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = (left >> shift_left) & mask;
119762306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = (right >> shift_right) & mask;
119862306a36Sopenharmony_ci	if (invert) {
119962306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
120062306a36Sopenharmony_ci		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci	return 0;
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic int snd_es18xx_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol);
120862306a36Sopenharmony_ci	int left_reg = kcontrol->private_value & 0xff;
120962306a36Sopenharmony_ci	int right_reg = (kcontrol->private_value >> 8) & 0xff;
121062306a36Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 16) & 0x07;
121162306a36Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 19) & 0x07;
121262306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
121362306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 22) & 1;
121462306a36Sopenharmony_ci	int change;
121562306a36Sopenharmony_ci	unsigned char val1, val2, mask1, mask2;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	val1 = ucontrol->value.integer.value[0] & mask;
121862306a36Sopenharmony_ci	val2 = ucontrol->value.integer.value[1] & mask;
121962306a36Sopenharmony_ci	if (invert) {
122062306a36Sopenharmony_ci		val1 = mask - val1;
122162306a36Sopenharmony_ci		val2 = mask - val2;
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci	val1 <<= shift_left;
122462306a36Sopenharmony_ci	val2 <<= shift_right;
122562306a36Sopenharmony_ci	mask1 = mask << shift_left;
122662306a36Sopenharmony_ci	mask2 = mask << shift_right;
122762306a36Sopenharmony_ci	if (left_reg != right_reg) {
122862306a36Sopenharmony_ci		change = 0;
122962306a36Sopenharmony_ci		if (snd_es18xx_reg_bits(chip, left_reg, mask1, val1) != val1)
123062306a36Sopenharmony_ci			change = 1;
123162306a36Sopenharmony_ci		if (snd_es18xx_reg_bits(chip, right_reg, mask2, val2) != val2)
123262306a36Sopenharmony_ci			change = 1;
123362306a36Sopenharmony_ci	} else {
123462306a36Sopenharmony_ci		change = (snd_es18xx_reg_bits(chip, left_reg, mask1 | mask2,
123562306a36Sopenharmony_ci					      val1 | val2) != (val1 | val2));
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci	return change;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci/* Mixer controls
124162306a36Sopenharmony_ci * These arrays contain setup data for mixer controls.
124262306a36Sopenharmony_ci *
124362306a36Sopenharmony_ci * The controls that are universal to all chipsets are fully initialized
124462306a36Sopenharmony_ci * here.
124562306a36Sopenharmony_ci */
124662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_base_controls[] = {
124762306a36Sopenharmony_ciES18XX_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
124862306a36Sopenharmony_ciES18XX_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
124962306a36Sopenharmony_ciES18XX_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
125062306a36Sopenharmony_ciES18XX_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
125162306a36Sopenharmony_ciES18XX_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
125262306a36Sopenharmony_ciES18XX_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
125362306a36Sopenharmony_ciES18XX_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
125462306a36Sopenharmony_ciES18XX_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
125562306a36Sopenharmony_ciES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
125862306a36Sopenharmony_ci	.name = "Capture Source",
125962306a36Sopenharmony_ci	.info = snd_es18xx_info_mux,
126062306a36Sopenharmony_ci	.get = snd_es18xx_get_mux,
126162306a36Sopenharmony_ci	.put = snd_es18xx_put_mux,
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci};
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_recmix_controls[] = {
126662306a36Sopenharmony_ciES18XX_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
126762306a36Sopenharmony_ciES18XX_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
126862306a36Sopenharmony_ciES18XX_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
126962306a36Sopenharmony_ciES18XX_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
127062306a36Sopenharmony_ciES18XX_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
127162306a36Sopenharmony_ciES18XX_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0)
127262306a36Sopenharmony_ci};
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci/*
127562306a36Sopenharmony_ci * The chipset specific mixer controls
127662306a36Sopenharmony_ci */
127762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_speaker =
127862306a36Sopenharmony_ci	ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_1869[] = {
128162306a36Sopenharmony_ciES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, ES18XX_FL_INVERT),
128262306a36Sopenharmony_ciES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0),
128362306a36Sopenharmony_ciES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
128462306a36Sopenharmony_ciES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)
128562306a36Sopenharmony_ci};
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_1878 =
128862306a36Sopenharmony_ci	ES18XX_DOUBLE("Video Playback Volume", 0, 0x68, 0x68, 4, 0, 15, 0);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_1879[] = {
129162306a36Sopenharmony_ciES18XX_SINGLE("Video Playback Switch", 0, 0x71, 6, 1, 0),
129262306a36Sopenharmony_ciES18XX_DOUBLE("Video Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
129362306a36Sopenharmony_ciES18XX_DOUBLE("Video Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)
129462306a36Sopenharmony_ci};
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_pcm1_controls[] = {
129762306a36Sopenharmony_ciES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0),
129862306a36Sopenharmony_ci};
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_pcm2_controls[] = {
130162306a36Sopenharmony_ciES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
130262306a36Sopenharmony_ciES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0)
130362306a36Sopenharmony_ci};
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_spatializer_controls[] = {
130662306a36Sopenharmony_ciES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
130962306a36Sopenharmony_ci	.name = "3D Control - Switch",
131062306a36Sopenharmony_ci	.info = snd_es18xx_info_spatializer_enable,
131162306a36Sopenharmony_ci	.get = snd_es18xx_get_spatializer_enable,
131262306a36Sopenharmony_ci	.put = snd_es18xx_put_spatializer_enable,
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci};
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_micpre1_control =
131762306a36Sopenharmony_ciES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_micpre2_control =
132062306a36Sopenharmony_ciES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0);
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
132562306a36Sopenharmony_ci	.name = "Hardware Master Playback Volume",
132662306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ,
132762306a36Sopenharmony_ci	.info = snd_es18xx_info_hw_volume,
132862306a36Sopenharmony_ci	.get = snd_es18xx_get_hw_volume,
132962306a36Sopenharmony_ci},
133062306a36Sopenharmony_ci{
133162306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
133262306a36Sopenharmony_ci	.name = "Hardware Master Playback Switch",
133362306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ,
133462306a36Sopenharmony_ci	.info = snd_es18xx_info_hw_switch,
133562306a36Sopenharmony_ci	.get = snd_es18xx_get_hw_switch,
133662306a36Sopenharmony_ci},
133762306a36Sopenharmony_ciES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
133862306a36Sopenharmony_ci};
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] = {
134162306a36Sopenharmony_ciES18XX_SINGLE("GPO0 Switch", 0, ES18XX_PM, 0, 1, ES18XX_FL_PMPORT),
134262306a36Sopenharmony_ciES18XX_SINGLE("GPO1 Switch", 0, ES18XX_PM, 1, 1, ES18XX_FL_PMPORT),
134362306a36Sopenharmony_ci};
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic int snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	outb(reg, chip->ctrl_port);
134862306a36Sopenharmony_ci	return inb(chip->ctrl_port + 1);
134962306a36Sopenharmony_ci}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cistatic void snd_es18xx_config_write(struct snd_es18xx *chip,
135262306a36Sopenharmony_ci				    unsigned char reg, unsigned char data)
135362306a36Sopenharmony_ci{
135462306a36Sopenharmony_ci	/* No need for spinlocks, this function is used only in
135562306a36Sopenharmony_ci	   otherwise protected init code */
135662306a36Sopenharmony_ci	outb(reg, chip->ctrl_port);
135762306a36Sopenharmony_ci	outb(data, chip->ctrl_port + 1);
135862306a36Sopenharmony_ci#ifdef REG_DEBUG
135962306a36Sopenharmony_ci	snd_printk(KERN_DEBUG "Config reg %02x set to %02x\n", reg, data);
136062306a36Sopenharmony_ci#endif
136162306a36Sopenharmony_ci}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_cistatic int snd_es18xx_initialize(struct snd_es18xx *chip,
136462306a36Sopenharmony_ci				 unsigned long mpu_port,
136562306a36Sopenharmony_ci				 unsigned long fm_port)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	int mask = 0;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci        /* enable extended mode */
137062306a36Sopenharmony_ci        snd_es18xx_dsp_command(chip, 0xC6);
137162306a36Sopenharmony_ci	/* Reset mixer registers */
137262306a36Sopenharmony_ci	snd_es18xx_mixer_write(chip, 0x00, 0x00);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci        /* Audio 1 DMA demand mode (4 bytes/request) */
137562306a36Sopenharmony_ci        snd_es18xx_write(chip, 0xB9, 2);
137662306a36Sopenharmony_ci	if (chip->caps & ES18XX_CONTROL) {
137762306a36Sopenharmony_ci		/* Hardware volume IRQ */
137862306a36Sopenharmony_ci		snd_es18xx_config_write(chip, 0x27, chip->irq);
137962306a36Sopenharmony_ci		if (fm_port > 0 && fm_port != SNDRV_AUTO_PORT) {
138062306a36Sopenharmony_ci			/* FM I/O */
138162306a36Sopenharmony_ci			snd_es18xx_config_write(chip, 0x62, fm_port >> 8);
138262306a36Sopenharmony_ci			snd_es18xx_config_write(chip, 0x63, fm_port & 0xff);
138362306a36Sopenharmony_ci		}
138462306a36Sopenharmony_ci		if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) {
138562306a36Sopenharmony_ci			/* MPU-401 I/O */
138662306a36Sopenharmony_ci			snd_es18xx_config_write(chip, 0x64, mpu_port >> 8);
138762306a36Sopenharmony_ci			snd_es18xx_config_write(chip, 0x65, mpu_port & 0xff);
138862306a36Sopenharmony_ci			/* MPU-401 IRQ */
138962306a36Sopenharmony_ci			snd_es18xx_config_write(chip, 0x28, chip->irq);
139062306a36Sopenharmony_ci		}
139162306a36Sopenharmony_ci		/* Audio1 IRQ */
139262306a36Sopenharmony_ci		snd_es18xx_config_write(chip, 0x70, chip->irq);
139362306a36Sopenharmony_ci		/* Audio2 IRQ */
139462306a36Sopenharmony_ci		snd_es18xx_config_write(chip, 0x72, chip->irq);
139562306a36Sopenharmony_ci		/* Audio1 DMA */
139662306a36Sopenharmony_ci		snd_es18xx_config_write(chip, 0x74, chip->dma1);
139762306a36Sopenharmony_ci		/* Audio2 DMA */
139862306a36Sopenharmony_ci		snd_es18xx_config_write(chip, 0x75, chip->dma2);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci		/* Enable Audio 1 IRQ */
140162306a36Sopenharmony_ci		snd_es18xx_write(chip, 0xB1, 0x50);
140262306a36Sopenharmony_ci		/* Enable Audio 2 IRQ */
140362306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x7A, 0x40);
140462306a36Sopenharmony_ci		/* Enable Audio 1 DMA */
140562306a36Sopenharmony_ci		snd_es18xx_write(chip, 0xB2, 0x50);
140662306a36Sopenharmony_ci		/* Enable MPU and hardware volume interrupt */
140762306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x64, 0x42);
140862306a36Sopenharmony_ci		/* Enable ESS wavetable input */
140962306a36Sopenharmony_ci		snd_es18xx_mixer_bits(chip, 0x48, 0x10, 0x10);
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci	else {
141262306a36Sopenharmony_ci		int irqmask, dma1mask, dma2mask;
141362306a36Sopenharmony_ci		switch (chip->irq) {
141462306a36Sopenharmony_ci		case 2:
141562306a36Sopenharmony_ci		case 9:
141662306a36Sopenharmony_ci			irqmask = 0;
141762306a36Sopenharmony_ci			break;
141862306a36Sopenharmony_ci		case 5:
141962306a36Sopenharmony_ci			irqmask = 1;
142062306a36Sopenharmony_ci			break;
142162306a36Sopenharmony_ci		case 7:
142262306a36Sopenharmony_ci			irqmask = 2;
142362306a36Sopenharmony_ci			break;
142462306a36Sopenharmony_ci		case 10:
142562306a36Sopenharmony_ci			irqmask = 3;
142662306a36Sopenharmony_ci			break;
142762306a36Sopenharmony_ci		default:
142862306a36Sopenharmony_ci			snd_printk(KERN_ERR "invalid irq %d\n", chip->irq);
142962306a36Sopenharmony_ci			return -ENODEV;
143062306a36Sopenharmony_ci		}
143162306a36Sopenharmony_ci		switch (chip->dma1) {
143262306a36Sopenharmony_ci		case 0:
143362306a36Sopenharmony_ci			dma1mask = 1;
143462306a36Sopenharmony_ci			break;
143562306a36Sopenharmony_ci		case 1:
143662306a36Sopenharmony_ci			dma1mask = 2;
143762306a36Sopenharmony_ci			break;
143862306a36Sopenharmony_ci		case 3:
143962306a36Sopenharmony_ci			dma1mask = 3;
144062306a36Sopenharmony_ci			break;
144162306a36Sopenharmony_ci		default:
144262306a36Sopenharmony_ci			snd_printk(KERN_ERR "invalid dma1 %d\n", chip->dma1);
144362306a36Sopenharmony_ci			return -ENODEV;
144462306a36Sopenharmony_ci		}
144562306a36Sopenharmony_ci		switch (chip->dma2) {
144662306a36Sopenharmony_ci		case 0:
144762306a36Sopenharmony_ci			dma2mask = 0;
144862306a36Sopenharmony_ci			break;
144962306a36Sopenharmony_ci		case 1:
145062306a36Sopenharmony_ci			dma2mask = 1;
145162306a36Sopenharmony_ci			break;
145262306a36Sopenharmony_ci		case 3:
145362306a36Sopenharmony_ci			dma2mask = 2;
145462306a36Sopenharmony_ci			break;
145562306a36Sopenharmony_ci		case 5:
145662306a36Sopenharmony_ci			dma2mask = 3;
145762306a36Sopenharmony_ci			break;
145862306a36Sopenharmony_ci		default:
145962306a36Sopenharmony_ci			snd_printk(KERN_ERR "invalid dma2 %d\n", chip->dma2);
146062306a36Sopenharmony_ci			return -ENODEV;
146162306a36Sopenharmony_ci		}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci		/* Enable and set Audio 1 IRQ */
146462306a36Sopenharmony_ci		snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2));
146562306a36Sopenharmony_ci		/* Enable and set Audio 1 DMA */
146662306a36Sopenharmony_ci		snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2));
146762306a36Sopenharmony_ci		/* Set Audio 2 DMA */
146862306a36Sopenharmony_ci		snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask);
146962306a36Sopenharmony_ci		/* Enable Audio 2 IRQ and DMA
147062306a36Sopenharmony_ci		   Set capture mixer input */
147162306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x7A, 0x68);
147262306a36Sopenharmony_ci		/* Enable and set hardware volume interrupt */
147362306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x64, 0x06);
147462306a36Sopenharmony_ci		if (mpu_port > 0 && mpu_port != SNDRV_AUTO_PORT) {
147562306a36Sopenharmony_ci			/* MPU401 share irq with audio
147662306a36Sopenharmony_ci			   Joystick enabled
147762306a36Sopenharmony_ci			   FM enabled */
147862306a36Sopenharmony_ci			snd_es18xx_mixer_write(chip, 0x40,
147962306a36Sopenharmony_ci					       0x43 | (mpu_port & 0xf0) >> 1);
148062306a36Sopenharmony_ci		}
148162306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01);
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci	if (chip->caps & ES18XX_NEW_RATE) {
148462306a36Sopenharmony_ci		/* Change behaviour of register A1
148562306a36Sopenharmony_ci		   4x oversampling
148662306a36Sopenharmony_ci		   2nd channel DAC asynchronous */
148762306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x71, 0x32);
148862306a36Sopenharmony_ci	}
148962306a36Sopenharmony_ci	if (!(chip->caps & ES18XX_PCM2)) {
149062306a36Sopenharmony_ci		/* Enable DMA FIFO */
149162306a36Sopenharmony_ci		snd_es18xx_write(chip, 0xB7, 0x80);
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci	if (chip->caps & ES18XX_SPATIALIZER) {
149462306a36Sopenharmony_ci		/* Set spatializer parameters to recommended values */
149562306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x54, 0x8f);
149662306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x56, 0x95);
149762306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x58, 0x94);
149862306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x5a, 0x80);
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci	/* Flip the "enable I2S" bits for those chipsets that need it */
150162306a36Sopenharmony_ci	switch (chip->version) {
150262306a36Sopenharmony_ci	case 0x1879:
150362306a36Sopenharmony_ci		//Leaving I2S enabled on the 1879 screws up the PCM playback (rate effected somehow)
150462306a36Sopenharmony_ci		//so a Switch control has been added to toggle this 0x71 bit on/off:
150562306a36Sopenharmony_ci		//snd_es18xx_mixer_bits(chip, 0x71, 0x40, 0x40);
150662306a36Sopenharmony_ci		/* Note: we fall through on purpose here. */
150762306a36Sopenharmony_ci	case 0x1878:
150862306a36Sopenharmony_ci		snd_es18xx_config_write(chip, 0x29, snd_es18xx_config_read(chip, 0x29) | 0x40);
150962306a36Sopenharmony_ci		break;
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci	/* Mute input source */
151262306a36Sopenharmony_ci	if (chip->caps & ES18XX_MUTEREC)
151362306a36Sopenharmony_ci		mask = 0x10;
151462306a36Sopenharmony_ci	if (chip->caps & ES18XX_RECMIX)
151562306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask);
151662306a36Sopenharmony_ci	else {
151762306a36Sopenharmony_ci		snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask);
151862306a36Sopenharmony_ci		snd_es18xx_write(chip, 0xb4, 0x00);
151962306a36Sopenharmony_ci	}
152062306a36Sopenharmony_ci#ifndef AVOID_POPS
152162306a36Sopenharmony_ci	/* Enable PCM output */
152262306a36Sopenharmony_ci	snd_es18xx_dsp_command(chip, 0xD1);
152362306a36Sopenharmony_ci#endif
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci        return 0;
152662306a36Sopenharmony_ci}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_cistatic int snd_es18xx_identify(struct snd_card *card, struct snd_es18xx *chip)
152962306a36Sopenharmony_ci{
153062306a36Sopenharmony_ci	int hi,lo;
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	/* reset */
153362306a36Sopenharmony_ci	if (snd_es18xx_reset(chip) < 0) {
153462306a36Sopenharmony_ci		snd_printk(KERN_ERR "reset at 0x%lx failed!!!\n", chip->port);
153562306a36Sopenharmony_ci		return -ENODEV;
153662306a36Sopenharmony_ci	}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	snd_es18xx_dsp_command(chip, 0xe7);
153962306a36Sopenharmony_ci	hi = snd_es18xx_dsp_get_byte(chip);
154062306a36Sopenharmony_ci	if (hi < 0) {
154162306a36Sopenharmony_ci		return hi;
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci	lo = snd_es18xx_dsp_get_byte(chip);
154462306a36Sopenharmony_ci	if ((lo & 0xf0) != 0x80) {
154562306a36Sopenharmony_ci		return -ENODEV;
154662306a36Sopenharmony_ci	}
154762306a36Sopenharmony_ci	if (hi == 0x48) {
154862306a36Sopenharmony_ci		chip->version = 0x488;
154962306a36Sopenharmony_ci		return 0;
155062306a36Sopenharmony_ci	}
155162306a36Sopenharmony_ci	if (hi != 0x68) {
155262306a36Sopenharmony_ci		return -ENODEV;
155362306a36Sopenharmony_ci	}
155462306a36Sopenharmony_ci	if ((lo & 0x0f) < 8) {
155562306a36Sopenharmony_ci		chip->version = 0x688;
155662306a36Sopenharmony_ci		return 0;
155762306a36Sopenharmony_ci	}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci        outb(0x40, chip->port + 0x04);
156062306a36Sopenharmony_ci	udelay(10);
156162306a36Sopenharmony_ci	hi = inb(chip->port + 0x05);
156262306a36Sopenharmony_ci	udelay(10);
156362306a36Sopenharmony_ci	lo = inb(chip->port + 0x05);
156462306a36Sopenharmony_ci	if (hi != lo) {
156562306a36Sopenharmony_ci		chip->version = hi << 8 | lo;
156662306a36Sopenharmony_ci		chip->ctrl_port = inb(chip->port + 0x05) << 8;
156762306a36Sopenharmony_ci		udelay(10);
156862306a36Sopenharmony_ci		chip->ctrl_port += inb(chip->port + 0x05);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci		if (!devm_request_region(card->dev, chip->ctrl_port, 8,
157162306a36Sopenharmony_ci					 "ES18xx - CTRL")) {
157262306a36Sopenharmony_ci			snd_printk(KERN_ERR PFX "unable go grab port 0x%lx\n", chip->ctrl_port);
157362306a36Sopenharmony_ci			return -EBUSY;
157462306a36Sopenharmony_ci		}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci		return 0;
157762306a36Sopenharmony_ci	}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	/* If has Hardware volume */
158062306a36Sopenharmony_ci	if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) {
158162306a36Sopenharmony_ci		/* If has Audio2 */
158262306a36Sopenharmony_ci		if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) {
158362306a36Sopenharmony_ci			/* If has volume count */
158462306a36Sopenharmony_ci			if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) {
158562306a36Sopenharmony_ci				chip->version = 0x1887;
158662306a36Sopenharmony_ci			} else {
158762306a36Sopenharmony_ci				chip->version = 0x1888;
158862306a36Sopenharmony_ci			}
158962306a36Sopenharmony_ci		} else {
159062306a36Sopenharmony_ci			chip->version = 0x1788;
159162306a36Sopenharmony_ci		}
159262306a36Sopenharmony_ci	}
159362306a36Sopenharmony_ci	else
159462306a36Sopenharmony_ci		chip->version = 0x1688;
159562306a36Sopenharmony_ci	return 0;
159662306a36Sopenharmony_ci}
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_cistatic int snd_es18xx_probe(struct snd_card *card,
159962306a36Sopenharmony_ci			    struct snd_es18xx *chip,
160062306a36Sopenharmony_ci			    unsigned long mpu_port,
160162306a36Sopenharmony_ci			    unsigned long fm_port)
160262306a36Sopenharmony_ci{
160362306a36Sopenharmony_ci	if (snd_es18xx_identify(card, chip) < 0) {
160462306a36Sopenharmony_ci		snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);
160562306a36Sopenharmony_ci                return -ENODEV;
160662306a36Sopenharmony_ci	}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	switch (chip->version) {
160962306a36Sopenharmony_ci	case 0x1868:
161062306a36Sopenharmony_ci		chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_GPO_2BIT;
161162306a36Sopenharmony_ci		break;
161262306a36Sopenharmony_ci	case 0x1869:
161362306a36Sopenharmony_ci		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV | ES18XX_GPO_2BIT;
161462306a36Sopenharmony_ci		break;
161562306a36Sopenharmony_ci	case 0x1878:
161662306a36Sopenharmony_ci		chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL;
161762306a36Sopenharmony_ci		break;
161862306a36Sopenharmony_ci	case 0x1879:
161962306a36Sopenharmony_ci		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;
162062306a36Sopenharmony_ci		break;
162162306a36Sopenharmony_ci	case 0x1887:
162262306a36Sopenharmony_ci	case 0x1888:
162362306a36Sopenharmony_ci		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_GPO_2BIT;
162462306a36Sopenharmony_ci		break;
162562306a36Sopenharmony_ci	default:
162662306a36Sopenharmony_ci		snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n",
162762306a36Sopenharmony_ci                           chip->port, chip->version);
162862306a36Sopenharmony_ci                return -ENODEV;
162962306a36Sopenharmony_ci        }
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci        snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (chip->dma1 == chip->dma2)
163462306a36Sopenharmony_ci		chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME);
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	return snd_es18xx_initialize(chip, mpu_port, fm_port);
163762306a36Sopenharmony_ci}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_es18xx_playback_ops = {
164062306a36Sopenharmony_ci	.open =		snd_es18xx_playback_open,
164162306a36Sopenharmony_ci	.close =	snd_es18xx_playback_close,
164262306a36Sopenharmony_ci	.hw_params =	snd_es18xx_playback_hw_params,
164362306a36Sopenharmony_ci	.prepare =	snd_es18xx_playback_prepare,
164462306a36Sopenharmony_ci	.trigger =	snd_es18xx_playback_trigger,
164562306a36Sopenharmony_ci	.pointer =	snd_es18xx_playback_pointer,
164662306a36Sopenharmony_ci};
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_es18xx_capture_ops = {
164962306a36Sopenharmony_ci	.open =		snd_es18xx_capture_open,
165062306a36Sopenharmony_ci	.close =	snd_es18xx_capture_close,
165162306a36Sopenharmony_ci	.hw_params =	snd_es18xx_capture_hw_params,
165262306a36Sopenharmony_ci	.prepare =	snd_es18xx_capture_prepare,
165362306a36Sopenharmony_ci	.trigger =	snd_es18xx_capture_trigger,
165462306a36Sopenharmony_ci	.pointer =	snd_es18xx_capture_pointer,
165562306a36Sopenharmony_ci};
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_cistatic int snd_es18xx_pcm(struct snd_card *card, int device)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	struct snd_es18xx *chip = card->private_data;
166062306a36Sopenharmony_ci        struct snd_pcm *pcm;
166162306a36Sopenharmony_ci	char str[16];
166262306a36Sopenharmony_ci	int err;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	sprintf(str, "ES%x", chip->version);
166562306a36Sopenharmony_ci	if (chip->caps & ES18XX_PCM2)
166662306a36Sopenharmony_ci		err = snd_pcm_new(card, str, device, 2, 1, &pcm);
166762306a36Sopenharmony_ci	else
166862306a36Sopenharmony_ci		err = snd_pcm_new(card, str, device, 1, 1, &pcm);
166962306a36Sopenharmony_ci        if (err < 0)
167062306a36Sopenharmony_ci                return err;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops);
167362306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	/* global setup */
167662306a36Sopenharmony_ci        pcm->private_data = chip;
167762306a36Sopenharmony_ci        pcm->info_flags = 0;
167862306a36Sopenharmony_ci	if (chip->caps & ES18XX_DUPLEX_SAME)
167962306a36Sopenharmony_ci		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
168062306a36Sopenharmony_ci	if (! (chip->caps & ES18XX_PCM2))
168162306a36Sopenharmony_ci		pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
168262306a36Sopenharmony_ci	sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version);
168362306a36Sopenharmony_ci        chip->pcm = pcm;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, card->dev,
168662306a36Sopenharmony_ci				       64*1024,
168762306a36Sopenharmony_ci				       chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
168862306a36Sopenharmony_ci	return 0;
168962306a36Sopenharmony_ci}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci/* Power Management support functions */
169262306a36Sopenharmony_ci#ifdef CONFIG_PM
169362306a36Sopenharmony_cistatic int snd_es18xx_suspend(struct snd_card *card, pm_message_t state)
169462306a36Sopenharmony_ci{
169562306a36Sopenharmony_ci	struct snd_es18xx *chip = card->private_data;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	/* power down */
170062306a36Sopenharmony_ci	chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM);
170162306a36Sopenharmony_ci	chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS);
170262306a36Sopenharmony_ci	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg);
170362306a36Sopenharmony_ci	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS);
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	return 0;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_cistatic int snd_es18xx_resume(struct snd_card *card)
170962306a36Sopenharmony_ci{
171062306a36Sopenharmony_ci	struct snd_es18xx *chip = card->private_data;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	/* restore PM register, we won't wake till (not 0x07) i/o activity though */
171362306a36Sopenharmony_ci	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM);
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
171662306a36Sopenharmony_ci	return 0;
171762306a36Sopenharmony_ci}
171862306a36Sopenharmony_ci#endif /* CONFIG_PM */
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_cistatic int snd_es18xx_new_device(struct snd_card *card,
172162306a36Sopenharmony_ci				 unsigned long port,
172262306a36Sopenharmony_ci				 unsigned long mpu_port,
172362306a36Sopenharmony_ci				 unsigned long fm_port,
172462306a36Sopenharmony_ci				 int irq, int dma1, int dma2)
172562306a36Sopenharmony_ci{
172662306a36Sopenharmony_ci	struct snd_es18xx *chip = card->private_data;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	spin_lock_init(&chip->reg_lock);
172962306a36Sopenharmony_ci 	spin_lock_init(&chip->mixer_lock);
173062306a36Sopenharmony_ci        chip->port = port;
173162306a36Sopenharmony_ci        chip->irq = -1;
173262306a36Sopenharmony_ci        chip->dma1 = -1;
173362306a36Sopenharmony_ci        chip->dma2 = -1;
173462306a36Sopenharmony_ci        chip->audio2_vol = 0x00;
173562306a36Sopenharmony_ci	chip->active = 0;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	if (!devm_request_region(card->dev, port, 16, "ES18xx")) {
173862306a36Sopenharmony_ci		snd_printk(KERN_ERR PFX "unable to grap ports 0x%lx-0x%lx\n", port, port + 16 - 1);
173962306a36Sopenharmony_ci		return -EBUSY;
174062306a36Sopenharmony_ci	}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	if (devm_request_irq(card->dev, irq, snd_es18xx_interrupt, 0, "ES18xx",
174362306a36Sopenharmony_ci			     (void *) card)) {
174462306a36Sopenharmony_ci		snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
174562306a36Sopenharmony_ci		return -EBUSY;
174662306a36Sopenharmony_ci	}
174762306a36Sopenharmony_ci	chip->irq = irq;
174862306a36Sopenharmony_ci	card->sync_irq = chip->irq;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	if (snd_devm_request_dma(card->dev, dma1, "ES18xx DMA 1")) {
175162306a36Sopenharmony_ci		snd_printk(KERN_ERR PFX "unable to grap DMA1 %d\n", dma1);
175262306a36Sopenharmony_ci		return -EBUSY;
175362306a36Sopenharmony_ci	}
175462306a36Sopenharmony_ci	chip->dma1 = dma1;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	if (dma2 != dma1 &&
175762306a36Sopenharmony_ci	    snd_devm_request_dma(card->dev, dma2, "ES18xx DMA 2")) {
175862306a36Sopenharmony_ci		snd_printk(KERN_ERR PFX "unable to grap DMA2 %d\n", dma2);
175962306a36Sopenharmony_ci		return -EBUSY;
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci	chip->dma2 = dma2;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	if (snd_es18xx_probe(card, chip, mpu_port, fm_port) < 0)
176462306a36Sopenharmony_ci		return -ENODEV;
176562306a36Sopenharmony_ci        return 0;
176662306a36Sopenharmony_ci}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cistatic int snd_es18xx_mixer(struct snd_card *card)
176962306a36Sopenharmony_ci{
177062306a36Sopenharmony_ci	struct snd_es18xx *chip = card->private_data;
177162306a36Sopenharmony_ci	int err;
177262306a36Sopenharmony_ci	unsigned int idx;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	strcpy(card->mixername, chip->pcm->name);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_base_controls); idx++) {
177762306a36Sopenharmony_ci		struct snd_kcontrol *kctl;
177862306a36Sopenharmony_ci		kctl = snd_ctl_new1(&snd_es18xx_base_controls[idx], chip);
177962306a36Sopenharmony_ci		if (chip->caps & ES18XX_HWV) {
178062306a36Sopenharmony_ci			switch (idx) {
178162306a36Sopenharmony_ci			case 0:
178262306a36Sopenharmony_ci				chip->master_volume = kctl;
178362306a36Sopenharmony_ci				kctl->private_free = snd_es18xx_hwv_free;
178462306a36Sopenharmony_ci				break;
178562306a36Sopenharmony_ci			case 1:
178662306a36Sopenharmony_ci				chip->master_switch = kctl;
178762306a36Sopenharmony_ci				kctl->private_free = snd_es18xx_hwv_free;
178862306a36Sopenharmony_ci				break;
178962306a36Sopenharmony_ci			}
179062306a36Sopenharmony_ci		}
179162306a36Sopenharmony_ci		err = snd_ctl_add(card, kctl);
179262306a36Sopenharmony_ci		if (err < 0)
179362306a36Sopenharmony_ci			return err;
179462306a36Sopenharmony_ci	}
179562306a36Sopenharmony_ci	if (chip->caps & ES18XX_PCM2) {
179662306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm2_controls); idx++) {
179762306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm2_controls[idx], chip));
179862306a36Sopenharmony_ci			if (err < 0)
179962306a36Sopenharmony_ci				return err;
180062306a36Sopenharmony_ci		}
180162306a36Sopenharmony_ci	} else {
180262306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_pcm1_controls); idx++) {
180362306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_pcm1_controls[idx], chip));
180462306a36Sopenharmony_ci			if (err < 0)
180562306a36Sopenharmony_ci				return err;
180662306a36Sopenharmony_ci		}
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	if (chip->caps & ES18XX_RECMIX) {
181062306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_recmix_controls); idx++) {
181162306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_recmix_controls[idx], chip));
181262306a36Sopenharmony_ci			if (err < 0)
181362306a36Sopenharmony_ci				return err;
181462306a36Sopenharmony_ci		}
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci	switch (chip->version) {
181762306a36Sopenharmony_ci	default:
181862306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre1_control, chip));
181962306a36Sopenharmony_ci		if (err < 0)
182062306a36Sopenharmony_ci			return err;
182162306a36Sopenharmony_ci		break;
182262306a36Sopenharmony_ci	case 0x1869:
182362306a36Sopenharmony_ci	case 0x1879:
182462306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_micpre2_control, chip));
182562306a36Sopenharmony_ci		if (err < 0)
182662306a36Sopenharmony_ci			return err;
182762306a36Sopenharmony_ci		break;
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci	if (chip->caps & ES18XX_SPATIALIZER) {
183062306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_spatializer_controls); idx++) {
183162306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_spatializer_controls[idx], chip));
183262306a36Sopenharmony_ci			if (err < 0)
183362306a36Sopenharmony_ci				return err;
183462306a36Sopenharmony_ci		}
183562306a36Sopenharmony_ci	}
183662306a36Sopenharmony_ci	if (chip->caps & ES18XX_HWV) {
183762306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_hw_volume_controls); idx++) {
183862306a36Sopenharmony_ci			struct snd_kcontrol *kctl;
183962306a36Sopenharmony_ci			kctl = snd_ctl_new1(&snd_es18xx_hw_volume_controls[idx], chip);
184062306a36Sopenharmony_ci			if (idx == 0)
184162306a36Sopenharmony_ci				chip->hw_volume = kctl;
184262306a36Sopenharmony_ci			else
184362306a36Sopenharmony_ci				chip->hw_switch = kctl;
184462306a36Sopenharmony_ci			kctl->private_free = snd_es18xx_hwv_free;
184562306a36Sopenharmony_ci			err = snd_ctl_add(card, kctl);
184662306a36Sopenharmony_ci			if (err < 0)
184762306a36Sopenharmony_ci				return err;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci		}
185062306a36Sopenharmony_ci	}
185162306a36Sopenharmony_ci	/* finish initializing other chipset specific controls
185262306a36Sopenharmony_ci	 */
185362306a36Sopenharmony_ci	if (chip->version != 0x1868) {
185462306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_opt_speaker,
185562306a36Sopenharmony_ci						     chip));
185662306a36Sopenharmony_ci		if (err < 0)
185762306a36Sopenharmony_ci			return err;
185862306a36Sopenharmony_ci	}
185962306a36Sopenharmony_ci	if (chip->version == 0x1869) {
186062306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_1869); idx++) {
186162306a36Sopenharmony_ci			err = snd_ctl_add(card,
186262306a36Sopenharmony_ci					  snd_ctl_new1(&snd_es18xx_opt_1869[idx],
186362306a36Sopenharmony_ci						       chip));
186462306a36Sopenharmony_ci			if (err < 0)
186562306a36Sopenharmony_ci				return err;
186662306a36Sopenharmony_ci		}
186762306a36Sopenharmony_ci	} else if (chip->version == 0x1878) {
186862306a36Sopenharmony_ci		err = snd_ctl_add(card, snd_ctl_new1(&snd_es18xx_opt_1878,
186962306a36Sopenharmony_ci						     chip));
187062306a36Sopenharmony_ci		if (err < 0)
187162306a36Sopenharmony_ci			return err;
187262306a36Sopenharmony_ci	} else if (chip->version == 0x1879) {
187362306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_1879); idx++) {
187462306a36Sopenharmony_ci			err = snd_ctl_add(card,
187562306a36Sopenharmony_ci					  snd_ctl_new1(&snd_es18xx_opt_1879[idx],
187662306a36Sopenharmony_ci						       chip));
187762306a36Sopenharmony_ci			if (err < 0)
187862306a36Sopenharmony_ci				return err;
187962306a36Sopenharmony_ci		}
188062306a36Sopenharmony_ci	}
188162306a36Sopenharmony_ci	if (chip->caps & ES18XX_GPO_2BIT) {
188262306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_gpo_2bit); idx++) {
188362306a36Sopenharmony_ci			err = snd_ctl_add(card,
188462306a36Sopenharmony_ci					  snd_ctl_new1(&snd_es18xx_opt_gpo_2bit[idx],
188562306a36Sopenharmony_ci						       chip));
188662306a36Sopenharmony_ci			if (err < 0)
188762306a36Sopenharmony_ci				return err;
188862306a36Sopenharmony_ci		}
188962306a36Sopenharmony_ci	}
189062306a36Sopenharmony_ci	return 0;
189162306a36Sopenharmony_ci}
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci/* Card level */
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ciMODULE_AUTHOR("Christian Fischbach <fishbach@pool.informatik.rwth-aachen.de>, Abramo Bagnara <abramo@alsa-project.org>");
189762306a36Sopenharmony_ciMODULE_DESCRIPTION("ESS ES18xx AudioDrive");
189862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
190162306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
190262306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
190362306a36Sopenharmony_ci#ifdef CONFIG_PNP
190462306a36Sopenharmony_cistatic bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP;
190562306a36Sopenharmony_ci#endif
190662306a36Sopenharmony_cistatic long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;	/* 0x220,0x240,0x260,0x280 */
190762306a36Sopenharmony_ci#ifndef CONFIG_PNP
190862306a36Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
190962306a36Sopenharmony_ci#else
191062306a36Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
191162306a36Sopenharmony_ci#endif
191262306a36Sopenharmony_cistatic long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
191362306a36Sopenharmony_cistatic int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5,7,9,10 */
191462306a36Sopenharmony_cistatic int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
191562306a36Sopenharmony_cistatic int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0,1,3 */
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
191862306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ES18xx soundcard.");
191962306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
192062306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ES18xx soundcard.");
192162306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
192262306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable ES18xx soundcard.");
192362306a36Sopenharmony_ci#ifdef CONFIG_PNP
192462306a36Sopenharmony_cimodule_param_array(isapnp, bool, NULL, 0444);
192562306a36Sopenharmony_ciMODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
192662306a36Sopenharmony_ci#endif
192762306a36Sopenharmony_cimodule_param_hw_array(port, long, ioport, NULL, 0444);
192862306a36Sopenharmony_ciMODULE_PARM_DESC(port, "Port # for ES18xx driver.");
192962306a36Sopenharmony_cimodule_param_hw_array(mpu_port, long, ioport, NULL, 0444);
193062306a36Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port # for ES18xx driver.");
193162306a36Sopenharmony_cimodule_param_hw_array(fm_port, long, ioport, NULL, 0444);
193262306a36Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM port # for ES18xx driver.");
193362306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0444);
193462306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ # for ES18xx driver.");
193562306a36Sopenharmony_cimodule_param_hw_array(dma1, int, dma, NULL, 0444);
193662306a36Sopenharmony_ciMODULE_PARM_DESC(dma1, "DMA 1 # for ES18xx driver.");
193762306a36Sopenharmony_cimodule_param_hw_array(dma2, int, dma, NULL, 0444);
193862306a36Sopenharmony_ciMODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver.");
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci#ifdef CONFIG_PNP
194162306a36Sopenharmony_cistatic int isa_registered;
194262306a36Sopenharmony_cistatic int pnp_registered;
194362306a36Sopenharmony_cistatic int pnpc_registered;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_cistatic const struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
194662306a36Sopenharmony_ci	{ .id = "ESS1869" },
194762306a36Sopenharmony_ci	{ .id = "ESS1879" },
194862306a36Sopenharmony_ci	{ .id = "" }		/* end */
194962306a36Sopenharmony_ci};
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci/* PnP main device initialization */
195462306a36Sopenharmony_cistatic int snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev)
195562306a36Sopenharmony_ci{
195662306a36Sopenharmony_ci	if (pnp_activate_dev(pdev) < 0) {
195762306a36Sopenharmony_ci		snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
195862306a36Sopenharmony_ci		return -EBUSY;
195962306a36Sopenharmony_ci	}
196062306a36Sopenharmony_ci	/* ok. hack using Vendor-Defined Card-Level registers */
196162306a36Sopenharmony_ci	/* skip csn and logdev initialization - already done in isapnp_configure */
196262306a36Sopenharmony_ci	if (pnp_device_is_isapnp(pdev)) {
196362306a36Sopenharmony_ci		isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev));
196462306a36Sopenharmony_ci		isapnp_write_byte(0x27, pnp_irq(pdev, 0));	/* Hardware Volume IRQ Number */
196562306a36Sopenharmony_ci		if (mpu_port[dev] != SNDRV_AUTO_PORT)
196662306a36Sopenharmony_ci			isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */
196762306a36Sopenharmony_ci		isapnp_write_byte(0x72, pnp_irq(pdev, 0));	/* second IRQ */
196862306a36Sopenharmony_ci		isapnp_cfg_end();
196962306a36Sopenharmony_ci	}
197062306a36Sopenharmony_ci	port[dev] = pnp_port_start(pdev, 0);
197162306a36Sopenharmony_ci	fm_port[dev] = pnp_port_start(pdev, 1);
197262306a36Sopenharmony_ci	mpu_port[dev] = pnp_port_start(pdev, 2);
197362306a36Sopenharmony_ci	dma1[dev] = pnp_dma(pdev, 0);
197462306a36Sopenharmony_ci	dma2[dev] = pnp_dma(pdev, 1);
197562306a36Sopenharmony_ci	irq[dev] = pnp_irq(pdev, 0);
197662306a36Sopenharmony_ci	snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]);
197762306a36Sopenharmony_ci	snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
197862306a36Sopenharmony_ci	return 0;
197962306a36Sopenharmony_ci}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_cistatic int snd_audiodrive_pnp(int dev, struct snd_es18xx *chip,
198262306a36Sopenharmony_ci			      struct pnp_dev *pdev)
198362306a36Sopenharmony_ci{
198462306a36Sopenharmony_ci	chip->dev = pdev;
198562306a36Sopenharmony_ci	if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
198662306a36Sopenharmony_ci		return -EBUSY;
198762306a36Sopenharmony_ci	return 0;
198862306a36Sopenharmony_ci}
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_cistatic const struct pnp_card_device_id snd_audiodrive_pnpids[] = {
199162306a36Sopenharmony_ci	/* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */
199262306a36Sopenharmony_ci	{ .id = "ESS1868", .devs = { { "ESS1868" }, { "ESS0000" } } },
199362306a36Sopenharmony_ci	/* ESS 1868 (integrated on Maxisound Cards) */
199462306a36Sopenharmony_ci	{ .id = "ESS1868", .devs = { { "ESS8601" }, { "ESS8600" } } },
199562306a36Sopenharmony_ci	/* ESS 1868 (integrated on Maxisound Cards) */
199662306a36Sopenharmony_ci	{ .id = "ESS1868", .devs = { { "ESS8611" }, { "ESS8610" } } },
199762306a36Sopenharmony_ci	/* ESS ES1869 Plug and Play AudioDrive */
199862306a36Sopenharmony_ci	{ .id = "ESS0003", .devs = { { "ESS1869" }, { "ESS0006" } } },
199962306a36Sopenharmony_ci	/* ESS 1869 */
200062306a36Sopenharmony_ci	{ .id = "ESS1869", .devs = { { "ESS1869" }, { "ESS0006" } } },
200162306a36Sopenharmony_ci	/* ESS 1878 */
200262306a36Sopenharmony_ci	{ .id = "ESS1878", .devs = { { "ESS1878" }, { "ESS0004" } } },
200362306a36Sopenharmony_ci	/* ESS 1879 */
200462306a36Sopenharmony_ci	{ .id = "ESS1879", .devs = { { "ESS1879" }, { "ESS0009" } } },
200562306a36Sopenharmony_ci	/* --- */
200662306a36Sopenharmony_ci	{ .id = "" } /* end */
200762306a36Sopenharmony_ci};
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_cistatic int snd_audiodrive_pnpc(int dev, struct snd_es18xx *chip,
201262306a36Sopenharmony_ci			       struct pnp_card_link *card,
201362306a36Sopenharmony_ci			       const struct pnp_card_device_id *id)
201462306a36Sopenharmony_ci{
201562306a36Sopenharmony_ci	chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
201662306a36Sopenharmony_ci	if (chip->dev == NULL)
201762306a36Sopenharmony_ci		return -EBUSY;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	chip->devc = pnp_request_card_device(card, id->devs[1].id, NULL);
202062306a36Sopenharmony_ci	if (chip->devc == NULL)
202162306a36Sopenharmony_ci		return -EBUSY;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	/* Control port initialization */
202462306a36Sopenharmony_ci	if (pnp_activate_dev(chip->devc) < 0) {
202562306a36Sopenharmony_ci		snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
202662306a36Sopenharmony_ci		return -EAGAIN;
202762306a36Sopenharmony_ci	}
202862306a36Sopenharmony_ci	snd_printdd("pnp: port=0x%llx\n",
202962306a36Sopenharmony_ci			(unsigned long long)pnp_port_start(chip->devc, 0));
203062306a36Sopenharmony_ci	if (snd_audiodrive_pnp_init_main(dev, chip->dev) < 0)
203162306a36Sopenharmony_ci		return -EBUSY;
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_ci	return 0;
203462306a36Sopenharmony_ci}
203562306a36Sopenharmony_ci#endif /* CONFIG_PNP */
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci#ifdef CONFIG_PNP
203862306a36Sopenharmony_ci#define is_isapnp_selected(dev)		isapnp[dev]
203962306a36Sopenharmony_ci#else
204062306a36Sopenharmony_ci#define is_isapnp_selected(dev)		0
204162306a36Sopenharmony_ci#endif
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_cistatic int snd_es18xx_card_new(struct device *pdev, int dev,
204462306a36Sopenharmony_ci			       struct snd_card **cardp)
204562306a36Sopenharmony_ci{
204662306a36Sopenharmony_ci	return snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
204762306a36Sopenharmony_ci				 sizeof(struct snd_es18xx), cardp);
204862306a36Sopenharmony_ci}
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_cistatic int snd_audiodrive_probe(struct snd_card *card, int dev)
205162306a36Sopenharmony_ci{
205262306a36Sopenharmony_ci	struct snd_es18xx *chip = card->private_data;
205362306a36Sopenharmony_ci	struct snd_opl3 *opl3;
205462306a36Sopenharmony_ci	int err;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	err = snd_es18xx_new_device(card,
205762306a36Sopenharmony_ci				    port[dev], mpu_port[dev], fm_port[dev],
205862306a36Sopenharmony_ci				    irq[dev], dma1[dev], dma2[dev]);
205962306a36Sopenharmony_ci	if (err < 0)
206062306a36Sopenharmony_ci		return err;
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	sprintf(card->driver, "ES%x", chip->version);
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	sprintf(card->shortname, "ESS AudioDrive ES%x", chip->version);
206562306a36Sopenharmony_ci	if (dma1[dev] != dma2[dev])
206662306a36Sopenharmony_ci		sprintf(card->longname, "%s at 0x%lx, irq %d, dma1 %d, dma2 %d",
206762306a36Sopenharmony_ci			card->shortname,
206862306a36Sopenharmony_ci			chip->port,
206962306a36Sopenharmony_ci			irq[dev], dma1[dev], dma2[dev]);
207062306a36Sopenharmony_ci	else
207162306a36Sopenharmony_ci		sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
207262306a36Sopenharmony_ci			card->shortname,
207362306a36Sopenharmony_ci			chip->port,
207462306a36Sopenharmony_ci			irq[dev], dma1[dev]);
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	err = snd_es18xx_pcm(card, 0);
207762306a36Sopenharmony_ci	if (err < 0)
207862306a36Sopenharmony_ci		return err;
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	err = snd_es18xx_mixer(card);
208162306a36Sopenharmony_ci	if (err < 0)
208262306a36Sopenharmony_ci		return err;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
208562306a36Sopenharmony_ci		if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
208662306a36Sopenharmony_ci				    OPL3_HW_OPL3, 0, &opl3) < 0) {
208762306a36Sopenharmony_ci			snd_printk(KERN_WARNING PFX
208862306a36Sopenharmony_ci				   "opl3 not detected at 0x%lx\n",
208962306a36Sopenharmony_ci				   fm_port[dev]);
209062306a36Sopenharmony_ci		} else {
209162306a36Sopenharmony_ci			err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
209262306a36Sopenharmony_ci			if (err < 0)
209362306a36Sopenharmony_ci				return err;
209462306a36Sopenharmony_ci		}
209562306a36Sopenharmony_ci	}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
209862306a36Sopenharmony_ci		err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
209962306a36Sopenharmony_ci					  mpu_port[dev], MPU401_INFO_IRQ_HOOK,
210062306a36Sopenharmony_ci					  -1, &chip->rmidi);
210162306a36Sopenharmony_ci		if (err < 0)
210262306a36Sopenharmony_ci			return err;
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	return snd_card_register(card);
210662306a36Sopenharmony_ci}
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_cistatic int snd_es18xx_isa_match(struct device *pdev, unsigned int dev)
210962306a36Sopenharmony_ci{
211062306a36Sopenharmony_ci	return enable[dev] && !is_isapnp_selected(dev);
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_cistatic int snd_es18xx_isa_probe1(int dev, struct device *devptr)
211462306a36Sopenharmony_ci{
211562306a36Sopenharmony_ci	struct snd_card *card;
211662306a36Sopenharmony_ci	int err;
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	err = snd_es18xx_card_new(devptr, dev, &card);
211962306a36Sopenharmony_ci	if (err < 0)
212062306a36Sopenharmony_ci		return err;
212162306a36Sopenharmony_ci	err = snd_audiodrive_probe(card, dev);
212262306a36Sopenharmony_ci	if (err < 0)
212362306a36Sopenharmony_ci		return err;
212462306a36Sopenharmony_ci	dev_set_drvdata(devptr, card);
212562306a36Sopenharmony_ci	return 0;
212662306a36Sopenharmony_ci}
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_cistatic int snd_es18xx_isa_probe(struct device *pdev, unsigned int dev)
212962306a36Sopenharmony_ci{
213062306a36Sopenharmony_ci	int err;
213162306a36Sopenharmony_ci	static const int possible_irqs[] = {5, 9, 10, 7, 11, 12, -1};
213262306a36Sopenharmony_ci	static const int possible_dmas[] = {1, 0, 3, 5, -1};
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	if (irq[dev] == SNDRV_AUTO_IRQ) {
213562306a36Sopenharmony_ci		irq[dev] = snd_legacy_find_free_irq(possible_irqs);
213662306a36Sopenharmony_ci		if (irq[dev] < 0) {
213762306a36Sopenharmony_ci			snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
213862306a36Sopenharmony_ci			return -EBUSY;
213962306a36Sopenharmony_ci		}
214062306a36Sopenharmony_ci	}
214162306a36Sopenharmony_ci	if (dma1[dev] == SNDRV_AUTO_DMA) {
214262306a36Sopenharmony_ci		dma1[dev] = snd_legacy_find_free_dma(possible_dmas);
214362306a36Sopenharmony_ci		if (dma1[dev] < 0) {
214462306a36Sopenharmony_ci			snd_printk(KERN_ERR PFX "unable to find a free DMA1\n");
214562306a36Sopenharmony_ci			return -EBUSY;
214662306a36Sopenharmony_ci		}
214762306a36Sopenharmony_ci	}
214862306a36Sopenharmony_ci	if (dma2[dev] == SNDRV_AUTO_DMA) {
214962306a36Sopenharmony_ci		dma2[dev] = snd_legacy_find_free_dma(possible_dmas);
215062306a36Sopenharmony_ci		if (dma2[dev] < 0) {
215162306a36Sopenharmony_ci			snd_printk(KERN_ERR PFX "unable to find a free DMA2\n");
215262306a36Sopenharmony_ci			return -EBUSY;
215362306a36Sopenharmony_ci		}
215462306a36Sopenharmony_ci	}
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	if (port[dev] != SNDRV_AUTO_PORT) {
215762306a36Sopenharmony_ci		return snd_es18xx_isa_probe1(dev, pdev);
215862306a36Sopenharmony_ci	} else {
215962306a36Sopenharmony_ci		static const unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280};
216062306a36Sopenharmony_ci		int i;
216162306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
216262306a36Sopenharmony_ci			port[dev] = possible_ports[i];
216362306a36Sopenharmony_ci			err = snd_es18xx_isa_probe1(dev, pdev);
216462306a36Sopenharmony_ci			if (! err)
216562306a36Sopenharmony_ci				return 0;
216662306a36Sopenharmony_ci		}
216762306a36Sopenharmony_ci		return err;
216862306a36Sopenharmony_ci	}
216962306a36Sopenharmony_ci}
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci#ifdef CONFIG_PM
217262306a36Sopenharmony_cistatic int snd_es18xx_isa_suspend(struct device *dev, unsigned int n,
217362306a36Sopenharmony_ci				  pm_message_t state)
217462306a36Sopenharmony_ci{
217562306a36Sopenharmony_ci	return snd_es18xx_suspend(dev_get_drvdata(dev), state);
217662306a36Sopenharmony_ci}
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_cistatic int snd_es18xx_isa_resume(struct device *dev, unsigned int n)
217962306a36Sopenharmony_ci{
218062306a36Sopenharmony_ci	return snd_es18xx_resume(dev_get_drvdata(dev));
218162306a36Sopenharmony_ci}
218262306a36Sopenharmony_ci#endif
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci#define DEV_NAME "es18xx"
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_cistatic struct isa_driver snd_es18xx_isa_driver = {
218762306a36Sopenharmony_ci	.match		= snd_es18xx_isa_match,
218862306a36Sopenharmony_ci	.probe		= snd_es18xx_isa_probe,
218962306a36Sopenharmony_ci#ifdef CONFIG_PM
219062306a36Sopenharmony_ci	.suspend	= snd_es18xx_isa_suspend,
219162306a36Sopenharmony_ci	.resume		= snd_es18xx_isa_resume,
219262306a36Sopenharmony_ci#endif
219362306a36Sopenharmony_ci	.driver		= {
219462306a36Sopenharmony_ci		.name	= DEV_NAME
219562306a36Sopenharmony_ci	},
219662306a36Sopenharmony_ci};
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci#ifdef CONFIG_PNP
220062306a36Sopenharmony_cistatic int snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
220162306a36Sopenharmony_ci				     const struct pnp_device_id *id)
220262306a36Sopenharmony_ci{
220362306a36Sopenharmony_ci	static int dev;
220462306a36Sopenharmony_ci	int err;
220562306a36Sopenharmony_ci	struct snd_card *card;
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	if (pnp_device_is_isapnp(pdev))
220862306a36Sopenharmony_ci		return -ENOENT;	/* we have another procedure - card */
220962306a36Sopenharmony_ci	for (; dev < SNDRV_CARDS; dev++) {
221062306a36Sopenharmony_ci		if (enable[dev] && isapnp[dev])
221162306a36Sopenharmony_ci			break;
221262306a36Sopenharmony_ci	}
221362306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
221462306a36Sopenharmony_ci		return -ENODEV;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	err = snd_es18xx_card_new(&pdev->dev, dev, &card);
221762306a36Sopenharmony_ci	if (err < 0)
221862306a36Sopenharmony_ci		return err;
221962306a36Sopenharmony_ci	err = snd_audiodrive_pnp(dev, card->private_data, pdev);
222062306a36Sopenharmony_ci	if (err < 0)
222162306a36Sopenharmony_ci		return err;
222262306a36Sopenharmony_ci	err = snd_audiodrive_probe(card, dev);
222362306a36Sopenharmony_ci	if (err < 0)
222462306a36Sopenharmony_ci		return err;
222562306a36Sopenharmony_ci	pnp_set_drvdata(pdev, card);
222662306a36Sopenharmony_ci	dev++;
222762306a36Sopenharmony_ci	return 0;
222862306a36Sopenharmony_ci}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci#ifdef CONFIG_PM
223162306a36Sopenharmony_cistatic int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
223262306a36Sopenharmony_ci{
223362306a36Sopenharmony_ci	return snd_es18xx_suspend(pnp_get_drvdata(pdev), state);
223462306a36Sopenharmony_ci}
223562306a36Sopenharmony_cistatic int snd_audiodrive_pnp_resume(struct pnp_dev *pdev)
223662306a36Sopenharmony_ci{
223762306a36Sopenharmony_ci	return snd_es18xx_resume(pnp_get_drvdata(pdev));
223862306a36Sopenharmony_ci}
223962306a36Sopenharmony_ci#endif
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_cistatic struct pnp_driver es18xx_pnp_driver = {
224262306a36Sopenharmony_ci	.name = "es18xx-pnpbios",
224362306a36Sopenharmony_ci	.id_table = snd_audiodrive_pnpbiosids,
224462306a36Sopenharmony_ci	.probe = snd_audiodrive_pnp_detect,
224562306a36Sopenharmony_ci#ifdef CONFIG_PM
224662306a36Sopenharmony_ci	.suspend = snd_audiodrive_pnp_suspend,
224762306a36Sopenharmony_ci	.resume = snd_audiodrive_pnp_resume,
224862306a36Sopenharmony_ci#endif
224962306a36Sopenharmony_ci};
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_cistatic int snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
225262306a36Sopenharmony_ci				      const struct pnp_card_device_id *pid)
225362306a36Sopenharmony_ci{
225462306a36Sopenharmony_ci	static int dev;
225562306a36Sopenharmony_ci	struct snd_card *card;
225662306a36Sopenharmony_ci	int res;
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	for ( ; dev < SNDRV_CARDS; dev++) {
225962306a36Sopenharmony_ci		if (enable[dev] && isapnp[dev])
226062306a36Sopenharmony_ci			break;
226162306a36Sopenharmony_ci	}
226262306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
226362306a36Sopenharmony_ci		return -ENODEV;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	res = snd_es18xx_card_new(&pcard->card->dev, dev, &card);
226662306a36Sopenharmony_ci	if (res < 0)
226762306a36Sopenharmony_ci		return res;
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci	res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid);
227062306a36Sopenharmony_ci	if (res < 0)
227162306a36Sopenharmony_ci		return res;
227262306a36Sopenharmony_ci	res = snd_audiodrive_probe(card, dev);
227362306a36Sopenharmony_ci	if (res < 0)
227462306a36Sopenharmony_ci		return res;
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	pnp_set_card_drvdata(pcard, card);
227762306a36Sopenharmony_ci	dev++;
227862306a36Sopenharmony_ci	return 0;
227962306a36Sopenharmony_ci}
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci#ifdef CONFIG_PM
228262306a36Sopenharmony_cistatic int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)
228362306a36Sopenharmony_ci{
228462306a36Sopenharmony_ci	return snd_es18xx_suspend(pnp_get_card_drvdata(pcard), state);
228562306a36Sopenharmony_ci}
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_cistatic int snd_audiodrive_pnpc_resume(struct pnp_card_link *pcard)
228862306a36Sopenharmony_ci{
228962306a36Sopenharmony_ci	return snd_es18xx_resume(pnp_get_card_drvdata(pcard));
229062306a36Sopenharmony_ci}
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci#endif
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_cistatic struct pnp_card_driver es18xx_pnpc_driver = {
229562306a36Sopenharmony_ci	.flags = PNP_DRIVER_RES_DISABLE,
229662306a36Sopenharmony_ci	.name = "es18xx",
229762306a36Sopenharmony_ci	.id_table = snd_audiodrive_pnpids,
229862306a36Sopenharmony_ci	.probe = snd_audiodrive_pnpc_detect,
229962306a36Sopenharmony_ci#ifdef CONFIG_PM
230062306a36Sopenharmony_ci	.suspend	= snd_audiodrive_pnpc_suspend,
230162306a36Sopenharmony_ci	.resume		= snd_audiodrive_pnpc_resume,
230262306a36Sopenharmony_ci#endif
230362306a36Sopenharmony_ci};
230462306a36Sopenharmony_ci#endif /* CONFIG_PNP */
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_cistatic int __init alsa_card_es18xx_init(void)
230762306a36Sopenharmony_ci{
230862306a36Sopenharmony_ci	int err;
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	err = isa_register_driver(&snd_es18xx_isa_driver, SNDRV_CARDS);
231162306a36Sopenharmony_ci#ifdef CONFIG_PNP
231262306a36Sopenharmony_ci	if (!err)
231362306a36Sopenharmony_ci		isa_registered = 1;
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	err = pnp_register_driver(&es18xx_pnp_driver);
231662306a36Sopenharmony_ci	if (!err)
231762306a36Sopenharmony_ci		pnp_registered = 1;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci	err = pnp_register_card_driver(&es18xx_pnpc_driver);
232062306a36Sopenharmony_ci	if (!err)
232162306a36Sopenharmony_ci		pnpc_registered = 1;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	if (isa_registered || pnp_registered)
232462306a36Sopenharmony_ci		err = 0;
232562306a36Sopenharmony_ci#endif
232662306a36Sopenharmony_ci	return err;
232762306a36Sopenharmony_ci}
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_cistatic void __exit alsa_card_es18xx_exit(void)
233062306a36Sopenharmony_ci{
233162306a36Sopenharmony_ci#ifdef CONFIG_PNP
233262306a36Sopenharmony_ci	if (pnpc_registered)
233362306a36Sopenharmony_ci		pnp_unregister_card_driver(&es18xx_pnpc_driver);
233462306a36Sopenharmony_ci	if (pnp_registered)
233562306a36Sopenharmony_ci		pnp_unregister_driver(&es18xx_pnp_driver);
233662306a36Sopenharmony_ci	if (isa_registered)
233762306a36Sopenharmony_ci#endif
233862306a36Sopenharmony_ci		isa_unregister_driver(&snd_es18xx_isa_driver);
233962306a36Sopenharmony_ci}
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_cimodule_init(alsa_card_es18xx_init)
234262306a36Sopenharmony_cimodule_exit(alsa_card_es18xx_exit)
2343