[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/Makefile b/src/kernel/linux/v4.14/sound/pci/emu10k1/Makefile
new file mode 100644
index 0000000..17d5527
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/Makefile
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
+#
+
+snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
+		    irq.o memory.o voice.o emumpu401.o emupcm.o io.o \
+		    emumixer.o emufx.o timer.o p16v.o
+snd-emu10k1-$(CONFIG_SND_PROC_FS) += emuproc.o
+snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
+snd-emu10k1x-objs := emu10k1x.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
+obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emu10k1-synth.o
+obj-$(CONFIG_SND_EMU10K1X) += snd-emu10k1x.o
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1.c
new file mode 100644
index 0000000..d3203df
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1.c
@@ -0,0 +1,283 @@
+/*
+ *  The driver for the EMU10K1 (SB Live!) based soundcards
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *      Added support for Audigy 2 Value.
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ * 
+ */
+
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+MODULE_DESCRIPTION("EMU10K1");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS},"
+	       "{Creative Labs,SB Audigy}}");
+
+#if IS_ENABLED(CONFIG_SND_SEQUENCER)
+#define ENABLE_SYNTH
+#include <sound/emu10k1_synth.h>
+#endif
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
+static int extin[SNDRV_CARDS];
+static int extout[SNDRV_CARDS];
+static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
+static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
+static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
+static bool enable_ir[SNDRV_CARDS];
+static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
+static uint delay_pcm_irq[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the EMU10K1 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the EMU10K1 soundcard.");
+module_param_array(extin, int, NULL, 0444);
+MODULE_PARM_DESC(extin, "Available external inputs for FX8010. Zero=default.");
+module_param_array(extout, int, NULL, 0444);
+MODULE_PARM_DESC(extout, "Available external outputs for FX8010. Zero=default.");
+module_param_array(seq_ports, int, NULL, 0444);
+MODULE_PARM_DESC(seq_ports, "Allocated sequencer ports for internal synthesizer.");
+module_param_array(max_synth_voices, int, NULL, 0444);
+MODULE_PARM_DESC(max_synth_voices, "Maximum number of voices for WaveTable.");
+module_param_array(max_buffer_size, int, NULL, 0444);
+MODULE_PARM_DESC(max_buffer_size, "Maximum sample buffer size in MB.");
+module_param_array(enable_ir, bool, NULL, 0444);
+MODULE_PARM_DESC(enable_ir, "Enable IR.");
+module_param_array(subsystem, uint, NULL, 0444);
+MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
+module_param_array(delay_pcm_irq, uint, NULL, 0444);
+MODULE_PARM_DESC(delay_pcm_irq, "Delay PCM interrupt by specified number of samples (default 0).");
+/*
+ * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value  Model:SB0400
+ */
+static const struct pci_device_id snd_emu10k1_ids[] = {
+	{ PCI_VDEVICE(CREATIVE, 0x0002), 0 },	/* EMU10K1 */
+	{ PCI_VDEVICE(CREATIVE, 0x0004), 1 },	/* Audigy */
+	{ PCI_VDEVICE(CREATIVE, 0x0008), 1 },	/* Audigy 2 Value SB0400 */
+	{ 0, }
+};
+
+/*
+ * Audigy 2 Value notes:
+ * A_IOCFG Input (GPIO)
+ * 0x400  = Front analog jack plugged in. (Green socket)
+ * 0x1000 = Read analog jack plugged in. (Black socket)
+ * 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
+ * A_IOCFG Output (GPIO)
+ * 0x60 = Sound out of front Left.
+ * Win sets it to 0xXX61
+ */
+
+MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids);
+
+static int snd_card_emu10k1_probe(struct pci_dev *pci,
+				  const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct snd_emu10k1 *emu;
+#ifdef ENABLE_SYNTH
+	struct snd_seq_device *wave = NULL;
+#endif
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+        	return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+			   0, &card);
+	if (err < 0)
+		return err;
+	if (max_buffer_size[dev] < 32)
+		max_buffer_size[dev] = 32;
+	else if (max_buffer_size[dev] > 1024)
+		max_buffer_size[dev] = 1024;
+	if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
+				      (long)max_buffer_size[dev] * 1024 * 1024,
+				      enable_ir[dev], subsystem[dev],
+				      &emu)) < 0)
+		goto error;
+	card->private_data = emu;
+	emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f;
+	if ((err = snd_emu10k1_pcm(emu, 0)) < 0)
+		goto error;
+	if ((err = snd_emu10k1_pcm_mic(emu, 1)) < 0)
+		goto error;
+	if ((err = snd_emu10k1_pcm_efx(emu, 2)) < 0)
+		goto error;
+	/* This stores the periods table. */
+	if (emu->card_capabilities->ca0151_chip) { /* P16V */	
+		if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+					       1024, &emu->p16v_buffer)) < 0)
+			goto error;
+	}
+
+	if ((err = snd_emu10k1_mixer(emu, 0, 3)) < 0)
+		goto error;
+	
+	if ((err = snd_emu10k1_timer(emu, 0)) < 0)
+		goto error;
+
+	if ((err = snd_emu10k1_pcm_multi(emu, 3)) < 0)
+		goto error;
+	if (emu->card_capabilities->ca0151_chip) { /* P16V */
+		if ((err = snd_p16v_pcm(emu, 4)) < 0)
+			goto error;
+	}
+	if (emu->audigy) {
+		if ((err = snd_emu10k1_audigy_midi(emu)) < 0)
+			goto error;
+	} else {
+		if ((err = snd_emu10k1_midi(emu)) < 0)
+			goto error;
+	}
+	if ((err = snd_emu10k1_fx8010_new(emu, 0)) < 0)
+		goto error;
+#ifdef ENABLE_SYNTH
+	if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
+			       sizeof(struct snd_emu10k1_synth_arg), &wave) < 0 ||
+	    wave == NULL) {
+		dev_warn(emu->card->dev,
+			 "can't initialize Emu10k1 wavetable synth\n");
+	} else {
+		struct snd_emu10k1_synth_arg *arg;
+		arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
+		strcpy(wave->name, "Emu-10k1 Synth");
+		arg->hwptr = emu;
+		arg->index = 1;
+		arg->seq_ports = seq_ports[dev];
+		arg->max_voices = max_synth_voices[dev];
+	}
+#endif
+ 
+	strlcpy(card->driver, emu->card_capabilities->driver,
+		sizeof(card->driver));
+	strlcpy(card->shortname, emu->card_capabilities->name,
+		sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s (rev.%d, serial:0x%x) at 0x%lx, irq %i",
+		 card->shortname, emu->revision, emu->serial, emu->port, emu->irq);
+
+	if ((err = snd_card_register(card)) < 0)
+		goto error;
+
+	if (emu->card_capabilities->emu_model)
+		schedule_delayed_work(&emu->emu1010.firmware_work, 0);
+
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+
+ error:
+	snd_card_free(card);
+	return err;
+}
+
+static void snd_card_emu10k1_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+}
+
+
+#ifdef CONFIG_PM_SLEEP
+static int snd_emu10k1_suspend(struct device *dev)
+{
+	struct snd_card *card = dev_get_drvdata(dev);
+	struct snd_emu10k1 *emu = card->private_data;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+	emu->suspend = 1;
+
+	cancel_delayed_work_sync(&emu->emu1010.firmware_work);
+
+	snd_pcm_suspend_all(emu->pcm);
+	snd_pcm_suspend_all(emu->pcm_mic);
+	snd_pcm_suspend_all(emu->pcm_efx);
+	snd_pcm_suspend_all(emu->pcm_multi);
+	snd_pcm_suspend_all(emu->pcm_p16v);
+
+	snd_ac97_suspend(emu->ac97);
+
+	snd_emu10k1_efx_suspend(emu);
+	snd_emu10k1_suspend_regs(emu);
+	if (emu->card_capabilities->ca0151_chip)
+		snd_p16v_suspend(emu);
+
+	snd_emu10k1_done(emu);
+	return 0;
+}
+
+static int snd_emu10k1_resume(struct device *dev)
+{
+	struct snd_card *card = dev_get_drvdata(dev);
+	struct snd_emu10k1 *emu = card->private_data;
+
+	snd_emu10k1_resume_init(emu);
+	snd_emu10k1_efx_resume(emu);
+	snd_ac97_resume(emu->ac97);
+	snd_emu10k1_resume_regs(emu);
+
+	if (emu->card_capabilities->ca0151_chip)
+		snd_p16v_resume(emu);
+
+	emu->suspend = 0;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+	if (emu->card_capabilities->emu_model)
+		schedule_delayed_work(&emu->emu1010.firmware_work, 0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_emu10k1_pm, snd_emu10k1_suspend, snd_emu10k1_resume);
+#define SND_EMU10K1_PM_OPS	&snd_emu10k1_pm
+#else
+#define SND_EMU10K1_PM_OPS	NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct pci_driver emu10k1_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = snd_emu10k1_ids,
+	.probe = snd_card_emu10k1_probe,
+	.remove = snd_card_emu10k1_remove,
+	.driver = {
+		.pm = SND_EMU10K1_PM_OPS,
+	},
+};
+
+module_pci_driver(emu10k1_driver);
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_callback.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_callback.c
new file mode 100644
index 0000000..aa2cc27
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_callback.c
@@ -0,0 +1,550 @@
+/*
+ *  synth callback routines for Emu10k1
+ *
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/export.h>
+#include "emu10k1_synth_local.h"
+#include <sound/asoundef.h>
+
+/* voice status */
+enum {
+	V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END
+};
+
+/* Keeps track of what we are finding */
+struct best_voice {
+	unsigned int time;
+	int voice;
+};
+
+/*
+ * prototypes
+ */
+static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,
+			  struct best_voice *best, int active_only);
+static struct snd_emux_voice *get_voice(struct snd_emux *emux,
+					struct snd_emux_port *port);
+static int start_voice(struct snd_emux_voice *vp);
+static void trigger_voice(struct snd_emux_voice *vp);
+static void release_voice(struct snd_emux_voice *vp);
+static void update_voice(struct snd_emux_voice *vp, int update);
+static void terminate_voice(struct snd_emux_voice *vp);
+static void free_voice(struct snd_emux_voice *vp);
+static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
+
+/*
+ * Ensure a value is between two points
+ * macro evaluates its args more than once, so changed to upper-case.
+ */
+#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
+#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
+
+
+/*
+ * set up operators
+ */
+static const struct snd_emux_operators emu10k1_ops = {
+	.owner =	THIS_MODULE,
+	.get_voice =	get_voice,
+	.prepare =	start_voice,
+	.trigger =	trigger_voice,
+	.release =	release_voice,
+	.update =	update_voice,
+	.terminate =	terminate_voice,
+	.free_voice =	free_voice,
+	.sample_new =	snd_emu10k1_sample_new,
+	.sample_free =	snd_emu10k1_sample_free,
+};
+
+void
+snd_emu10k1_ops_setup(struct snd_emux *emux)
+{
+	emux->ops = emu10k1_ops;
+}
+
+
+/*
+ * get more voice for pcm
+ *
+ * terminate most inactive voice and give it as a pcm voice.
+ *
+ * voice_lock is already held.
+ */
+int
+snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
+{
+	struct snd_emux *emu;
+	struct snd_emux_voice *vp;
+	struct best_voice best[V_END];
+	int i;
+
+	emu = hw->synth;
+
+	lookup_voices(emu, hw, best, 1); /* no OFF voices */
+	for (i = 0; i < V_END; i++) {
+		if (best[i].voice >= 0) {
+			int ch;
+			vp = &emu->voices[best[i].voice];
+			if ((ch = vp->ch) < 0) {
+				/*
+				dev_warn(emu->card->dev,
+				       "synth_get_voice: ch < 0 (%d) ??", i);
+				*/
+				continue;
+			}
+			vp->emu->num_voices--;
+			vp->ch = -1;
+			vp->state = SNDRV_EMUX_ST_OFF;
+			return ch;
+		}
+	}
+
+	/* not found */
+	return -ENOMEM;
+}
+
+
+/*
+ * turn off the voice (not terminated)
+ */
+static void
+release_voice(struct snd_emux_voice *vp)
+{
+	int dcysusv;
+	struct snd_emu10k1 *hw;
+	
+	hw = vp->hw;
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
+	snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
+	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
+	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
+}
+
+
+/*
+ * terminate the voice
+ */
+static void
+terminate_voice(struct snd_emux_voice *vp)
+{
+	struct snd_emu10k1 *hw;
+	
+	if (snd_BUG_ON(!vp))
+		return;
+	hw = vp->hw;
+	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+	if (vp->block) {
+		struct snd_emu10k1_memblk *emem;
+		emem = (struct snd_emu10k1_memblk *)vp->block;
+		if (emem->map_locked > 0)
+			emem->map_locked--;
+	}
+}
+
+/*
+ * release the voice to system
+ */
+static void
+free_voice(struct snd_emux_voice *vp)
+{
+	struct snd_emu10k1 *hw;
+	
+	hw = vp->hw;
+	/* FIXME: emu10k1_synth is broken. */
+	/* This can get called with hw == 0 */
+	/* Problem apparent on plug, unplug then plug */
+	/* on the Audigy 2 ZS Notebook. */
+	if (hw && (vp->ch >= 0)) {
+		snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
+		snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
+		// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
+		snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);
+		snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);
+		snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
+		vp->emu->num_voices--;
+		vp->ch = -1;
+	}
+}
+
+
+/*
+ * update registers
+ */
+static void
+update_voice(struct snd_emux_voice *vp, int update)
+{
+	struct snd_emu10k1 *hw;
+	
+	hw = vp->hw;
+	if (update & SNDRV_EMUX_UPDATE_VOLUME)
+		snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);
+	if (update & SNDRV_EMUX_UPDATE_PITCH)
+		snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
+	if (update & SNDRV_EMUX_UPDATE_PAN) {
+		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);
+		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
+	}
+	if (update & SNDRV_EMUX_UPDATE_FMMOD)
+		set_fmmod(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
+		snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
+	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
+		set_fm2frq2(hw, vp);
+	if (update & SNDRV_EMUX_UPDATE_Q)
+		set_filterQ(hw, vp);
+}
+
+
+/*
+ * look up voice table - get the best voice in order of preference
+ */
+/* spinlock held! */
+static void
+lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
+	      struct best_voice *best, int active_only)
+{
+	struct snd_emux_voice *vp;
+	struct best_voice *bp;
+	int  i;
+
+	for (i = 0; i < V_END; i++) {
+		best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */
+		best[i].voice = -1;
+	}
+
+	/*
+	 * Go through them all and get a best one to use.
+	 * NOTE: could also look at volume and pick the quietest one.
+	 */
+	for (i = 0; i < emu->max_voices; i++) {
+		int state, val;
+
+		vp = &emu->voices[i];
+		state = vp->state;
+		if (state == SNDRV_EMUX_ST_OFF) {
+			if (vp->ch < 0) {
+				if (active_only)
+					continue;
+				bp = best + V_FREE;
+			} else
+				bp = best + V_OFF;
+		}
+		else if (state == SNDRV_EMUX_ST_RELEASED ||
+			 state == SNDRV_EMUX_ST_PENDING) {
+			bp = best + V_RELEASED;
+#if 1
+			val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);
+			if (! val)
+				bp = best + V_OFF;
+#endif
+		}
+		else if (state == SNDRV_EMUX_ST_STANDBY)
+			continue;
+		else if (state & SNDRV_EMUX_ST_ON)
+			bp = best + V_PLAYING;
+		else
+			continue;
+
+		/* check if sample is finished playing (non-looping only) */
+		if (bp != best + V_OFF && bp != best + V_FREE &&
+		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
+			val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
+			if (val >= vp->reg.loopstart)
+				bp = best + V_OFF;
+		}
+
+		if (vp->time < bp->time) {
+			bp->time = vp->time;
+			bp->voice = i;
+		}
+	}
+}
+
+/*
+ * get an empty voice
+ *
+ * emu->voice_lock is already held.
+ */
+static struct snd_emux_voice *
+get_voice(struct snd_emux *emu, struct snd_emux_port *port)
+{
+	struct snd_emu10k1 *hw;
+	struct snd_emux_voice *vp;
+	struct best_voice best[V_END];
+	int i;
+
+	hw = emu->hw;
+
+	lookup_voices(emu, hw, best, 0);
+	for (i = 0; i < V_END; i++) {
+		if (best[i].voice >= 0) {
+			vp = &emu->voices[best[i].voice];
+			if (vp->ch < 0) {
+				/* allocate a voice */
+				struct snd_emu10k1_voice *hwvoice;
+				if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)
+					continue;
+				vp->ch = hwvoice->number;
+				emu->num_voices++;
+			}
+			return vp;
+		}
+	}
+
+	/* not found */
+	return NULL;
+}
+
+/*
+ * prepare envelopes and LFOs
+ */
+static int
+start_voice(struct snd_emux_voice *vp)
+{
+	unsigned int temp;
+	int ch;
+	unsigned int addr, mapped_offset;
+	struct snd_midi_channel *chan;
+	struct snd_emu10k1 *hw;
+	struct snd_emu10k1_memblk *emem;
+	
+	hw = vp->hw;
+	ch = vp->ch;
+	if (snd_BUG_ON(ch < 0))
+		return -EINVAL;
+	chan = vp->chan;
+
+	emem = (struct snd_emu10k1_memblk *)vp->block;
+	if (emem == NULL)
+		return -EINVAL;
+	emem->map_locked++;
+	if (snd_emu10k1_memblk_map(hw, emem) < 0) {
+		/* dev_err(hw->card->devK, "emu: cannot map!\n"); */
+		return -ENOMEM;
+	}
+	mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
+	vp->reg.start += mapped_offset;
+	vp->reg.end += mapped_offset;
+	vp->reg.loopstart += mapped_offset;
+	vp->reg.loopend += mapped_offset;
+
+	/* set channel routing */
+	/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */
+	if (hw->audigy) {
+		temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | 
+			(FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);
+		snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);
+	} else {
+		temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | 
+			(FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);
+		snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
+	}
+
+	/* channel to be silent and idle */
+	snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000);
+	snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
+	snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
+	snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
+	snd_emu10k1_ptr_write(hw, CPF, ch, 0);
+
+	/* set pitch offset */
+	snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
+
+	/* set envelope parameters */
+	snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);
+	snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);
+	snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);
+	snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);
+	snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);
+	/* decay/sustain parameter for volume envelope is used
+	   for triggerg the voice */
+
+	/* cutoff and volume */
+	temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;
+	snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);
+
+	/* modulation envelope heights */
+	snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);
+
+	/* lfo1/2 delay */
+	snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);
+	snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);
+
+	/* lfo1 pitch & cutoff shift */
+	set_fmmod(hw, vp);
+	/* lfo1 volume & freq */
+	snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
+	/* lfo2 pitch & freq */
+	set_fm2frq2(hw, vp);
+
+	/* reverb and loop start (reverb 8bit, MSB) */
+	temp = vp->reg.parm.reverb;
+	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	addr = vp->reg.loopstart;
+	snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);
+
+	/* chorus & loop end (chorus 8bit, MSB) */
+	addr = vp->reg.loopend;
+	temp = vp->reg.parm.chorus;
+	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
+	LIMITMAX(temp, 255);
+	temp = (temp <<24) | addr;
+	snd_emu10k1_ptr_write(hw, DSL, ch, temp);
+
+	/* clear filter delay memory */
+	snd_emu10k1_ptr_write(hw, Z1, ch, 0);
+	snd_emu10k1_ptr_write(hw, Z2, ch, 0);
+
+	/* invalidate maps */
+	temp = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
+	snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
+	snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
+#if 0
+	/* cache */
+	{
+		unsigned int val, sample;
+		val = 32;
+		if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
+			sample = 0x80808080;
+		else {
+			sample = 0;
+			val *= 2;
+		}
+
+		/* cache */
+		snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);
+		snd_emu10k1_ptr_write(hw, CDE, ch, sample);
+		snd_emu10k1_ptr_write(hw, CDF, ch, sample);
+
+		/* invalidate maps */
+		temp = ((unsigned int)hw->silent_page.addr << hw_address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
+		snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
+		snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
+		
+		/* fill cache */
+		val -= 4;
+		val <<= 25;
+		val |= 0x1c << 16;
+		snd_emu10k1_ptr_write(hw, CCR, ch, val);
+	}
+#endif
+
+	/* Q & current address (Q 4bit value, MSB) */
+	addr = vp->reg.start;
+	temp = vp->reg.parm.filterQ;
+	temp = (temp<<28) | addr;
+	if (vp->apitch < 0xe400)
+		temp |= CCCA_INTERPROM_0;
+	else {
+		unsigned int shift = (vp->apitch - 0xe000) >> 10;
+		temp |= shift << 25;
+	}
+	if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
+		temp |= CCCA_8BITSELECT;
+	snd_emu10k1_ptr_write(hw, CCCA, ch, temp);
+
+	/* reset volume */
+	temp = (unsigned int)vp->vtarget << 16;
+	snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
+	snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
+	return 0;
+}
+
+/*
+ * Start envelope
+ */
+static void
+trigger_voice(struct snd_emux_voice *vp)
+{
+	unsigned int temp, ptarget;
+	struct snd_emu10k1 *hw;
+	struct snd_emu10k1_memblk *emem;
+	
+	hw = vp->hw;
+
+	emem = (struct snd_emu10k1_memblk *)vp->block;
+	if (! emem || emem->mapped_page < 0)
+		return; /* not mapped */
+
+#if 0
+	ptarget = (unsigned int)vp->ptarget << 16;
+#else
+	ptarget = IP_TO_CP(vp->apitch);
+#endif
+	/* set pitch target and pan (volume) */
+	temp = ptarget | (vp->apan << 8) | vp->aaux;
+	snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);
+
+	/* pitch target */
+	snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);
+
+	/* trigger voice */
+	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);
+}
+
+#define MOD_SENSE 18
+
+/* set lfo1 modulation height and cutoff */
+static void
+set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+{
+	unsigned short fmmod;
+	short pitch;
+	unsigned char cutoff;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fmmod>>8);
+	cutoff = (vp->reg.parm.fmmod & 0xff);
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fmmod = ((unsigned char)pitch<<8) | cutoff;
+	snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);
+}
+
+/* set lfo2 pitch & frequency */
+static void
+set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+{
+	unsigned short fm2frq2;
+	short pitch;
+	unsigned char freq;
+	int modulation;
+
+	pitch = (char)(vp->reg.parm.fm2frq2>>8);
+	freq = vp->reg.parm.fm2frq2 & 0xff;
+	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
+	pitch += (MOD_SENSE * modulation) / 1200;
+	LIMITVALUE(pitch, -128, 127);
+	fm2frq2 = ((unsigned char)pitch<<8) | freq;
+	snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);
+}
+
+/* set filterQ */
+static void
+set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
+{
+	unsigned int val;
+	val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
+	val |= (vp->reg.parm.filterQ << 28);
+	snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
+}
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_main.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_main.c
new file mode 100644
index 0000000..ccf4415
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_main.c
@@ -0,0 +1,2142 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ *      Added support for Audigy 2 Value.
+ *  	Added EMU 1010 support.
+ *  	General bug fixes and enhancements.
+ *
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mutex.h>
+
+
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#include <linux/firmware.h>
+#include "p16v.h"
+#include "tina2.h"
+#include "p17v.h"
+
+
+#define HANA_FILENAME "emu/hana.fw"
+#define DOCK_FILENAME "emu/audio_dock.fw"
+#define EMU1010B_FILENAME "emu/emu1010b.fw"
+#define MICRO_DOCK_FILENAME "emu/micro_dock.fw"
+#define EMU0404_FILENAME "emu/emu0404.fw"
+#define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw"
+
+MODULE_FIRMWARE(HANA_FILENAME);
+MODULE_FIRMWARE(DOCK_FILENAME);
+MODULE_FIRMWARE(EMU1010B_FILENAME);
+MODULE_FIRMWARE(MICRO_DOCK_FILENAME);
+MODULE_FIRMWARE(EMU0404_FILENAME);
+MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
+
+
+/*************************************************************************
+ * EMU10K1 init / done
+ *************************************************************************/
+
+void snd_emu10k1_voice_init(struct snd_emu10k1 *emu, int ch)
+{
+	snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+	snd_emu10k1_ptr_write(emu, IP, ch, 0);
+	snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff);
+	snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff);
+	snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+	snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+	snd_emu10k1_ptr_write(emu, CCR, ch, 0);
+
+	snd_emu10k1_ptr_write(emu, PSST, ch, 0);
+	snd_emu10k1_ptr_write(emu, DSL, ch, 0x10);
+	snd_emu10k1_ptr_write(emu, CCCA, ch, 0);
+	snd_emu10k1_ptr_write(emu, Z1, ch, 0);
+	snd_emu10k1_ptr_write(emu, Z2, ch, 0);
+	snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000);
+
+	snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0);
+	snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0);
+	snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff);
+	snd_emu10k1_ptr_write(emu, PEFE, ch, 0);
+	snd_emu10k1_ptr_write(emu, FMMOD, ch, 0);
+	snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24);	/* 1 Hz */
+	snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24);	/* 1 Hz */
+	snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0);
+
+	/*** these are last so OFF prevents writing ***/
+	snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0);
+	snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0);
+	snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0);
+	snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0);
+	snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0);
+
+	/* Audigy extra stuffs */
+	if (emu->audigy) {
+		snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */
+		snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100);
+		snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f);
+		snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0);
+	}
+}
+
+static unsigned int spi_dac_init[] = {
+		0x00ff,
+		0x02ff,
+		0x0400,
+		0x0520,
+		0x0600,
+		0x08ff,
+		0x0aff,
+		0x0cff,
+		0x0eff,
+		0x10ff,
+		0x1200,
+		0x1400,
+		0x1480,
+		0x1800,
+		0x1aff,
+		0x1cff,
+		0x1e00,
+		0x0530,
+		0x0602,
+		0x0622,
+		0x1400,
+};
+
+static unsigned int i2c_adc_init[][2] = {
+	{ 0x17, 0x00 }, /* Reset */
+	{ 0x07, 0x00 }, /* Timeout */
+	{ 0x0b, 0x22 },  /* Interface control */
+	{ 0x0c, 0x22 },  /* Master mode control */
+	{ 0x0d, 0x08 },  /* Powerdown control */
+	{ 0x0e, 0xcf },  /* Attenuation Left  0x01 = -103dB, 0xff = 24dB */
+	{ 0x0f, 0xcf },  /* Attenuation Right 0.5dB steps */
+	{ 0x10, 0x7b },  /* ALC Control 1 */
+	{ 0x11, 0x00 },  /* ALC Control 2 */
+	{ 0x12, 0x32 },  /* ALC Control 3 */
+	{ 0x13, 0x00 },  /* Noise gate control */
+	{ 0x14, 0xa6 },  /* Limiter control */
+	{ 0x15, ADC_MUX_2 },  /* ADC Mixer control. Mic for A2ZS Notebook */
+};
+
+static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume)
+{
+	unsigned int silent_page;
+	int ch;
+	u32 tmp;
+
+	/* disable audio and lock cache */
+	outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
+		HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
+
+	/* reset recording buffers */
+	snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+
+	/* disable channel interrupt */
+	outl(0, emu->port + INTE);
+	snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+	if (emu->audigy) {
+		/* set SPDIF bypass mode */
+		snd_emu10k1_ptr_write(emu, SPBYPASS, 0, SPBYPASS_FORMAT);
+		/* enable rear left + rear right AC97 slots */
+		snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_REAR_RIGHT |
+				      AC97SLOT_REAR_LEFT);
+	}
+
+	/* init envelope engine */
+	for (ch = 0; ch < NUM_G; ch++)
+		snd_emu10k1_voice_init(emu, ch);
+
+	snd_emu10k1_ptr_write(emu, SPCS0, 0, emu->spdif_bits[0]);
+	snd_emu10k1_ptr_write(emu, SPCS1, 0, emu->spdif_bits[1]);
+	snd_emu10k1_ptr_write(emu, SPCS2, 0, emu->spdif_bits[2]);
+
+	if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
+		/* Hacks for Alice3 to work independent of haP16V driver */
+		/* Setup SRCMulti_I2S SamplingRate */
+		tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+		tmp &= 0xfffff1ff;
+		tmp |= (0x2<<9);
+		snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+
+		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+		snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14);
+		/* Setup SRCMulti Input Audio Enable */
+		/* Use 0xFFFFFFFF to enable P16V sounds. */
+		snd_emu10k1_ptr20_write(emu, SRCMULTI_ENABLE, 0, 0xFFFFFFFF);
+
+		/* Enabled Phased (8-channel) P16V playback */
+		outl(0x0201, emu->port + HCFG2);
+		/* Set playback routing. */
+		snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4);
+	}
+	if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */
+		/* Hacks for Alice3 to work independent of haP16V driver */
+		dev_info(emu->card->dev, "Audigy2 value: Special config.\n");
+		/* Setup SRCMulti_I2S SamplingRate */
+		tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+		tmp &= 0xfffff1ff;
+		tmp |= (0x2<<9);
+		snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+
+		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+		outl(0x600000, emu->port + 0x20);
+		outl(0x14, emu->port + 0x24);
+
+		/* Setup SRCMulti Input Audio Enable */
+		outl(0x7b0000, emu->port + 0x20);
+		outl(0xFF000000, emu->port + 0x24);
+
+		/* Setup SPDIF Out Audio Enable */
+		/* The Audigy 2 Value has a separate SPDIF out,
+		 * so no need for a mixer switch
+		 */
+		outl(0x7a0000, emu->port + 0x20);
+		outl(0xFF000000, emu->port + 0x24);
+		tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */
+		outl(tmp, emu->port + A_IOCFG);
+	}
+	if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */
+		int size, n;
+
+		size = ARRAY_SIZE(spi_dac_init);
+		for (n = 0; n < size; n++)
+			snd_emu10k1_spi_write(emu, spi_dac_init[n]);
+
+		snd_emu10k1_ptr20_write(emu, 0x60, 0, 0x10);
+		/* Enable GPIOs
+		 * GPIO0: Unknown
+		 * GPIO1: Speakers-enabled.
+		 * GPIO2: Unknown
+		 * GPIO3: Unknown
+		 * GPIO4: IEC958 Output on.
+		 * GPIO5: Unknown
+		 * GPIO6: Unknown
+		 * GPIO7: Unknown
+		 */
+		outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */
+	}
+	if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */
+		int size, n;
+
+		snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f);
+		tmp = inl(emu->port + A_IOCFG);
+		outl(tmp | 0x4, emu->port + A_IOCFG);  /* Set bit 2 for mic input */
+		tmp = inl(emu->port + A_IOCFG);
+		size = ARRAY_SIZE(i2c_adc_init);
+		for (n = 0; n < size; n++)
+			snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]);
+		for (n = 0; n < 4; n++) {
+			emu->i2c_capture_volume[n][0] = 0xcf;
+			emu->i2c_capture_volume[n][1] = 0xcf;
+		}
+	}
+
+
+	snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr);
+	snd_emu10k1_ptr_write(emu, TCB, 0, 0);	/* taken from original driver */
+	snd_emu10k1_ptr_write(emu, TCBS, 0, 4);	/* taken from original driver */
+
+	silent_page = (emu->silent_page.addr << emu->address_mode) | (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
+	for (ch = 0; ch < NUM_G; ch++) {
+		snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page);
+		snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page);
+	}
+
+	if (emu->card_capabilities->emu_model) {
+		outl(HCFG_AUTOMUTE_ASYNC |
+			HCFG_EMU32_SLAVE |
+			HCFG_AUDIOENABLE, emu->port + HCFG);
+	/*
+	 *  Hokay, setup HCFG
+	 *   Mute Disable Audio = 0
+	 *   Lock Tank Memory = 1
+	 *   Lock Sound Memory = 0
+	 *   Auto Mute = 1
+	 */
+	} else if (emu->audigy) {
+		if (emu->revision == 4) /* audigy2 */
+			outl(HCFG_AUDIOENABLE |
+			     HCFG_AC3ENABLE_CDSPDIF |
+			     HCFG_AC3ENABLE_GPSPDIF |
+			     HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+		else
+			outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+	/* FIXME: Remove all these emu->model and replace it with a card recognition parameter,
+	 * e.g. card_capabilities->joystick */
+	} else if (emu->model == 0x20 ||
+	    emu->model == 0xc400 ||
+	    (emu->model == 0x21 && emu->revision < 6))
+		outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG);
+	else
+		/* With on-chip joystick */
+		outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG);
+
+	if (enable_ir) {	/* enable IR for SB Live */
+		if (emu->card_capabilities->emu_model) {
+			;  /* Disable all access to A_IOCFG for the emu1010 */
+		} else if (emu->card_capabilities->i2c_adc) {
+			;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
+		} else if (emu->audigy) {
+			unsigned int reg = inl(emu->port + A_IOCFG);
+			outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+			udelay(500);
+			outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG);
+			udelay(100);
+			outl(reg, emu->port + A_IOCFG);
+		} else {
+			unsigned int reg = inl(emu->port + HCFG);
+			outl(reg | HCFG_GPOUT2, emu->port + HCFG);
+			udelay(500);
+			outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG);
+			udelay(100);
+			outl(reg, emu->port + HCFG);
+		}
+	}
+
+	if (emu->card_capabilities->emu_model) {
+		;  /* Disable all access to A_IOCFG for the emu1010 */
+	} else if (emu->card_capabilities->i2c_adc) {
+		;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
+	} else if (emu->audigy) {	/* enable analog output */
+		unsigned int reg = inl(emu->port + A_IOCFG);
+		outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG);
+	}
+
+	if (emu->address_mode == 0) {
+		/* use 16M in 4G */
+		outl(inl(emu->port + HCFG) | HCFG_EXPANDED_MEM, emu->port + HCFG);
+	}
+
+	return 0;
+}
+
+static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu)
+{
+	/*
+	 *  Enable the audio bit
+	 */
+	outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG);
+
+	/* Enable analog/digital outs on audigy */
+	if (emu->card_capabilities->emu_model) {
+		;  /* Disable all access to A_IOCFG for the emu1010 */
+	} else if (emu->card_capabilities->i2c_adc) {
+		;  /* Disable A_IOCFG for Audigy 2 ZS Notebook */
+	} else if (emu->audigy) {
+		outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG);
+
+		if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
+			/* Unmute Analog now.  Set GPO6 to 1 for Apollo.
+			 * This has to be done after init ALice3 I2SOut beyond 48KHz.
+			 * So, sequence is important. */
+			outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG);
+		} else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */
+			/* Unmute Analog now. */
+			outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG);
+		} else {
+			/* Disable routing from AC97 line out to Front speakers */
+			outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG);
+		}
+	}
+
+#if 0
+	{
+	unsigned int tmp;
+	/* FIXME: the following routine disables LiveDrive-II !! */
+	/* TOSLink detection */
+	emu->tos_link = 0;
+	tmp = inl(emu->port + HCFG);
+	if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) {
+		outl(tmp|0x800, emu->port + HCFG);
+		udelay(50);
+		if (tmp != (inl(emu->port + HCFG) & ~0x800)) {
+			emu->tos_link = 1;
+			outl(tmp, emu->port + HCFG);
+		}
+	}
+	}
+#endif
+
+	snd_emu10k1_intr_enable(emu, INTE_PCIERRORENABLE);
+}
+
+int snd_emu10k1_done(struct snd_emu10k1 *emu)
+{
+	int ch;
+
+	outl(0, emu->port + INTE);
+
+	/*
+	 *  Shutdown the chip
+	 */
+	for (ch = 0; ch < NUM_G; ch++)
+		snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0);
+	for (ch = 0; ch < NUM_G; ch++) {
+		snd_emu10k1_ptr_write(emu, VTFT, ch, 0);
+		snd_emu10k1_ptr_write(emu, CVCF, ch, 0);
+		snd_emu10k1_ptr_write(emu, PTRX, ch, 0);
+		snd_emu10k1_ptr_write(emu, CPF, ch, 0);
+	}
+
+	/* reset recording buffers */
+	snd_emu10k1_ptr_write(emu, MICBS, 0, 0);
+	snd_emu10k1_ptr_write(emu, MICBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXBS, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+	snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE);
+	snd_emu10k1_ptr_write(emu, ADCBA, 0, 0);
+	snd_emu10k1_ptr_write(emu, TCBS, 0, TCBS_BUFFSIZE_16K);
+	snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, EMU10K1_DBG_SINGLE_STEP);
+
+	/* disable channel interrupt */
+	snd_emu10k1_ptr_write(emu, CLIEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, CLIEH, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEL, 0, 0);
+	snd_emu10k1_ptr_write(emu, SOLEH, 0, 0);
+
+	/* disable audio and lock cache */
+	outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG);
+	snd_emu10k1_ptr_write(emu, PTB, 0, 0);
+
+	return 0;
+}
+
+/*************************************************************************
+ * ECARD functional implementation
+ *************************************************************************/
+
+/* In A1 Silicon, these bits are in the HC register */
+#define HOOKN_BIT		(1L << 12)
+#define HANDN_BIT		(1L << 11)
+#define PULSEN_BIT		(1L << 10)
+
+#define EC_GDI1			(1 << 13)
+#define EC_GDI0			(1 << 14)
+
+#define EC_NUM_CONTROL_BITS	20
+
+#define EC_AC3_DATA_SELN	0x0001L
+#define EC_EE_DATA_SEL		0x0002L
+#define EC_EE_CNTRL_SELN	0x0004L
+#define EC_EECLK		0x0008L
+#define EC_EECS			0x0010L
+#define EC_EESDO		0x0020L
+#define EC_TRIM_CSN		0x0040L
+#define EC_TRIM_SCLK		0x0080L
+#define EC_TRIM_SDATA		0x0100L
+#define EC_TRIM_MUTEN		0x0200L
+#define EC_ADCCAL		0x0400L
+#define EC_ADCRSTN		0x0800L
+#define EC_DACCAL		0x1000L
+#define EC_DACMUTEN		0x2000L
+#define EC_LEDN			0x4000L
+
+#define EC_SPDIF0_SEL_SHIFT	15
+#define EC_SPDIF1_SEL_SHIFT	17
+#define EC_SPDIF0_SEL_MASK	(0x3L << EC_SPDIF0_SEL_SHIFT)
+#define EC_SPDIF1_SEL_MASK	(0x7L << EC_SPDIF1_SEL_SHIFT)
+#define EC_SPDIF0_SELECT(_x)	(((_x) << EC_SPDIF0_SEL_SHIFT) & EC_SPDIF0_SEL_MASK)
+#define EC_SPDIF1_SELECT(_x)	(((_x) << EC_SPDIF1_SEL_SHIFT) & EC_SPDIF1_SEL_MASK)
+#define EC_CURRENT_PROM_VERSION 0x01	/* Self-explanatory.  This should
+					 * be incremented any time the EEPROM's
+					 * format is changed.  */
+
+#define EC_EEPROM_SIZE		0x40	/* ECARD EEPROM has 64 16-bit words */
+
+/* Addresses for special values stored in to EEPROM */
+#define EC_PROM_VERSION_ADDR	0x20	/* Address of the current prom version */
+#define EC_BOARDREV0_ADDR	0x21	/* LSW of board rev */
+#define EC_BOARDREV1_ADDR	0x22	/* MSW of board rev */
+
+#define EC_LAST_PROMFILE_ADDR	0x2f
+
+#define EC_SERIALNUM_ADDR	0x30	/* First word of serial number.  The
+					 * can be up to 30 characters in length
+					 * and is stored as a NULL-terminated
+					 * ASCII string.  Any unused bytes must be
+					 * filled with zeros */
+#define EC_CHECKSUM_ADDR	0x3f	/* Location at which checksum is stored */
+
+
+/* Most of this stuff is pretty self-evident.  According to the hardware
+ * dudes, we need to leave the ADCCAL bit low in order to avoid a DC
+ * offset problem.  Weird.
+ */
+#define EC_RAW_RUN_MODE		(EC_DACMUTEN | EC_ADCRSTN | EC_TRIM_MUTEN | \
+				 EC_TRIM_CSN)
+
+
+#define EC_DEFAULT_ADC_GAIN	0xC4C4
+#define EC_DEFAULT_SPDIF0_SEL	0x0
+#define EC_DEFAULT_SPDIF1_SEL	0x4
+
+/**************************************************************************
+ * @func Clock bits into the Ecard's control latch.  The Ecard uses a
+ *  control latch will is loaded bit-serially by toggling the Modem control
+ *  lines from function 2 on the E8010.  This function hides these details
+ *  and presents the illusion that we are actually writing to a distinct
+ *  register.
+ */
+
+static void snd_emu10k1_ecard_write(struct snd_emu10k1 *emu, unsigned int value)
+{
+	unsigned short count;
+	unsigned int data;
+	unsigned long hc_port;
+	unsigned int hc_value;
+
+	hc_port = emu->port + HCFG;
+	hc_value = inl(hc_port) & ~(HOOKN_BIT | HANDN_BIT | PULSEN_BIT);
+	outl(hc_value, hc_port);
+
+	for (count = 0; count < EC_NUM_CONTROL_BITS; count++) {
+
+		/* Set up the value */
+		data = ((value & 0x1) ? PULSEN_BIT : 0);
+		value >>= 1;
+
+		outl(hc_value | data, hc_port);
+
+		/* Clock the shift register */
+		outl(hc_value | data | HANDN_BIT, hc_port);
+		outl(hc_value | data, hc_port);
+	}
+
+	/* Latch the bits */
+	outl(hc_value | HOOKN_BIT, hc_port);
+	outl(hc_value, hc_port);
+}
+
+/**************************************************************************
+ * @func Set the gain of the ECARD's CS3310 Trim/gain controller.  The
+ * trim value consists of a 16bit value which is composed of two
+ * 8 bit gain/trim values, one for the left channel and one for the
+ * right channel.  The following table maps from the Gain/Attenuation
+ * value in decibels into the corresponding bit pattern for a single
+ * channel.
+ */
+
+static void snd_emu10k1_ecard_setadcgain(struct snd_emu10k1 *emu,
+					 unsigned short gain)
+{
+	unsigned int bit;
+
+	/* Enable writing to the TRIM registers */
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+	/* Do it again to insure that we meet hold time requirements */
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl & ~EC_TRIM_CSN);
+
+	for (bit = (1 << 15); bit; bit >>= 1) {
+		unsigned int value;
+
+		value = emu->ecard_ctrl & ~(EC_TRIM_CSN | EC_TRIM_SDATA);
+
+		if (gain & bit)
+			value |= EC_TRIM_SDATA;
+
+		/* Clock the bit */
+		snd_emu10k1_ecard_write(emu, value);
+		snd_emu10k1_ecard_write(emu, value | EC_TRIM_SCLK);
+		snd_emu10k1_ecard_write(emu, value);
+	}
+
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+}
+
+static int snd_emu10k1_ecard_init(struct snd_emu10k1 *emu)
+{
+	unsigned int hc_value;
+
+	/* Set up the initial settings */
+	emu->ecard_ctrl = EC_RAW_RUN_MODE |
+			  EC_SPDIF0_SELECT(EC_DEFAULT_SPDIF0_SEL) |
+			  EC_SPDIF1_SELECT(EC_DEFAULT_SPDIF1_SEL);
+
+	/* Step 0: Set the codec type in the hardware control register
+	 * and enable audio output */
+	hc_value = inl(emu->port + HCFG);
+	outl(hc_value | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S, emu->port + HCFG);
+	inl(emu->port + HCFG);
+
+	/* Step 1: Turn off the led and deassert TRIM_CS */
+	snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 2: Calibrate the ADC and DAC */
+	snd_emu10k1_ecard_write(emu, EC_DACCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 3: Wait for awhile;   XXX We can't get away with this
+	 * under a real operating system; we'll need to block and wait that
+	 * way. */
+	snd_emu10k1_wait(emu, 48000);
+
+	/* Step 4: Switch off the DAC and ADC calibration.  Note
+	 * That ADC_CAL is actually an inverted signal, so we assert
+	 * it here to stop calibration.  */
+	snd_emu10k1_ecard_write(emu, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN);
+
+	/* Step 4: Switch into run mode */
+	snd_emu10k1_ecard_write(emu, emu->ecard_ctrl);
+
+	/* Step 5: Set the analog input gain */
+	snd_emu10k1_ecard_setadcgain(emu, EC_DEFAULT_ADC_GAIN);
+
+	return 0;
+}
+
+static int snd_emu10k1_cardbus_init(struct snd_emu10k1 *emu)
+{
+	unsigned long special_port;
+	unsigned int value;
+
+	/* Special initialisation routine
+	 * before the rest of the IO-Ports become active.
+	 */
+	special_port = emu->port + 0x38;
+	value = inl(special_port);
+	outl(0x00d00000, special_port);
+	value = inl(special_port);
+	outl(0x00d00001, special_port);
+	value = inl(special_port);
+	outl(0x00d0005f, special_port);
+	value = inl(special_port);
+	outl(0x00d0007f, special_port);
+	value = inl(special_port);
+	outl(0x0090007f, special_port);
+	value = inl(special_port);
+
+	snd_emu10k1_ptr20_write(emu, TINA2_VOLUME, 0, 0xfefefefe); /* Defaults to 0x30303030 */
+	/* Delay to give time for ADC chip to switch on. It needs 113ms */
+	msleep(200);
+	return 0;
+}
+
+static int snd_emu1010_load_firmware_entry(struct snd_emu10k1 *emu,
+				     const struct firmware *fw_entry)
+{
+	int n, i;
+	int reg;
+	int value;
+	unsigned int write_post;
+	unsigned long flags;
+
+	if (!fw_entry)
+		return -EIO;
+
+	/* The FPGA is a Xilinx Spartan IIE XC2S50E */
+	/* GPIO7 -> FPGA PGMN
+	 * GPIO6 -> FPGA CCLK
+	 * GPIO5 -> FPGA DIN
+	 * FPGA CONFIG OFF -> FPGA PGMN
+	 */
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(0x00, emu->port + A_IOCFG); /* Set PGMN low for 1uS. */
+	write_post = inl(emu->port + A_IOCFG);
+	udelay(100);
+	outl(0x80, emu->port + A_IOCFG); /* Leave bit 7 set during netlist setup. */
+	write_post = inl(emu->port + A_IOCFG);
+	udelay(100); /* Allow FPGA memory to clean */
+	for (n = 0; n < fw_entry->size; n++) {
+		value = fw_entry->data[n];
+		for (i = 0; i < 8; i++) {
+			reg = 0x80;
+			if (value & 0x1)
+				reg = reg | 0x20;
+			value = value >> 1;
+			outl(reg, emu->port + A_IOCFG);
+			write_post = inl(emu->port + A_IOCFG);
+			outl(reg | 0x40, emu->port + A_IOCFG);
+			write_post = inl(emu->port + A_IOCFG);
+		}
+	}
+	/* After programming, set GPIO bit 4 high again. */
+	outl(0x10, emu->port + A_IOCFG);
+	write_post = inl(emu->port + A_IOCFG);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+	return 0;
+}
+
+/* firmware file names, per model, init-fw and dock-fw (optional) */
+static const char * const firmware_names[5][2] = {
+	[EMU_MODEL_EMU1010] = {
+		HANA_FILENAME, DOCK_FILENAME
+	},
+	[EMU_MODEL_EMU1010B] = {
+		EMU1010B_FILENAME, MICRO_DOCK_FILENAME
+	},
+	[EMU_MODEL_EMU1616] = {
+		EMU1010_NOTEBOOK_FILENAME, MICRO_DOCK_FILENAME
+	},
+	[EMU_MODEL_EMU0404] = {
+		EMU0404_FILENAME, NULL
+	},
+};
+
+static int snd_emu1010_load_firmware(struct snd_emu10k1 *emu, int dock,
+				     const struct firmware **fw)
+{
+	const char *filename;
+	int err;
+
+	if (!*fw) {
+		filename = firmware_names[emu->card_capabilities->emu_model][dock];
+		if (!filename)
+			return 0;
+		err = request_firmware(fw, filename, &emu->pci->dev);
+		if (err)
+			return err;
+	}
+
+	return snd_emu1010_load_firmware_entry(emu, *fw);
+}
+
+static void emu1010_firmware_work(struct work_struct *work)
+{
+	struct snd_emu10k1 *emu;
+	u32 tmp, tmp2, reg;
+	int err;
+
+	emu = container_of(work, struct snd_emu10k1,
+			   emu1010.firmware_work.work);
+	if (emu->card->shutdown)
+		return;
+#ifdef CONFIG_PM_SLEEP
+	if (emu->suspend)
+		return;
+#endif
+	snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp); /* IRQ Status */
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg); /* OPTIONS: Which cards are attached to the EMU */
+	if (reg & EMU_HANA_OPTION_DOCK_OFFLINE) {
+		/* Audio Dock attached */
+		/* Return to Audio Dock programming mode */
+		dev_info(emu->card->dev,
+			 "emu1010: Loading Audio Dock Firmware\n");
+		snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG,
+				       EMU_HANA_FPGA_CONFIG_AUDIODOCK);
+		err = snd_emu1010_load_firmware(emu, 1, &emu->dock_fw);
+		if (err < 0)
+			goto next;
+
+		snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0);
+		snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &tmp);
+		dev_info(emu->card->dev,
+			 "emu1010: EMU_HANA+DOCK_IRQ_STATUS = 0x%x\n", tmp);
+		/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+		snd_emu1010_fpga_read(emu, EMU_HANA_ID, &tmp);
+		dev_info(emu->card->dev,
+			 "emu1010: EMU_HANA+DOCK_ID = 0x%x\n", tmp);
+		if ((tmp & 0x1f) != 0x15) {
+			/* FPGA failed to be programmed */
+			dev_info(emu->card->dev,
+				 "emu1010: Loading Audio Dock Firmware file failed, reg = 0x%x\n",
+				 tmp);
+			goto next;
+		}
+		dev_info(emu->card->dev,
+			 "emu1010: Audio Dock Firmware loaded\n");
+		snd_emu1010_fpga_read(emu, EMU_DOCK_MAJOR_REV, &tmp);
+		snd_emu1010_fpga_read(emu, EMU_DOCK_MINOR_REV, &tmp2);
+		dev_info(emu->card->dev, "Audio Dock ver: %u.%u\n", tmp, tmp2);
+		/* Sync clocking between 1010 and Dock */
+		/* Allow DLL to settle */
+		msleep(10);
+		/* Unmute all. Default is muted after a firmware load */
+		snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+	} else if (!reg && emu->emu1010.last_reg) {
+		/* Audio Dock removed */
+		dev_info(emu->card->dev, "emu1010: Audio Dock detached\n");
+		/* Unmute all */
+		snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE);
+	}
+
+ next:
+	emu->emu1010.last_reg = reg;
+	if (!emu->card->shutdown)
+		schedule_delayed_work(&emu->emu1010.firmware_work,
+				      msecs_to_jiffies(1000));
+}
+
+/*
+ * EMU-1010 - details found out from this driver, official MS Win drivers,
+ * testing the card:
+ *
+ * Audigy2 (aka Alice2):
+ * ---------------------
+ * 	* communication over PCI
+ * 	* conversion of 32-bit data coming over EMU32 links from HANA FPGA
+ *	  to 2 x 16-bit, using internal DSP instructions
+ * 	* slave mode, clock supplied by HANA
+ * 	* linked to HANA using:
+ * 		32 x 32-bit serial EMU32 output channels
+ * 		16 x EMU32 input channels
+ * 		(?) x I2S I/O channels (?)
+ *
+ * FPGA (aka HANA):
+ * ---------------
+ * 	* provides all (?) physical inputs and outputs of the card
+ * 		(ADC, DAC, SPDIF I/O, ADAT I/O, etc.)
+ * 	* provides clock signal for the card and Alice2
+ * 	* two crystals - for 44.1kHz and 48kHz multiples
+ * 	* provides internal routing of signal sources to signal destinations
+ * 	* inputs/outputs to Alice2 - see above
+ *
+ * Current status of the driver:
+ * ----------------------------
+ * 	* only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz)
+ * 	* PCM device nb. 2:
+ *		16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops
+ * 		16 x 32-bit capture - snd_emu10k1_capture_efx_ops
+ */
+static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
+{
+	unsigned int i;
+	u32 tmp, tmp2, reg;
+	int err;
+
+	dev_info(emu->card->dev, "emu1010: Special config.\n");
+	/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+	 * Lock Sound Memory Cache, Lock Tank Memory Cache,
+	 * Mute all codecs.
+	 */
+	outl(0x0005a00c, emu->port + HCFG);
+	/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+	 * Lock Tank Memory Cache,
+	 * Mute all codecs.
+	 */
+	outl(0x0005a004, emu->port + HCFG);
+	/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+	 * Mute all codecs.
+	 */
+	outl(0x0005a000, emu->port + HCFG);
+	/* AC97 2.1, Any 16Meg of 4Gig address, Auto-Mute, EMU32 Slave,
+	 * Mute all codecs.
+	 */
+	outl(0x0005a000, emu->port + HCFG);
+
+	/* Disable 48Volt power to Audio Dock */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
+
+	/* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
+	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
+	dev_dbg(emu->card->dev, "reg1 = 0x%x\n", reg);
+	if ((reg & 0x3f) == 0x15) {
+		/* FPGA netlist already present so clear it */
+		/* Return to programming mode */
+
+		snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0x02);
+	}
+	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
+	dev_dbg(emu->card->dev, "reg2 = 0x%x\n", reg);
+	if ((reg & 0x3f) == 0x15) {
+		/* FPGA failed to return to programming mode */
+		dev_info(emu->card->dev,
+			 "emu1010: FPGA failed to return to programming mode\n");
+		return -ENODEV;
+	}
+	dev_info(emu->card->dev, "emu1010: EMU_HANA_ID = 0x%x\n", reg);
+
+	err = snd_emu1010_load_firmware(emu, 0, &emu->firmware);
+	if (err < 0) {
+		dev_info(emu->card->dev, "emu1010: Loading Firmware failed\n");
+		return err;
+	}
+
+	/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg);
+	if ((reg & 0x3f) != 0x15) {
+		/* FPGA failed to be programmed */
+		dev_info(emu->card->dev,
+			 "emu1010: Loading Hana Firmware file failed, reg = 0x%x\n",
+			 reg);
+		return -ENODEV;
+	}
+
+	dev_info(emu->card->dev, "emu1010: Hana Firmware loaded\n");
+	snd_emu1010_fpga_read(emu, EMU_HANA_MAJOR_REV, &tmp);
+	snd_emu1010_fpga_read(emu, EMU_HANA_MINOR_REV, &tmp2);
+	dev_info(emu->card->dev, "emu1010: Hana version: %u.%u\n", tmp, tmp2);
+	/* Enable 48Volt power to Audio Dock */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, EMU_HANA_DOCK_PWR_ON);
+
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
+	dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
+	dev_info(emu->card->dev, "emu1010: Card options = 0x%x\n", reg);
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTICAL_TYPE, &tmp);
+	/* Optical -> ADAT I/O  */
+	/* 0 : SPDIF
+	 * 1 : ADAT
+	 */
+	emu->emu1010.optical_in = 1; /* IN_ADAT */
+	emu->emu1010.optical_out = 1; /* IN_ADAT */
+	tmp = 0;
+	tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
+		(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
+	snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+	snd_emu1010_fpga_read(emu, EMU_HANA_ADC_PADS, &tmp);
+	/* Set no attenuation on Audio Dock pads. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, 0x00);
+	emu->emu1010.adc_pads = 0x00;
+	snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
+	/* Unmute Audio dock DACs, Headphone source DAC-4. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
+	snd_emu1010_fpga_read(emu, EMU_HANA_DAC_PADS, &tmp);
+	/* DAC PADs. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, 0x0f);
+	emu->emu1010.dac_pads = 0x0f;
+	snd_emu1010_fpga_read(emu, EMU_HANA_DOCK_MISC, &tmp);
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_MISC, 0x30);
+	snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
+	/* SPDIF Format. Set Consumer mode, 24bit, copy enable */
+	snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10);
+	/* MIDI routing */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19);
+	/* Unknown. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c);
+	/* IRQ Enable: All on */
+	/* snd_emu1010_fpga_write(emu, 0x09, 0x0f ); */
+	/* IRQ Enable: All off */
+	snd_emu1010_fpga_write(emu, EMU_HANA_IRQ_ENABLE, 0x00);
+
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &reg);
+	dev_info(emu->card->dev, "emu1010: Card options3 = 0x%x\n", reg);
+	/* Default WCLK set to 48kHz. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x00);
+	/* Word Clock source, Internal 48kHz x1 */
+	snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
+	/* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
+	/* Audio Dock LEDs. */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12);
+
+#if 0
+	/* For 96kHz */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT2);
+#endif
+#if 0
+	/* For 192kHz */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_0, EMU_SRC_HAMOA_ADC_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_1, EMU_SRC_HAMOA_ADC_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_RIGHT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_4, EMU_SRC_HAMOA_ADC_LEFT3);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_5, EMU_SRC_HAMOA_ADC_RIGHT3);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_6, EMU_SRC_HAMOA_ADC_LEFT4);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_7, EMU_SRC_HAMOA_ADC_RIGHT4);
+#endif
+#if 1
+	/* For 48kHz */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_0, EMU_SRC_DOCK_MIC_A1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_1, EMU_SRC_DOCK_MIC_B1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_2, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_3, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_4, EMU_SRC_DOCK_ADC1_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_5, EMU_SRC_DOCK_ADC1_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
+	/* Pavel Hofman - setting defaults for 8 more capture channels
+	 * Defaults only, users will set their own values anyways, let's
+	 * just copy/paste.
+	 */
+
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1);
+#endif
+#if 0
+	/* Original */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_4, EMU_SRC_HANA_ADAT);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_5, EMU_SRC_HANA_ADAT + 1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_6, EMU_SRC_HANA_ADAT + 2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_7, EMU_SRC_HANA_ADAT + 3);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_8, EMU_SRC_HANA_ADAT + 4);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_9, EMU_SRC_HANA_ADAT + 5);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_A, EMU_SRC_HANA_ADAT + 6);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_B, EMU_SRC_HANA_ADAT + 7);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_MIC_A1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_MIC_B1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_E, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_F, EMU_SRC_HAMOA_ADC_LEFT2);
+#endif
+	for (i = 0; i < 0x20; i++) {
+		/* AudioDock Elink <- Silence */
+		snd_emu1010_fpga_link_dst_src_write(emu, 0x0100 + i, EMU_SRC_SILENCE);
+	}
+	for (i = 0; i < 4; i++) {
+		/* Hana SPDIF Out <- Silence */
+		snd_emu1010_fpga_link_dst_src_write(emu, 0x0200 + i, EMU_SRC_SILENCE);
+	}
+	for (i = 0; i < 7; i++) {
+		/* Hamoa DAC <- Silence */
+		snd_emu1010_fpga_link_dst_src_write(emu, 0x0300 + i, EMU_SRC_SILENCE);
+	}
+	for (i = 0; i < 7; i++) {
+		/* Hana ADAT Out <- Silence */
+		snd_emu1010_fpga_link_dst_src_write(emu, EMU_DST_HANA_ADAT + i, EMU_SRC_SILENCE);
+	}
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S0_LEFT, EMU_SRC_DOCK_ADC1_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S0_RIGHT, EMU_SRC_DOCK_ADC1_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S1_LEFT, EMU_SRC_DOCK_ADC2_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S1_RIGHT, EMU_SRC_DOCK_ADC2_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S2_LEFT, EMU_SRC_DOCK_ADC3_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE_I2S2_RIGHT, EMU_SRC_DOCK_ADC3_RIGHT1);
+	snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x01); /* Unmute all */
+
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
+
+	/* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
+	 * Lock Sound Memory Cache, Lock Tank Memory Cache,
+	 * Mute all codecs.
+	 */
+	outl(0x0000a000, emu->port + HCFG);
+	/* AC97 1.03, Any 32Meg of 2Gig address, Auto-Mute, EMU32 Slave,
+	 * Lock Sound Memory Cache, Lock Tank Memory Cache,
+	 * Un-Mute all codecs.
+	 */
+	outl(0x0000a001, emu->port + HCFG);
+
+	/* Initial boot complete. Now patches */
+
+	snd_emu1010_fpga_read(emu, EMU_HANA_OPTION_CARDS, &tmp);
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_IN, 0x19); /* MIDI Route */
+	snd_emu1010_fpga_write(emu, EMU_HANA_MIDI_OUT, 0x0c); /* Unknown */
+	snd_emu1010_fpga_read(emu, EMU_HANA_SPDIF_MODE, &tmp);
+	snd_emu1010_fpga_write(emu, EMU_HANA_SPDIF_MODE, 0x10); /* SPDIF Format spdif  (or 0x11 for aes/ebu) */
+
+#if 0
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32B + 2); /* ALICE2 bus 0xa2 */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32B + 3); /* ALICE2 bus 0xa3 */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 2); /* ALICE2 bus 0xb2 */
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 3); /* ALICE2 bus 0xb3 */
+#endif
+	/* Default outputs */
+	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+		/* 1616(M) cardbus default outputs */
+		/* ALICE2 bus 0xa0 */
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+		emu->emu1010.output_source[0] = 17;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+		emu->emu1010.output_source[1] = 18;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
+		emu->emu1010.output_source[2] = 19;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
+		emu->emu1010.output_source[3] = 20;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
+		emu->emu1010.output_source[4] = 21;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
+		emu->emu1010.output_source[5] = 22;
+		/* ALICE2 bus 0xa0 */
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_MANA_DAC_LEFT, EMU_SRC_ALICE_EMU32A + 0);
+		emu->emu1010.output_source[16] = 17;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_MANA_DAC_RIGHT, EMU_SRC_ALICE_EMU32A + 1);
+		emu->emu1010.output_source[17] = 18;
+	} else {
+		/* ALICE2 bus 0xa0 */
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC1_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+		emu->emu1010.output_source[0] = 21;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC1_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+		emu->emu1010.output_source[1] = 22;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC2_LEFT1, EMU_SRC_ALICE_EMU32A + 2);
+		emu->emu1010.output_source[2] = 23;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC2_RIGHT1, EMU_SRC_ALICE_EMU32A + 3);
+		emu->emu1010.output_source[3] = 24;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC3_LEFT1, EMU_SRC_ALICE_EMU32A + 4);
+		emu->emu1010.output_source[4] = 25;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC3_RIGHT1, EMU_SRC_ALICE_EMU32A + 5);
+		emu->emu1010.output_source[5] = 26;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC4_LEFT1, EMU_SRC_ALICE_EMU32A + 6);
+		emu->emu1010.output_source[6] = 27;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_DAC4_RIGHT1, EMU_SRC_ALICE_EMU32A + 7);
+		emu->emu1010.output_source[7] = 28;
+		/* ALICE2 bus 0xa0 */
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_PHONES_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+		emu->emu1010.output_source[8] = 21;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_PHONES_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+		emu->emu1010.output_source[9] = 22;
+		/* ALICE2 bus 0xa0 */
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+		emu->emu1010.output_source[10] = 21;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_DOCK_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+		emu->emu1010.output_source[11] = 22;
+		/* ALICE2 bus 0xa0 */
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_SPDIF_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+		emu->emu1010.output_source[12] = 21;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_SPDIF_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+		emu->emu1010.output_source[13] = 22;
+		/* ALICE2 bus 0xa0 */
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HAMOA_DAC_LEFT1, EMU_SRC_ALICE_EMU32A + 0);
+		emu->emu1010.output_source[14] = 21;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HAMOA_DAC_RIGHT1, EMU_SRC_ALICE_EMU32A + 1);
+		emu->emu1010.output_source[15] = 22;
+		/* ALICE2 bus 0xa0 */
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_ADAT, EMU_SRC_ALICE_EMU32A + 0);
+		emu->emu1010.output_source[16] = 21;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_ADAT + 1, EMU_SRC_ALICE_EMU32A + 1);
+		emu->emu1010.output_source[17] = 22;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_ADAT + 2, EMU_SRC_ALICE_EMU32A + 2);
+		emu->emu1010.output_source[18] = 23;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_ADAT + 3, EMU_SRC_ALICE_EMU32A + 3);
+		emu->emu1010.output_source[19] = 24;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_ADAT + 4, EMU_SRC_ALICE_EMU32A + 4);
+		emu->emu1010.output_source[20] = 25;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_ADAT + 5, EMU_SRC_ALICE_EMU32A + 5);
+		emu->emu1010.output_source[21] = 26;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_ADAT + 6, EMU_SRC_ALICE_EMU32A + 6);
+		emu->emu1010.output_source[22] = 27;
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			EMU_DST_HANA_ADAT + 7, EMU_SRC_ALICE_EMU32A + 7);
+		emu->emu1010.output_source[23] = 28;
+	}
+	/* TEMP: Select SPDIF in/out */
+	/* snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, 0x0); */ /* Output spdif */
+
+	/* TEMP: Select 48kHz SPDIF out */
+	snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x0); /* Mute all */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, 0x0); /* Default fallback clock 48kHz */
+	/* Word Clock source, Internal 48kHz x1 */
+	snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K);
+	/* snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK, EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_4X); */
+	emu->emu1010.internal_clock = 1; /* 48000 */
+	snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, 0x12); /* Set LEDs on Audio Dock */
+	snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, 0x1); /* Unmute all */
+	/* snd_emu1010_fpga_write(emu, 0x7, 0x0); */ /* Mute all */
+	/* snd_emu1010_fpga_write(emu, 0x7, 0x1); */ /* Unmute all */
+	/* snd_emu1010_fpga_write(emu, 0xe, 0x12); */ /* Set LEDs on Audio Dock */
+
+	return 0;
+}
+/*
+ *  Create the EMU10K1 instance
+ */
+
+#ifdef CONFIG_PM_SLEEP
+static int alloc_pm_buffer(struct snd_emu10k1 *emu);
+static void free_pm_buffer(struct snd_emu10k1 *emu);
+#endif
+
+static int snd_emu10k1_free(struct snd_emu10k1 *emu)
+{
+	if (emu->port) {	/* avoid access to already used hardware */
+		snd_emu10k1_fx8010_tram_setup(emu, 0);
+		snd_emu10k1_done(emu);
+		snd_emu10k1_free_efx(emu);
+	}
+	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1010) {
+		/* Disable 48Volt power to Audio Dock */
+		snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_PWR, 0);
+	}
+	cancel_delayed_work_sync(&emu->emu1010.firmware_work);
+	release_firmware(emu->firmware);
+	release_firmware(emu->dock_fw);
+	if (emu->irq >= 0)
+		free_irq(emu->irq, emu);
+	/* remove reserved page */
+	if (emu->reserved_page) {
+		snd_emu10k1_synth_free(emu,
+			(struct snd_util_memblk *)emu->reserved_page);
+		emu->reserved_page = NULL;
+	}
+	snd_util_memhdr_free(emu->memhdr);
+	if (emu->silent_page.area)
+		snd_dma_free_pages(&emu->silent_page);
+	if (emu->ptb_pages.area)
+		snd_dma_free_pages(&emu->ptb_pages);
+	vfree(emu->page_ptr_table);
+	vfree(emu->page_addr_table);
+#ifdef CONFIG_PM_SLEEP
+	free_pm_buffer(emu);
+#endif
+	if (emu->port)
+		pci_release_regions(emu->pci);
+	if (emu->card_capabilities->ca0151_chip) /* P16V */
+		snd_p16v_free(emu);
+	pci_disable_device(emu->pci);
+	kfree(emu);
+	return 0;
+}
+
+static int snd_emu10k1_dev_free(struct snd_device *device)
+{
+	struct snd_emu10k1 *emu = device->device_data;
+	return snd_emu10k1_free(emu);
+}
+
+static struct snd_emu_chip_details emu_chip_details[] = {
+	/* Audigy 5/Rx SB1550 */
+	/* Tested by michael@gernoth.net 28 Mar 2015 */
+	/* DSP: CA10300-IAT LF
+	 * DAC: Cirrus Logic CS4382-KQZ
+	 * ADC: Philips 1361T
+	 * AC97: Sigmatel STAC9750
+	 * CA0151: None
+	 */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10241102,
+	 .driver = "Audigy2", .name = "SB Audigy 5/Rx [SB1550]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1,
+	 .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
+	 .ac97_chip = 1},
+	/* Audigy4 (Not PRO) SB0610 */
+	/* Tested by James@superbug.co.uk 4th April 2006 */
+	/* A_IOCFG bits
+	 * Output
+	 * 0: ?
+	 * 1: ?
+	 * 2: ?
+	 * 3: 0 - Digital Out, 1 - Line in
+	 * 4: ?
+	 * 5: ?
+	 * 6: ?
+	 * 7: ?
+	 * Input
+	 * 8: ?
+	 * 9: ?
+	 * A: Green jack sense (Front)
+	 * B: ?
+	 * C: Black jack sense (Rear/Side Right)
+	 * D: Yellow jack sense (Center/LFE/Side Left)
+	 * E: ?
+	 * F: ?
+	 *
+	 * Digital Out/Line in switch using A_IOCFG bit 3 (0x08)
+	 * 0 - Digital Out
+	 * 1 - Line in
+	 */
+	/* Mic input not tested.
+	 * Analog CD input not tested
+	 * Digital Out not tested.
+	 * Line in working.
+	 * Audio output 5.1 working. Side outputs not working.
+	 */
+	/* DSP: CA10300-IAT LF
+	 * DAC: Cirrus Logic CS4382-KQZ
+	 * ADC: Philips 1361T
+	 * AC97: Sigmatel STAC9750
+	 * CA0151: None
+	 */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102,
+	 .driver = "Audigy2", .name = "SB Audigy 4 [SB0610]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1,
+	 .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
+	 .ac97_chip = 1} ,
+	/* Audigy 2 Value AC3 out does not work yet.
+	 * Need to find out how to turn off interpolators.
+	 */
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
+	/* DSP: CA0108-IAT
+	 * DAC: CS4382-KQ
+	 * ADC: Philips 1361T
+	 * AC97: STAC9750
+	 * CA0151: None
+	 */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
+	 .driver = "Audigy2", .name = "SB Audigy 2 Value [SB0400]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1,
+	 .ac97_chip = 1} ,
+	/* Audigy 2 ZS Notebook Cardbus card.*/
+	/* Tested by James@superbug.co.uk 6th November 2006 */
+	/* Audio output 7.1/Headphones working.
+	 * Digital output working. (AC3 not checked, only PCM)
+	 * Audio Mic/Line inputs working.
+	 * Digital input not tested.
+	 */
+	/* DSP: Tina2
+	 * DAC: Wolfson WM8768/WM8568
+	 * ADC: Wolfson WM8775
+	 * AC97: None
+	 * CA0151: None
+	 */
+	/* Tested by James@superbug.co.uk 4th April 2006 */
+	/* A_IOCFG bits
+	 * Output
+	 * 0: Not Used
+	 * 1: 0 = Mute all the 7.1 channel out. 1 = unmute.
+	 * 2: Analog input 0 = line in, 1 = mic in
+	 * 3: Not Used
+	 * 4: Digital output 0 = off, 1 = on.
+	 * 5: Not Used
+	 * 6: Not Used
+	 * 7: Not Used
+	 * Input
+	 *      All bits 1 (0x3fxx) means nothing plugged in.
+	 * 8-9: 0 = Line in/Mic, 2 = Optical in, 3 = Nothing.
+	 * A-B: 0 = Headphones, 2 = Optical out, 3 = Nothing.
+	 * C-D: 2 = Front/Rear/etc, 3 = nothing.
+	 * E-F: Always 0
+	 *
+	 */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x20011102,
+	 .driver = "Audigy2", .name = "Audigy 2 ZS Notebook [SB0530]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .ca_cardbus_chip = 1,
+	 .spi_dac = 1,
+	 .i2c_adc = 1,
+	 .spk71 = 1} ,
+	/* Tested by James@superbug.co.uk 4th Nov 2007. */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x42011102,
+	 .driver = "Audigy2", .name = "E-mu 1010 Notebook [MAEM8950]",
+	 .id = "EMU1010",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .ca_cardbus_chip = 1,
+	 .spk71 = 1 ,
+	 .emu_model = EMU_MODEL_EMU1616},
+	/* Tested by James@superbug.co.uk 4th Nov 2007. */
+	/* This is MAEM8960, 0202 is MAEM 8980 */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
+	 .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM8960]",
+	 .id = "EMU1010",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1,
+	 .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 new revision */
+	/* Tested by Maxim Kachur <mcdebugger@duganet.ru> 17th Oct 2012. */
+	/* This is MAEM8986, 0202 is MAEM8980 */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40071102,
+	 .driver = "Audigy2", .name = "E-mu 1010 PCIe [MAEM8986]",
+	 .id = "EMU1010",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1,
+	 .emu_model = EMU_MODEL_EMU1010B}, /* EMU 1010 PCIe */
+	/* Tested by James@superbug.co.uk 8th July 2005. */
+	/* This is MAEM8810, 0202 is MAEM8820 */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102,
+	 .driver = "Audigy2", .name = "E-mu 1010 [MAEM8810]",
+	 .id = "EMU1010",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .spk71 = 1,
+	 .emu_model = EMU_MODEL_EMU1010}, /* EMU 1010 old revision */
+	/* EMU0404b */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40021102,
+	 .driver = "Audigy2", .name = "E-mu 0404b PCI [MAEM8852]",
+	 .id = "EMU0404",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1,
+	 .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 new revision */
+	/* Tested by James@superbug.co.uk 20-3-2007. */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40021102,
+	 .driver = "Audigy2", .name = "E-mu 0404 [MAEM8850]",
+	 .id = "EMU0404",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .spk71 = 1,
+	 .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 */
+	/* EMU0404 PCIe */
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40051102,
+	 .driver = "Audigy2", .name = "E-mu 0404 PCIe [MAEM8984]",
+	 .id = "EMU0404",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1,
+	 .emu_model = EMU_MODEL_EMU0404}, /* EMU 0404 PCIe ver_03 */
+	/* Note that all E-mu cards require kernel 2.6 or newer. */
+	{.vendor = 0x1102, .device = 0x0008,
+	 .driver = "Audigy2", .name = "SB Audigy 2 Value [Unknown]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .ac97_chip = 1} ,
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
+	 .driver = "Audigy2", .name = "SB Audigy 4 PRO [SB0380]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	/* Tested by shane-alsa@cm.nu 5th Nov 2005 */
+	/* The 0x20061102 does have SB0350 written on it
+	 * Just like 0x20021102
+	 */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20061102,
+	 .driver = "Audigy2", .name = "SB Audigy 2 [SB0350b]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .invert_shared_spdif = 1,	/* digital/analog switch swapped */
+	 .ac97_chip = 1} ,
+	/* 0x20051102 also has SB0350 written on it, treated as Audigy 2 ZS by
+	   Creative's Windows driver */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20051102,
+	 .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0350a]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .invert_shared_spdif = 1,	/* digital/analog switch swapped */
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
+	 .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0350]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .invert_shared_spdif = 1,	/* digital/analog switch swapped */
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102,
+	 .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0360]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .invert_shared_spdif = 1,	/* digital/analog switch swapped */
+	 .ac97_chip = 1} ,
+	/* Audigy 2 */
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
+	/* DSP: CA0102-IAT
+	 * DAC: CS4382-KQ
+	 * ADC: Philips 1361T
+	 * AC97: STAC9721
+	 * CA0151: Yes
+	 */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102,
+	 .driver = "Audigy2", .name = "SB Audigy 2 [SB0240]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .adc_1361t = 1,  /* 24 bit capture instead of 16bit */
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
+	 .driver = "Audigy2", .name = "Audigy 2 Platinum EX [SB0280]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1} ,
+	/* Dell OEM/Creative Labs Audigy 2 ZS */
+	/* See ALSA bug#1365 */
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10031102,
+	 .driver = "Audigy2", .name = "SB Audigy 2 ZS [SB0353]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .invert_shared_spdif = 1,	/* digital/analog switch swapped */
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
+	 .driver = "Audigy2", .name = "SB Audigy 2 Platinum [SB0240P]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spk71 = 1,
+	 .spdif_bug = 1,
+	 .invert_shared_spdif = 1,	/* digital/analog switch swapped */
+	 .adc_1361t = 1,  /* 24 bit capture instead of 16bit. Fixes ALSA bug#324 */
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
+	 .driver = "Audigy2", .name = "SB Audigy 2 [Unknown]",
+	 .id = "Audigy2",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ca0151_chip = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102,
+	 .driver = "Audigy", .name = "SB Audigy 1 [SB0092]",
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00521102,
+	 .driver = "Audigy", .name = "SB Audigy 1 ES [SB0160]",
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .spdif_bug = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102,
+	 .driver = "Audigy", .name = "SB Audigy 1 [SB0090]",
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0004,
+	 .driver = "Audigy", .name = "Audigy 1 [Unknown]",
+	 .id = "Audigy",
+	 .emu10k2_chip = 1,
+	 .ca0102_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x100a1102,
+	 .driver = "EMU10K1", .name = "SB Live! 5.1 [SB0220]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806b1102,
+	 .driver = "EMU10K1", .name = "SB Live! [SB0105]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806a1102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [SB0103]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [SB0101]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	/* Tested by ALSA bug#1680 26th December 2005 */
+	/* note: It really has SB0220 written on the card, */
+	/* but it's SB0228 according to kx.inf */
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80661102,
+	 .driver = "EMU10K1", .name = "SB Live! 5.1 Dell OEM [SB0228]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	/* Tested by Thomas Zehetbauer 27th Aug 2005 */
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80651102,
+	 .driver = "EMU10K1", .name = "SB Live! 5.1 [SB0220]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
+	 .driver = "EMU10K1", .name = "SB Live! 5.1",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	/* Tested by alsa bugtrack user "hus" bug #1297 12th Aug 2005 */
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
+	 .driver = "EMU10K1", .name = "SB Live! 5.1 [SB0060]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 2, /* ac97 is optional; both SBLive 5.1 and platinum
+			  * share the same IDs!
+			  */
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [CT4850]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
+	 .driver = "EMU10K1", .name = "SB Live! Platinum [CT4760P]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [CT4871]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [CT4831]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [CT4870]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	/* Tested by James@superbug.co.uk 3rd July 2005 */
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [CT4832]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [CT4830]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102,
+	 .driver = "EMU10K1", .name = "SB PCI512 [CT4790]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [CT4780]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
+	 .driver = "EMU10K1", .name = "E-mu APS [PC545]",
+	 .id = "APS",
+	 .emu10k1_chip = 1,
+	 .ecard = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102,
+	 .driver = "EMU10K1", .name = "SB Live! [CT4620]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102,
+	 .driver = "EMU10K1", .name = "SB Live! Value [CT4670]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{.vendor = 0x1102, .device = 0x0002,
+	 .driver = "EMU10K1", .name = "SB Live! [Unknown]",
+	 .id = "Live",
+	 .emu10k1_chip = 1,
+	 .ac97_chip = 1,
+	 .sblive51 = 1} ,
+	{ } /* terminator */
+};
+
+int snd_emu10k1_create(struct snd_card *card,
+		       struct pci_dev *pci,
+		       unsigned short extin_mask,
+		       unsigned short extout_mask,
+		       long max_cache_bytes,
+		       int enable_ir,
+		       uint subsystem,
+		       struct snd_emu10k1 **remu)
+{
+	struct snd_emu10k1 *emu;
+	int idx, err;
+	int is_audigy;
+	unsigned int silent_page;
+	const struct snd_emu_chip_details *c;
+	static struct snd_device_ops ops = {
+		.dev_free =	snd_emu10k1_dev_free,
+	};
+
+	*remu = NULL;
+
+	/* enable PCI device */
+	err = pci_enable_device(pci);
+	if (err < 0)
+		return err;
+
+	emu = kzalloc(sizeof(*emu), GFP_KERNEL);
+	if (emu == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+	emu->card = card;
+	spin_lock_init(&emu->reg_lock);
+	spin_lock_init(&emu->emu_lock);
+	spin_lock_init(&emu->spi_lock);
+	spin_lock_init(&emu->i2c_lock);
+	spin_lock_init(&emu->voice_lock);
+	spin_lock_init(&emu->synth_lock);
+	spin_lock_init(&emu->memblk_lock);
+	mutex_init(&emu->fx8010.lock);
+	INIT_LIST_HEAD(&emu->mapped_link_head);
+	INIT_LIST_HEAD(&emu->mapped_order_link_head);
+	emu->pci = pci;
+	emu->irq = -1;
+	emu->synth = NULL;
+	emu->get_synth_voice = NULL;
+	INIT_DELAYED_WORK(&emu->emu1010.firmware_work, emu1010_firmware_work);
+	/* read revision & serial */
+	emu->revision = pci->revision;
+	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
+	dev_dbg(card->dev,
+		"vendor = 0x%x, device = 0x%x, subsystem_vendor_id = 0x%x, subsystem_id = 0x%x\n",
+		pci->vendor, pci->device, emu->serial, emu->model);
+
+	for (c = emu_chip_details; c->vendor; c++) {
+		if (c->vendor == pci->vendor && c->device == pci->device) {
+			if (subsystem) {
+				if (c->subsystem && (c->subsystem == subsystem))
+					break;
+				else
+					continue;
+			} else {
+				if (c->subsystem && (c->subsystem != emu->serial))
+					continue;
+				if (c->revision && c->revision != emu->revision)
+					continue;
+			}
+			break;
+		}
+	}
+	if (c->vendor == 0) {
+		dev_err(card->dev, "emu10k1: Card not recognised\n");
+		kfree(emu);
+		pci_disable_device(pci);
+		return -ENOENT;
+	}
+	emu->card_capabilities = c;
+	if (c->subsystem && !subsystem)
+		dev_dbg(card->dev, "Sound card name = %s\n", c->name);
+	else if (subsystem)
+		dev_dbg(card->dev, "Sound card name = %s, "
+			"vendor = 0x%x, device = 0x%x, subsystem = 0x%x. "
+			"Forced to subsystem = 0x%x\n",	c->name,
+			pci->vendor, pci->device, emu->serial, c->subsystem);
+	else
+		dev_dbg(card->dev, "Sound card name = %s, "
+			"vendor = 0x%x, device = 0x%x, subsystem = 0x%x.\n",
+			c->name, pci->vendor, pci->device,
+			emu->serial);
+
+	if (!*card->id && c->id) {
+		int i, n = 0;
+		strlcpy(card->id, c->id, sizeof(card->id));
+		for (;;) {
+			for (i = 0; i < snd_ecards_limit; i++) {
+				if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id))
+					break;
+			}
+			if (i >= snd_ecards_limit)
+				break;
+			n++;
+			if (n >= SNDRV_CARDS)
+				break;
+			snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n);
+		}
+	}
+
+	is_audigy = emu->audigy = c->emu10k2_chip;
+
+	/* set addressing mode */
+	emu->address_mode = is_audigy ? 0 : 1;
+	/* set the DMA transfer mask */
+	emu->dma_mask = emu->address_mode ? EMU10K1_DMA_MASK : AUDIGY_DMA_MASK;
+	if (dma_set_mask(&pci->dev, emu->dma_mask) < 0 ||
+	    dma_set_coherent_mask(&pci->dev, emu->dma_mask) < 0) {
+		dev_err(card->dev,
+			"architecture does not support PCI busmaster DMA with mask 0x%lx\n",
+			emu->dma_mask);
+		kfree(emu);
+		pci_disable_device(pci);
+		return -ENXIO;
+	}
+	if (is_audigy)
+		emu->gpr_base = A_FXGPREGBASE;
+	else
+		emu->gpr_base = FXGPREGBASE;
+
+	err = pci_request_regions(pci, "EMU10K1");
+	if (err < 0) {
+		kfree(emu);
+		pci_disable_device(pci);
+		return err;
+	}
+	emu->port = pci_resource_start(pci, 0);
+
+	emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT;
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+				(emu->address_mode ? 32 : 16) * 1024, &emu->ptb_pages) < 0) {
+		err = -ENOMEM;
+		goto error;
+	}
+
+	emu->page_ptr_table = vmalloc(emu->max_cache_pages * sizeof(void *));
+	emu->page_addr_table = vmalloc(emu->max_cache_pages *
+				       sizeof(unsigned long));
+	if (emu->page_ptr_table == NULL || emu->page_addr_table == NULL) {
+		err = -ENOMEM;
+		goto error;
+	}
+
+	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+				EMUPAGESIZE, &emu->silent_page) < 0) {
+		err = -ENOMEM;
+		goto error;
+	}
+	emu->memhdr = snd_util_memhdr_new(emu->max_cache_pages * PAGE_SIZE);
+	if (emu->memhdr == NULL) {
+		err = -ENOMEM;
+		goto error;
+	}
+	emu->memhdr->block_extra_size = sizeof(struct snd_emu10k1_memblk) -
+		sizeof(struct snd_util_memblk);
+
+	pci_set_master(pci);
+
+	emu->fx8010.fxbus_mask = 0x303f;
+	if (extin_mask == 0)
+		extin_mask = 0x3fcf;
+	if (extout_mask == 0)
+		extout_mask = 0x7fff;
+	emu->fx8010.extin_mask = extin_mask;
+	emu->fx8010.extout_mask = extout_mask;
+	emu->enable_ir = enable_ir;
+
+	if (emu->card_capabilities->ca_cardbus_chip) {
+		err = snd_emu10k1_cardbus_init(emu);
+		if (err < 0)
+			goto error;
+	}
+	if (emu->card_capabilities->ecard) {
+		err = snd_emu10k1_ecard_init(emu);
+		if (err < 0)
+			goto error;
+	} else if (emu->card_capabilities->emu_model) {
+		err = snd_emu10k1_emu1010_init(emu);
+		if (err < 0) {
+			snd_emu10k1_free(emu);
+			return err;
+		}
+	} else {
+		/* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
+			does not support this, it shouldn't do any harm */
+		snd_emu10k1_ptr_write(emu, AC97SLOT, 0,
+					AC97SLOT_CNTR|AC97SLOT_LFE);
+	}
+
+	/* initialize TRAM setup */
+	emu->fx8010.itram_size = (16 * 1024)/2;
+	emu->fx8010.etram_pages.area = NULL;
+	emu->fx8010.etram_pages.bytes = 0;
+
+	/* irq handler must be registered after I/O ports are activated */
+	if (request_irq(pci->irq, snd_emu10k1_interrupt, IRQF_SHARED,
+			KBUILD_MODNAME, emu)) {
+		err = -EBUSY;
+		goto error;
+	}
+	emu->irq = pci->irq;
+
+	/*
+	 *  Init to 0x02109204 :
+	 *  Clock accuracy    = 0     (1000ppm)
+	 *  Sample Rate       = 2     (48kHz)
+	 *  Audio Channel     = 1     (Left of 2)
+	 *  Source Number     = 0     (Unspecified)
+	 *  Generation Status = 1     (Original for Cat Code 12)
+	 *  Cat Code          = 12    (Digital Signal Mixer)
+	 *  Mode              = 0     (Mode 0)
+	 *  Emphasis          = 0     (None)
+	 *  CP                = 1     (Copyright unasserted)
+	 *  AN                = 0     (Audio data)
+	 *  P                 = 0     (Consumer)
+	 */
+	emu->spdif_bits[0] = emu->spdif_bits[1] =
+		emu->spdif_bits[2] = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+		SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+		SPCS_GENERATIONSTATUS | 0x00001200 |
+		0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT;
+
+	emu->reserved_page = (struct snd_emu10k1_memblk *)
+		snd_emu10k1_synth_alloc(emu, 4096);
+	if (emu->reserved_page)
+		emu->reserved_page->map_locked = 1;
+
+	/* Clear silent pages and set up pointers */
+	memset(emu->silent_page.area, 0, PAGE_SIZE);
+	silent_page = emu->silent_page.addr << emu->address_mode;
+	for (idx = 0; idx < (emu->address_mode ? MAXPAGES1 : MAXPAGES0); idx++)
+		((u32 *)emu->ptb_pages.area)[idx] = cpu_to_le32(silent_page | idx);
+
+	/* set up voice indices */
+	for (idx = 0; idx < NUM_G; idx++) {
+		emu->voices[idx].emu = emu;
+		emu->voices[idx].number = idx;
+	}
+
+	err = snd_emu10k1_init(emu, enable_ir, 0);
+	if (err < 0)
+		goto error;
+#ifdef CONFIG_PM_SLEEP
+	err = alloc_pm_buffer(emu);
+	if (err < 0)
+		goto error;
+#endif
+
+	/*  Initialize the effect engine */
+	err = snd_emu10k1_init_efx(emu);
+	if (err < 0)
+		goto error;
+	snd_emu10k1_audio_enable(emu);
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, emu, &ops);
+	if (err < 0)
+		goto error;
+
+#ifdef CONFIG_SND_PROC_FS
+	snd_emu10k1_proc_init(emu);
+#endif
+
+	*remu = emu;
+	return 0;
+
+ error:
+	snd_emu10k1_free(emu);
+	return err;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static unsigned char saved_regs[] = {
+	CPF, PTRX, CVCF, VTFT, Z1, Z2, PSST, DSL, CCCA, CCR, CLP,
+	FXRT, MAPA, MAPB, ENVVOL, ATKHLDV, DCYSUSV, LFOVAL1, ENVVAL,
+	ATKHLDM, DCYSUSM, LFOVAL2, IP, IFATN, PEFE, FMMOD, TREMFRQ, FM2FRQ2,
+	TEMPENV, ADCCR, FXWC, MICBA, ADCBA, FXBA,
+	MICBS, ADCBS, FXBS, CDCS, GPSCS, SPCS0, SPCS1, SPCS2,
+	SPBYPASS, AC97SLOT, CDSRCS, GPSRCS, ZVSRCS, MICIDX, ADCIDX, FXIDX,
+	0xff /* end */
+};
+static unsigned char saved_regs_audigy[] = {
+	A_ADCIDX, A_MICIDX, A_FXWC1, A_FXWC2, A_SAMPLE_RATE,
+	A_FXRT2, A_SENDAMOUNTS, A_FXRT1,
+	0xff /* end */
+};
+
+static int alloc_pm_buffer(struct snd_emu10k1 *emu)
+{
+	int size;
+
+	size = ARRAY_SIZE(saved_regs);
+	if (emu->audigy)
+		size += ARRAY_SIZE(saved_regs_audigy);
+	emu->saved_ptr = vmalloc(4 * NUM_G * size);
+	if (!emu->saved_ptr)
+		return -ENOMEM;
+	if (snd_emu10k1_efx_alloc_pm_buffer(emu) < 0)
+		return -ENOMEM;
+	if (emu->card_capabilities->ca0151_chip &&
+	    snd_p16v_alloc_pm_buffer(emu) < 0)
+		return -ENOMEM;
+	return 0;
+}
+
+static void free_pm_buffer(struct snd_emu10k1 *emu)
+{
+	vfree(emu->saved_ptr);
+	snd_emu10k1_efx_free_pm_buffer(emu);
+	if (emu->card_capabilities->ca0151_chip)
+		snd_p16v_free_pm_buffer(emu);
+}
+
+void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
+{
+	int i;
+	unsigned char *reg;
+	unsigned int *val;
+
+	val = emu->saved_ptr;
+	for (reg = saved_regs; *reg != 0xff; reg++)
+		for (i = 0; i < NUM_G; i++, val++)
+			*val = snd_emu10k1_ptr_read(emu, *reg, i);
+	if (emu->audigy) {
+		for (reg = saved_regs_audigy; *reg != 0xff; reg++)
+			for (i = 0; i < NUM_G; i++, val++)
+				*val = snd_emu10k1_ptr_read(emu, *reg, i);
+	}
+	if (emu->audigy)
+		emu->saved_a_iocfg = inl(emu->port + A_IOCFG);
+	emu->saved_hcfg = inl(emu->port + HCFG);
+}
+
+void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
+{
+	if (emu->card_capabilities->ca_cardbus_chip)
+		snd_emu10k1_cardbus_init(emu);
+	if (emu->card_capabilities->ecard)
+		snd_emu10k1_ecard_init(emu);
+	else if (emu->card_capabilities->emu_model)
+		snd_emu10k1_emu1010_init(emu);
+	else
+		snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE);
+	snd_emu10k1_init(emu, emu->enable_ir, 1);
+}
+
+void snd_emu10k1_resume_regs(struct snd_emu10k1 *emu)
+{
+	int i;
+	unsigned char *reg;
+	unsigned int *val;
+
+	snd_emu10k1_audio_enable(emu);
+
+	/* resore for spdif */
+	if (emu->audigy)
+		outl(emu->saved_a_iocfg, emu->port + A_IOCFG);
+	outl(emu->saved_hcfg, emu->port + HCFG);
+
+	val = emu->saved_ptr;
+	for (reg = saved_regs; *reg != 0xff; reg++)
+		for (i = 0; i < NUM_G; i++, val++)
+			snd_emu10k1_ptr_write(emu, *reg, i, *val);
+	if (emu->audigy) {
+		for (reg = saved_regs_audigy; *reg != 0xff; reg++)
+			for (i = 0; i < NUM_G; i++, val++)
+				snd_emu10k1_ptr_write(emu, *reg, i, *val);
+	}
+}
+#endif
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_patch.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_patch.c
new file mode 100644
index 0000000..0e069ae
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_patch.c
@@ -0,0 +1,231 @@
+/*
+ *  Patch transfer callback for Emu10k1
+ *
+ *  Copyright (C) 2000 Takashi iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+/*
+ * All the code for loading in a patch.  There is very little that is
+ * chip specific here.  Just the actual writing to the board.
+ */
+
+#include "emu10k1_synth_local.h"
+
+/*
+ */
+#define BLANK_LOOP_START	4
+#define BLANK_LOOP_END		8
+#define BLANK_LOOP_SIZE		12
+#define BLANK_HEAD_SIZE		32
+
+/*
+ * allocate a sample block and copy data from userspace
+ */
+int
+snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
+		       struct snd_util_memhdr *hdr,
+		       const void __user *data, long count)
+{
+	int offset;
+	int truesize, size, loopsize, blocksize;
+	int loopend, sampleend;
+	unsigned int start_addr;
+	struct snd_emu10k1 *emu;
+
+	emu = rec->hw;
+	if (snd_BUG_ON(!sp || !hdr))
+		return -EINVAL;
+
+	if (sp->v.size == 0) {
+		dev_dbg(emu->card->dev,
+			"emu: rom font for sample %d\n", sp->v.sample);
+		return 0;
+	}
+
+	/* recalculate address offset */
+	sp->v.end -= sp->v.start;
+	sp->v.loopstart -= sp->v.start;
+	sp->v.loopend -= sp->v.start;
+	sp->v.start = 0;
+
+	/* some samples have invalid data.  the addresses are corrected in voice info */
+	sampleend = sp->v.end;
+	if (sampleend > sp->v.size)
+		sampleend = sp->v.size;
+	loopend = sp->v.loopend;
+	if (loopend > sampleend)
+		loopend = sampleend;
+
+	/* be sure loop points start < end */
+	if (sp->v.loopstart >= sp->v.loopend) {
+		int tmp = sp->v.loopstart;
+		sp->v.loopstart = sp->v.loopend;
+		sp->v.loopend = tmp;
+	}
+
+	/* compute true data size to be loaded */
+	truesize = sp->v.size + BLANK_HEAD_SIZE;
+	loopsize = 0;
+#if 0 /* not supported */
+	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
+		loopsize = sp->v.loopend - sp->v.loopstart;
+	truesize += loopsize;
+#endif
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+		truesize += BLANK_LOOP_SIZE;
+
+	/* try to allocate a memory block */
+	blocksize = truesize;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		blocksize *= 2;
+	sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
+	if (sp->block == NULL) {
+		dev_dbg(emu->card->dev,
+			"synth malloc failed (size=%d)\n", blocksize);
+		/* not ENOMEM (for compatibility with OSS) */
+		return -ENOSPC;
+	}
+	/* set the total size */
+	sp->v.truesize = blocksize;
+
+	/* write blank samples at head */
+	offset = 0;
+	size = BLANK_HEAD_SIZE;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		size *= 2;
+	if (offset + size > blocksize)
+		return -EINVAL;
+	snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
+	offset += size;
+
+	/* copy start->loopend */
+	size = loopend;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		size *= 2;
+	if (offset + size > blocksize)
+		return -EINVAL;
+	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+		snd_emu10k1_synth_free(emu, sp->block);
+		sp->block = NULL;
+		return -EFAULT;
+	}
+	offset += size;
+	data += size;
+
+#if 0 /* not supported yet */
+	/* handle reverse (or bidirectional) loop */
+	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
+		/* copy loop in reverse */
+		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+			int woffset;
+			unsigned short *wblock = (unsigned short*)block;
+			woffset = offset / 2;
+			if (offset + loopsize * 2 > blocksize)
+				return -EINVAL;
+			for (i = 0; i < loopsize; i++)
+				wblock[woffset + i] = wblock[woffset - i -1];
+			offset += loopsize * 2;
+		} else {
+			if (offset + loopsize > blocksize)
+				return -EINVAL;
+			for (i = 0; i < loopsize; i++)
+				block[offset + i] = block[offset - i -1];
+			offset += loopsize;
+		}
+
+		/* modify loop pointers */
+		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
+			sp->v.loopend += loopsize;
+		} else {
+			sp->v.loopstart += loopsize;
+			sp->v.loopend += loopsize;
+		}
+		/* add sample pointer */
+		sp->v.end += loopsize;
+	}
+#endif
+
+	/* loopend -> sample end */
+	size = sp->v.size - loopend;
+	if (size < 0)
+		return -EINVAL;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		size *= 2;
+	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
+		snd_emu10k1_synth_free(emu, sp->block);
+		sp->block = NULL;
+		return -EFAULT;
+	}
+	offset += size;
+
+	/* clear rest of samples (if any) */
+	if (offset < blocksize)
+		snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
+
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
+		/* if no blank loop is attached in the sample, add it */
+		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
+			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
+			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
+		}
+	}
+
+#if 0 /* not supported yet */
+	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
+		/* unsigned -> signed */
+		if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
+			unsigned short *wblock = (unsigned short*)block;
+			for (i = 0; i < truesize; i++)
+				wblock[i] ^= 0x8000;
+		} else {
+			for (i = 0; i < truesize; i++)
+				block[i] ^= 0x80;
+		}
+	}
+#endif
+
+	/* recalculate offset */
+	start_addr = BLANK_HEAD_SIZE * 2;
+	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
+		start_addr >>= 1;
+	sp->v.start += start_addr;
+	sp->v.end += start_addr;
+	sp->v.loopstart += start_addr;
+	sp->v.loopend += start_addr;
+
+	return 0;
+}
+
+/*
+ * free a sample block
+ */
+int
+snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
+			struct snd_util_memhdr *hdr)
+{
+	struct snd_emu10k1 *emu;
+
+	emu = rec->hw;
+	if (snd_BUG_ON(!sp || !hdr))
+		return -EINVAL;
+
+	if (sp->block) {
+		snd_emu10k1_synth_free(emu, sp->block);
+		sp->block = NULL;
+	}
+	return 0;
+}
+
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_synth.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_synth.c
new file mode 100644
index 0000000..5457d56
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_synth.c
@@ -0,0 +1,119 @@
+/*
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *  Routines for control of EMU10K1 WaveTable synth
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include "emu10k1_synth_local.h"
+#include <linux/init.h>
+#include <linux/module.h>
+
+MODULE_AUTHOR("Takashi Iwai");
+MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth");
+MODULE_LICENSE("GPL");
+
+/*
+ * create a new hardware dependent device for Emu10k1
+ */
+static int snd_emu10k1_synth_probe(struct device *_dev)
+{
+	struct snd_seq_device *dev = to_seq_dev(_dev);
+	struct snd_emux *emux;
+	struct snd_emu10k1 *hw;
+	struct snd_emu10k1_synth_arg *arg;
+	unsigned long flags;
+
+	arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
+	if (arg == NULL)
+		return -EINVAL;
+
+	if (arg->seq_ports <= 0)
+		return 0; /* nothing */
+	if (arg->max_voices < 1)
+		arg->max_voices = 1;
+	else if (arg->max_voices > 64)
+		arg->max_voices = 64;
+
+	if (snd_emux_new(&emux) < 0)
+		return -ENOMEM;
+
+	snd_emu10k1_ops_setup(emux);
+	hw = arg->hwptr;
+	emux->hw = hw;
+	emux->max_voices = arg->max_voices;
+	emux->num_ports = arg->seq_ports;
+	emux->pitch_shift = -501;
+	emux->memhdr = hw->memhdr;
+	/* maximum two ports */
+	emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2;
+	/* audigy has two external midis */
+	emux->midi_devidx = hw->audigy ? 2 : 1;
+	emux->linear_panning = 0;
+	emux->hwdep_idx = 2; /* FIXED */
+
+	if (snd_emux_register(emux, dev->card, arg->index, "Emu10k1") < 0) {
+		snd_emux_free(emux);
+		return -ENOMEM;
+	}
+
+	spin_lock_irqsave(&hw->voice_lock, flags);
+	hw->synth = emux;
+	hw->get_synth_voice = snd_emu10k1_synth_get_voice;
+	spin_unlock_irqrestore(&hw->voice_lock, flags);
+
+	dev->driver_data = emux;
+
+	return 0;
+}
+
+static int snd_emu10k1_synth_remove(struct device *_dev)
+{
+	struct snd_seq_device *dev = to_seq_dev(_dev);
+	struct snd_emux *emux;
+	struct snd_emu10k1 *hw;
+	unsigned long flags;
+
+	if (dev->driver_data == NULL)
+		return 0; /* not registered actually */
+
+	emux = dev->driver_data;
+
+	hw = emux->hw;
+	spin_lock_irqsave(&hw->voice_lock, flags);
+	hw->synth = NULL;
+	hw->get_synth_voice = NULL;
+	spin_unlock_irqrestore(&hw->voice_lock, flags);
+
+	snd_emux_free(emux);
+	return 0;
+}
+
+/*
+ *  INIT part
+ */
+
+static struct snd_seq_driver emu10k1_synth_driver = {
+	.driver = {
+		.name = KBUILD_MODNAME,
+		.probe = snd_emu10k1_synth_probe,
+		.remove = snd_emu10k1_synth_remove,
+	},
+	.id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
+	.argsize = sizeof(struct snd_emu10k1_synth_arg),
+};
+
+module_snd_seq_driver(emu10k1_synth_driver);
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_synth_local.h b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_synth_local.h
new file mode 100644
index 0000000..25f328f
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1_synth_local.h
@@ -0,0 +1,42 @@
+#ifndef __EMU10K1_SYNTH_LOCAL_H
+#define __EMU10K1_SYNTH_LOCAL_H
+/*
+ *  Local defininitons for Emu10k1 wavetable
+ *
+ *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1_synth.h>
+
+/* emu10k1_patch.c */
+int snd_emu10k1_sample_new(struct snd_emux *private_data,
+			   struct snd_sf_sample *sp,
+			   struct snd_util_memhdr *hdr,
+			   const void __user *_data, long count);
+int snd_emu10k1_sample_free(struct snd_emux *private_data,
+			    struct snd_sf_sample *sp,
+			    struct snd_util_memhdr *hdr);
+int snd_emu10k1_memhdr_init(struct snd_emux *emu);
+
+/* emu10k1_callback.c */
+void snd_emu10k1_ops_setup(struct snd_emux *emu);
+int snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw);
+
+
+#endif	/* __EMU10K1_SYNTH_LOCAL_H */
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1x.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1x.c
new file mode 100644
index 0000000..2c2b12a
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emu10k1x.c
@@ -0,0 +1,1642 @@
+/*
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *  Driver EMU10K1X chips
+ *
+ *  Parts of this code were adapted from audigyls.c driver which is
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *
+ *  Chips (SB0200 model):
+ *    - EMU10K1X-DBQ
+ *    - STAC 9708T
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+
+MODULE_AUTHOR("Francisco Moraes <fmoraes@nc.rr.com>");
+MODULE_DESCRIPTION("EMU10K1X");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Dell Creative Labs,SB Live!}");
+
+// module parameters (see "Module Parameters")
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the EMU10K1X soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the EMU10K1X soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");
+
+
+// some definitions were borrowed from emu10k1 driver as they seem to be the same
+/************************************************************************************************/
+/* PCI function 0 registers, address = <val> + PCIBASE0						*/
+/************************************************************************************************/
+
+#define PTR			0x00		/* Indexed register set pointer register	*/
+						/* NOTE: The CHANNELNUM and ADDRESS words can	*/
+						/* be modified independently of each other.	*/
+
+#define DATA			0x04		/* Indexed register set data register		*/
+
+#define IPR			0x08		/* Global interrupt pending register		*/
+						/* Clear pending interrupts by writing a 1 to	*/
+						/* the relevant bits and zero to the other bits	*/
+#define IPR_MIDITRANSBUFEMPTY   0x00000001	/* MIDI UART transmit buffer empty		*/
+#define IPR_MIDIRECVBUFEMPTY    0x00000002	/* MIDI UART receive buffer empty		*/
+#define IPR_CH_0_LOOP           0x00000800      /* Channel 0 loop                               */
+#define IPR_CH_0_HALF_LOOP      0x00000100      /* Channel 0 half loop                          */
+#define IPR_CAP_0_LOOP          0x00080000      /* Channel capture loop                         */
+#define IPR_CAP_0_HALF_LOOP     0x00010000      /* Channel capture half loop                    */
+
+#define INTE			0x0c		/* Interrupt enable register			*/
+#define INTE_MIDITXENABLE       0x00000001	/* Enable MIDI transmit-buffer-empty interrupts	*/
+#define INTE_MIDIRXENABLE       0x00000002	/* Enable MIDI receive-buffer-empty interrupts	*/
+#define INTE_CH_0_LOOP          0x00000800      /* Channel 0 loop                               */
+#define INTE_CH_0_HALF_LOOP     0x00000100      /* Channel 0 half loop                          */
+#define INTE_CAP_0_LOOP         0x00080000      /* Channel capture loop                         */
+#define INTE_CAP_0_HALF_LOOP    0x00010000      /* Channel capture half loop                    */
+
+#define HCFG			0x14		/* Hardware config register			*/
+
+#define HCFG_LOCKSOUNDCACHE	0x00000008	/* 1 = Cancel bustmaster accesses to soundcache */
+						/* NOTE: This should generally never be used.  	*/
+#define HCFG_AUDIOENABLE	0x00000001	/* 0 = CODECs transmit zero-valued samples	*/
+						/* Should be set to 1 when the EMU10K1 is	*/
+						/* completely initialized.			*/
+#define GPIO			0x18		/* Defaults: 00001080-Analog, 00001000-SPDIF.   */
+
+
+#define AC97DATA		0x1c		/* AC97 register set data register (16 bit)	*/
+
+#define AC97ADDRESS		0x1e		/* AC97 register set address register (8 bit)	*/
+
+/********************************************************************************************************/
+/* Emu10k1x pointer-offset register set, accessed through the PTR and DATA registers			*/
+/********************************************************************************************************/
+#define PLAYBACK_LIST_ADDR	0x00		/* Base DMA address of a list of pointers to each period/size */
+						/* One list entry: 4 bytes for DMA address, 
+						 * 4 bytes for period_size << 16.
+						 * One list entry is 8 bytes long.
+						 * One list entry for each period in the buffer.
+						 */
+#define PLAYBACK_LIST_SIZE	0x01		/* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000  */
+#define PLAYBACK_LIST_PTR	0x02		/* Pointer to the current period being played */
+#define PLAYBACK_DMA_ADDR	0x04		/* Playback DMA address */
+#define PLAYBACK_PERIOD_SIZE	0x05		/* Playback period size */
+#define PLAYBACK_POINTER	0x06		/* Playback period pointer. Sample currently in DAC */
+#define PLAYBACK_UNKNOWN1       0x07
+#define PLAYBACK_UNKNOWN2       0x08
+
+/* Only one capture channel supported */
+#define CAPTURE_DMA_ADDR	0x10		/* Capture DMA address */
+#define CAPTURE_BUFFER_SIZE	0x11		/* Capture buffer size */
+#define CAPTURE_POINTER		0x12		/* Capture buffer pointer. Sample currently in ADC */
+#define CAPTURE_UNKNOWN         0x13
+
+/* From 0x20 - 0x3f, last samples played on each channel */
+
+#define TRIGGER_CHANNEL         0x40            /* Trigger channel playback                     */
+#define TRIGGER_CHANNEL_0       0x00000001      /* Trigger channel 0                            */
+#define TRIGGER_CHANNEL_1       0x00000002      /* Trigger channel 1                            */
+#define TRIGGER_CHANNEL_2       0x00000004      /* Trigger channel 2                            */
+#define TRIGGER_CAPTURE         0x00000100      /* Trigger capture channel                      */
+
+#define ROUTING                 0x41            /* Setup sound routing ?                        */
+#define ROUTING_FRONT_LEFT      0x00000001
+#define ROUTING_FRONT_RIGHT     0x00000002
+#define ROUTING_REAR_LEFT       0x00000004
+#define ROUTING_REAR_RIGHT      0x00000008
+#define ROUTING_CENTER_LFE      0x00010000
+
+#define SPCS0			0x42		/* SPDIF output Channel Status 0 register	*/
+
+#define SPCS1			0x43		/* SPDIF output Channel Status 1 register	*/
+
+#define SPCS2			0x44		/* SPDIF output Channel Status 2 register	*/
+
+#define SPCS_CLKACCYMASK	0x30000000	/* Clock accuracy				*/
+#define SPCS_CLKACCY_1000PPM	0x00000000	/* 1000 parts per million			*/
+#define SPCS_CLKACCY_50PPM	0x10000000	/* 50 parts per million				*/
+#define SPCS_CLKACCY_VARIABLE	0x20000000	/* Variable accuracy				*/
+#define SPCS_SAMPLERATEMASK	0x0f000000	/* Sample rate					*/
+#define SPCS_SAMPLERATE_44	0x00000000	/* 44.1kHz sample rate				*/
+#define SPCS_SAMPLERATE_48	0x02000000	/* 48kHz sample rate				*/
+#define SPCS_SAMPLERATE_32	0x03000000	/* 32kHz sample rate				*/
+#define SPCS_CHANNELNUMMASK	0x00f00000	/* Channel number				*/
+#define SPCS_CHANNELNUM_UNSPEC	0x00000000	/* Unspecified channel number			*/
+#define SPCS_CHANNELNUM_LEFT	0x00100000	/* Left channel					*/
+#define SPCS_CHANNELNUM_RIGHT	0x00200000	/* Right channel				*/
+#define SPCS_SOURCENUMMASK	0x000f0000	/* Source number				*/
+#define SPCS_SOURCENUM_UNSPEC	0x00000000	/* Unspecified source number			*/
+#define SPCS_GENERATIONSTATUS	0x00008000	/* Originality flag (see IEC-958 spec)		*/
+#define SPCS_CATEGORYCODEMASK	0x00007f00	/* Category code (see IEC-958 spec)		*/
+#define SPCS_MODEMASK		0x000000c0	/* Mode (see IEC-958 spec)			*/
+#define SPCS_EMPHASISMASK	0x00000038	/* Emphasis					*/
+#define SPCS_EMPHASIS_NONE	0x00000000	/* No emphasis					*/
+#define SPCS_EMPHASIS_50_15	0x00000008	/* 50/15 usec 2 channel				*/
+#define SPCS_COPYRIGHT		0x00000004	/* Copyright asserted flag -- do not modify	*/
+#define SPCS_NOTAUDIODATA	0x00000002	/* 0 = Digital audio, 1 = not audio		*/
+#define SPCS_PROFESSIONAL	0x00000001	/* 0 = Consumer (IEC-958), 1 = pro (AES3-1992)	*/
+
+#define SPDIF_SELECT		0x45		/* Enables SPDIF or Analogue outputs 0-Analogue, 0x700-SPDIF */
+
+/* This is the MPU port on the card                      					*/
+#define MUDATA		0x47
+#define MUCMD		0x48
+#define MUSTAT		MUCMD
+
+/* From 0x50 - 0x5f, last samples captured */
+
+/*
+ * The hardware has 3 channels for playback and 1 for capture.
+ *  - channel 0 is the front channel
+ *  - channel 1 is the rear channel
+ *  - channel 2 is the center/lfe channel
+ * Volume is controlled by the AC97 for the front and rear channels by
+ * the PCM Playback Volume, Sigmatel Surround Playback Volume and 
+ * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects
+ * the front/rear channel mixing in the REAR OUT jack. When using the
+ * 4-Speaker Stereo, both front and rear channels will be mixed in the
+ * REAR OUT.
+ * The center/lfe channel has no volume control and cannot be muted during
+ * playback.
+ */
+
+struct emu10k1x_voice {
+	struct emu10k1x *emu;
+	int number;
+	int use;
+  
+	struct emu10k1x_pcm *epcm;
+};
+
+struct emu10k1x_pcm {
+	struct emu10k1x *emu;
+	struct snd_pcm_substream *substream;
+	struct emu10k1x_voice *voice;
+	unsigned short running;
+};
+
+struct emu10k1x_midi {
+	struct emu10k1x *emu;
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_substream *substream_input;
+	struct snd_rawmidi_substream *substream_output;
+	unsigned int midi_mode;
+	spinlock_t input_lock;
+	spinlock_t output_lock;
+	spinlock_t open_lock;
+	int tx_enable, rx_enable;
+	int port;
+	int ipr_tx, ipr_rx;
+	void (*interrupt)(struct emu10k1x *emu, unsigned int status);
+};
+
+// definition of the chip-specific record
+struct emu10k1x {
+	struct snd_card *card;
+	struct pci_dev *pci;
+
+	unsigned long port;
+	struct resource *res_port;
+	int irq;
+
+	unsigned char revision;		/* chip revision */
+	unsigned int serial;            /* serial number */
+	unsigned short model;		/* subsystem id */
+
+	spinlock_t emu_lock;
+	spinlock_t voice_lock;
+
+	struct snd_ac97 *ac97;
+	struct snd_pcm *pcm;
+
+	struct emu10k1x_voice voices[3];
+	struct emu10k1x_voice capture_voice;
+	u32 spdif_bits[3]; // SPDIF out setup
+
+	struct snd_dma_buffer dma_buffer;
+
+	struct emu10k1x_midi midi;
+};
+
+/* hardware definition */
+static const struct snd_pcm_hardware snd_emu10k1x_playback_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | 
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(32*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(16*1024),
+	.periods_min =		2,
+	.periods_max =		8,
+	.fifo_size =		0,
+};
+
+static const struct snd_pcm_hardware snd_emu10k1x_capture_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP | 
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(32*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(16*1024),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+static unsigned int snd_emu10k1x_ptr_read(struct emu10k1x * emu, 
+					  unsigned int reg, 
+					  unsigned int chn)
+{
+	unsigned long flags;
+	unsigned int regptr, val;
+  
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + PTR);
+	val = inl(emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+static void snd_emu10k1x_ptr_write(struct emu10k1x *emu, 
+				   unsigned int reg, 
+				   unsigned int chn, 
+				   unsigned int data)
+{
+	unsigned int regptr;
+	unsigned long flags;
+
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + PTR);
+	outl(data, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int intr_enable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	intr_enable = inl(emu->port + INTE) | intrenb;
+	outl(intr_enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int intr_enable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	intr_enable = inl(emu->port + INTE) & ~intrenb;
+	outl(intr_enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_gpio_write(struct emu10k1x *emu, unsigned int value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(value, emu->port + GPIO);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_emu10k1x_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+	kfree(runtime->private_data);
+}
+
+static void snd_emu10k1x_pcm_interrupt(struct emu10k1x *emu, struct emu10k1x_voice *voice)
+{
+	struct emu10k1x_pcm *epcm;
+
+	if ((epcm = voice->epcm) == NULL)
+		return;
+	if (epcm->substream == NULL)
+		return;
+#if 0
+	dev_info(emu->card->dev,
+		 "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+		   epcm->substream->ops->pointer(epcm->substream),
+		   snd_pcm_lib_period_bytes(epcm->substream),
+		   snd_pcm_lib_buffer_bytes(epcm->substream));
+#endif
+	snd_pcm_period_elapsed(epcm->substream);
+}
+
+/* open callback */
+static int snd_emu10k1x_playback_open(struct snd_pcm_substream *substream)
+{
+	struct emu10k1x *chip = snd_pcm_substream_chip(substream);
+	struct emu10k1x_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+		return err;
+	}
+	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = chip;
+	epcm->substream = substream;
+  
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1x_pcm_free_substream;
+  
+	runtime->hw = snd_emu10k1x_playback_hw;
+
+	return 0;
+}
+
+/* close callback */
+static int snd_emu10k1x_playback_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+/* hw_params callback */
+static int snd_emu10k1x_pcm_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct emu10k1x_pcm *epcm = runtime->private_data;
+
+	if (! epcm->voice) {
+		epcm->voice = &epcm->emu->voices[substream->pcm->device];
+		epcm->voice->use = 1;
+		epcm->voice->epcm = epcm;
+	}
+
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_emu10k1x_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct emu10k1x_pcm *epcm;
+
+	if (runtime->private_data == NULL)
+		return 0;
+	
+	epcm = runtime->private_data;
+
+	if (epcm->voice) {
+		epcm->voice->use = 0;
+		epcm->voice->epcm = NULL;
+		epcm->voice = NULL;
+	}
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare callback */
+static int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct emu10k1x_pcm *epcm = runtime->private_data;
+	int voice = epcm->voice->number;
+	u32 *table_base = (u32 *)(emu->dma_buffer.area+1024*voice);
+	u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+	int i;
+	
+	for(i = 0; i < runtime->periods; i++) {
+		*table_base++=runtime->dma_addr+(i*period_size_bytes);
+		*table_base++=period_size_bytes<<16;
+	}
+
+	snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer.addr+1024*voice);
+	snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_SIZE, voice, (runtime->periods - 1) << 19);
+	snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_PTR, voice, 0);
+	snd_emu10k1x_ptr_write(emu, PLAYBACK_POINTER, voice, 0);
+	snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN1, voice, 0);
+	snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN2, voice, 0);
+	snd_emu10k1x_ptr_write(emu, PLAYBACK_DMA_ADDR, voice, runtime->dma_addr);
+
+	snd_emu10k1x_ptr_write(emu, PLAYBACK_PERIOD_SIZE, voice, frames_to_bytes(runtime, runtime->period_size)<<16);
+
+	return 0;
+}
+
+/* trigger callback */
+static int snd_emu10k1x_pcm_trigger(struct snd_pcm_substream *substream,
+				    int cmd)
+{
+	struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct emu10k1x_pcm *epcm = runtime->private_data;
+	int channel = epcm->voice->number;
+	int result = 0;
+
+	/*
+	dev_dbg(emu->card->dev,
+		"trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n",
+		(int)emu, cmd, (int)substream->ops->pointer(substream));
+	*/
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if(runtime->periods == 2)
+			snd_emu10k1x_intr_enable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel);
+		else
+			snd_emu10k1x_intr_enable(emu, INTE_CH_0_LOOP << channel);
+		epcm->running = 1;
+		snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|(TRIGGER_CHANNEL_0<<channel));
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		epcm->running = 0;
+		snd_emu10k1x_intr_disable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel);
+		snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CHANNEL_0<<channel));
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	return result;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t
+snd_emu10k1x_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct emu10k1x_pcm *epcm = runtime->private_data;
+	int channel = epcm->voice->number;
+	snd_pcm_uframes_t ptr = 0, ptr1 = 0, ptr2= 0,ptr3 = 0,ptr4 = 0;
+
+	if (!epcm->running)
+		return 0;
+
+	ptr3 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+	ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel);
+	ptr4 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel);
+
+	if(ptr4 == 0 && ptr1 == frames_to_bytes(runtime, runtime->buffer_size))
+		return 0;
+	
+	if (ptr3 != ptr4) 
+		ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel);
+	ptr2 = bytes_to_frames(runtime, ptr1);
+	ptr2 += (ptr4 >> 3) * runtime->period_size;
+	ptr = ptr2;
+
+	if (ptr >= runtime->buffer_size)
+		ptr -= runtime->buffer_size;
+
+	return ptr;
+}
+
+/* operators */
+static const struct snd_pcm_ops snd_emu10k1x_playback_ops = {
+	.open =        snd_emu10k1x_playback_open,
+	.close =       snd_emu10k1x_playback_close,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_emu10k1x_pcm_hw_params,
+	.hw_free =     snd_emu10k1x_pcm_hw_free,
+	.prepare =     snd_emu10k1x_pcm_prepare,
+	.trigger =     snd_emu10k1x_pcm_trigger,
+	.pointer =     snd_emu10k1x_pcm_pointer,
+};
+
+/* open_capture callback */
+static int snd_emu10k1x_pcm_open_capture(struct snd_pcm_substream *substream)
+{
+	struct emu10k1x *chip = snd_pcm_substream_chip(substream);
+	struct emu10k1x_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+                return err;
+	if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0)
+                return err;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+
+	epcm->emu = chip;
+	epcm->substream = substream;
+
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1x_pcm_free_substream;
+
+	runtime->hw = snd_emu10k1x_capture_hw;
+
+	return 0;
+}
+
+/* close callback */
+static int snd_emu10k1x_pcm_close_capture(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+/* hw_params callback */
+static int snd_emu10k1x_pcm_hw_params_capture(struct snd_pcm_substream *substream,
+					      struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct emu10k1x_pcm *epcm = runtime->private_data;
+
+	if (! epcm->voice) {
+		if (epcm->emu->capture_voice.use)
+			return -EBUSY;
+		epcm->voice = &epcm->emu->capture_voice;
+		epcm->voice->epcm = epcm;
+		epcm->voice->use = 1;
+	}
+
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+/* hw_free callback */
+static int snd_emu10k1x_pcm_hw_free_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	struct emu10k1x_pcm *epcm;
+
+	if (runtime->private_data == NULL)
+		return 0;
+	epcm = runtime->private_data;
+
+	if (epcm->voice) {
+		epcm->voice->use = 0;
+		epcm->voice->epcm = NULL;
+		epcm->voice = NULL;
+	}
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare capture callback */
+static int snd_emu10k1x_pcm_prepare_capture(struct snd_pcm_substream *substream)
+{
+	struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	snd_emu10k1x_ptr_write(emu, CAPTURE_DMA_ADDR, 0, runtime->dma_addr);
+	snd_emu10k1x_ptr_write(emu, CAPTURE_BUFFER_SIZE, 0, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+	snd_emu10k1x_ptr_write(emu, CAPTURE_POINTER, 0, 0);
+	snd_emu10k1x_ptr_write(emu, CAPTURE_UNKNOWN, 0, 0);
+
+	return 0;
+}
+
+/* trigger_capture callback */
+static int snd_emu10k1x_pcm_trigger_capture(struct snd_pcm_substream *substream,
+					    int cmd)
+{
+	struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct emu10k1x_pcm *epcm = runtime->private_data;
+	int result = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_emu10k1x_intr_enable(emu, INTE_CAP_0_LOOP | 
+					 INTE_CAP_0_HALF_LOOP);
+		snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|TRIGGER_CAPTURE);
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		epcm->running = 0;
+		snd_emu10k1x_intr_disable(emu, INTE_CAP_0_LOOP | 
+					  INTE_CAP_0_HALF_LOOP);
+		snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CAPTURE));
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	return result;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_emu10k1x_pcm_pointer_capture(struct snd_pcm_substream *substream)
+{
+	struct emu10k1x *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct emu10k1x_pcm *epcm = runtime->private_data;
+	snd_pcm_uframes_t ptr;
+
+	if (!epcm->running)
+		return 0;
+
+	ptr = bytes_to_frames(runtime, snd_emu10k1x_ptr_read(emu, CAPTURE_POINTER, 0));
+	if (ptr >= runtime->buffer_size)
+		ptr -= runtime->buffer_size;
+
+	return ptr;
+}
+
+static const struct snd_pcm_ops snd_emu10k1x_capture_ops = {
+	.open =        snd_emu10k1x_pcm_open_capture,
+	.close =       snd_emu10k1x_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_emu10k1x_pcm_hw_params_capture,
+	.hw_free =     snd_emu10k1x_pcm_hw_free_capture,
+	.prepare =     snd_emu10k1x_pcm_prepare_capture,
+	.trigger =     snd_emu10k1x_pcm_trigger_capture,
+	.pointer =     snd_emu10k1x_pcm_pointer_capture,
+};
+
+static unsigned short snd_emu10k1x_ac97_read(struct snd_ac97 *ac97,
+					     unsigned short reg)
+{
+	struct emu10k1x *emu = ac97->private_data;
+	unsigned long flags;
+	unsigned short val;
+  
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	val = inw(emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+static void snd_emu10k1x_ac97_write(struct snd_ac97 *ac97,
+				    unsigned short reg, unsigned short val)
+{
+	struct emu10k1x *emu = ac97->private_data;
+	unsigned long flags;
+  
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	outw(val, emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static int snd_emu10k1x_ac97(struct emu10k1x *chip)
+{
+	struct snd_ac97_bus *pbus;
+	struct snd_ac97_template ac97;
+	int err;
+	static struct snd_ac97_bus_ops ops = {
+		.write = snd_emu10k1x_ac97_write,
+		.read = snd_emu10k1x_ac97_read,
+	};
+  
+	if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
+		return err;
+	pbus->no_vra = 1; /* we don't need VRA */
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+	ac97.scaps = AC97_SCAP_NO_SPDIF;
+	return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
+}
+
+static int snd_emu10k1x_free(struct emu10k1x *chip)
+{
+	snd_emu10k1x_ptr_write(chip, TRIGGER_CHANNEL, 0, 0);
+	// disable interrupts
+	outl(0, chip->port + INTE);
+	// disable audio
+	outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG);
+
+	/* release the irq */
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+
+	// release the i/o port
+	release_and_free_resource(chip->res_port);
+
+	// release the DMA
+	if (chip->dma_buffer.area) {
+		snd_dma_free_pages(&chip->dma_buffer);
+	}
+
+	pci_disable_device(chip->pci);
+
+	// release the data
+	kfree(chip);
+	return 0;
+}
+
+static int snd_emu10k1x_dev_free(struct snd_device *device)
+{
+	struct emu10k1x *chip = device->device_data;
+	return snd_emu10k1x_free(chip);
+}
+
+static irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id)
+{
+	unsigned int status;
+
+	struct emu10k1x *chip = dev_id;
+	struct emu10k1x_voice *pvoice = chip->voices;
+	int i;
+	int mask;
+
+	status = inl(chip->port + IPR);
+
+	if (! status)
+		return IRQ_NONE;
+
+	// capture interrupt
+	if (status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) {
+		struct emu10k1x_voice *cap_voice = &chip->capture_voice;
+		if (cap_voice->use)
+			snd_emu10k1x_pcm_interrupt(chip, cap_voice);
+		else
+			snd_emu10k1x_intr_disable(chip, 
+						  INTE_CAP_0_LOOP |
+						  INTE_CAP_0_HALF_LOOP);
+	}
+		
+	mask = IPR_CH_0_LOOP|IPR_CH_0_HALF_LOOP;
+	for (i = 0; i < 3; i++) {
+		if (status & mask) {
+			if (pvoice->use)
+				snd_emu10k1x_pcm_interrupt(chip, pvoice);
+			else 
+				snd_emu10k1x_intr_disable(chip, mask);
+		}
+		pvoice++;
+		mask <<= 1;
+	}
+		
+	if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
+		if (chip->midi.interrupt)
+			chip->midi.interrupt(chip, status);
+		else
+			snd_emu10k1x_intr_disable(chip, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
+	}
+		
+	// acknowledge the interrupt if necessary
+	outl(status, chip->port + IPR);
+
+	/* dev_dbg(chip->card->dev, "interrupt %08x\n", status); */
+	return IRQ_HANDLED;
+}
+
+static const struct snd_pcm_chmap_elem surround_map[] = {
+	{ .channels = 2,
+	  .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
+	{ }
+};
+
+static const struct snd_pcm_chmap_elem clfe_map[] = {
+	{ .channels = 2,
+	  .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } },
+	{ }
+};
+
+static int snd_emu10k1x_pcm(struct emu10k1x *emu, int device)
+{
+	struct snd_pcm *pcm;
+	const struct snd_pcm_chmap_elem *map = NULL;
+	int err;
+	int capture = 0;
+  
+	if (device == 0)
+		capture = 1;
+	
+	if ((err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm)) < 0)
+		return err;
+  
+	pcm->private_data = emu;
+	
+	switch(device) {
+	case 0:
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1x_capture_ops);
+		break;
+	case 1:
+	case 2:
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops);
+		break;
+	}
+
+	pcm->info_flags = 0;
+	switch(device) {
+	case 0:
+		strcpy(pcm->name, "EMU10K1X Front");
+		map = snd_pcm_std_chmaps;
+		break;
+	case 1:
+		strcpy(pcm->name, "EMU10K1X Rear");
+		map = surround_map;
+		break;
+	case 2:
+		strcpy(pcm->name, "EMU10K1X Center/LFE");
+		map = clfe_map;
+		break;
+	}
+	emu->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+					      snd_dma_pci_data(emu->pci), 
+					      32*1024, 32*1024);
+  
+	return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2,
+				     1 << 2, NULL);
+}
+
+static int snd_emu10k1x_create(struct snd_card *card,
+			       struct pci_dev *pci,
+			       struct emu10k1x **rchip)
+{
+	struct emu10k1x *chip;
+	int err;
+	int ch;
+	static struct snd_device_ops ops = {
+		.dev_free = snd_emu10k1x_dev_free,
+	};
+
+	*rchip = NULL;
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
+	    pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
+		dev_err(card->dev, "error to set 28bit mask DMA\n");
+		pci_disable_device(pci);
+		return -ENXIO;
+	}
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+
+	spin_lock_init(&chip->emu_lock);
+	spin_lock_init(&chip->voice_lock);
+  
+	chip->port = pci_resource_start(pci, 0);
+	if ((chip->res_port = request_region(chip->port, 8,
+					     "EMU10K1X")) == NULL) { 
+		dev_err(card->dev, "cannot allocate the port 0x%lx\n",
+			chip->port);
+		snd_emu10k1x_free(chip);
+		return -EBUSY;
+	}
+
+	if (request_irq(pci->irq, snd_emu10k1x_interrupt,
+			IRQF_SHARED, KBUILD_MODNAME, chip)) {
+		dev_err(card->dev, "cannot grab irq %d\n", pci->irq);
+		snd_emu10k1x_free(chip);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+  
+	if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+			       4 * 1024, &chip->dma_buffer) < 0) {
+		snd_emu10k1x_free(chip);
+		return -ENOMEM;
+	}
+
+	pci_set_master(pci);
+	/* read revision & serial */
+	chip->revision = pci->revision;
+	pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model);
+	dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n", chip->model,
+		   chip->revision, chip->serial);
+
+	outl(0, chip->port + INTE);	
+
+	for(ch = 0; ch < 3; ch++) {
+		chip->voices[ch].emu = chip;
+		chip->voices[ch].number = ch;
+	}
+
+	/*
+	 *  Init to 0x02109204 :
+	 *  Clock accuracy    = 0     (1000ppm)
+	 *  Sample Rate       = 2     (48kHz)
+	 *  Audio Channel     = 1     (Left of 2)
+	 *  Source Number     = 0     (Unspecified)
+	 *  Generation Status = 1     (Original for Cat Code 12)
+	 *  Cat Code          = 12    (Digital Signal Mixer)
+	 *  Mode              = 0     (Mode 0)
+	 *  Emphasis          = 0     (None)
+	 *  CP                = 1     (Copyright unasserted)
+	 *  AN                = 0     (Audio data)
+	 *  P                 = 0     (Consumer)
+	 */
+	snd_emu10k1x_ptr_write(chip, SPCS0, 0,
+			       chip->spdif_bits[0] = 
+			       SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+			       SPCS_GENERATIONSTATUS | 0x00001200 |
+			       0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	snd_emu10k1x_ptr_write(chip, SPCS1, 0,
+			       chip->spdif_bits[1] = 
+			       SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+			       SPCS_GENERATIONSTATUS | 0x00001200 |
+			       0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+	snd_emu10k1x_ptr_write(chip, SPCS2, 0,
+			       chip->spdif_bits[2] = 
+			       SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC |
+			       SPCS_GENERATIONSTATUS | 0x00001200 |
+			       0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
+
+	snd_emu10k1x_ptr_write(chip, SPDIF_SELECT, 0, 0x700); // disable SPDIF
+	snd_emu10k1x_ptr_write(chip, ROUTING, 0, 0x1003F); // routing
+	snd_emu10k1x_gpio_write(chip, 0x1080); // analog mode
+
+	outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
+				  chip, &ops)) < 0) {
+		snd_emu10k1x_free(chip);
+		return err;
+	}
+	*rchip = chip;
+	return 0;
+}
+
+static void snd_emu10k1x_proc_reg_read(struct snd_info_entry *entry, 
+				       struct snd_info_buffer *buffer)
+{
+	struct emu10k1x *emu = entry->private_data;
+	unsigned long value,value1,value2;
+	unsigned long flags;
+	int i;
+
+	snd_iprintf(buffer, "Registers:\n\n");
+	for(i = 0; i < 0x20; i+=4) {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		value = inl(emu->port + i);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		snd_iprintf(buffer, "Register %02X: %08lX\n", i, value);
+	}
+	snd_iprintf(buffer, "\nRegisters\n\n");
+	for(i = 0; i <= 0x48; i++) {
+		value = snd_emu10k1x_ptr_read(emu, i, 0);
+		if(i < 0x10 || (i >= 0x20 && i < 0x40)) {
+			value1 = snd_emu10k1x_ptr_read(emu, i, 1);
+			value2 = snd_emu10k1x_ptr_read(emu, i, 2);
+			snd_iprintf(buffer, "%02X: %08lX %08lX %08lX\n", i, value, value1, value2);
+		} else {
+			snd_iprintf(buffer, "%02X: %08lX\n", i, value);
+		}
+	}
+}
+
+static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, 
+					struct snd_info_buffer *buffer)
+{
+	struct emu10k1x *emu = entry->private_data;
+	char line[64];
+	unsigned int reg, channel_id , val;
+
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+			continue;
+
+		if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2)
+			snd_emu10k1x_ptr_write(emu, reg, channel_id, val);
+	}
+}
+
+static int snd_emu10k1x_proc_init(struct emu10k1x *emu)
+{
+	struct snd_info_entry *entry;
+	
+	if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) {
+		snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read);
+		entry->c.text.write = snd_emu10k1x_proc_reg_write;
+		entry->mode |= S_IWUSR;
+		entry->private_data = emu;
+	}
+	
+	return 0;
+}
+
+#define snd_emu10k1x_shared_spdif_info	snd_ctl_boolean_mono_info
+
+static int snd_emu10k1x_shared_spdif_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.integer.value[0] = (snd_emu10k1x_ptr_read(emu, SPDIF_SELECT, 0) == 0x700) ? 0 : 1;
+
+	return 0;
+}
+
+static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+
+	val = ucontrol->value.integer.value[0] ;
+
+	if (val) {
+		// enable spdif output
+		snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x000);
+		snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x700);
+		snd_emu10k1x_gpio_write(emu, 0x1000);
+	} else {
+		// disable spdif output
+		snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x700);
+		snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x1003F);
+		snd_emu10k1x_gpio_write(emu, 0x1080);
+	}
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1x_shared_spdif =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Analog/Digital Output Jack",
+	.info =		snd_emu10k1x_shared_spdif_info,
+	.get =		snd_emu10k1x_shared_spdif_get,
+	.put =		snd_emu10k1x_shared_spdif_put
+};
+
+static int snd_emu10k1x_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_emu10k1x_spdif_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+	return 0;
+}
+
+static int snd_emu10k1x_spdif_get_mask(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0xff;
+	return 0;
+}
+
+static int snd_emu10k1x_spdif_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct emu10k1x *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	int change;
+	unsigned int val;
+
+	val = (ucontrol->value.iec958.status[0] << 0) |
+		(ucontrol->value.iec958.status[1] << 8) |
+		(ucontrol->value.iec958.status[2] << 16) |
+		(ucontrol->value.iec958.status[3] << 24);
+	change = val != emu->spdif_bits[idx];
+	if (change) {
+		snd_emu10k1x_ptr_write(emu, SPCS0 + idx, 0, val);
+		emu->spdif_bits[idx] = val;
+	}
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+	.count =	3,
+	.info =         snd_emu10k1x_spdif_info,
+	.get =          snd_emu10k1x_spdif_get_mask
+};
+
+static const struct snd_kcontrol_new snd_emu10k1x_spdif_control =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.count =	3,
+	.info =         snd_emu10k1x_spdif_info,
+	.get =          snd_emu10k1x_spdif_get,
+	.put =          snd_emu10k1x_spdif_put
+};
+
+static int snd_emu10k1x_mixer(struct emu10k1x *emu)
+{
+	int err;
+	struct snd_kcontrol *kctl;
+	struct snd_card *card = emu->card;
+
+	if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu)) == NULL)
+		return -ENOMEM;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+	if ((kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu)) == NULL)
+		return -ENOMEM;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+	if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu)) == NULL)
+		return -ENOMEM;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+
+	return 0;
+}
+
+#define EMU10K1X_MIDI_MODE_INPUT	(1<<0)
+#define EMU10K1X_MIDI_MODE_OUTPUT	(1<<1)
+
+static inline unsigned char mpu401_read(struct emu10k1x *emu, struct emu10k1x_midi *mpu, int idx)
+{
+	return (unsigned char)snd_emu10k1x_ptr_read(emu, mpu->port + idx, 0);
+}
+
+static inline void mpu401_write(struct emu10k1x *emu, struct emu10k1x_midi *mpu, int data, int idx)
+{
+	snd_emu10k1x_ptr_write(emu, mpu->port + idx, 0, data);
+}
+
+#define mpu401_write_data(emu, mpu, data)	mpu401_write(emu, mpu, data, 0)
+#define mpu401_write_cmd(emu, mpu, data)	mpu401_write(emu, mpu, data, 1)
+#define mpu401_read_data(emu, mpu)		mpu401_read(emu, mpu, 0)
+#define mpu401_read_stat(emu, mpu)		mpu401_read(emu, mpu, 1)
+
+#define mpu401_input_avail(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x80))
+#define mpu401_output_ready(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x40))
+
+#define MPU401_RESET		0xff
+#define MPU401_ENTER_UART	0x3f
+#define MPU401_ACK		0xfe
+
+static void mpu401_clear_rx(struct emu10k1x *emu, struct emu10k1x_midi *mpu)
+{
+	int timeout = 100000;
+	for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
+		mpu401_read_data(emu, mpu);
+#ifdef CONFIG_SND_DEBUG
+	if (timeout <= 0)
+		dev_err(emu->card->dev,
+			"cmd: clear rx timeout (status = 0x%x)\n",
+			mpu401_read_stat(emu, mpu));
+#endif
+}
+
+/*
+
+ */
+
+static void do_emu10k1x_midi_interrupt(struct emu10k1x *emu,
+				       struct emu10k1x_midi *midi, unsigned int status)
+{
+	unsigned char byte;
+
+	if (midi->rmidi == NULL) {
+		snd_emu10k1x_intr_disable(emu, midi->tx_enable | midi->rx_enable);
+		return;
+	}
+
+	spin_lock(&midi->input_lock);
+	if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+		if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+			mpu401_clear_rx(emu, midi);
+		} else {
+			byte = mpu401_read_data(emu, midi);
+			if (midi->substream_input)
+				snd_rawmidi_receive(midi->substream_input, &byte, 1);
+		}
+	}
+	spin_unlock(&midi->input_lock);
+
+	spin_lock(&midi->output_lock);
+	if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+		if (midi->substream_output &&
+		    snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+			mpu401_write_data(emu, midi, byte);
+		} else {
+			snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+		}
+	}
+	spin_unlock(&midi->output_lock);
+}
+
+static void snd_emu10k1x_midi_interrupt(struct emu10k1x *emu, unsigned int status)
+{
+	do_emu10k1x_midi_interrupt(emu, &emu->midi, status);
+}
+
+static int snd_emu10k1x_midi_cmd(struct emu10k1x * emu,
+				  struct emu10k1x_midi *midi, unsigned char cmd, int ack)
+{
+	unsigned long flags;
+	int timeout, ok;
+
+	spin_lock_irqsave(&midi->input_lock, flags);
+	mpu401_write_data(emu, midi, 0x00);
+	/* mpu401_clear_rx(emu, midi); */
+
+	mpu401_write_cmd(emu, midi, cmd);
+	if (ack) {
+		ok = 0;
+		timeout = 10000;
+		while (!ok && timeout-- > 0) {
+			if (mpu401_input_avail(emu, midi)) {
+				if (mpu401_read_data(emu, midi) == MPU401_ACK)
+					ok = 1;
+			}
+		}
+		if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+			ok = 1;
+	} else {
+		ok = 1;
+	}
+	spin_unlock_irqrestore(&midi->input_lock, flags);
+	if (!ok) {
+		dev_err(emu->card->dev,
+			"midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+			   cmd, emu->port,
+			   mpu401_read_stat(emu, midi),
+			   mpu401_read_data(emu, midi));
+		return 1;
+	}
+	return 0;
+}
+
+static int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+	struct emu10k1x *emu;
+	struct emu10k1x_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+	
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
+	spin_lock_irqsave(&midi->open_lock, flags);
+	midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;
+	midi->substream_input = substream;
+	if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+			goto error_out;
+		if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+			goto error_out;
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+
+error_out:
+	return -EIO;
+}
+
+static int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+	struct emu10k1x *emu;
+	struct emu10k1x_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
+	spin_lock_irqsave(&midi->open_lock, flags);
+	midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;
+	midi->substream_output = substream;
+	if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1))
+			goto error_out;
+		if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+			goto error_out;
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+
+error_out:
+	return -EIO;
+}
+
+static int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+	struct emu10k1x *emu;
+	struct emu10k1x_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+	int err = 0;
+
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
+	spin_lock_irqsave(&midi->open_lock, flags);
+	snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+	midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT;
+	midi->substream_input = NULL;
+	if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return err;
+}
+
+static int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+	struct emu10k1x *emu;
+	struct emu10k1x_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+	int err = 0;
+
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
+	spin_lock_irqsave(&midi->open_lock, flags);
+	snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+	midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT;
+	midi->substream_output = NULL;
+	if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return err;
+}
+
+static void snd_emu10k1x_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct emu10k1x *emu;
+	struct emu10k1x_midi *midi = substream->rmidi->private_data;
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return;
+
+	if (up)
+		snd_emu10k1x_intr_enable(emu, midi->rx_enable);
+	else
+		snd_emu10k1x_intr_disable(emu, midi->rx_enable);
+}
+
+static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct emu10k1x *emu;
+	struct emu10k1x_midi *midi = substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return;
+
+	if (up) {
+		int max = 4;
+		unsigned char byte;
+	
+		/* try to send some amount of bytes here before interrupts */
+		spin_lock_irqsave(&midi->output_lock, flags);
+		while (max > 0) {
+			if (mpu401_output_ready(emu, midi)) {
+				if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) ||
+				    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					/* no more data */
+					spin_unlock_irqrestore(&midi->output_lock, flags);
+					return;
+				}
+				mpu401_write_data(emu, midi, byte);
+				max--;
+			} else {
+				break;
+			}
+		}
+		spin_unlock_irqrestore(&midi->output_lock, flags);
+		snd_emu10k1x_intr_enable(emu, midi->tx_enable);
+	} else {
+		snd_emu10k1x_intr_disable(emu, midi->tx_enable);
+	}
+}
+
+/*
+
+ */
+
+static const struct snd_rawmidi_ops snd_emu10k1x_midi_output =
+{
+	.open =		snd_emu10k1x_midi_output_open,
+	.close =	snd_emu10k1x_midi_output_close,
+	.trigger =	snd_emu10k1x_midi_output_trigger,
+};
+
+static const struct snd_rawmidi_ops snd_emu10k1x_midi_input =
+{
+	.open =		snd_emu10k1x_midi_input_open,
+	.close =	snd_emu10k1x_midi_input_close,
+	.trigger =	snd_emu10k1x_midi_input_trigger,
+};
+
+static void snd_emu10k1x_midi_free(struct snd_rawmidi *rmidi)
+{
+	struct emu10k1x_midi *midi = rmidi->private_data;
+	midi->interrupt = NULL;
+	midi->rmidi = NULL;
+}
+
+static int emu10k1x_midi_init(struct emu10k1x *emu,
+			      struct emu10k1x_midi *midi, int device,
+			      char *name)
+{
+	struct snd_rawmidi *rmidi;
+	int err;
+
+	if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+		return err;
+	midi->emu = emu;
+	spin_lock_init(&midi->open_lock);
+	spin_lock_init(&midi->input_lock);
+	spin_lock_init(&midi->output_lock);
+	strcpy(rmidi->name, name);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+	                     SNDRV_RAWMIDI_INFO_INPUT |
+	                     SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = midi;
+	rmidi->private_free = snd_emu10k1x_midi_free;
+	midi->rmidi = rmidi;
+	return 0;
+}
+
+static int snd_emu10k1x_midi(struct emu10k1x *emu)
+{
+	struct emu10k1x_midi *midi = &emu->midi;
+	int err;
+
+	if ((err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)")) < 0)
+		return err;
+
+	midi->tx_enable = INTE_MIDITXENABLE;
+	midi->rx_enable = INTE_MIDIRXENABLE;
+	midi->port = MUDATA;
+	midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+	midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+	midi->interrupt = snd_emu10k1x_midi_interrupt;
+	return 0;
+}
+
+static int snd_emu10k1x_probe(struct pci_dev *pci,
+			      const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct emu10k1x *chip;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
+			   0, &card);
+	if (err < 0)
+		return err;
+
+	if ((err = snd_emu10k1x_create(card, pci, &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_emu10k1x_pcm(chip, 0)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_emu10k1x_pcm(chip, 1)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if ((err = snd_emu10k1x_pcm(chip, 2)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_emu10k1x_ac97(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_emu10k1x_mixer(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	if ((err = snd_emu10k1x_midi(chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	snd_emu10k1x_proc_init(chip);
+
+	strcpy(card->driver, "EMU10K1X");
+	strcpy(card->shortname, "Dell Sound Blaster Live!");
+	sprintf(card->longname, "%s at 0x%lx irq %i",
+		card->shortname, chip->port, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void snd_emu10k1x_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+}
+
+// PCI IDs
+static const struct pci_device_id snd_emu10k1x_ids[] = {
+	{ PCI_VDEVICE(CREATIVE, 0x0006), 0 },	/* Dell OEM version (EMU10K1) */
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
+
+// pci_driver definition
+static struct pci_driver emu10k1x_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = snd_emu10k1x_ids,
+	.probe = snd_emu10k1x_probe,
+	.remove = snd_emu10k1x_remove,
+};
+
+module_pci_driver(emu10k1x_driver);
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emufx.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emufx.c
new file mode 100644
index 0000000..5c00e35
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emufx.c
@@ -0,0 +1,2801 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for effect processor FX8010
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ *  	Added EMU 1010 support.
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/capability.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/moduleparam.h>
+#include <linux/nospec.h>
+
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/emu10k1.h>
+
+#if 0		/* for testing purposes - digital out -> capture */
+#define EMU10K1_CAPTURE_DIGITAL_OUT
+#endif
+#if 0		/* for testing purposes - set S/PDIF to AC3 output */
+#define EMU10K1_SET_AC3_IEC958
+#endif
+#if 0		/* for testing purposes - feed the front signal to Center/LFE outputs */
+#define EMU10K1_CENTER_LFE_FROM_FRONT
+#endif
+
+static bool high_res_gpr_volume;
+module_param(high_res_gpr_volume, bool, 0444);
+MODULE_PARM_DESC(high_res_gpr_volume, "GPR mixer controls use 31-bit range.");
+
+/*
+ *  Tables
+ */ 
+
+static char *fxbuses[16] = {
+	/* 0x00 */ "PCM Left",
+	/* 0x01 */ "PCM Right",
+	/* 0x02 */ "PCM Surround Left",
+	/* 0x03 */ "PCM Surround Right",
+	/* 0x04 */ "MIDI Left",
+	/* 0x05 */ "MIDI Right",
+	/* 0x06 */ "Center",
+	/* 0x07 */ "LFE",
+	/* 0x08 */ NULL,
+	/* 0x09 */ NULL,
+	/* 0x0a */ NULL,
+	/* 0x0b */ NULL,
+	/* 0x0c */ "MIDI Reverb",
+	/* 0x0d */ "MIDI Chorus",
+	/* 0x0e */ NULL,
+	/* 0x0f */ NULL
+};
+
+static char *creative_ins[16] = {
+	/* 0x00 */ "AC97 Left",
+	/* 0x01 */ "AC97 Right",
+	/* 0x02 */ "TTL IEC958 Left",
+	/* 0x03 */ "TTL IEC958 Right",
+	/* 0x04 */ "Zoom Video Left",
+	/* 0x05 */ "Zoom Video Right",
+	/* 0x06 */ "Optical IEC958 Left",
+	/* 0x07 */ "Optical IEC958 Right",
+	/* 0x08 */ "Line/Mic 1 Left",
+	/* 0x09 */ "Line/Mic 1 Right",
+	/* 0x0a */ "Coaxial IEC958 Left",
+	/* 0x0b */ "Coaxial IEC958 Right",
+	/* 0x0c */ "Line/Mic 2 Left",
+	/* 0x0d */ "Line/Mic 2 Right",
+	/* 0x0e */ NULL,
+	/* 0x0f */ NULL
+};
+
+static char *audigy_ins[16] = {
+	/* 0x00 */ "AC97 Left",
+	/* 0x01 */ "AC97 Right",
+	/* 0x02 */ "Audigy CD Left",
+	/* 0x03 */ "Audigy CD Right",
+	/* 0x04 */ "Optical IEC958 Left",
+	/* 0x05 */ "Optical IEC958 Right",
+	/* 0x06 */ NULL,
+	/* 0x07 */ NULL,
+	/* 0x08 */ "Line/Mic 2 Left",
+	/* 0x09 */ "Line/Mic 2 Right",
+	/* 0x0a */ "SPDIF Left",
+	/* 0x0b */ "SPDIF Right",
+	/* 0x0c */ "Aux2 Left",
+	/* 0x0d */ "Aux2 Right",
+	/* 0x0e */ NULL,
+	/* 0x0f */ NULL
+};
+
+static char *creative_outs[32] = {
+	/* 0x00 */ "AC97 Left",
+	/* 0x01 */ "AC97 Right",
+	/* 0x02 */ "Optical IEC958 Left",
+	/* 0x03 */ "Optical IEC958 Right",
+	/* 0x04 */ "Center",
+	/* 0x05 */ "LFE",
+	/* 0x06 */ "Headphone Left",
+	/* 0x07 */ "Headphone Right",
+	/* 0x08 */ "Surround Left",
+	/* 0x09 */ "Surround Right",
+	/* 0x0a */ "PCM Capture Left",
+	/* 0x0b */ "PCM Capture Right",
+	/* 0x0c */ "MIC Capture",
+	/* 0x0d */ "AC97 Surround Left",
+	/* 0x0e */ "AC97 Surround Right",
+	/* 0x0f */ NULL,
+	/* 0x10 */ NULL,
+	/* 0x11 */ "Analog Center",
+	/* 0x12 */ "Analog LFE",
+	/* 0x13 */ NULL,
+	/* 0x14 */ NULL,
+	/* 0x15 */ NULL,
+	/* 0x16 */ NULL,
+	/* 0x17 */ NULL,
+	/* 0x18 */ NULL,
+	/* 0x19 */ NULL,
+	/* 0x1a */ NULL,
+	/* 0x1b */ NULL,
+	/* 0x1c */ NULL,
+	/* 0x1d */ NULL,
+	/* 0x1e */ NULL,
+	/* 0x1f */ NULL,
+};
+
+static char *audigy_outs[32] = {
+	/* 0x00 */ "Digital Front Left",
+	/* 0x01 */ "Digital Front Right",
+	/* 0x02 */ "Digital Center",
+	/* 0x03 */ "Digital LEF",
+	/* 0x04 */ "Headphone Left",
+	/* 0x05 */ "Headphone Right",
+	/* 0x06 */ "Digital Rear Left",
+	/* 0x07 */ "Digital Rear Right",
+	/* 0x08 */ "Front Left",
+	/* 0x09 */ "Front Right",
+	/* 0x0a */ "Center",
+	/* 0x0b */ "LFE",
+	/* 0x0c */ NULL,
+	/* 0x0d */ NULL,
+	/* 0x0e */ "Rear Left",
+	/* 0x0f */ "Rear Right",
+	/* 0x10 */ "AC97 Front Left",
+	/* 0x11 */ "AC97 Front Right",
+	/* 0x12 */ "ADC Caputre Left",
+	/* 0x13 */ "ADC Capture Right",
+	/* 0x14 */ NULL,
+	/* 0x15 */ NULL,
+	/* 0x16 */ NULL,
+	/* 0x17 */ NULL,
+	/* 0x18 */ NULL,
+	/* 0x19 */ NULL,
+	/* 0x1a */ NULL,
+	/* 0x1b */ NULL,
+	/* 0x1c */ NULL,
+	/* 0x1d */ NULL,
+	/* 0x1e */ NULL,
+	/* 0x1f */ NULL,
+};
+
+static const u32 bass_table[41][5] = {
+	{ 0x3e4f844f, 0x84ed4cc3, 0x3cc69927, 0x7b03553a, 0xc4da8486 },
+	{ 0x3e69a17a, 0x84c280fb, 0x3cd77cd4, 0x7b2f2a6f, 0xc4b08d1d },
+	{ 0x3e82ff42, 0x849991d5, 0x3ce7466b, 0x7b5917c6, 0xc48863ee },
+	{ 0x3e9bab3c, 0x847267f0, 0x3cf5ffe8, 0x7b813560, 0xc461f22c },
+	{ 0x3eb3b275, 0x844ced29, 0x3d03b295, 0x7ba79a1c, 0xc43d223b },
+	{ 0x3ecb2174, 0x84290c8b, 0x3d106714, 0x7bcc5ba3, 0xc419dfa5 },
+	{ 0x3ee2044b, 0x8406b244, 0x3d1c2561, 0x7bef8e77, 0xc3f8170f },
+	{ 0x3ef86698, 0x83e5cb96, 0x3d26f4d8, 0x7c114600, 0xc3d7b625 },
+	{ 0x3f0e5390, 0x83c646c9, 0x3d30dc39, 0x7c319498, 0xc3b8ab97 },
+	{ 0x3f23d60b, 0x83a81321, 0x3d39e1af, 0x7c508b9c, 0xc39ae704 },
+	{ 0x3f38f884, 0x838b20d2, 0x3d420ad2, 0x7c6e3b75, 0xc37e58f1 },
+	{ 0x3f4dc52c, 0x836f60ef, 0x3d495cab, 0x7c8ab3a6, 0xc362f2be },
+	{ 0x3f6245e8, 0x8354c565, 0x3d4fdbb8, 0x7ca602d6, 0xc348a69b },
+	{ 0x3f76845f, 0x833b40ec, 0x3d558bf0, 0x7cc036df, 0xc32f677c },
+	{ 0x3f8a8a03, 0x8322c6fb, 0x3d5a70c4, 0x7cd95cd7, 0xc317290b },
+	{ 0x3f9e6014, 0x830b4bc3, 0x3d5e8d25, 0x7cf1811a, 0xc2ffdfa5 },
+	{ 0x3fb20fae, 0x82f4c420, 0x3d61e37f, 0x7d08af56, 0xc2e9804a },
+	{ 0x3fc5a1cc, 0x82df2592, 0x3d6475c3, 0x7d1ef294, 0xc2d40096 },
+	{ 0x3fd91f55, 0x82ca6632, 0x3d664564, 0x7d345541, 0xc2bf56b9 },
+	{ 0x3fec9120, 0x82b67cac, 0x3d675356, 0x7d48e138, 0xc2ab796e },
+	{ 0x40000000, 0x82a36037, 0x3d67a012, 0x7d5c9fc9, 0xc2985fee },
+	{ 0x401374c7, 0x8291088a, 0x3d672b93, 0x7d6f99c3, 0xc28601f2 },
+	{ 0x4026f857, 0x827f6dd7, 0x3d65f559, 0x7d81d77c, 0xc27457a3 },
+	{ 0x403a939f, 0x826e88c5, 0x3d63fc63, 0x7d9360d4, 0xc2635996 },
+	{ 0x404e4faf, 0x825e5266, 0x3d613f32, 0x7da43d42, 0xc25300c6 },
+	{ 0x406235ba, 0x824ec434, 0x3d5dbbc3, 0x7db473d7, 0xc243468e },
+	{ 0x40764f1f, 0x823fd80c, 0x3d596f8f, 0x7dc40b44, 0xc23424a2 },
+	{ 0x408aa576, 0x82318824, 0x3d545787, 0x7dd309e2, 0xc2259509 },
+	{ 0x409f4296, 0x8223cf0b, 0x3d4e7012, 0x7de175b5, 0xc2179218 },
+	{ 0x40b430a0, 0x8216a7a1, 0x3d47b505, 0x7def5475, 0xc20a1670 },
+	{ 0x40c97a0a, 0x820a0d12, 0x3d4021a1, 0x7dfcab8d, 0xc1fd1cf5 },
+	{ 0x40df29a6, 0x81fdfad6, 0x3d37b08d, 0x7e098028, 0xc1f0a0ca },
+	{ 0x40f54ab1, 0x81f26ca9, 0x3d2e5bd1, 0x7e15d72b, 0xc1e49d52 },
+	{ 0x410be8da, 0x81e75e89, 0x3d241cce, 0x7e21b544, 0xc1d90e24 },
+	{ 0x41231051, 0x81dcccb3, 0x3d18ec37, 0x7e2d1ee6, 0xc1cdef10 },
+	{ 0x413acdd0, 0x81d2b39e, 0x3d0cc20a, 0x7e38184e, 0xc1c33c13 },
+	{ 0x41532ea7, 0x81c90ffb, 0x3cff9585, 0x7e42a58b, 0xc1b8f15a },
+	{ 0x416c40cd, 0x81bfdeb2, 0x3cf15d21, 0x7e4cca7c, 0xc1af0b3f },
+	{ 0x418612ea, 0x81b71cdc, 0x3ce20e85, 0x7e568ad3, 0xc1a58640 },
+	{ 0x41a0b465, 0x81aec7c5, 0x3cd19e7c, 0x7e5fea1e, 0xc19c5f03 },
+	{ 0x41bc3573, 0x81a6dcea, 0x3cc000e9, 0x7e68ebc2, 0xc1939250 }
+};
+
+static const u32 treble_table[41][5] = {
+	{ 0x0125cba9, 0xfed5debd, 0x00599b6c, 0x0d2506da, 0xfa85b354 },
+	{ 0x0142f67e, 0xfeb03163, 0x0066cd0f, 0x0d14c69d, 0xfa914473 },
+	{ 0x016328bd, 0xfe860158, 0x0075b7f2, 0x0d03eb27, 0xfa9d32d2 },
+	{ 0x0186b438, 0xfe56c982, 0x00869234, 0x0cf27048, 0xfaa97fca },
+	{ 0x01adf358, 0xfe21f5fe, 0x00999842, 0x0ce051c2, 0xfab62ca5 },
+	{ 0x01d949fa, 0xfde6e287, 0x00af0d8d, 0x0ccd8b4a, 0xfac33aa7 },
+	{ 0x02092669, 0xfda4d8bf, 0x00c73d4c, 0x0cba1884, 0xfad0ab07 },
+	{ 0x023e0268, 0xfd5b0e4a, 0x00e27b54, 0x0ca5f509, 0xfade7ef2 },
+	{ 0x0278645c, 0xfd08a2b0, 0x01012509, 0x0c911c63, 0xfaecb788 },
+	{ 0x02b8e091, 0xfcac9d1a, 0x0123a262, 0x0c7b8a14, 0xfafb55df },
+	{ 0x03001a9a, 0xfc45e9ce, 0x014a6709, 0x0c65398f, 0xfb0a5aff },
+	{ 0x034ec6d7, 0xfbd3576b, 0x0175f397, 0x0c4e2643, 0xfb19c7e4 },
+	{ 0x03a5ac15, 0xfb5393ee, 0x01a6d6ed, 0x0c364b94, 0xfb299d7c },
+	{ 0x0405a562, 0xfac52968, 0x01ddafae, 0x0c1da4e2, 0xfb39dca5 },
+	{ 0x046fa3fe, 0xfa267a66, 0x021b2ddd, 0x0c042d8d, 0xfb4a8631 },
+	{ 0x04e4b17f, 0xf975be0f, 0x0260149f, 0x0be9e0f2, 0xfb5b9ae0 },
+	{ 0x0565f220, 0xf8b0fbe5, 0x02ad3c29, 0x0bceba73, 0xfb6d1b60 },
+	{ 0x05f4a745, 0xf7d60722, 0x030393d4, 0x0bb2b578, 0xfb7f084d },
+	{ 0x06923236, 0xf6e279bd, 0x03642465, 0x0b95cd75, 0xfb916233 },
+	{ 0x07401713, 0xf5d3aef9, 0x03d01283, 0x0b77fded, 0xfba42984 },
+	{ 0x08000000, 0xf4a6bd88, 0x0448a161, 0x0b594278, 0xfbb75e9f },
+	{ 0x08d3c097, 0xf3587131, 0x04cf35a4, 0x0b3996c9, 0xfbcb01cb },
+	{ 0x09bd59a2, 0xf1e543f9, 0x05655880, 0x0b18f6b2, 0xfbdf1333 },
+	{ 0x0abefd0f, 0xf04956ca, 0x060cbb12, 0x0af75e2c, 0xfbf392e8 },
+	{ 0x0bdb123e, 0xee806984, 0x06c739fe, 0x0ad4c962, 0xfc0880dd },
+	{ 0x0d143a94, 0xec85d287, 0x0796e150, 0x0ab134b0, 0xfc1ddce5 },
+	{ 0x0e6d5664, 0xea547598, 0x087df0a0, 0x0a8c9cb6, 0xfc33a6ad },
+	{ 0x0fe98a2a, 0xe7e6ba35, 0x097edf83, 0x0a66fe5b, 0xfc49ddc2 },
+	{ 0x118c4421, 0xe536813a, 0x0a9c6248, 0x0a4056d7, 0xfc608185 },
+	{ 0x1359422e, 0xe23d19eb, 0x0bd96efb, 0x0a18a3bf, 0xfc77912c },
+	{ 0x1554982b, 0xdef33645, 0x0d3942bd, 0x09efe312, 0xfc8f0bc1 },
+	{ 0x1782b68a, 0xdb50deb1, 0x0ebf676d, 0x09c6133f, 0xfca6f019 },
+	{ 0x19e8715d, 0xd74d64fd, 0x106fb999, 0x099b3337, 0xfcbf3cd6 },
+	{ 0x1c8b07b8, 0xd2df56ab, 0x124e6ec8, 0x096f4274, 0xfcd7f060 },
+	{ 0x1f702b6d, 0xcdfc6e92, 0x14601c10, 0x0942410b, 0xfcf108e5 },
+	{ 0x229e0933, 0xc89985cd, 0x16a9bcfa, 0x09142fb5, 0xfd0a8451 },
+	{ 0x261b5118, 0xc2aa8409, 0x1930bab6, 0x08e50fdc, 0xfd24604d },
+	{ 0x29ef3f5d, 0xbc224f28, 0x1bfaf396, 0x08b4e3aa, 0xfd3e9a3b },
+	{ 0x2e21a59b, 0xb4f2ba46, 0x1f0ec2d6, 0x0883ae15, 0xfd592f33 },
+	{ 0x32baf44b, 0xad0c7429, 0x227308a3, 0x085172eb, 0xfd741bfd },
+	{ 0x37c4448b, 0xa45ef51d, 0x262f3267, 0x081e36dc, 0xfd8f5d14 }
+};
+
+/* dB gain = (float) 20 * log10( float(db_table_value) / 0x8000000 ) */
+static const u32 db_table[101] = {
+	0x00000000, 0x01571f82, 0x01674b41, 0x01783a1b, 0x0189f540,
+	0x019c8651, 0x01aff763, 0x01c45306, 0x01d9a446, 0x01eff6b8,
+	0x0207567a, 0x021fd03d, 0x0239714c, 0x02544792, 0x027061a1,
+	0x028dcebb, 0x02ac9edc, 0x02cce2bf, 0x02eeabe8, 0x03120cb0,
+	0x0337184e, 0x035de2df, 0x03868173, 0x03b10a18, 0x03dd93e9,
+	0x040c3713, 0x043d0cea, 0x04702ff3, 0x04a5bbf2, 0x04ddcdfb,
+	0x0518847f, 0x0555ff62, 0x05966005, 0x05d9c95d, 0x06206005,
+	0x066a4a52, 0x06b7b067, 0x0708bc4c, 0x075d9a01, 0x07b6779d,
+	0x08138561, 0x0874f5d5, 0x08dafde1, 0x0945d4ed, 0x09b5b4fd,
+	0x0a2adad1, 0x0aa58605, 0x0b25f936, 0x0bac7a24, 0x0c3951d8,
+	0x0ccccccc, 0x0d673b17, 0x0e08f093, 0x0eb24510, 0x0f639481,
+	0x101d3f2d, 0x10dfa9e6, 0x11ab3e3f, 0x12806ac3, 0x135fa333,
+	0x144960c5, 0x153e2266, 0x163e6cfe, 0x174acbb7, 0x1863d04d,
+	0x198a1357, 0x1abe349f, 0x1c00db77, 0x1d52b712, 0x1eb47ee6,
+	0x2026f30f, 0x21aadcb6, 0x23410e7e, 0x24ea64f9, 0x26a7c71d,
+	0x287a26c4, 0x2a62812c, 0x2c61df84, 0x2e795779, 0x30aa0bcf,
+	0x32f52cfe, 0x355bf9d8, 0x37dfc033, 0x3a81dda4, 0x3d43c038,
+	0x4026e73c, 0x432ce40f, 0x46575af8, 0x49a8040f, 0x4d20ac2a,
+	0x50c335d3, 0x54919a57, 0x588dead1, 0x5cba514a, 0x611911ea,
+	0x65ac8c2f, 0x6a773c39, 0x6f7bbc23, 0x74bcc56c, 0x7a3d3272,
+	0x7fffffff,
+};
+
+/* EMU10k1/EMU10k2 DSP control db gain */
+static const DECLARE_TLV_DB_SCALE(snd_emu10k1_db_scale1, -4000, 40, 1);
+static const DECLARE_TLV_DB_LINEAR(snd_emu10k1_db_linear, TLV_DB_GAIN_MUTE, 0);
+
+/* EMU10K1 bass/treble db gain */
+static const DECLARE_TLV_DB_SCALE(snd_emu10k1_bass_treble_db_scale, -1200, 60, 0);
+
+static const u32 onoff_table[2] = {
+	0x00000000, 0x00000001
+};
+
+/*
+ *   controls
+ */
+
+static int snd_emu10k1_gpr_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_emu10k1_fx8010_ctl *ctl =
+		(struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
+
+	if (ctl->min == 0 && ctl->max == 1)
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	else
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = ctl->vcount;
+	uinfo->value.integer.min = ctl->min;
+	uinfo->value.integer.max = ctl->max;
+	return 0;
+}
+
+static int snd_emu10k1_gpr_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_fx8010_ctl *ctl =
+		(struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
+	unsigned long flags;
+	unsigned int i;
+	
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (i = 0; i < ctl->vcount; i++)
+		ucontrol->value.integer.value[i] = ctl->value[i];
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_gpr_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_fx8010_ctl *ctl =
+		(struct snd_emu10k1_fx8010_ctl *) kcontrol->private_value;
+	unsigned long flags;
+	unsigned int nval, val;
+	unsigned int i, j;
+	int change = 0;
+	
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (i = 0; i < ctl->vcount; i++) {
+		nval = ucontrol->value.integer.value[i];
+		if (nval < ctl->min)
+			nval = ctl->min;
+		if (nval > ctl->max)
+			nval = ctl->max;
+		if (nval != ctl->value[i])
+			change = 1;
+		val = ctl->value[i] = nval;
+		switch (ctl->translation) {
+		case EMU10K1_GPR_TRANSLATION_NONE:
+			snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, val);
+			break;
+		case EMU10K1_GPR_TRANSLATION_TABLE100:
+			snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, db_table[val]);
+			break;
+		case EMU10K1_GPR_TRANSLATION_BASS:
+			if ((ctl->count % 5) != 0 || (ctl->count / 5) != ctl->vcount) {
+				change = -EIO;
+				goto __error;
+			}
+			for (j = 0; j < 5; j++)
+				snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, bass_table[val][j]);
+			break;
+		case EMU10K1_GPR_TRANSLATION_TREBLE:
+			if ((ctl->count % 5) != 0 || (ctl->count / 5) != ctl->vcount) {
+				change = -EIO;
+				goto __error;
+			}
+			for (j = 0; j < 5; j++)
+				snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, treble_table[val][j]);
+			break;
+		case EMU10K1_GPR_TRANSLATION_ONOFF:
+			snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, onoff_table[val]);
+			break;
+		}
+	}
+      __error:
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+/*
+ *   Interrupt handler
+ */
+
+static void snd_emu10k1_fx8010_interrupt(struct snd_emu10k1 *emu)
+{
+	struct snd_emu10k1_fx8010_irq *irq, *nirq;
+
+	irq = emu->fx8010.irq_handlers;
+	while (irq) {
+		nirq = irq->next;	/* irq ptr can be removed from list */
+		if (snd_emu10k1_ptr_read(emu, emu->gpr_base + irq->gpr_running, 0) & 0xffff0000) {
+			if (irq->handler)
+				irq->handler(emu, irq->private_data);
+			snd_emu10k1_ptr_write(emu, emu->gpr_base + irq->gpr_running, 0, 1);
+		}
+		irq = nirq;
+	}
+}
+
+int snd_emu10k1_fx8010_register_irq_handler(struct snd_emu10k1 *emu,
+					    snd_fx8010_irq_handler_t *handler,
+					    unsigned char gpr_running,
+					    void *private_data,
+					    struct snd_emu10k1_fx8010_irq **r_irq)
+{
+	struct snd_emu10k1_fx8010_irq *irq;
+	unsigned long flags;
+	
+	irq = kmalloc(sizeof(*irq), GFP_ATOMIC);
+	if (irq == NULL)
+		return -ENOMEM;
+	irq->handler = handler;
+	irq->gpr_running = gpr_running;
+	irq->private_data = private_data;
+	irq->next = NULL;
+	spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+	if (emu->fx8010.irq_handlers == NULL) {
+		emu->fx8010.irq_handlers = irq;
+		emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt;
+		snd_emu10k1_intr_enable(emu, INTE_FXDSPENABLE);
+	} else {
+		irq->next = emu->fx8010.irq_handlers;
+		emu->fx8010.irq_handlers = irq;
+	}
+	spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
+	if (r_irq)
+		*r_irq = irq;
+	return 0;
+}
+
+int snd_emu10k1_fx8010_unregister_irq_handler(struct snd_emu10k1 *emu,
+					      struct snd_emu10k1_fx8010_irq *irq)
+{
+	struct snd_emu10k1_fx8010_irq *tmp;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&emu->fx8010.irq_lock, flags);
+	if ((tmp = emu->fx8010.irq_handlers) == irq) {
+		emu->fx8010.irq_handlers = tmp->next;
+		if (emu->fx8010.irq_handlers == NULL) {
+			snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
+			emu->dsp_interrupt = NULL;
+		}
+	} else {
+		while (tmp && tmp->next != irq)
+			tmp = tmp->next;
+		if (tmp)
+			tmp->next = tmp->next->next;
+	}
+	spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags);
+	kfree(irq);
+	return 0;
+}
+
+/*************************************************************************
+ * EMU10K1 effect manager
+ *************************************************************************/
+
+static void snd_emu10k1_write_op(struct snd_emu10k1_fx8010_code *icode,
+				 unsigned int *ptr,
+				 u32 op, u32 r, u32 a, u32 x, u32 y)
+{
+	u_int32_t *code;
+	if (snd_BUG_ON(*ptr >= 512))
+		return;
+	code = (u_int32_t __force *)icode->code + (*ptr) * 2;
+	set_bit(*ptr, icode->code_valid);
+	code[0] = ((x & 0x3ff) << 10) | (y & 0x3ff);
+	code[1] = ((op & 0x0f) << 20) | ((r & 0x3ff) << 10) | (a & 0x3ff);
+	(*ptr)++;
+}
+
+#define OP(icode, ptr, op, r, a, x, y) \
+	snd_emu10k1_write_op(icode, ptr, op, r, a, x, y)
+
+static void snd_emu10k1_audigy_write_op(struct snd_emu10k1_fx8010_code *icode,
+					unsigned int *ptr,
+					u32 op, u32 r, u32 a, u32 x, u32 y)
+{
+	u_int32_t *code;
+	if (snd_BUG_ON(*ptr >= 1024))
+		return;
+	code = (u_int32_t __force *)icode->code + (*ptr) * 2;
+	set_bit(*ptr, icode->code_valid);
+	code[0] = ((x & 0x7ff) << 12) | (y & 0x7ff);
+	code[1] = ((op & 0x0f) << 24) | ((r & 0x7ff) << 12) | (a & 0x7ff);
+	(*ptr)++;
+}
+
+#define A_OP(icode, ptr, op, r, a, x, y) \
+	snd_emu10k1_audigy_write_op(icode, ptr, op, r, a, x, y)
+
+static void snd_emu10k1_efx_write(struct snd_emu10k1 *emu, unsigned int pc, unsigned int data)
+{
+	pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+	snd_emu10k1_ptr_write(emu, pc, 0, data);
+}
+
+unsigned int snd_emu10k1_efx_read(struct snd_emu10k1 *emu, unsigned int pc)
+{
+	pc += emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+	return snd_emu10k1_ptr_read(emu, pc, 0);
+}
+
+static int snd_emu10k1_gpr_poke(struct snd_emu10k1 *emu,
+				struct snd_emu10k1_fx8010_code *icode,
+				bool in_kernel)
+{
+	int gpr;
+	u32 val;
+
+	for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
+		if (!test_bit(gpr, icode->gpr_valid))
+			continue;
+		if (in_kernel)
+			val = *(u32 *)&icode->gpr_map[gpr];
+		else if (get_user(val, &icode->gpr_map[gpr]))
+			return -EFAULT;
+		snd_emu10k1_ptr_write(emu, emu->gpr_base + gpr, 0, val);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_gpr_peek(struct snd_emu10k1 *emu,
+				struct snd_emu10k1_fx8010_code *icode)
+{
+	int gpr;
+	u32 val;
+
+	for (gpr = 0; gpr < (emu->audigy ? 0x200 : 0x100); gpr++) {
+		set_bit(gpr, icode->gpr_valid);
+		val = snd_emu10k1_ptr_read(emu, emu->gpr_base + gpr, 0);
+		if (put_user(val, &icode->gpr_map[gpr]))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+static int snd_emu10k1_tram_poke(struct snd_emu10k1 *emu,
+				 struct snd_emu10k1_fx8010_code *icode,
+				 bool in_kernel)
+{
+	int tram;
+	u32 addr, val;
+
+	for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
+		if (!test_bit(tram, icode->tram_valid))
+			continue;
+		if (in_kernel) {
+			val = *(u32 *)&icode->tram_data_map[tram];
+			addr = *(u32 *)&icode->tram_addr_map[tram];
+		} else {
+			if (get_user(val, &icode->tram_data_map[tram]) ||
+			    get_user(addr, &icode->tram_addr_map[tram]))
+				return -EFAULT;
+		}
+		snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + tram, 0, val);
+		if (!emu->audigy) {
+			snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr);
+		} else {
+			snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + tram, 0, addr << 12);
+			snd_emu10k1_ptr_write(emu, A_TANKMEMCTLREGBASE + tram, 0, addr >> 20);
+		}
+	}
+	return 0;
+}
+
+static int snd_emu10k1_tram_peek(struct snd_emu10k1 *emu,
+				 struct snd_emu10k1_fx8010_code *icode)
+{
+	int tram;
+	u32 val, addr;
+
+	memset(icode->tram_valid, 0, sizeof(icode->tram_valid));
+	for (tram = 0; tram < (emu->audigy ? 0x100 : 0xa0); tram++) {
+		set_bit(tram, icode->tram_valid);
+		val = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + tram, 0);
+		if (!emu->audigy) {
+			addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0);
+		} else {
+			addr = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + tram, 0) >> 12;
+			addr |= snd_emu10k1_ptr_read(emu, A_TANKMEMCTLREGBASE + tram, 0) << 20;
+		}
+		if (put_user(val, &icode->tram_data_map[tram]) ||
+		    put_user(addr, &icode->tram_addr_map[tram]))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+static int snd_emu10k1_code_poke(struct snd_emu10k1 *emu,
+				 struct snd_emu10k1_fx8010_code *icode,
+				 bool in_kernel)
+{
+	u32 pc, lo, hi;
+
+	for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
+		if (!test_bit(pc / 2, icode->code_valid))
+			continue;
+		if (in_kernel) {
+			lo = *(u32 *)&icode->code[pc + 0];
+			hi = *(u32 *)&icode->code[pc + 1];
+		} else {
+			if (get_user(lo, &icode->code[pc + 0]) ||
+			    get_user(hi, &icode->code[pc + 1]))
+				return -EFAULT;
+		}
+		snd_emu10k1_efx_write(emu, pc + 0, lo);
+		snd_emu10k1_efx_write(emu, pc + 1, hi);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_code_peek(struct snd_emu10k1 *emu,
+				 struct snd_emu10k1_fx8010_code *icode)
+{
+	u32 pc;
+
+	memset(icode->code_valid, 0, sizeof(icode->code_valid));
+	for (pc = 0; pc < (emu->audigy ? 2*1024 : 2*512); pc += 2) {
+		set_bit(pc / 2, icode->code_valid);
+		if (put_user(snd_emu10k1_efx_read(emu, pc + 0), &icode->code[pc + 0]))
+			return -EFAULT;
+		if (put_user(snd_emu10k1_efx_read(emu, pc + 1), &icode->code[pc + 1]))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+static struct snd_emu10k1_fx8010_ctl *
+snd_emu10k1_look_for_ctl(struct snd_emu10k1 *emu, struct snd_ctl_elem_id *id)
+{
+	struct snd_emu10k1_fx8010_ctl *ctl;
+	struct snd_kcontrol *kcontrol;
+
+	list_for_each_entry(ctl, &emu->fx8010.gpr_ctl, list) {
+		kcontrol = ctl->kcontrol;
+		if (kcontrol->id.iface == id->iface &&
+		    !strcmp(kcontrol->id.name, id->name) &&
+		    kcontrol->id.index == id->index)
+			return ctl;
+	}
+	return NULL;
+}
+
+#define MAX_TLV_SIZE	256
+
+static unsigned int *copy_tlv(const unsigned int __user *_tlv, bool in_kernel)
+{
+	unsigned int data[2];
+	unsigned int *tlv;
+
+	if (!_tlv)
+		return NULL;
+	if (in_kernel)
+		memcpy(data, (void *)_tlv, sizeof(data));
+	else if (copy_from_user(data, _tlv, sizeof(data)))
+		return NULL;
+	if (data[1] >= MAX_TLV_SIZE)
+		return NULL;
+	tlv = kmalloc(data[1] + sizeof(data), GFP_KERNEL);
+	if (!tlv)
+		return NULL;
+	memcpy(tlv, data, sizeof(data));
+	if (in_kernel) {
+		memcpy(tlv + 2, (void *)(_tlv + 2),  data[1]);
+	} else if (copy_from_user(tlv + 2, _tlv + 2, data[1])) {
+		kfree(tlv);
+		return NULL;
+	}
+	return tlv;
+}
+
+static int copy_gctl(struct snd_emu10k1 *emu,
+		     struct snd_emu10k1_fx8010_control_gpr *gctl,
+		     struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
+		     int idx, bool in_kernel)
+{
+	struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+
+	if (emu->support_tlv) {
+		if (in_kernel)
+			memcpy(gctl, (void *)&_gctl[idx], sizeof(*gctl));
+		else if (copy_from_user(gctl, &_gctl[idx], sizeof(*gctl)))
+			return -EFAULT;
+		return 0;
+	}
+
+	octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
+	if (in_kernel)
+		memcpy(gctl, (void *)&octl[idx], sizeof(*octl));
+	else if (copy_from_user(gctl, &octl[idx], sizeof(*octl)))
+		return -EFAULT;
+	gctl->tlv = NULL;
+	return 0;
+}
+
+static int copy_gctl_to_user(struct snd_emu10k1 *emu,
+		     struct snd_emu10k1_fx8010_control_gpr __user *_gctl,
+		     struct snd_emu10k1_fx8010_control_gpr *gctl,
+		     int idx)
+{
+	struct snd_emu10k1_fx8010_control_old_gpr __user *octl;
+
+	if (emu->support_tlv)
+		return copy_to_user(&_gctl[idx], gctl, sizeof(*gctl));
+	
+	octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)_gctl;
+	return copy_to_user(&octl[idx], gctl, sizeof(*octl));
+}
+
+static int snd_emu10k1_verify_controls(struct snd_emu10k1 *emu,
+				       struct snd_emu10k1_fx8010_code *icode,
+				       bool in_kernel)
+{
+	unsigned int i;
+	struct snd_ctl_elem_id __user *_id;
+	struct snd_ctl_elem_id id;
+	struct snd_emu10k1_fx8010_control_gpr *gctl;
+	int err;
+	
+	for (i = 0, _id = icode->gpr_del_controls;
+	     i < icode->gpr_del_control_count; i++, _id++) {
+		if (in_kernel)
+			id = *(struct snd_ctl_elem_id *)_id;
+		else if (copy_from_user(&id, _id, sizeof(id)))
+	     		return -EFAULT;
+		if (snd_emu10k1_look_for_ctl(emu, &id) == NULL)
+			return -ENOENT;
+	}
+	gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
+	if (! gctl)
+		return -ENOMEM;
+	err = 0;
+	for (i = 0; i < icode->gpr_add_control_count; i++) {
+		if (copy_gctl(emu, gctl, icode->gpr_add_controls, i,
+			      in_kernel)) {
+			err = -EFAULT;
+			goto __error;
+		}
+		if (snd_emu10k1_look_for_ctl(emu, &gctl->id))
+			continue;
+		down_read(&emu->card->controls_rwsem);
+		if (snd_ctl_find_id(emu->card, &gctl->id) != NULL) {
+			up_read(&emu->card->controls_rwsem);
+			err = -EEXIST;
+			goto __error;
+		}
+		up_read(&emu->card->controls_rwsem);
+		if (gctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+		    gctl->id.iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+			err = -EINVAL;
+			goto __error;
+		}
+	}
+	for (i = 0; i < icode->gpr_list_control_count; i++) {
+	     	/* FIXME: we need to check the WRITE access */
+		if (copy_gctl(emu, gctl, icode->gpr_list_controls, i,
+			      in_kernel)) {
+			err = -EFAULT;
+			goto __error;
+		}
+	}
+ __error:
+	kfree(gctl);
+	return err;
+}
+
+static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl)
+{
+	struct snd_emu10k1_fx8010_ctl *ctl;
+	
+	ctl = (struct snd_emu10k1_fx8010_ctl *) kctl->private_value;
+	kctl->private_value = 0;
+	list_del(&ctl->list);
+	kfree(ctl);
+	kfree(kctl->tlv.p);
+}
+
+static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu,
+				    struct snd_emu10k1_fx8010_code *icode,
+				    bool in_kernel)
+{
+	unsigned int i, j;
+	struct snd_emu10k1_fx8010_control_gpr *gctl;
+	struct snd_emu10k1_fx8010_ctl *ctl, *nctl;
+	struct snd_kcontrol_new knew;
+	struct snd_kcontrol *kctl;
+	struct snd_ctl_elem_value *val;
+	int err = 0;
+
+	val = kmalloc(sizeof(*val), GFP_KERNEL);
+	gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
+	nctl = kmalloc(sizeof(*nctl), GFP_KERNEL);
+	if (!val || !gctl || !nctl) {
+		err = -ENOMEM;
+		goto __error;
+	}
+
+	for (i = 0; i < icode->gpr_add_control_count; i++) {
+		if (copy_gctl(emu, gctl, icode->gpr_add_controls, i,
+			      in_kernel)) {
+			err = -EFAULT;
+			goto __error;
+		}
+		if (gctl->id.iface != SNDRV_CTL_ELEM_IFACE_MIXER &&
+		    gctl->id.iface != SNDRV_CTL_ELEM_IFACE_PCM) {
+			err = -EINVAL;
+			goto __error;
+		}
+		if (! gctl->id.name[0]) {
+			err = -EINVAL;
+			goto __error;
+		}
+		ctl = snd_emu10k1_look_for_ctl(emu, &gctl->id);
+		memset(&knew, 0, sizeof(knew));
+		knew.iface = gctl->id.iface;
+		knew.name = gctl->id.name;
+		knew.index = gctl->id.index;
+		knew.device = gctl->id.device;
+		knew.subdevice = gctl->id.subdevice;
+		knew.info = snd_emu10k1_gpr_ctl_info;
+		knew.tlv.p = copy_tlv(gctl->tlv, in_kernel);
+		if (knew.tlv.p)
+			knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+				SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+		knew.get = snd_emu10k1_gpr_ctl_get;
+		knew.put = snd_emu10k1_gpr_ctl_put;
+		memset(nctl, 0, sizeof(*nctl));
+		nctl->vcount = gctl->vcount;
+		nctl->count = gctl->count;
+		for (j = 0; j < 32; j++) {
+			nctl->gpr[j] = gctl->gpr[j];
+			nctl->value[j] = ~gctl->value[j];	/* inverted, we want to write new value in gpr_ctl_put() */
+			val->value.integer.value[j] = gctl->value[j];
+		}
+		nctl->min = gctl->min;
+		nctl->max = gctl->max;
+		nctl->translation = gctl->translation;
+		if (ctl == NULL) {
+			ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
+			if (ctl == NULL) {
+				err = -ENOMEM;
+				kfree(knew.tlv.p);
+				goto __error;
+			}
+			knew.private_value = (unsigned long)ctl;
+			*ctl = *nctl;
+			if ((err = snd_ctl_add(emu->card, kctl = snd_ctl_new1(&knew, emu))) < 0) {
+				kfree(ctl);
+				kfree(knew.tlv.p);
+				goto __error;
+			}
+			kctl->private_free = snd_emu10k1_ctl_private_free;
+			ctl->kcontrol = kctl;
+			list_add_tail(&ctl->list, &emu->fx8010.gpr_ctl);
+		} else {
+			/* overwrite */
+			nctl->list = ctl->list;
+			nctl->kcontrol = ctl->kcontrol;
+			*ctl = *nctl;
+			snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE |
+			                          SNDRV_CTL_EVENT_MASK_INFO, &ctl->kcontrol->id);
+		}
+		snd_emu10k1_gpr_ctl_put(ctl->kcontrol, val);
+	}
+      __error:
+	kfree(nctl);
+	kfree(gctl);
+	kfree(val);
+	return err;
+}
+
+static int snd_emu10k1_del_controls(struct snd_emu10k1 *emu,
+				    struct snd_emu10k1_fx8010_code *icode,
+				    bool in_kernel)
+{
+	unsigned int i;
+	struct snd_ctl_elem_id id;
+	struct snd_ctl_elem_id __user *_id;
+	struct snd_emu10k1_fx8010_ctl *ctl;
+	struct snd_card *card = emu->card;
+	
+	for (i = 0, _id = icode->gpr_del_controls;
+	     i < icode->gpr_del_control_count; i++, _id++) {
+		if (in_kernel)
+			id = *(struct snd_ctl_elem_id *)_id;
+		else if (copy_from_user(&id, _id, sizeof(id)))
+			return -EFAULT;
+		down_write(&card->controls_rwsem);
+		ctl = snd_emu10k1_look_for_ctl(emu, &id);
+		if (ctl)
+			snd_ctl_remove(card, ctl->kcontrol);
+		up_write(&card->controls_rwsem);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_list_controls(struct snd_emu10k1 *emu,
+				     struct snd_emu10k1_fx8010_code *icode)
+{
+	unsigned int i = 0, j;
+	unsigned int total = 0;
+	struct snd_emu10k1_fx8010_control_gpr *gctl;
+	struct snd_emu10k1_fx8010_ctl *ctl;
+	struct snd_ctl_elem_id *id;
+
+	gctl = kmalloc(sizeof(*gctl), GFP_KERNEL);
+	if (! gctl)
+		return -ENOMEM;
+
+	list_for_each_entry(ctl, &emu->fx8010.gpr_ctl, list) {
+		total++;
+		if (icode->gpr_list_controls &&
+		    i < icode->gpr_list_control_count) {
+			memset(gctl, 0, sizeof(*gctl));
+			id = &ctl->kcontrol->id;
+			gctl->id.iface = id->iface;
+			strlcpy(gctl->id.name, id->name, sizeof(gctl->id.name));
+			gctl->id.index = id->index;
+			gctl->id.device = id->device;
+			gctl->id.subdevice = id->subdevice;
+			gctl->vcount = ctl->vcount;
+			gctl->count = ctl->count;
+			for (j = 0; j < 32; j++) {
+				gctl->gpr[j] = ctl->gpr[j];
+				gctl->value[j] = ctl->value[j];
+			}
+			gctl->min = ctl->min;
+			gctl->max = ctl->max;
+			gctl->translation = ctl->translation;
+			if (copy_gctl_to_user(emu, icode->gpr_list_controls,
+					      gctl, i)) {
+				kfree(gctl);
+				return -EFAULT;
+			}
+			i++;
+		}
+	}
+	icode->gpr_list_control_total = total;
+	kfree(gctl);
+	return 0;
+}
+
+static int snd_emu10k1_icode_poke(struct snd_emu10k1 *emu,
+				  struct snd_emu10k1_fx8010_code *icode,
+				  bool in_kernel)
+{
+	int err = 0;
+
+	mutex_lock(&emu->fx8010.lock);
+	err = snd_emu10k1_verify_controls(emu, icode, in_kernel);
+	if (err < 0)
+		goto __error;
+	strlcpy(emu->fx8010.name, icode->name, sizeof(emu->fx8010.name));
+	/* stop FX processor - this may be dangerous, but it's better to miss
+	   some samples than generate wrong ones - [jk] */
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP);
+	/* ok, do the main job */
+	err = snd_emu10k1_del_controls(emu, icode, in_kernel);
+	if (err < 0)
+		goto __error;
+	err = snd_emu10k1_gpr_poke(emu, icode, in_kernel);
+	if (err < 0)
+		goto __error;
+	err = snd_emu10k1_tram_poke(emu, icode, in_kernel);
+	if (err < 0)
+		goto __error;
+	err = snd_emu10k1_code_poke(emu, icode, in_kernel);
+	if (err < 0)
+		goto __error;
+	err = snd_emu10k1_add_controls(emu, icode, in_kernel);
+	if (err < 0)
+		goto __error;
+	/* start FX processor when the DSP code is updated */
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+      __error:
+	mutex_unlock(&emu->fx8010.lock);
+	return err;
+}
+
+static int snd_emu10k1_icode_peek(struct snd_emu10k1 *emu,
+				  struct snd_emu10k1_fx8010_code *icode)
+{
+	int err;
+
+	mutex_lock(&emu->fx8010.lock);
+	strlcpy(icode->name, emu->fx8010.name, sizeof(icode->name));
+	/* ok, do the main job */
+	err = snd_emu10k1_gpr_peek(emu, icode);
+	if (err >= 0)
+		err = snd_emu10k1_tram_peek(emu, icode);
+	if (err >= 0)
+		err = snd_emu10k1_code_peek(emu, icode);
+	if (err >= 0)
+		err = snd_emu10k1_list_controls(emu, icode);
+	mutex_unlock(&emu->fx8010.lock);
+	return err;
+}
+
+static int snd_emu10k1_ipcm_poke(struct snd_emu10k1 *emu,
+				 struct snd_emu10k1_fx8010_pcm_rec *ipcm)
+{
+	unsigned int i;
+	int err = 0;
+	struct snd_emu10k1_fx8010_pcm *pcm;
+
+	if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
+		return -EINVAL;
+	ipcm->substream = array_index_nospec(ipcm->substream,
+					     EMU10K1_FX8010_PCM_COUNT);
+	if (ipcm->channels > 32)
+		return -EINVAL;
+	pcm = &emu->fx8010.pcm[ipcm->substream];
+	mutex_lock(&emu->fx8010.lock);
+	spin_lock_irq(&emu->reg_lock);
+	if (pcm->opened) {
+		err = -EBUSY;
+		goto __error;
+	}
+	if (ipcm->channels == 0) {	/* remove */
+		pcm->valid = 0;
+	} else {
+		/* FIXME: we need to add universal code to the PCM transfer routine */
+		if (ipcm->channels != 2) {
+			err = -EINVAL;
+			goto __error;
+		}
+		pcm->valid = 1;
+		pcm->opened = 0;
+		pcm->channels = ipcm->channels;
+		pcm->tram_start = ipcm->tram_start;
+		pcm->buffer_size = ipcm->buffer_size;
+		pcm->gpr_size = ipcm->gpr_size;
+		pcm->gpr_count = ipcm->gpr_count;
+		pcm->gpr_tmpcount = ipcm->gpr_tmpcount;
+		pcm->gpr_ptr = ipcm->gpr_ptr;
+		pcm->gpr_trigger = ipcm->gpr_trigger;
+		pcm->gpr_running = ipcm->gpr_running;
+		for (i = 0; i < pcm->channels; i++)
+			pcm->etram[i] = ipcm->etram[i];
+	}
+      __error:
+	spin_unlock_irq(&emu->reg_lock);
+	mutex_unlock(&emu->fx8010.lock);
+	return err;
+}
+
+static int snd_emu10k1_ipcm_peek(struct snd_emu10k1 *emu,
+				 struct snd_emu10k1_fx8010_pcm_rec *ipcm)
+{
+	unsigned int i;
+	int err = 0;
+	struct snd_emu10k1_fx8010_pcm *pcm;
+
+	if (ipcm->substream >= EMU10K1_FX8010_PCM_COUNT)
+		return -EINVAL;
+	ipcm->substream = array_index_nospec(ipcm->substream,
+					     EMU10K1_FX8010_PCM_COUNT);
+	pcm = &emu->fx8010.pcm[ipcm->substream];
+	mutex_lock(&emu->fx8010.lock);
+	spin_lock_irq(&emu->reg_lock);
+	ipcm->channels = pcm->channels;
+	ipcm->tram_start = pcm->tram_start;
+	ipcm->buffer_size = pcm->buffer_size;
+	ipcm->gpr_size = pcm->gpr_size;
+	ipcm->gpr_ptr = pcm->gpr_ptr;
+	ipcm->gpr_count = pcm->gpr_count;
+	ipcm->gpr_tmpcount = pcm->gpr_tmpcount;
+	ipcm->gpr_trigger = pcm->gpr_trigger;
+	ipcm->gpr_running = pcm->gpr_running;
+	for (i = 0; i < pcm->channels; i++)
+		ipcm->etram[i] = pcm->etram[i];
+	ipcm->res1 = ipcm->res2 = 0;
+	ipcm->pad = 0;
+	spin_unlock_irq(&emu->reg_lock);
+	mutex_unlock(&emu->fx8010.lock);
+	return err;
+}
+
+#define SND_EMU10K1_GPR_CONTROLS	44
+#define SND_EMU10K1_INPUTS		12
+#define SND_EMU10K1_PLAYBACK_CHANNELS	8
+#define SND_EMU10K1_CAPTURE_CHANNELS	4
+
+static void
+snd_emu10k1_init_mono_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
+			      const char *name, int gpr, int defval)
+{
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, name);
+	ctl->vcount = ctl->count = 1;
+	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+	if (high_res_gpr_volume) {
+		ctl->min = 0;
+		ctl->max = 0x7fffffff;
+		ctl->tlv = snd_emu10k1_db_linear;
+		ctl->translation = EMU10K1_GPR_TRANSLATION_NONE;
+	} else {
+		ctl->min = 0;
+		ctl->max = 100;
+		ctl->tlv = snd_emu10k1_db_scale1;
+		ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+	}
+}
+
+static void
+snd_emu10k1_init_stereo_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
+				const char *name, int gpr, int defval)
+{
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, name);
+	ctl->vcount = ctl->count = 2;
+	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+	ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
+	if (high_res_gpr_volume) {
+		ctl->min = 0;
+		ctl->max = 0x7fffffff;
+		ctl->tlv = snd_emu10k1_db_linear;
+		ctl->translation = EMU10K1_GPR_TRANSLATION_NONE;
+	} else {
+		ctl->min = 0;
+		ctl->max = 100;
+		ctl->tlv = snd_emu10k1_db_scale1;
+		ctl->translation = EMU10K1_GPR_TRANSLATION_TABLE100;
+	}
+}
+
+static void
+snd_emu10k1_init_mono_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
+				    const char *name, int gpr, int defval)
+{
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, name);
+	ctl->vcount = ctl->count = 1;
+	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+	ctl->min = 0;
+	ctl->max = 1;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
+}
+
+static void
+snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl,
+				      const char *name, int gpr, int defval)
+{
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, name);
+	ctl->vcount = ctl->count = 2;
+	ctl->gpr[0] = gpr + 0; ctl->value[0] = defval;
+	ctl->gpr[1] = gpr + 1; ctl->value[1] = defval;
+	ctl->min = 0;
+	ctl->max = 1;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
+}
+
+/*
+ * Used for emu1010 - conversion from 32-bit capture inputs from HANA
+ * to 2 x 16-bit registers in audigy - their values are read via DMA.
+ * Conversion is performed by Audigy DSP instructions of FX8010.
+ */
+static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+				struct snd_emu10k1_fx8010_code *icode,
+				u32 *ptr, int tmp, int bit_shifter16,
+				int reg_in, int reg_out)
+{
+	A_OP(icode, ptr, iACC3, A_GPR(tmp + 1), reg_in, A_C_00000000, A_C_00000000);
+	A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp + 1), A_GPR(bit_shifter16 - 1), A_C_00000000);
+	A_OP(icode, ptr, iTSTNEG, A_GPR(tmp + 2), A_GPR(tmp), A_C_80000000, A_GPR(bit_shifter16 - 2));
+	A_OP(icode, ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_C_80000000, A_C_00000000);
+	A_OP(icode, ptr, iANDXOR, A_GPR(tmp), A_GPR(tmp), A_GPR(bit_shifter16 - 3), A_C_00000000);
+	A_OP(icode, ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A_GPR(tmp), A_C_00010000);
+	A_OP(icode, ptr, iANDXOR, reg_out, A_GPR(tmp), A_C_ffffffff, A_GPR(tmp + 2));
+	A_OP(icode, ptr, iACC3, reg_out + 1, A_GPR(tmp + 1), A_C_00000000, A_C_00000000);
+	return 1;
+}
+
+/*
+ * initial DSP configuration for Audigy
+ */
+
+static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
+{
+	int err, i, z, gpr, nctl;
+	int bit_shifter16;
+	const int playback = 10;
+	const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
+	const int stereo_mix = capture + 2;
+	const int tmp = 0x88;
+	u32 ptr;
+	struct snd_emu10k1_fx8010_code *icode = NULL;
+	struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
+	u32 *gpr_map;
+
+	err = -ENOMEM;
+	icode = kzalloc(sizeof(*icode), GFP_KERNEL);
+	if (!icode)
+		return err;
+
+	icode->gpr_map = (u_int32_t __user *) kcalloc(512 + 256 + 256 + 2 * 1024,
+						      sizeof(u_int32_t), GFP_KERNEL);
+	if (!icode->gpr_map)
+		goto __err_gpr;
+	controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+			   sizeof(*controls), GFP_KERNEL);
+	if (!controls)
+		goto __err_ctrls;
+
+	gpr_map = (u32 __force *)icode->gpr_map;
+
+	icode->tram_data_map = icode->gpr_map + 512;
+	icode->tram_addr_map = icode->tram_data_map + 256;
+	icode->code = icode->tram_addr_map + 256;
+
+	/* clear free GPRs */
+	for (i = 0; i < 512; i++)
+		set_bit(i, icode->gpr_valid);
+		
+	/* clear TRAM data & address lines */
+	for (i = 0; i < 256; i++)
+		set_bit(i, icode->tram_valid);
+
+	strcpy(icode->name, "Audigy DSP code for ALSA");
+	ptr = 0;
+	nctl = 0;
+	gpr = stereo_mix + 10;
+	gpr_map[gpr++] = 0x00007fff;
+	gpr_map[gpr++] = 0x00008000;
+	gpr_map[gpr++] = 0x0000ffff;
+	bit_shifter16 = gpr;
+
+	/* stop FX processor */
+	snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
+
+#if 1
+	/* PCM front Playback Volume (independent from stereo mix)
+	 * playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
+	 * where gpr contains attenuation from corresponding mixer control
+	 * (snd_emu10k1_init_stereo_control)
+	 */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* PCM Surround Playback (independent from stereo mix) */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_REAR));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Surround Playback Volume", gpr, 100);
+	gpr += 2;
+	
+	/* PCM Side Playback (independent from stereo mix) */
+	if (emu->card_capabilities->spk71) {
+		A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
+		A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
+		snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100);
+		gpr += 2;
+	}
+
+	/* PCM Center Playback (independent from stereo mix) */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER));
+	snd_emu10k1_init_mono_control(&controls[nctl++], "PCM Center Playback Volume", gpr, 100);
+	gpr++;
+
+	/* PCM LFE Playback (independent from stereo mix) */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LFE));
+	snd_emu10k1_init_mono_control(&controls[nctl++], "PCM LFE Playback Volume", gpr, 100);
+	gpr++;
+	
+	/*
+	 * Stereo Mix
+	 */
+	/* Wave (PCM) Playback Volume (will be renamed later) */
+	A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Synth Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+0), A_GPR(stereo_mix+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(stereo_mix+1), A_GPR(stereo_mix+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Wave (PCM) Capture */
+	A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Capture Volume", gpr, 0);
+	gpr += 2;
+
+	/* Synth Capture */
+	A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_FXBUS(FXBUS_MIDI_LEFT));
+	A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_FXBUS(FXBUS_MIDI_RIGHT));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Synth Capture Volume", gpr, 0);
+	gpr += 2;
+      
+	/*
+	 * inputs
+	 */
+#define A_ADD_VOLUME_IN(var,vol,input) \
+A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
+
+	/* emu1212 DSP 0 and DSP 1 Capture */
+	if (emu->card_capabilities->emu_model) {
+		if (emu->card_capabilities->ca0108_chip) {
+			/* Note:JCD:No longer bit shift lower 16bits to upper 16bits of 32bit value. */
+			A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x0), A_C_00000001);
+			A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_GPR(tmp));
+			A_OP(icode, &ptr, iMACINT0, A_GPR(tmp), A_C_00000000, A3_EMU32IN(0x1), A_C_00000001);
+			A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr), A_GPR(tmp));
+		} else {
+			A_OP(icode, &ptr, iMAC0, A_GPR(capture+0), A_GPR(capture+0), A_GPR(gpr), A_P16VIN(0x0));
+			A_OP(icode, &ptr, iMAC0, A_GPR(capture+1), A_GPR(capture+1), A_GPR(gpr+1), A_P16VIN(0x1));
+		}
+		snd_emu10k1_init_stereo_control(&controls[nctl++], "EMU Capture Volume", gpr, 0);
+		gpr += 2;
+	}
+	/* AC'97 Playback Volume - used only for mic (renamed later) */
+	A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AC97_L);
+	A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AC97_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "AMic Playback Volume", gpr, 0);
+	gpr += 2;
+	/* AC'97 Capture Volume - used only for mic */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AC97_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AC97_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Mic Capture Volume", gpr, 0);
+	gpr += 2;
+
+	/* mic capture buffer */	
+	A_OP(icode, &ptr, iINTERP, A_EXTOUT(A_EXTOUT_MIC_CAP), A_EXTIN(A_EXTIN_AC97_L), 0xcd, A_EXTIN(A_EXTIN_AC97_R));
+
+	/* Audigy CD Playback Volume */
+	A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
+	A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++],
+					emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume",
+					gpr, 0);
+	gpr += 2;
+	/* Audigy CD Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++],
+					emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume",
+					gpr, 0);
+	gpr += 2;
+
+ 	/* Optical SPDIF Playback Volume */
+	A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_OPT_SPDIF_L);
+	A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",PLAYBACK,VOLUME), gpr, 0);
+	gpr += 2;
+	/* Optical SPDIF Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",CAPTURE,VOLUME), gpr, 0);
+	gpr += 2;
+
+	/* Line2 Playback Volume */
+	A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
+	A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++],
+					emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume",
+					gpr, 0);
+	gpr += 2;
+	/* Line2 Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++],
+					emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume",
+					gpr, 0);
+	gpr += 2;
+        
+	/* Philips ADC Playback Volume */
+	A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_ADC_L);
+	A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_ADC_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Playback Volume", gpr, 0);
+	gpr += 2;
+	/* Philips ADC Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_ADC_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_ADC_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Analog Mix Capture Volume", gpr, 0);
+	gpr += 2;
+
+	/* Aux2 Playback Volume */
+	A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
+	A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++],
+					emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume",
+					gpr, 0);
+	gpr += 2;
+	/* Aux2 Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++],
+					emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume",
+					gpr, 0);
+	gpr += 2;
+	
+	/* Stereo Mix Front Playback Volume */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_GPR(playback), A_GPR(gpr), A_GPR(stereo_mix));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_GPR(playback+1), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Front Playback Volume", gpr, 100);
+	gpr += 2;
+	
+	/* Stereo Mix Surround Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+2), A_GPR(playback+2), A_GPR(gpr), A_GPR(stereo_mix));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_GPR(playback+3), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Surround Playback Volume", gpr, 0);
+	gpr += 2;
+
+	/* Stereo Mix Center Playback */
+	/* Center = sub = Left/2 + Right/2 */
+	A_OP(icode, &ptr, iINTERP, A_GPR(tmp), A_GPR(stereo_mix), 0xcd, A_GPR(stereo_mix+1));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_GPR(playback+4), A_GPR(gpr), A_GPR(tmp));
+	snd_emu10k1_init_mono_control(&controls[nctl++], "Center Playback Volume", gpr, 0);
+	gpr++;
+
+	/* Stereo Mix LFE Playback */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp));
+	snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0);
+	gpr++;
+	
+	if (emu->card_capabilities->spk71) {
+		/* Stereo Mix Side Playback */
+		A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
+		A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
+		snd_emu10k1_init_stereo_control(&controls[nctl++], "Side Playback Volume", gpr, 0);
+		gpr += 2;
+	}
+
+	/*
+	 * outputs
+	 */
+#define A_PUT_OUTPUT(out,src) A_OP(icode, &ptr, iACC3, A_EXTOUT(out), A_C_00000000, A_C_00000000, A_GPR(src))
+#define A_PUT_STEREO_OUTPUT(out1,out2,src) \
+	{A_PUT_OUTPUT(out1,src); A_PUT_OUTPUT(out2,src+1);}
+
+#define _A_SWITCH(icode, ptr, dst, src, sw) \
+	A_OP((icode), ptr, iMACINT0, dst, A_C_00000000, src, sw);
+#define A_SWITCH(icode, ptr, dst, src, sw) \
+		_A_SWITCH(icode, ptr, A_GPR(dst), A_GPR(src), A_GPR(sw))
+#define _A_SWITCH_NEG(icode, ptr, dst, src) \
+	A_OP((icode), ptr, iANDXOR, dst, src, A_C_00000001, A_C_00000001);
+#define A_SWITCH_NEG(icode, ptr, dst, src) \
+		_A_SWITCH_NEG(icode, ptr, A_GPR(dst), A_GPR(src))
+
+
+	/*
+	 *  Process tone control
+	 */
+	A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), A_GPR(playback + 0), A_C_00000000, A_C_00000000); /* left */
+	A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), A_GPR(playback + 1), A_C_00000000, A_C_00000000); /* right */
+	A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), A_GPR(playback + 2), A_C_00000000, A_C_00000000); /* rear left */
+	A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */
+	A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */
+	A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */
+	if (emu->card_capabilities->spk71) {
+		A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 6), A_GPR(playback + 6), A_C_00000000, A_C_00000000); /* side left */
+		A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 7), A_GPR(playback + 7), A_C_00000000, A_C_00000000); /* side right */
+	}
+	
+
+	ctl = &controls[nctl + 0];
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, "Tone Control - Bass");
+	ctl->vcount = 2;
+	ctl->count = 10;
+	ctl->min = 0;
+	ctl->max = 40;
+	ctl->value[0] = ctl->value[1] = 20;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
+	ctl = &controls[nctl + 1];
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, "Tone Control - Treble");
+	ctl->vcount = 2;
+	ctl->count = 10;
+	ctl->min = 0;
+	ctl->max = 40;
+	ctl->value[0] = ctl->value[1] = 20;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
+
+#define BASS_GPR	0x8c
+#define TREBLE_GPR	0x96
+
+	for (z = 0; z < 5; z++) {
+		int j;
+		for (j = 0; j < 2; j++) {
+			controls[nctl + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j;
+			controls[nctl + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
+		}
+	}
+	for (z = 0; z < 4; z++) {		/* front/rear/center-lfe/side */
+		int j, k, l, d;
+		for (j = 0; j < 2; j++) {	/* left/right */
+			k = 0xb0 + (z * 8) + (j * 4);
+			l = 0xe0 + (z * 8) + (j * 4);
+			d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
+
+			A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(d), A_GPR(BASS_GPR + 0 + j));
+			A_OP(icode, &ptr, iMACMV, A_GPR(k+1), A_GPR(k), A_GPR(k+1), A_GPR(BASS_GPR + 4 + j));
+			A_OP(icode, &ptr, iMACMV, A_GPR(k), A_GPR(d), A_GPR(k), A_GPR(BASS_GPR + 2 + j));
+			A_OP(icode, &ptr, iMACMV, A_GPR(k+3), A_GPR(k+2), A_GPR(k+3), A_GPR(BASS_GPR + 8 + j));
+			A_OP(icode, &ptr, iMAC0, A_GPR(k+2), A_GPR_ACCU, A_GPR(k+2), A_GPR(BASS_GPR + 6 + j));
+			A_OP(icode, &ptr, iACC3, A_GPR(k+2), A_GPR(k+2), A_GPR(k+2), A_C_00000000);
+
+			A_OP(icode, &ptr, iMAC0, A_C_00000000, A_C_00000000, A_GPR(k+2), A_GPR(TREBLE_GPR + 0 + j));
+			A_OP(icode, &ptr, iMACMV, A_GPR(l+1), A_GPR(l), A_GPR(l+1), A_GPR(TREBLE_GPR + 4 + j));
+			A_OP(icode, &ptr, iMACMV, A_GPR(l), A_GPR(k+2), A_GPR(l), A_GPR(TREBLE_GPR + 2 + j));
+			A_OP(icode, &ptr, iMACMV, A_GPR(l+3), A_GPR(l+2), A_GPR(l+3), A_GPR(TREBLE_GPR + 8 + j));
+			A_OP(icode, &ptr, iMAC0, A_GPR(l+2), A_GPR_ACCU, A_GPR(l+2), A_GPR(TREBLE_GPR + 6 + j));
+			A_OP(icode, &ptr, iMACINT0, A_GPR(l+2), A_C_00000000, A_GPR(l+2), A_C_00000010);
+
+			A_OP(icode, &ptr, iACC3, A_GPR(d), A_GPR(l+2), A_C_00000000, A_C_00000000);
+
+			if (z == 2)	/* center */
+				break;
+		}
+	}
+	nctl += 2;
+
+#undef BASS_GPR
+#undef TREBLE_GPR
+
+	for (z = 0; z < 8; z++) {
+		A_SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
+		A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
+		A_SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
+		A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+	}
+	snd_emu10k1_init_stereo_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0);
+	gpr += 2;
+
+	/* Master volume (will be renamed later) */
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS));
+	A_OP(icode, &ptr, iMAC0, A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS));
+	snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0);
+	gpr += 2;
+
+	/* analog speakers */
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
+	A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
+	A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
+	if (emu->card_capabilities->spk71)
+		A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6 + SND_EMU10K1_PLAYBACK_CHANNELS);
+
+	/* headphone */
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+
+	/* digital outputs */
+	/* A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); */
+	if (emu->card_capabilities->emu_model) {
+		/* EMU1010 Outputs from PCM Front, Rear, Center, LFE, Side */
+		dev_info(emu->card->dev, "EMU outputs on\n");
+		for (z = 0; z < 8; z++) {
+			if (emu->card_capabilities->ca0108_chip) {
+				A_OP(icode, &ptr, iACC3, A3_EMU32OUT(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+			} else {
+				A_OP(icode, &ptr, iACC3, A_EMU32OUTL(z), A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), A_C_00000000, A_C_00000000);
+			}
+		}
+	}
+
+	/* IEC958 Optical Raw Playback Switch */ 
+	gpr_map[gpr++] = 0;
+	gpr_map[gpr++] = 0x1008;
+	gpr_map[gpr++] = 0xffff0000;
+	for (z = 0; z < 2; z++) {
+		A_OP(icode, &ptr, iMAC0, A_GPR(tmp + 2), A_FXBUS(FXBUS_PT_LEFT + z), A_C_00000000, A_C_00000000);
+		A_OP(icode, &ptr, iSKIP, A_GPR_COND, A_GPR_COND, A_GPR(gpr - 2), A_C_00000001);
+		A_OP(icode, &ptr, iACC3, A_GPR(tmp + 2), A_C_00000000, A_C_00010000, A_GPR(tmp + 2));
+		A_OP(icode, &ptr, iANDXOR, A_GPR(tmp + 2), A_GPR(tmp + 2), A_GPR(gpr - 1), A_C_00000000);
+		A_SWITCH(icode, &ptr, tmp + 0, tmp + 2, gpr + z);
+		A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
+		A_SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+		if ((z==1) && (emu->card_capabilities->spdif_bug)) {
+			/* Due to a SPDIF output bug on some Audigy cards, this code delays the Right channel by 1 sample */
+			dev_info(emu->card->dev,
+				 "Installing spdif_bug patch: %s\n",
+				 emu->card_capabilities->name);
+			A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(gpr - 3), A_C_00000000, A_C_00000000);
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 3), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+		} else {
+			A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000);
+		}
+	}
+	snd_emu10k1_init_stereo_onoff_control(controls + nctl++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0);
+	gpr += 2;
+	
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
+	A_PUT_OUTPUT(A_EXTOUT_CENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
+	A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
+
+	/* ADC buffer */
+#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
+	A_PUT_STEREO_OUTPUT(A_EXTOUT_ADC_CAP_L, A_EXTOUT_ADC_CAP_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
+#else
+	A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_L, capture);
+	A_PUT_OUTPUT(A_EXTOUT_ADC_CAP_R, capture+1);
+#endif
+
+	if (emu->card_capabilities->emu_model) {
+		if (emu->card_capabilities->ca0108_chip) {
+			dev_info(emu->card->dev, "EMU2 inputs on\n");
+			for (z = 0; z < 0x10; z++) {
+				snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, 
+									bit_shifter16,
+									A3_EMU32IN(z),
+									A_FXBUS2(z*2) );
+			}
+		} else {
+			dev_info(emu->card->dev, "EMU inputs on\n");
+			/* Capture 16 (originally 8) channels of S32_LE sound */
+
+			/*
+			dev_dbg(emu->card->dev, "emufx.c: gpr=0x%x, tmp=0x%x\n",
+			       gpr, tmp);
+			*/
+			/* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
+			/* A_P16VIN(0) is delayed by one sample,
+			 * so all other A_P16VIN channels will need to also be delayed
+			 */
+			/* Left ADC in. 1 of 2 */
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
+			/* Right ADC in 1 of 2 */
+			gpr_map[gpr++] = 0x00000000;
+			/* Delaying by one sample: instead of copying the input
+			 * value A_P16VIN to output A_FXBUS2 as in the first channel,
+			 * we use an auxiliary register, delaying the value by one
+			 * sample
+			 */
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(4) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x2), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(6) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x3), A_C_00000000, A_C_00000000);
+			/* For 96kHz mode */
+			/* Left ADC in. 2 of 2 */
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0x8) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x4), A_C_00000000, A_C_00000000);
+			/* Right ADC in 2 of 2 */
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xa) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x5), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xc) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x6), A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
+			/* Pavel Hofman - we still have voices, A_FXBUS2s, and
+			 * A_P16VINs available -
+			 * let's add 8 more capture channels - total of 16
+			 */
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x10));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x8),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x12));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x9),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x14));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xa),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x16));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xb),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x18));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xc),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x1a));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xd),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x1c));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xe),
+			     A_C_00000000, A_C_00000000);
+			gpr_map[gpr++] = 0x00000000;
+			snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+								  bit_shifter16,
+								  A_GPR(gpr - 1),
+								  A_FXBUS2(0x1e));
+			A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xf),
+			     A_C_00000000, A_C_00000000);
+		}
+
+#if 0
+		for (z = 4; z < 8; z++) {
+			A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000);
+		}
+		for (z = 0xc; z < 0x10; z++) {
+			A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_C_00000000);
+		}
+#endif
+	} else {
+		/* EFX capture - capture the 16 EXTINs */
+		/* Capture 16 channels of S16_LE sound */
+		for (z = 0; z < 16; z++) {
+			A_OP(icode, &ptr, iACC3, A_FXBUS2(z), A_C_00000000, A_C_00000000, A_EXTIN(z));
+		}
+	}
+	
+#endif /* JCD test */
+	/*
+	 * ok, set up done..
+	 */
+
+	if (gpr > tmp) {
+		snd_BUG();
+		err = -EIO;
+		goto __err;
+	}
+	/* clear remaining instruction memory */
+	while (ptr < 0x400)
+		A_OP(icode, &ptr, 0x0f, 0xc0, 0xc0, 0xcf, 0xc0);
+
+	icode->gpr_add_control_count = nctl;
+	icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+	emu->support_tlv = 1; /* support TLV */
+	err = snd_emu10k1_icode_poke(emu, icode, true);
+	emu->support_tlv = 0; /* clear again */
+
+__err:
+	kfree(controls);
+__err_ctrls:
+	kfree((void __force *)icode->gpr_map);
+__err_gpr:
+	kfree(icode);
+	return err;
+}
+
+
+/*
+ * initial DSP configuration for Emu10k1
+ */
+
+/* when volume = max, then copy only to avoid volume modification */
+/* with iMAC0 (negative values) */
+static void _volume(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+	OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+	OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+	OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000001);
+	OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
+}
+static void _volume_add(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+	OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+	OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+	OP(icode, ptr, iMACINT0, dst, dst, src, C_00000001);
+	OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
+	OP(icode, ptr, iMAC0, dst, dst, src, vol);
+}
+static void _volume_out(struct snd_emu10k1_fx8010_code *icode, u32 *ptr, u32 dst, u32 src, u32 vol)
+{
+	OP(icode, ptr, iANDXOR, C_00000000, vol, C_ffffffff, C_7fffffff);
+	OP(icode, ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+	OP(icode, ptr, iACC3, dst, src, C_00000000, C_00000000);
+	OP(icode, ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000001);
+	OP(icode, ptr, iMAC0, dst, C_00000000, src, vol);
+}
+
+#define VOLUME(icode, ptr, dst, src, vol) \
+		_volume(icode, ptr, GPR(dst), GPR(src), GPR(vol))
+#define VOLUME_IN(icode, ptr, dst, src, vol) \
+		_volume(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
+#define VOLUME_ADD(icode, ptr, dst, src, vol) \
+		_volume_add(icode, ptr, GPR(dst), GPR(src), GPR(vol))
+#define VOLUME_ADDIN(icode, ptr, dst, src, vol) \
+		_volume_add(icode, ptr, GPR(dst), EXTIN(src), GPR(vol))
+#define VOLUME_OUT(icode, ptr, dst, src, vol) \
+		_volume_out(icode, ptr, EXTOUT(dst), GPR(src), GPR(vol))
+#define _SWITCH(icode, ptr, dst, src, sw) \
+	OP((icode), ptr, iMACINT0, dst, C_00000000, src, sw);
+#define SWITCH(icode, ptr, dst, src, sw) \
+		_SWITCH(icode, ptr, GPR(dst), GPR(src), GPR(sw))
+#define SWITCH_IN(icode, ptr, dst, src, sw) \
+		_SWITCH(icode, ptr, GPR(dst), EXTIN(src), GPR(sw))
+#define _SWITCH_NEG(icode, ptr, dst, src) \
+	OP((icode), ptr, iANDXOR, dst, src, C_00000001, C_00000001);
+#define SWITCH_NEG(icode, ptr, dst, src) \
+		_SWITCH_NEG(icode, ptr, GPR(dst), GPR(src))
+
+
+static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
+{
+	int err, i, z, gpr, tmp, playback, capture;
+	u32 ptr;
+	struct snd_emu10k1_fx8010_code *icode;
+	struct snd_emu10k1_fx8010_pcm_rec *ipcm = NULL;
+	struct snd_emu10k1_fx8010_control_gpr *controls = NULL, *ctl;
+	u32 *gpr_map;
+
+	err = -ENOMEM;
+	icode = kzalloc(sizeof(*icode), GFP_KERNEL);
+	if (!icode)
+		return err;
+
+	icode->gpr_map = (u_int32_t __user *) kcalloc(256 + 160 + 160 + 2 * 512,
+						      sizeof(u_int32_t), GFP_KERNEL);
+	if (!icode->gpr_map)
+		goto __err_gpr;
+
+	controls = kcalloc(SND_EMU10K1_GPR_CONTROLS,
+			   sizeof(struct snd_emu10k1_fx8010_control_gpr),
+			   GFP_KERNEL);
+	if (!controls)
+		goto __err_ctrls;
+
+	ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL);
+	if (!ipcm)
+		goto __err_ipcm;
+
+	gpr_map = (u32 __force *)icode->gpr_map;
+
+	icode->tram_data_map = icode->gpr_map + 256;
+	icode->tram_addr_map = icode->tram_data_map + 160;
+	icode->code = icode->tram_addr_map + 160;
+	
+	/* clear free GPRs */
+	for (i = 0; i < 256; i++)
+		set_bit(i, icode->gpr_valid);
+
+	/* clear TRAM data & address lines */
+	for (i = 0; i < 160; i++)
+		set_bit(i, icode->tram_valid);
+
+	strcpy(icode->name, "SB Live! FX8010 code for ALSA v1.2 by Jaroslav Kysela");
+	ptr = 0; i = 0;
+	/* we have 12 inputs */
+	playback = SND_EMU10K1_INPUTS;
+	/* we have 6 playback channels and tone control doubles */
+	capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2);
+	gpr = capture + SND_EMU10K1_CAPTURE_CHANNELS;
+	tmp = 0x88;	/* we need 4 temporary GPR */
+	/* from 0x8c to 0xff is the area for tone control */
+
+	/* stop FX processor */
+	snd_emu10k1_ptr_write(emu, DBG, 0, (emu->fx8010.dbg = 0) | EMU10K1_DBG_SINGLE_STEP);
+
+	/*
+	 *  Process FX Buses
+	 */
+	OP(icode, &ptr, iMACINT0, GPR(0), C_00000000, FXBUS(FXBUS_PCM_LEFT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(1), C_00000000, FXBUS(FXBUS_PCM_RIGHT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(2), C_00000000, FXBUS(FXBUS_MIDI_LEFT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(3), C_00000000, FXBUS(FXBUS_MIDI_RIGHT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(4), C_00000000, FXBUS(FXBUS_PCM_LEFT_REAR), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(5), C_00000000, FXBUS(FXBUS_PCM_RIGHT_REAR), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(6), C_00000000, FXBUS(FXBUS_PCM_CENTER), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(7), C_00000000, FXBUS(FXBUS_PCM_LFE), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(8), C_00000000, C_00000000, C_00000000);	/* S/PDIF left */
+	OP(icode, &ptr, iMACINT0, GPR(9), C_00000000, C_00000000, C_00000000);	/* S/PDIF right */
+	OP(icode, &ptr, iMACINT0, GPR(10), C_00000000, FXBUS(FXBUS_PCM_LEFT_FRONT), C_00000004);
+	OP(icode, &ptr, iMACINT0, GPR(11), C_00000000, FXBUS(FXBUS_PCM_RIGHT_FRONT), C_00000004);
+
+	/* Raw S/PDIF PCM */
+	ipcm->substream = 0;
+	ipcm->channels = 2;
+	ipcm->tram_start = 0;
+	ipcm->buffer_size = (64 * 1024) / 2;
+	ipcm->gpr_size = gpr++;
+	ipcm->gpr_ptr = gpr++;
+	ipcm->gpr_count = gpr++;
+	ipcm->gpr_tmpcount = gpr++;
+	ipcm->gpr_trigger = gpr++;
+	ipcm->gpr_running = gpr++;
+	ipcm->etram[0] = 0;
+	ipcm->etram[1] = 1;
+
+	gpr_map[gpr + 0] = 0xfffff000;
+	gpr_map[gpr + 1] = 0xffff0000;
+	gpr_map[gpr + 2] = 0x70000000;
+	gpr_map[gpr + 3] = 0x00000007;
+	gpr_map[gpr + 4] = 0x001f << 11;
+	gpr_map[gpr + 5] = 0x001c << 11;
+	gpr_map[gpr + 6] = (0x22  - 0x01) - 1;	/* skip at 01 to 22 */
+	gpr_map[gpr + 7] = (0x22  - 0x06) - 1;	/* skip at 06 to 22 */
+	gpr_map[gpr + 8] = 0x2000000 + (2<<11);
+	gpr_map[gpr + 9] = 0x4000000 + (2<<11);
+	gpr_map[gpr + 10] = 1<<11;
+	gpr_map[gpr + 11] = (0x24 - 0x0a) - 1;	/* skip at 0a to 24 */
+	gpr_map[gpr + 12] = 0;
+
+	/* if the trigger flag is not set, skip */
+	/* 00: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_trigger), C_00000000, C_00000000);
+	/* 01: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_ZERO, GPR(gpr + 6));
+	/* if the running flag is set, we're running */
+	/* 02: */ OP(icode, &ptr, iMAC0, C_00000000, GPR(ipcm->gpr_running), C_00000000, C_00000000);
+	/* 03: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000004);
+	/* wait until ((GPR_DBAC>>11) & 0x1f) == 0x1c) */
+	/* 04: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), GPR_DBAC, GPR(gpr + 4), C_00000000);
+	/* 05: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(gpr + 5));
+	/* 06: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 7));
+	/* 07: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000010, C_00000001, C_00000000);
+
+	/* 08: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000000, C_00000001);
+	/* 09: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), GPR(gpr + 12), C_ffffffff, C_00000000);
+	/* 0a: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, GPR(gpr + 11));
+	/* 0b: */ OP(icode, &ptr, iACC3, GPR(gpr + 12), C_00000001, C_00000000, C_00000000);
+
+	/* 0c: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[0]), GPR(gpr + 0), C_00000000);
+	/* 0d: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000);
+	/* 0e: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2));
+	/* 0f: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001);
+	/* 10: */ OP(icode, &ptr, iANDXOR, GPR(8), GPR(8), GPR(gpr + 1), GPR(gpr + 2));
+
+	/* 11: */ OP(icode, &ptr, iANDXOR, GPR(tmp + 0), ETRAM_DATA(ipcm->etram[1]), GPR(gpr + 0), C_00000000);
+	/* 12: */ OP(icode, &ptr, iLOG, GPR(tmp + 0), GPR(tmp + 0), GPR(gpr + 3), C_00000000);
+	/* 13: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(tmp + 0), GPR(gpr + 1), GPR(gpr + 2));
+	/* 14: */ OP(icode, &ptr, iSKIP, C_00000000, GPR_COND, CC_REG_MINUS, C_00000001);
+	/* 15: */ OP(icode, &ptr, iANDXOR, GPR(9), GPR(9), GPR(gpr + 1), GPR(gpr + 2));
+
+	/* 16: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(ipcm->gpr_ptr), C_00000001, C_00000000);
+	/* 17: */ OP(icode, &ptr, iMACINT0, C_00000000, GPR(tmp + 0), C_ffffffff, GPR(ipcm->gpr_size));
+	/* 18: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_MINUS, C_00000001);
+	/* 19: */ OP(icode, &ptr, iACC3, GPR(tmp + 0), C_00000000, C_00000000, C_00000000);
+	/* 1a: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_ptr), GPR(tmp + 0), C_00000000, C_00000000);
+	
+	/* 1b: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_tmpcount), C_ffffffff, C_00000000);
+	/* 1c: */ OP(icode, &ptr, iSKIP, GPR_COND, GPR_COND, CC_REG_NONZERO, C_00000002);
+	/* 1d: */ OP(icode, &ptr, iACC3, GPR(ipcm->gpr_tmpcount), GPR(ipcm->gpr_count), C_00000000, C_00000000);
+	/* 1e: */ OP(icode, &ptr, iACC3, GPR_IRQ, C_80000000, C_00000000, C_00000000);
+	/* 1f: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00000001, C_00010000);
+
+	/* 20: */ OP(icode, &ptr, iANDXOR, GPR(ipcm->gpr_running), GPR(ipcm->gpr_running), C_00010000, C_00000001);
+	/* 21: */ OP(icode, &ptr, iSKIP, C_00000000, C_7fffffff, C_7fffffff, C_00000002);
+
+	/* 22: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[0]), GPR(gpr + 8), GPR_DBAC, C_ffffffff);
+	/* 23: */ OP(icode, &ptr, iMACINT1, ETRAM_ADDR(ipcm->etram[1]), GPR(gpr + 9), GPR_DBAC, C_ffffffff);
+
+	/* 24: */
+	gpr += 13;
+
+	/* Wave Playback Volume */
+	for (z = 0; z < 2; z++)
+		VOLUME(icode, &ptr, playback + z, z, gpr + z);
+	snd_emu10k1_init_stereo_control(controls + i++, "Wave Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Wave Surround Playback Volume */
+	for (z = 0; z < 2; z++)
+		VOLUME(icode, &ptr, playback + 2 + z, z, gpr + z);
+	snd_emu10k1_init_stereo_control(controls + i++, "Wave Surround Playback Volume", gpr, 0);
+	gpr += 2;
+	
+	/* Wave Center/LFE Playback Volume */
+	OP(icode, &ptr, iACC3, GPR(tmp + 0), FXBUS(FXBUS_PCM_LEFT), FXBUS(FXBUS_PCM_RIGHT), C_00000000);
+	OP(icode, &ptr, iMACINT0, GPR(tmp + 0), C_00000000, GPR(tmp + 0), C_00000002);
+	VOLUME(icode, &ptr, playback + 4, tmp + 0, gpr);
+	snd_emu10k1_init_mono_control(controls + i++, "Wave Center Playback Volume", gpr++, 0);
+	VOLUME(icode, &ptr, playback + 5, tmp + 0, gpr);
+	snd_emu10k1_init_mono_control(controls + i++, "Wave LFE Playback Volume", gpr++, 0);
+
+	/* Wave Capture Volume + Switch */
+	for (z = 0; z < 2; z++) {
+		SWITCH(icode, &ptr, tmp + 0, z, gpr + 2 + z);
+		VOLUME(icode, &ptr, capture + z, tmp + 0, gpr + z);
+	}
+	snd_emu10k1_init_stereo_control(controls + i++, "Wave Capture Volume", gpr, 0);
+	snd_emu10k1_init_stereo_onoff_control(controls + i++, "Wave Capture Switch", gpr + 2, 0);
+	gpr += 4;
+
+	/* Synth Playback Volume */
+	for (z = 0; z < 2; z++)
+		VOLUME_ADD(icode, &ptr, playback + z, 2 + z, gpr + z);
+	snd_emu10k1_init_stereo_control(controls + i++, "Synth Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Synth Capture Volume + Switch */
+	for (z = 0; z < 2; z++) {
+		SWITCH(icode, &ptr, tmp + 0, 2 + z, gpr + 2 + z);
+		VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+	}
+	snd_emu10k1_init_stereo_control(controls + i++, "Synth Capture Volume", gpr, 0);
+	snd_emu10k1_init_stereo_onoff_control(controls + i++, "Synth Capture Switch", gpr + 2, 0);
+	gpr += 4;
+
+	/* Surround Digital Playback Volume (renamed later without Digital) */
+	for (z = 0; z < 2; z++)
+		VOLUME_ADD(icode, &ptr, playback + 2 + z, 4 + z, gpr + z);
+	snd_emu10k1_init_stereo_control(controls + i++, "Surround Digital Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Surround Capture Volume + Switch */
+	for (z = 0; z < 2; z++) {
+		SWITCH(icode, &ptr, tmp + 0, 4 + z, gpr + 2 + z);
+		VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+	}
+	snd_emu10k1_init_stereo_control(controls + i++, "Surround Capture Volume", gpr, 0);
+	snd_emu10k1_init_stereo_onoff_control(controls + i++, "Surround Capture Switch", gpr + 2, 0);
+	gpr += 4;
+
+	/* Center Playback Volume (renamed later without Digital) */
+	VOLUME_ADD(icode, &ptr, playback + 4, 6, gpr);
+	snd_emu10k1_init_mono_control(controls + i++, "Center Digital Playback Volume", gpr++, 100);
+
+	/* LFE Playback Volume + Switch (renamed later without Digital) */
+	VOLUME_ADD(icode, &ptr, playback + 5, 7, gpr);
+	snd_emu10k1_init_mono_control(controls + i++, "LFE Digital Playback Volume", gpr++, 100);
+
+	/* Front Playback Volume */
+	for (z = 0; z < 2; z++)
+		VOLUME_ADD(icode, &ptr, playback + z, 10 + z, gpr + z);
+	snd_emu10k1_init_stereo_control(controls + i++, "Front Playback Volume", gpr, 100);
+	gpr += 2;
+
+	/* Front Capture Volume + Switch */
+	for (z = 0; z < 2; z++) {
+		SWITCH(icode, &ptr, tmp + 0, 10 + z, gpr + 2);
+		VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+	}
+	snd_emu10k1_init_stereo_control(controls + i++, "Front Capture Volume", gpr, 0);
+	snd_emu10k1_init_mono_onoff_control(controls + i++, "Front Capture Switch", gpr + 2, 0);
+	gpr += 3;
+
+	/*
+	 *  Process inputs
+	 */
+
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_AC97_L)|(1<<EXTIN_AC97_R))) {
+		/* AC'97 Playback Volume */
+		VOLUME_ADDIN(icode, &ptr, playback + 0, EXTIN_AC97_L, gpr); gpr++;
+		VOLUME_ADDIN(icode, &ptr, playback + 1, EXTIN_AC97_R, gpr); gpr++;
+		snd_emu10k1_init_stereo_control(controls + i++, "AC97 Playback Volume", gpr-2, 0);
+		/* AC'97 Capture Volume */
+		VOLUME_ADDIN(icode, &ptr, capture + 0, EXTIN_AC97_L, gpr); gpr++;
+		VOLUME_ADDIN(icode, &ptr, capture + 1, EXTIN_AC97_R, gpr); gpr++;
+		snd_emu10k1_init_stereo_control(controls + i++, "AC97 Capture Volume", gpr-2, 100);
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_SPDIF_CD_L)|(1<<EXTIN_SPDIF_CD_R))) {
+		/* IEC958 TTL Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_SPDIF_CD_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("TTL ",PLAYBACK,VOLUME), gpr, 0);
+		gpr += 2;
+	
+		/* IEC958 TTL Capture Volume + Switch */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_SPDIF_CD_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("TTL ",CAPTURE,VOLUME), gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("TTL ",CAPTURE,SWITCH), gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_ZOOM_L)|(1<<EXTIN_ZOOM_R))) {
+		/* Zoom Video Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_ZOOM_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, "Zoom Video Playback Volume", gpr, 0);
+		gpr += 2;
+	
+		/* Zoom Video Capture Volume + Switch */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_ZOOM_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, "Zoom Video Capture Volume", gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "Zoom Video Capture Switch", gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_TOSLINK_L)|(1<<EXTIN_TOSLINK_R))) {
+		/* IEC958 Optical Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_TOSLINK_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("LiveDrive ",PLAYBACK,VOLUME), gpr, 0);
+		gpr += 2;
+	
+		/* IEC958 Optical Capture Volume */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_TOSLINK_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("LiveDrive ",CAPTURE,VOLUME), gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("LiveDrive ",CAPTURE,SWITCH), gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_LINE1_L)|(1<<EXTIN_LINE1_R))) {
+		/* Line LiveDrive Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_LINE1_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Playback Volume", gpr, 0);
+		gpr += 2;
+	
+		/* Line LiveDrive Capture Volume + Switch */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_LINE1_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, "Line LiveDrive Capture Volume", gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "Line LiveDrive Capture Switch", gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_COAX_SPDIF_L)|(1<<EXTIN_COAX_SPDIF_R))) {
+		/* IEC958 Coax Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_COAX_SPDIF_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("Coaxial ",PLAYBACK,VOLUME), gpr, 0);
+		gpr += 2;
+	
+		/* IEC958 Coax Capture Volume + Switch */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_COAX_SPDIF_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("Coaxial ",CAPTURE,VOLUME), gpr, 0);
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("Coaxial ",CAPTURE,SWITCH), gpr + 2, 0);
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extin_mask & ((1<<EXTIN_LINE2_L)|(1<<EXTIN_LINE2_R))) {
+		/* Line LiveDrive Playback Volume */
+		for (z = 0; z < 2; z++)
+			VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_LINE2_L + z, gpr + z);
+		snd_emu10k1_init_stereo_control(controls + i++, "Line2 LiveDrive Playback Volume", gpr, 0);
+		controls[i-1].id.index = 1;
+		gpr += 2;
+	
+		/* Line LiveDrive Capture Volume */
+		for (z = 0; z < 2; z++) {
+			SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_LINE2_L + z, gpr + 2 + z);
+			VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z);
+		}
+		snd_emu10k1_init_stereo_control(controls + i++, "Line2 LiveDrive Capture Volume", gpr, 0);
+		controls[i-1].id.index = 1;
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, "Line2 LiveDrive Capture Switch", gpr + 2, 0);
+		controls[i-1].id.index = 1;
+		gpr += 4;
+	}
+
+	/*
+	 *  Process tone control
+	 */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), GPR(playback + 0), C_00000000, C_00000000); /* left */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), GPR(playback + 1), C_00000000, C_00000000); /* right */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2), GPR(playback + 2), C_00000000, C_00000000); /* rear left */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), GPR(playback + 3), C_00000000, C_00000000); /* rear right */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), GPR(playback + 4), C_00000000, C_00000000); /* center */
+	OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), GPR(playback + 5), C_00000000, C_00000000); /* LFE */
+
+	ctl = &controls[i + 0];
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, "Tone Control - Bass");
+	ctl->vcount = 2;
+	ctl->count = 10;
+	ctl->min = 0;
+	ctl->max = 40;
+	ctl->value[0] = ctl->value[1] = 20;
+	ctl->tlv = snd_emu10k1_bass_treble_db_scale;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_BASS;
+	ctl = &controls[i + 1];
+	ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	strcpy(ctl->id.name, "Tone Control - Treble");
+	ctl->vcount = 2;
+	ctl->count = 10;
+	ctl->min = 0;
+	ctl->max = 40;
+	ctl->value[0] = ctl->value[1] = 20;
+	ctl->tlv = snd_emu10k1_bass_treble_db_scale;
+	ctl->translation = EMU10K1_GPR_TRANSLATION_TREBLE;
+
+#define BASS_GPR	0x8c
+#define TREBLE_GPR	0x96
+
+	for (z = 0; z < 5; z++) {
+		int j;
+		for (j = 0; j < 2; j++) {
+			controls[i + 0].gpr[z * 2 + j] = BASS_GPR + z * 2 + j;
+			controls[i + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j;
+		}
+	}
+	for (z = 0; z < 3; z++) {		/* front/rear/center-lfe */
+		int j, k, l, d;
+		for (j = 0; j < 2; j++) {	/* left/right */
+			k = 0xa0 + (z * 8) + (j * 4);
+			l = 0xd0 + (z * 8) + (j * 4);
+			d = playback + SND_EMU10K1_PLAYBACK_CHANNELS + z * 2 + j;
+
+			OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(d), GPR(BASS_GPR + 0 + j));
+			OP(icode, &ptr, iMACMV, GPR(k+1), GPR(k), GPR(k+1), GPR(BASS_GPR + 4 + j));
+			OP(icode, &ptr, iMACMV, GPR(k), GPR(d), GPR(k), GPR(BASS_GPR + 2 + j));
+			OP(icode, &ptr, iMACMV, GPR(k+3), GPR(k+2), GPR(k+3), GPR(BASS_GPR + 8 + j));
+			OP(icode, &ptr, iMAC0, GPR(k+2), GPR_ACCU, GPR(k+2), GPR(BASS_GPR + 6 + j));
+			OP(icode, &ptr, iACC3, GPR(k+2), GPR(k+2), GPR(k+2), C_00000000);
+
+			OP(icode, &ptr, iMAC0, C_00000000, C_00000000, GPR(k+2), GPR(TREBLE_GPR + 0 + j));
+			OP(icode, &ptr, iMACMV, GPR(l+1), GPR(l), GPR(l+1), GPR(TREBLE_GPR + 4 + j));
+			OP(icode, &ptr, iMACMV, GPR(l), GPR(k+2), GPR(l), GPR(TREBLE_GPR + 2 + j));
+			OP(icode, &ptr, iMACMV, GPR(l+3), GPR(l+2), GPR(l+3), GPR(TREBLE_GPR + 8 + j));
+			OP(icode, &ptr, iMAC0, GPR(l+2), GPR_ACCU, GPR(l+2), GPR(TREBLE_GPR + 6 + j));
+			OP(icode, &ptr, iMACINT0, GPR(l+2), C_00000000, GPR(l+2), C_00000010);
+
+			OP(icode, &ptr, iACC3, GPR(d), GPR(l+2), C_00000000, C_00000000);
+
+			if (z == 2)	/* center */
+				break;
+		}
+	}
+	i += 2;
+
+#undef BASS_GPR
+#undef TREBLE_GPR
+
+	for (z = 0; z < 6; z++) {
+		SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0);
+		SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0);
+		SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1);
+		OP(icode, &ptr, iACC3, GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+	}
+	snd_emu10k1_init_stereo_onoff_control(controls + i++, "Tone Control - Switch", gpr, 0);
+	gpr += 2;
+
+	/*
+	 *  Process outputs
+	 */
+	if (emu->fx8010.extout_mask & ((1<<EXTOUT_AC97_L)|(1<<EXTOUT_AC97_R))) {
+		/* AC'97 Playback Volume */
+
+		for (z = 0; z < 2; z++)
+			OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + z), C_00000000, C_00000000);
+	}
+
+	if (emu->fx8010.extout_mask & ((1<<EXTOUT_TOSLINK_L)|(1<<EXTOUT_TOSLINK_R))) {
+		/* IEC958 Optical Raw Playback Switch */
+
+		for (z = 0; z < 2; z++) {
+			SWITCH(icode, &ptr, tmp + 0, 8 + z, gpr + z);
+			SWITCH_NEG(icode, &ptr, tmp + 1, gpr + z);
+			SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+			OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_TOSLINK_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+#ifdef EMU10K1_CAPTURE_DIGITAL_OUT
+	 		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+#endif
+		}
+
+		snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0);
+		gpr += 2;
+	}
+
+	if (emu->fx8010.extout_mask & ((1<<EXTOUT_HEADPHONE_L)|(1<<EXTOUT_HEADPHONE_R))) {
+		/* Headphone Playback Volume */
+
+		for (z = 0; z < 2; z++) {
+			SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4 + z, gpr + 2 + z);
+			SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 2 + z);
+			SWITCH(icode, &ptr, tmp + 1, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, tmp + 1);
+			OP(icode, &ptr, iACC3, GPR(tmp + 0), GPR(tmp + 0), GPR(tmp + 1), C_00000000);
+			VOLUME_OUT(icode, &ptr, EXTOUT_HEADPHONE_L + z, tmp + 0, gpr + z);
+		}
+
+		snd_emu10k1_init_stereo_control(controls + i++, "Headphone Playback Volume", gpr + 0, 0);
+		controls[i-1].id.index = 1;	/* AC'97 can have also Headphone control */
+		snd_emu10k1_init_mono_onoff_control(controls + i++, "Headphone Center Playback Switch", gpr + 2, 0);
+		controls[i-1].id.index = 1;
+		snd_emu10k1_init_mono_onoff_control(controls + i++, "Headphone LFE Playback Switch", gpr + 3, 0);
+		controls[i-1].id.index = 1;
+
+		gpr += 4;
+	}
+	
+	if (emu->fx8010.extout_mask & ((1<<EXTOUT_REAR_L)|(1<<EXTOUT_REAR_R)))
+		for (z = 0; z < 2; z++)
+			OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+
+	if (emu->fx8010.extout_mask & ((1<<EXTOUT_AC97_REAR_L)|(1<<EXTOUT_AC97_REAR_R)))
+		for (z = 0; z < 2; z++)
+			OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_REAR_L + z), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 2 + z), C_00000000, C_00000000);
+
+	if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_CENTER)) {
+#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), C_00000000, C_00000000);
+#else
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_CENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ACENTER), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 0), C_00000000, C_00000000);
+#endif
+	}
+
+	if (emu->fx8010.extout_mask & (1<<EXTOUT_AC97_LFE)) {
+#ifndef EMU10K1_CENTER_LFE_FROM_FRONT
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), C_00000000, C_00000000);
+#else
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_AC97_LFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ALFE), GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 1), C_00000000, C_00000000);
+#endif
+	}
+	
+#ifndef EMU10K1_CAPTURE_DIGITAL_OUT
+	for (z = 0; z < 2; z++)
+ 		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_ADC_CAP_L + z), GPR(capture + z), C_00000000, C_00000000);
+#endif
+	
+	if (emu->fx8010.extout_mask & (1<<EXTOUT_MIC_CAP))
+		OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_MIC_CAP), GPR(capture + 2), C_00000000, C_00000000);
+
+	/* EFX capture - capture the 16 EXTINS */
+	if (emu->card_capabilities->sblive51) {
+		/* On the Live! 5.1, FXBUS2(1) and FXBUS(2) are shared with EXTOUT_ACENTER
+		 * and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording.
+		 *
+		 * Since only 14 of the 16 EXTINs are used, this is not a big problem.  
+		 * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture 
+		 * 0 and 3, then the rest of the EXTINs to the corresponding FX capture 
+		 * channel.  Multitrack recorders will still see the center/lfe output signal 
+		 * on the second and third channels.
+		 */
+		OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0));
+		OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1));
+		OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2));
+		OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3));
+		for (z = 4; z < 14; z++)
+			OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+	} else {
+		for (z = 0; z < 16; z++)
+			OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+	}
+	    
+
+	if (gpr > tmp) {
+		snd_BUG();
+		err = -EIO;
+		goto __err;
+	}
+	if (i > SND_EMU10K1_GPR_CONTROLS) {
+		snd_BUG();
+		err = -EIO;
+		goto __err;
+	}
+	
+	/* clear remaining instruction memory */
+	while (ptr < 0x200)
+		OP(icode, &ptr, iACC3, C_00000000, C_00000000, C_00000000, C_00000000);
+
+	if ((err = snd_emu10k1_fx8010_tram_setup(emu, ipcm->buffer_size)) < 0)
+		goto __err;
+	icode->gpr_add_control_count = i;
+	icode->gpr_add_controls = (struct snd_emu10k1_fx8010_control_gpr __user *)controls;
+	emu->support_tlv = 1; /* support TLV */
+	err = snd_emu10k1_icode_poke(emu, icode, true);
+	emu->support_tlv = 0; /* clear again */
+	if (err >= 0)
+		err = snd_emu10k1_ipcm_poke(emu, ipcm);
+__err:
+	kfree(ipcm);
+__err_ipcm:
+	kfree(controls);
+__err_ctrls:
+	kfree((void __force *)icode->gpr_map);
+__err_gpr:
+	kfree(icode);
+	return err;
+}
+
+int snd_emu10k1_init_efx(struct snd_emu10k1 *emu)
+{
+	spin_lock_init(&emu->fx8010.irq_lock);
+	INIT_LIST_HEAD(&emu->fx8010.gpr_ctl);
+	if (emu->audigy)
+		return _snd_emu10k1_audigy_init_efx(emu);
+	else
+		return _snd_emu10k1_init_efx(emu);
+}
+
+void snd_emu10k1_free_efx(struct snd_emu10k1 *emu)
+{
+	/* stop processor */
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = A_DBG_SINGLE_STEP);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = EMU10K1_DBG_SINGLE_STEP);
+}
+
+#if 0 /* FIXME: who use them? */
+int snd_emu10k1_fx8010_tone_control_activate(struct snd_emu10k1 *emu, int output)
+{
+	if (output < 0 || output >= 6)
+		return -EINVAL;
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 1);
+	return 0;
+}
+
+int snd_emu10k1_fx8010_tone_control_deactivate(struct snd_emu10k1 *emu, int output)
+{
+	if (output < 0 || output >= 6)
+		return -EINVAL;
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + 0x94 + output, 0, 0);
+	return 0;
+}
+#endif
+
+int snd_emu10k1_fx8010_tram_setup(struct snd_emu10k1 *emu, u32 size)
+{
+	u8 size_reg = 0;
+
+	/* size is in samples */
+	if (size != 0) {
+		size = (size - 1) >> 13;
+
+		while (size) {
+			size >>= 1;
+			size_reg++;
+		}
+		size = 0x2000 << size_reg;
+	}
+	if ((emu->fx8010.etram_pages.bytes / 2) == size)
+		return 0;
+	spin_lock_irq(&emu->emu_lock);
+	outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
+	spin_unlock_irq(&emu->emu_lock);
+	snd_emu10k1_ptr_write(emu, TCB, 0, 0);
+	snd_emu10k1_ptr_write(emu, TCBS, 0, 0);
+	if (emu->fx8010.etram_pages.area != NULL) {
+		snd_dma_free_pages(&emu->fx8010.etram_pages);
+		emu->fx8010.etram_pages.area = NULL;
+		emu->fx8010.etram_pages.bytes = 0;
+	}
+
+	if (size > 0) {
+		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci),
+					size * 2, &emu->fx8010.etram_pages) < 0)
+			return -ENOMEM;
+		memset(emu->fx8010.etram_pages.area, 0, size * 2);
+		snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr);
+		snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg);
+		spin_lock_irq(&emu->emu_lock);
+		outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
+		spin_unlock_irq(&emu->emu_lock);
+	}
+
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_open(struct snd_hwdep * hw, struct file *file)
+{
+	return 0;
+}
+
+static void copy_string(char *dst, char *src, char *null, int idx)
+{
+	if (src == NULL)
+		sprintf(dst, "%s %02X", null, idx);
+	else
+		strcpy(dst, src);
+}
+
+static void snd_emu10k1_fx8010_info(struct snd_emu10k1 *emu,
+				   struct snd_emu10k1_fx8010_info *info)
+{
+	char **fxbus, **extin, **extout;
+	unsigned short fxbus_mask, extin_mask, extout_mask;
+	int res;
+
+	info->internal_tram_size = emu->fx8010.itram_size;
+	info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
+	fxbus = fxbuses;
+	extin = emu->audigy ? audigy_ins : creative_ins;
+	extout = emu->audigy ? audigy_outs : creative_outs;
+	fxbus_mask = emu->fx8010.fxbus_mask;
+	extin_mask = emu->fx8010.extin_mask;
+	extout_mask = emu->fx8010.extout_mask;
+	for (res = 0; res < 16; res++, fxbus++, extin++, extout++) {
+		copy_string(info->fxbus_names[res], fxbus_mask & (1 << res) ? *fxbus : NULL, "FXBUS", res);
+		copy_string(info->extin_names[res], extin_mask & (1 << res) ? *extin : NULL, "Unused", res);
+		copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
+	}
+	for (res = 16; res < 32; res++, extout++)
+		copy_string(info->extout_names[res], extout_mask & (1 << res) ? *extout : NULL, "Unused", res);
+	info->gpr_controls = emu->fx8010.gpr_count;
+}
+
+static int snd_emu10k1_fx8010_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct snd_emu10k1 *emu = hw->private_data;
+	struct snd_emu10k1_fx8010_info *info;
+	struct snd_emu10k1_fx8010_code *icode;
+	struct snd_emu10k1_fx8010_pcm_rec *ipcm;
+	unsigned int addr;
+	void __user *argp = (void __user *)arg;
+	int res;
+	
+	switch (cmd) {
+	case SNDRV_EMU10K1_IOCTL_PVERSION:
+		emu->support_tlv = 1;
+		return put_user(SNDRV_EMU10K1_VERSION, (int __user *)argp);
+	case SNDRV_EMU10K1_IOCTL_INFO:
+		info = kzalloc(sizeof(*info), GFP_KERNEL);
+		if (!info)
+			return -ENOMEM;
+		snd_emu10k1_fx8010_info(emu, info);
+		if (copy_to_user(argp, info, sizeof(*info))) {
+			kfree(info);
+			return -EFAULT;
+		}
+		kfree(info);
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_CODE_POKE:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		icode = memdup_user(argp, sizeof(*icode));
+		if (IS_ERR(icode))
+			return PTR_ERR(icode);
+		res = snd_emu10k1_icode_poke(emu, icode, false);
+		kfree(icode);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_CODE_PEEK:
+		icode = memdup_user(argp, sizeof(*icode));
+		if (IS_ERR(icode))
+			return PTR_ERR(icode);
+		res = snd_emu10k1_icode_peek(emu, icode);
+		if (res == 0 && copy_to_user(argp, icode, sizeof(*icode))) {
+			kfree(icode);
+			return -EFAULT;
+		}
+		kfree(icode);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_PCM_POKE:
+		ipcm = memdup_user(argp, sizeof(*ipcm));
+		if (IS_ERR(ipcm))
+			return PTR_ERR(ipcm);
+		res = snd_emu10k1_ipcm_poke(emu, ipcm);
+		kfree(ipcm);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_PCM_PEEK:
+		ipcm = memdup_user(argp, sizeof(*ipcm));
+		if (IS_ERR(ipcm))
+			return PTR_ERR(ipcm);
+		res = snd_emu10k1_ipcm_peek(emu, ipcm);
+		if (res == 0 && copy_to_user(argp, ipcm, sizeof(*ipcm))) {
+			kfree(ipcm);
+			return -EFAULT;
+		}
+		kfree(ipcm);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_TRAM_SETUP:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (get_user(addr, (unsigned int __user *)argp))
+			return -EFAULT;
+		mutex_lock(&emu->fx8010.lock);
+		res = snd_emu10k1_fx8010_tram_setup(emu, addr);
+		mutex_unlock(&emu->fx8010.lock);
+		return res;
+	case SNDRV_EMU10K1_IOCTL_STOP:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP);
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_CONTINUE:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg = 0);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg = 0);
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_ZC);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_ZC);
+		udelay(10);
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_SINGLE_STEP:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		if (get_user(addr, (unsigned int __user *)argp))
+			return -EFAULT;
+		if (addr > 0x1ff)
+			return -EINVAL;
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | addr);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | addr);
+		udelay(10);
+		if (emu->audigy)
+			snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg |= A_DBG_SINGLE_STEP | A_DBG_STEP_ADDR | addr);
+		else
+			snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg |= EMU10K1_DBG_SINGLE_STEP | EMU10K1_DBG_STEP | addr);
+		return 0;
+	case SNDRV_EMU10K1_IOCTL_DBG_READ:
+		if (emu->audigy)
+			addr = snd_emu10k1_ptr_read(emu, A_DBG, 0);
+		else
+			addr = snd_emu10k1_ptr_read(emu, DBG, 0);
+		if (put_user(addr, (unsigned int __user *)argp))
+			return -EFAULT;
+		return 0;
+	}
+	return -ENOTTY;
+}
+
+static int snd_emu10k1_fx8010_release(struct snd_hwdep * hw, struct file *file)
+{
+	return 0;
+}
+
+int snd_emu10k1_fx8010_new(struct snd_emu10k1 *emu, int device)
+{
+	struct snd_hwdep *hw;
+	int err;
+	
+	if ((err = snd_hwdep_new(emu->card, "FX8010", device, &hw)) < 0)
+		return err;
+	strcpy(hw->name, "EMU10K1 (FX8010)");
+	hw->iface = SNDRV_HWDEP_IFACE_EMU10K1;
+	hw->ops.open = snd_emu10k1_fx8010_open;
+	hw->ops.ioctl = snd_emu10k1_fx8010_ioctl;
+	hw->ops.release = snd_emu10k1_fx8010_release;
+	hw->private_data = emu;
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+int snd_emu10k1_efx_alloc_pm_buffer(struct snd_emu10k1 *emu)
+{
+	int len;
+
+	len = emu->audigy ? 0x200 : 0x100;
+	emu->saved_gpr = kmalloc(len * 4, GFP_KERNEL);
+	if (! emu->saved_gpr)
+		return -ENOMEM;
+	len = emu->audigy ? 0x100 : 0xa0;
+	emu->tram_val_saved = kmalloc(len * 4, GFP_KERNEL);
+	emu->tram_addr_saved = kmalloc(len * 4, GFP_KERNEL);
+	if (! emu->tram_val_saved || ! emu->tram_addr_saved)
+		return -ENOMEM;
+	len = emu->audigy ? 2 * 1024 : 2 * 512;
+	emu->saved_icode = vmalloc(len * 4);
+	if (! emu->saved_icode)
+		return -ENOMEM;
+	return 0;
+}
+
+void snd_emu10k1_efx_free_pm_buffer(struct snd_emu10k1 *emu)
+{
+	kfree(emu->saved_gpr);
+	kfree(emu->tram_val_saved);
+	kfree(emu->tram_addr_saved);
+	vfree(emu->saved_icode);
+}
+
+/*
+ * save/restore GPR, TRAM and codes
+ */
+void snd_emu10k1_efx_suspend(struct snd_emu10k1 *emu)
+{
+	int i, len;
+
+	len = emu->audigy ? 0x200 : 0x100;
+	for (i = 0; i < len; i++)
+		emu->saved_gpr[i] = snd_emu10k1_ptr_read(emu, emu->gpr_base + i, 0);
+
+	len = emu->audigy ? 0x100 : 0xa0;
+	for (i = 0; i < len; i++) {
+		emu->tram_val_saved[i] = snd_emu10k1_ptr_read(emu, TANKMEMDATAREGBASE + i, 0);
+		emu->tram_addr_saved[i] = snd_emu10k1_ptr_read(emu, TANKMEMADDRREGBASE + i, 0);
+		if (emu->audigy) {
+			emu->tram_addr_saved[i] >>= 12;
+			emu->tram_addr_saved[i] |=
+				snd_emu10k1_ptr_read(emu, A_TANKMEMCTLREGBASE + i, 0) << 20;
+		}
+	}
+
+	len = emu->audigy ? 2 * 1024 : 2 * 512;
+	for (i = 0; i < len; i++)
+		emu->saved_icode[i] = snd_emu10k1_efx_read(emu, i);
+}
+
+void snd_emu10k1_efx_resume(struct snd_emu10k1 *emu)
+{
+	int i, len;
+
+	/* set up TRAM */
+	if (emu->fx8010.etram_pages.bytes > 0) {
+		unsigned size, size_reg = 0;
+		size = emu->fx8010.etram_pages.bytes / 2;
+		size = (size - 1) >> 13;
+		while (size) {
+			size >>= 1;
+			size_reg++;
+		}
+		outl(HCFG_LOCKTANKCACHE_MASK | inl(emu->port + HCFG), emu->port + HCFG);
+		snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr);
+		snd_emu10k1_ptr_write(emu, TCBS, 0, size_reg);
+		outl(inl(emu->port + HCFG) & ~HCFG_LOCKTANKCACHE_MASK, emu->port + HCFG);
+	}
+
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg | A_DBG_SINGLE_STEP);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg | EMU10K1_DBG_SINGLE_STEP);
+
+	len = emu->audigy ? 0x200 : 0x100;
+	for (i = 0; i < len; i++)
+		snd_emu10k1_ptr_write(emu, emu->gpr_base + i, 0, emu->saved_gpr[i]);
+
+	len = emu->audigy ? 0x100 : 0xa0;
+	for (i = 0; i < len; i++) {
+		snd_emu10k1_ptr_write(emu, TANKMEMDATAREGBASE + i, 0,
+				      emu->tram_val_saved[i]);
+		if (! emu->audigy)
+			snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + i, 0,
+					      emu->tram_addr_saved[i]);
+		else {
+			snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + i, 0,
+					      emu->tram_addr_saved[i] << 12);
+			snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + i, 0,
+					      emu->tram_addr_saved[i] >> 20);
+		}
+	}
+
+	len = emu->audigy ? 2 * 1024 : 2 * 512;
+	for (i = 0; i < len; i++)
+		snd_emu10k1_efx_write(emu, i, emu->saved_icode[i]);
+
+	/* start FX processor when the DSP code is updated */
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, A_DBG, 0, emu->fx8010.dbg);
+	else
+		snd_emu10k1_ptr_write(emu, DBG, 0, emu->fx8010.dbg);
+}
+#endif
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emumixer.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emumixer.c
new file mode 100644
index 0000000..b2219a7
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emumixer.c
@@ -0,0 +1,2234 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
+ *                   Takashi Iwai <tiwai@suse.de>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips / mixer routines
+ *  Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ *  	Added EMU 1010 support.
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#include <linux/delay.h>
+#include <sound/tlv.h>
+
+#include "p17v.h"
+
+#define AC97_ID_STAC9758	0x83847658
+
+static const DECLARE_TLV_DB_SCALE(snd_audigy_db_scale2, -10350, 50, 1); /* WM8775 gain scale */
+
+static int snd_emu10k1_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int snd_emu10k1_spdif_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	unsigned long flags;
+
+	/* Limit: emu->spdif_bits */
+	if (idx >= 3)
+		return -EINVAL;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0xff;
+	return 0;
+}
+
+/*
+ * Items labels in enum mixer controls assigning source data to
+ * each destination
+ */
+static const char * const emu1010_src_texts[] = {
+	"Silence",
+	"Dock Mic A",
+	"Dock Mic B",
+	"Dock ADC1 Left",
+	"Dock ADC1 Right",
+	"Dock ADC2 Left",
+	"Dock ADC2 Right",
+	"Dock ADC3 Left",
+	"Dock ADC3 Right",
+	"0202 ADC Left",
+	"0202 ADC Right",
+	"0202 SPDIF Left",
+	"0202 SPDIF Right",
+	"ADAT 0",
+	"ADAT 1",
+	"ADAT 2",
+	"ADAT 3",
+	"ADAT 4",
+	"ADAT 5",
+	"ADAT 6",
+	"ADAT 7",
+	"DSP 0",
+	"DSP 1",
+	"DSP 2",
+	"DSP 3",
+	"DSP 4",
+	"DSP 5",
+	"DSP 6",
+	"DSP 7",
+	"DSP 8",
+	"DSP 9",
+	"DSP 10",
+	"DSP 11",
+	"DSP 12",
+	"DSP 13",
+	"DSP 14",
+	"DSP 15",
+	"DSP 16",
+	"DSP 17",
+	"DSP 18",
+	"DSP 19",
+	"DSP 20",
+	"DSP 21",
+	"DSP 22",
+	"DSP 23",
+	"DSP 24",
+	"DSP 25",
+	"DSP 26",
+	"DSP 27",
+	"DSP 28",
+	"DSP 29",
+	"DSP 30",
+	"DSP 31",
+};
+
+/* 1616(m) cardbus */
+
+static const char * const emu1616_src_texts[] = {
+	"Silence",
+	"Dock Mic A",
+	"Dock Mic B",
+	"Dock ADC1 Left",
+	"Dock ADC1 Right",
+	"Dock ADC2 Left",
+	"Dock ADC2 Right",
+	"Dock SPDIF Left",
+	"Dock SPDIF Right",
+	"ADAT 0",
+	"ADAT 1",
+	"ADAT 2",
+	"ADAT 3",
+	"ADAT 4",
+	"ADAT 5",
+	"ADAT 6",
+	"ADAT 7",
+	"DSP 0",
+	"DSP 1",
+	"DSP 2",
+	"DSP 3",
+	"DSP 4",
+	"DSP 5",
+	"DSP 6",
+	"DSP 7",
+	"DSP 8",
+	"DSP 9",
+	"DSP 10",
+	"DSP 11",
+	"DSP 12",
+	"DSP 13",
+	"DSP 14",
+	"DSP 15",
+	"DSP 16",
+	"DSP 17",
+	"DSP 18",
+	"DSP 19",
+	"DSP 20",
+	"DSP 21",
+	"DSP 22",
+	"DSP 23",
+	"DSP 24",
+	"DSP 25",
+	"DSP 26",
+	"DSP 27",
+	"DSP 28",
+	"DSP 29",
+	"DSP 30",
+	"DSP 31",
+};
+
+
+/*
+ * List of data sources available for each destination
+ */
+static unsigned int emu1010_src_regs[] = {
+	EMU_SRC_SILENCE,/* 0 */
+	EMU_SRC_DOCK_MIC_A1, /* 1 */
+	EMU_SRC_DOCK_MIC_B1, /* 2 */
+	EMU_SRC_DOCK_ADC1_LEFT1, /* 3 */
+	EMU_SRC_DOCK_ADC1_RIGHT1, /* 4 */
+	EMU_SRC_DOCK_ADC2_LEFT1, /* 5 */
+	EMU_SRC_DOCK_ADC2_RIGHT1, /* 6 */
+	EMU_SRC_DOCK_ADC3_LEFT1, /* 7 */
+	EMU_SRC_DOCK_ADC3_RIGHT1, /* 8 */
+	EMU_SRC_HAMOA_ADC_LEFT1, /* 9 */
+	EMU_SRC_HAMOA_ADC_RIGHT1, /* 10 */
+	EMU_SRC_HANA_SPDIF_LEFT1, /* 11 */
+	EMU_SRC_HANA_SPDIF_RIGHT1, /* 12 */
+	EMU_SRC_HANA_ADAT, /* 13 */
+	EMU_SRC_HANA_ADAT+1, /* 14 */
+	EMU_SRC_HANA_ADAT+2, /* 15 */
+	EMU_SRC_HANA_ADAT+3, /* 16 */
+	EMU_SRC_HANA_ADAT+4, /* 17 */
+	EMU_SRC_HANA_ADAT+5, /* 18 */
+	EMU_SRC_HANA_ADAT+6, /* 19 */
+	EMU_SRC_HANA_ADAT+7, /* 20 */
+	EMU_SRC_ALICE_EMU32A, /* 21 */
+	EMU_SRC_ALICE_EMU32A+1, /* 22 */
+	EMU_SRC_ALICE_EMU32A+2, /* 23 */
+	EMU_SRC_ALICE_EMU32A+3, /* 24 */
+	EMU_SRC_ALICE_EMU32A+4, /* 25 */
+	EMU_SRC_ALICE_EMU32A+5, /* 26 */
+	EMU_SRC_ALICE_EMU32A+6, /* 27 */
+	EMU_SRC_ALICE_EMU32A+7, /* 28 */
+	EMU_SRC_ALICE_EMU32A+8, /* 29 */
+	EMU_SRC_ALICE_EMU32A+9, /* 30 */
+	EMU_SRC_ALICE_EMU32A+0xa, /* 31 */
+	EMU_SRC_ALICE_EMU32A+0xb, /* 32 */
+	EMU_SRC_ALICE_EMU32A+0xc, /* 33 */
+	EMU_SRC_ALICE_EMU32A+0xd, /* 34 */
+	EMU_SRC_ALICE_EMU32A+0xe, /* 35 */
+	EMU_SRC_ALICE_EMU32A+0xf, /* 36 */
+	EMU_SRC_ALICE_EMU32B, /* 37 */
+	EMU_SRC_ALICE_EMU32B+1, /* 38 */
+	EMU_SRC_ALICE_EMU32B+2, /* 39 */
+	EMU_SRC_ALICE_EMU32B+3, /* 40 */
+	EMU_SRC_ALICE_EMU32B+4, /* 41 */
+	EMU_SRC_ALICE_EMU32B+5, /* 42 */
+	EMU_SRC_ALICE_EMU32B+6, /* 43 */
+	EMU_SRC_ALICE_EMU32B+7, /* 44 */
+	EMU_SRC_ALICE_EMU32B+8, /* 45 */
+	EMU_SRC_ALICE_EMU32B+9, /* 46 */
+	EMU_SRC_ALICE_EMU32B+0xa, /* 47 */
+	EMU_SRC_ALICE_EMU32B+0xb, /* 48 */
+	EMU_SRC_ALICE_EMU32B+0xc, /* 49 */
+	EMU_SRC_ALICE_EMU32B+0xd, /* 50 */
+	EMU_SRC_ALICE_EMU32B+0xe, /* 51 */
+	EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
+};
+
+/* 1616(m) cardbus */
+static unsigned int emu1616_src_regs[] = {
+	EMU_SRC_SILENCE,
+	EMU_SRC_DOCK_MIC_A1,
+	EMU_SRC_DOCK_MIC_B1,
+	EMU_SRC_DOCK_ADC1_LEFT1,
+	EMU_SRC_DOCK_ADC1_RIGHT1,
+	EMU_SRC_DOCK_ADC2_LEFT1,
+	EMU_SRC_DOCK_ADC2_RIGHT1,
+	EMU_SRC_MDOCK_SPDIF_LEFT1,
+	EMU_SRC_MDOCK_SPDIF_RIGHT1,
+	EMU_SRC_MDOCK_ADAT,
+	EMU_SRC_MDOCK_ADAT+1,
+	EMU_SRC_MDOCK_ADAT+2,
+	EMU_SRC_MDOCK_ADAT+3,
+	EMU_SRC_MDOCK_ADAT+4,
+	EMU_SRC_MDOCK_ADAT+5,
+	EMU_SRC_MDOCK_ADAT+6,
+	EMU_SRC_MDOCK_ADAT+7,
+	EMU_SRC_ALICE_EMU32A,
+	EMU_SRC_ALICE_EMU32A+1,
+	EMU_SRC_ALICE_EMU32A+2,
+	EMU_SRC_ALICE_EMU32A+3,
+	EMU_SRC_ALICE_EMU32A+4,
+	EMU_SRC_ALICE_EMU32A+5,
+	EMU_SRC_ALICE_EMU32A+6,
+	EMU_SRC_ALICE_EMU32A+7,
+	EMU_SRC_ALICE_EMU32A+8,
+	EMU_SRC_ALICE_EMU32A+9,
+	EMU_SRC_ALICE_EMU32A+0xa,
+	EMU_SRC_ALICE_EMU32A+0xb,
+	EMU_SRC_ALICE_EMU32A+0xc,
+	EMU_SRC_ALICE_EMU32A+0xd,
+	EMU_SRC_ALICE_EMU32A+0xe,
+	EMU_SRC_ALICE_EMU32A+0xf,
+	EMU_SRC_ALICE_EMU32B,
+	EMU_SRC_ALICE_EMU32B+1,
+	EMU_SRC_ALICE_EMU32B+2,
+	EMU_SRC_ALICE_EMU32B+3,
+	EMU_SRC_ALICE_EMU32B+4,
+	EMU_SRC_ALICE_EMU32B+5,
+	EMU_SRC_ALICE_EMU32B+6,
+	EMU_SRC_ALICE_EMU32B+7,
+	EMU_SRC_ALICE_EMU32B+8,
+	EMU_SRC_ALICE_EMU32B+9,
+	EMU_SRC_ALICE_EMU32B+0xa,
+	EMU_SRC_ALICE_EMU32B+0xb,
+	EMU_SRC_ALICE_EMU32B+0xc,
+	EMU_SRC_ALICE_EMU32B+0xd,
+	EMU_SRC_ALICE_EMU32B+0xe,
+	EMU_SRC_ALICE_EMU32B+0xf,
+};
+
+/*
+ * Data destinations - physical EMU outputs.
+ * Each destination has an enum mixer control to choose a data source
+ */
+static unsigned int emu1010_output_dst[] = {
+	EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
+	EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
+	EMU_DST_DOCK_DAC2_LEFT1, /* 2 */
+	EMU_DST_DOCK_DAC2_RIGHT1, /* 3 */
+	EMU_DST_DOCK_DAC3_LEFT1, /* 4 */
+	EMU_DST_DOCK_DAC3_RIGHT1, /* 5 */
+	EMU_DST_DOCK_DAC4_LEFT1, /* 6 */
+	EMU_DST_DOCK_DAC4_RIGHT1, /* 7 */
+	EMU_DST_DOCK_PHONES_LEFT1, /* 8 */
+	EMU_DST_DOCK_PHONES_RIGHT1, /* 9 */
+	EMU_DST_DOCK_SPDIF_LEFT1, /* 10 */
+	EMU_DST_DOCK_SPDIF_RIGHT1, /* 11 */
+	EMU_DST_HANA_SPDIF_LEFT1, /* 12 */
+	EMU_DST_HANA_SPDIF_RIGHT1, /* 13 */
+	EMU_DST_HAMOA_DAC_LEFT1, /* 14 */
+	EMU_DST_HAMOA_DAC_RIGHT1, /* 15 */
+	EMU_DST_HANA_ADAT, /* 16 */
+	EMU_DST_HANA_ADAT+1, /* 17 */
+	EMU_DST_HANA_ADAT+2, /* 18 */
+	EMU_DST_HANA_ADAT+3, /* 19 */
+	EMU_DST_HANA_ADAT+4, /* 20 */
+	EMU_DST_HANA_ADAT+5, /* 21 */
+	EMU_DST_HANA_ADAT+6, /* 22 */
+	EMU_DST_HANA_ADAT+7, /* 23 */
+};
+
+/* 1616(m) cardbus */
+static unsigned int emu1616_output_dst[] = {
+	EMU_DST_DOCK_DAC1_LEFT1,
+	EMU_DST_DOCK_DAC1_RIGHT1,
+	EMU_DST_DOCK_DAC2_LEFT1,
+	EMU_DST_DOCK_DAC2_RIGHT1,
+	EMU_DST_DOCK_DAC3_LEFT1,
+	EMU_DST_DOCK_DAC3_RIGHT1,
+	EMU_DST_MDOCK_SPDIF_LEFT1,
+	EMU_DST_MDOCK_SPDIF_RIGHT1,
+	EMU_DST_MDOCK_ADAT,
+	EMU_DST_MDOCK_ADAT+1,
+	EMU_DST_MDOCK_ADAT+2,
+	EMU_DST_MDOCK_ADAT+3,
+	EMU_DST_MDOCK_ADAT+4,
+	EMU_DST_MDOCK_ADAT+5,
+	EMU_DST_MDOCK_ADAT+6,
+	EMU_DST_MDOCK_ADAT+7,
+	EMU_DST_MANA_DAC_LEFT,
+	EMU_DST_MANA_DAC_RIGHT,
+};
+
+/*
+ * Data destinations - HANA outputs going to Alice2 (audigy) for
+ *   capture (EMU32 + I2S links)
+ * Each destination has an enum mixer control to choose a data source
+ */
+static unsigned int emu1010_input_dst[] = {
+	EMU_DST_ALICE2_EMU32_0,
+	EMU_DST_ALICE2_EMU32_1,
+	EMU_DST_ALICE2_EMU32_2,
+	EMU_DST_ALICE2_EMU32_3,
+	EMU_DST_ALICE2_EMU32_4,
+	EMU_DST_ALICE2_EMU32_5,
+	EMU_DST_ALICE2_EMU32_6,
+	EMU_DST_ALICE2_EMU32_7,
+	EMU_DST_ALICE2_EMU32_8,
+	EMU_DST_ALICE2_EMU32_9,
+	EMU_DST_ALICE2_EMU32_A,
+	EMU_DST_ALICE2_EMU32_B,
+	EMU_DST_ALICE2_EMU32_C,
+	EMU_DST_ALICE2_EMU32_D,
+	EMU_DST_ALICE2_EMU32_E,
+	EMU_DST_ALICE2_EMU32_F,
+	EMU_DST_ALICE_I2S0_LEFT,
+	EMU_DST_ALICE_I2S0_RIGHT,
+	EMU_DST_ALICE_I2S1_LEFT,
+	EMU_DST_ALICE_I2S1_RIGHT,
+	EMU_DST_ALICE_I2S2_LEFT,
+	EMU_DST_ALICE_I2S2_RIGHT,
+};
+
+static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol,
+						struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
+		return snd_ctl_enum_info(uinfo, 1, 49, emu1616_src_texts);
+	else
+		return snd_ctl_enum_info(uinfo, 1, 53, emu1010_src_texts);
+}
+
+static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int channel;
+
+	channel = (kcontrol->private_value) & 0xff;
+	/* Limit: emu1010_output_dst, emu->emu1010.output_source */
+	if (channel >= 24 ||
+	    (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+	     channel >= 18))
+		return -EINVAL;
+	ucontrol->value.enumerated.item[0] = emu->emu1010.output_source[channel];
+	return 0;
+}
+
+static int snd_emu1010_output_source_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	unsigned int channel;
+
+	val = ucontrol->value.enumerated.item[0];
+	if (val >= 53 ||
+	    (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+	     val >= 49))
+		return -EINVAL;
+	channel = (kcontrol->private_value) & 0xff;
+	/* Limit: emu1010_output_dst, emu->emu1010.output_source */
+	if (channel >= 24 ||
+	    (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+	     channel >= 18))
+		return -EINVAL;
+	if (emu->emu1010.output_source[channel] == val)
+		return 0;
+	emu->emu1010.output_source[channel] = val;
+	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			emu1616_output_dst[channel], emu1616_src_regs[val]);
+	else
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			emu1010_output_dst[channel], emu1010_src_regs[val]);
+	return 1;
+}
+
+static int snd_emu1010_input_source_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int channel;
+
+	channel = (kcontrol->private_value) & 0xff;
+	/* Limit: emu1010_input_dst, emu->emu1010.input_source */
+	if (channel >= 22)
+		return -EINVAL;
+	ucontrol->value.enumerated.item[0] = emu->emu1010.input_source[channel];
+	return 0;
+}
+
+static int snd_emu1010_input_source_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	unsigned int channel;
+
+	val = ucontrol->value.enumerated.item[0];
+	if (val >= 53 ||
+	    (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616 &&
+	     val >= 49))
+		return -EINVAL;
+	channel = (kcontrol->private_value) & 0xff;
+	/* Limit: emu1010_input_dst, emu->emu1010.input_source */
+	if (channel >= 22)
+		return -EINVAL;
+	if (emu->emu1010.input_source[channel] == val)
+		return 0;
+	emu->emu1010.input_source[channel] = val;
+	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616)
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			emu1010_input_dst[channel], emu1616_src_regs[val]);
+	else
+		snd_emu1010_fpga_link_dst_src_write(emu,
+			emu1010_input_dst[channel], emu1010_src_regs[val]);
+	return 1;
+}
+
+#define EMU1010_SOURCE_OUTPUT(xname,chid) \
+{								\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
+	.info =  snd_emu1010_input_output_source_info,		\
+	.get =   snd_emu1010_output_source_get,			\
+	.put =   snd_emu1010_output_source_put,			\
+	.private_value = chid					\
+}
+
+static struct snd_kcontrol_new snd_emu1010_output_enum_ctls[] = {
+	EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
+	EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
+	EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
+	EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
+	EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
+	EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
+	EMU1010_SOURCE_OUTPUT("Dock DAC4 Left Playback Enum", 6),
+	EMU1010_SOURCE_OUTPUT("Dock DAC4 Right Playback Enum", 7),
+	EMU1010_SOURCE_OUTPUT("Dock Phones Left Playback Enum", 8),
+	EMU1010_SOURCE_OUTPUT("Dock Phones Right Playback Enum", 9),
+	EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 0xa),
+	EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 0xb),
+	EMU1010_SOURCE_OUTPUT("1010 SPDIF Left Playback Enum", 0xc),
+	EMU1010_SOURCE_OUTPUT("1010 SPDIF Right Playback Enum", 0xd),
+	EMU1010_SOURCE_OUTPUT("0202 DAC Left Playback Enum", 0xe),
+	EMU1010_SOURCE_OUTPUT("0202 DAC Right Playback Enum", 0xf),
+	EMU1010_SOURCE_OUTPUT("1010 ADAT 0 Playback Enum", 0x10),
+	EMU1010_SOURCE_OUTPUT("1010 ADAT 1 Playback Enum", 0x11),
+	EMU1010_SOURCE_OUTPUT("1010 ADAT 2 Playback Enum", 0x12),
+	EMU1010_SOURCE_OUTPUT("1010 ADAT 3 Playback Enum", 0x13),
+	EMU1010_SOURCE_OUTPUT("1010 ADAT 4 Playback Enum", 0x14),
+	EMU1010_SOURCE_OUTPUT("1010 ADAT 5 Playback Enum", 0x15),
+	EMU1010_SOURCE_OUTPUT("1010 ADAT 6 Playback Enum", 0x16),
+	EMU1010_SOURCE_OUTPUT("1010 ADAT 7 Playback Enum", 0x17),
+};
+
+
+/* 1616(m) cardbus */
+static struct snd_kcontrol_new snd_emu1616_output_enum_ctls[] = {
+	EMU1010_SOURCE_OUTPUT("Dock DAC1 Left Playback Enum", 0),
+	EMU1010_SOURCE_OUTPUT("Dock DAC1 Right Playback Enum", 1),
+	EMU1010_SOURCE_OUTPUT("Dock DAC2 Left Playback Enum", 2),
+	EMU1010_SOURCE_OUTPUT("Dock DAC2 Right Playback Enum", 3),
+	EMU1010_SOURCE_OUTPUT("Dock DAC3 Left Playback Enum", 4),
+	EMU1010_SOURCE_OUTPUT("Dock DAC3 Right Playback Enum", 5),
+	EMU1010_SOURCE_OUTPUT("Dock SPDIF Left Playback Enum", 6),
+	EMU1010_SOURCE_OUTPUT("Dock SPDIF Right Playback Enum", 7),
+	EMU1010_SOURCE_OUTPUT("Dock ADAT 0 Playback Enum", 8),
+	EMU1010_SOURCE_OUTPUT("Dock ADAT 1 Playback Enum", 9),
+	EMU1010_SOURCE_OUTPUT("Dock ADAT 2 Playback Enum", 0xa),
+	EMU1010_SOURCE_OUTPUT("Dock ADAT 3 Playback Enum", 0xb),
+	EMU1010_SOURCE_OUTPUT("Dock ADAT 4 Playback Enum", 0xc),
+	EMU1010_SOURCE_OUTPUT("Dock ADAT 5 Playback Enum", 0xd),
+	EMU1010_SOURCE_OUTPUT("Dock ADAT 6 Playback Enum", 0xe),
+	EMU1010_SOURCE_OUTPUT("Dock ADAT 7 Playback Enum", 0xf),
+	EMU1010_SOURCE_OUTPUT("Mana DAC Left Playback Enum", 0x10),
+	EMU1010_SOURCE_OUTPUT("Mana DAC Right Playback Enum", 0x11),
+};
+
+
+#define EMU1010_SOURCE_INPUT(xname,chid) \
+{								\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
+	.info =  snd_emu1010_input_output_source_info,		\
+	.get =   snd_emu1010_input_source_get,			\
+	.put =   snd_emu1010_input_source_put,			\
+	.private_value = chid					\
+}
+
+static struct snd_kcontrol_new snd_emu1010_input_enum_ctls[] = {
+	EMU1010_SOURCE_INPUT("DSP 0 Capture Enum", 0),
+	EMU1010_SOURCE_INPUT("DSP 1 Capture Enum", 1),
+	EMU1010_SOURCE_INPUT("DSP 2 Capture Enum", 2),
+	EMU1010_SOURCE_INPUT("DSP 3 Capture Enum", 3),
+	EMU1010_SOURCE_INPUT("DSP 4 Capture Enum", 4),
+	EMU1010_SOURCE_INPUT("DSP 5 Capture Enum", 5),
+	EMU1010_SOURCE_INPUT("DSP 6 Capture Enum", 6),
+	EMU1010_SOURCE_INPUT("DSP 7 Capture Enum", 7),
+	EMU1010_SOURCE_INPUT("DSP 8 Capture Enum", 8),
+	EMU1010_SOURCE_INPUT("DSP 9 Capture Enum", 9),
+	EMU1010_SOURCE_INPUT("DSP A Capture Enum", 0xa),
+	EMU1010_SOURCE_INPUT("DSP B Capture Enum", 0xb),
+	EMU1010_SOURCE_INPUT("DSP C Capture Enum", 0xc),
+	EMU1010_SOURCE_INPUT("DSP D Capture Enum", 0xd),
+	EMU1010_SOURCE_INPUT("DSP E Capture Enum", 0xe),
+	EMU1010_SOURCE_INPUT("DSP F Capture Enum", 0xf),
+	EMU1010_SOURCE_INPUT("DSP 10 Capture Enum", 0x10),
+	EMU1010_SOURCE_INPUT("DSP 11 Capture Enum", 0x11),
+	EMU1010_SOURCE_INPUT("DSP 12 Capture Enum", 0x12),
+	EMU1010_SOURCE_INPUT("DSP 13 Capture Enum", 0x13),
+	EMU1010_SOURCE_INPUT("DSP 14 Capture Enum", 0x14),
+	EMU1010_SOURCE_INPUT("DSP 15 Capture Enum", 0x15),
+};
+
+
+
+#define snd_emu1010_adc_pads_info	snd_ctl_boolean_mono_info
+
+static int snd_emu1010_adc_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int mask = kcontrol->private_value & 0xff;
+	ucontrol->value.integer.value[0] = (emu->emu1010.adc_pads & mask) ? 1 : 0;
+	return 0;
+}
+
+static int snd_emu1010_adc_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int mask = kcontrol->private_value & 0xff;
+	unsigned int val, cache;
+	val = ucontrol->value.integer.value[0];
+	cache = emu->emu1010.adc_pads;
+	if (val == 1) 
+		cache = cache | mask;
+	else
+		cache = cache & ~mask;
+	if (cache != emu->emu1010.adc_pads) {
+		snd_emu1010_fpga_write(emu, EMU_HANA_ADC_PADS, cache );
+	        emu->emu1010.adc_pads = cache;
+	}
+
+	return 0;
+}
+
+
+
+#define EMU1010_ADC_PADS(xname,chid) \
+{								\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
+	.info =  snd_emu1010_adc_pads_info,			\
+	.get =   snd_emu1010_adc_pads_get,			\
+	.put =   snd_emu1010_adc_pads_put,			\
+	.private_value = chid					\
+}
+
+static struct snd_kcontrol_new snd_emu1010_adc_pads[] = {
+	EMU1010_ADC_PADS("ADC1 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD1),
+	EMU1010_ADC_PADS("ADC2 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD2),
+	EMU1010_ADC_PADS("ADC3 14dB PAD Audio Dock Capture Switch", EMU_HANA_DOCK_ADC_PAD3),
+	EMU1010_ADC_PADS("ADC1 14dB PAD 0202 Capture Switch", EMU_HANA_0202_ADC_PAD1),
+};
+
+#define snd_emu1010_dac_pads_info	snd_ctl_boolean_mono_info
+
+static int snd_emu1010_dac_pads_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int mask = kcontrol->private_value & 0xff;
+	ucontrol->value.integer.value[0] = (emu->emu1010.dac_pads & mask) ? 1 : 0;
+	return 0;
+}
+
+static int snd_emu1010_dac_pads_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int mask = kcontrol->private_value & 0xff;
+	unsigned int val, cache;
+	val = ucontrol->value.integer.value[0];
+	cache = emu->emu1010.dac_pads;
+	if (val == 1) 
+		cache = cache | mask;
+	else
+		cache = cache & ~mask;
+	if (cache != emu->emu1010.dac_pads) {
+		snd_emu1010_fpga_write(emu, EMU_HANA_DAC_PADS, cache );
+	        emu->emu1010.dac_pads = cache;
+	}
+
+	return 0;
+}
+
+
+
+#define EMU1010_DAC_PADS(xname,chid) \
+{								\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,		\
+	.info =  snd_emu1010_dac_pads_info,			\
+	.get =   snd_emu1010_dac_pads_get,			\
+	.put =   snd_emu1010_dac_pads_put,			\
+	.private_value = chid					\
+}
+
+static struct snd_kcontrol_new snd_emu1010_dac_pads[] = {
+	EMU1010_DAC_PADS("DAC1 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD1),
+	EMU1010_DAC_PADS("DAC2 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD2),
+	EMU1010_DAC_PADS("DAC3 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD3),
+	EMU1010_DAC_PADS("DAC4 Audio Dock 14dB PAD Playback Switch", EMU_HANA_DOCK_DAC_PAD4),
+	EMU1010_DAC_PADS("DAC1 0202 14dB PAD Playback Switch", EMU_HANA_0202_DAC_PAD1),
+};
+
+
+static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_info *uinfo)
+{
+	static const char * const texts[4] = {
+		"44100", "48000", "SPDIF", "ADAT"
+	};
+		
+	return snd_ctl_enum_info(uinfo, 1, 4, texts);
+}
+
+static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->emu1010.internal_clock;
+	return 0;
+}
+
+static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+
+	val = ucontrol->value.enumerated.item[0] ;
+	/* Limit: uinfo->value.enumerated.items = 4; */
+	if (val >= 4)
+		return -EINVAL;
+	change = (emu->emu1010.internal_clock != val);
+	if (change) {
+		emu->emu1010.internal_clock = val;
+		switch (val) {
+		case 0:
+			/* 44100 */
+			/* Mute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+			/* Default fallback clock 48kHz */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_44_1K );
+			/* Word Clock source, Internal 44.1kHz x1 */
+			snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+			EMU_HANA_WCLOCK_INT_44_1K | EMU_HANA_WCLOCK_1X );
+			/* Set LEDs on Audio Dock */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
+				EMU_HANA_DOCK_LEDS_2_44K | EMU_HANA_DOCK_LEDS_2_LOCK );
+			/* Allow DLL to settle */
+			msleep(10);
+			/* Unmute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+			break;
+		case 1:
+			/* 48000 */
+			/* Mute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+			/* Default fallback clock 48kHz */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
+			/* Word Clock source, Internal 48kHz x1 */
+			snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+				EMU_HANA_WCLOCK_INT_48K | EMU_HANA_WCLOCK_1X );
+			/* Set LEDs on Audio Dock */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
+				EMU_HANA_DOCK_LEDS_2_48K | EMU_HANA_DOCK_LEDS_2_LOCK );
+			/* Allow DLL to settle */
+			msleep(10);
+			/* Unmute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+			break;
+			
+		case 2: /* Take clock from S/PDIF IN */
+			/* Mute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+			/* Default fallback clock 48kHz */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
+			/* Word Clock source, sync to S/PDIF input */
+			snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+				EMU_HANA_WCLOCK_HANA_SPDIF_IN | EMU_HANA_WCLOCK_1X );
+			/* Set LEDs on Audio Dock */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2,
+				EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
+			/* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */	
+			/* Allow DLL to settle */
+			msleep(10);
+			/* Unmute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+			break;
+		
+		case 3: 			
+			/* Take clock from ADAT IN */
+			/* Mute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_MUTE );
+			/* Default fallback clock 48kHz */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DEFCLOCK, EMU_HANA_DEFCLOCK_48K );
+			/* Word Clock source, sync to ADAT input */
+			snd_emu1010_fpga_write(emu, EMU_HANA_WCLOCK,
+				EMU_HANA_WCLOCK_HANA_ADAT_IN | EMU_HANA_WCLOCK_1X );
+			/* Set LEDs on Audio Dock */
+			snd_emu1010_fpga_write(emu, EMU_HANA_DOCK_LEDS_2, EMU_HANA_DOCK_LEDS_2_EXT | EMU_HANA_DOCK_LEDS_2_LOCK );
+			/* FIXME: We should set EMU_HANA_DOCK_LEDS_2_LOCK only when clock signal is present and valid */	
+			/* Allow DLL to settle */
+			msleep(10);
+			/*   Unmute all */
+			snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE );
+			 
+			
+			break;		
+		}
+	}
+        return change;
+}
+
+static const struct snd_kcontrol_new snd_emu1010_internal_clock =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Clock Internal Rate",
+	.count =	1,
+	.info =         snd_emu1010_internal_clock_info,
+	.get =          snd_emu1010_internal_clock_get,
+	.put =          snd_emu1010_internal_clock_put
+};
+
+static int snd_emu1010_optical_out_info(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_info *uinfo)
+{
+	static const char * const texts[2] = {
+		"SPDIF", "ADAT"
+	};
+
+	return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static int snd_emu1010_optical_out_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->emu1010.optical_out;
+	return 0;
+}
+
+static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	u32 tmp;
+	int change = 0;
+
+	val = ucontrol->value.enumerated.item[0];
+	/* Limit: uinfo->value.enumerated.items = 2; */
+	if (val >= 2)
+		return -EINVAL;
+	change = (emu->emu1010.optical_out != val);
+	if (change) {
+		emu->emu1010.optical_out = val;
+		tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
+			(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
+		snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+	}
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu1010_optical_out = {
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Optical Output Mode",
+	.count =	1,
+	.info =         snd_emu1010_optical_out_info,
+	.get =          snd_emu1010_optical_out_get,
+	.put =          snd_emu1010_optical_out_put
+};
+
+static int snd_emu1010_optical_in_info(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_info *uinfo)
+{
+	static const char * const texts[2] = {
+		"SPDIF", "ADAT"
+	};
+
+	return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static int snd_emu1010_optical_in_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->emu1010.optical_in;
+	return 0;
+}
+
+static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	u32 tmp;
+	int change = 0;
+
+	val = ucontrol->value.enumerated.item[0];
+	/* Limit: uinfo->value.enumerated.items = 2; */
+	if (val >= 2)
+		return -EINVAL;
+	change = (emu->emu1010.optical_in != val);
+	if (change) {
+		emu->emu1010.optical_in = val;
+		tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) |
+			(emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0);
+		snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp);
+	}
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu1010_optical_in = {
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Optical Input Mode",
+	.count =	1,
+	.info =         snd_emu1010_optical_in_info,
+	.get =          snd_emu1010_optical_in_get,
+	.put =          snd_emu1010_optical_in_put
+};
+
+static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_info *uinfo)
+{
+#if 0
+	static const char * const texts[4] = {
+		"Unknown1", "Unknown2", "Mic", "Line"
+	};
+#endif
+	static const char * const texts[2] = {
+		"Mic", "Line"
+	};
+
+	return snd_ctl_enum_info(uinfo, 1, 2, texts);
+}
+
+static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->i2c_capture_source;
+	return 0;
+}
+
+static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int source_id;
+	unsigned int ngain, ogain;
+	u32 gpio;
+	int change = 0;
+	unsigned long flags;
+	u32 source;
+	/* If the capture source has changed,
+	 * update the capture volume from the cached value
+	 * for the particular source.
+	 */
+	source_id = ucontrol->value.enumerated.item[0];
+	/* Limit: uinfo->value.enumerated.items = 2; */
+	/*        emu->i2c_capture_volume */
+	if (source_id >= 2)
+		return -EINVAL;
+	change = (emu->i2c_capture_source != source_id);
+	if (change) {
+		snd_emu10k1_i2c_write(emu, ADC_MUX, 0); /* Mute input */
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		gpio = inl(emu->port + A_IOCFG);
+		if (source_id==0)
+			outl(gpio | 0x4, emu->port + A_IOCFG);
+		else
+			outl(gpio & ~0x4, emu->port + A_IOCFG);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+		ngain = emu->i2c_capture_volume[source_id][0]; /* Left */
+		ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */
+		if (ngain != ogain)
+			snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff));
+		ngain = emu->i2c_capture_volume[source_id][1]; /* Right */
+		ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Right */
+		if (ngain != ogain)
+			snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
+
+		source = 1 << (source_id + 2);
+		snd_emu10k1_i2c_write(emu, ADC_MUX, source); /* Set source */
+		emu->i2c_capture_source = source_id;
+	}
+        return change;
+}
+
+static const struct snd_kcontrol_new snd_audigy_i2c_capture_source =
+{
+		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =		"Capture Source",
+		.info =		snd_audigy_i2c_capture_source_info,
+		.get =		snd_audigy_i2c_capture_source_get,
+		.put =		snd_audigy_i2c_capture_source_put
+};
+
+static int snd_audigy_i2c_volume_info(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_audigy_i2c_volume_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int source_id;
+
+	source_id = kcontrol->private_value;
+	/* Limit: emu->i2c_capture_volume */
+        /*        capture_source: uinfo->value.enumerated.items = 2 */
+	if (source_id >= 2)
+		return -EINVAL;
+
+	ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0];
+	ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1];
+	return 0;
+}
+
+static int snd_audigy_i2c_volume_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int ogain;
+	unsigned int ngain;
+	unsigned int source_id;
+	int change = 0;
+
+	source_id = kcontrol->private_value;
+	/* Limit: emu->i2c_capture_volume */
+        /*        capture_source: uinfo->value.enumerated.items = 2 */
+	if (source_id >= 2)
+		return -EINVAL;
+	ogain = emu->i2c_capture_volume[source_id][0]; /* Left */
+	ngain = ucontrol->value.integer.value[0];
+	if (ngain > 0xff)
+		return 0;
+	if (ogain != ngain) {
+		if (emu->i2c_capture_source == source_id)
+			snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) );
+		emu->i2c_capture_volume[source_id][0] = ngain;
+		change = 1;
+	}
+	ogain = emu->i2c_capture_volume[source_id][1]; /* Right */
+	ngain = ucontrol->value.integer.value[1];
+	if (ngain > 0xff)
+		return 0;
+	if (ogain != ngain) {
+		if (emu->i2c_capture_source == source_id)
+			snd_emu10k1_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff));
+		emu->i2c_capture_volume[source_id][1] = ngain;
+		change = 1;
+	}
+
+	return change;
+}
+
+#define I2C_VOLUME(xname,chid) \
+{								\
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |		\
+	          SNDRV_CTL_ELEM_ACCESS_TLV_READ,		\
+	.info =  snd_audigy_i2c_volume_info,			\
+	.get =   snd_audigy_i2c_volume_get,			\
+	.put =   snd_audigy_i2c_volume_put,			\
+	.tlv = { .p = snd_audigy_db_scale2 },			\
+	.private_value = chid					\
+}
+
+
+static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] = {
+	I2C_VOLUME("Mic Capture Volume", 0),
+	I2C_VOLUME("Line Capture Volume", 0)
+};
+
+#if 0
+static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	static const char * const texts[] = {"44100", "48000", "96000"};
+
+	return snd_ctl_enum_info(uinfo, 1, 3, texts);
+}
+
+static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int tmp;
+	unsigned long flags;
+	
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+	switch (tmp & A_SPDIF_RATE_MASK) {
+	case A_SPDIF_44100:
+		ucontrol->value.enumerated.item[0] = 0;
+		break;
+	case A_SPDIF_48000:
+		ucontrol->value.enumerated.item[0] = 1;
+		break;
+	case A_SPDIF_96000:
+		ucontrol->value.enumerated.item[0] = 2;
+		break;
+	default:
+		ucontrol->value.enumerated.item[0] = 1;
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_audigy_spdif_output_rate_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int change;
+	unsigned int reg, val, tmp;
+	unsigned long flags;
+
+	switch(ucontrol->value.enumerated.item[0]) {
+	case 0:
+		val = A_SPDIF_44100;
+		break;
+	case 1:
+		val = A_SPDIF_48000;
+		break;
+	case 2:
+		val = A_SPDIF_96000;
+		break;
+	default:
+		val = A_SPDIF_48000;
+		break;
+	}
+
+	
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	reg = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0);
+	tmp = reg & ~A_SPDIF_RATE_MASK;
+	tmp |= val;
+	if ((change = (tmp != reg)))
+		snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp);
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+static struct snd_kcontrol_new snd_audigy_spdif_output_rate =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Audigy SPDIF Output Sample Rate",
+	.count =	1,
+	.info =         snd_audigy_spdif_output_rate_info,
+	.get =          snd_audigy_spdif_output_rate_get,
+	.put =          snd_audigy_spdif_output_rate_put
+};
+#endif
+
+static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	int change;
+	unsigned int val;
+	unsigned long flags;
+
+	/* Limit: emu->spdif_bits */
+	if (idx >= 3)
+		return -EINVAL;
+	val = (ucontrol->value.iec958.status[0] << 0) |
+	      (ucontrol->value.iec958.status[1] << 8) |
+	      (ucontrol->value.iec958.status[2] << 16) |
+	      (ucontrol->value.iec958.status[3] << 24);
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	change = val != emu->spdif_bits[idx];
+	if (change) {
+		snd_emu10k1_ptr_write(emu, SPCS0 + idx, 0, val);
+		emu->spdif_bits[idx] = val;
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+	.count =	3,
+	.info =         snd_emu10k1_spdif_info,
+	.get =          snd_emu10k1_spdif_get_mask
+};
+
+static const struct snd_kcontrol_new snd_emu10k1_spdif_control =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.count =	3,
+	.info =         snd_emu10k1_spdif_info,
+	.get =          snd_emu10k1_spdif_get,
+	.put =          snd_emu10k1_spdif_put
+};
+
+
+static void update_emu10k1_fxrt(struct snd_emu10k1 *emu, int voice, unsigned char *route)
+{
+	if (emu->audigy) {
+		snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
+				      snd_emu10k1_compose_audigy_fxrt1(route));
+		snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
+				      snd_emu10k1_compose_audigy_fxrt2(route));
+	} else {
+		snd_emu10k1_ptr_write(emu, FXRT, voice,
+				      snd_emu10k1_compose_send_routing(route));
+	}
+}
+
+static void update_emu10k1_send_volume(struct snd_emu10k1 *emu, int voice, unsigned char *volume)
+{
+	snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_A, voice, volume[0]);
+	snd_emu10k1_ptr_write(emu, PTRX_FXSENDAMOUNT_B, voice, volume[1]);
+	snd_emu10k1_ptr_write(emu, PSST_FXSENDAMOUNT_C, voice, volume[2]);
+	snd_emu10k1_ptr_write(emu, DSL_FXSENDAMOUNT_D, voice, volume[3]);
+	if (emu->audigy) {
+		unsigned int val = ((unsigned int)volume[4] << 24) |
+			((unsigned int)volume[5] << 16) |
+			((unsigned int)volume[6] << 8) |
+			(unsigned int)volume[7];
+		snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice, val);
+	}
+}
+
+/* PCM stream controls */
+
+static int snd_emu10k1_send_routing_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = emu->audigy ? 3*8 : 3*4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
+	return 0;
+}
+
+static int snd_emu10k1_send_routing_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_pcm_mixer *mix =
+		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+	int voice, idx;
+	int num_efx = emu->audigy ? 8 : 4;
+	int mask = emu->audigy ? 0x3f : 0x0f;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (voice = 0; voice < 3; voice++)
+		for (idx = 0; idx < num_efx; idx++)
+			ucontrol->value.integer.value[(voice * num_efx) + idx] = 
+				mix->send_routing[voice][idx] & mask;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_pcm_mixer *mix =
+		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+	int change = 0, voice, idx, val;
+	int num_efx = emu->audigy ? 8 : 4;
+	int mask = emu->audigy ? 0x3f : 0x0f;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (voice = 0; voice < 3; voice++)
+		for (idx = 0; idx < num_efx; idx++) {
+			val = ucontrol->value.integer.value[(voice * num_efx) + idx] & mask;
+			if (mix->send_routing[voice][idx] != val) {
+				mix->send_routing[voice][idx] = val;
+				change = 1;
+			}
+		}	
+	if (change && mix->epcm) {
+		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+			update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+					    &mix->send_routing[1][0]);
+			update_emu10k1_fxrt(emu, mix->epcm->voices[1]->number,
+					    &mix->send_routing[2][0]);
+		} else if (mix->epcm->voices[0]) {
+			update_emu10k1_fxrt(emu, mix->epcm->voices[0]->number,
+					    &mix->send_routing[0][0]);
+		}
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1_send_routing_control =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "EMU10K1 PCM Send Routing",
+	.count =	32,
+	.info =         snd_emu10k1_send_routing_info,
+	.get =          snd_emu10k1_send_routing_get,
+	.put =          snd_emu10k1_send_routing_put
+};
+
+static int snd_emu10k1_send_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = emu->audigy ? 3*8 : 3*4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_emu10k1_send_volume_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_pcm_mixer *mix =
+		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+	int idx;
+	int num_efx = emu->audigy ? 8 : 4;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < 3*num_efx; idx++)
+		ucontrol->value.integer.value[idx] = mix->send_volume[idx/num_efx][idx%num_efx];
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_pcm_mixer *mix =
+		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+	int change = 0, idx, val;
+	int num_efx = emu->audigy ? 8 : 4;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < 3*num_efx; idx++) {
+		val = ucontrol->value.integer.value[idx] & 255;
+		if (mix->send_volume[idx/num_efx][idx%num_efx] != val) {
+			mix->send_volume[idx/num_efx][idx%num_efx] = val;
+			change = 1;
+		}
+	}
+	if (change && mix->epcm) {
+		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+			update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
+						   &mix->send_volume[1][0]);
+			update_emu10k1_send_volume(emu, mix->epcm->voices[1]->number,
+						   &mix->send_volume[2][0]);
+		} else if (mix->epcm->voices[0]) {
+			update_emu10k1_send_volume(emu, mix->epcm->voices[0]->number,
+						   &mix->send_volume[0][0]);
+		}
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1_send_volume_control =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "EMU10K1 PCM Send Volume",
+	.count =	32,
+	.info =         snd_emu10k1_send_volume_info,
+	.get =          snd_emu10k1_send_volume_get,
+	.put =          snd_emu10k1_send_volume_put
+};
+
+static int snd_emu10k1_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 3;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0xffff;
+	return 0;
+}
+
+static int snd_emu10k1_attn_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_pcm_mixer *mix =
+		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+	unsigned long flags;
+	int idx;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < 3; idx++)
+		ucontrol->value.integer.value[idx] = mix->attn[idx];
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_pcm_mixer *mix =
+		&emu->pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+	int change = 0, idx, val;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < 3; idx++) {
+		val = ucontrol->value.integer.value[idx] & 0xffff;
+		if (mix->attn[idx] != val) {
+			mix->attn[idx] = val;
+			change = 1;
+		}
+	}
+	if (change && mix->epcm) {
+		if (mix->epcm->voices[0] && mix->epcm->voices[1]) {
+			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[1]);
+			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[1]->number, mix->attn[2]);
+		} else if (mix->epcm->voices[0]) {
+			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[0]->number, mix->attn[0]);
+		}
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1_attn_control =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "EMU10K1 PCM Volume",
+	.count =	32,
+	.info =         snd_emu10k1_attn_info,
+	.get =          snd_emu10k1_attn_get,
+	.put =          snd_emu10k1_attn_put
+};
+
+/* Mutichannel PCM stream controls */
+
+static int snd_emu10k1_efx_send_routing_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = emu->audigy ? 8 : 4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = emu->audigy ? 0x3f : 0x0f;
+	return 0;
+}
+
+static int snd_emu10k1_efx_send_routing_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_pcm_mixer *mix =
+		&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+	int idx;
+	int num_efx = emu->audigy ? 8 : 4;
+	int mask = emu->audigy ? 0x3f : 0x0f;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < num_efx; idx++)
+		ucontrol->value.integer.value[idx] = 
+			mix->send_routing[0][idx] & mask;
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
+	int change = 0, idx, val;
+	int num_efx = emu->audigy ? 8 : 4;
+	int mask = emu->audigy ? 0x3f : 0x0f;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < num_efx; idx++) {
+		val = ucontrol->value.integer.value[idx] & mask;
+		if (mix->send_routing[0][idx] != val) {
+			mix->send_routing[0][idx] = val;
+			change = 1;
+		}
+	}	
+
+	if (change && mix->epcm) {
+		if (mix->epcm->voices[ch]) {
+			update_emu10k1_fxrt(emu, mix->epcm->voices[ch]->number,
+					&mix->send_routing[0][0]);
+		}
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "Multichannel PCM Send Routing",
+	.count =	16,
+	.info =         snd_emu10k1_efx_send_routing_info,
+	.get =          snd_emu10k1_efx_send_routing_get,
+	.put =          snd_emu10k1_efx_send_routing_put
+};
+
+static int snd_emu10k1_efx_send_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = emu->audigy ? 8 : 4;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_emu10k1_efx_send_volume_get(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_pcm_mixer *mix =
+		&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+	int idx;
+	int num_efx = emu->audigy ? 8 : 4;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < num_efx; idx++)
+		ucontrol->value.integer.value[idx] = mix->send_volume[0][idx];
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
+	int change = 0, idx, val;
+	int num_efx = emu->audigy ? 8 : 4;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	for (idx = 0; idx < num_efx; idx++) {
+		val = ucontrol->value.integer.value[idx] & 255;
+		if (mix->send_volume[0][idx] != val) {
+			mix->send_volume[0][idx] = val;
+			change = 1;
+		}
+	}
+	if (change && mix->epcm) {
+		if (mix->epcm->voices[ch]) {
+			update_emu10k1_send_volume(emu, mix->epcm->voices[ch]->number,
+						   &mix->send_volume[0][0]);
+		}
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+
+static const struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "Multichannel PCM Send Volume",
+	.count =	16,
+	.info =         snd_emu10k1_efx_send_volume_info,
+	.get =          snd_emu10k1_efx_send_volume_get,
+	.put =          snd_emu10k1_efx_send_volume_put
+};
+
+static int snd_emu10k1_efx_attn_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0xffff;
+	return 0;
+}
+
+static int snd_emu10k1_efx_attn_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	struct snd_emu10k1_pcm_mixer *mix =
+		&emu->efx_pcm_mixer[snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)];
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	ucontrol->value.integer.value[0] = mix->attn[0];
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int ch = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	struct snd_emu10k1_pcm_mixer *mix = &emu->efx_pcm_mixer[ch];
+	int change = 0, val;
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	val = ucontrol->value.integer.value[0] & 0xffff;
+	if (mix->attn[0] != val) {
+		mix->attn[0] = val;
+		change = 1;
+	}
+	if (change && mix->epcm) {
+		if (mix->epcm->voices[ch]) {
+			snd_emu10k1_ptr_write(emu, VTFT_VOLUMETARGET, mix->epcm->voices[ch]->number, mix->attn[0]);
+		}
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
+{
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "Multichannel PCM Volume",
+	.count =	16,
+	.info =         snd_emu10k1_efx_attn_info,
+	.get =          snd_emu10k1_efx_attn_get,
+	.put =          snd_emu10k1_efx_attn_put
+};
+
+#define snd_emu10k1_shared_spdif_info	snd_ctl_boolean_mono_info
+
+static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+	if (emu->audigy)
+		ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0;
+	else
+		ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0;
+	if (emu->card_capabilities->invert_shared_spdif)
+		ucontrol->value.integer.value[0] =
+			!ucontrol->value.integer.value[0];
+		
+	return 0;
+}
+
+static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned long flags;
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int reg, val, sw;
+	int change = 0;
+
+	sw = ucontrol->value.integer.value[0];
+	if (emu->card_capabilities->invert_shared_spdif)
+		sw = !sw;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	if ( emu->card_capabilities->i2c_adc) {
+		/* Do nothing for Audigy 2 ZS Notebook */
+	} else if (emu->audigy) {
+		reg = inl(emu->port + A_IOCFG);
+		val = sw ? A_IOCFG_GPOUT0 : 0;
+		change = (reg & A_IOCFG_GPOUT0) != val;
+		if (change) {
+			reg &= ~A_IOCFG_GPOUT0;
+			reg |= val;
+			outl(reg | val, emu->port + A_IOCFG);
+		}
+	}
+	reg = inl(emu->port + HCFG);
+	val = sw ? HCFG_GPOUT0 : 0;
+	change |= (reg & HCFG_GPOUT0) != val;
+	if (change) {
+		reg &= ~HCFG_GPOUT0;
+		reg |= val;
+		outl(reg | val, emu->port + HCFG);
+	}
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1_shared_spdif =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"SB Live Analog/Digital Output Jack",
+	.info =		snd_emu10k1_shared_spdif_info,
+	.get =		snd_emu10k1_shared_spdif_get,
+	.put =		snd_emu10k1_shared_spdif_put
+};
+
+static const struct snd_kcontrol_new snd_audigy_shared_spdif =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Audigy Analog/Digital Output Jack",
+	.info =		snd_emu10k1_shared_spdif_info,
+	.get =		snd_emu10k1_shared_spdif_get,
+	.put =		snd_emu10k1_shared_spdif_put
+};
+
+/* workaround for too low volume on Audigy due to 16bit/24bit conversion */
+
+#define snd_audigy_capture_boost_info	snd_ctl_boolean_mono_info
+
+static int snd_audigy_capture_boost_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+
+	/* FIXME: better to use a cached version */
+	val = snd_ac97_read(emu->ac97, AC97_REC_GAIN);
+	ucontrol->value.integer.value[0] = !!val;
+	return 0;
+}
+
+static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+
+	if (ucontrol->value.integer.value[0])
+		val = 0x0f0f;
+	else
+		val = 0;
+	return snd_ac97_update(emu->ac97, AC97_REC_GAIN, val);
+}
+
+static const struct snd_kcontrol_new snd_audigy_capture_boost =
+{
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Mic Extra Boost",
+	.info =		snd_audigy_capture_boost_info,
+	.get =		snd_audigy_capture_boost_get,
+	.put =		snd_audigy_capture_boost_put
+};
+
+
+/*
+ */
+static void snd_emu10k1_mixer_free_ac97(struct snd_ac97 *ac97)
+{
+	struct snd_emu10k1 *emu = ac97->private_data;
+	emu->ac97 = NULL;
+}
+
+/*
+ */
+static int remove_ctl(struct snd_card *card, const char *name)
+{
+	struct snd_ctl_elem_id id;
+	memset(&id, 0, sizeof(id));
+	strcpy(id.name, name);
+	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	return snd_ctl_remove_id(card, &id);
+}
+
+static struct snd_kcontrol *ctl_find(struct snd_card *card, const char *name)
+{
+	struct snd_ctl_elem_id sid;
+	memset(&sid, 0, sizeof(sid));
+	strcpy(sid.name, name);
+	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	return snd_ctl_find_id(card, &sid);
+}
+
+static int rename_ctl(struct snd_card *card, const char *src, const char *dst)
+{
+	struct snd_kcontrol *kctl = ctl_find(card, src);
+	if (kctl) {
+		strcpy(kctl->id.name, dst);
+		return 0;
+	}
+	return -ENOENT;
+}
+
+int snd_emu10k1_mixer(struct snd_emu10k1 *emu,
+		      int pcm_device, int multi_device)
+{
+	int err, pcm;
+	struct snd_kcontrol *kctl;
+	struct snd_card *card = emu->card;
+	char **c;
+	static char *emu10k1_remove_ctls[] = {
+		/* no AC97 mono, surround, center/lfe */
+		"Master Mono Playback Switch",
+		"Master Mono Playback Volume",
+		"PCM Out Path & Mute",
+		"Mono Output Select",
+		"Surround Playback Switch",
+		"Surround Playback Volume",
+		"Center Playback Switch",
+		"Center Playback Volume",
+		"LFE Playback Switch",
+		"LFE Playback Volume",
+		NULL
+	};
+	static char *emu10k1_rename_ctls[] = {
+		"Surround Digital Playback Volume", "Surround Playback Volume",
+		"Center Digital Playback Volume", "Center Playback Volume",
+		"LFE Digital Playback Volume", "LFE Playback Volume",
+		NULL
+	};
+	static char *audigy_remove_ctls[] = {
+		/* Master/PCM controls on ac97 of Audigy has no effect */
+		/* On the Audigy2 the AC97 playback is piped into
+		 * the Philips ADC for 24bit capture */
+		"PCM Playback Switch",
+		"PCM Playback Volume",
+		"Master Playback Switch",
+		"Master Playback Volume",
+		"PCM Out Path & Mute",
+		"Mono Output Select",
+		/* remove unused AC97 capture controls */
+		"Capture Source",
+		"Capture Switch",
+		"Capture Volume",
+		"Mic Select",
+		"Headphone Playback Switch",
+		"Headphone Playback Volume",
+		"3D Control - Center",
+		"3D Control - Depth",
+		"3D Control - Switch",
+		"Video Playback Switch",
+		"Video Playback Volume",
+		"Mic Playback Switch",
+		"Mic Playback Volume",
+		"External Amplifier",
+		NULL
+	};
+	static char *audigy_rename_ctls[] = {
+		/* use conventional names */
+		"Wave Playback Volume", "PCM Playback Volume",
+		/* "Wave Capture Volume", "PCM Capture Volume", */
+		"Wave Master Playback Volume", "Master Playback Volume",
+		"AMic Playback Volume", "Mic Playback Volume",
+		"Master Mono Playback Switch", "Phone Output Playback Switch",
+		"Master Mono Playback Volume", "Phone Output Playback Volume",
+		NULL
+	};
+	static char *audigy_rename_ctls_i2c_adc[] = {
+		//"Analog Mix Capture Volume","OLD Analog Mix Capture Volume",
+		"Line Capture Volume", "Analog Mix Capture Volume",
+		"Wave Playback Volume", "OLD PCM Playback Volume",
+		"Wave Master Playback Volume", "Master Playback Volume",
+		"AMic Playback Volume", "Old Mic Playback Volume",
+		"CD Capture Volume", "IEC958 Optical Capture Volume",
+		NULL
+	};
+	static char *audigy_remove_ctls_i2c_adc[] = {
+		/* On the Audigy2 ZS Notebook
+		 * Capture via WM8775  */
+		"Mic Capture Volume",
+		"Analog Mix Capture Volume",
+		"Aux Capture Volume",
+		"IEC958 Optical Capture Volume",
+		NULL
+	};
+	static char *audigy_remove_ctls_1361t_adc[] = {
+		/* On the Audigy2 the AC97 playback is piped into
+		 * the Philips ADC for 24bit capture */
+		"PCM Playback Switch",
+		"PCM Playback Volume",
+		"Capture Source",
+		"Capture Switch",
+		"Capture Volume",
+		"Mic Capture Volume",
+		"Headphone Playback Switch",
+		"Headphone Playback Volume",
+		"3D Control - Center",
+		"3D Control - Depth",
+		"3D Control - Switch",
+		"Line2 Playback Volume",
+		"Line2 Capture Volume",
+		NULL
+	};
+	static char *audigy_rename_ctls_1361t_adc[] = {
+		"Master Playback Switch", "Master Capture Switch",
+		"Master Playback Volume", "Master Capture Volume",
+		"Wave Master Playback Volume", "Master Playback Volume",
+		"Beep Playback Switch", "Beep Capture Switch",
+		"Beep Playback Volume", "Beep Capture Volume",
+		"Phone Playback Switch", "Phone Capture Switch",
+		"Phone Playback Volume", "Phone Capture Volume",
+		"Mic Playback Switch", "Mic Capture Switch",
+		"Mic Playback Volume", "Mic Capture Volume",
+		"Line Playback Switch", "Line Capture Switch",
+		"Line Playback Volume", "Line Capture Volume",
+		"CD Playback Switch", "CD Capture Switch",
+		"CD Playback Volume", "CD Capture Volume",
+		"Aux Playback Switch", "Aux Capture Switch",
+		"Aux Playback Volume", "Aux Capture Volume",
+		"Video Playback Switch", "Video Capture Switch",
+		"Video Playback Volume", "Video Capture Volume",
+		"Master Mono Playback Switch", "Phone Output Playback Switch",
+		"Master Mono Playback Volume", "Phone Output Playback Volume",
+		NULL
+	};
+
+	if (emu->card_capabilities->ac97_chip) {
+		struct snd_ac97_bus *pbus;
+		struct snd_ac97_template ac97;
+		static struct snd_ac97_bus_ops ops = {
+			.write = snd_emu10k1_ac97_write,
+			.read = snd_emu10k1_ac97_read,
+		};
+
+		if ((err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus)) < 0)
+			return err;
+		pbus->no_vra = 1; /* we don't need VRA */
+		
+		memset(&ac97, 0, sizeof(ac97));
+		ac97.private_data = emu;
+		ac97.private_free = snd_emu10k1_mixer_free_ac97;
+		ac97.scaps = AC97_SCAP_NO_SPDIF;
+		if ((err = snd_ac97_mixer(pbus, &ac97, &emu->ac97)) < 0) {
+			if (emu->card_capabilities->ac97_chip == 1)
+				return err;
+			dev_info(emu->card->dev,
+				 "AC97 is optional on this board\n");
+			dev_info(emu->card->dev,
+				 "Proceeding without ac97 mixers...\n");
+			snd_device_free(emu->card, pbus);
+			goto no_ac97; /* FIXME: get rid of ugly gotos.. */
+		}
+		if (emu->audigy) {
+			/* set master volume to 0 dB */
+			snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000);
+			/* set capture source to mic */
+			snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000);
+			/* set mono output (TAD) to mic */
+			snd_ac97_update_bits(emu->ac97, AC97_GENERAL_PURPOSE,
+				0x0200, 0x0200);
+			if (emu->card_capabilities->adc_1361t)
+				c = audigy_remove_ctls_1361t_adc;
+			else 
+				c = audigy_remove_ctls;
+		} else {
+			/*
+			 * Credits for cards based on STAC9758:
+			 *   James Courtier-Dutton <James@superbug.demon.co.uk>
+			 *   Voluspa <voluspa@comhem.se>
+			 */
+			if (emu->ac97->id == AC97_ID_STAC9758) {
+				emu->rear_ac97 = 1;
+				snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_CNTR|AC97SLOT_LFE|AC97SLOT_REAR_LEFT|AC97SLOT_REAR_RIGHT);
+				snd_ac97_write_cache(emu->ac97, AC97_HEADPHONE, 0x0202);
+				remove_ctl(card,"Front Playback Volume");
+				remove_ctl(card,"Front Playback Switch");
+			}
+			/* remove unused AC97 controls */
+			snd_ac97_write_cache(emu->ac97, AC97_SURROUND_MASTER, 0x0202);
+			snd_ac97_write_cache(emu->ac97, AC97_CENTER_LFE_MASTER, 0x0202);
+			c = emu10k1_remove_ctls;
+		}
+		for (; *c; c++)
+			remove_ctl(card, *c);
+	} else if (emu->card_capabilities->i2c_adc) {
+		c = audigy_remove_ctls_i2c_adc;
+		for (; *c; c++)
+			remove_ctl(card, *c);
+	} else {
+	no_ac97:
+		if (emu->card_capabilities->ecard)
+			strcpy(emu->card->mixername, "EMU APS");
+		else if (emu->audigy)
+			strcpy(emu->card->mixername, "SB Audigy");
+		else
+			strcpy(emu->card->mixername, "Emu10k1");
+	}
+
+	if (emu->audigy)
+		if (emu->card_capabilities->adc_1361t)
+			c = audigy_rename_ctls_1361t_adc;
+		else if (emu->card_capabilities->i2c_adc)
+			c = audigy_rename_ctls_i2c_adc;
+		else
+			c = audigy_rename_ctls;
+	else
+		c = emu10k1_rename_ctls;
+	for (; *c; c += 2)
+		rename_ctl(card, c[0], c[1]);
+
+	if (emu->card_capabilities->subsystem == 0x80401102) { /* SB Live! Platinum CT4760P */
+		remove_ctl(card, "Center Playback Volume");
+		remove_ctl(card, "LFE Playback Volume");
+		remove_ctl(card, "Wave Center Playback Volume");
+		remove_ctl(card, "Wave LFE Playback Volume");
+	}
+	if (emu->card_capabilities->subsystem == 0x20071102) {  /* Audigy 4 Pro */
+		rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume");
+		rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume");
+		rename_ctl(card, "Aux2 Capture Volume", "Line3 Capture Volume");
+		rename_ctl(card, "Mic Capture Volume", "Unknown1 Capture Volume");
+	}
+	if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL)
+		return -ENOMEM;
+	kctl->id.device = pcm_device;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+	if ((kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL)
+		return -ENOMEM;
+	kctl->id.device = pcm_device;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+	if ((kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL)
+		return -ENOMEM;
+	kctl->id.device = pcm_device;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+
+	if ((kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu)) == NULL)
+		return -ENOMEM;
+	kctl->id.device = multi_device;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+	
+	if ((kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu)) == NULL)
+		return -ENOMEM;
+	kctl->id.device = multi_device;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+	
+	if ((kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu)) == NULL)
+		return -ENOMEM;
+	kctl->id.device = multi_device;
+	if ((err = snd_ctl_add(card, kctl)))
+		return err;
+
+	/* initialize the routing and volume table for each pcm playback stream */
+	for (pcm = 0; pcm < 32; pcm++) {
+		struct snd_emu10k1_pcm_mixer *mix;
+		int v;
+		
+		mix = &emu->pcm_mixer[pcm];
+		mix->epcm = NULL;
+
+		for (v = 0; v < 4; v++)
+			mix->send_routing[0][v] = 
+				mix->send_routing[1][v] = 
+				mix->send_routing[2][v] = v;
+		
+		memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+		mix->send_volume[0][0] = mix->send_volume[0][1] =
+		mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+		
+		mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+	}
+	
+	/* initialize the routing and volume table for the multichannel playback stream */
+	for (pcm = 0; pcm < NUM_EFX_PLAYBACK; pcm++) {
+		struct snd_emu10k1_pcm_mixer *mix;
+		int v;
+		
+		mix = &emu->efx_pcm_mixer[pcm];
+		mix->epcm = NULL;
+
+		mix->send_routing[0][0] = pcm;
+		mix->send_routing[0][1] = (pcm == 0) ? 1 : 0;
+		for (v = 0; v < 2; v++)
+			mix->send_routing[0][2+v] = 13+v;
+		if (emu->audigy)
+			for (v = 0; v < 4; v++)
+				mix->send_routing[0][4+v] = 60+v;
+		
+		memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+		mix->send_volume[0][0]  = 255;
+		
+		mix->attn[0] = 0xffff;
+	}
+	
+	if (! emu->card_capabilities->ecard) { /* FIXME: APS has these controls? */
+		/* sb live! and audigy */
+		if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
+			return -ENOMEM;
+		if (!emu->audigy)
+			kctl->id.device = emu->pcm_efx->device;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+		if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL)
+			return -ENOMEM;
+		if (!emu->audigy)
+			kctl->id.device = emu->pcm_efx->device;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+	}
+
+	if (emu->card_capabilities->emu_model) {
+		;  /* Disable the snd_audigy_spdif_shared_spdif */
+	} else if (emu->audigy) {
+		if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
+			return -ENOMEM;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+#if 0
+		if ((kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu)) == NULL)
+			return -ENOMEM;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+#endif
+	} else if (! emu->card_capabilities->ecard) {
+		/* sb live! */
+		if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
+			return -ENOMEM;
+		if ((err = snd_ctl_add(card, kctl)))
+			return err;
+	}
+	if (emu->card_capabilities->ca0151_chip) { /* P16V */
+		if ((err = snd_p16v_mixer(emu)))
+			return err;
+	}
+
+	if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) {
+		/* 1616(m) cardbus */
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(snd_emu1616_output_enum_ctls); i++) {
+			err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_emu1616_output_enum_ctls[i],
+					     emu));
+			if (err < 0)
+				return err;
+		}
+		for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
+			err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
+					     emu));
+			if (err < 0)
+				return err;
+		}
+		for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads) - 2; i++) {
+			err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+			if (err < 0)
+				return err;
+		}
+		for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads) - 2; i++) {
+			err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+			if (err < 0)
+				return err;
+		}
+		err = snd_ctl_add(card,
+			snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(card,
+			snd_ctl_new1(&snd_emu1010_optical_out, emu));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(card,
+			snd_ctl_new1(&snd_emu1010_optical_in, emu));
+		if (err < 0)
+			return err;
+
+	} else if (emu->card_capabilities->emu_model) {
+		/* all other e-mu cards for now */
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(snd_emu1010_output_enum_ctls); i++) {
+			err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_emu1010_output_enum_ctls[i],
+					     emu));
+			if (err < 0)
+				return err;
+		}
+		for (i = 0; i < ARRAY_SIZE(snd_emu1010_input_enum_ctls); i++) {
+			err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_emu1010_input_enum_ctls[i],
+					     emu));
+			if (err < 0)
+				return err;
+		}
+		for (i = 0; i < ARRAY_SIZE(snd_emu1010_adc_pads); i++) {
+			err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_emu1010_adc_pads[i], emu));
+			if (err < 0)
+				return err;
+		}
+		for (i = 0; i < ARRAY_SIZE(snd_emu1010_dac_pads); i++) {
+			err = snd_ctl_add(card,
+				snd_ctl_new1(&snd_emu1010_dac_pads[i], emu));
+			if (err < 0)
+				return err;
+		}
+		err = snd_ctl_add(card,
+			snd_ctl_new1(&snd_emu1010_internal_clock, emu));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(card,
+			snd_ctl_new1(&snd_emu1010_optical_out, emu));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(card,
+			snd_ctl_new1(&snd_emu1010_optical_in, emu));
+		if (err < 0)
+			return err;
+	}
+
+	if ( emu->card_capabilities->i2c_adc) {
+		int i;
+
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_capture_source, emu));
+		if (err < 0)
+			return err;
+
+		for (i = 0; i < ARRAY_SIZE(snd_audigy_i2c_volume_ctls); i++) {
+			err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_i2c_volume_ctls[i], emu));
+			if (err < 0)
+				return err;
+		}
+	}
+		
+	if (emu->card_capabilities->ac97_chip && emu->audigy) {
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_audigy_capture_boost,
+						     emu));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emumpu401.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emumpu401.c
new file mode 100644
index 0000000..b6650f5
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emumpu401.c
@@ -0,0 +1,399 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *  Routines for control of EMU10K1 MPU-401 in UART mode
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+#define EMU10K1_MIDI_MODE_INPUT		(1<<0)
+#define EMU10K1_MIDI_MODE_OUTPUT	(1<<1)
+
+static inline unsigned char mpu401_read(struct snd_emu10k1 *emu,
+					struct snd_emu10k1_midi *mpu, int idx)
+{
+	if (emu->audigy)
+		return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0);
+	else
+		return inb(emu->port + mpu->port + idx);
+}
+
+static inline void mpu401_write(struct snd_emu10k1 *emu,
+				struct snd_emu10k1_midi *mpu, int data, int idx)
+{
+	if (emu->audigy)
+		snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data);
+	else
+		outb(data, emu->port + mpu->port + idx);
+}
+
+#define mpu401_write_data(emu, mpu, data)	mpu401_write(emu, mpu, data, 0)
+#define mpu401_write_cmd(emu, mpu, data)	mpu401_write(emu, mpu, data, 1)
+#define mpu401_read_data(emu, mpu)		mpu401_read(emu, mpu, 0)
+#define mpu401_read_stat(emu, mpu)		mpu401_read(emu, mpu, 1)
+
+#define mpu401_input_avail(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x80))
+#define mpu401_output_ready(emu,mpu)	(!(mpu401_read_stat(emu,mpu) & 0x40))
+
+#define MPU401_RESET		0xff
+#define MPU401_ENTER_UART	0x3f
+#define MPU401_ACK		0xfe
+
+static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu)
+{
+	int timeout = 100000;
+	for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
+		mpu401_read_data(emu, mpu);
+#ifdef CONFIG_SND_DEBUG
+	if (timeout <= 0)
+		dev_err(emu->card->dev,
+			"cmd: clear rx timeout (status = 0x%x)\n",
+			mpu401_read_stat(emu, mpu));
+#endif
+}
+
+/*
+
+ */
+
+static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, unsigned int status)
+{
+	unsigned char byte;
+
+	if (midi->rmidi == NULL) {
+		snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
+		return;
+	}
+
+	spin_lock(&midi->input_lock);
+	if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
+		if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+			mpu401_clear_rx(emu, midi);
+		} else {
+			byte = mpu401_read_data(emu, midi);
+			if (midi->substream_input)
+				snd_rawmidi_receive(midi->substream_input, &byte, 1);
+		}
+	}
+	spin_unlock(&midi->input_lock);
+
+	spin_lock(&midi->output_lock);
+	if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
+		if (midi->substream_output &&
+		    snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
+			mpu401_write_data(emu, midi, byte);
+		} else {
+			snd_emu10k1_intr_disable(emu, midi->tx_enable);
+		}
+	}
+	spin_unlock(&midi->output_lock);
+}
+
+static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
+{
+	do_emu10k1_midi_interrupt(emu, &emu->midi, status);
+}
+
+static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int status)
+{
+	do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
+}
+
+static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
+{
+	unsigned long flags;
+	int timeout, ok;
+
+	spin_lock_irqsave(&midi->input_lock, flags);
+	mpu401_write_data(emu, midi, 0x00);
+	/* mpu401_clear_rx(emu, midi); */
+
+	mpu401_write_cmd(emu, midi, cmd);
+	if (ack) {
+		ok = 0;
+		timeout = 10000;
+		while (!ok && timeout-- > 0) {
+			if (mpu401_input_avail(emu, midi)) {
+				if (mpu401_read_data(emu, midi) == MPU401_ACK)
+					ok = 1;
+			}
+		}
+		if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
+			ok = 1;
+	} else {
+		ok = 1;
+	}
+	spin_unlock_irqrestore(&midi->input_lock, flags);
+	if (!ok) {
+		dev_err(emu->card->dev,
+			"midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
+			   cmd, emu->port,
+			   mpu401_read_stat(emu, midi),
+			   mpu401_read_data(emu, midi));
+		return 1;
+	}
+	return 0;
+}
+
+static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_emu10k1 *emu;
+	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
+	spin_lock_irqsave(&midi->open_lock, flags);
+	midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
+	midi->substream_input = substream;
+	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+			goto error_out;
+		if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+			goto error_out;
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+
+error_out:
+	return -EIO;
+}
+
+static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_emu10k1 *emu;
+	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
+	spin_lock_irqsave(&midi->open_lock, flags);
+	midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
+	midi->substream_output = substream;
+	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
+			goto error_out;
+		if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
+			goto error_out;
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return 0;
+
+error_out:
+	return -EIO;
+}
+
+static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_emu10k1 *emu;
+	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+	unsigned long flags;
+	int err = 0;
+
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
+	spin_lock_irqsave(&midi->open_lock, flags);
+	snd_emu10k1_intr_disable(emu, midi->rx_enable);
+	midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
+	midi->substream_input = NULL;
+	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return err;
+}
+
+static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_emu10k1 *emu;
+	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+	unsigned long flags;
+	int err = 0;
+
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return -ENXIO;
+	spin_lock_irqsave(&midi->open_lock, flags);
+	snd_emu10k1_intr_disable(emu, midi->tx_enable);
+	midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
+	midi->substream_output = NULL;
+	if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+		err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
+	} else {
+		spin_unlock_irqrestore(&midi->open_lock, flags);
+	}
+	return err;
+}
+
+static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct snd_emu10k1 *emu;
+	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return;
+
+	if (up)
+		snd_emu10k1_intr_enable(emu, midi->rx_enable);
+	else
+		snd_emu10k1_intr_disable(emu, midi->rx_enable);
+}
+
+static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+	struct snd_emu10k1 *emu;
+	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
+	unsigned long flags;
+
+	emu = midi->emu;
+	if (snd_BUG_ON(!emu))
+		return;
+
+	if (up) {
+		int max = 4;
+		unsigned char byte;
+	
+		/* try to send some amount of bytes here before interrupts */
+		spin_lock_irqsave(&midi->output_lock, flags);
+		while (max > 0) {
+			if (mpu401_output_ready(emu, midi)) {
+				if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
+				    snd_rawmidi_transmit(substream, &byte, 1) != 1) {
+					/* no more data */
+					spin_unlock_irqrestore(&midi->output_lock, flags);
+					return;
+				}
+				mpu401_write_data(emu, midi, byte);
+				max--;
+			} else {
+				break;
+			}
+		}
+		spin_unlock_irqrestore(&midi->output_lock, flags);
+		snd_emu10k1_intr_enable(emu, midi->tx_enable);
+	} else {
+		snd_emu10k1_intr_disable(emu, midi->tx_enable);
+	}
+}
+
+/*
+
+ */
+
+static const struct snd_rawmidi_ops snd_emu10k1_midi_output =
+{
+	.open =		snd_emu10k1_midi_output_open,
+	.close =	snd_emu10k1_midi_output_close,
+	.trigger =	snd_emu10k1_midi_output_trigger,
+};
+
+static const struct snd_rawmidi_ops snd_emu10k1_midi_input =
+{
+	.open =		snd_emu10k1_midi_input_open,
+	.close =	snd_emu10k1_midi_input_close,
+	.trigger =	snd_emu10k1_midi_input_trigger,
+};
+
+static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
+{
+	struct snd_emu10k1_midi *midi = rmidi->private_data;
+	midi->interrupt = NULL;
+	midi->rmidi = NULL;
+}
+
+static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
+{
+	struct snd_rawmidi *rmidi;
+	int err;
+
+	if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
+		return err;
+	midi->emu = emu;
+	spin_lock_init(&midi->open_lock);
+	spin_lock_init(&midi->input_lock);
+	spin_lock_init(&midi->output_lock);
+	strcpy(rmidi->name, name);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
+	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
+	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+	                     SNDRV_RAWMIDI_INFO_INPUT |
+	                     SNDRV_RAWMIDI_INFO_DUPLEX;
+	rmidi->private_data = midi;
+	rmidi->private_free = snd_emu10k1_midi_free;
+	midi->rmidi = rmidi;
+	return 0;
+}
+
+int snd_emu10k1_midi(struct snd_emu10k1 *emu)
+{
+	struct snd_emu10k1_midi *midi = &emu->midi;
+	int err;
+
+	if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
+		return err;
+
+	midi->tx_enable = INTE_MIDITXENABLE;
+	midi->rx_enable = INTE_MIDIRXENABLE;
+	midi->port = MUDATA;
+	midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+	midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+	midi->interrupt = snd_emu10k1_midi_interrupt;
+	return 0;
+}
+
+int snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
+{
+	struct snd_emu10k1_midi *midi;
+	int err;
+
+	midi = &emu->midi;
+	if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
+		return err;
+
+	midi->tx_enable = INTE_MIDITXENABLE;
+	midi->rx_enable = INTE_MIDIRXENABLE;
+	midi->port = A_MUDATA1;
+	midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
+	midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
+	midi->interrupt = snd_emu10k1_midi_interrupt;
+
+	midi = &emu->midi2;
+	if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
+		return err;
+
+	midi->tx_enable = INTE_A_MIDITXENABLE2;
+	midi->rx_enable = INTE_A_MIDIRXENABLE2;
+	midi->port = A_MUDATA2;
+	midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
+	midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
+	midi->interrupt = snd_emu10k1_midi_interrupt2;
+	return 0;
+}
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emupcm.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emupcm.c
new file mode 100644
index 0000000..56be163
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emupcm.c
@@ -0,0 +1,1860 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips / PCM routines
+ *  Multichannel PCM support Copyright (c) Lee Revell <rlrevell@joe-job.com>
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static void snd_emu10k1_pcm_interrupt(struct snd_emu10k1 *emu,
+				      struct snd_emu10k1_voice *voice)
+{
+	struct snd_emu10k1_pcm *epcm;
+
+	if ((epcm = voice->epcm) == NULL)
+		return;
+	if (epcm->substream == NULL)
+		return;
+#if 0
+	dev_dbg(emu->card->dev,
+		"IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n",
+			epcm->substream->runtime->hw->pointer(emu, epcm->substream),
+			snd_pcm_lib_period_bytes(epcm->substream),
+			snd_pcm_lib_buffer_bytes(epcm->substream));
+#endif
+	snd_pcm_period_elapsed(epcm->substream);
+}
+
+static void snd_emu10k1_pcm_ac97adc_interrupt(struct snd_emu10k1 *emu,
+					      unsigned int status)
+{
+#if 0
+	if (status & IPR_ADCBUFHALFFULL) {
+		if (emu->pcm_capture_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+			return;
+	}
+#endif
+	snd_pcm_period_elapsed(emu->pcm_capture_substream);
+}
+
+static void snd_emu10k1_pcm_ac97mic_interrupt(struct snd_emu10k1 *emu,
+					      unsigned int status)
+{
+#if 0
+	if (status & IPR_MICBUFHALFFULL) {
+		if (emu->pcm_capture_mic_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+			return;
+	}
+#endif
+	snd_pcm_period_elapsed(emu->pcm_capture_mic_substream);
+}
+
+static void snd_emu10k1_pcm_efx_interrupt(struct snd_emu10k1 *emu,
+					  unsigned int status)
+{
+#if 0
+	if (status & IPR_EFXBUFHALFFULL) {
+		if (emu->pcm_capture_efx_substream->runtime->mode == SNDRV_PCM_MODE_FRAME)
+			return;
+	}
+#endif
+	snd_pcm_period_elapsed(emu->pcm_capture_efx_substream);
+}	 
+
+static snd_pcm_uframes_t snd_emu10k1_efx_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int ptr;
+
+	if (!epcm->running)
+		return 0;
+	ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+	ptr += runtime->buffer_size;
+	ptr -= epcm->ccca_start_addr;
+	ptr %= runtime->buffer_size;
+
+	return ptr;
+}
+
+static int snd_emu10k1_pcm_channel_alloc(struct snd_emu10k1_pcm * epcm, int voices)
+{
+	int err, i;
+
+	if (epcm->voices[1] != NULL && voices < 2) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
+		epcm->voices[1] = NULL;
+	}
+	for (i = 0; i < voices; i++) {
+		if (epcm->voices[i] == NULL)
+			break;
+	}
+	if (i == voices)
+		return 0; /* already allocated */
+
+	for (i = 0; i < ARRAY_SIZE(epcm->voices); i++) {
+		if (epcm->voices[i]) {
+			snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+			epcm->voices[i] = NULL;
+		}
+	}
+	err = snd_emu10k1_voice_alloc(epcm->emu,
+				      epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
+				      voices,
+				      &epcm->voices[0]);
+	
+	if (err < 0)
+		return err;
+	epcm->voices[0]->epcm = epcm;
+	if (voices > 1) {
+		for (i = 1; i < voices; i++) {
+			epcm->voices[i] = &epcm->emu->voices[epcm->voices[0]->number + i];
+			epcm->voices[i]->epcm = epcm;
+		}
+	}
+	if (epcm->extra == NULL) {
+		err = snd_emu10k1_voice_alloc(epcm->emu,
+					      epcm->type == PLAYBACK_EMUVOICE ? EMU10K1_PCM : EMU10K1_EFX,
+					      1,
+					      &epcm->extra);
+		if (err < 0) {
+			/*
+			dev_dbg(emu->card->dev, "pcm_channel_alloc: "
+			       "failed extra: voices=%d, frame=%d\n",
+			       voices, frame);
+			*/
+			for (i = 0; i < voices; i++) {
+				snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+				epcm->voices[i] = NULL;
+			}
+			return err;
+		}
+		epcm->extra->epcm = epcm;
+		epcm->extra->interrupt = snd_emu10k1_pcm_interrupt;
+	}
+	return 0;
+}
+
+static const unsigned int capture_period_sizes[31] = {
+	384,	448,	512,	640,
+	384*2,	448*2,	512*2,	640*2,
+	384*4,	448*4,	512*4,	640*4,
+	384*8,	448*8,	512*8,	640*8,
+	384*16,	448*16,	512*16,	640*16,
+	384*32,	448*32,	512*32,	640*32,
+	384*64,	448*64,	512*64,	640*64,
+	384*128,448*128,512*128
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_capture_period_sizes = {
+	.count = 31,
+	.list = capture_period_sizes,
+	.mask = 0
+};
+
+static const unsigned int capture_rates[8] = {
+	8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list hw_constraints_capture_rates = {
+	.count = 8,
+	.list = capture_rates,
+	.mask = 0
+};
+
+static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate)
+{
+	switch (rate) {
+	case 8000:	return ADCCR_SAMPLERATE_8;
+	case 11025:	return ADCCR_SAMPLERATE_11;
+	case 16000:	return ADCCR_SAMPLERATE_16;
+	case 22050:	return ADCCR_SAMPLERATE_22;
+	case 24000:	return ADCCR_SAMPLERATE_24;
+	case 32000:	return ADCCR_SAMPLERATE_32;
+	case 44100:	return ADCCR_SAMPLERATE_44;
+	case 48000:	return ADCCR_SAMPLERATE_48;
+	default:
+			snd_BUG();
+			return ADCCR_SAMPLERATE_8;
+	}
+}
+
+static unsigned int snd_emu10k1_audigy_capture_rate_reg(unsigned int rate)
+{
+	switch (rate) {
+	case 8000:	return A_ADCCR_SAMPLERATE_8;
+	case 11025:	return A_ADCCR_SAMPLERATE_11;
+	case 12000:	return A_ADCCR_SAMPLERATE_12; /* really supported? */
+	case 16000:	return ADCCR_SAMPLERATE_16;
+	case 22050:	return ADCCR_SAMPLERATE_22;
+	case 24000:	return ADCCR_SAMPLERATE_24;
+	case 32000:	return ADCCR_SAMPLERATE_32;
+	case 44100:	return ADCCR_SAMPLERATE_44;
+	case 48000:	return ADCCR_SAMPLERATE_48;
+	default:
+			snd_BUG();
+			return A_ADCCR_SAMPLERATE_8;
+	}
+}
+
+static unsigned int emu10k1_calc_pitch_target(unsigned int rate)
+{
+	unsigned int pitch_target;
+
+	pitch_target = (rate << 8) / 375;
+	pitch_target = (pitch_target >> 1) + (pitch_target & 1);
+	return pitch_target;
+}
+
+#define PITCH_48000 0x00004000
+#define PITCH_96000 0x00008000
+#define PITCH_85000 0x00007155
+#define PITCH_80726 0x00006ba2
+#define PITCH_67882 0x00005a82
+#define PITCH_57081 0x00004c1c
+
+static unsigned int emu10k1_select_interprom(unsigned int pitch_target)
+{
+	if (pitch_target == PITCH_48000)
+		return CCCA_INTERPROM_0;
+	else if (pitch_target < PITCH_48000)
+		return CCCA_INTERPROM_1;
+	else if (pitch_target >= PITCH_96000)
+		return CCCA_INTERPROM_0;
+	else if (pitch_target >= PITCH_85000)
+		return CCCA_INTERPROM_6;
+	else if (pitch_target >= PITCH_80726)
+		return CCCA_INTERPROM_5;
+	else if (pitch_target >= PITCH_67882)
+		return CCCA_INTERPROM_4;
+	else if (pitch_target >= PITCH_57081)
+		return CCCA_INTERPROM_3;
+	else  
+		return CCCA_INTERPROM_2;
+}
+
+/*
+ * calculate cache invalidate size 
+ *
+ * stereo: channel is stereo
+ * w_16: using 16bit samples
+ *
+ * returns: cache invalidate size in samples
+ */
+static inline int emu10k1_ccis(int stereo, int w_16)
+{
+	if (w_16) {
+		return stereo ? 24 : 26;
+	} else {
+		return stereo ? 24*2 : 26*2;
+	}
+}
+
+static void snd_emu10k1_pcm_init_voice(struct snd_emu10k1 *emu,
+				       int master, int extra,
+				       struct snd_emu10k1_voice *evoice,
+				       unsigned int start_addr,
+				       unsigned int end_addr,
+				       struct snd_emu10k1_pcm_mixer *mix)
+{
+	struct snd_pcm_substream *substream = evoice->epcm->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned int silent_page, tmp;
+	int voice, stereo, w_16;
+	unsigned char attn, send_amount[8];
+	unsigned char send_routing[8];
+	unsigned long flags;
+	unsigned int pitch_target;
+	unsigned int ccis;
+
+	voice = evoice->number;
+	stereo = runtime->channels == 2;
+	w_16 = snd_pcm_format_width(runtime->format) == 16;
+
+	if (!extra && stereo) {
+		start_addr >>= 1;
+		end_addr >>= 1;
+	}
+	if (w_16) {
+		start_addr >>= 1;
+		end_addr >>= 1;
+	}
+
+	spin_lock_irqsave(&emu->reg_lock, flags);
+
+	/* volume parameters */
+	if (extra) {
+		attn = 0;
+		memset(send_routing, 0, sizeof(send_routing));
+		send_routing[0] = 0;
+		send_routing[1] = 1;
+		send_routing[2] = 2;
+		send_routing[3] = 3;
+		memset(send_amount, 0, sizeof(send_amount));
+	} else {
+		/* mono, left, right (master voice = left) */
+		tmp = stereo ? (master ? 1 : 2) : 0;
+		memcpy(send_routing, &mix->send_routing[tmp][0], 8);
+		memcpy(send_amount, &mix->send_volume[tmp][0], 8);
+	}
+
+	ccis = emu10k1_ccis(stereo, w_16);
+	
+	if (master) {
+		evoice->epcm->ccca_start_addr = start_addr + ccis;
+		if (extra) {
+			start_addr += ccis;
+			end_addr += ccis + emu->delay_pcm_irq;
+		}
+		if (stereo && !extra) {
+			snd_emu10k1_ptr_write(emu, CPF, voice, CPF_STEREO_MASK);
+			snd_emu10k1_ptr_write(emu, CPF, (voice + 1), CPF_STEREO_MASK);
+		} else {
+			snd_emu10k1_ptr_write(emu, CPF, voice, 0);
+		}
+	}
+
+	/* setup routing */
+	if (emu->audigy) {
+		snd_emu10k1_ptr_write(emu, A_FXRT1, voice,
+				      snd_emu10k1_compose_audigy_fxrt1(send_routing));
+		snd_emu10k1_ptr_write(emu, A_FXRT2, voice,
+				      snd_emu10k1_compose_audigy_fxrt2(send_routing));
+		snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, voice,
+				      ((unsigned int)send_amount[4] << 24) |
+				      ((unsigned int)send_amount[5] << 16) |
+				      ((unsigned int)send_amount[6] << 8) |
+				      (unsigned int)send_amount[7]);
+	} else
+		snd_emu10k1_ptr_write(emu, FXRT, voice,
+				      snd_emu10k1_compose_send_routing(send_routing));
+	/* Stop CA */
+	/* Assumption that PT is already 0 so no harm overwriting */
+	snd_emu10k1_ptr_write(emu, PTRX, voice, (send_amount[0] << 8) | send_amount[1]);
+	snd_emu10k1_ptr_write(emu, DSL, voice, end_addr | (send_amount[3] << 24));
+	snd_emu10k1_ptr_write(emu, PSST, voice,
+			(start_addr + (extra ? emu->delay_pcm_irq : 0)) |
+			(send_amount[2] << 24));
+	if (emu->card_capabilities->emu_model)
+		pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
+	else 
+		pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+	if (extra)
+		snd_emu10k1_ptr_write(emu, CCCA, voice, start_addr |
+			      emu10k1_select_interprom(pitch_target) |
+			      (w_16 ? 0 : CCCA_8BITSELECT));
+	else
+		snd_emu10k1_ptr_write(emu, CCCA, voice, (start_addr + ccis) |
+			      emu10k1_select_interprom(pitch_target) |
+			      (w_16 ? 0 : CCCA_8BITSELECT));
+	/* Clear filter delay memory */
+	snd_emu10k1_ptr_write(emu, Z1, voice, 0);
+	snd_emu10k1_ptr_write(emu, Z2, voice, 0);
+	/* invalidate maps */
+	silent_page = ((unsigned int)emu->silent_page.addr << emu->address_mode) | (emu->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
+	snd_emu10k1_ptr_write(emu, MAPA, voice, silent_page);
+	snd_emu10k1_ptr_write(emu, MAPB, voice, silent_page);
+	/* modulation envelope */
+	snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, ATKHLDM, voice, 0);
+	snd_emu10k1_ptr_write(emu, DCYSUSM, voice, 0x007f);
+	snd_emu10k1_ptr_write(emu, LFOVAL1, voice, 0x8000);
+	snd_emu10k1_ptr_write(emu, LFOVAL2, voice, 0x8000);
+	snd_emu10k1_ptr_write(emu, FMMOD, voice, 0);
+	snd_emu10k1_ptr_write(emu, TREMFRQ, voice, 0);
+	snd_emu10k1_ptr_write(emu, FM2FRQ2, voice, 0);
+	snd_emu10k1_ptr_write(emu, ENVVAL, voice, 0x8000);
+	/* volume envelope */
+	snd_emu10k1_ptr_write(emu, ATKHLDV, voice, 0x7f7f);
+	snd_emu10k1_ptr_write(emu, ENVVOL, voice, 0x0000);
+	/* filter envelope */
+	snd_emu10k1_ptr_write(emu, PEFE_FILTERAMOUNT, voice, 0x7f);
+	/* pitch envelope */
+	snd_emu10k1_ptr_write(emu, PEFE_PITCHAMOUNT, voice, 0);
+
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+static int snd_emu10k1_playback_hw_params(struct snd_pcm_substream *substream,
+					  struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	int err;
+
+	if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0)
+		return err;
+	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+		return err;
+	if (err > 0) {	/* change */
+		int mapped;
+		if (epcm->memblk != NULL)
+			snd_emu10k1_free_pages(emu, epcm->memblk);
+		epcm->memblk = snd_emu10k1_alloc_pages(emu, substream);
+		epcm->start_addr = 0;
+		if (! epcm->memblk)
+			return -ENOMEM;
+		mapped = ((struct snd_emu10k1_memblk *)epcm->memblk)->mapped_page;
+		if (mapped < 0)
+			return -ENOMEM;
+		epcm->start_addr = mapped << PAGE_SHIFT;
+	}
+	return 0;
+}
+
+static int snd_emu10k1_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm;
+
+	if (runtime->private_data == NULL)
+		return 0;
+	epcm = runtime->private_data;
+	if (epcm->extra) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->extra);
+		epcm->extra = NULL;
+	}
+	if (epcm->voices[1]) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]);
+		epcm->voices[1] = NULL;
+	}
+	if (epcm->voices[0]) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]);
+		epcm->voices[0] = NULL;
+	}
+	if (epcm->memblk) {
+		snd_emu10k1_free_pages(emu, epcm->memblk);
+		epcm->memblk = NULL;
+		epcm->start_addr = 0;
+	}
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_emu10k1_efx_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm;
+	int i;
+
+	if (runtime->private_data == NULL)
+		return 0;
+	epcm = runtime->private_data;
+	if (epcm->extra) {
+		snd_emu10k1_voice_free(epcm->emu, epcm->extra);
+		epcm->extra = NULL;
+	}
+	for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+		if (epcm->voices[i]) {
+			snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]);
+			epcm->voices[i] = NULL;
+		}
+	}
+	if (epcm->memblk) {
+		snd_emu10k1_free_pages(emu, epcm->memblk);
+		epcm->memblk = NULL;
+		epcm->start_addr = 0;
+	}
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_emu10k1_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int start_addr, end_addr;
+
+	start_addr = epcm->start_addr;
+	end_addr = snd_pcm_lib_period_bytes(substream);
+	if (runtime->channels == 2) {
+		start_addr >>= 1;
+		end_addr >>= 1;
+	}
+	end_addr += start_addr;
+	snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
+				   start_addr, end_addr, NULL);
+	start_addr = epcm->start_addr;
+	end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+	snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
+				   start_addr, end_addr,
+				   &emu->pcm_mixer[substream->number]);
+	if (epcm->voices[1])
+		snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1],
+					   start_addr, end_addr,
+					   &emu->pcm_mixer[substream->number]);
+	return 0;
+}
+
+static int snd_emu10k1_efx_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int start_addr, end_addr;
+	unsigned int channel_size;
+	int i;
+
+	start_addr = epcm->start_addr;
+	end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream);
+
+	/*
+	 * the kX driver leaves some space between voices
+	 */
+	channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK;
+
+	snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra,
+				   start_addr, start_addr + (channel_size / 2), NULL);
+
+	/* only difference with the master voice is we use it for the pointer */
+	snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0],
+				   start_addr, start_addr + channel_size,
+				   &emu->efx_pcm_mixer[0]);
+
+	start_addr += channel_size;
+	for (i = 1; i < NUM_EFX_PLAYBACK; i++) {
+		snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[i],
+					   start_addr, start_addr + channel_size,
+					   &emu->efx_pcm_mixer[i]);
+		start_addr += channel_size;
+	}
+
+	return 0;
+}
+
+static const struct snd_pcm_hardware snd_emu10k1_efx_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		NUM_EFX_PLAYBACK,
+	.channels_max =		NUM_EFX_PLAYBACK,
+	.buffer_bytes_max =	(64*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+static int snd_emu10k1_capture_hw_params(struct snd_pcm_substream *substream,
+					 struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_emu10k1_capture_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_emu10k1_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	int idx;
+
+	/* zeroing the buffer size will stop capture */
+	snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
+	switch (epcm->type) {
+	case CAPTURE_AC97ADC:
+		snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
+		break;
+	case CAPTURE_EFX:
+		if (emu->audigy) {
+			snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
+			snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+		} else
+			snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+		break;
+	default:
+		break;
+	}	
+	snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr);
+	epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream);
+	epcm->capture_bs_val = 0;
+	for (idx = 0; idx < 31; idx++) {
+		if (capture_period_sizes[idx] == epcm->capture_bufsize) {
+			epcm->capture_bs_val = idx + 1;
+			break;
+		}
+	}
+	if (epcm->capture_bs_val == 0) {
+		snd_BUG();
+		epcm->capture_bs_val++;
+	}
+	if (epcm->type == CAPTURE_AC97ADC) {
+		epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE;
+		if (runtime->channels > 1)
+			epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE;
+		epcm->capture_cr_val |= emu->audigy ?
+			snd_emu10k1_audigy_capture_rate_reg(runtime->rate) :
+			snd_emu10k1_capture_rate_reg(runtime->rate);
+	}
+	return 0;
+}
+
+static void snd_emu10k1_playback_invalidate_cache(struct snd_emu10k1 *emu, int extra, struct snd_emu10k1_voice *evoice)
+{
+	struct snd_pcm_runtime *runtime;
+	unsigned int voice, stereo, i, ccis, cra = 64, cs, sample;
+
+	if (evoice == NULL)
+		return;
+	runtime = evoice->epcm->substream->runtime;
+	voice = evoice->number;
+	stereo = (!extra && runtime->channels == 2);
+	sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080;
+	ccis = emu10k1_ccis(stereo, sample == 0);
+	/* set cs to 2 * number of cache registers beside the invalidated */
+	cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1;
+	if (cs > 16) cs = 16;
+	for (i = 0; i < cs; i++) {
+		snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample);
+		if (stereo) {
+			snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample);
+		}
+	}
+	/* reset cache */
+	snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0);
+	snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra);
+	if (stereo) {
+		snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0);
+		snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra);
+	}
+	/* fill cache */
+	snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);
+	if (stereo) {
+		snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis);
+	}
+}
+
+static void snd_emu10k1_playback_prepare_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice,
+					       int master, int extra,
+					       struct snd_emu10k1_pcm_mixer *mix)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_runtime *runtime;
+	unsigned int attn, vattn;
+	unsigned int voice, tmp;
+
+	if (evoice == NULL)	/* skip second voice for mono */
+		return;
+	substream = evoice->epcm->substream;
+	runtime = substream->runtime;
+	voice = evoice->number;
+
+	attn = extra ? 0 : 0x00ff;
+	tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0;
+	vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0;
+	snd_emu10k1_ptr_write(emu, IFATN, voice, attn);
+	snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | 0xffff);
+	snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | 0xffff);
+	snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f);
+	snd_emu10k1_voice_clear_loop_stop(emu, voice);
+}	
+
+static void snd_emu10k1_playback_trigger_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice, int master, int extra)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_runtime *runtime;
+	unsigned int voice, pitch, pitch_target;
+
+	if (evoice == NULL)	/* skip second voice for mono */
+		return;
+	substream = evoice->epcm->substream;
+	runtime = substream->runtime;
+	voice = evoice->number;
+
+	pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8;
+	if (emu->card_capabilities->emu_model)
+		pitch_target = PITCH_48000; /* Disable interpolators on emu1010 card */
+	else 
+		pitch_target = emu10k1_calc_pitch_target(runtime->rate);
+	snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target);
+	if (master || evoice->epcm->type == PLAYBACK_EFX)
+		snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target);
+	snd_emu10k1_ptr_write(emu, IP, voice, pitch);
+	if (extra)
+		snd_emu10k1_voice_intr_enable(emu, voice);
+}
+
+static void snd_emu10k1_playback_stop_voice(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *evoice)
+{
+	unsigned int voice;
+
+	if (evoice == NULL)
+		return;
+	voice = evoice->number;
+	snd_emu10k1_voice_intr_disable(emu, voice);
+	snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0);
+	snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0);
+	snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff);
+	snd_emu10k1_ptr_write(emu, IP, voice, 0);
+}
+
+static inline void snd_emu10k1_playback_mangle_extra(struct snd_emu10k1 *emu,
+		struct snd_emu10k1_pcm *epcm,
+		struct snd_pcm_substream *substream,
+		struct snd_pcm_runtime *runtime)
+{
+	unsigned int ptr, period_pos;
+
+	/* try to sychronize the current position for the interrupt
+	   source voice */
+	period_pos = runtime->status->hw_ptr - runtime->hw_ptr_interrupt;
+	period_pos %= runtime->period_size;
+	ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->extra->number);
+	ptr &= ~0x00ffffff;
+	ptr |= epcm->ccca_start_addr + period_pos;
+	snd_emu10k1_ptr_write(emu, CCCA, epcm->extra->number, ptr);
+}
+
+static int snd_emu10k1_playback_trigger(struct snd_pcm_substream *substream,
+				        int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	struct snd_emu10k1_pcm_mixer *mix;
+	int result = 0;
+
+	/*
+	dev_dbg(emu->card->dev,
+		"trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n",
+	       (int)emu, cmd, substream->ops->pointer(substream))
+	*/
+	spin_lock(&emu->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);	/* do we need this? */
+		snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]);
+		/* follow thru */
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
+			snd_emu10k1_playback_mangle_extra(emu, epcm, substream, runtime);
+		mix = &emu->pcm_mixer[substream->number];
+		snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix);
+		snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix);
+		snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		epcm->running = 0;
+		snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]);
+		snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]);
+		snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock(&emu->reg_lock);
+	return result;
+}
+
+static int snd_emu10k1_capture_trigger(struct snd_pcm_substream *substream,
+				       int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	int result = 0;
+
+	spin_lock(&emu->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		/* hmm this should cause full and half full interrupt to be raised? */
+		outl(epcm->capture_ipr, emu->port + IPR);
+		snd_emu10k1_intr_enable(emu, epcm->capture_inte);
+		/*
+		dev_dbg(emu->card->dev, "adccr = 0x%x, adcbs = 0x%x\n",
+		       epcm->adccr, epcm->adcbs);
+		*/
+		switch (epcm->type) {
+		case CAPTURE_AC97ADC:
+			snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val);
+			break;
+		case CAPTURE_EFX:
+			if (emu->audigy) {
+				snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val);
+				snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2);
+				dev_dbg(emu->card->dev,
+					"cr_val=0x%x, cr_val2=0x%x\n",
+					epcm->capture_cr_val,
+					epcm->capture_cr_val2);
+			} else
+				snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val);
+			break;
+		default:	
+			break;
+		}
+		snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val);
+		epcm->running = 1;
+		epcm->first_ptr = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		epcm->running = 0;
+		snd_emu10k1_intr_disable(emu, epcm->capture_inte);
+		outl(epcm->capture_ipr, emu->port + IPR);
+		snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0);
+		switch (epcm->type) {
+		case CAPTURE_AC97ADC:
+			snd_emu10k1_ptr_write(emu, ADCCR, 0, 0);
+			break;
+		case CAPTURE_EFX:
+			if (emu->audigy) {
+				snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0);
+				snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0);
+			} else
+				snd_emu10k1_ptr_write(emu, FXWC, 0, 0);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		result = -EINVAL;
+	}
+	spin_unlock(&emu->reg_lock);
+	return result;
+}
+
+static snd_pcm_uframes_t snd_emu10k1_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int ptr;
+
+	if (!epcm->running)
+		return 0;
+	ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;
+#if 0	/* Perex's code */
+	ptr += runtime->buffer_size;
+	ptr -= epcm->ccca_start_addr;
+	ptr %= runtime->buffer_size;
+#else	/* EMU10K1 Open Source code from Creative */
+	if (ptr < epcm->ccca_start_addr)
+		ptr += runtime->buffer_size - epcm->ccca_start_addr;
+	else {
+		ptr -= epcm->ccca_start_addr;
+		if (ptr >= runtime->buffer_size)
+			ptr -= runtime->buffer_size;
+	}
+#endif
+	/*
+	dev_dbg(emu->card->dev,
+	       "ptr = 0x%lx, buffer_size = 0x%lx, period_size = 0x%lx\n",
+	       (long)ptr, (long)runtime->buffer_size,
+	       (long)runtime->period_size);
+	*/
+	return ptr;
+}
+
+
+static int snd_emu10k1_efx_playback_trigger(struct snd_pcm_substream *substream,
+				        int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	int i;
+	int result = 0;
+
+	spin_lock(&emu->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* prepare voices */
+		for (i = 0; i < NUM_EFX_PLAYBACK; i++) {	
+			snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]);
+		}
+		snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra);
+
+		/* follow thru */
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL);
+		snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 0, 0,
+						   &emu->efx_pcm_mixer[0]);
+		for (i = 1; i < NUM_EFX_PLAYBACK; i++)
+			snd_emu10k1_playback_prepare_voice(emu, epcm->voices[i], 0, 0,
+							   &emu->efx_pcm_mixer[i]);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 0, 0);
+		snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1);
+		for (i = 1; i < NUM_EFX_PLAYBACK; i++)
+			snd_emu10k1_playback_trigger_voice(emu, epcm->voices[i], 0, 0);
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		epcm->running = 0;
+		for (i = 0; i < NUM_EFX_PLAYBACK; i++) {	
+			snd_emu10k1_playback_stop_voice(emu, epcm->voices[i]);
+		}
+		snd_emu10k1_playback_stop_voice(emu, epcm->extra);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	spin_unlock(&emu->reg_lock);
+	return result;
+}
+
+
+static snd_pcm_uframes_t snd_emu10k1_capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	unsigned int ptr;
+
+	if (!epcm->running)
+		return 0;
+	if (epcm->first_ptr) {
+		udelay(50);	/* hack, it takes awhile until capture is started */
+		epcm->first_ptr = 0;
+	}
+	ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff;
+	return bytes_to_frames(runtime, ptr);
+}
+
+/*
+ *  Playback support device description
+ */
+
+static const struct snd_pcm_hardware snd_emu10k1_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_96000,
+	.rate_min =		4000,
+	.rate_max =		96000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+/*
+ *  Capture support device description
+ */
+
+static const struct snd_pcm_hardware snd_emu10k1_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(64*1024),
+	.period_bytes_min =	384,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+static const struct snd_pcm_hardware snd_emu10k1_capture_efx =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 
+				 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | 
+				 SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+	.rate_min =		44100,
+	.rate_max =		192000,
+	.channels_min =		8,
+	.channels_max =		8,
+	.buffer_bytes_max =	(64*1024),
+	.period_bytes_min =	384,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+/*
+ *
+ */
+
+static void snd_emu10k1_pcm_mixer_notify1(struct snd_emu10k1 *emu, struct snd_kcontrol *kctl, int idx, int activate)
+{
+	struct snd_ctl_elem_id id;
+
+	if (! kctl)
+		return;
+	if (activate)
+		kctl->vd[idx].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	else
+		kctl->vd[idx].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+	snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE |
+		       SNDRV_CTL_EVENT_MASK_INFO,
+		       snd_ctl_build_ioff(&id, kctl, idx));
+}
+
+static void snd_emu10k1_pcm_mixer_notify(struct snd_emu10k1 *emu, int idx, int activate)
+{
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_routing, idx, activate);
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_volume, idx, activate);
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_attn, idx, activate);
+}
+
+static void snd_emu10k1_pcm_efx_mixer_notify(struct snd_emu10k1 *emu, int idx, int activate)
+{
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_routing, idx, activate);
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_send_volume, idx, activate);
+	snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_efx_attn, idx, activate);
+}
+
+static void snd_emu10k1_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+	kfree(runtime->private_data);
+}
+
+static int snd_emu10k1_efx_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm_mixer *mix;
+	int i;
+
+	for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+		mix = &emu->efx_pcm_mixer[i];
+		mix->epcm = NULL;
+		snd_emu10k1_pcm_efx_mixer_notify(emu, i, 0);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_efx_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_emu10k1_pcm_mixer *mix;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int i;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = PLAYBACK_EFX;
+	epcm->substream = substream;
+	
+	emu->pcm_playback_efx_substream = substream;
+
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_efx_playback;
+	
+	for (i = 0; i < NUM_EFX_PLAYBACK; i++) {
+		mix = &emu->efx_pcm_mixer[i];
+		mix->send_routing[0][0] = i;
+		memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+		mix->send_volume[0][0] = 255;
+		mix->attn[0] = 0xffff;
+		mix->epcm = epcm;
+		snd_emu10k1_pcm_efx_mixer_notify(emu, i, 1);
+	}
+	return 0;
+}
+
+static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_emu10k1_pcm_mixer *mix;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int i, err, sample_rate;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = PLAYBACK_EMUVOICE;
+	epcm->substream = substream;
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_playback;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) {
+		kfree(epcm);
+		return err;
+	}
+	if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) {
+		kfree(epcm);
+		return err;
+	}
+	if (emu->card_capabilities->emu_model && emu->emu1010.internal_clock == 0)
+		sample_rate = 44100;
+	else
+		sample_rate = 48000;
+	err = snd_pcm_hw_rule_noresample(runtime, sample_rate);
+	if (err < 0) {
+		kfree(epcm);
+		return err;
+	}
+	mix = &emu->pcm_mixer[substream->number];
+	for (i = 0; i < 4; i++)
+		mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i;
+	memset(&mix->send_volume, 0, sizeof(mix->send_volume));
+	mix->send_volume[0][0] = mix->send_volume[0][1] =
+	mix->send_volume[1][0] = mix->send_volume[2][1] = 255;
+	mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
+	mix->epcm = epcm;
+	snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1);
+	return 0;
+}
+
+static int snd_emu10k1_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm_mixer *mix = &emu->pcm_mixer[substream->number];
+
+	mix->epcm = NULL;
+	snd_emu10k1_pcm_mixer_notify(emu, substream->number, 0);
+	return 0;
+}
+
+static int snd_emu10k1_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = CAPTURE_AC97ADC;
+	epcm->substream = substream;
+	epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL;
+	epcm->capture_inte = INTE_ADCBUFENABLE;
+	epcm->capture_ba_reg = ADCBA;
+	epcm->capture_bs_reg = ADCBS;
+	epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX;
+	runtime->private_data = epcm;
+	runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_capture;
+	emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt;
+	emu->pcm_capture_substream = substream;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates);
+	return 0;
+}
+
+static int snd_emu10k1_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+
+	emu->capture_interrupt = NULL;
+	emu->pcm_capture_substream = NULL;
+	return 0;
+}
+
+static int snd_emu10k1_capture_mic_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = CAPTURE_AC97MIC;
+	epcm->substream = substream;
+	epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL;
+	epcm->capture_inte = INTE_MICBUFENABLE;
+	epcm->capture_ba_reg = MICBA;
+	epcm->capture_bs_reg = MICBS;
+	epcm->capture_idx_reg = emu->audigy ? A_MICIDX : MICIDX;
+	substream->runtime->private_data = epcm;
+	substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_capture;
+	runtime->hw.rates = SNDRV_PCM_RATE_8000;
+	runtime->hw.rate_min = runtime->hw.rate_max = 8000;
+	runtime->hw.channels_min = 1;
+	emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt;
+	emu->pcm_capture_mic_substream = substream;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+	return 0;
+}
+
+static int snd_emu10k1_capture_mic_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+
+	emu->capture_interrupt = NULL;
+	emu->pcm_capture_mic_substream = NULL;
+	return 0;
+}
+
+static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int nefx = emu->audigy ? 64 : 32;
+	int idx;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->type = CAPTURE_EFX;
+	epcm->substream = substream;
+	epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL;
+	epcm->capture_inte = INTE_EFXBUFENABLE;
+	epcm->capture_ba_reg = FXBA;
+	epcm->capture_bs_reg = FXBS;
+	epcm->capture_idx_reg = FXIDX;
+	substream->runtime->private_data = epcm;
+	substream->runtime->private_free = snd_emu10k1_pcm_free_substream;
+	runtime->hw = snd_emu10k1_capture_efx;
+	runtime->hw.rates = SNDRV_PCM_RATE_48000;
+	runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+	spin_lock_irq(&emu->reg_lock);
+	if (emu->card_capabilities->emu_model) {
+		/*  Nb. of channels has been increased to 16 */
+		/* TODO
+		 * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
+		 * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+		 * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+		 * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
+		 * rate_min = 44100,
+		 * rate_max = 192000,
+		 * channels_min = 16,
+		 * channels_max = 16,
+		 * Need to add mixer control to fix sample rate
+		 *                 
+		 * There are 32 mono channels of 16bits each.
+		 * 24bit Audio uses 2x channels over 16bit
+		 * 96kHz uses 2x channels over 48kHz
+		 * 192kHz uses 4x channels over 48kHz
+		 * So, for 48kHz 24bit, one has 16 channels
+		 * for 96kHz 24bit, one has 8 channels
+		 * for 192kHz 24bit, one has 4 channels
+		 *
+		 */
+#if 1
+		switch (emu->emu1010.internal_clock) {
+		case 0:
+			/* For 44.1kHz */
+			runtime->hw.rates = SNDRV_PCM_RATE_44100;
+			runtime->hw.rate_min = runtime->hw.rate_max = 44100;
+			runtime->hw.channels_min =
+				runtime->hw.channels_max = 16;
+			break;
+		case 1:
+			/* For 48kHz */
+			runtime->hw.rates = SNDRV_PCM_RATE_48000;
+			runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+			runtime->hw.channels_min =
+				runtime->hw.channels_max = 16;
+			break;
+		}
+#endif
+#if 0
+		/* For 96kHz */
+		runtime->hw.rates = SNDRV_PCM_RATE_96000;
+		runtime->hw.rate_min = runtime->hw.rate_max = 96000;
+		runtime->hw.channels_min = runtime->hw.channels_max = 4;
+#endif
+#if 0
+		/* For 192kHz */
+		runtime->hw.rates = SNDRV_PCM_RATE_192000;
+		runtime->hw.rate_min = runtime->hw.rate_max = 192000;
+		runtime->hw.channels_min = runtime->hw.channels_max = 2;
+#endif
+		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+		/* efx_voices_mask[0] is expected to be zero
+ 		 * efx_voices_mask[1] is expected to have 32bits set
+		 */
+	} else {
+		runtime->hw.channels_min = runtime->hw.channels_max = 0;
+		for (idx = 0; idx < nefx; idx++) {
+			if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) {
+				runtime->hw.channels_min++;
+				runtime->hw.channels_max++;
+			}
+		}
+	}
+	epcm->capture_cr_val = emu->efx_voices_mask[0];
+	epcm->capture_cr_val2 = emu->efx_voices_mask[1];
+	spin_unlock_irq(&emu->reg_lock);
+	emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt;
+	emu->pcm_capture_efx_substream = substream;
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes);
+	return 0;
+}
+
+static int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+
+	emu->capture_interrupt = NULL;
+	emu->pcm_capture_efx_substream = NULL;
+	return 0;
+}
+
+static const struct snd_pcm_ops snd_emu10k1_playback_ops = {
+	.open =			snd_emu10k1_playback_open,
+	.close =		snd_emu10k1_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_playback_hw_params,
+	.hw_free =		snd_emu10k1_playback_hw_free,
+	.prepare =		snd_emu10k1_playback_prepare,
+	.trigger =		snd_emu10k1_playback_trigger,
+	.pointer =		snd_emu10k1_playback_pointer,
+	.page =			snd_pcm_sgbuf_ops_page,
+};
+
+static const struct snd_pcm_ops snd_emu10k1_capture_ops = {
+	.open =			snd_emu10k1_capture_open,
+	.close =		snd_emu10k1_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_capture_hw_params,
+	.hw_free =		snd_emu10k1_capture_hw_free,
+	.prepare =		snd_emu10k1_capture_prepare,
+	.trigger =		snd_emu10k1_capture_trigger,
+	.pointer =		snd_emu10k1_capture_pointer,
+};
+
+/* EFX playback */
+static const struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
+	.open =			snd_emu10k1_efx_playback_open,
+	.close =		snd_emu10k1_efx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_playback_hw_params,
+	.hw_free =		snd_emu10k1_efx_playback_hw_free,
+	.prepare =		snd_emu10k1_efx_playback_prepare,
+	.trigger =		snd_emu10k1_efx_playback_trigger,
+	.pointer =		snd_emu10k1_efx_playback_pointer,
+	.page =			snd_pcm_sgbuf_ops_page,
+};
+
+int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device)
+{
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+	int err;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1", device, 32, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_ops);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "ADC Capture/Standard PCM Playback");
+	emu->pcm = pcm;
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+		if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
+			return err;
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next)
+		snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+
+	return 0;
+}
+
+int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device)
+{
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+	int err;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_efx_playback_ops);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "Multichannel Playback");
+	emu->pcm_multi = pcm;
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
+		if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0)
+			return err;
+
+	return 0;
+}
+
+
+static const struct snd_pcm_ops snd_emu10k1_capture_mic_ops = {
+	.open =			snd_emu10k1_capture_mic_open,
+	.close =		snd_emu10k1_capture_mic_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_capture_hw_params,
+	.hw_free =		snd_emu10k1_capture_hw_free,
+	.prepare =		snd_emu10k1_capture_prepare,
+	.trigger =		snd_emu10k1_capture_trigger,
+	.pointer =		snd_emu10k1_capture_pointer,
+};
+
+int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "Mic Capture");
+	emu->pcm_mic = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+
+	return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int nefx = emu->audigy ? 64 : 32;
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = nefx;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int nefx = emu->audigy ? 64 : 32;
+	int idx;
+	
+	spin_lock_irq(&emu->reg_lock);
+	for (idx = 0; idx < nefx; idx++)
+		ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0;
+	spin_unlock_irq(&emu->reg_lock);
+	return 0;
+}
+
+static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int nval[2], bits;
+	int nefx = emu->audigy ? 64 : 32;
+	int nefxb = emu->audigy ? 7 : 6;
+	int change, idx;
+	
+	nval[0] = nval[1] = 0;
+	for (idx = 0, bits = 0; idx < nefx; idx++)
+		if (ucontrol->value.integer.value[idx]) {
+			nval[idx / 32] |= 1 << (idx % 32);
+			bits++;
+		}
+		
+	for (idx = 0; idx < nefxb; idx++)
+		if (1 << idx == bits)
+			break;
+	
+	if (idx >= nefxb)
+		return -EINVAL;
+
+	spin_lock_irq(&emu->reg_lock);
+	change = (nval[0] != emu->efx_voices_mask[0]) ||
+		(nval[1] != emu->efx_voices_mask[1]);
+	emu->efx_voices_mask[0] = nval[0];
+	emu->efx_voices_mask[1] = nval[1];
+	spin_unlock_irq(&emu->reg_lock);
+	return change;
+}
+
+static const struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.name = "Captured FX8010 Outputs",
+	.info = snd_emu10k1_pcm_efx_voices_mask_info,
+	.get = snd_emu10k1_pcm_efx_voices_mask_get,
+	.put = snd_emu10k1_pcm_efx_voices_mask_put
+};
+
+static const struct snd_pcm_ops snd_emu10k1_capture_efx_ops = {
+	.open =			snd_emu10k1_capture_efx_open,
+	.close =		snd_emu10k1_capture_efx_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_capture_hw_params,
+	.hw_free =		snd_emu10k1_capture_hw_free,
+	.prepare =		snd_emu10k1_capture_prepare,
+	.trigger =		snd_emu10k1_capture_trigger,
+	.pointer =		snd_emu10k1_capture_pointer,
+};
+
+
+/* EFX playback */
+
+#define INITIAL_TRAM_SHIFT     14
+#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1)
+
+static void snd_emu10k1_fx8010_playback_irq(struct snd_emu10k1 *emu, void *private_data)
+{
+	struct snd_pcm_substream *substream = private_data;
+	snd_pcm_period_elapsed(substream);
+}
+
+static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left,
+						   unsigned short *dst_right,
+						   unsigned short *src,
+						   unsigned int count,
+						   unsigned int tram_shift)
+{
+	/*
+	dev_dbg(emu->card->dev,
+		"tram_poke1: dst_left = 0x%p, dst_right = 0x%p, "
+	       "src = 0x%p, count = 0x%x\n",
+	       dst_left, dst_right, src, count);
+	*/
+	if ((tram_shift & 1) == 0) {
+		while (count--) {
+			*dst_left-- = *src++;
+			*dst_right-- = *src++;
+		}
+	} else {
+		while (count--) {
+			*dst_right-- = *src++;
+			*dst_left-- = *src++;
+		}
+	}
+}
+
+static void fx8010_pb_trans_copy(struct snd_pcm_substream *substream,
+				 struct snd_pcm_indirect *rec, size_t bytes)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	unsigned int tram_size = pcm->buffer_size;
+	unsigned short *src = (unsigned short *)(substream->runtime->dma_area + rec->sw_data);
+	unsigned int frames = bytes >> 2, count;
+	unsigned int tram_pos = pcm->tram_pos;
+	unsigned int tram_shift = pcm->tram_shift;
+
+	while (frames > tram_pos) {
+		count = tram_pos + 1;
+		snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos,
+						       (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2,
+						       src, count, tram_shift);
+		src += count * 2;
+		frames -= count;
+		tram_pos = (tram_size / 2) - 1;
+		tram_shift++;
+	}
+	snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos,
+					       (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2,
+					       src, frames, tram_shift);
+	tram_pos -= frames;
+	pcm->tram_pos = tram_pos;
+	pcm->tram_shift = tram_shift;
+}
+
+static int snd_emu10k1_fx8010_playback_transfer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+
+	return snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec,
+						  fx8010_pb_trans_copy);
+}
+
+static int snd_emu10k1_fx8010_playback_hw_params(struct snd_pcm_substream *substream,
+						 struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_emu10k1_fx8010_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	unsigned int i;
+
+	for (i = 0; i < pcm->channels; i++)
+		snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0);
+	snd_pcm_lib_free_pages(substream);
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	unsigned int i;
+	
+	/*
+	dev_dbg(emu->card->dev, "prepare: etram_pages = 0x%p, dma_area = 0x%x, "
+	       "buffer_size = 0x%x (0x%x)\n",
+	       emu->fx8010.etram_pages, runtime->dma_area,
+	       runtime->buffer_size, runtime->buffer_size << 2);
+	*/
+	memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec));
+	pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */
+	pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
+	pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
+	pcm->tram_shift = 0;
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0);	/* reset */
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);	/* reset */
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size);
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0);		/* reset ptr number */
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size);
+	snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size);
+	for (i = 0; i < pcm->channels; i++)
+		snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels));
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	int result = 0;
+
+	spin_lock(&emu->reg_lock);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* follow thru */
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+#ifdef EMU10K1_SET_AC3_IEC958
+	{
+		int i;
+		for (i = 0; i < 3; i++) {
+			unsigned int bits;
+			bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 |
+			       SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS |
+			       0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA;
+			snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits);
+		}
+	}
+#endif
+		result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq);
+		if (result < 0)
+			goto __err;
+		snd_emu10k1_fx8010_playback_transfer(substream);	/* roll the ball */
+		snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL;
+		snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0);
+		pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size);
+		pcm->tram_shift = 0;
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+      __err:
+	spin_unlock(&emu->reg_lock);
+	return result;
+}
+
+static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+	size_t ptr; /* byte pointer */
+
+	if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0))
+		return 0;
+	ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0) << 2;
+	return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr);
+}
+
+static const struct snd_pcm_hardware snd_emu10k1_fx8010_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_RESUME |
+				 /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		1,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	1024,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static int snd_emu10k1_fx8010_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+
+	runtime->hw = snd_emu10k1_fx8010_playback;
+	runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels;
+	runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2;
+	spin_lock_irq(&emu->reg_lock);
+	if (pcm->valid == 0) {
+		spin_unlock_irq(&emu->reg_lock);
+		return -ENODEV;
+	}
+	pcm->opened = 1;
+	spin_unlock_irq(&emu->reg_lock);
+	return 0;
+}
+
+static int snd_emu10k1_fx8010_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_fx8010_pcm *pcm = &emu->fx8010.pcm[substream->number];
+
+	spin_lock_irq(&emu->reg_lock);
+	pcm->opened = 0;
+	spin_unlock_irq(&emu->reg_lock);
+	return 0;
+}
+
+static const struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
+	.open =			snd_emu10k1_fx8010_playback_open,
+	.close =		snd_emu10k1_fx8010_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_fx8010_playback_hw_params,
+	.hw_free =		snd_emu10k1_fx8010_playback_hw_free,
+	.prepare =		snd_emu10k1_fx8010_playback_prepare,
+	.trigger =		snd_emu10k1_fx8010_playback_trigger,
+	.pointer =		snd_emu10k1_fx8010_playback_pointer,
+	.ack =			snd_emu10k1_fx8010_playback_transfer,
+};
+
+int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device)
+{
+	struct snd_pcm *pcm;
+	struct snd_kcontrol *kctl;
+	int err;
+
+	if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0)
+		return err;
+
+	pcm->private_data = emu;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "Multichannel Capture/PT Playback");
+	emu->pcm_efx = pcm;
+
+	/* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs 
+	 * to these
+	 */	
+	
+	/* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
+	if (emu->audigy) {
+		emu->efx_voices_mask[0] = 0;
+		if (emu->card_capabilities->emu_model)
+			/* Pavel Hofman - 32 voices will be used for
+			 * capture (write mode) -
+			 * each bit = corresponding voice
+			 */
+			emu->efx_voices_mask[1] = 0xffffffff;
+		else
+			emu->efx_voices_mask[1] = 0xffff;
+	} else {
+		emu->efx_voices_mask[0] = 0xffff0000;
+		emu->efx_voices_mask[1] = 0;
+	}
+	/* For emu1010, the control has to set 32 upper bits (voices)
+	 * out of the 64 bits (voices) to true for the 16-channels capture
+	 * to work correctly. Correct A_FXWC2 initial value (0xffffffff)
+	 * is already defined but the snd_emu10k1_pcm_efx_voices_mask
+	 * control can override this register's value.
+	 */
+	kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
+	if (!kctl)
+		return -ENOMEM;
+	kctl->id.device = device;
+	err = snd_ctl_add(emu->card, kctl);
+	if (err < 0)
+		return err;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024);
+
+	return 0;
+}
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/emuproc.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/emuproc.c
new file mode 100644
index 0000000..cf05229
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/emuproc.c
@@ -0,0 +1,657 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips / proc interface routines
+ *
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
+ *  	Added EMU 1010 support.
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#include "p16v.h"
+
+static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
+					  struct snd_info_buffer *buffer,
+					  char *title,
+					  int status_reg,
+					  int rate_reg)
+{
+	static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" };
+	static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+	static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
+	static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" };
+	unsigned int status, rate = 0;
+	
+	status = snd_emu10k1_ptr_read(emu, status_reg, 0);
+
+	snd_iprintf(buffer, "\n%s\n", title);
+
+	if (status != 0xffffffff) {
+		snd_iprintf(buffer, "Professional Mode     : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
+		snd_iprintf(buffer, "Not Audio Data        : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
+		snd_iprintf(buffer, "Copyright             : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
+		snd_iprintf(buffer, "Emphasis              : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
+		snd_iprintf(buffer, "Mode                  : %i\n", (status & SPCS_MODEMASK) >> 6);
+		snd_iprintf(buffer, "Category Code         : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
+		snd_iprintf(buffer, "Generation Status     : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy");
+		snd_iprintf(buffer, "Source Mask           : %i\n", (status & SPCS_SOURCENUMMASK) >> 16);
+		snd_iprintf(buffer, "Channel Number        : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]);
+		snd_iprintf(buffer, "Sample Rate           : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]);
+		snd_iprintf(buffer, "Clock Accuracy        : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]);
+
+		if (rate_reg > 0) {
+			rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
+			snd_iprintf(buffer, "S/PDIF Valid          : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off");
+			snd_iprintf(buffer, "S/PDIF Locked         : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
+			snd_iprintf(buffer, "Rate Locked           : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
+			/* From ((Rate * 48000 ) / 262144); */
+			snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11); 
+		}
+	} else {
+		snd_iprintf(buffer, "No signal detected.\n");
+	}
+
+}
+
+static void snd_emu10k1_proc_read(struct snd_info_entry *entry, 
+				  struct snd_info_buffer *buffer)
+{
+	/* FIXME - output names are in emufx.c too */
+	static char *creative_outs[32] = {
+		/* 00 */ "AC97 Left",
+		/* 01 */ "AC97 Right",
+		/* 02 */ "Optical IEC958 Left",
+		/* 03 */ "Optical IEC958 Right",
+		/* 04 */ "Center",
+		/* 05 */ "LFE",
+		/* 06 */ "Headphone Left",
+		/* 07 */ "Headphone Right",
+		/* 08 */ "Surround Left",
+		/* 09 */ "Surround Right",
+		/* 10 */ "PCM Capture Left",
+		/* 11 */ "PCM Capture Right",
+		/* 12 */ "MIC Capture",
+		/* 13 */ "AC97 Surround Left",
+		/* 14 */ "AC97 Surround Right",
+		/* 15 */ "???",
+		/* 16 */ "???",
+		/* 17 */ "Analog Center",
+		/* 18 */ "Analog LFE",
+		/* 19 */ "???",
+		/* 20 */ "???",
+		/* 21 */ "???",
+		/* 22 */ "???",
+		/* 23 */ "???",
+		/* 24 */ "???",
+		/* 25 */ "???",
+		/* 26 */ "???",
+		/* 27 */ "???",
+		/* 28 */ "???",
+		/* 29 */ "???",
+		/* 30 */ "???",
+		/* 31 */ "???"
+	};
+
+	static char *audigy_outs[64] = {
+		/* 00 */ "Digital Front Left",
+		/* 01 */ "Digital Front Right",
+		/* 02 */ "Digital Center",
+		/* 03 */ "Digital LEF",
+		/* 04 */ "Headphone Left",
+		/* 05 */ "Headphone Right",
+		/* 06 */ "Digital Rear Left",
+		/* 07 */ "Digital Rear Right",
+		/* 08 */ "Front Left",
+		/* 09 */ "Front Right",
+		/* 10 */ "Center",
+		/* 11 */ "LFE",
+		/* 12 */ "???",
+		/* 13 */ "???",
+		/* 14 */ "Rear Left",
+		/* 15 */ "Rear Right",
+		/* 16 */ "AC97 Front Left",
+		/* 17 */ "AC97 Front Right",
+		/* 18 */ "ADC Caputre Left",
+		/* 19 */ "ADC Capture Right",
+		/* 20 */ "???",
+		/* 21 */ "???",
+		/* 22 */ "???",
+		/* 23 */ "???",
+		/* 24 */ "???",
+		/* 25 */ "???",
+		/* 26 */ "???",
+		/* 27 */ "???",
+		/* 28 */ "???",
+		/* 29 */ "???",
+		/* 30 */ "???",
+		/* 31 */ "???",
+		/* 32 */ "FXBUS2_0",
+		/* 33 */ "FXBUS2_1",
+		/* 34 */ "FXBUS2_2",
+		/* 35 */ "FXBUS2_3",
+		/* 36 */ "FXBUS2_4",
+		/* 37 */ "FXBUS2_5",
+		/* 38 */ "FXBUS2_6",
+		/* 39 */ "FXBUS2_7",
+		/* 40 */ "FXBUS2_8",
+		/* 41 */ "FXBUS2_9",
+		/* 42 */ "FXBUS2_10",
+		/* 43 */ "FXBUS2_11",
+		/* 44 */ "FXBUS2_12",
+		/* 45 */ "FXBUS2_13",
+		/* 46 */ "FXBUS2_14",
+		/* 47 */ "FXBUS2_15",
+		/* 48 */ "FXBUS2_16",
+		/* 49 */ "FXBUS2_17",
+		/* 50 */ "FXBUS2_18",
+		/* 51 */ "FXBUS2_19",
+		/* 52 */ "FXBUS2_20",
+		/* 53 */ "FXBUS2_21",
+		/* 54 */ "FXBUS2_22",
+		/* 55 */ "FXBUS2_23",
+		/* 56 */ "FXBUS2_24",
+		/* 57 */ "FXBUS2_25",
+		/* 58 */ "FXBUS2_26",
+		/* 59 */ "FXBUS2_27",
+		/* 60 */ "FXBUS2_28",
+		/* 61 */ "FXBUS2_29",
+		/* 62 */ "FXBUS2_30",
+		/* 63 */ "FXBUS2_31"
+	};
+
+	struct snd_emu10k1 *emu = entry->private_data;
+	unsigned int val, val1;
+	int nefx = emu->audigy ? 64 : 32;
+	char **outputs = emu->audigy ? audigy_outs : creative_outs;
+	int idx;
+	
+	snd_iprintf(buffer, "EMU10K1\n\n");
+	snd_iprintf(buffer, "Card                  : %s\n",
+		    emu->audigy ? "Audigy" : (emu->card_capabilities->ecard ? "EMU APS" : "Creative"));
+	snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
+	snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes / 2);
+	snd_iprintf(buffer, "\n");
+	snd_iprintf(buffer, "Effect Send Routing   :\n");
+	for (idx = 0; idx < NUM_G; idx++) {
+		val = emu->audigy ?
+			snd_emu10k1_ptr_read(emu, A_FXRT1, idx) :
+			snd_emu10k1_ptr_read(emu, FXRT, idx);
+		val1 = emu->audigy ?
+			snd_emu10k1_ptr_read(emu, A_FXRT2, idx) :
+			0;
+		if (emu->audigy) {
+			snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i, ",
+				idx,
+				val & 0x3f,
+				(val >> 8) & 0x3f,
+				(val >> 16) & 0x3f,
+				(val >> 24) & 0x3f);
+			snd_iprintf(buffer, "E=%i, F=%i, G=%i, H=%i\n",
+				val1 & 0x3f,
+				(val1 >> 8) & 0x3f,
+				(val1 >> 16) & 0x3f,
+				(val1 >> 24) & 0x3f);
+		} else {
+			snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i\n",
+				idx,
+				(val >> 16) & 0x0f,
+				(val >> 20) & 0x0f,
+				(val >> 24) & 0x0f,
+				(val >> 28) & 0x0f);
+		}
+	}
+	snd_iprintf(buffer, "\nCaptured FX Outputs   :\n");
+	for (idx = 0; idx < nefx; idx++) {
+		if (emu->efx_voices_mask[idx/32] & (1 << (idx%32)))
+			snd_iprintf(buffer, "  Output %02i [%s]\n", idx, outputs[idx]);
+	}
+	snd_iprintf(buffer, "\nAll FX Outputs        :\n");
+	for (idx = 0; idx < (emu->audigy ? 64 : 32); idx++)
+		snd_iprintf(buffer, "  Output %02i [%s]\n", idx, outputs[idx]);
+}
+
+static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, 
+				  struct snd_info_buffer *buffer)
+{
+	struct snd_emu10k1 *emu = entry->private_data;
+	u32 value;
+	u32 value2;
+	u32 rate;
+
+	if (emu->card_capabilities->emu_model) {
+		snd_emu1010_fpga_read(emu, 0x38, &value);
+		if ((value & 0x1) == 0) {
+			snd_emu1010_fpga_read(emu, 0x2a, &value);
+			snd_emu1010_fpga_read(emu, 0x2b, &value2);
+			rate = 0x1770000 / (((value << 5) | value2)+1);	
+			snd_iprintf(buffer, "ADAT Locked : %u\n", rate);
+		} else {
+			snd_iprintf(buffer, "ADAT Unlocked\n");
+		}
+		snd_emu1010_fpga_read(emu, 0x20, &value);
+		if ((value & 0x4) == 0) {
+			snd_emu1010_fpga_read(emu, 0x28, &value);
+			snd_emu1010_fpga_read(emu, 0x29, &value2);
+			rate = 0x1770000 / (((value << 5) | value2)+1);	
+			snd_iprintf(buffer, "SPDIF Locked : %d\n", rate);
+		} else {
+			snd_iprintf(buffer, "SPDIF Unlocked\n");
+		}
+	} else {
+		snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
+		snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
+	}
+#if 0
+	val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
+	snd_iprintf(buffer, "\nZoomed Video\n");
+	snd_iprintf(buffer, "Rate Locked           : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
+	snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
+#endif
+}
+
+static void snd_emu10k1_proc_rates_read(struct snd_info_entry *entry, 
+				  struct snd_info_buffer *buffer)
+{
+	static int samplerate[8] = { 44100, 48000, 96000, 192000, 4, 5, 6, 7 };
+	struct snd_emu10k1 *emu = entry->private_data;
+	unsigned int val, tmp, n;
+	val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0);
+	tmp = (val >> 16) & 0x8;
+	for (n = 0; n < 4; n++) {
+		tmp = val >> (16 + (n*4));
+		if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]);
+		else snd_iprintf(buffer, "Channel %d: No input\n", n);
+	}
+}
+
+static void snd_emu10k1_proc_acode_read(struct snd_info_entry *entry, 
+				        struct snd_info_buffer *buffer)
+{
+	u32 pc;
+	struct snd_emu10k1 *emu = entry->private_data;
+
+	snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name);
+	snd_iprintf(buffer, "  Code dump      :\n");
+	for (pc = 0; pc < (emu->audigy ? 1024 : 512); pc++) {
+		u32 low, high;
+			
+		low = snd_emu10k1_efx_read(emu, pc * 2);
+		high = snd_emu10k1_efx_read(emu, pc * 2 + 1);
+		if (emu->audigy)
+			snd_iprintf(buffer, "    OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
+				    (high >> 24) & 0x0f,
+				    (high >> 12) & 0x7ff,
+				    (high >> 0) & 0x7ff,
+				    (low >> 12) & 0x7ff,
+				    (low >> 0) & 0x7ff,
+				    pc,
+				    high, low);
+		else
+			snd_iprintf(buffer, "    OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
+				    (high >> 20) & 0x0f,
+				    (high >> 10) & 0x3ff,
+				    (high >> 0) & 0x3ff,
+				    (low >> 10) & 0x3ff,
+				    (low >> 0) & 0x3ff,
+				    pc,
+				    high, low);
+	}
+}
+
+#define TOTAL_SIZE_GPR		(0x100*4)
+#define A_TOTAL_SIZE_GPR	(0x200*4)
+#define TOTAL_SIZE_TANKMEM_DATA	(0xa0*4)
+#define TOTAL_SIZE_TANKMEM_ADDR (0xa0*4)
+#define A_TOTAL_SIZE_TANKMEM_DATA (0x100*4)
+#define A_TOTAL_SIZE_TANKMEM_ADDR (0x100*4)
+#define TOTAL_SIZE_CODE		(0x200*8)
+#define A_TOTAL_SIZE_CODE	(0x400*8)
+
+static ssize_t snd_emu10k1_fx8010_read(struct snd_info_entry *entry,
+				       void *file_private_data,
+				       struct file *file, char __user *buf,
+				       size_t count, loff_t pos)
+{
+	struct snd_emu10k1 *emu = entry->private_data;
+	unsigned int offset;
+	int tram_addr = 0;
+	unsigned int *tmp;
+	long res;
+	unsigned int idx;
+	
+	if (!strcmp(entry->name, "fx8010_tram_addr")) {
+		offset = TANKMEMADDRREGBASE;
+		tram_addr = 1;
+	} else if (!strcmp(entry->name, "fx8010_tram_data")) {
+		offset = TANKMEMDATAREGBASE;
+	} else if (!strcmp(entry->name, "fx8010_code")) {
+		offset = emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
+	} else {
+		offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE;
+	}
+
+	tmp = kmalloc(count + 8, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+	for (idx = 0; idx < ((pos & 3) + count + 3) >> 2; idx++) {
+		unsigned int val;
+		val = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0);
+		if (tram_addr && emu->audigy) {
+			val >>= 11;
+			val |= snd_emu10k1_ptr_read(emu, 0x100 + idx + (pos >> 2), 0) << 20;
+		}
+		tmp[idx] = val;
+	}
+	if (copy_to_user(buf, ((char *)tmp) + (pos & 3), count))
+		res = -EFAULT;
+	else
+		res = count;
+	kfree(tmp);
+	return res;
+}
+
+static void snd_emu10k1_proc_voices_read(struct snd_info_entry *entry, 
+				  struct snd_info_buffer *buffer)
+{
+	struct snd_emu10k1 *emu = entry->private_data;
+	struct snd_emu10k1_voice *voice;
+	int idx;
+	
+	snd_iprintf(buffer, "ch\tuse\tpcm\tefx\tsynth\tmidi\n");
+	for (idx = 0; idx < NUM_G; idx++) {
+		voice = &emu->voices[idx];
+		snd_iprintf(buffer, "%i\t%i\t%i\t%i\t%i\t%i\n",
+			idx,
+			voice->use,
+			voice->pcm,
+			voice->efx,
+			voice->synth,
+			voice->midi);
+	}
+}
+
+#ifdef CONFIG_SND_DEBUG
+static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
+				     struct snd_info_buffer *buffer)
+{
+	struct snd_emu10k1 *emu = entry->private_data;
+	u32 value;
+	int i;
+	snd_iprintf(buffer, "EMU1010 Registers:\n\n");
+
+	for(i = 0; i < 0x40; i+=1) {
+		snd_emu1010_fpga_read(emu, i, &value);
+		snd_iprintf(buffer, "%02X: %08X, %02X\n", i, value, (value >> 8) & 0x7f);
+	}
+}
+
+static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry,
+				     struct snd_info_buffer *buffer)
+{
+	struct snd_emu10k1 *emu = entry->private_data;
+	unsigned long value;
+	unsigned long flags;
+	int i;
+	snd_iprintf(buffer, "IO Registers:\n\n");
+	for(i = 0; i < 0x40; i+=4) {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		value = inl(emu->port + i);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		snd_iprintf(buffer, "%02X: %08lX\n", i, value);
+	}
+}
+
+static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry,
+                                      struct snd_info_buffer *buffer)
+{
+	struct snd_emu10k1 *emu = entry->private_data;
+	unsigned long flags;
+	char line[64];
+	u32 reg, val;
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		if (sscanf(line, "%x %x", &reg, &val) != 2)
+			continue;
+		if (reg < 0x40 && val <= 0xffffffff) {
+			spin_lock_irqsave(&emu->emu_lock, flags);
+			outl(val, emu->port + (reg & 0xfffffffc));
+			spin_unlock_irqrestore(&emu->emu_lock, flags);
+		}
+	}
+}
+
+static unsigned int snd_ptr_read(struct snd_emu10k1 * emu,
+				 unsigned int iobase,
+				 unsigned int reg,
+				 unsigned int chn)
+{
+	unsigned long flags;
+	unsigned int regptr, val;
+
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + iobase + PTR);
+	val = inl(emu->port + iobase + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+static void snd_ptr_write(struct snd_emu10k1 *emu,
+			  unsigned int iobase,
+			  unsigned int reg,
+			  unsigned int chn,
+			  unsigned int data)
+{
+	unsigned int regptr;
+	unsigned long flags;
+
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + iobase + PTR);
+	outl(data, emu->port + iobase + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+
+static void snd_emu_proc_ptr_reg_read(struct snd_info_entry *entry,
+				      struct snd_info_buffer *buffer, int iobase, int offset, int length, int voices)
+{
+	struct snd_emu10k1 *emu = entry->private_data;
+	unsigned long value;
+	int i,j;
+	if (offset+length > 0xa0) {
+		snd_iprintf(buffer, "Input values out of range\n");
+		return;
+	}
+	snd_iprintf(buffer, "Registers 0x%x\n", iobase);
+	for(i = offset; i < offset+length; i++) {
+		snd_iprintf(buffer, "%02X: ",i);
+		for (j = 0; j < voices; j++) {
+			if(iobase == 0)
+                		value = snd_ptr_read(emu, 0, i, j);
+			else
+                		value = snd_ptr_read(emu, 0x20, i, j);
+			snd_iprintf(buffer, "%08lX ", value);
+		}
+		snd_iprintf(buffer, "\n");
+	}
+}
+
+static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
+				       struct snd_info_buffer *buffer, int iobase)
+{
+	struct snd_emu10k1 *emu = entry->private_data;
+	char line[64];
+	unsigned int reg, channel_id , val;
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		if (sscanf(line, "%x %x %x", &reg, &channel_id, &val) != 3)
+			continue;
+		if (reg < 0xa0 && val <= 0xffffffff && channel_id <= 3)
+			snd_ptr_write(emu, iobase, reg, channel_id, val);
+	}
+}
+
+static void snd_emu_proc_ptr_reg_write00(struct snd_info_entry *entry,
+					 struct snd_info_buffer *buffer)
+{
+	snd_emu_proc_ptr_reg_write(entry, buffer, 0);
+}
+
+static void snd_emu_proc_ptr_reg_write20(struct snd_info_entry *entry,
+					 struct snd_info_buffer *buffer)
+{
+	snd_emu_proc_ptr_reg_write(entry, buffer, 0x20);
+}
+	
+
+static void snd_emu_proc_ptr_reg_read00a(struct snd_info_entry *entry,
+					 struct snd_info_buffer *buffer)
+{
+	snd_emu_proc_ptr_reg_read(entry, buffer, 0, 0, 0x40, 64);
+}
+
+static void snd_emu_proc_ptr_reg_read00b(struct snd_info_entry *entry,
+					 struct snd_info_buffer *buffer)
+{
+	snd_emu_proc_ptr_reg_read(entry, buffer, 0, 0x40, 0x40, 64);
+}
+
+static void snd_emu_proc_ptr_reg_read20a(struct snd_info_entry *entry,
+					 struct snd_info_buffer *buffer)
+{
+	snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0, 0x40, 4);
+}
+
+static void snd_emu_proc_ptr_reg_read20b(struct snd_info_entry *entry,
+					 struct snd_info_buffer *buffer)
+{
+	snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0x40, 0x40, 4);
+}
+
+static void snd_emu_proc_ptr_reg_read20c(struct snd_info_entry *entry,
+					 struct snd_info_buffer * buffer)
+{
+	snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0x80, 0x20, 4);
+}
+#endif
+
+static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
+	.read = snd_emu10k1_fx8010_read,
+};
+
+int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
+{
+	struct snd_info_entry *entry;
+#ifdef CONFIG_SND_DEBUG
+	if (emu->card_capabilities->emu_model) {
+		if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry)) 
+			snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
+	}
+	if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
+		snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read);
+		entry->c.text.write = snd_emu_proc_io_reg_write;
+		entry->mode |= S_IWUSR;
+	}
+	if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
+		snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a);
+		entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+		entry->mode |= S_IWUSR;
+	}
+	if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
+		snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b);
+		entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+		entry->mode |= S_IWUSR;
+	}
+	if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
+		snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a);
+		entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+		entry->mode |= S_IWUSR;
+	}
+	if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
+		snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b);
+		entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+		entry->mode |= S_IWUSR;
+	}
+	if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) {
+		snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c);
+		entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+		entry->mode |= S_IWUSR;
+	}
+#endif
+	
+	if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
+		snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read);
+
+	if (emu->card_capabilities->emu10k2_chip) {
+		if (! snd_card_proc_new(emu->card, "spdif-in", &entry))
+			snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_spdif_read);
+	}
+	if (emu->card_capabilities->ca0151_chip) {
+		if (! snd_card_proc_new(emu->card, "capture-rates", &entry))
+			snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_rates_read);
+	}
+
+	if (! snd_card_proc_new(emu->card, "voices", &entry))
+		snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_voices_read);
+
+	if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+		entry->size = emu->audigy ? A_TOTAL_SIZE_GPR : TOTAL_SIZE_GPR;
+		entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+	}
+	if (! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+		entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_DATA : TOTAL_SIZE_TANKMEM_DATA ;
+		entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+	}
+	if (! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+		entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_ADDR : TOTAL_SIZE_TANKMEM_ADDR ;
+		entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+	}
+	if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) {
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+		entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE;
+		entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
+	}
+	if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = emu;
+		entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
+		entry->c.text.read = snd_emu10k1_proc_acode_read;
+	}
+	return 0;
+}
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/io.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/io.c
new file mode 100644
index 0000000..706b4f0
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/io.c
@@ -0,0 +1,580 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for control of EMU10K1 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include "p17v.h"
+
+unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
+{
+	unsigned long flags;
+	unsigned int regptr, val;
+	unsigned int mask;
+
+	mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
+	regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+
+	if (reg & 0xff000000) {
+		unsigned char size, offset;
+		
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		
+		return (val & mask) >> offset;
+	} else {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+		return val;
+	}
+}
+
+EXPORT_SYMBOL(snd_emu10k1_ptr_read);
+
+void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
+{
+	unsigned int regptr;
+	unsigned long flags;
+	unsigned int mask;
+
+	if (snd_BUG_ON(!emu))
+		return;
+	mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
+	regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
+
+	if (reg & 0xff000000) {
+		unsigned char size, offset;
+
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		data = (data << offset) & mask;
+
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		data |= inl(emu->port + DATA) & ~mask;
+		outl(data, emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);		
+	} else {
+		spin_lock_irqsave(&emu->emu_lock, flags);
+		outl(regptr, emu->port + PTR);
+		outl(data, emu->port + DATA);
+		spin_unlock_irqrestore(&emu->emu_lock, flags);
+	}
+}
+
+EXPORT_SYMBOL(snd_emu10k1_ptr_write);
+
+unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, 
+					  unsigned int reg, 
+					  unsigned int chn)
+{
+	unsigned long flags;
+	unsigned int regptr, val;
+  
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + 0x20 + PTR);
+	val = inl(emu->port + 0x20 + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, 
+				   unsigned int reg, 
+				   unsigned int chn, 
+				   unsigned int data)
+{
+	unsigned int regptr;
+	unsigned long flags;
+
+	regptr = (reg << 16) | chn;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(regptr, emu->port + 0x20 + PTR);
+	outl(data, emu->port + 0x20 + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
+				   unsigned int data)
+{
+	unsigned int reset, set;
+	unsigned int reg, tmp;
+	int n, result;
+	int err = 0;
+
+	/* This function is not re-entrant, so protect against it. */
+	spin_lock(&emu->spi_lock);
+	if (emu->card_capabilities->ca0108_chip)
+		reg = 0x3c; /* PTR20, reg 0x3c */
+	else {
+		/* For other chip types the SPI register
+		 * is currently unknown. */
+		err = 1;
+		goto spi_write_exit;
+	}
+	if (data > 0xffff) {
+		/* Only 16bit values allowed */
+		err = 1;
+		goto spi_write_exit;
+	}
+
+	tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
+	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
+	set = reset | 0x10000; /* Set xxx1xxxx */
+	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
+	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
+	snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
+	result = 1;
+	/* Wait for status bit to return to 0 */
+	for (n = 0; n < 100; n++) {
+		udelay(10);
+		tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
+		if (!(tmp & 0x10000)) {
+			result = 0;
+			break;
+		}
+	}
+	if (result) {
+		/* Timed out */
+		err = 1;
+		goto spi_write_exit;
+	}
+	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
+	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
+	err = 0;
+spi_write_exit:
+	spin_unlock(&emu->spi_lock);
+	return err;
+}
+
+/* The ADC does not support i2c read, so only write is implemented */
+int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
+				u32 reg,
+				u32 value)
+{
+	u32 tmp;
+	int timeout = 0;
+	int status;
+	int retry;
+	int err = 0;
+
+	if ((reg > 0x7f) || (value > 0x1ff)) {
+		dev_err(emu->card->dev, "i2c_write: invalid values.\n");
+		return -EINVAL;
+	}
+
+	/* This function is not re-entrant, so protect against it. */
+	spin_lock(&emu->i2c_lock);
+
+	tmp = reg << 25 | value << 16;
+
+	/* This controls the I2C connected to the WM8775 ADC Codec */
+	snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
+	tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
+
+	for (retry = 0; retry < 10; retry++) {
+		/* Send the data to i2c */
+		tmp = 0;
+		tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
+		snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
+
+		/* Wait till the transaction ends */
+		while (1) {
+			mdelay(1);
+			status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
+			timeout++;
+			if ((status & I2C_A_ADC_START) == 0)
+				break;
+
+			if (timeout > 1000) {
+				dev_warn(emu->card->dev,
+					   "emu10k1:I2C:timeout status=0x%x\n",
+					   status);
+				break;
+			}
+		}
+		//Read back and see if the transaction is successful
+		if ((status & I2C_A_ADC_ABORT) == 0)
+			break;
+	}
+
+	if (retry == 10) {
+		dev_err(emu->card->dev, "Writing to ADC failed!\n");
+		dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
+			status, reg, value);
+		/* dump_stack(); */
+		err = -EINVAL;
+	}
+    
+	spin_unlock(&emu->i2c_lock);
+	return err;
+}
+
+int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
+{
+	unsigned long flags;
+
+	if (reg > 0x3f)
+		return 1;
+	reg += 0x40; /* 0x40 upwards are registers. */
+	if (value > 0x3f) /* 0 to 0x3f are values */
+		return 1;
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(reg, emu->port + A_IOCFG);
+	udelay(10);
+	outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+	udelay(10);
+	outl(value, emu->port + A_IOCFG);
+	udelay(10);
+	outl(value | 0x80 , emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+	return 0;
+}
+
+int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
+{
+	unsigned long flags;
+	if (reg > 0x3f)
+		return 1;
+	reg += 0x40; /* 0x40 upwards are registers. */
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outl(reg, emu->port + A_IOCFG);
+	udelay(10);
+	outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
+	udelay(10);
+	*value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+
+	return 0;
+}
+
+/* Each Destination has one and only one Source,
+ * but one Source can feed any number of Destinations simultaneously.
+ */
+int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src)
+{
+	snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
+	snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
+	snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
+	snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
+
+	return 0;
+}
+
+void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) | intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE) & ~intrenb;
+	outl(enable, emu->port + INTE);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(CLIEH << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val |= 1 << (voicenum - 32);
+	} else {
+		outl(CLIEL << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val |= 1 << voicenum;
+	}
+	outl(val, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(CLIEH << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val &= ~(1 << (voicenum - 32));
+	} else {
+		outl(CLIEL << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val &= ~(1 << voicenum);
+	}
+	outl(val, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(CLIPH << 16, emu->port + PTR);
+		voicenum = 1 << (voicenum - 32);
+	} else {
+		outl(CLIPL << 16, emu->port + PTR);
+		voicenum = 1 << voicenum;
+	}
+	outl(voicenum, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(HLIEH << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val |= 1 << (voicenum - 32);
+	} else {
+		outl(HLIEL << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val |= 1 << voicenum;
+	}
+	outl(val, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(HLIEH << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val &= ~(1 << (voicenum - 32));
+	} else {
+		outl(HLIEL << 16, emu->port + PTR);
+		val = inl(emu->port + DATA);
+		val &= ~(1 << voicenum);
+	}
+	outl(val, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(HLIPH << 16, emu->port + PTR);
+		voicenum = 1 << (voicenum - 32);
+	} else {
+		outl(HLIPL << 16, emu->port + PTR);
+		voicenum = 1 << voicenum;
+	}
+	outl(voicenum, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int sol;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(SOLEH << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol |= 1 << (voicenum - 32);
+	} else {
+		outl(SOLEL << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol |= 1 << voicenum;
+	}
+	outl(sol, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
+{
+	unsigned long flags;
+	unsigned int sol;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	/* voice interrupt */
+	if (voicenum >= 32) {
+		outl(SOLEH << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol &= ~(1 << (voicenum - 32));
+	} else {
+		outl(SOLEL << 16, emu->port + PTR);
+		sol = inl(emu->port + DATA);
+		sol &= ~(1 << voicenum);
+	}
+	outl(sol, emu->port + DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
+{
+	volatile unsigned count;
+	unsigned int newtime = 0, curtime;
+
+	curtime = inl(emu->port + WC) >> 6;
+	while (wait-- > 0) {
+		count = 0;
+		while (count++ < 16384) {
+			newtime = inl(emu->port + WC) >> 6;
+			if (newtime != curtime)
+				break;
+		}
+		if (count > 16384)
+			break;
+		curtime = newtime;
+	}
+}
+
+unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+	struct snd_emu10k1 *emu = ac97->private_data;
+	unsigned long flags;
+	unsigned short val;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	val = inw(emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+	return val;
+}
+
+void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
+{
+	struct snd_emu10k1 *emu = ac97->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	outb(reg, emu->port + AC97ADDRESS);
+	outw(data, emu->port + AC97DATA);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+/*
+ *  convert rate to pitch
+ */
+
+unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
+{
+	static u32 logMagTable[128] = {
+		0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
+		0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
+		0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
+		0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
+		0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
+		0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
+		0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
+		0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
+		0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
+		0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
+		0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
+		0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
+		0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
+		0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
+		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
+		0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
+	};
+	static char logSlopeTable[128] = {
+		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
+		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
+		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
+		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
+		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
+		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
+		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
+		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
+		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
+		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
+		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
+		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
+		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
+		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
+		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
+		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
+	};
+	int i;
+
+	if (rate == 0)
+		return 0;	/* Bail out if no leading "1" */
+	rate *= 11185;		/* Scale 48000 to 0x20002380 */
+	for (i = 31; i > 0; i--) {
+		if (rate & 0x80000000) {	/* Detect leading "1" */
+			return (((unsigned int) (i - 15) << 20) +
+			       logMagTable[0x7f & (rate >> 24)] +
+					(0x7f & (rate >> 17)) *
+					logSlopeTable[0x7f & (rate >> 24)]);
+		}
+		rate <<= 1;
+	}
+
+	return 0;		/* Should never reach this point */
+}
+
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/irq.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/irq.c
new file mode 100644
index 0000000..3c5c5e3
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/irq.c
@@ -0,0 +1,213 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *                   Creative Labs, Inc.
+ *  Routines for IRQ control of EMU10K1 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
+{
+	struct snd_emu10k1 *emu = dev_id;
+	unsigned int status, status2, orig_status, orig_status2;
+	int handled = 0;
+	int timeout = 0;
+
+	while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
+		timeout++;
+		orig_status = status;
+		handled = 1;
+		if ((status & 0xffffffff) == 0xffffffff) {
+			dev_info(emu->card->dev,
+				 "Suspected sound card removal\n");
+			break;
+		}
+		if (status & IPR_PCIERROR) {
+			dev_err(emu->card->dev, "interrupt: PCI error\n");
+			snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
+			status &= ~IPR_PCIERROR;
+		}
+		if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
+			if (emu->hwvol_interrupt)
+				emu->hwvol_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
+			status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
+		}
+		if (status & IPR_CHANNELLOOP) {
+			int voice;
+			int voice_max = status & IPR_CHANNELNUMBERMASK;
+			u32 val;
+			struct snd_emu10k1_voice *pvoice = emu->voices;
+
+			val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
+			for (voice = 0; voice <= voice_max; voice++) {
+				if (voice == 0x20)
+					val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
+				if (val & 1) {
+					if (pvoice->use && pvoice->interrupt != NULL) {
+						pvoice->interrupt(emu, pvoice);
+						snd_emu10k1_voice_intr_ack(emu, voice);
+					} else {
+						snd_emu10k1_voice_intr_disable(emu, voice);
+					}
+				}
+				val >>= 1;
+				pvoice++;
+			}
+			val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
+			for (voice = 0; voice <= voice_max; voice++) {
+				if (voice == 0x20)
+					val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
+				if (val & 1) {
+					if (pvoice->use && pvoice->interrupt != NULL) {
+						pvoice->interrupt(emu, pvoice);
+						snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
+					} else {
+						snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
+					}
+				}
+				val >>= 1;
+				pvoice++;
+			}
+			status &= ~IPR_CHANNELLOOP;
+		}
+		status &= ~IPR_CHANNELNUMBERMASK;
+		if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
+			if (emu->capture_interrupt)
+				emu->capture_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
+			status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
+		}
+		if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
+			if (emu->capture_mic_interrupt)
+				emu->capture_mic_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
+			status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
+		}
+		if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
+			if (emu->capture_efx_interrupt)
+				emu->capture_efx_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
+			status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
+		}
+		if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
+			if (emu->midi.interrupt)
+				emu->midi.interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
+			status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
+		}
+		if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
+			if (emu->midi2.interrupt)
+				emu->midi2.interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
+			status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
+		}
+		if (status & IPR_INTERVALTIMER) {
+			if (emu->timer)
+				snd_timer_interrupt(emu->timer, emu->timer->sticks);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
+			status &= ~IPR_INTERVALTIMER;
+		}
+		if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
+			if (emu->spdif_interrupt)
+				emu->spdif_interrupt(emu, status);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
+			status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
+		}
+		if (status & IPR_FXDSP) {
+			if (emu->dsp_interrupt)
+				emu->dsp_interrupt(emu);
+			else
+				snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
+			status &= ~IPR_FXDSP;
+		}
+		if (status & IPR_P16V) {
+			while ((status2 = inl(emu->port + IPR2)) != 0) {
+				u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
+				struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]);
+				struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice);
+
+				/* dev_dbg(emu->card->dev, "status2=0x%x\n", status2); */
+				orig_status2 = status2;
+				if(status2 & mask) {
+					if(pvoice->use) {
+						snd_pcm_period_elapsed(pvoice->epcm->substream);
+					} else { 
+						dev_err(emu->card->dev,
+							"p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n",
+							status2, mask, pvoice,
+							pvoice->use);
+					}
+				}
+				if(status2 & 0x110000) {
+					/* dev_info(emu->card->dev, "capture int found\n"); */
+					if(cvoice->use) {
+						/* dev_info(emu->card->dev, "capture period_elapsed\n"); */
+						snd_pcm_period_elapsed(cvoice->epcm->substream);
+					}
+				}
+				outl(orig_status2, emu->port + IPR2); /* ack all */
+			}
+			status &= ~IPR_P16V;
+		}
+
+		if (status) {
+			unsigned int bits;
+			dev_err(emu->card->dev,
+				"unhandled interrupt: 0x%08x\n", status);
+			//make sure any interrupts we don't handle are disabled:
+			bits = INTE_FXDSPENABLE |
+				INTE_PCIERRORENABLE |
+				INTE_VOLINCRENABLE |
+				INTE_VOLDECRENABLE |
+				INTE_MUTEENABLE |
+				INTE_MICBUFENABLE |
+				INTE_ADCBUFENABLE |
+				INTE_EFXBUFENABLE |
+				INTE_GPSPDIFENABLE |
+				INTE_CDSPDIFENABLE |
+				INTE_INTERVALTIMERENB |
+				INTE_MIDITXENABLE |
+				INTE_MIDIRXENABLE;
+			if (emu->audigy)
+				bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
+			snd_emu10k1_intr_disable(emu, bits);
+		}
+		outl(orig_status, emu->port + IPR); /* ack all */
+	}
+	if (timeout == 1000)
+		dev_info(emu->card->dev, "emu10k1 irq routine failure\n");
+
+	return IRQ_RETVAL(handled);
+}
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/memory.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/memory.c
new file mode 100644
index 0000000..8c778fa
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/memory.c
@@ -0,0 +1,581 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *  EMU10K1 memory page allocation (PTB area)
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/gfp.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <linux/export.h>
+
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+/* page arguments of these two macros are Emu page (4096 bytes), not like
+ * aligned pages in others
+ */
+#define __set_ptb_entry(emu,page,addr) \
+	(((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << (emu->address_mode)) | (page)))
+
+#define UNIT_PAGES		(PAGE_SIZE / EMUPAGESIZE)
+#define MAX_ALIGN_PAGES0		(MAXPAGES0 / UNIT_PAGES)
+#define MAX_ALIGN_PAGES1		(MAXPAGES1 / UNIT_PAGES)
+/* get aligned page from offset address */
+#define get_aligned_page(offset)	((offset) >> PAGE_SHIFT)
+/* get offset address from aligned page */
+#define aligned_page_offset(page)	((page) << PAGE_SHIFT)
+
+#if PAGE_SIZE == 4096
+/* page size == EMUPAGESIZE */
+/* fill PTB entrie(s) corresponding to page with addr */
+#define set_ptb_entry(emu,page,addr)	__set_ptb_entry(emu,page,addr)
+/* fill PTB entrie(s) corresponding to page with silence pointer */
+#define set_silent_ptb(emu,page)	__set_ptb_entry(emu,page,emu->silent_page.addr)
+#else
+/* fill PTB entries -- we need to fill UNIT_PAGES entries */
+static inline void set_ptb_entry(struct snd_emu10k1 *emu, int page, dma_addr_t addr)
+{
+	int i;
+	page *= UNIT_PAGES;
+	for (i = 0; i < UNIT_PAGES; i++, page++) {
+		__set_ptb_entry(emu, page, addr);
+		addr += EMUPAGESIZE;
+	}
+}
+static inline void set_silent_ptb(struct snd_emu10k1 *emu, int page)
+{
+	int i;
+	page *= UNIT_PAGES;
+	for (i = 0; i < UNIT_PAGES; i++, page++)
+		/* do not increment ptr */
+		__set_ptb_entry(emu, page, emu->silent_page.addr);
+}
+#endif /* PAGE_SIZE */
+
+
+/*
+ */
+static int synth_alloc_pages(struct snd_emu10k1 *hw, struct snd_emu10k1_memblk *blk);
+static int synth_free_pages(struct snd_emu10k1 *hw, struct snd_emu10k1_memblk *blk);
+
+#define get_emu10k1_memblk(l,member)	list_entry(l, struct snd_emu10k1_memblk, member)
+
+
+/* initialize emu10k1 part */
+static void emu10k1_memblk_init(struct snd_emu10k1_memblk *blk)
+{
+	blk->mapped_page = -1;
+	INIT_LIST_HEAD(&blk->mapped_link);
+	INIT_LIST_HEAD(&blk->mapped_order_link);
+	blk->map_locked = 0;
+
+	blk->first_page = get_aligned_page(blk->mem.offset);
+	blk->last_page = get_aligned_page(blk->mem.offset + blk->mem.size - 1);
+	blk->pages = blk->last_page - blk->first_page + 1;
+}
+
+/*
+ * search empty region on PTB with the given size
+ *
+ * if an empty region is found, return the page and store the next mapped block
+ * in nextp
+ * if not found, return a negative error code.
+ */
+static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct list_head **nextp)
+{
+	int page = 0, found_page = -ENOMEM;
+	int max_size = npages;
+	int size;
+	struct list_head *candidate = &emu->mapped_link_head;
+	struct list_head *pos;
+
+	list_for_each (pos, &emu->mapped_link_head) {
+		struct snd_emu10k1_memblk *blk = get_emu10k1_memblk(pos, mapped_link);
+		if (blk->mapped_page < 0)
+			continue;
+		size = blk->mapped_page - page;
+		if (size == npages) {
+			*nextp = pos;
+			return page;
+		}
+		else if (size > max_size) {
+			/* we look for the maximum empty hole */
+			max_size = size;
+			candidate = pos;
+			found_page = page;
+		}
+		page = blk->mapped_page + blk->pages;
+	}
+	size = (emu->address_mode ? MAX_ALIGN_PAGES1 : MAX_ALIGN_PAGES0) - page;
+	if (size >= max_size) {
+		*nextp = pos;
+		return page;
+	}
+	*nextp = candidate;
+	return found_page;
+}
+
+/*
+ * map a memory block onto emu10k1's PTB
+ *
+ * call with memblk_lock held
+ */
+static int map_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+	int page, pg;
+	struct list_head *next;
+
+	page = search_empty_map_area(emu, blk->pages, &next);
+	if (page < 0) /* not found */
+		return page;
+	/* insert this block in the proper position of mapped list */
+	list_add_tail(&blk->mapped_link, next);
+	/* append this as a newest block in order list */
+	list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
+	blk->mapped_page = page;
+	/* fill PTB */
+	for (pg = blk->first_page; pg <= blk->last_page; pg++) {
+		set_ptb_entry(emu, page, emu->page_addr_table[pg]);
+		page++;
+	}
+	return 0;
+}
+
+/*
+ * unmap the block
+ * return the size of resultant empty pages
+ *
+ * call with memblk_lock held
+ */
+static int unmap_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+	int start_page, end_page, mpage, pg;
+	struct list_head *p;
+	struct snd_emu10k1_memblk *q;
+
+	/* calculate the expected size of empty region */
+	if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) {
+		q = get_emu10k1_memblk(p, mapped_link);
+		start_page = q->mapped_page + q->pages;
+	} else
+		start_page = 0;
+	if ((p = blk->mapped_link.next) != &emu->mapped_link_head) {
+		q = get_emu10k1_memblk(p, mapped_link);
+		end_page = q->mapped_page;
+	} else
+		end_page = (emu->address_mode ? MAX_ALIGN_PAGES1 : MAX_ALIGN_PAGES0);
+
+	/* remove links */
+	list_del(&blk->mapped_link);
+	list_del(&blk->mapped_order_link);
+	/* clear PTB */
+	mpage = blk->mapped_page;
+	for (pg = blk->first_page; pg <= blk->last_page; pg++) {
+		set_silent_ptb(emu, mpage);
+		mpage++;
+	}
+	blk->mapped_page = -1;
+	return end_page - start_page; /* return the new empty size */
+}
+
+/*
+ * search empty pages with the given size, and create a memory block
+ *
+ * unlike synth_alloc the memory block is aligned to the page start
+ */
+static struct snd_emu10k1_memblk *
+search_empty(struct snd_emu10k1 *emu, int size)
+{
+	struct list_head *p;
+	struct snd_emu10k1_memblk *blk;
+	int page, psize;
+
+	psize = get_aligned_page(size + PAGE_SIZE -1);
+	page = 0;
+	list_for_each(p, &emu->memhdr->block) {
+		blk = get_emu10k1_memblk(p, mem.list);
+		if (page + psize <= blk->first_page)
+			goto __found_pages;
+		page = blk->last_page + 1;
+	}
+	if (page + psize > emu->max_cache_pages)
+		return NULL;
+
+__found_pages:
+	/* create a new memory block */
+	blk = (struct snd_emu10k1_memblk *)__snd_util_memblk_new(emu->memhdr, psize << PAGE_SHIFT, p->prev);
+	if (blk == NULL)
+		return NULL;
+	blk->mem.offset = aligned_page_offset(page); /* set aligned offset */
+	emu10k1_memblk_init(blk);
+	return blk;
+}
+
+
+/*
+ * check if the given pointer is valid for pages
+ */
+static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
+{
+	if (addr & ~emu->dma_mask) {
+		dev_err_ratelimited(emu->card->dev,
+			"max memory size is 0x%lx (addr = 0x%lx)!!\n",
+			emu->dma_mask, (unsigned long)addr);
+		return 0;
+	}
+	if (addr & (EMUPAGESIZE-1)) {
+		dev_err_ratelimited(emu->card->dev, "page is not aligned\n");
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * map the given memory block on PTB.
+ * if the block is already mapped, update the link order.
+ * if no empty pages are found, tries to release unused memory blocks
+ * and retry the mapping.
+ */
+int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+	int err;
+	int size;
+	struct list_head *p, *nextp;
+	struct snd_emu10k1_memblk *deleted;
+	unsigned long flags;
+
+	spin_lock_irqsave(&emu->memblk_lock, flags);
+	if (blk->mapped_page >= 0) {
+		/* update order link */
+		list_move_tail(&blk->mapped_order_link,
+			       &emu->mapped_order_link_head);
+		spin_unlock_irqrestore(&emu->memblk_lock, flags);
+		return 0;
+	}
+	if ((err = map_memblk(emu, blk)) < 0) {
+		/* no enough page - try to unmap some blocks */
+		/* starting from the oldest block */
+		p = emu->mapped_order_link_head.next;
+		for (; p != &emu->mapped_order_link_head; p = nextp) {
+			nextp = p->next;
+			deleted = get_emu10k1_memblk(p, mapped_order_link);
+			if (deleted->map_locked)
+				continue;
+			size = unmap_memblk(emu, deleted);
+			if (size >= blk->pages) {
+				/* ok the empty region is enough large */
+				err = map_memblk(emu, blk);
+				break;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&emu->memblk_lock, flags);
+	return err;
+}
+
+EXPORT_SYMBOL(snd_emu10k1_memblk_map);
+
+/*
+ * page allocation for DMA
+ */
+struct snd_util_memblk *
+snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_util_memhdr *hdr;
+	struct snd_emu10k1_memblk *blk;
+	int page, err, idx;
+
+	if (snd_BUG_ON(!emu))
+		return NULL;
+	if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
+		       runtime->dma_bytes >= (emu->address_mode ? MAXPAGES1 : MAXPAGES0) * EMUPAGESIZE))
+		return NULL;
+	hdr = emu->memhdr;
+	if (snd_BUG_ON(!hdr))
+		return NULL;
+
+	idx = runtime->period_size >= runtime->buffer_size ?
+					(emu->delay_pcm_irq * 2) : 0;
+	mutex_lock(&hdr->block_mutex);
+	blk = search_empty(emu, runtime->dma_bytes + idx);
+	if (blk == NULL) {
+		mutex_unlock(&hdr->block_mutex);
+		return NULL;
+	}
+	/* fill buffer addresses but pointers are not stored so that
+	 * snd_free_pci_page() is not called in in synth_free()
+	 */
+	idx = 0;
+	for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
+		unsigned long ofs = idx << PAGE_SHIFT;
+		dma_addr_t addr;
+		if (ofs >= runtime->dma_bytes)
+			addr = emu->silent_page.addr;
+		else
+			addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+		if (! is_valid_page(emu, addr)) {
+			dev_err_ratelimited(emu->card->dev,
+				"emu: failure page = %d\n", idx);
+			mutex_unlock(&hdr->block_mutex);
+			return NULL;
+		}
+		emu->page_addr_table[page] = addr;
+		emu->page_ptr_table[page] = NULL;
+	}
+
+	/* set PTB entries */
+	blk->map_locked = 1; /* do not unmap this block! */
+	err = snd_emu10k1_memblk_map(emu, blk);
+	if (err < 0) {
+		__snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
+		mutex_unlock(&hdr->block_mutex);
+		return NULL;
+	}
+	mutex_unlock(&hdr->block_mutex);
+	return (struct snd_util_memblk *)blk;
+}
+
+
+/*
+ * release DMA buffer from page table
+ */
+int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk)
+{
+	if (snd_BUG_ON(!emu || !blk))
+		return -EINVAL;
+	return snd_emu10k1_synth_free(emu, blk);
+}
+
+
+/*
+ * memory allocation using multiple pages (for synth)
+ * Unlike the DMA allocation above, non-contiguous pages are assined.
+ */
+
+/*
+ * allocate a synth sample area
+ */
+struct snd_util_memblk *
+snd_emu10k1_synth_alloc(struct snd_emu10k1 *hw, unsigned int size)
+{
+	struct snd_emu10k1_memblk *blk;
+	struct snd_util_memhdr *hdr = hw->memhdr; 
+
+	mutex_lock(&hdr->block_mutex);
+	blk = (struct snd_emu10k1_memblk *)__snd_util_mem_alloc(hdr, size);
+	if (blk == NULL) {
+		mutex_unlock(&hdr->block_mutex);
+		return NULL;
+	}
+	if (synth_alloc_pages(hw, blk)) {
+		__snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
+		mutex_unlock(&hdr->block_mutex);
+		return NULL;
+	}
+	snd_emu10k1_memblk_map(hw, blk);
+	mutex_unlock(&hdr->block_mutex);
+	return (struct snd_util_memblk *)blk;
+}
+
+EXPORT_SYMBOL(snd_emu10k1_synth_alloc);
+
+/*
+ * free a synth sample area
+ */
+int
+snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *memblk)
+{
+	struct snd_util_memhdr *hdr = emu->memhdr; 
+	struct snd_emu10k1_memblk *blk = (struct snd_emu10k1_memblk *)memblk;
+	unsigned long flags;
+
+	mutex_lock(&hdr->block_mutex);
+	spin_lock_irqsave(&emu->memblk_lock, flags);
+	if (blk->mapped_page >= 0)
+		unmap_memblk(emu, blk);
+	spin_unlock_irqrestore(&emu->memblk_lock, flags);
+	synth_free_pages(emu, blk);
+	 __snd_util_mem_free(hdr, memblk);
+	mutex_unlock(&hdr->block_mutex);
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_emu10k1_synth_free);
+
+/* check new allocation range */
+static void get_single_page_range(struct snd_util_memhdr *hdr,
+				  struct snd_emu10k1_memblk *blk,
+				  int *first_page_ret, int *last_page_ret)
+{
+	struct list_head *p;
+	struct snd_emu10k1_memblk *q;
+	int first_page, last_page;
+	first_page = blk->first_page;
+	if ((p = blk->mem.list.prev) != &hdr->block) {
+		q = get_emu10k1_memblk(p, mem.list);
+		if (q->last_page == first_page)
+			first_page++;  /* first page was already allocated */
+	}
+	last_page = blk->last_page;
+	if ((p = blk->mem.list.next) != &hdr->block) {
+		q = get_emu10k1_memblk(p, mem.list);
+		if (q->first_page == last_page)
+			last_page--; /* last page was already allocated */
+	}
+	*first_page_ret = first_page;
+	*last_page_ret = last_page;
+}
+
+/* release allocated pages */
+static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
+			       int last_page)
+{
+	int page;
+
+	for (page = first_page; page <= last_page; page++) {
+		free_page((unsigned long)emu->page_ptr_table[page]);
+		emu->page_addr_table[page] = 0;
+		emu->page_ptr_table[page] = NULL;
+	}
+}
+
+/*
+ * allocate kernel pages
+ */
+static int synth_alloc_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+	int page, first_page, last_page;
+
+	emu10k1_memblk_init(blk);
+	get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
+	/* allocate kernel pages */
+	for (page = first_page; page <= last_page; page++) {
+		/* first try to allocate from <4GB zone */
+		struct page *p = alloc_page(GFP_KERNEL | GFP_DMA32 |
+					    __GFP_NOWARN);
+		if (!p || (page_to_pfn(p) & ~(emu->dma_mask >> PAGE_SHIFT))) {
+			if (p)
+				__free_page(p);
+			/* try to allocate from <16MB zone */
+			p = alloc_page(GFP_ATOMIC | GFP_DMA |
+				       __GFP_NORETRY | /* no OOM-killer */
+				       __GFP_NOWARN);
+		}
+		if (!p) {
+			__synth_free_pages(emu, first_page, page - 1);
+			return -ENOMEM;
+		}
+		emu->page_addr_table[page] = page_to_phys(p);
+		emu->page_ptr_table[page] = page_address(p);
+	}
+	return 0;
+}
+
+/*
+ * free pages
+ */
+static int synth_free_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
+{
+	int first_page, last_page;
+
+	get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
+	__synth_free_pages(emu, first_page, last_page);
+	return 0;
+}
+
+/* calculate buffer pointer from offset address */
+static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
+{
+	char *ptr;
+	if (snd_BUG_ON(page < 0 || page >= emu->max_cache_pages))
+		return NULL;
+	ptr = emu->page_ptr_table[page];
+	if (! ptr) {
+		dev_err(emu->card->dev,
+			"access to NULL ptr: page = %d\n", page);
+		return NULL;
+	}
+	ptr += offset & (PAGE_SIZE - 1);
+	return (void*)ptr;
+}
+
+/*
+ * bzero(blk + offset, size)
+ */
+int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
+			    int offset, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+	void *ptr;
+	struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+
+	offset += blk->offset & (PAGE_SIZE - 1);
+	end_offset = offset + size;
+	page = get_aligned_page(offset);
+	do {
+		nextofs = aligned_page_offset(page + 1);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		ptr = offset_ptr(emu, page + p->first_page, offset);
+		if (ptr)
+			memset(ptr, 0, temp);
+		offset = nextofs;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_emu10k1_synth_bzero);
+
+/*
+ * copy_from_user(blk + offset, data, size)
+ */
+int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
+				     int offset, const char __user *data, int size)
+{
+	int page, nextofs, end_offset, temp, temp1;
+	void *ptr;
+	struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
+
+	offset += blk->offset & (PAGE_SIZE - 1);
+	end_offset = offset + size;
+	page = get_aligned_page(offset);
+	do {
+		nextofs = aligned_page_offset(page + 1);
+		temp = nextofs - offset;
+		temp1 = end_offset - offset;
+		if (temp1 < temp)
+			temp = temp1;
+		ptr = offset_ptr(emu, page + p->first_page, offset);
+		if (ptr && copy_from_user(ptr, data, temp))
+			return -EFAULT;
+		offset = nextofs;
+		data += temp;
+		page++;
+	} while (offset < end_offset);
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user);
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/p16v.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/p16v.c
new file mode 100644
index 0000000..a30da78
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/p16v.c
@@ -0,0 +1,909 @@
+/*
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver p16v chips
+ *  Version: 0.25
+ *
+ *  FEATURES currently supported:
+ *    Output fixed at S32_LE, 2 channel to hw:0,0
+ *    Rates: 44.1, 48, 96, 192.
+ *
+ *  Changelog:
+ *  0.8
+ *    Use separate card based buffer for periods table.
+ *  0.9
+ *    Use 2 channel output streams instead of 8 channel.
+ *       (8 channel output streams might be good for ASIO type output)
+ *    Corrected speaker output, so Front -> Front etc.
+ *  0.10
+ *    Fixed missed interrupts.
+ *  0.11
+ *    Add Sound card model number and names.
+ *    Add Analog volume controls.
+ *  0.12
+ *    Corrected playback interrupts. Now interrupt per period, instead of half period.
+ *  0.13
+ *    Use single trigger for multichannel.
+ *  0.14
+ *    Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
+ *  0.15
+ *    Force buffer_size / period_size == INTEGER.
+ *  0.16
+ *    Update p16v.c to work with changed alsa api.
+ *  0.17
+ *    Update p16v.c to work with changed alsa api. Removed boot_devs.
+ *  0.18
+ *    Merging with snd-emu10k1 driver.
+ *  0.19
+ *    One stereo channel at 24bit now works.
+ *  0.20
+ *    Added better register defines.
+ *  0.21
+ *    Integrated with snd-emu10k1 driver.
+ *  0.22
+ *    Removed #if 0 ... #endif
+ *  0.23
+ *    Implement different capture rates.
+ *  0.24
+ *    Implement different capture source channels.
+ *    e.g. When HD Capture source is set to SPDIF,
+ *    setting HD Capture channel to 0 captures from CDROM digital input.
+ *    setting HD Capture channel to 1 captures from SPDIF in.
+ *  0.25
+ *    Include capture buffer sizes.
+ *
+ *  BUGS:
+ *    Some stability problems when unloading the snd-p16v kernel module.
+ *    --
+ *
+ *  TODO:
+ *    SPDIF out.
+ *    Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
+ *    Currently capture fixed at 48000Hz.
+ *
+ *    --
+ *  GENERAL INFO:
+ *    Model: SB0240
+ *    P16V Chip: CA0151-DBS
+ *    Audigy 2 Chip: CA0102-IAT
+ *    AC97 Codec: STAC 9721
+ *    ADC: Philips 1361T (Stereo 24bit)
+ *    DAC: CS4382-K (8-channel, 24bit, 192Khz)
+ *
+ *  This code was initially based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/info.h>
+#include <sound/tlv.h>
+#include <sound/emu10k1.h>
+#include "p16v.h"
+
+#define SET_CHANNEL 0  /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
+#define PCM_FRONT_CHANNEL 0
+#define PCM_REAR_CHANNEL 1
+#define PCM_CENTER_LFE_CHANNEL 2
+#define PCM_SIDE_CHANNEL 3
+#define CONTROL_FRONT_CHANNEL 0
+#define CONTROL_REAR_CHANNEL 3
+#define CONTROL_CENTER_LFE_CHANNEL 1
+#define CONTROL_SIDE_CHANNEL 2
+
+/* Card IDs:
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2002 -> Audigy2 ZS 7.1 Model:SB0350
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1007 -> Audigy2 6.1    Model:SB0240
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1002 -> Audigy2 Platinum  Model:SB msb0240230009266
+ * Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2007 -> Audigy4 Pro Model:SB0380 M1SB0380472001901E
+ *
+ */
+
+ /* hardware definition */
+static const struct snd_pcm_hardware snd_p16v_playback_hw = {
+	.info =			SNDRV_PCM_INFO_MMAP | 
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_RESUME |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_SYNC_START,
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */
+	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, 
+	.rate_min =		44100,
+	.rate_max =		192000,
+	.channels_min =		8, 
+	.channels_max =		8,
+	.buffer_bytes_max =	((65536 - 64) * 8),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(65536 - 64),
+	.periods_min =		2,
+	.periods_max =		8,
+	.fifo_size =		0,
+};
+
+static const struct snd_pcm_hardware snd_p16v_capture_hw = {
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, 
+	.rate_min =		44100,
+	.rate_max =		192000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(65536 - 64),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(65536 - 128) >> 1,  /* size has to be N*64 bytes */
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+
+	kfree(epcm);
+}
+
+/* open_playback callback */
+static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+        struct snd_emu10k1_voice *channel = &(emu->p16v_voices[channel_id]);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	/* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
+
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->substream = substream;
+	/*
+	dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
+		   substream->pcm->device, channel_id);
+	*/
+	runtime->private_data = epcm;
+	runtime->private_free = snd_p16v_pcm_free_substream;
+  
+	runtime->hw = snd_p16v_playback_hw;
+
+        channel->emu = emu;
+        channel->number = channel_id;
+
+        channel->use=1;
+#if 0 /* debug */
+	dev_dbg(emu->card->dev,
+		   "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
+		   channel_id, channel, channel->use);
+	dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
+	       channel_id, chip, channel);
+#endif /* debug */
+	/* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
+	channel->epcm = epcm;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+                return err;
+
+	runtime->sync.id32[0] = substream->pcm->card->number;
+	runtime->sync.id32[1] = 'P';
+	runtime->sync.id32[2] = 16;
+	runtime->sync.id32[3] = 'V';
+
+	return 0;
+}
+/* open_capture callback */
+static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream, int channel_id)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_emu10k1_voice *channel = &(emu->p16v_capture_voice);
+	struct snd_emu10k1_pcm *epcm;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
+	/* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
+
+	if (epcm == NULL)
+		return -ENOMEM;
+	epcm->emu = emu;
+	epcm->substream = substream;
+	/*
+	dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
+		   substream->pcm->device, channel_id);
+	*/
+	runtime->private_data = epcm;
+	runtime->private_free = snd_p16v_pcm_free_substream;
+  
+	runtime->hw = snd_p16v_capture_hw;
+
+	channel->emu = emu;
+	channel->number = channel_id;
+
+	channel->use=1;
+#if 0 /* debug */
+	dev_dbg(emu->card->dev,
+		   "p16v: open channel_id=%d, channel=%p, use=0x%x\n",
+		   channel_id, channel, channel->use);
+	dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
+	       channel_id, chip, channel);
+#endif /* debug */
+	/* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
+	channel->epcm = epcm;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+
+	return 0;
+}
+
+
+/* close callback */
+static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	//struct snd_pcm_runtime *runtime = substream->runtime;
+	//struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use = 0;
+	/* FIXME: maybe zero others */
+	return 0;
+}
+
+/* close callback */
+static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	//struct snd_pcm_runtime *runtime = substream->runtime;
+	//struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	emu->p16v_capture_voice.use = 0;
+	/* FIXME: maybe zero others */
+	return 0;
+}
+
+static int snd_p16v_pcm_open_playback_front(struct snd_pcm_substream *substream)
+{
+	return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
+}
+
+static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream)
+{
+	// Only using channel 0 for now, but the card has 2 channels.
+	return snd_p16v_pcm_open_capture_channel(substream, 0);
+}
+
+/* hw_params callback */
+static int snd_p16v_pcm_hw_params_playback(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+/* hw_params callback */
+static int snd_p16v_pcm_hw_params_capture(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+
+/* hw_free callback */
+static int snd_p16v_pcm_hw_free_playback(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/* hw_free callback */
+static int snd_p16v_pcm_hw_free_capture(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+
+/* prepare playback callback */
+static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int channel = substream->pcm->device - emu->p16v_device_offset;
+	u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
+	u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
+	int i;
+	u32 tmp;
+	
+#if 0 /* debug */
+	dev_dbg(emu->card->dev,
+		"prepare:channel_number=%d, rate=%d, "
+		   "format=0x%x, channels=%d, buffer_size=%ld, "
+		   "period_size=%ld, periods=%u, frames_to_bytes=%d\n",
+		   channel, runtime->rate, runtime->format, runtime->channels,
+		   runtime->buffer_size, runtime->period_size,
+		   runtime->periods, frames_to_bytes(runtime, 1));
+	dev_dbg(emu->card->dev,
+		"dma_addr=%x, dma_area=%p, table_base=%p\n",
+		   runtime->dma_addr, runtime->dma_area, table_base);
+	dev_dbg(emu->card->dev,
+		"dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
+		   emu->p16v_buffer.addr, emu->p16v_buffer.area,
+		   emu->p16v_buffer.bytes);
+#endif /* debug */
+	tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+        switch (runtime->rate) {
+	case 44100:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080);
+	  break;
+	case 96000:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040);
+	  break;
+	case 192000:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020);
+	  break;
+	case 48000:
+	default:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000);
+	  break;
+	}
+	/* FIXME: Check emu->buffer.size before actually writing to it. */
+	for(i = 0; i < runtime->periods; i++) {
+		table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
+		table_base[(i*2)+1]=period_size_bytes<<16;
+	}
+ 
+	snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer.addr+(8*16*channel));
+	snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
+	snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
+	snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
+	//snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+	snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
+	snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
+	snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
+	snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
+
+	return 0;
+}
+
+/* prepare capture callback */
+static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int channel = substream->pcm->device - emu->p16v_device_offset;
+	u32 tmp;
+
+	/*
+	dev_dbg(emu->card->dev, "prepare capture:channel_number=%d, rate=%d, "
+	       "format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, "
+	       "frames_to_bytes=%d\n",
+	       channel, runtime->rate, runtime->format, runtime->channels,
+	       runtime->buffer_size, runtime->period_size,
+	       frames_to_bytes(runtime, 1));
+	*/
+	tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+        switch (runtime->rate) {
+	case 44100:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800);
+	  break;
+	case 96000:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400);
+	  break;
+	case 192000:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200);
+	  break;
+	case 48000:
+	default:
+	  snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000);
+	  break;
+	}
+	/* FIXME: Check emu->buffer.size before actually writing to it. */
+	snd_emu10k1_ptr20_write(emu, 0x13, channel, 0);
+	snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
+	snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size) << 16); // buffer size in bytes
+	snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0);
+	//snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */
+	//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
+
+	return 0;
+}
+
+static void snd_p16v_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int enable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	enable = inl(emu->port + INTE2) | intrenb;
+	outl(enable, emu->port + INTE2);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
+{
+	unsigned long flags;
+	unsigned int disable;
+
+	spin_lock_irqsave(&emu->emu_lock, flags);
+	disable = inl(emu->port + INTE2) & (~intrenb);
+	outl(disable, emu->port + INTE2);
+	spin_unlock_irqrestore(&emu->emu_lock, flags);
+}
+
+/* trigger_playback callback */
+static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
+				    int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime;
+	struct snd_emu10k1_pcm *epcm;
+	int channel;
+	int result = 0;
+        struct snd_pcm_substream *s;
+	u32 basic = 0;
+	u32 inte = 0;
+	int running = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		running=1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	default:
+		running = 0;
+		break;
+	}
+        snd_pcm_group_for_each_entry(s, substream) {
+		if (snd_pcm_substream_chip(s) != emu ||
+		    s->stream != SNDRV_PCM_STREAM_PLAYBACK)
+			continue;
+		runtime = s->runtime;
+		epcm = runtime->private_data;
+		channel = substream->pcm->device-emu->p16v_device_offset;
+		/* dev_dbg(emu->card->dev, "p16v channel=%d\n", channel); */
+		epcm->running = running;
+		basic |= (0x1<<channel);
+		inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
+                snd_pcm_trigger_done(s, substream);
+        }
+	/* dev_dbg(emu->card->dev, "basic=0x%x, inte=0x%x\n", basic, inte); */
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_p16v_intr_enable(emu, inte);
+		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)| (basic));
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
+		snd_p16v_intr_disable(emu, inte);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	return result;
+}
+
+/* trigger_capture callback */
+static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
+                                   int cmd)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	int channel = 0;
+	int result = 0;
+	u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		snd_p16v_intr_enable(emu, inte);
+		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
+		epcm->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
+		snd_p16v_intr_disable(emu, inte);
+		//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
+		epcm->running = 0;
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+	return result;
+}
+
+/* pointer_playback callback */
+static snd_pcm_uframes_t
+snd_p16v_pcm_pointer_playback(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
+	int channel = substream->pcm->device - emu->p16v_device_offset;
+	if (!epcm->running)
+		return 0;
+
+	ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
+	ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
+	ptr4 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
+	if (ptr3 != ptr4) ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
+	ptr2 = bytes_to_frames(runtime, ptr1);
+	ptr2+= (ptr4 >> 3) * runtime->period_size;
+	ptr=ptr2;
+        if (ptr >= runtime->buffer_size)
+		ptr -= runtime->buffer_size;
+
+	return ptr;
+}
+
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
+{
+	struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_emu10k1_pcm *epcm = runtime->private_data;
+	snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
+	int channel = 0;
+
+	if (!epcm->running)
+		return 0;
+
+	ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel);
+	ptr2 = bytes_to_frames(runtime, ptr1);
+	ptr=ptr2;
+	if (ptr >= runtime->buffer_size) {
+		ptr -= runtime->buffer_size;
+		dev_warn(emu->card->dev, "buffer capture limited!\n");
+	}
+	/*
+	dev_dbg(emu->card->dev, "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
+	       "buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
+	       ptr1, ptr2, ptr, (int)runtime->buffer_size,
+	       (int)runtime->period_size, (int)runtime->frame_bits,
+	       (int)runtime->rate);
+	*/
+	return ptr;
+}
+
+/* operators */
+static const struct snd_pcm_ops snd_p16v_playback_front_ops = {
+	.open =        snd_p16v_pcm_open_playback_front,
+	.close =       snd_p16v_pcm_close_playback,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_p16v_pcm_hw_params_playback,
+	.hw_free =     snd_p16v_pcm_hw_free_playback,
+	.prepare =     snd_p16v_pcm_prepare_playback,
+	.trigger =     snd_p16v_pcm_trigger_playback,
+	.pointer =     snd_p16v_pcm_pointer_playback,
+};
+
+static const struct snd_pcm_ops snd_p16v_capture_ops = {
+	.open =        snd_p16v_pcm_open_capture,
+	.close =       snd_p16v_pcm_close_capture,
+	.ioctl =       snd_pcm_lib_ioctl,
+	.hw_params =   snd_p16v_pcm_hw_params_capture,
+	.hw_free =     snd_p16v_pcm_hw_free_capture,
+	.prepare =     snd_p16v_pcm_prepare_capture,
+	.trigger =     snd_p16v_pcm_trigger_capture,
+	.pointer =     snd_p16v_pcm_pointer_capture,
+};
+
+
+int snd_p16v_free(struct snd_emu10k1 *chip)
+{
+	// release the data
+	if (chip->p16v_buffer.area) {
+		snd_dma_free_pages(&chip->p16v_buffer);
+		/*
+		dev_dbg(chip->card->dev, "period lables free: %p\n",
+			   &chip->p16v_buffer);
+		*/
+	}
+	return 0;
+}
+
+int snd_p16v_pcm(struct snd_emu10k1 *emu, int device)
+{
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+	int err;
+        int capture=1;
+  
+	/* dev_dbg(emu->card->dev, "snd_p16v_pcm called. device=%d\n", device); */
+	emu->p16v_device_offset = device;
+
+	if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
+		return err;
+  
+	pcm->private_data = emu;
+	// Single playback 8 channel device.
+	// Single capture 2 channel device.
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_p16v_playback_front_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_p16v_capture_ops);
+
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strcpy(pcm->name, "p16v");
+	emu->pcm_p16v = pcm;
+
+	for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 
+	    substream; 
+	    substream = substream->next) {
+		if ((err = snd_pcm_lib_preallocate_pages(substream, 
+							 SNDRV_DMA_TYPE_DEV, 
+							 snd_dma_pci_data(emu->pci), 
+							 ((65536 - 64) * 8), ((65536 - 64) * 8))) < 0) 
+			return err;
+		/*
+		dev_dbg(emu->card->dev,
+			   "preallocate playback substream: err=%d\n", err);
+		*/
+	}
+
+	for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 
+	      substream; 
+	      substream = substream->next) {
+ 		if ((err = snd_pcm_lib_preallocate_pages(substream, 
+	                                           SNDRV_DMA_TYPE_DEV, 
+	                                           snd_dma_pci_data(emu->pci), 
+	                                           65536 - 64, 65536 - 64)) < 0)
+			return err;
+		/*
+		dev_dbg(emu->card->dev,
+			   "preallocate capture substream: err=%d\n", err);
+		*/
+	}
+  
+	return 0;
+}
+
+static int snd_p16v_volume_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+        uinfo->count = 2;
+        uinfo->value.integer.min = 0;
+        uinfo->value.integer.max = 255;
+        return 0;
+}
+
+static int snd_p16v_volume_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int high_low = (kcontrol->private_value >> 8) & 0xff;
+	int reg = kcontrol->private_value & 0xff;
+	u32 value;
+
+	value = snd_emu10k1_ptr20_read(emu, reg, high_low);
+	if (high_low) {
+		ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
+		ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
+	} else {
+		ucontrol->value.integer.value[0] = 0xff - ((value >> 8) & 0xff); /* Left */
+		ucontrol->value.integer.value[1] = 0xff - ((value >> 0) & 0xff); /* Right */
+	}
+	return 0;
+}
+
+static int snd_p16v_volume_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+        struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	int high_low = (kcontrol->private_value >> 8) & 0xff;
+	int reg = kcontrol->private_value & 0xff;
+        u32 value, oval;
+
+	oval = value = snd_emu10k1_ptr20_read(emu, reg, 0);
+	if (high_low == 1) {
+		value &= 0xffff;
+		value |= ((0xff - ucontrol->value.integer.value[0]) << 24) |
+			((0xff - ucontrol->value.integer.value[1]) << 16);
+	} else {
+		value &= 0xffff0000;
+		value |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
+			((0xff - ucontrol->value.integer.value[1]) );
+	}
+	if (value != oval) {
+		snd_emu10k1_ptr20_write(emu, reg, 0, value);
+		return 1;
+	}
+	return 0;
+}
+
+static int snd_p16v_capture_source_info(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_info *uinfo)
+{
+	static const char * const texts[8] = {
+		"SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S",
+		"CDIF", "FX", "AC97"
+	};
+
+	return snd_ctl_enum_info(uinfo, 1, 8, texts);
+}
+
+static int snd_p16v_capture_source_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->p16v_capture_source;
+	return 0;
+}
+
+static int snd_p16v_capture_source_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+	u32 mask;
+	u32 source;
+
+	val = ucontrol->value.enumerated.item[0] ;
+	if (val > 7)
+		return -EINVAL;
+	change = (emu->p16v_capture_source != val);
+	if (change) {
+		emu->p16v_capture_source = val;
+		source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
+		mask = snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & 0xffff;
+		snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, source | mask);
+	}
+        return change;
+}
+
+static int snd_p16v_capture_channel_info(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_info *uinfo)
+{
+	static const char * const texts[4] = { "0", "1", "2", "3", };
+
+	return snd_ctl_enum_info(uinfo, 1, 4, texts);
+}
+
+static int snd_p16v_capture_channel_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = emu->p16v_capture_channel;
+	return 0;
+}
+
+static int snd_p16v_capture_channel_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	int change = 0;
+	u32 tmp;
+
+	val = ucontrol->value.enumerated.item[0] ;
+	if (val > 3)
+		return -EINVAL;
+	change = (emu->p16v_capture_channel != val);
+	if (change) {
+		emu->p16v_capture_channel = val;
+		tmp = snd_emu10k1_ptr20_read(emu, CAPTURE_P16V_SOURCE, 0) & 0xfffc;
+		snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, tmp | val);
+	}
+        return change;
+}
+static const DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
+
+#define P16V_VOL(xname,xreg,xhl) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+        .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |             \
+                  SNDRV_CTL_ELEM_ACCESS_TLV_READ,               \
+	.info = snd_p16v_volume_info, \
+	.get = snd_p16v_volume_get, \
+	.put = snd_p16v_volume_put, \
+	.tlv = { .p = snd_p16v_db_scale1 },	\
+	.private_value = ((xreg) | ((xhl) << 8)) \
+}
+
+static struct snd_kcontrol_new p16v_mixer_controls[] = {
+	P16V_VOL("HD Analog Front Playback Volume", PLAYBACK_VOLUME_MIXER9, 0),
+	P16V_VOL("HD Analog Rear Playback Volume", PLAYBACK_VOLUME_MIXER10, 1),
+	P16V_VOL("HD Analog Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER9, 1),
+	P16V_VOL("HD Analog Side Playback Volume", PLAYBACK_VOLUME_MIXER10, 0),
+	P16V_VOL("HD SPDIF Front Playback Volume", PLAYBACK_VOLUME_MIXER7, 0),
+	P16V_VOL("HD SPDIF Rear Playback Volume", PLAYBACK_VOLUME_MIXER8, 1),
+	P16V_VOL("HD SPDIF Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER7, 1),
+	P16V_VOL("HD SPDIF Side Playback Volume", PLAYBACK_VOLUME_MIXER8, 0),
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =		"HD source Capture",
+		.info =		snd_p16v_capture_source_info,
+		.get =		snd_p16v_capture_source_get,
+		.put =		snd_p16v_capture_source_put
+	},
+	{
+		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name =		"HD channel Capture",
+		.info =		snd_p16v_capture_channel_info,
+		.get =		snd_p16v_capture_channel_get,
+		.put =		snd_p16v_capture_channel_put
+	},
+};
+
+
+int snd_p16v_mixer(struct snd_emu10k1 *emu)
+{
+	int i, err;
+        struct snd_card *card = emu->card;
+
+	for (i = 0; i < ARRAY_SIZE(p16v_mixer_controls); i++) {
+		if ((err = snd_ctl_add(card, snd_ctl_new1(&p16v_mixer_controls[i],
+							  emu))) < 0)
+			return err;
+	}
+        return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+#define NUM_CHS	1	/* up to 4, but only first channel is used */
+
+int snd_p16v_alloc_pm_buffer(struct snd_emu10k1 *emu)
+{
+	emu->p16v_saved = vmalloc(NUM_CHS * 4 * 0x80);
+	if (! emu->p16v_saved)
+		return -ENOMEM;
+	return 0;
+}
+
+void snd_p16v_free_pm_buffer(struct snd_emu10k1 *emu)
+{
+	vfree(emu->p16v_saved);
+}
+
+void snd_p16v_suspend(struct snd_emu10k1 *emu)
+{
+	int i, ch;
+	unsigned int *val;
+
+	val = emu->p16v_saved;
+	for (ch = 0; ch < NUM_CHS; ch++)
+		for (i = 0; i < 0x80; i++, val++)
+			*val = snd_emu10k1_ptr20_read(emu, i, ch);
+}
+
+void snd_p16v_resume(struct snd_emu10k1 *emu)
+{
+	int i, ch;
+	unsigned int *val;
+
+	val = emu->p16v_saved;
+	for (ch = 0; ch < NUM_CHS; ch++)
+		for (i = 0; i < 0x80; i++, val++)
+			snd_emu10k1_ptr20_write(emu, i, ch, *val);
+}
+#endif
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/p16v.h b/src/kernel/linux/v4.14/sound/pci/emu10k1/p16v.h
new file mode 100644
index 0000000..4e0ee1a
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/p16v.h
@@ -0,0 +1,299 @@
+/*
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver p16v chips
+ *  Version: 0.21
+ *
+ *  FEATURES currently supported:
+ *    Output fixed at S32_LE, 2 channel to hw:0,0
+ *    Rates: 44.1, 48, 96, 192.
+ *
+ *  Changelog:
+ *  0.8
+ *    Use separate card based buffer for periods table.
+ *  0.9
+ *    Use 2 channel output streams instead of 8 channel.
+ *       (8 channel output streams might be good for ASIO type output)
+ *    Corrected speaker output, so Front -> Front etc.
+ *  0.10
+ *    Fixed missed interrupts.
+ *  0.11
+ *    Add Sound card model number and names.
+ *    Add Analog volume controls.
+ *  0.12
+ *    Corrected playback interrupts. Now interrupt per period, instead of half period.
+ *  0.13
+ *    Use single trigger for multichannel.
+ *  0.14
+ *    Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
+ *  0.15
+ *    Force buffer_size / period_size == INTEGER.
+ *  0.16
+ *    Update p16v.c to work with changed alsa api.
+ *  0.17
+ *    Update p16v.c to work with changed alsa api. Removed boot_devs.
+ *  0.18
+ *    Merging with snd-emu10k1 driver.
+ *  0.19
+ *    One stereo channel at 24bit now works.
+ *  0.20
+ *    Added better register defines.
+ *  0.21
+ *    Split from p16v.c
+ *
+ *
+ *  BUGS:
+ *    Some stability problems when unloading the snd-p16v kernel module.
+ *    --
+ *
+ *  TODO:
+ *    SPDIF out.
+ *    Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
+ *    Currently capture fixed at 48000Hz.
+ *
+ *    --
+ *  GENERAL INFO:
+ *    Model: SB0240
+ *    P16V Chip: CA0151-DBS
+ *    Audigy 2 Chip: CA0102-IAT
+ *    AC97 Codec: STAC 9721
+ *    ADC: Philips 1361T (Stereo 24bit)
+ *    DAC: CS4382-K (8-channel, 24bit, 192Khz)
+ *
+ *  This code was initially based on code from ALSA's emu10k1x.c which is:
+ *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/********************************************************************************************************/
+/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers                     */
+/********************************************************************************************************/
+                                                                                                                           
+/* The sample rate of the SPDIF outputs is set by modifying a register in the EMU10K2 PTR register A_SPDIF_SAMPLERATE.
+ * The sample rate is also controlled by the same registers that control the rate of the EMU10K2 sample rate converters.
+ */
+
+/* Initially all registers from 0x00 to 0x3f have zero contents. */
+#define PLAYBACK_LIST_ADDR	0x00		/* Base DMA address of a list of pointers to each period/size */
+						/* One list entry: 4 bytes for DMA address, 
+						 * 4 bytes for period_size << 16.
+						 * One list entry is 8 bytes long.
+						 * One list entry for each period in the buffer.
+						 */
+#define PLAYBACK_LIST_SIZE	0x01		/* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000  */
+#define PLAYBACK_LIST_PTR	0x02		/* Pointer to the current period being played */
+#define PLAYBACK_UNKNOWN3	0x03		/* Not used */
+#define PLAYBACK_DMA_ADDR	0x04		/* Playback DMA address */
+#define PLAYBACK_PERIOD_SIZE	0x05		/* Playback period size. win2000 uses 0x04000000 */
+#define PLAYBACK_POINTER	0x06		/* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
+#define PLAYBACK_FIFO_END_ADDRESS	0x07		/* Playback FIFO end address */
+#define PLAYBACK_FIFO_POINTER	0x08		/* Playback FIFO pointer and number of valid sound samples in cache */
+#define PLAYBACK_UNKNOWN9	0x09		/* Not used */
+#define CAPTURE_DMA_ADDR	0x10		/* Capture DMA address */
+#define CAPTURE_BUFFER_SIZE	0x11		/* Capture buffer size */
+#define CAPTURE_POINTER		0x12		/* Capture buffer pointer. Sample currently in ADC */
+#define CAPTURE_FIFO_POINTER	0x13		/* Capture FIFO pointer and number of valid sound samples in cache */
+#define CAPTURE_P16V_VOLUME1	0x14		/* Low: Capture volume 0xXXXX3030 */
+#define CAPTURE_P16V_VOLUME2	0x15		/* High:Has no effect on capture volume */
+#define CAPTURE_P16V_SOURCE     0x16            /* P16V source select. Set to 0x0700E4E5 for AC97 CAPTURE */
+						/* [0:1] Capture input 0 channel select. 0 = Capture output 0.
+						 *                                       1 = Capture output 1.
+						 *                                       2 = Capture output 2.
+						 *                                       3 = Capture output 3.
+						 * [3:2] Capture input 1 channel select. 0 = Capture output 0.
+						 *                                       1 = Capture output 1.
+						 *                                       2 = Capture output 2.
+						 *                                       3 = Capture output 3.
+						 * [5:4] Capture input 2 channel select. 0 = Capture output 0.
+						 *                                       1 = Capture output 1.
+						 *                                       2 = Capture output 2.
+						 *                                       3 = Capture output 3.
+						 * [7:6] Capture input 3 channel select. 0 = Capture output 0.
+						 *                                       1 = Capture output 1.
+						 *                                       2 = Capture output 2.
+						 *                                       3 = Capture output 3.
+						 * [9:8] Playback input 0 channel select. 0 = Play output 0.
+						 *                                        1 = Play output 1.
+						 *                                        2 = Play output 2.
+						 *                                        3 = Play output 3.
+						 * [11:10] Playback input 1 channel select. 0 = Play output 0.
+						 *                                          1 = Play output 1.
+						 *                                          2 = Play output 2.
+						 *                                          3 = Play output 3.
+						 * [13:12] Playback input 2 channel select. 0 = Play output 0.
+						 *                                          1 = Play output 1.
+						 *                                          2 = Play output 2.
+						 *                                          3 = Play output 3.
+						 * [15:14] Playback input 3 channel select. 0 = Play output 0.
+						 *                                          1 = Play output 1.
+						 *                                          2 = Play output 2.
+						 *                                          3 = Play output 3.
+						 * [19:16] Playback mixer output enable. 1 bit per channel.
+						 * [23:20] Capture mixer output enable. 1 bit per channel.
+						 * [26:24] FX engine channel capture 0 = 0x60-0x67.
+						 *                                   1 = 0x68-0x6f.
+						 *                                   2 = 0x70-0x77.
+						 *                                   3 = 0x78-0x7f.
+						 *                                   4 = 0x80-0x87.
+						 *                                   5 = 0x88-0x8f.
+						 *                                   6 = 0x90-0x97.
+						 *                                   7 = 0x98-0x9f.
+						 * [31:27] Not used.
+						 */
+
+						/* 0x1 = capture on.
+						 * 0x100 = capture off.
+						 * 0x200 = capture off.
+						 * 0x1000 = capture off.
+						 */
+#define CAPTURE_RATE_STATUS		0x17		/* Capture sample rate. Read only */
+						/* [15:0] Not used.
+						 * [18:16] Channel 0 Detected sample rate. 0 - 44.1khz
+						 *                               1 - 48 khz
+						 *                               2 - 96 khz
+						 *                               3 - 192 khz
+						 *                               7 - undefined rate.
+						 * [19] Channel 0. 1 - Valid, 0 - Not Valid.
+						 * [22:20] Channel 1 Detected sample rate. 
+						 * [23] Channel 1. 1 - Valid, 0 - Not Valid.
+						 * [26:24] Channel 2 Detected sample rate. 
+						 * [27] Channel 2. 1 - Valid, 0 - Not Valid.
+						 * [30:28] Channel 3 Detected sample rate. 
+						 * [31] Channel 3. 1 - Valid, 0 - Not Valid.
+						 */
+/* 0x18 - 0x1f unused */
+#define PLAYBACK_LAST_SAMPLE    0x20		/* The sample currently being played. Read only */
+/* 0x21 - 0x3f unused */
+#define BASIC_INTERRUPT         0x40		/* Used by both playback and capture interrupt handler */
+						/* Playback (0x1<<channel_id) Don't touch high 16bits. */
+						/* Capture  (0x100<<channel_id). not tested */
+						/* Start Playback [3:0] (one bit per channel)
+						 * Start Capture [11:8] (one bit per channel)
+						 * Record source select for channel 0 [18:16]
+						 * Record source select for channel 1 [22:20]
+						 * Record source select for channel 2 [26:24]
+						 * Record source select for channel 3 [30:28]
+						 * 0 - SPDIF channel.
+						 * 1 - I2S channel.
+						 * 2 - SRC48 channel.
+						 * 3 - SRCMulti_SPDIF channel.
+						 * 4 - SRCMulti_I2S channel.
+						 * 5 - SPDIF channel.
+						 * 6 - fxengine capture.
+						 * 7 - AC97 capture.
+						 */
+						/* Default 41110000.
+						 * Writing 0xffffffff hangs the PC.
+						 * Writing 0xffff0000 -> 77770000 so it must be some sort of route.
+						 * bit 0x1 starts DMA playback on channel_id 0
+						 */
+/* 0x41,42 take values from 0 - 0xffffffff, but have no effect on playback */
+/* 0x43,0x48 do not remember settings */
+/* 0x41-45 unused */
+#define WATERMARK            0x46		/* Test bit to indicate cache level usage */
+						/* Values it can have while playing on channel 0. 
+						 * 0000f000, 0000f004, 0000f008, 0000f00c.
+						 * Readonly.
+						 */
+/* 0x47-0x4f unused */
+/* 0x50-0x5f Capture cache data */
+#define SRCSel			0x60            /* SRCSel. Default 0x4. Bypass P16V 0x14 */
+						/* [0] 0 = 10K2 audio, 1 = SRC48 mixer output.
+						 * [2] 0 = 10K2 audio, 1 = SRCMulti SPDIF mixer output.
+						 * [4] 0 = 10K2 audio, 1 = SRCMulti I2S mixer output.
+						 */
+						/* SRC48 converts samples rates 44.1, 48, 96, 192 to 48 khz. */
+						/* SRCMulti converts 48khz samples rates to 44.1, 48, 96, 192 to 48. */
+						/* SRC48 and SRCMULTI sample rate select and output select. */
+						/* 0xffffffff -> 0xC0000015
+						 * 0xXXXXXXX4 = Enable Front Left/Right
+						 * Enable PCMs
+						 */
+
+/* 0x61 -> 0x6c are Volume controls */
+#define PLAYBACK_VOLUME_MIXER1  0x61		/* SRC48 Low to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER2  0x62		/* SRC48 High to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER3  0x63		/* SRCMULTI SPDIF Low to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER4  0x64		/* SRCMULTI SPDIF High to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER5  0x65		/* SRCMULTI I2S Low to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER6  0x66		/* SRCMULTI I2S High to mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER7  0x67		/* P16V Low to SRCMULTI SPDIF mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER8  0x68		/* P16V High to SRCMULTI SPDIF mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER9  0x69		/* P16V Low to SRCMULTI I2S mixer input volume control. */
+						/* 0xXXXX3030 = PCM0 Volume (Front).
+						 * 0x3030XXXX = PCM1 Volume (Center)
+						 */
+#define PLAYBACK_VOLUME_MIXER10  0x6a		/* P16V High to SRCMULTI I2S mixer input volume control. */
+						/* 0x3030XXXX = PCM3 Volume (Rear). */
+#define PLAYBACK_VOLUME_MIXER11  0x6b		/* E10K2 Low to SRC48 mixer input volume control. */
+#define PLAYBACK_VOLUME_MIXER12 0x6c		/* E10K2 High to SRC48 mixer input volume control. */
+
+#define SRC48_ENABLE            0x6d            /* SRC48 input audio enable */
+						/* SRC48 converts samples rates 44.1, 48, 96, 192 to 48 khz. */
+						/* [23:16] The corresponding P16V channel to SRC48 enabled if == 1.
+						 * [31:24] The corresponding E10K2 channel to SRC48 enabled.
+						 */
+#define SRCMULTI_ENABLE         0x6e            /* SRCMulti input audio enable. Default 0xffffffff */
+						/* SRCMulti converts 48khz samples rates to 44.1, 48, 96, 192 to 48. */
+						/* [7:0] The corresponding P16V channel to SRCMulti_I2S enabled if == 1.
+						 * [15:8] The corresponding E10K2 channel to SRCMulti I2S enabled.
+						 * [23:16] The corresponding P16V channel to SRCMulti SPDIF enabled.
+						 * [31:24] The corresponding E10K2 channel to SRCMulti SPDIF enabled.
+						 */
+						/* Bypass P16V 0xff00ff00 
+						 * Bitmap. 0 = Off, 1 = On.
+						 * P16V playback outputs:
+						 * 0xXXXXXXX1 = PCM0 Left. (Front)
+						 * 0xXXXXXXX2 = PCM0 Right.
+						 * 0xXXXXXXX4 = PCM1 Left. (Center/LFE)
+						 * 0xXXXXXXX8 = PCM1 Right.
+						 * 0xXXXXXX1X = PCM2 Left. (Unknown)
+						 * 0xXXXXXX2X = PCM2 Right.
+						 * 0xXXXXXX4X = PCM3 Left. (Rear)
+						 * 0xXXXXXX8X = PCM3 Right.
+						 */
+#define AUDIO_OUT_ENABLE        0x6f            /* Default: 000100FF */
+						/* [3:0] Does something, but not documented. Probably capture enable.
+						 * [7:4] Playback channels enable. not documented.
+						 * [16] AC97 output enable if == 1
+						 * [30] 0 = SRCMulti_I2S input from fxengine 0x68-0x6f.
+						 *      1 = SRCMulti_I2S input from SRC48 output.
+						 * [31] 0 = SRCMulti_SPDIF input from fxengine 0x60-0x67.
+						 *      1 = SRCMulti_SPDIF input from SRC48 output.
+						 */
+						/* 0xffffffff -> C00100FF */
+						/* 0 -> Not playback sound, irq still running */
+						/* 0xXXXXXX10 = PCM0 Left/Right On. (Front)
+						 * 0xXXXXXX20 = PCM1 Left/Right On. (Center/LFE)
+						 * 0xXXXXXX40 = PCM2 Left/Right On. (Unknown)
+						 * 0xXXXXXX80 = PCM3 Left/Right On. (Rear)
+						 */
+#define PLAYBACK_SPDIF_SELECT     0x70          /* Default: 12030F00 */
+						/* 0xffffffff -> 3FF30FFF */
+						/* 0x00000001 pauses stream/irq fail. */
+						/* All other bits do not effect playback */
+#define PLAYBACK_SPDIF_SRC_SELECT 0x71          /* Default: 0000E4E4 */
+						/* 0xffffffff -> F33FFFFF */
+						/* All bits do not effect playback */
+#define PLAYBACK_SPDIF_USER_DATA0 0x72		/* SPDIF out user data 0 */
+#define PLAYBACK_SPDIF_USER_DATA1 0x73		/* SPDIF out user data 1 */
+/* 0x74-0x75 unknown */
+#define CAPTURE_SPDIF_CONTROL	0x76		/* SPDIF in control setting */
+#define CAPTURE_SPDIF_STATUS	0x77		/* SPDIF in status */
+#define CAPURE_SPDIF_USER_DATA0 0x78		/* SPDIF in user data 0 */
+#define CAPURE_SPDIF_USER_DATA1 0x79		/* SPDIF in user data 1 */
+#define CAPURE_SPDIF_USER_DATA2 0x7a		/* SPDIF in user data 2 */
+
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/p17v.h b/src/kernel/linux/v4.14/sound/pci/emu10k1/p17v.h
new file mode 100644
index 0000000..4ef5f68
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/p17v.h
@@ -0,0 +1,158 @@
+/*
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver p17v chips
+ *  Version: 0.01
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/******************************************************************************/
+/* Audigy2Value Tina (P17V) pointer-offset register set,
+ * accessed through the PTR20 and DATA24 registers  */
+/******************************************************************************/
+
+/* 00 - 07: Not used */
+#define P17V_PLAYBACK_FIFO_PTR	0x08	/* Current playback fifo pointer
+					 * and number of sound samples in cache.
+					 */  
+/* 09 - 12: Not used */
+#define P17V_CAPTURE_FIFO_PTR	0x13	/* Current capture fifo pointer
+					 * and number of sound samples in cache.
+					 */  
+/* 14 - 17: Not used */
+#define P17V_PB_CHN_SEL		0x18	/* P17v playback channel select */
+#define P17V_SE_SLOT_SEL_L	0x19	/* Sound Engine slot select low */
+#define P17V_SE_SLOT_SEL_H	0x1a	/* Sound Engine slot select high */
+/* 1b - 1f: Not used */
+/* 20 - 2f: Not used */
+/* 30 - 3b: Not used */
+#define P17V_SPI		0x3c	/* SPI interface register */
+#define P17V_I2C_ADDR		0x3d	/* I2C Address */
+#define P17V_I2C_0		0x3e	/* I2C Data */
+#define P17V_I2C_1		0x3f	/* I2C Data */
+/* I2C values */
+#define I2C_A_ADC_ADD_MASK	0x000000fe	/*The address is a 7 bit address */
+#define I2C_A_ADC_RW_MASK	0x00000001	/*bit mask for R/W */
+#define I2C_A_ADC_TRANS_MASK	0x00000010  	/*Bit mask for I2c address DAC value  */
+#define I2C_A_ADC_ABORT_MASK	0x00000020	/*Bit mask for I2C transaction abort flag */
+#define I2C_A_ADC_LAST_MASK	0x00000040	/*Bit mask for Last word transaction */
+#define I2C_A_ADC_BYTE_MASK	0x00000080	/*Bit mask for Byte Mode */
+
+#define I2C_A_ADC_ADD		0x00000034	/*This is the Device address for ADC  */
+#define I2C_A_ADC_READ		0x00000001	/*To perform a read operation */
+#define I2C_A_ADC_START		0x00000100	/*Start I2C transaction */
+#define I2C_A_ADC_ABORT		0x00000200	/*I2C transaction abort */
+#define I2C_A_ADC_LAST		0x00000400	/*I2C last transaction */
+#define I2C_A_ADC_BYTE		0x00000800	/*I2C one byte mode */
+
+#define I2C_D_ADC_REG_MASK	0xfe000000  	/*ADC address register */ 
+#define I2C_D_ADC_DAT_MASK	0x01ff0000  	/*ADC data register */
+
+#define ADC_TIMEOUT		0x00000007	/*ADC Timeout Clock Disable */
+#define ADC_IFC_CTRL		0x0000000b	/*ADC Interface Control */
+#define ADC_MASTER		0x0000000c	/*ADC Master Mode Control */
+#define ADC_POWER		0x0000000d	/*ADC PowerDown Control */
+#define ADC_ATTEN_ADCL		0x0000000e	/*ADC Attenuation ADCL */
+#define ADC_ATTEN_ADCR		0x0000000f	/*ADC Attenuation ADCR */
+#define ADC_ALC_CTRL1		0x00000010	/*ADC ALC Control 1 */
+#define ADC_ALC_CTRL2		0x00000011	/*ADC ALC Control 2 */
+#define ADC_ALC_CTRL3		0x00000012	/*ADC ALC Control 3 */
+#define ADC_NOISE_CTRL		0x00000013	/*ADC Noise Gate Control */
+#define ADC_LIMIT_CTRL		0x00000014	/*ADC Limiter Control */
+#define ADC_MUX			0x00000015  	/*ADC Mux offset */
+#if 0
+/* FIXME: Not tested yet. */
+#define ADC_GAIN_MASK		0x000000ff	//Mask for ADC Gain
+#define ADC_ZERODB		0x000000cf	//Value to set ADC to 0dB
+#define ADC_MUTE_MASK		0x000000c0	//Mask for ADC mute
+#define ADC_MUTE		0x000000c0	//Value to mute ADC
+#define ADC_OSR			0x00000008	//Mask for ADC oversample rate select
+#define ADC_TIMEOUT_DISABLE	0x00000008	//Value and mask to disable Timeout clock
+#define ADC_HPF_DISABLE		0x00000100	//Value and mask to disable High pass filter
+#define ADC_TRANWIN_MASK	0x00000070	//Mask for Length of Transient Window
+#endif
+
+#define ADC_MUX_MASK		0x0000000f	//Mask for ADC Mux
+#define ADC_MUX_0		0x00000001	//Value to select Unknown at ADC Mux (Not used)
+#define ADC_MUX_1		0x00000002	//Value to select Unknown at ADC Mux (Not used)
+#define ADC_MUX_2		0x00000004	//Value to select Mic at ADC Mux
+#define ADC_MUX_3		0x00000008	//Value to select Line-In at ADC Mux
+
+#define P17V_START_AUDIO	0x40	/* Start Audio bit */
+/* 41 - 47: Reserved */
+#define P17V_START_CAPTURE	0x48	/* Start Capture bit */
+#define P17V_CAPTURE_FIFO_BASE	0x49	/* Record FIFO base address */
+#define P17V_CAPTURE_FIFO_SIZE	0x4a	/* Record FIFO buffer size */
+#define P17V_CAPTURE_FIFO_INDEX	0x4b	/* Record FIFO capture index */
+#define P17V_CAPTURE_VOL_H	0x4c	/* P17v capture volume control */
+#define P17V_CAPTURE_VOL_L	0x4d	/* P17v capture volume control */
+/* 4e - 4f: Not used */
+/* 50 - 5f: Not used */
+#define P17V_SRCSel		0x60	/* SRC48 and SRCMulti sample rate select
+					 * and output select
+					 */
+#define P17V_MIXER_AC97_10K1_VOL_L	0x61	/* 10K to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_10K1_VOL_H	0x62	/* 10K to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_P17V_VOL_L	0x63	/* P17V to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_P17V_VOL_H	0x64	/* P17V to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_SRP_REC_VOL_L	0x65	/* SRP Record to Mixer_AC97 input volume control */
+#define P17V_MIXER_AC97_SRP_REC_VOL_H	0x66	/* SRP Record to Mixer_AC97 input volume control */
+/* 67 - 68: Reserved */
+#define P17V_MIXER_Spdif_10K1_VOL_L	0x69	/* 10K to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_10K1_VOL_H	0x6A	/* 10K to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_P17V_VOL_L	0x6B	/* P17V to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_P17V_VOL_H	0x6C	/* P17V to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_SRP_REC_VOL_L	0x6D	/* SRP Record to Mixer_Spdif input volume control */
+#define P17V_MIXER_Spdif_SRP_REC_VOL_H	0x6E	/* SRP Record to Mixer_Spdif input volume control */
+/* 6f - 70: Reserved */
+#define P17V_MIXER_I2S_10K1_VOL_L	0x71	/* 10K to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_10K1_VOL_H	0x72	/* 10K to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_P17V_VOL_L	0x73	/* P17V to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_P17V_VOL_H	0x74	/* P17V to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_SRP_REC_VOL_L	0x75	/* SRP Record to Mixer_I2S input volume control */
+#define P17V_MIXER_I2S_SRP_REC_VOL_H	0x76	/* SRP Record to Mixer_I2S input volume control */
+/* 77 - 78: Reserved */
+#define P17V_MIXER_AC97_ENABLE		0x79	/* Mixer AC97 input audio enable */
+#define P17V_MIXER_SPDIF_ENABLE		0x7A	/* Mixer SPDIF input audio enable */
+#define P17V_MIXER_I2S_ENABLE		0x7B	/* Mixer I2S input audio enable */
+#define P17V_AUDIO_OUT_ENABLE		0x7C	/* Audio out enable */
+#define P17V_MIXER_ATT			0x7D	/* SRP Mixer Attenuation Select */
+#define P17V_SRP_RECORD_SRR		0x7E	/* SRP Record channel source Select */
+#define P17V_SOFT_RESET_SRP_MIXER	0x7F	/* SRP and mixer soft reset */
+
+#define P17V_AC97_OUT_MASTER_VOL_L	0x80	/* AC97 Output master volume control */
+#define P17V_AC97_OUT_MASTER_VOL_H	0x81	/* AC97 Output master volume control */
+#define P17V_SPDIF_OUT_MASTER_VOL_L	0x82	/* SPDIF Output master volume control */
+#define P17V_SPDIF_OUT_MASTER_VOL_H	0x83	/* SPDIF Output master volume control */
+#define P17V_I2S_OUT_MASTER_VOL_L	0x84	/* I2S Output master volume control */
+#define P17V_I2S_OUT_MASTER_VOL_H	0x85	/* I2S Output master volume control */
+/* 86 - 87: Not used */
+#define P17V_I2S_CHANNEL_SWAP_PHASE_INVERSE	0x88	/* I2S out mono channel swap
+							 * and phase inverse */
+#define P17V_SPDIF_CHANNEL_SWAP_PHASE_INVERSE	0x89	/* SPDIF out mono channel swap
+							 * and phase inverse */
+/* 8A: Not used */
+#define P17V_SRP_P17V_ESR		0x8B	/* SRP_P17V estimated sample rate and rate lock */
+#define P17V_SRP_REC_ESR		0x8C	/* SRP_REC estimated sample rate and rate lock */
+#define P17V_SRP_BYPASS			0x8D	/* srps channel bypass and srps bypass */
+/* 8E - 92: Not used */
+#define P17V_I2S_SRC_SEL		0x93	/* I2SIN mode sel */
+
+
+
+
+
+
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/timer.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/timer.c
new file mode 100644
index 0000000..b69a7f8
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/timer.c
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (c) by Lee Revell <rlrevell@joe-job.com>
+ *                   Clemens Ladisch <clemens@ladisch.de>
+ *  Routines for control of EMU10K1 chips
+ *
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+static int snd_emu10k1_timer_start(struct snd_timer *timer)
+{
+	struct snd_emu10k1 *emu;
+	unsigned long flags;
+	unsigned int delay;
+
+	emu = snd_timer_chip(timer);
+	delay = timer->sticks - 1;
+	if (delay < 5 ) /* minimum time is 5 ticks */
+		delay = 5;
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	snd_emu10k1_intr_enable(emu, INTE_INTERVALTIMERENB);
+	outw(delay & TIMER_RATE_MASK, emu->port + TIMER);
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_timer_stop(struct snd_timer *timer)
+{
+	struct snd_emu10k1 *emu;
+	unsigned long flags;
+
+	emu = snd_timer_chip(timer);
+	spin_lock_irqsave(&emu->reg_lock, flags);
+	snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
+	spin_unlock_irqrestore(&emu->reg_lock, flags);
+	return 0;
+}
+
+static int snd_emu10k1_timer_precise_resolution(struct snd_timer *timer,
+					       unsigned long *num, unsigned long *den)
+{
+	*num = 1;
+	*den = 48000;
+	return 0;
+}
+
+static struct snd_timer_hardware snd_emu10k1_timer_hw = {
+	.flags = SNDRV_TIMER_HW_AUTO,
+	.resolution = 20833, /* 1 sample @ 48KHZ = 20.833...us */
+	.ticks = 1024,
+	.start = snd_emu10k1_timer_start,
+	.stop = snd_emu10k1_timer_stop,
+	.precise_resolution = snd_emu10k1_timer_precise_resolution,
+};
+
+int snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
+{
+	struct snd_timer *timer = NULL;
+	struct snd_timer_id tid;
+	int err;
+
+	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
+	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
+	tid.card = emu->card->number;
+	tid.device = device;
+	tid.subdevice = 0;
+	if ((err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer)) >= 0) {
+		strcpy(timer->name, "EMU10K1 timer");
+		timer->private_data = emu;
+		timer->hw = snd_emu10k1_timer_hw;
+	}
+	emu->timer = timer;
+	return err;
+}
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/tina2.h b/src/kernel/linux/v4.14/sound/pci/emu10k1/tina2.h
new file mode 100644
index 0000000..f2d8eb6
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/tina2.h
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
+ *  Driver tina2 chips
+ *  Version: 0.1
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/********************************************************************************************************/
+/* Audigy2 Tina2 (notebook) pointer-offset register set, accessed through the PTR2 and DATA2 registers  */
+/********************************************************************************************************/
+
+#define TINA2_VOLUME	0x71	/* Attenuate playback volume to prevent distortion. */
+				/* The windows driver does not use this register,
+				 * so it must use some other attenuation method.
+				 * Without this, the output is 12dB too loud,
+				 * resulting in distortion.
+				 */
+
diff --git a/src/kernel/linux/v4.14/sound/pci/emu10k1/voice.c b/src/kernel/linux/v4.14/sound/pci/emu10k1/voice.c
new file mode 100644
index 0000000..f16fd5c
--- /dev/null
+++ b/src/kernel/linux/v4.14/sound/pci/emu10k1/voice.c
@@ -0,0 +1,168 @@
+/*
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *                   Creative Labs, Inc.
+ *                   Lee Revell <rlrevell@joe-job.com>
+ *  Routines for control of EMU10K1 chips - voice manager
+ *
+ *  Rewrote voice allocator for multichannel support - rlrevell 12/2004
+ * 
+ *  BUGS:
+ *    --
+ *
+ *  TODO:
+ *    --
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/time.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include <sound/emu10k1.h>
+
+/* Previously the voice allocator started at 0 every time.  The new voice 
+ * allocator uses a round robin scheme.  The next free voice is tracked in 
+ * the card record and each allocation begins where the last left off.  The 
+ * hardware requires stereo interleaved voices be aligned to an even/odd 
+ * boundary.  For multichannel voice allocation we ensure than the block of 
+ * voices does not cross the 32 voice boundary.  This simplifies the 
+ * multichannel support and ensures we can use a single write to the 
+ * (set|clear)_loop_stop registers.  Otherwise (for example) the voices would 
+ * get out of sync when pausing/resuming a stream.
+ *							--rlrevell
+ */
+
+static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
+		       struct snd_emu10k1_voice **rvoice)
+{
+	struct snd_emu10k1_voice *voice;
+	int i, j, k, first_voice, last_voice, skip;
+
+	*rvoice = NULL;
+	first_voice = last_voice = 0;
+	for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
+		/*
+		dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",
+		       i, j, emu->next_free_voice);
+		*/
+		i %= NUM_G;
+
+		/* stereo voices must be even/odd */
+		if ((number == 2) && (i % 2)) {
+			i++;
+			continue;
+		}
+			
+		skip = 0;
+		for (k = 0; k < number; k++) {
+			voice = &emu->voices[(i+k) % NUM_G];
+			if (voice->use) {
+				skip = 1;
+				break;
+			}
+		}
+		if (!skip) {
+			/* dev_dbg(emu->card->dev, "allocated voice %d\n", i); */
+			first_voice = i;
+			last_voice = (i + number) % NUM_G;
+			emu->next_free_voice = last_voice;
+			break;
+		}
+	}
+	
+	if (first_voice == last_voice)
+		return -ENOMEM;
+	
+	for (i = 0; i < number; i++) {
+		voice = &emu->voices[(first_voice + i) % NUM_G];
+		/*
+		dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
+		       voice->number, idx-first_voice+1, number);
+		*/
+		voice->use = 1;
+		switch (type) {
+		case EMU10K1_PCM:
+			voice->pcm = 1;
+			break;
+		case EMU10K1_SYNTH:
+			voice->synth = 1;
+			break;
+		case EMU10K1_MIDI:
+			voice->midi = 1;
+			break;
+		case EMU10K1_EFX:
+			voice->efx = 1;
+			break;
+		}
+	}
+	*rvoice = &emu->voices[first_voice];
+	return 0;
+}
+
+int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
+			    struct snd_emu10k1_voice **rvoice)
+{
+	unsigned long flags;
+	int result;
+
+	if (snd_BUG_ON(!rvoice))
+		return -EINVAL;
+	if (snd_BUG_ON(!number))
+		return -EINVAL;
+
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	for (;;) {
+		result = voice_alloc(emu, type, number, rvoice);
+		if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
+			break;
+
+		/* free a voice from synth */
+		if (emu->get_synth_voice) {
+			result = emu->get_synth_voice(emu);
+			if (result >= 0) {
+				struct snd_emu10k1_voice *pvoice = &emu->voices[result];
+				pvoice->interrupt = NULL;
+				pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
+				pvoice->epcm = NULL;
+			}
+		}
+		if (result < 0)
+			break;
+	}
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+
+	return result;
+}
+
+EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
+
+int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
+			   struct snd_emu10k1_voice *pvoice)
+{
+	unsigned long flags;
+
+	if (snd_BUG_ON(!pvoice))
+		return -EINVAL;
+	spin_lock_irqsave(&emu->voice_lock, flags);
+	pvoice->interrupt = NULL;
+	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
+	pvoice->epcm = NULL;
+	snd_emu10k1_voice_init(emu, pvoice->number);
+	spin_unlock_irqrestore(&emu->voice_lock, flags);
+	return 0;
+}
+
+EXPORT_SYMBOL(snd_emu10k1_voice_free);