summaryrefslogtreecommitdiff
path: root/sound/isa/sb
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-17 02:20:36 +0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-17 02:20:36 +0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/isa/sb
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'sound/isa/sb')
-rw-r--r--sound/isa/sb/Makefile39
-rw-r--r--sound/isa/sb/emu8000.c1170
-rw-r--r--sound/isa/sb/emu8000_callback.c543
-rw-r--r--sound/isa/sb/emu8000_local.h43
-rw-r--r--sound/isa/sb/emu8000_patch.c303
-rw-r--r--sound/isa/sb/emu8000_pcm.c704
-rw-r--r--sound/isa/sb/emu8000_synth.c134
-rw-r--r--sound/isa/sb/es968.c235
-rw-r--r--sound/isa/sb/sb16.c678
-rw-r--r--sound/isa/sb/sb16_csp.c1175
-rw-r--r--sound/isa/sb/sb16_csp_codecs.h949
-rw-r--r--sound/isa/sb/sb16_main.c916
-rw-r--r--sound/isa/sb/sb8.c223
-rw-r--r--sound/isa/sb/sb8_main.c565
-rw-r--r--sound/isa/sb/sb8_midi.c293
-rw-r--r--sound/isa/sb/sb_common.c313
-rw-r--r--sound/isa/sb/sb_mixer.c844
-rw-r--r--sound/isa/sb/sbawe.c2
18 files changed, 9129 insertions, 0 deletions
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
new file mode 100644
index 000000000000..fd9d9c5726fc
--- /dev/null
+++ b/sound/isa/sb/Makefile
@@ -0,0 +1,39 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-sb-common-objs := sb_common.o sb_mixer.o
+snd-sb8-dsp-objs := sb8_main.o sb8_midi.o
+snd-sb16-dsp-objs := sb16_main.o
+snd-sb16-csp-objs := sb16_csp.o
+snd-sb8-objs := sb8.o
+snd-sb16-objs := sb16.o
+snd-sbawe-objs := sbawe.o emu8000.o
+snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
+snd-es968-objs := es968.o
+
+#
+# this function returns:
+# "m" - CONFIG_SND_SEQUENCER is m
+# <empty string> - CONFIG_SND_SEQUENCER is undefined
+# otherwise parameter #1 value
+#
+sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_DT019X) += snd-sb16-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o
+obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o
+ifeq ($(CONFIG_SND_SB16_CSP),y)
+ obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
+ obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
+endif
+obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emu8000-synth.o
+
+obj-m := $(sort $(obj-m))
diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
new file mode 100644
index 000000000000..028af4066595
--- /dev/null
+++ b/sound/isa/sb/emu8000.c
@@ -0,0 +1,1170 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
+ * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ * Routines for control of EMU8000 chip
+ *
+ * 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 <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/emu8000.h>
+#include <sound/emu8000_reg.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+/*
+ * emu8000 register controls
+ */
+
+/*
+ * The following routines read and write registers on the emu8000. They
+ * should always be called via the EMU8000*READ/WRITE macros and never
+ * directly. The macros handle the port number and command word.
+ */
+/* Write a word */
+void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ if (reg != emu->last_reg) {
+ outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+ emu->last_reg = reg;
+ }
+ outw((unsigned short)val, port); /* Send data */
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+/* Read a word */
+unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, unsigned int reg)
+{
+ unsigned short res;
+ unsigned long flags;
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ if (reg != emu->last_reg) {
+ outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+ emu->last_reg = reg;
+ }
+ res = inw(port); /* Read data */
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return res;
+}
+
+/* Write a double word */
+void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ if (reg != emu->last_reg) {
+ outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+ emu->last_reg = reg;
+ }
+ outw((unsigned short)val, port); /* Send low word of data */
+ outw((unsigned short)(val>>16), port+2); /* Send high word of data */
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+}
+
+/* Read a double word */
+unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, unsigned int reg)
+{
+ unsigned short low;
+ unsigned int res;
+ unsigned long flags;
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ if (reg != emu->last_reg) {
+ outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */
+ emu->last_reg = reg;
+ }
+ low = inw(port); /* Read low word of data */
+ res = low + (inw(port+2) << 16);
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ return res;
+}
+
+/*
+ * Set up / close a channel to be used for DMA.
+ */
+/*exported*/ void
+snd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode)
+{
+ unsigned right_bit = (mode & EMU8000_RAM_RIGHT) ? 0x01000000 : 0;
+ mode &= EMU8000_RAM_MODE_MASK;
+ if (mode == EMU8000_RAM_CLOSE) {
+ EMU8000_CCCA_WRITE(emu, ch, 0);
+ EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F);
+ return;
+ }
+ EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
+ EMU8000_VTFT_WRITE(emu, ch, 0);
+ EMU8000_CVCF_WRITE(emu, ch, 0);
+ EMU8000_PTRX_WRITE(emu, ch, 0x40000000);
+ EMU8000_CPF_WRITE(emu, ch, 0x40000000);
+ EMU8000_PSST_WRITE(emu, ch, 0);
+ EMU8000_CSL_WRITE(emu, ch, 0);
+ if (mode == EMU8000_RAM_WRITE) /* DMA write */
+ EMU8000_CCCA_WRITE(emu, ch, 0x06000000 | right_bit);
+ else /* DMA read */
+ EMU8000_CCCA_WRITE(emu, ch, 0x04000000 | right_bit);
+}
+
+/*
+ */
+static void __init
+snd_emu8000_read_wait(emu8000_t *emu)
+{
+ while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ if (signal_pending(current))
+ break;
+ }
+}
+
+/*
+ */
+static void __init
+snd_emu8000_write_wait(emu8000_t *emu)
+{
+ while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ if (signal_pending(current))
+ break;
+ }
+}
+
+/*
+ * detect a card at the given port
+ */
+static int __init
+snd_emu8000_detect(emu8000_t *emu)
+{
+ /* Initialise */
+ EMU8000_HWCF1_WRITE(emu, 0x0059);
+ EMU8000_HWCF2_WRITE(emu, 0x0020);
+ EMU8000_HWCF3_WRITE(emu, 0x0000);
+ /* Check for a recognisable emu8000 */
+ /*
+ if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c)
+ return -ENODEV;
+ */
+ if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058)
+ return -ENODEV;
+ if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003)
+ return -ENODEV;
+
+ snd_printdd("EMU8000 [0x%lx]: Synth chip found\n",
+ emu->port1);
+ return 0;
+}
+
+
+/*
+ * intiailize audio channels
+ */
+static void __init
+init_audio(emu8000_t *emu)
+{
+ int ch;
+
+ /* turn off envelope engines */
+ for (ch = 0; ch < EMU8000_CHANNELS; ch++)
+ EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
+
+ /* reset all other parameters to zero */
+ for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
+ EMU8000_ENVVOL_WRITE(emu, ch, 0);
+ EMU8000_ENVVAL_WRITE(emu, ch, 0);
+ EMU8000_DCYSUS_WRITE(emu, ch, 0);
+ EMU8000_ATKHLDV_WRITE(emu, ch, 0);
+ EMU8000_LFO1VAL_WRITE(emu, ch, 0);
+ EMU8000_ATKHLD_WRITE(emu, ch, 0);
+ EMU8000_LFO2VAL_WRITE(emu, ch, 0);
+ EMU8000_IP_WRITE(emu, ch, 0);
+ EMU8000_IFATN_WRITE(emu, ch, 0);
+ EMU8000_PEFE_WRITE(emu, ch, 0);
+ EMU8000_FMMOD_WRITE(emu, ch, 0);
+ EMU8000_TREMFRQ_WRITE(emu, ch, 0);
+ EMU8000_FM2FRQ2_WRITE(emu, ch, 0);
+ EMU8000_PTRX_WRITE(emu, ch, 0);
+ EMU8000_VTFT_WRITE(emu, ch, 0);
+ EMU8000_PSST_WRITE(emu, ch, 0);
+ EMU8000_CSL_WRITE(emu, ch, 0);
+ EMU8000_CCCA_WRITE(emu, ch, 0);
+ }
+
+ for (ch = 0; ch < EMU8000_CHANNELS; ch++) {
+ EMU8000_CPF_WRITE(emu, ch, 0);
+ EMU8000_CVCF_WRITE(emu, ch, 0);
+ }
+}
+
+
+/*
+ * initialize DMA address
+ */
+static void __init
+init_dma(emu8000_t *emu)
+{
+ EMU8000_SMALR_WRITE(emu, 0);
+ EMU8000_SMARR_WRITE(emu, 0);
+ EMU8000_SMALW_WRITE(emu, 0);
+ EMU8000_SMARW_WRITE(emu, 0);
+}
+
+/*
+ * initialization arrays; from ADIP
+ */
+static unsigned short init1[128] /*__devinitdata*/ = {
+ 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330,
+ 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730,
+ 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30,
+ 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30,
+
+ 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330,
+ 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730,
+ 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30,
+ 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30,
+
+ 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330,
+ 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730,
+ 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30,
+ 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30,
+
+ 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330,
+ 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730,
+ 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30,
+ 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,
+};
+
+static unsigned short init2[128] /*__devinitdata*/ = {
+ 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330,
+ 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730,
+ 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30,
+ 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30,
+
+ 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330,
+ 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730,
+ 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30,
+ 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30,
+
+ 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330,
+ 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
+ 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
+ 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
+
+ 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
+ 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
+ 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
+ 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,
+};
+
+static unsigned short init3[128] /*__devinitdata*/ = {
+ 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+ 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254,
+ 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234,
+ 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224,
+
+ 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254,
+ 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264,
+ 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294,
+ 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3,
+
+ 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287,
+ 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7,
+ 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386,
+ 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55,
+
+ 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308,
+ 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F,
+ 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319,
+ 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+static unsigned short init4[128] /*__devinitdata*/ = {
+ 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5,
+ 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254,
+ 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234,
+ 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224,
+
+ 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254,
+ 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264,
+ 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294,
+ 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3,
+
+ 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287,
+ 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7,
+ 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386,
+ 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55,
+
+ 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308,
+ 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F,
+ 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319,
+ 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,
+};
+
+/* send an initialization array
+ * Taken from the oss driver, not obvious from the doc how this
+ * is meant to work
+ */
+static void __init
+send_array(emu8000_t *emu, unsigned short *data, int size)
+{
+ int i;
+ unsigned short *p;
+
+ p = data;
+ for (i = 0; i < size; i++, p++)
+ EMU8000_INIT1_WRITE(emu, i, *p);
+ for (i = 0; i < size; i++, p++)
+ EMU8000_INIT2_WRITE(emu, i, *p);
+ for (i = 0; i < size; i++, p++)
+ EMU8000_INIT3_WRITE(emu, i, *p);
+ for (i = 0; i < size; i++, p++)
+ EMU8000_INIT4_WRITE(emu, i, *p);
+}
+
+
+/*
+ * Send initialization arrays to start up, this just follows the
+ * initialisation sequence in the adip.
+ */
+static void __init
+init_arrays(emu8000_t *emu)
+{
+ send_array(emu, init1, ARRAY_SIZE(init1)/4);
+
+ msleep((1024 * 1000) / 44100); /* wait for 1024 clocks */
+ send_array(emu, init2, ARRAY_SIZE(init2)/4);
+ send_array(emu, init3, ARRAY_SIZE(init3)/4);
+
+ EMU8000_HWCF4_WRITE(emu, 0);
+ EMU8000_HWCF5_WRITE(emu, 0x83);
+ EMU8000_HWCF6_WRITE(emu, 0x8000);
+
+ send_array(emu, init4, ARRAY_SIZE(init4)/4);
+}
+
+
+#define UNIQUE_ID1 0xa5b9
+#define UNIQUE_ID2 0x9d53
+
+/*
+ * Size the onboard memory.
+ * This is written so as not to need arbitary delays after the write. It
+ * seems that the only way to do this is to use the one channel and keep
+ * reallocating between read and write.
+ */
+static void __init
+size_dram(emu8000_t *emu)
+{
+ int i, size;
+
+ if (emu->dram_checked)
+ return;
+
+ size = 0;
+
+ /* write out a magic number */
+ snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);
+ snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ);
+ EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
+ EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
+ snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
+
+ while (size < EMU8000_MAX_DRAM) {
+
+ size += 512 * 1024; /* increment 512kbytes */
+
+ /* Write a unique data on the test address.
+ * if the address is out of range, the data is written on
+ * 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is
+ * changed by this data.
+ */
+ /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/
+ EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
+ EMU8000_SMLD_WRITE(emu, UNIQUE_ID2);
+ snd_emu8000_write_wait(emu);
+
+ /*
+ * read the data on the just written DRAM address
+ * if not the same then we have reached the end of ram.
+ */
+ /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/
+ EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
+ /*snd_emu8000_read_wait(emu);*/
+ EMU8000_SMLD_READ(emu); /* discard stale data */
+ if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2)
+ break; /* we must have wrapped around */
+
+ snd_emu8000_read_wait(emu);
+
+ /*
+ * If it is the same it could be that the address just
+ * wraps back to the beginning; so check to see if the
+ * initial value has been overwritten.
+ */
+ EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
+ EMU8000_SMLD_READ(emu); /* discard stale data */
+ if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1)
+ break; /* we must have wrapped around */
+ snd_emu8000_read_wait(emu);
+ }
+
+ /* wait until FULL bit in SMAxW register is false */
+ for (i = 0; i < 10000; i++) {
+ if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0)
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ if (signal_pending(current))
+ break;
+ }
+ snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE);
+ snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE);
+
+ snd_printdd("EMU8000 [0x%lx]: %d Kb on-board memory detected\n",
+ emu->port1, size/1024);
+
+ emu->mem_size = size;
+ emu->dram_checked = 1;
+}
+
+
+/*
+ * Initiailise the FM section. You have to do this to use sample RAM
+ * and therefore lose 2 voices.
+ */
+/*exported*/ void
+snd_emu8000_init_fm(emu8000_t *emu)
+{
+ unsigned long flags;
+
+ /* Initialize the last two channels for DRAM refresh and producing
+ the reverb and chorus effects for Yamaha OPL-3 synthesizer */
+
+ /* 31: FM left channel, 0xffffe0-0xffffe8 */
+ EMU8000_DCYSUSV_WRITE(emu, 30, 0x80);
+ EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */
+ EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24));
+ EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8));
+ EMU8000_CPF_WRITE(emu, 30, 0);
+ EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3);
+
+ /* 32: FM right channel, 0xfffff0-0xfffff8 */
+ EMU8000_DCYSUSV_WRITE(emu, 31, 0x80);
+ EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */
+ EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24));
+ EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8));
+ EMU8000_CPF_WRITE(emu, 31, 0x8000);
+ EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3);
+
+ snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0);
+
+ spin_lock_irqsave(&emu->reg_lock, flags);
+ while (!(inw(EMU8000_PTR(emu)) & 0x1000))
+ ;
+ while ((inw(EMU8000_PTR(emu)) & 0x1000))
+ ;
+ spin_unlock_irqrestore(&emu->reg_lock, flags);
+ snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828);
+ /* this is really odd part.. */
+ outb(0x3C, EMU8000_PTR(emu));
+ outb(0, EMU8000_DATA1(emu));
+
+ /* skew volume & cutoff */
+ EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF);
+ EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF);
+}
+
+
+/*
+ * The main initialization routine.
+ */
+static void __init
+snd_emu8000_init_hw(emu8000_t *emu)
+{
+ int i;
+
+ emu->last_reg = 0xffff; /* reset the last register index */
+
+ /* initialize hardware configuration */
+ EMU8000_HWCF1_WRITE(emu, 0x0059);
+ EMU8000_HWCF2_WRITE(emu, 0x0020);
+
+ /* disable audio; this seems to reduce a clicking noise a bit.. */
+ EMU8000_HWCF3_WRITE(emu, 0);
+
+ /* initialize audio channels */
+ init_audio(emu);
+
+ /* initialize DMA */
+ init_dma(emu);
+
+ /* initialize init arrays */
+ init_arrays(emu);
+
+ /*
+ * Initialize the FM section of the AWE32, this is needed
+ * for DRAM refresh as well
+ */
+ snd_emu8000_init_fm(emu);
+
+ /* terminate all voices */
+ for (i = 0; i < EMU8000_DRAM_VOICES; i++)
+ EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F);
+
+ /* check DRAM memory size */
+ size_dram(emu);
+
+ /* enable audio */
+ EMU8000_HWCF3_WRITE(emu, 0x4);
+
+ /* set equzlier, chorus and reverb modes */
+ snd_emu8000_update_equalizer(emu);
+ snd_emu8000_update_chorus_mode(emu);
+ snd_emu8000_update_reverb_mode(emu);
+}
+
+
+/*----------------------------------------------------------------
+ * Bass/Treble Equalizer
+ *----------------------------------------------------------------*/
+
+static unsigned short bass_parm[12][3] = {
+ {0xD26A, 0xD36A, 0x0000}, /* -12 dB */
+ {0xD25B, 0xD35B, 0x0000}, /* -8 */
+ {0xD24C, 0xD34C, 0x0000}, /* -6 */
+ {0xD23D, 0xD33D, 0x0000}, /* -4 */
+ {0xD21F, 0xD31F, 0x0000}, /* -2 */
+ {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */
+ {0xC219, 0xC319, 0x0001}, /* +2 */
+ {0xC22A, 0xC32A, 0x0001}, /* +4 */
+ {0xC24C, 0xC34C, 0x0001}, /* +6 */
+ {0xC26E, 0xC36E, 0x0001}, /* +8 */
+ {0xC248, 0xC384, 0x0002}, /* +10 */
+ {0xC26A, 0xC36A, 0x0002}, /* +12 dB */
+};
+
+static unsigned short treble_parm[12][9] = {
+ {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */
+ {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002},
+ {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */
+ {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002},
+ {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002} /* +12 dB */
+};
+
+
+/*
+ * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
+ */
+/*exported*/ void
+snd_emu8000_update_equalizer(emu8000_t *emu)
+{
+ unsigned short w;
+ int bass = emu->bass_level;
+ int treble = emu->treble_level;
+
+ if (bass < 0 || bass > 11 || treble < 0 || treble > 11)
+ return;
+ EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]);
+ EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]);
+ EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]);
+ EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]);
+ EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]);
+ EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]);
+ EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]);
+ EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]);
+ EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]);
+ EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]);
+ w = bass_parm[bass][2] + treble_parm[treble][8];
+ EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262));
+ EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362));
+}
+
+
+/*----------------------------------------------------------------
+ * Chorus mode control
+ *----------------------------------------------------------------*/
+
+/*
+ * chorus mode parameters
+ */
+#define SNDRV_EMU8000_CHORUS_1 0
+#define SNDRV_EMU8000_CHORUS_2 1
+#define SNDRV_EMU8000_CHORUS_3 2
+#define SNDRV_EMU8000_CHORUS_4 3
+#define SNDRV_EMU8000_CHORUS_FEEDBACK 4
+#define SNDRV_EMU8000_CHORUS_FLANGER 5
+#define SNDRV_EMU8000_CHORUS_SHORTDELAY 6
+#define SNDRV_EMU8000_CHORUS_SHORTDELAY2 7
+#define SNDRV_EMU8000_CHORUS_PREDEFINED 8
+/* user can define chorus modes up to 32 */
+#define SNDRV_EMU8000_CHORUS_NUMBERS 32
+
+typedef struct soundfont_chorus_fx_t {
+ unsigned short feedback; /* feedback level (0xE600-0xE6FF) */
+ unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */
+ unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */
+ unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */
+ unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */
+} soundfont_chorus_fx_t;
+
+/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
+static char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
+static soundfont_chorus_fx_t chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = {
+ {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */
+ {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */
+ {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */
+ {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */
+ {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */
+ {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */
+ {0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */
+ {0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */
+};
+
+/*exported*/ int
+snd_emu8000_load_chorus_fx(emu8000_t *emu, int mode, const void __user *buf, long len)
+{
+ soundfont_chorus_fx_t rec;
+ if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) {
+ snd_printk(KERN_WARNING "invalid chorus mode %d for uploading\n", mode);
+ return -EINVAL;
+ }
+ if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
+ return -EFAULT;
+ chorus_parm[mode] = rec;
+ chorus_defined[mode] = 1;
+ return 0;
+}
+
+/*exported*/ void
+snd_emu8000_update_chorus_mode(emu8000_t *emu)
+{
+ int effect = emu->chorus_mode;
+ if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS ||
+ (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect]))
+ return;
+ EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback);
+ EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset);
+ EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth);
+ EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay);
+ EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq);
+ EMU8000_HWCF6_WRITE(emu, 0x8000);
+ EMU8000_HWCF7_WRITE(emu, 0x0000);
+}
+
+/*----------------------------------------------------------------
+ * Reverb mode control
+ *----------------------------------------------------------------*/
+
+/*
+ * reverb mode parameters
+ */
+#define SNDRV_EMU8000_REVERB_ROOM1 0
+#define SNDRV_EMU8000_REVERB_ROOM2 1
+#define SNDRV_EMU8000_REVERB_ROOM3 2
+#define SNDRV_EMU8000_REVERB_HALL1 3
+#define SNDRV_EMU8000_REVERB_HALL2 4
+#define SNDRV_EMU8000_REVERB_PLATE 5
+#define SNDRV_EMU8000_REVERB_DELAY 6
+#define SNDRV_EMU8000_REVERB_PANNINGDELAY 7
+#define SNDRV_EMU8000_REVERB_PREDEFINED 8
+/* user can define reverb modes up to 32 */
+#define SNDRV_EMU8000_REVERB_NUMBERS 32
+
+typedef struct soundfont_reverb_fx_t {
+ unsigned short parms[28];
+} soundfont_reverb_fx_t;
+
+/* reverb mode settings; write the following 28 data of 16 bit length
+ * on the corresponding ports in the reverb_cmds array
+ */
+static char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS];
+static soundfont_reverb_fx_t reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = {
+{{ /* room 1 */
+ 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
+ 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
+ 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+ 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{ /* room 2 */
+ 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+ 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+ 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+ 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{ /* room 3 */
+ 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+ 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
+ 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
+ 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
+}},
+{{ /* hall 1 */
+ 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+ 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+ 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
+ 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
+}},
+{{ /* hall 2 */
+ 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
+ 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
+ 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+ 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{ /* plate */
+ 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
+ 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
+ 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+ 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+}},
+{{ /* delay */
+ 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
+ 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+ 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+ 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+{{ /* panning delay */
+ 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
+ 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+ 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+ 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+}},
+};
+
+enum { DATA1, DATA2 };
+#define AWE_INIT1(c) EMU8000_CMD(2,c), DATA1
+#define AWE_INIT2(c) EMU8000_CMD(2,c), DATA2
+#define AWE_INIT3(c) EMU8000_CMD(3,c), DATA1
+#define AWE_INIT4(c) EMU8000_CMD(3,c), DATA2
+
+static struct reverb_cmd_pair {
+ unsigned short cmd, port;
+} reverb_cmds[28] = {
+ {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
+ {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
+ {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
+ {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
+ {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
+ {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
+ {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
+};
+
+/*exported*/ int
+snd_emu8000_load_reverb_fx(emu8000_t *emu, int mode, const void __user *buf, long len)
+{
+ soundfont_reverb_fx_t rec;
+
+ if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) {
+ snd_printk(KERN_WARNING "invalid reverb mode %d for uploading\n", mode);
+ return -EINVAL;
+ }
+ if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec)))
+ return -EFAULT;
+ reverb_parm[mode] = rec;
+ reverb_defined[mode] = 1;
+ return 0;
+}
+
+/*exported*/ void
+snd_emu8000_update_reverb_mode(emu8000_t *emu)
+{
+ int effect = emu->reverb_mode;
+ int i;
+
+ if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS ||
+ (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect]))
+ return;
+ for (i = 0; i < 28; i++) {
+ int port;
+ if (reverb_cmds[i].port == DATA1)
+ port = EMU8000_DATA1(emu);
+ else
+ port = EMU8000_DATA2(emu);
+ snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]);
+ }
+}
+
+
+/*----------------------------------------------------------------
+ * mixer interface
+ *----------------------------------------------------------------*/
+
+/*
+ * bass/treble
+ */
+static int mixer_bass_treble_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 11;
+ return 0;
+}
+
+static int mixer_bass_treble_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level;
+ return 0;
+}
+
+static int mixer_bass_treble_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int change;
+ unsigned short val1;
+
+ val1 = ucontrol->value.integer.value[0] % 12;
+ spin_lock_irqsave(&emu->control_lock, flags);
+ if (kcontrol->private_value) {
+ change = val1 != emu->treble_level;
+ emu->treble_level = val1;
+ } else {
+ change = val1 != emu->bass_level;
+ emu->bass_level = val1;
+ }
+ spin_unlock_irqrestore(&emu->control_lock, flags);
+ snd_emu8000_update_equalizer(emu);
+ return change;
+}
+
+static snd_kcontrol_new_t mixer_bass_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Synth Tone Control - Bass",
+ .info = mixer_bass_treble_info,
+ .get = mixer_bass_treble_get,
+ .put = mixer_bass_treble_put,
+ .private_value = 0,
+};
+
+static snd_kcontrol_new_t mixer_treble_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Synth Tone Control - Treble",
+ .info = mixer_bass_treble_info,
+ .get = mixer_bass_treble_get,
+ .put = mixer_bass_treble_put,
+ .private_value = 1,
+};
+
+/*
+ * chorus/reverb mode
+ */
+static int mixer_chorus_reverb_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1);
+ return 0;
+}
+
+static int mixer_chorus_reverb_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode;
+ return 0;
+}
+
+static int mixer_chorus_reverb_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int change;
+ unsigned short val1;
+
+ spin_lock_irqsave(&emu->control_lock, flags);
+ if (kcontrol->private_value) {
+ val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS;
+ change = val1 != emu->chorus_mode;
+ emu->chorus_mode = val1;
+ } else {
+ val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS;
+ change = val1 != emu->reverb_mode;
+ emu->reverb_mode = val1;
+ }
+ spin_unlock_irqrestore(&emu->control_lock, flags);
+ if (change) {
+ if (kcontrol->private_value)
+ snd_emu8000_update_chorus_mode(emu);
+ else
+ snd_emu8000_update_reverb_mode(emu);
+ }
+ return change;
+}
+
+static snd_kcontrol_new_t mixer_chorus_mode_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Chorus Mode",
+ .info = mixer_chorus_reverb_info,
+ .get = mixer_chorus_reverb_get,
+ .put = mixer_chorus_reverb_put,
+ .private_value = 1,
+};
+
+static snd_kcontrol_new_t mixer_reverb_mode_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Reverb Mode",
+ .info = mixer_chorus_reverb_info,
+ .get = mixer_chorus_reverb_get,
+ .put = mixer_chorus_reverb_put,
+ .private_value = 0,
+};
+
+/*
+ * FM OPL3 chorus/reverb depth
+ */
+static int mixer_fm_depth_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 255;
+ return 0;
+}
+
+static int mixer_fm_depth_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth;
+ return 0;
+}
+
+static int mixer_fm_depth_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ emu8000_t *emu = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int change;
+ unsigned short val1;
+
+ val1 = ucontrol->value.integer.value[0] % 256;
+ spin_lock_irqsave(&emu->control_lock, flags);
+ if (kcontrol->private_value) {
+ change = val1 != emu->fm_chorus_depth;
+ emu->fm_chorus_depth = val1;
+ } else {
+ change = val1 != emu->fm_reverb_depth;
+ emu->fm_reverb_depth = val1;
+ }
+ spin_unlock_irqrestore(&emu->control_lock, flags);
+ if (change)
+ snd_emu8000_init_fm(emu);
+ return change;
+}
+
+static snd_kcontrol_new_t mixer_fm_chorus_depth_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "FM Chorus Depth",
+ .info = mixer_fm_depth_info,
+ .get = mixer_fm_depth_get,
+ .put = mixer_fm_depth_put,
+ .private_value = 1,
+};
+
+static snd_kcontrol_new_t mixer_fm_reverb_depth_control =
+{
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "FM Reverb Depth",
+ .info = mixer_fm_depth_info,
+ .get = mixer_fm_depth_get,
+ .put = mixer_fm_depth_put,
+ .private_value = 0,
+};
+
+
+static snd_kcontrol_new_t *mixer_defs[EMU8000_NUM_CONTROLS] = {
+ &mixer_bass_control,
+ &mixer_treble_control,
+ &mixer_chorus_mode_control,
+ &mixer_reverb_mode_control,
+ &mixer_fm_chorus_depth_control,
+ &mixer_fm_reverb_depth_control,
+};
+
+/*
+ * create and attach mixer elements for WaveTable treble/bass controls
+ */
+static int __init
+snd_emu8000_create_mixer(snd_card_t *card, emu8000_t *emu)
+{
+ int i, err = 0;
+
+ snd_assert(emu != NULL && card != NULL, return -EINVAL);
+
+ spin_lock_init(&emu->control_lock);
+
+ memset(emu->controls, 0, sizeof(emu->controls));
+ for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
+ if ((err = snd_ctl_add(card, emu->controls[i] = snd_ctl_new1(mixer_defs[i], emu))) < 0)
+ goto __error;
+ }
+ return 0;
+
+__error:
+ for (i = 0; i < EMU8000_NUM_CONTROLS; i++) {
+ down_write(&card->controls_rwsem);
+ if (emu->controls[i])
+ snd_ctl_remove(card, emu->controls[i]);
+ up_write(&card->controls_rwsem);
+ }
+ return err;
+}
+
+
+/*
+ * free resources
+ */
+static int snd_emu8000_free(emu8000_t *hw)
+{
+ if (hw->res_port1) {
+ release_resource(hw->res_port1);
+ kfree_nocheck(hw->res_port1);
+ }
+ if (hw->res_port2) {
+ release_resource(hw->res_port2);
+ kfree_nocheck(hw->res_port2);
+ }
+ if (hw->res_port3) {
+ release_resource(hw->res_port3);
+ kfree_nocheck(hw->res_port3);
+ }
+ kfree(hw);
+ return 0;
+}
+
+/*
+ */
+static int snd_emu8000_dev_free(snd_device_t *device)
+{
+ emu8000_t *hw = device->device_data;
+ return snd_emu8000_free(hw);
+}
+
+/*
+ * initialize and register emu8000 synth device.
+ */
+int __init
+snd_emu8000_new(snd_card_t *card, int index, long port, int seq_ports, snd_seq_device_t **awe_ret)
+{
+ snd_seq_device_t *awe;
+ emu8000_t *hw;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_emu8000_dev_free,
+ };
+
+ if (awe_ret)
+ *awe_ret = NULL;
+
+ if (seq_ports <= 0)
+ return 0;
+
+ hw = kcalloc(1, sizeof(*hw), GFP_KERNEL);
+ if (hw == NULL)
+ return -ENOMEM;
+ spin_lock_init(&hw->reg_lock);
+ hw->index = index;
+ hw->port1 = port;
+ hw->port2 = port + 0x400;
+ hw->port3 = port + 0x800;
+ if (!(hw->res_port1 = request_region(hw->port1, 4, "Emu8000-1")) ||
+ !(hw->res_port2 = request_region(hw->port2, 4, "Emu8000-2")) ||
+ !(hw->res_port3 = request_region(hw->port3, 4, "Emu8000-3"))) {
+ snd_printk(KERN_ERR "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n", hw->port1, hw->port2, hw->port3);
+ snd_emu8000_free(hw);
+ return -EBUSY;
+ }
+ hw->mem_size = 0;
+ hw->card = card;
+ hw->seq_ports = seq_ports;
+ hw->bass_level = 5;
+ hw->treble_level = 9;
+ hw->chorus_mode = 2;
+ hw->reverb_mode = 4;
+ hw->fm_chorus_depth = 0;
+ hw->fm_reverb_depth = 0;
+
+ if (snd_emu8000_detect(hw) < 0) {
+ snd_emu8000_free(hw);
+ return -ENODEV;
+ }
+
+ snd_emu8000_init_hw(hw);
+ if ((err = snd_emu8000_create_mixer(card, hw)) < 0) {
+ snd_emu8000_free(hw);
+ return err;
+ }
+
+ if ((err = snd_device_new(card, SNDRV_DEV_CODEC, hw, &ops)) < 0) {
+ snd_emu8000_free(hw);
+ return err;
+ }
+#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
+ if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000,
+ sizeof(emu8000_t*), &awe) >= 0) {
+ strcpy(awe->name, "EMU-8000");
+ *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw;
+ }
+#else
+ awe = NULL;
+#endif
+ if (awe_ret)
+ *awe_ret = awe;
+
+ return 0;
+}
+
+
+/*
+ * exported stuff
+ */
+
+EXPORT_SYMBOL(snd_emu8000_poke);
+EXPORT_SYMBOL(snd_emu8000_peek);
+EXPORT_SYMBOL(snd_emu8000_poke_dw);
+EXPORT_SYMBOL(snd_emu8000_peek_dw);
+EXPORT_SYMBOL(snd_emu8000_dma_chan);
+EXPORT_SYMBOL(snd_emu8000_init_fm);
+EXPORT_SYMBOL(snd_emu8000_load_chorus_fx);
+EXPORT_SYMBOL(snd_emu8000_load_reverb_fx);
+EXPORT_SYMBOL(snd_emu8000_update_chorus_mode);
+EXPORT_SYMBOL(snd_emu8000_update_reverb_mode);
+EXPORT_SYMBOL(snd_emu8000_update_equalizer);
diff --git a/sound/isa/sb/emu8000_callback.c b/sound/isa/sb/emu8000_callback.c
new file mode 100644
index 000000000000..1cc4101a17a4
--- /dev/null
+++ b/sound/isa/sb/emu8000_callback.c
@@ -0,0 +1,543 @@
+/*
+ * synth callback routines for the emu8000 (AWE32/64)
+ *
+ * Copyright (C) 1999 Steve Ratcliffe
+ * Copyright (C) 1999-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 "emu8000_local.h"
+#include <sound/asoundef.h>
+
+/*
+ * prototypes
+ */
+static snd_emux_voice_t *get_voice(snd_emux_t *emu, snd_emux_port_t *port);
+static int start_voice(snd_emux_voice_t *vp);
+static void trigger_voice(snd_emux_voice_t *vp);
+static void release_voice(snd_emux_voice_t *vp);
+static void update_voice(snd_emux_voice_t *vp, int update);
+static void reset_voice(snd_emux_t *emu, int ch);
+static void terminate_voice(snd_emux_voice_t *vp);
+static void sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset);
+#ifdef CONFIG_SND_SEQUENCER_OSS
+static int oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2);
+#endif
+static int load_fx(snd_emux_t *emu, int type, int mode, const void __user *buf, long len);
+
+static void set_pitch(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_volume(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_pan(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_fmmod(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *vp);
+static void set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp);
+static void snd_emu8000_tweak_voice(emu8000_t *emu, int ch);
+
+/*
+ * 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 snd_emux_operators_t emu8000_ops = {
+ .owner = THIS_MODULE,
+ .get_voice = get_voice,
+ .prepare = start_voice,
+ .trigger = trigger_voice,
+ .release = release_voice,
+ .update = update_voice,
+ .terminate = terminate_voice,
+ .reset = reset_voice,
+ .sample_new = snd_emu8000_sample_new,
+ .sample_free = snd_emu8000_sample_free,
+ .sample_reset = snd_emu8000_sample_reset,
+ .load_fx = load_fx,
+ .sysex = sysex,
+#ifdef CONFIG_SND_SEQUENCER_OSS
+ .oss_ioctl = oss_ioctl,
+#endif
+};
+
+void
+snd_emu8000_ops_setup(emu8000_t *hw)
+{
+ hw->emu->ops = emu8000_ops;
+}
+
+
+
+/*
+ * Terminate a voice
+ */
+static void
+release_voice(snd_emux_voice_t *vp)
+{
+ int dcysusv;
+ emu8000_t *hw;
+
+ hw = vp->hw;
+ dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
+ EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv);
+ dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease;
+ EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv);
+}
+
+
+/*
+ */
+static void
+terminate_voice(snd_emux_voice_t *vp)
+{
+ emu8000_t *hw;
+
+ hw = vp->hw;
+ EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F);
+}
+
+
+/*
+ */
+static void
+update_voice(snd_emux_voice_t *vp, int update)
+{
+ emu8000_t *hw;
+
+ hw = vp->hw;
+ if (update & SNDRV_EMUX_UPDATE_VOLUME)
+ set_volume(hw, vp);
+ if (update & SNDRV_EMUX_UPDATE_PITCH)
+ set_pitch(hw, vp);
+ if ((update & SNDRV_EMUX_UPDATE_PAN) &&
+ vp->port->ctrls[EMUX_MD_REALTIME_PAN])
+ set_pan(hw, vp);
+ if (update & SNDRV_EMUX_UPDATE_FMMOD)
+ set_fmmod(hw, vp);
+ if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
+ set_tremfreq(hw, vp);
+ if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
+ set_fm2frq2(hw, vp);
+ if (update & SNDRV_EMUX_UPDATE_Q)
+ set_filterQ(hw, vp);
+}
+
+
+/*
+ * Find a channel (voice) within the EMU that is not in use or at least
+ * less in use than other channels. Always returns a valid pointer
+ * no matter what. If there is a real shortage of voices then one
+ * will be cut. Such is life.
+ *
+ * The channel index (vp->ch) must be initialized in this routine.
+ * In Emu8k, it is identical with the array index.
+ */
+static snd_emux_voice_t *
+get_voice(snd_emux_t *emu, snd_emux_port_t *port)
+{
+ int i;
+ snd_emux_voice_t *vp;
+ emu8000_t *hw;
+
+ /* what we are looking for, in order of preference */
+ enum {
+ OFF=0, RELEASED, PLAYING, END
+ };
+
+ /* Keeps track of what we are finding */
+ struct best {
+ unsigned int time;
+ int voice;
+ } best[END];
+ struct best *bp;
+
+ hw = emu->hw;
+
+ for (i = 0; i < 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.
+ */
+ for (i = 0; i < emu->max_voices; i++) {
+ int state, val;
+
+ vp = &emu->voices[i];
+ state = vp->state;
+
+ if (state == SNDRV_EMUX_ST_OFF)
+ bp = best + OFF;
+ else if (state == SNDRV_EMUX_ST_RELEASED ||
+ state == SNDRV_EMUX_ST_PENDING) {
+ bp = best + RELEASED;
+ val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff;
+ if (! val)
+ bp = best + OFF;
+ }
+ else if (state & SNDRV_EMUX_ST_ON)
+ bp = best + PLAYING;
+ else
+ continue;
+
+ /* check if sample is finished playing (non-looping only) */
+ if (state != SNDRV_EMUX_ST_OFF &&
+ (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
+ val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
+ if (val >= vp->reg.loopstart)
+ bp = best + OFF;
+ }
+
+ if (vp->time < bp->time) {
+ bp->time = vp->time;
+ bp->voice = i;
+ }
+ }
+
+ for (i = 0; i < END; i++) {
+ if (best[i].voice >= 0) {
+ vp = &emu->voices[best[i].voice];
+ vp->ch = best[i].voice;
+ return vp;
+ }
+ }
+
+ /* not found */
+ return NULL;
+}
+
+/*
+ */
+static int
+start_voice(snd_emux_voice_t *vp)
+{
+ unsigned int temp;
+ int ch;
+ int addr;
+ snd_midi_channel_t *chan;
+ emu8000_t *hw;
+
+ hw = vp->hw;
+ ch = vp->ch;
+ chan = vp->chan;
+
+ /* channel to be silent and idle */
+ EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);
+ EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);
+ EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);
+ EMU8000_PTRX_WRITE(hw, ch, 0);
+ EMU8000_CPF_WRITE(hw, ch, 0);
+
+ /* set pitch offset */
+ set_pitch(hw, vp);
+
+ /* set envelope parameters */
+ EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay);
+ EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld);
+ EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus);
+ EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay);
+ EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld);
+ /* decay/sustain parameter for volume envelope is used
+ for triggerg the voice */
+
+ /* cutoff and volume */
+ set_volume(hw, vp);
+
+ /* modulation envelope heights */
+ EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe);
+
+ /* lfo1/2 delay */
+ EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay);
+ EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay);
+
+ /* lfo1 pitch & cutoff shift */
+ set_fmmod(hw, vp);
+ /* lfo1 volume & freq */
+ set_tremfreq(hw, vp);
+ /* lfo2 pitch & freq */
+ set_fm2frq2(hw, vp);
+ /* pan & loop start */
+ set_pan(hw, vp);
+
+ /* chorus & loop end (chorus 8bit, MSB) */
+ addr = vp->reg.loopend - 1;
+ temp = vp->reg.parm.chorus;
+ temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
+ LIMITMAX(temp, 255);
+ temp = (temp <<24) | (unsigned int)addr;
+ EMU8000_CSL_WRITE(hw, ch, temp);
+
+ /* Q & current address (Q 4bit value, MSB) */
+ addr = vp->reg.start - 1;
+ temp = vp->reg.parm.filterQ;
+ temp = (temp<<28) | (unsigned int)addr;
+ EMU8000_CCCA_WRITE(hw, ch, temp);
+
+ /* clear unknown registers */
+ EMU8000_00A0_WRITE(hw, ch, 0);
+ EMU8000_0080_WRITE(hw, ch, 0);
+
+ /* reset volume */
+ temp = vp->vtarget << 16;
+ EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget);
+ EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00);
+
+ return 0;
+}
+
+/*
+ * Start envelope
+ */
+static void
+trigger_voice(snd_emux_voice_t *vp)
+{
+ int ch = vp->ch;
+ unsigned int temp;
+ emu8000_t *hw;
+
+ hw = vp->hw;
+
+ /* set reverb and pitch target */
+ temp = vp->reg.parm.reverb;
+ temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
+ LIMITMAX(temp, 255);
+ temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux;
+ EMU8000_PTRX_WRITE(hw, ch, temp);
+ EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16);
+ EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus);
+}
+
+/*
+ * reset voice parameters
+ */
+static void
+reset_voice(snd_emux_t *emu, int ch)
+{
+ emu8000_t *hw;
+
+ hw = emu->hw;
+ EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
+ snd_emu8000_tweak_voice(hw, ch);
+}
+
+/*
+ * Set the pitch of a possibly playing note.
+ */
+static void
+set_pitch(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+ EMU8000_IP_WRITE(hw, vp->ch, vp->apitch);
+}
+
+/*
+ * Set the volume of a possibly already playing note
+ */
+static void
+set_volume(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+ int ifatn;
+
+ ifatn = (unsigned char)vp->acutoff;
+ ifatn = (ifatn << 8);
+ ifatn |= (unsigned char)vp->avol;
+ EMU8000_IFATN_WRITE(hw, vp->ch, ifatn);
+}
+
+/*
+ * Set pan and loop start address.
+ */
+static void
+set_pan(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+ unsigned int temp;
+
+ temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1);
+ EMU8000_PSST_WRITE(hw, vp->ch, temp);
+}
+
+#define MOD_SENSE 18
+
+static void
+set_fmmod(emu8000_t *hw, snd_emux_voice_t *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;
+ EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod);
+}
+
+/* set tremolo (lfo1) volume & frequency */
+static void
+set_tremfreq(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+ EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq);
+}
+
+/* set lfo2 pitch & frequency */
+static void
+set_fm2frq2(emu8000_t *hw, snd_emux_voice_t *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;
+ EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2);
+}
+
+/* set filterQ */
+static void
+set_filterQ(emu8000_t *hw, snd_emux_voice_t *vp)
+{
+ unsigned int addr;
+ addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
+ addr |= (vp->reg.parm.filterQ << 28);
+ EMU8000_CCCA_WRITE(hw, vp->ch, addr);
+}
+
+/*
+ * set the envelope & LFO parameters to the default values
+ */
+static void
+snd_emu8000_tweak_voice(emu8000_t *emu, int i)
+{
+ /* set all mod/vol envelope shape to minimum */
+ EMU8000_ENVVOL_WRITE(emu, i, 0x8000);
+ EMU8000_ENVVAL_WRITE(emu, i, 0x8000);
+ EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F);
+ EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F);
+ EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F);
+ EMU8000_PEFE_WRITE(emu, i, 0); /* mod envelope height to zero */
+ EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */
+ EMU8000_LFO2VAL_WRITE(emu, i, 0x8000);
+ EMU8000_IP_WRITE(emu, i, 0xE000); /* no pitch shift */
+ EMU8000_IFATN_WRITE(emu, i, 0xFF00); /* volume to minimum */
+ EMU8000_FMMOD_WRITE(emu, i, 0);
+ EMU8000_TREMFRQ_WRITE(emu, i, 0);
+ EMU8000_FM2FRQ2_WRITE(emu, i, 0);
+}
+
+/*
+ * sysex callback
+ */
+static void
+sysex(snd_emux_t *emu, char *buf, int len, int parsed, snd_midi_channel_set_t *chset)
+{
+ emu8000_t *hw;
+
+ hw = emu->hw;
+
+ switch (parsed) {
+ case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE:
+ hw->chorus_mode = chset->gs_chorus_mode;
+ snd_emu8000_update_chorus_mode(hw);
+ break;
+
+ case SNDRV_MIDI_SYSEX_GS_REVERB_MODE:
+ hw->reverb_mode = chset->gs_reverb_mode;
+ snd_emu8000_update_reverb_mode(hw);
+ break;
+ }
+}
+
+
+#ifdef CONFIG_SND_SEQUENCER_OSS
+/*
+ * OSS ioctl callback
+ */
+static int
+oss_ioctl(snd_emux_t *emu, int cmd, int p1, int p2)
+{
+ emu8000_t *hw;
+
+ hw = emu->hw;
+
+ switch (cmd) {
+ case _EMUX_OSS_REVERB_MODE:
+ hw->reverb_mode = p1;
+ snd_emu8000_update_reverb_mode(hw);
+ break;
+
+ case _EMUX_OSS_CHORUS_MODE:
+ hw->chorus_mode = p1;
+ snd_emu8000_update_chorus_mode(hw);
+ break;
+
+ case _EMUX_OSS_INITIALIZE_CHIP:
+ /* snd_emu8000_init(hw); */ /*ignored*/
+ break;
+
+ case _EMUX_OSS_EQUALIZER:
+ hw->bass_level = p1;
+ hw->treble_level = p2;
+ snd_emu8000_update_equalizer(hw);
+ break;
+ }
+ return 0;
+}
+#endif
+
+
+/*
+ * additional patch keys
+ */
+
+#define SNDRV_EMU8000_LOAD_CHORUS_FX 0x10 /* optarg=mode */
+#define SNDRV_EMU8000_LOAD_REVERB_FX 0x11 /* optarg=mode */
+
+
+/*
+ * callback routine
+ */
+
+static int
+load_fx(snd_emux_t *emu, int type, int mode, const void __user *buf, long len)
+{
+ emu8000_t *hw;
+ hw = emu->hw;
+
+ /* skip header */
+ buf += 16;
+ len -= 16;
+
+ switch (type) {
+ case SNDRV_EMU8000_LOAD_CHORUS_FX:
+ return snd_emu8000_load_chorus_fx(hw, mode, buf, len);
+ case SNDRV_EMU8000_LOAD_REVERB_FX:
+ return snd_emu8000_load_reverb_fx(hw, mode, buf, len);
+ }
+ return -EINVAL;
+}
+
diff --git a/sound/isa/sb/emu8000_local.h b/sound/isa/sb/emu8000_local.h
new file mode 100644
index 000000000000..ea4996a895fc
--- /dev/null
+++ b/sound/isa/sb/emu8000_local.h
@@ -0,0 +1,43 @@
+#ifndef __EMU8000_LOCAL_H
+#define __EMU8000_LOCAL_H
+/*
+ * Local defininitons for the emu8000 (AWE32/64)
+ *
+ * Copyright (C) 1999 Steve Ratcliffe
+ * Copyright (C) 1999-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 <sound/driver.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/emu8000.h>
+#include <sound/emu8000_reg.h>
+
+/* emu8000_patch.c */
+int snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void __user *data, long count);
+int snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr);
+void snd_emu8000_sample_reset(snd_emux_t *rec);
+
+/* emu8000_callback.c */
+void snd_emu8000_ops_setup(emu8000_t *emu);
+
+/* emu8000_pcm.c */
+int snd_emu8000_pcm_new(snd_card_t *card, emu8000_t *emu, int index);
+
+#endif /* __EMU8000_LOCAL_H */
diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c
new file mode 100644
index 000000000000..4afc4a1bc140
--- /dev/null
+++ b/sound/isa/sb/emu8000_patch.c
@@ -0,0 +1,303 @@
+/*
+ * Patch routines for the emu8000 (AWE32/64)
+ *
+ * Copyright (C) 1999 Steve Ratcliffe
+ * Copyright (C) 1999-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 "emu8000_local.h"
+#include <asm/uaccess.h>
+#include <linux/moduleparam.h>
+
+static int emu8000_reset_addr = 0;
+module_param(emu8000_reset_addr, int, 0444);
+MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)");
+
+
+/*
+ * Open up channels.
+ */
+static int
+snd_emu8000_open_dma(emu8000_t *emu, int write)
+{
+ int i;
+
+ /* reserve all 30 voices for loading */
+ for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
+ snd_emux_lock_voice(emu->emu, i);
+ snd_emu8000_dma_chan(emu, i, write);
+ }
+
+ /* assign voice 31 and 32 to ROM */
+ EMU8000_VTFT_WRITE(emu, 30, 0);
+ EMU8000_PSST_WRITE(emu, 30, 0x1d8);
+ EMU8000_CSL_WRITE(emu, 30, 0x1e0);
+ EMU8000_CCCA_WRITE(emu, 30, 0x1d8);
+ EMU8000_VTFT_WRITE(emu, 31, 0);
+ EMU8000_PSST_WRITE(emu, 31, 0x1d8);
+ EMU8000_CSL_WRITE(emu, 31, 0x1e0);
+ EMU8000_CCCA_WRITE(emu, 31, 0x1d8);
+
+ return 0;
+}
+
+/*
+ * Close all dram channels.
+ */
+static void
+snd_emu8000_close_dma(emu8000_t *emu)
+{
+ int i;
+
+ for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
+ snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE);
+ snd_emux_unlock_voice(emu->emu, i);
+ }
+}
+
+/*
+ */
+
+#define BLANK_LOOP_START 4
+#define BLANK_LOOP_END 8
+#define BLANK_LOOP_SIZE 12
+#define BLANK_HEAD_SIZE 48
+
+/*
+ * Read a word from userland, taking care of conversions from
+ * 8bit samples etc.
+ */
+static unsigned short
+read_word(const void __user *buf, int offset, int mode)
+{
+ unsigned short c;
+ if (mode & SNDRV_SFNT_SAMPLE_8BITS) {
+ unsigned char cc;
+ get_user(cc, (unsigned char __user *)buf + offset);
+ c = cc << 8; /* convert 8bit -> 16bit */
+ } else {
+#ifdef SNDRV_LITTLE_ENDIAN
+ get_user(c, (unsigned short __user *)buf + offset);
+#else
+ unsigned short cc;
+ get_user(cc, (unsigned short __user *)buf + offset);
+ c = swab16(cc);
+#endif
+ }
+ if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED)
+ c ^= 0x8000; /* unsigned -> signed */
+ return c;
+}
+
+/*
+ */
+static void
+snd_emu8000_write_wait(emu8000_t *emu)
+{
+ while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ if (signal_pending(current))
+ break;
+ }
+}
+
+/*
+ * write sample word data
+ *
+ * You should not have to keep resetting the address each time
+ * as the chip is supposed to step on the next address automatically.
+ * It mostly does, but during writes of some samples at random it
+ * completely loses words (every one in 16 roughly but with no
+ * obvious pattern).
+ *
+ * This is therefore much slower than need be, but is at least
+ * working.
+ */
+inline static void
+write_word(emu8000_t *emu, int *offset, unsigned short data)
+{
+ if (emu8000_reset_addr) {
+ if (emu8000_reset_addr > 1)
+ snd_emu8000_write_wait(emu);
+ EMU8000_SMALW_WRITE(emu, *offset);
+ }
+ EMU8000_SMLD_WRITE(emu, data);
+ *offset += 1;
+}
+
+/*
+ * Write the sample to EMU800 memory. This routine is invoked out of
+ * the generic soundfont routines as a callback.
+ */
+int
+snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp,
+ snd_util_memhdr_t *hdr, const void __user *data, long count)
+{
+ int i;
+ int rc;
+ int offset;
+ int truesize;
+ int dram_offset, dram_start;
+ emu8000_t *emu;
+
+ emu = rec->hw;
+ snd_assert(sp != NULL, return -EINVAL);
+
+ if (sp->v.size == 0)
+ return 0;
+
+ /* 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;
+ if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
+ truesize += sp->v.loopend - sp->v.loopstart;
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
+ truesize += BLANK_LOOP_SIZE;
+
+ sp->block = snd_util_mem_alloc(hdr, truesize * 2);
+ if (sp->block == NULL) {
+ /*snd_printd("EMU8000: out of memory\n");*/
+ /* not ENOMEM (for compatibility) */
+ return -ENOSPC;
+ }
+
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
+ if (!access_ok(VERIFY_READ, data, sp->v.size))
+ return -EFAULT;
+ } else {
+ if (!access_ok(VERIFY_READ, data, sp->v.size * 2))
+ return -EFAULT;
+ }
+
+ /* 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;
+
+ /* dram position (in word) -- mem_offset is byte */
+ dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
+ dram_start = dram_offset;
+
+ /* set the total size (store onto obsolete checksum value) */
+ sp->v.truesize = truesize * 2; /* in bytes */
+
+ snd_emux_terminate_all(emu->emu);
+ if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0)
+ return rc;
+
+ /* Set the address to start writing at */
+ snd_emu8000_write_wait(emu);
+ EMU8000_SMALW_WRITE(emu, dram_offset);
+
+ /*snd_emu8000_init_fm(emu);*/
+
+#if 0
+ /* first block - write 48 samples for silence */
+ if (! sp->block->offset) {
+ for (i = 0; i < BLANK_HEAD_SIZE; i++) {
+ write_word(emu, &dram_offset, 0);
+ }
+ }
+#endif
+
+ offset = 0;
+ for (i = 0; i < sp->v.size; i++) {
+ unsigned short s;
+
+ s = read_word(data, offset, sp->v.mode_flags);
+ offset++;
+ write_word(emu, &dram_offset, s);
+
+ /* we may take too long time in this loop.
+ * so give controls back to kernel if needed.
+ */
+ cond_resched();
+
+ if (i == sp->v.loopend &&
+ (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)))
+ {
+ int looplen = sp->v.loopend - sp->v.loopstart;
+ int k;
+
+ /* copy reverse loop */
+ for (k = 1; k <= looplen; k++) {
+ s = read_word(data, offset - k, sp->v.mode_flags);
+ write_word(emu, &dram_offset, s);
+ }
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
+ sp->v.loopend += looplen;
+ } else {
+ sp->v.loopstart += looplen;
+ sp->v.loopend += looplen;
+ }
+ sp->v.end += looplen;
+ }
+ }
+
+ /* if no blank loop is attached in the sample, add it */
+ if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
+ for (i = 0; i < BLANK_LOOP_SIZE; i++) {
+ write_word(emu, &dram_offset, 0);
+ }
+ 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;
+ }
+ }
+
+ /* add dram offset */
+ sp->v.start += dram_start;
+ sp->v.end += dram_start;
+ sp->v.loopstart += dram_start;
+ sp->v.loopend += dram_start;
+
+ snd_emu8000_close_dma(emu);
+ snd_emu8000_init_fm(emu);
+
+ return 0;
+}
+
+/*
+ * free a sample block
+ */
+int
+snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr)
+{
+ if (sp->block) {
+ snd_util_mem_free(hdr, sp->block);
+ sp->block = NULL;
+ }
+ return 0;
+}
+
+
+/*
+ * sample_reset callback - terminate voices
+ */
+void
+snd_emu8000_sample_reset(snd_emux_t *rec)
+{
+ snd_emux_terminate_all(rec);
+}
diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c
new file mode 100644
index 000000000000..db5eb8b55058
--- /dev/null
+++ b/sound/isa/sb/emu8000_pcm.c
@@ -0,0 +1,704 @@
+/*
+ * pcm emulation on emu8000 wavetable
+ *
+ * Copyright (C) 2002 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 "emu8000_local.h"
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+
+/*
+ * define the following if you want to use this pcm with non-interleaved mode
+ */
+/* #define USE_NONINTERLEAVE */
+
+/* NOTE: for using the non-interleaved mode with alsa-lib, you have to set
+ * mmap_emulation flag to 1 in your .asoundrc, such like
+ *
+ * pcm.emu8k {
+ * type plug
+ * slave.pcm {
+ * type hw
+ * card 0
+ * device 1
+ * mmap_emulation 1
+ * }
+ * }
+ *
+ * besides, for the time being, the non-interleaved mode doesn't work well on
+ * alsa-lib...
+ */
+
+
+typedef struct snd_emu8k_pcm emu8k_pcm_t;
+
+struct snd_emu8k_pcm {
+ emu8000_t *emu;
+ snd_pcm_substream_t *substream;
+
+ unsigned int allocated_bytes;
+ snd_util_memblk_t *block;
+ unsigned int offset;
+ unsigned int buf_size;
+ unsigned int period_size;
+ unsigned int loop_start[2];
+ unsigned int pitch;
+ int panning[2];
+ int last_ptr;
+ int period_pos;
+ int voices;
+ unsigned int dram_opened: 1;
+ unsigned int running: 1;
+ unsigned int timer_running: 1;
+ struct timer_list timer;
+ spinlock_t timer_lock;
+};
+
+#define LOOP_BLANK_SIZE 8
+
+
+/*
+ * open up channels for the simultaneous data transfer and playback
+ */
+static int
+emu8k_open_dram_for_pcm(emu8000_t *emu, int channels)
+{
+ int i;
+
+ /* reserve up to 2 voices for playback */
+ snd_emux_lock_voice(emu->emu, 0);
+ if (channels > 1)
+ snd_emux_lock_voice(emu->emu, 1);
+
+ /* reserve 28 voices for loading */
+ for (i = channels + 1; i < EMU8000_DRAM_VOICES; i++) {
+ unsigned int mode = EMU8000_RAM_WRITE;
+ snd_emux_lock_voice(emu->emu, i);
+#ifndef USE_NONINTERLEAVE
+ if (channels > 1 && (i & 1) != 0)
+ mode |= EMU8000_RAM_RIGHT;
+#endif
+ snd_emu8000_dma_chan(emu, i, mode);
+ }
+
+ /* assign voice 31 and 32 to ROM */
+ EMU8000_VTFT_WRITE(emu, 30, 0);
+ EMU8000_PSST_WRITE(emu, 30, 0x1d8);
+ EMU8000_CSL_WRITE(emu, 30, 0x1e0);
+ EMU8000_CCCA_WRITE(emu, 30, 0x1d8);
+ EMU8000_VTFT_WRITE(emu, 31, 0);
+ EMU8000_PSST_WRITE(emu, 31, 0x1d8);
+ EMU8000_CSL_WRITE(emu, 31, 0x1e0);
+ EMU8000_CCCA_WRITE(emu, 31, 0x1d8);
+
+ return 0;
+}
+
+/*
+ */
+static void
+snd_emu8000_write_wait(emu8000_t *emu, int can_schedule)
+{
+ while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
+ if (can_schedule) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ if (signal_pending(current))
+ break;
+ }
+ }
+}
+
+/*
+ * close all channels
+ */
+static void
+emu8k_close_dram(emu8000_t *emu)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ snd_emux_unlock_voice(emu->emu, i);
+ for (; i < EMU8000_DRAM_VOICES; i++) {
+ snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE);
+ snd_emux_unlock_voice(emu->emu, i);
+ }
+}
+
+/*
+ * convert Hz to AWE32 rate offset (see emux/soundfont.c)
+ */
+
+#define OFFSET_SAMPLERATE 1011119 /* base = 44100 */
+#define SAMPLERATE_RATIO 4096
+
+static int calc_rate_offset(int hz)
+{
+ return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO);
+}
+
+
+/*
+ */
+
+static snd_pcm_hardware_t emu8k_pcm_hw = {
+#ifdef USE_NONINTERLEAVE
+ .info = SNDRV_PCM_INFO_NONINTERLEAVED,
+#else
+ .info = SNDRV_PCM_INFO_INTERLEAVED,
+#endif
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+ .rate_min = 4000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 2,
+ .buffer_bytes_max = (128*1024),
+ .period_bytes_min = 1024,
+ .period_bytes_max = (128*1024),
+ .periods_min = 2,
+ .periods_max = 1024,
+ .fifo_size = 0,
+
+};
+
+/*
+ * get the current position at the given channel from CCCA register
+ */
+static inline int emu8k_get_curpos(emu8k_pcm_t *rec, int ch)
+{
+ int val = EMU8000_CCCA_READ(rec->emu, ch) & 0xfffffff;
+ val -= rec->loop_start[ch] - 1;
+ return val;
+}
+
+
+/*
+ * timer interrupt handler
+ * check the current position and update the period if necessary.
+ */
+static void emu8k_pcm_timer_func(unsigned long data)
+{
+ emu8k_pcm_t *rec = (emu8k_pcm_t *)data;
+ int ptr, delta;
+
+ spin_lock(&rec->timer_lock);
+ /* update the current pointer */
+ ptr = emu8k_get_curpos(rec, 0);
+ if (ptr < rec->last_ptr)
+ delta = ptr + rec->buf_size - rec->last_ptr;
+ else
+ delta = ptr - rec->last_ptr;
+ rec->period_pos += delta;
+ rec->last_ptr = ptr;
+
+ /* reprogram timer */
+ rec->timer.expires = jiffies + 1;
+ add_timer(&rec->timer);
+
+ /* update period */
+ if (rec->period_pos >= (int)rec->period_size) {
+ rec->period_pos %= rec->period_size;
+ spin_unlock(&rec->timer_lock);
+ snd_pcm_period_elapsed(rec->substream);
+ return;
+ }
+ spin_unlock(&rec->timer_lock);
+}
+
+
+/*
+ * open pcm
+ * creating an instance here
+ */
+static int emu8k_pcm_open(snd_pcm_substream_t *subs)
+{
+ emu8000_t *emu = snd_pcm_substream_chip(subs);
+ emu8k_pcm_t *rec;
+ snd_pcm_runtime_t *runtime = subs->runtime;
+
+ rec = kcalloc(1, sizeof(*rec), GFP_KERNEL);
+ if (! rec)
+ return -ENOMEM;
+
+ rec->emu = emu;
+ rec->substream = subs;
+ runtime->private_data = rec;
+
+ spin_lock_init(&rec->timer_lock);
+ init_timer(&rec->timer);
+ rec->timer.function = emu8k_pcm_timer_func;
+ rec->timer.data = (unsigned long)rec;
+
+ runtime->hw = emu8k_pcm_hw;
+ runtime->hw.buffer_bytes_max = emu->mem_size - LOOP_BLANK_SIZE * 3;
+ runtime->hw.period_bytes_max = runtime->hw.buffer_bytes_max / 2;
+
+ /* use timer to update periods.. (specified in msec) */
+ snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ (1000000 + HZ - 1) / HZ, UINT_MAX);
+
+ return 0;
+}
+
+static int emu8k_pcm_close(snd_pcm_substream_t *subs)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+ kfree(rec);
+ subs->runtime->private_data = NULL;
+ return 0;
+}
+
+/*
+ * calculate pitch target
+ */
+static int calc_pitch_target(int pitch)
+{
+ int ptarget = 1 << (pitch >> 12);
+ if (pitch & 0x800) ptarget += (ptarget * 0x102e) / 0x2710;
+ if (pitch & 0x400) ptarget += (ptarget * 0x764) / 0x2710;
+ if (pitch & 0x200) ptarget += (ptarget * 0x389) / 0x2710;
+ ptarget += (ptarget >> 1);
+ if (ptarget > 0xffff) ptarget = 0xffff;
+ return ptarget;
+}
+
+/*
+ * set up the voice
+ */
+static void setup_voice(emu8k_pcm_t *rec, int ch)
+{
+ emu8000_t *hw = rec->emu;
+ unsigned int temp;
+
+ /* channel to be silent and idle */
+ EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);
+ EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);
+ EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);
+ EMU8000_PTRX_WRITE(hw, ch, 0);
+ EMU8000_CPF_WRITE(hw, ch, 0);
+
+ /* pitch offset */
+ EMU8000_IP_WRITE(hw, ch, rec->pitch);
+ /* set envelope parameters */
+ EMU8000_ENVVAL_WRITE(hw, ch, 0x8000);
+ EMU8000_ATKHLD_WRITE(hw, ch, 0x7f7f);
+ EMU8000_DCYSUS_WRITE(hw, ch, 0x7f7f);
+ EMU8000_ENVVOL_WRITE(hw, ch, 0x8000);
+ EMU8000_ATKHLDV_WRITE(hw, ch, 0x7f7f);
+ /* decay/sustain parameter for volume envelope is used
+ for triggerg the voice */
+ /* modulation envelope heights */
+ EMU8000_PEFE_WRITE(hw, ch, 0x0);
+ /* lfo1/2 delay */
+ EMU8000_LFO1VAL_WRITE(hw, ch, 0x8000);
+ EMU8000_LFO2VAL_WRITE(hw, ch, 0x8000);
+ /* lfo1 pitch & cutoff shift */
+ EMU8000_FMMOD_WRITE(hw, ch, 0);
+ /* lfo1 volume & freq */
+ EMU8000_TREMFRQ_WRITE(hw, ch, 0);
+ /* lfo2 pitch & freq */
+ EMU8000_FM2FRQ2_WRITE(hw, ch, 0);
+ /* pan & loop start */
+ temp = rec->panning[ch];
+ temp = (temp <<24) | ((unsigned int)rec->loop_start[ch] - 1);
+ EMU8000_PSST_WRITE(hw, ch, temp);
+ /* chorus & loop end (chorus 8bit, MSB) */
+ temp = 0; // chorus
+ temp = (temp << 24) | ((unsigned int)rec->loop_start[ch] + rec->buf_size - 1);
+ EMU8000_CSL_WRITE(hw, ch, temp);
+ /* Q & current address (Q 4bit value, MSB) */
+ temp = 0; // filterQ
+ temp = (temp << 28) | ((unsigned int)rec->loop_start[ch] - 1);
+ EMU8000_CCCA_WRITE(hw, ch, temp);
+ /* clear unknown registers */
+ EMU8000_00A0_WRITE(hw, ch, 0);
+ EMU8000_0080_WRITE(hw, ch, 0);
+}
+
+/*
+ * trigger the voice
+ */
+static void start_voice(emu8k_pcm_t *rec, int ch)
+{
+ unsigned long flags;
+ emu8000_t *hw = rec->emu;
+ unsigned int temp, aux;
+ int pt = calc_pitch_target(rec->pitch);
+
+ /* cutoff and volume */
+ EMU8000_IFATN_WRITE(hw, ch, 0xff00);
+ EMU8000_VTFT_WRITE(hw, ch, 0xffff);
+ EMU8000_CVCF_WRITE(hw, ch, 0xffff);
+ /* trigger envelope */
+ EMU8000_DCYSUSV_WRITE(hw, ch, 0x7f7f);
+ /* set reverb and pitch target */
+ temp = 0; // reverb
+ if (rec->panning[ch] == 0)
+ aux = 0xff;
+ else
+ aux = (-rec->panning[ch]) & 0xff;
+ temp = (temp << 8) | (pt << 16) | aux;
+ EMU8000_PTRX_WRITE(hw, ch, temp);
+ EMU8000_CPF_WRITE(hw, ch, pt << 16);
+
+ /* start timer */
+ spin_lock_irqsave(&rec->timer_lock, flags);
+ if (! rec->timer_running) {
+ rec->timer.expires = jiffies + 1;
+ add_timer(&rec->timer);
+ rec->timer_running = 1;
+ }
+ spin_unlock_irqrestore(&rec->timer_lock, flags);
+}
+
+/*
+ * stop the voice immediately
+ */
+static void stop_voice(emu8k_pcm_t *rec, int ch)
+{
+ unsigned long flags;
+ emu8000_t *hw = rec->emu;
+
+ EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
+
+ /* stop timer */
+ spin_lock_irqsave(&rec->timer_lock, flags);
+ if (rec->timer_running) {
+ del_timer(&rec->timer);
+ rec->timer_running = 0;
+ }
+ spin_unlock_irqrestore(&rec->timer_lock, flags);
+}
+
+static int emu8k_pcm_trigger(snd_pcm_substream_t *subs, int cmd)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+ int ch;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ for (ch = 0; ch < rec->voices; ch++)
+ start_voice(rec, ch);
+ rec->running = 1;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ rec->running = 0;
+ for (ch = 0; ch < rec->voices; ch++)
+ stop_voice(rec, ch);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/*
+ * copy / silence ops
+ */
+
+/*
+ * this macro should be inserted in the copy/silence loops
+ * to reduce the latency. without this, the system will hang up
+ * during the whole loop.
+ */
+#define CHECK_SCHEDULER() \
+do { \
+ cond_resched();\
+ if (signal_pending(current))\
+ return -EAGAIN;\
+} while (0)
+
+
+#ifdef USE_NONINTERLEAVE
+/* copy one channel block */
+static int emu8k_transfer_block(emu8000_t *emu, int offset, unsigned short *buf, int count)
+{
+ EMU8000_SMALW_WRITE(emu, offset);
+ while (count > 0) {
+ unsigned short sval;
+ CHECK_SCHEDULER();
+ get_user(sval, buf);
+ EMU8000_SMLD_WRITE(emu, sval);
+ buf++;
+ count--;
+ }
+ return 0;
+}
+
+static int emu8k_pcm_copy(snd_pcm_substream_t *subs,
+ int voice,
+ snd_pcm_uframes_t pos,
+ void *src,
+ snd_pcm_uframes_t count)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+ emu8000_t *emu = rec->emu;
+
+ snd_emu8000_write_wait(emu, 1);
+ if (voice == -1) {
+ unsigned short *buf = src;
+ int i, err;
+ count /= rec->voices;
+ for (i = 0; i < rec->voices; i++) {
+ err = emu8k_transfer_block(emu, pos + rec->loop_start[i], buf, count);
+ if (err < 0)
+ return err;
+ buf += count;
+ }
+ return 0;
+ } else {
+ return emu8k_transfer_block(emu, pos + rec->loop_start[voice], src, count);
+ }
+}
+
+/* make a channel block silence */
+static int emu8k_silence_block(emu8000_t *emu, int offset, int count)
+{
+ EMU8000_SMALW_WRITE(emu, offset);
+ while (count > 0) {
+ CHECK_SCHEDULER();
+ EMU8000_SMLD_WRITE(emu, 0);
+ count--;
+ }
+ return 0;
+}
+
+static int emu8k_pcm_silence(snd_pcm_substream_t *subs,
+ int voice,
+ snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+ emu8000_t *emu = rec->emu;
+
+ snd_emu8000_write_wait(emu, 1);
+ if (voice == -1 && rec->voices == 1)
+ voice = 0;
+ if (voice == -1) {
+ int err;
+ err = emu8k_silence_block(emu, pos + rec->loop_start[0], count / 2);
+ if (err < 0)
+ return err;
+ return emu8k_silence_block(emu, pos + rec->loop_start[1], count / 2);
+ } else {
+ return emu8k_silence_block(emu, pos + rec->loop_start[voice], count);
+ }
+}
+
+#else /* interleave */
+
+/*
+ * copy the interleaved data can be done easily by using
+ * DMA "left" and "right" channels on emu8k engine.
+ */
+static int emu8k_pcm_copy(snd_pcm_substream_t *subs,
+ int voice,
+ snd_pcm_uframes_t pos,
+ void __user *src,
+ snd_pcm_uframes_t count)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+ emu8000_t *emu = rec->emu;
+ unsigned short __user *buf = src;
+
+ snd_emu8000_write_wait(emu, 1);
+ EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]);
+ if (rec->voices > 1)
+ EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]);
+
+ while (count-- > 0) {
+ unsigned short sval;
+ CHECK_SCHEDULER();
+ get_user(sval, buf);
+ EMU8000_SMLD_WRITE(emu, sval);
+ buf++;
+ if (rec->voices > 1) {
+ CHECK_SCHEDULER();
+ get_user(sval, buf);
+ EMU8000_SMRD_WRITE(emu, sval);
+ buf++;
+ }
+ }
+ return 0;
+}
+
+static int emu8k_pcm_silence(snd_pcm_substream_t *subs,
+ int voice,
+ snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+ emu8000_t *emu = rec->emu;
+
+ snd_emu8000_write_wait(emu, 1);
+ EMU8000_SMALW_WRITE(emu, rec->loop_start[0] + pos);
+ if (rec->voices > 1)
+ EMU8000_SMARW_WRITE(emu, rec->loop_start[1] + pos);
+ while (count-- > 0) {
+ CHECK_SCHEDULER();
+ EMU8000_SMLD_WRITE(emu, 0);
+ if (rec->voices > 1) {
+ CHECK_SCHEDULER();
+ EMU8000_SMRD_WRITE(emu, 0);
+ }
+ }
+ return 0;
+}
+#endif
+
+
+/*
+ * allocate a memory block
+ */
+static int emu8k_pcm_hw_params(snd_pcm_substream_t *subs,
+ snd_pcm_hw_params_t *hw_params)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+
+ if (rec->block) {
+ /* reallocation - release the old block */
+ snd_util_mem_free(rec->emu->memhdr, rec->block);
+ rec->block = NULL;
+ }
+
+ rec->allocated_bytes = params_buffer_bytes(hw_params) + LOOP_BLANK_SIZE * 4;
+ rec->block = snd_util_mem_alloc(rec->emu->memhdr, rec->allocated_bytes);
+ if (! rec->block)
+ return -ENOMEM;
+ rec->offset = EMU8000_DRAM_OFFSET + (rec->block->offset >> 1); /* in word */
+ /* at least dma_bytes must be set for non-interleaved mode */
+ subs->dma_buffer.bytes = params_buffer_bytes(hw_params);
+
+ return 0;
+}
+
+/*
+ * free the memory block
+ */
+static int emu8k_pcm_hw_free(snd_pcm_substream_t *subs)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+
+ if (rec->block) {
+ int ch;
+ for (ch = 0; ch < rec->voices; ch++)
+ stop_voice(rec, ch); // to be sure
+ if (rec->dram_opened)
+ emu8k_close_dram(rec->emu);
+ snd_util_mem_free(rec->emu->memhdr, rec->block);
+ rec->block = NULL;
+ }
+ return 0;
+}
+
+/*
+ */
+static int emu8k_pcm_prepare(snd_pcm_substream_t *subs)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+
+ rec->pitch = 0xe000 + calc_rate_offset(subs->runtime->rate);
+ rec->last_ptr = 0;
+ rec->period_pos = 0;
+
+ rec->buf_size = subs->runtime->buffer_size;
+ rec->period_size = subs->runtime->period_size;
+ rec->voices = subs->runtime->channels;
+ rec->loop_start[0] = rec->offset + LOOP_BLANK_SIZE;
+ if (rec->voices > 1)
+ rec->loop_start[1] = rec->loop_start[0] + rec->buf_size + LOOP_BLANK_SIZE;
+ if (rec->voices > 1) {
+ rec->panning[0] = 0xff;
+ rec->panning[1] = 0x00;
+ } else
+ rec->panning[0] = 0x80;
+
+ if (! rec->dram_opened) {
+ int err, i, ch;
+
+ snd_emux_terminate_all(rec->emu->emu);
+ if ((err = emu8k_open_dram_for_pcm(rec->emu, rec->voices)) != 0)
+ return err;
+ rec->dram_opened = 1;
+
+ /* clear loop blanks */
+ snd_emu8000_write_wait(rec->emu, 0);
+ EMU8000_SMALW_WRITE(rec->emu, rec->offset);
+ for (i = 0; i < LOOP_BLANK_SIZE; i++)
+ EMU8000_SMLD_WRITE(rec->emu, 0);
+ for (ch = 0; ch < rec->voices; ch++) {
+ EMU8000_SMALW_WRITE(rec->emu, rec->loop_start[ch] + rec->buf_size);
+ for (i = 0; i < LOOP_BLANK_SIZE; i++)
+ EMU8000_SMLD_WRITE(rec->emu, 0);
+ }
+ }
+
+ setup_voice(rec, 0);
+ if (rec->voices > 1)
+ setup_voice(rec, 1);
+ return 0;
+}
+
+static snd_pcm_uframes_t emu8k_pcm_pointer(snd_pcm_substream_t *subs)
+{
+ emu8k_pcm_t *rec = subs->runtime->private_data;
+ if (rec->running)
+ return emu8k_get_curpos(rec, 0);
+ return 0;
+}
+
+
+static snd_pcm_ops_t emu8k_pcm_ops = {
+ .open = emu8k_pcm_open,
+ .close = emu8k_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = emu8k_pcm_hw_params,
+ .hw_free = emu8k_pcm_hw_free,
+ .prepare = emu8k_pcm_prepare,
+ .trigger = emu8k_pcm_trigger,
+ .pointer = emu8k_pcm_pointer,
+ .copy = emu8k_pcm_copy,
+ .silence = emu8k_pcm_silence,
+};
+
+
+static void snd_emu8000_pcm_free(snd_pcm_t *pcm)
+{
+ emu8000_t *emu = pcm->private_data;
+ emu->pcm = NULL;
+}
+
+int snd_emu8000_pcm_new(snd_card_t *card, emu8000_t *emu, int index)
+{
+ snd_pcm_t *pcm;
+ int err;
+
+ if ((err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm)) < 0)
+ return err;
+ pcm->private_data = emu;
+ pcm->private_free = snd_emu8000_pcm_free;
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &emu8k_pcm_ops);
+ emu->pcm = pcm;
+
+ snd_device_register(card, pcm);
+
+ return 0;
+}
diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c
new file mode 100644
index 000000000000..1f63aa52d596
--- /dev/null
+++ b/sound/isa/sb/emu8000_synth.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk>
+ * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
+ *
+ * Emu8000 synth plug-in routine
+ *
+ * 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 "emu8000_local.h"
+#include <linux/init.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe");
+MODULE_DESCRIPTION("Emu8000 synth plug-in routine");
+MODULE_LICENSE("GPL");
+
+/*----------------------------------------------------------------*/
+
+/*
+ * create a new hardware dependent device for Emu8000
+ */
+static int snd_emu8000_new_device(snd_seq_device_t *dev)
+{
+ emu8000_t *hw;
+ snd_emux_t *emu;
+
+ hw = *(emu8000_t**)SNDRV_SEQ_DEVICE_ARGPTR(dev);
+ if (hw == NULL)
+ return -EINVAL;
+
+ if (hw->emu)
+ return -EBUSY; /* already exists..? */
+
+ if (snd_emux_new(&emu) < 0)
+ return -ENOMEM;
+
+ hw->emu = emu;
+ snd_emu8000_ops_setup(hw);
+
+ emu->hw = hw;
+ emu->max_voices = EMU8000_DRAM_VOICES;
+ emu->num_ports = hw->seq_ports;
+
+ if (hw->memhdr) {
+ snd_printk("memhdr is already initialized!?\n");
+ snd_util_memhdr_free(hw->memhdr);
+ }
+ hw->memhdr = snd_util_memhdr_new(hw->mem_size);
+ if (hw->memhdr == NULL) {
+ snd_emux_free(emu);
+ hw->emu = NULL;
+ return -ENOMEM;
+ }
+
+ emu->memhdr = hw->memhdr;
+ emu->midi_ports = hw->seq_ports < 2 ? hw->seq_ports : 2; /* number of virmidi ports */
+ emu->midi_devidx = 1;
+ emu->linear_panning = 1;
+ emu->hwdep_idx = 2; /* FIXED */
+
+ if (snd_emux_register(emu, dev->card, hw->index, "Emu8000") < 0) {
+ snd_emux_free(emu);
+ snd_util_memhdr_free(hw->memhdr);
+ hw->emu = NULL;
+ hw->memhdr = NULL;
+ return -ENOMEM;
+ }
+
+ if (hw->mem_size > 0)
+ snd_emu8000_pcm_new(dev->card, hw, 1);
+
+ dev->driver_data = hw;
+
+ return 0;
+}
+
+
+/*
+ * free all resources
+ */
+static int snd_emu8000_delete_device(snd_seq_device_t *dev)
+{
+ emu8000_t *hw;
+
+ if (dev->driver_data == NULL)
+ return 0; /* no synth was allocated actually */
+
+ hw = dev->driver_data;
+ if (hw->pcm)
+ snd_device_free(dev->card, hw->pcm);
+ if (hw->emu)
+ snd_emux_free(hw->emu);
+ if (hw->memhdr)
+ snd_util_memhdr_free(hw->memhdr);
+ hw->emu = NULL;
+ hw->memhdr = NULL;
+ return 0;
+}
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_emu8000_init(void)
+{
+
+ static snd_seq_dev_ops_t ops = {
+ snd_emu8000_new_device,
+ snd_emu8000_delete_device,
+ };
+ return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, sizeof(emu8000_t*));
+}
+
+static void __exit alsa_emu8000_exit(void)
+{
+ snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000);
+}
+
+module_init(alsa_emu8000_init)
+module_exit(alsa_emu8000_exit)
diff --git a/sound/isa/sb/es968.c b/sound/isa/sb/es968.c
new file mode 100644
index 000000000000..c859917c14db
--- /dev/null
+++ b/sound/isa/sb/es968.c
@@ -0,0 +1,235 @@
+
+/*
+ card-es968.c - driver for ESS AudioDrive ES968 based soundcards.
+ Copyright (C) 1999 by Massimo Piccioni <dafastidio@libero.it>
+
+ Thanks to Pierfrancesco 'qM2' Passerini.
+
+ 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 <sound/driver.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/pnp.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/sb.h>
+
+#define PFX "es968: "
+
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_DESCRIPTION("ESS AudioDrive ES968");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ESS,AudioDrive ES968}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */
+static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for es968 based soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for es968 based soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable es968 based soundcard.");
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for es968 driver.");
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for es968 driver.");
+module_param_array(dma8, int, NULL, 0444);
+MODULE_PARM_DESC(dma8, "8-bit DMA # for es968 driver.");
+
+struct snd_card_es968 {
+ struct pnp_dev *dev;
+};
+
+static struct pnp_card_device_id snd_es968_pnpids[] = {
+ { .id = "ESS0968", .devs = { { "@@@0968" }, } },
+ { .id = "", } /* end */
+};
+
+MODULE_DEVICE_TABLE(pnp_card, snd_es968_pnpids);
+
+#define DRIVER_NAME "snd-card-es968"
+
+static irqreturn_t snd_card_es968_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ sb_t *chip = dev_id;
+
+ if (chip->open & SB_OPEN_PCM) {
+ return snd_sb8dsp_interrupt(chip);
+ } else {
+ return snd_sb8dsp_midi_interrupt(chip);
+ }
+}
+
+static int __devinit snd_card_es968_pnp(int dev, struct snd_card_es968 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
+{
+ struct pnp_dev *pdev;
+ struct pnp_resource_table *cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
+ int err;
+ if (!cfg)
+ return -ENOMEM;
+ acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+ if (acard->dev == NULL) {
+ kfree(cfg);
+ return -ENODEV;
+ }
+
+ pdev = acard->dev;
+
+ pnp_init_resource_table(cfg);
+
+ /* override resources */
+ if (port[dev] != SNDRV_AUTO_PORT)
+ pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+ if (dma8[dev] != SNDRV_AUTO_DMA)
+ pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
+ if (irq[dev] != SNDRV_AUTO_IRQ)
+ pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+ if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+ snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
+ err = pnp_activate_dev(pdev);
+ if (err < 0) {
+ snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+ kfree(cfg);
+ return err;
+ }
+ port[dev] = pnp_port_start(pdev, 0);
+ dma8[dev] = pnp_dma(pdev, 1);
+ irq[dev] = pnp_irq(pdev, 0);
+
+ kfree(cfg);
+ return 0;
+}
+
+static int __init snd_card_es968_probe(int dev,
+ struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
+{
+ int error;
+ sb_t *chip;
+ snd_card_t *card;
+ struct snd_card_es968 *acard;
+
+ if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_es968))) == NULL)
+ return -ENOMEM;
+ acard = (struct snd_card_es968 *)card->private_data;
+ if ((error = snd_card_es968_pnp(dev, acard, pcard, pid))) {
+ snd_card_free(card);
+ return error;
+ }
+ snd_card_set_dev(card, &pcard->card->dev);
+
+ if ((error = snd_sbdsp_create(card, port[dev],
+ irq[dev],
+ snd_card_es968_interrupt,
+ dma8[dev],
+ -1,
+ SB_HW_AUTO, &chip)) < 0) {
+ snd_card_free(card);
+ return error;
+ }
+
+ if ((error = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return error;
+ }
+
+ if ((error = snd_sbmixer_new(chip)) < 0) {
+ snd_card_free(card);
+ return error;
+ }
+
+ if ((error = snd_sb8dsp_midi(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return error;
+ }
+
+ strcpy(card->driver, "ES968");
+ strcpy(card->shortname, "ESS ES968");
+ sprintf(card->longname, "%s soundcard, %s at 0x%lx, irq %d, dma %d",
+ card->shortname, chip->name, chip->port, irq[dev], dma8[dev]);
+
+ if ((error = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return error;
+ }
+ pnp_set_card_drvdata(pcard, card);
+ return 0;
+}
+
+static int __devinit snd_es968_pnp_detect(struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
+{
+ static int dev;
+ int res;
+
+ for ( ; dev < SNDRV_CARDS; dev++) {
+ if (!enable[dev])
+ continue;
+ res = snd_card_es968_probe(dev, card, id);
+ if (res < 0)
+ return res;
+ dev++;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static void __devexit snd_es968_pnp_remove(struct pnp_card_link * pcard)
+{
+ snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard);
+
+ snd_card_disconnect(card);
+ snd_card_free_in_thread(card);
+}
+
+static struct pnp_card_driver es968_pnpc_driver = {
+ .flags = PNP_DRIVER_RES_DISABLE,
+ .name = "es968",
+ .id_table = snd_es968_pnpids,
+ .probe = snd_es968_pnp_detect,
+ .remove = __devexit_p(snd_es968_pnp_remove),
+};
+
+static int __init alsa_card_es968_init(void)
+{
+ int cards = pnp_register_card_driver(&es968_pnpc_driver);
+#ifdef MODULE
+ if (cards == 0) {
+ pnp_unregister_card_driver(&es968_pnpc_driver);
+ snd_printk(KERN_ERR "no ES968 based soundcards found\n");
+ }
+#endif
+ return cards ? 0 : -ENODEV;
+}
+
+static void __exit alsa_card_es968_exit(void)
+{
+ pnp_unregister_card_driver(&es968_pnpc_driver);
+}
+
+module_init(alsa_card_es968_init)
+module_exit(alsa_card_es968_exit)
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
new file mode 100644
index 000000000000..60e2c53c49fc
--- /dev/null
+++ b/sound/isa/sb/sb16.c
@@ -0,0 +1,678 @@
+/*
+ * Driver for SoundBlaster 16/AWE32/AWE64 soundcards
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * 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 <sound/driver.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/pnp.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/sb16_csp.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/emu8000.h>
+#include <sound/seq_device.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#include <sound/initval.h>
+
+#ifdef SNDRV_SBAWE
+#define PFX "sbawe: "
+#else
+#define PFX "sb16: "
+#endif
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_LICENSE("GPL");
+#ifndef SNDRV_SBAWE
+MODULE_DESCRIPTION("Sound Blaster 16");
+MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 16},"
+ "{Creative Labs,SB Vibra16S},"
+ "{Creative Labs,SB Vibra16C},"
+ "{Creative Labs,SB Vibra16CL},"
+ "{Creative Labs,SB Vibra16X}}");
+#else
+MODULE_DESCRIPTION("Sound Blaster AWE");
+MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB AWE 32},"
+ "{Creative Labs,SB AWE 64},"
+ "{Creative Labs,SB AWE 64 Gold}}");
+#endif
+
+#if 0
+#define SNDRV_DEBUG_IRQ
+#endif
+
+#if defined(SNDRV_SBAWE) && (defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)))
+#define SNDRV_SBAWE_EMU8000
+#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 int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_ISAPNP; /* Enable this card */
+#ifdef CONFIG_PNP
+static int isapnp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#endif
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260,0x280 */
+static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x330,0x300 */
+static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+#ifdef SNDRV_SBAWE_EMU8000
+static long awe_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+#endif
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */
+static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */
+static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 5,6,7 */
+static int mic_agc[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
+#ifdef CONFIG_SND_SB16_CSP
+static int csp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0};
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
+#endif
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for SoundBlaster 16 soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for SoundBlaster 16 soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable SoundBlaster 16 soundcard.");
+#ifdef CONFIG_PNP
+module_param_array(isapnp, bool, NULL, 0444);
+MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
+#endif
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for SB16 driver.");
+module_param_array(mpu_port, long, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port # for SB16 driver.");
+module_param_array(fm_port, long, NULL, 0444);
+MODULE_PARM_DESC(fm_port, "FM port # for SB16 PnP driver.");
+#ifdef SNDRV_SBAWE_EMU8000
+module_param_array(awe_port, long, NULL, 0444);
+MODULE_PARM_DESC(awe_port, "AWE port # for SB16 PnP driver.");
+#endif
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for SB16 driver.");
+module_param_array(dma8, int, NULL, 0444);
+MODULE_PARM_DESC(dma8, "8-bit DMA # for SB16 driver.");
+module_param_array(dma16, int, NULL, 0444);
+MODULE_PARM_DESC(dma16, "16-bit DMA # for SB16 driver.");
+module_param_array(mic_agc, int, NULL, 0444);
+MODULE_PARM_DESC(mic_agc, "Mic Auto-Gain-Control switch.");
+#ifdef CONFIG_SND_SB16_CSP
+module_param_array(csp, int, NULL, 0444);
+MODULE_PARM_DESC(csp, "ASP/CSP chip support.");
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+module_param_array(seq_ports, int, NULL, 0444);
+MODULE_PARM_DESC(seq_ports, "Number of sequencer ports for WaveTable synth.");
+#endif
+
+struct snd_card_sb16 {
+ struct resource *fm_res; /* used to block FM i/o region for legacy cards */
+#ifdef CONFIG_PNP
+ int dev_no;
+ struct pnp_dev *dev;
+#ifdef SNDRV_SBAWE_EMU8000
+ struct pnp_dev *devwt;
+#endif
+#endif
+};
+
+static snd_card_t *snd_sb16_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+#ifdef CONFIG_PNP
+
+static struct pnp_card_device_id snd_sb16_pnpids[] = {
+#ifndef SNDRV_SBAWE
+ /* Sound Blaster 16 PnP */
+ { .id = "CTL0024", .devs = { { "CTL0031" } } },
+ /* Sound Blaster 16 PnP */
+ { .id = "CTL0025", .devs = { { "CTL0031" } } },
+ /* Sound Blaster 16 PnP */
+ { .id = "CTL0026", .devs = { { "CTL0031" } } },
+ /* Sound Blaster 16 PnP */
+ { .id = "CTL0027", .devs = { { "CTL0031" } } },
+ /* Sound Blaster 16 PnP */
+ { .id = "CTL0028", .devs = { { "CTL0031" } } },
+ /* Sound Blaster 16 PnP */
+ { .id = "CTL0029", .devs = { { "CTL0031" } } },
+ /* Sound Blaster 16 PnP */
+ { .id = "CTL002a", .devs = { { "CTL0031" } } },
+ /* Sound Blaster 16 PnP */
+ /* Note: This card has also a CTL0051:StereoEnhance device!!! */
+ { .id = "CTL002b", .devs = { { "CTL0031" } } },
+ /* Sound Blaster 16 PnP */
+ { .id = "CTL002c", .devs = { { "CTL0031" } } },
+ /* Sound Blaster Vibra16S */
+ { .id = "CTL0051", .devs = { { "CTL0001" } } },
+ /* Sound Blaster Vibra16C */
+ { .id = "CTL0070", .devs = { { "CTL0001" } } },
+ /* Sound Blaster Vibra16CL - added by ctm@ardi.com */
+ { .id = "CTL0080", .devs = { { "CTL0041" } } },
+ /* Sound Blaster 16 'value' PnP. It says model ct4130 on the pcb, */
+ /* but ct4131 on a sticker on the board.. */
+ { .id = "CTL0086", .devs = { { "CTL0041" } } },
+ /* Sound Blaster Vibra16X */
+ { .id = "CTL00f0", .devs = { { "CTL0043" } } },
+#else /* SNDRV_SBAWE defined */
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL0035", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL0039", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL0042", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL0043", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ /* Note: This card has also a CTL0051:StereoEnhance device!!! */
+ { .id = "CTL0044", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ /* Note: This card has also a CTL0051:StereoEnhance device!!! */
+ { .id = "CTL0045", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL0046", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL0047", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL0048", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL0054", .devs = { { "CTL0031" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL009a", .devs = { { "CTL0041" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 32 PnP */
+ { .id = "CTL009c", .devs = { { "CTL0041" }, { "CTL0021" } } },
+ /* Sound Blaster 32 PnP */
+ { .id = "CTL009f", .devs = { { "CTL0041" }, { "CTL0021" } } },
+ /* Sound Blaster AWE 64 PnP */
+ { .id = "CTL009d", .devs = { { "CTL0042" }, { "CTL0022" } } },
+ /* Sound Blaster AWE 64 PnP Gold */
+ { .id = "CTL009e", .devs = { { "CTL0044" }, { "CTL0023" } } },
+ /* Sound Blaster AWE 64 PnP Gold */
+ { .id = "CTL00b2", .devs = { { "CTL0044" }, { "CTL0023" } } },
+ /* Sound Blaster AWE 64 PnP */
+ { .id = "CTL00c1", .devs = { { "CTL0042" }, { "CTL0022" } } },
+ /* Sound Blaster AWE 64 PnP */
+ { .id = "CTL00c3", .devs = { { "CTL0045" }, { "CTL0022" } } },
+ /* Sound Blaster AWE 64 PnP */
+ { .id = "CTL00c5", .devs = { { "CTL0045" }, { "CTL0022" } } },
+ /* Sound Blaster AWE 64 PnP */
+ { .id = "CTL00c7", .devs = { { "CTL0045" }, { "CTL0022" } } },
+ /* Sound Blaster AWE 64 PnP */
+ { .id = "CTL00e4", .devs = { { "CTL0045" }, { "CTL0022" } } },
+ /* Sound Blaster AWE 64 PnP */
+ { .id = "CTL00e9", .devs = { { "CTL0045" }, { "CTL0022" } } },
+ /* Sound Blaster 16 PnP (AWE) */
+ { .id = "CTL00ed", .devs = { { "CTL0041" }, { "CTL0070" } } },
+ /* Generic entries */
+ { .id = "CTLXXXX" , .devs = { { "CTL0031" }, { "CTL0021" } } },
+ { .id = "CTLXXXX" , .devs = { { "CTL0041" }, { "CTL0021" } } },
+ { .id = "CTLXXXX" , .devs = { { "CTL0042" }, { "CTL0022" } } },
+ { .id = "CTLXXXX" , .devs = { { "CTL0044" }, { "CTL0023" } } },
+ { .id = "CTLXXXX" , .devs = { { "CTL0045" }, { "CTL0022" } } },
+#endif /* SNDRV_SBAWE */
+ /* Sound Blaster 16 PnP (Virtual PC 2004)*/
+ { .id = "tBA03b0", .devs = { { "PNPb003" } } },
+ { .id = "", }
+};
+
+MODULE_DEVICE_TABLE(pnp_card, snd_sb16_pnpids);
+
+#endif /* CONFIG_PNP */
+
+#ifdef SNDRV_SBAWE_EMU8000
+#define DRIVER_NAME "snd-card-sbawe"
+#else
+#define DRIVER_NAME "snd-card-sb16"
+#endif
+
+#ifdef CONFIG_PNP
+
+static int __devinit snd_card_sb16_pnp(int dev, struct snd_card_sb16 *acard,
+ struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
+{
+ struct pnp_dev *pdev;
+ struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+ int err;
+
+ if (!cfg)
+ return -ENOMEM;
+ acard->dev = pnp_request_card_device(card, id->devs[0].id, NULL);
+ if (acard->dev == NULL) {
+ kfree(cfg);
+ return -ENODEV;
+ }
+#ifdef SNDRV_SBAWE_EMU8000
+ acard->devwt = pnp_request_card_device(card, id->devs[1].id, acard->dev);
+#endif
+ /* Audio initialization */
+ pdev = acard->dev;
+
+ pnp_init_resource_table(cfg);
+
+ /* override resources */
+
+ if (port[dev] != SNDRV_AUTO_PORT)
+ pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+ if (mpu_port[dev] != SNDRV_AUTO_PORT)
+ pnp_resource_change(&cfg->port_resource[1], mpu_port[dev], 2);
+ if (fm_port[dev] != SNDRV_AUTO_PORT)
+ pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4);
+ if (dma8[dev] != SNDRV_AUTO_DMA)
+ pnp_resource_change(&cfg->dma_resource[0], dma8[dev], 1);
+ if (dma16[dev] != SNDRV_AUTO_DMA)
+ pnp_resource_change(&cfg->dma_resource[1], dma16[dev], 1);
+ if (irq[dev] != SNDRV_AUTO_IRQ)
+ pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+ if (pnp_manual_config_dev(pdev, cfg, 0) < 0)
+ snd_printk(KERN_ERR PFX "AUDIO the requested resources are invalid, using auto config\n");
+ err = pnp_activate_dev(pdev);
+ if (err < 0) {
+ snd_printk(KERN_ERR PFX "AUDIO pnp configure failure\n");
+ kfree(cfg);
+ return err;
+ }
+ port[dev] = pnp_port_start(pdev, 0);
+ mpu_port[dev] = pnp_port_start(pdev, 1);
+ fm_port[dev] = pnp_port_start(pdev, 2);
+ dma8[dev] = pnp_dma(pdev, 0);
+ dma16[dev] = pnp_dma(pdev, 1);
+ irq[dev] = pnp_irq(pdev, 0);
+ snd_printdd("pnp SB16: port=0x%lx, mpu port=0x%lx, fm port=0x%lx\n",
+ port[dev], mpu_port[dev], fm_port[dev]);
+ snd_printdd("pnp SB16: dma1=%i, dma2=%i, irq=%i\n",
+ dma8[dev], dma16[dev], irq[dev]);
+#ifdef SNDRV_SBAWE_EMU8000
+ /* WaveTable initialization */
+ pdev = acard->devwt;
+ if (pdev != NULL) {
+ pnp_init_resource_table(cfg);
+
+ /* override resources */
+
+ if (awe_port[dev] != SNDRV_AUTO_PORT) {
+ pnp_resource_change(&cfg->port_resource[0], awe_port[dev], 4);
+ pnp_resource_change(&cfg->port_resource[1], awe_port[dev] + 0x400, 4);
+ pnp_resource_change(&cfg->port_resource[2], awe_port[dev] + 0x800, 4);
+ }
+ if ((pnp_manual_config_dev(pdev, cfg, 0)) < 0)
+ snd_printk(KERN_ERR PFX "WaveTable the requested resources are invalid, using auto config\n");
+ err = pnp_activate_dev(pdev);
+ if (err < 0) {
+ goto __wt_error;
+ }
+ awe_port[dev] = pnp_port_start(pdev, 0);
+ snd_printdd("pnp SB16: wavetable port=0x%lx\n", pnp_port_start(pdev, 0));
+ } else {
+__wt_error:
+ if (pdev) {
+ pnp_release_card_device(pdev);
+ snd_printk(KERN_ERR PFX "WaveTable pnp configure failure\n");
+ }
+ acard->devwt = NULL;
+ awe_port[dev] = -1;
+ }
+#endif
+ kfree(cfg);
+ return 0;
+}
+
+#endif /* CONFIG_PNP */
+
+static void snd_sb16_free(snd_card_t *card)
+{
+ struct snd_card_sb16 *acard = (struct snd_card_sb16 *)card->private_data;
+
+ if (acard == NULL)
+ return;
+ if (acard->fm_res) {
+ release_resource(acard->fm_res);
+ kfree_nocheck(acard->fm_res);
+ }
+}
+
+static int __init snd_sb16_probe(int dev,
+ struct pnp_card_link *pcard,
+ const struct pnp_card_device_id *pid)
+{
+ static int possible_irqs[] = {5, 9, 10, 7, -1};
+ static int possible_dmas8[] = {1, 3, 0, -1};
+ static int possible_dmas16[] = {5, 6, 7, -1};
+ int xirq, xdma8, xdma16;
+ sb_t *chip;
+ snd_card_t *card;
+ struct snd_card_sb16 *acard;
+ opl3_t *opl3;
+ snd_hwdep_t *synth = NULL;
+#ifdef CONFIG_SND_SB16_CSP
+ snd_hwdep_t *xcsp = NULL;
+#endif
+ unsigned long flags;
+ int err;
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_card_sb16));
+ if (card == NULL)
+ return -ENOMEM;
+ acard = (struct snd_card_sb16 *) card->private_data;
+ card->private_free = snd_sb16_free;
+#ifdef CONFIG_PNP
+ if (isapnp[dev]) {
+ if ((err = snd_card_sb16_pnp(dev, acard, pcard, pid))) {
+ snd_card_free(card);
+ return err;
+ }
+ snd_card_set_dev(card, &pcard->card->dev);
+ }
+#endif
+
+ xirq = irq[dev];
+ xdma8 = dma8[dev];
+ xdma16 = dma16[dev];
+#ifdef CONFIG_PNP
+ if (!isapnp[dev]) {
+#endif
+ if (xirq == SNDRV_AUTO_IRQ) {
+ if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
+ return -EBUSY;
+ }
+ }
+ if (xdma8 == SNDRV_AUTO_DMA) {
+ if ((xdma8 = snd_legacy_find_free_dma(possible_dmas8)) < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR PFX "unable to find a free 8-bit DMA\n");
+ return -EBUSY;
+ }
+ }
+ if (xdma16 == SNDRV_AUTO_DMA) {
+ if ((xdma16 = snd_legacy_find_free_dma(possible_dmas16)) < 0) {
+ snd_card_free(card);
+ snd_printk(KERN_ERR PFX "unable to find a free 16-bit DMA\n");
+ return -EBUSY;
+ }
+ }
+ /* non-PnP FM port address is hardwired with base port address */
+ fm_port[dev] = port[dev];
+ /* block the 0x388 port to avoid PnP conflicts */
+ acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+#ifdef SNDRV_SBAWE_EMU8000
+ /* non-PnP AWE port address is hardwired with base port address */
+ awe_port[dev] = port[dev] + 0x400;
+#endif
+#ifdef CONFIG_PNP
+ }
+#endif
+
+ if ((err = snd_sbdsp_create(card,
+ port[dev],
+ xirq,
+ snd_sb16dsp_interrupt,
+ xdma8,
+ xdma16,
+ SB_HW_AUTO,
+ &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if (chip->hardware != SB_HW_16) {
+ snd_card_free(card);
+ snd_printdd("SB 16 chip was not detected at 0x%lx\n", port[dev]);
+ return -ENODEV;
+ }
+ chip->mpu_port = mpu_port[dev];
+#ifdef CONFIG_PNP
+ if (!isapnp[dev] && (err = snd_sb16dsp_configure(chip)) < 0) {
+#else
+ if ((err = snd_sb16dsp_configure(chip)) < 0) {
+#endif
+ snd_card_free(card);
+ return -ENXIO;
+ }
+ if ((err = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return -ENXIO;
+ }
+
+ strcpy(card->driver,
+#ifdef SNDRV_SBAWE_EMU8000
+ awe_port[dev] > 0 ? "SB AWE" :
+#endif
+ "SB16");
+ strcpy(card->shortname, chip->name);
+ sprintf(card->longname, "%s at 0x%lx, irq %i, dma ",
+ chip->name,
+ chip->port,
+ xirq);
+ if (xdma8 >= 0)
+ sprintf(card->longname + strlen(card->longname), "%d", xdma8);
+ if (xdma16 >= 0)
+ sprintf(card->longname + strlen(card->longname), "%s%d",
+ xdma8 >= 0 ? "&" : "", xdma16);
+
+ if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
+ if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB,
+ chip->mpu_port, 0,
+ xirq, 0, &chip->rmidi)) < 0) {
+ snd_card_free(card);
+ return -ENXIO;
+ }
+ chip->rmidi_callback = snd_mpu401_uart_interrupt;
+ }
+
+#ifdef SNDRV_SBAWE_EMU8000
+ if (awe_port[dev] == SNDRV_AUTO_PORT)
+ awe_port[dev] = 0; /* disable */
+#endif
+
+ if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
+ if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
+ OPL3_HW_OPL3,
+ acard->fm_res != NULL || fm_port[dev] == port[dev],
+ &opl3) < 0) {
+ snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
+ fm_port[dev], fm_port[dev] + 2);
+ } else {
+#ifdef SNDRV_SBAWE_EMU8000
+ int seqdev = awe_port[dev] > 0 ? 2 : 1;
+#else
+ int seqdev = 1;
+#endif
+ if ((err = snd_opl3_hwdep_new(opl3, 0, seqdev, &synth)) < 0) {
+ snd_card_free(card);
+ return -ENXIO;
+ }
+ }
+ }
+
+ if ((err = snd_sbmixer_new(chip)) < 0) {
+ snd_card_free(card);
+ return -ENXIO;
+ }
+
+#ifdef CONFIG_SND_SB16_CSP
+ /* CSP chip on SB16ASP/AWE32 */
+ if ((chip->hardware == SB_HW_16) && csp[dev]) {
+ snd_sb_csp_new(chip, synth != NULL ? 1 : 0, &xcsp);
+ if (xcsp) {
+ chip->csp = xcsp->private_data;
+ chip->hardware = SB_HW_16CSP;
+ } else {
+ snd_printk(KERN_INFO PFX "warning - CSP chip not detected on soundcard #%i\n", dev + 1);
+ }
+ }
+#endif
+#ifdef SNDRV_SBAWE_EMU8000
+ if (awe_port[dev] > 0) {
+ if (snd_emu8000_new(card, 1, awe_port[dev],
+ seq_ports[dev], NULL) < 0) {
+ snd_printk(KERN_ERR PFX "fatal error - EMU-8000 synthesizer not detected at 0x%lx\n", awe_port[dev]);
+ snd_card_free(card);
+ return -ENXIO;
+ }
+ }
+#endif
+
+ /* setup Mic AGC */
+ spin_lock_irqsave(&chip->mixer_lock, flags);
+ snd_sbmixer_write(chip, SB_DSP4_MIC_AGC,
+ (snd_sbmixer_read(chip, SB_DSP4_MIC_AGC) & 0x01) |
+ (mic_agc[dev] ? 0x00 : 0x01));
+ spin_unlock_irqrestore(&chip->mixer_lock, flags);
+
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if (pcard)
+ pnp_set_card_drvdata(pcard, card);
+ else
+ snd_sb16_legacy[dev] = card;
+ return 0;
+}
+
+static int __init snd_sb16_probe_legacy_port(unsigned long xport)
+{
+ static int dev;
+ int res;
+
+ for ( ; dev < SNDRV_CARDS; dev++) {
+ if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT)
+ continue;
+#ifdef CONFIG_PNP
+ if (isapnp[dev])
+ continue;
+#endif
+ port[dev] = xport;
+ res = snd_sb16_probe(dev, NULL, NULL);
+ if (res < 0)
+ port[dev] = SNDRV_AUTO_PORT;
+ return res;
+ }
+ return -ENODEV;
+}
+
+#ifdef CONFIG_PNP
+
+static int __devinit snd_sb16_pnp_detect(struct pnp_card_link *card,
+ const struct pnp_card_device_id *id)
+{
+ static int dev;
+ int res;
+
+ for ( ; dev < SNDRV_CARDS; dev++) {
+ if (!enable[dev] || !isapnp[dev])
+ continue;
+ res = snd_sb16_probe(dev, card, id);
+ if (res < 0)
+ return res;
+ dev++;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static void __devexit snd_sb16_pnp_remove(struct pnp_card_link * pcard)
+{
+ snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard);
+
+ snd_card_disconnect(card);
+ snd_card_free_in_thread(card);
+}
+
+static struct pnp_card_driver sb16_pnpc_driver = {
+ .flags = PNP_DRIVER_RES_DISABLE,
+ .name = "sb16",
+ .id_table = snd_sb16_pnpids,
+ .probe = snd_sb16_pnp_detect,
+ .remove = __devexit_p(snd_sb16_pnp_remove),
+};
+
+#endif /* CONFIG_PNP */
+
+static int __init alsa_card_sb16_init(void)
+{
+ int dev, cards = 0, i;
+ static unsigned long possible_ports[] = {0x220, 0x240, 0x260, 0x280, -1};
+
+ /* legacy non-auto cards at first */
+ for (dev = 0; dev < SNDRV_CARDS; dev++) {
+ if (!enable[dev] || port[dev] == SNDRV_AUTO_PORT)
+ continue;
+#ifdef CONFIG_PNP
+ if (isapnp[dev])
+ continue;
+#endif
+ if (!snd_sb16_probe(dev, NULL, NULL)) {
+ cards++;
+ continue;
+ }
+#ifdef MODULE
+ snd_printk(KERN_ERR "Sound Blaster 16+ soundcard #%i not found at 0x%lx or device busy\n", dev, port[dev]);
+#endif
+ }
+ /* legacy auto configured cards */
+ i = snd_legacy_auto_probe(possible_ports, snd_sb16_probe_legacy_port);
+ if (i > 0)
+ cards += i;
+
+#ifdef CONFIG_PNP
+ /* PnP cards at last */
+ i = pnp_register_card_driver(&sb16_pnpc_driver);
+ if (i >0)
+ cards += i;
+#endif
+
+ if (!cards) {
+#ifdef CONFIG_PNP
+ pnp_unregister_card_driver(&sb16_pnpc_driver);
+#endif
+#ifdef MODULE
+ snd_printk(KERN_ERR "Sound Blaster 16 soundcard not found or device busy\n");
+#ifdef SNDRV_SBAWE_EMU8000
+ snd_printk(KERN_ERR "In case, if you have non-AWE card, try snd-sb16 module\n");
+#else
+ snd_printk(KERN_ERR "In case, if you have AWE card, try snd-sbawe module\n");
+#endif
+#endif
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __exit alsa_card_sb16_exit(void)
+{
+ int dev;
+
+#ifdef CONFIG_PNP
+ /* PnP cards first */
+ pnp_unregister_card_driver(&sb16_pnpc_driver);
+#endif
+ for (dev = 0; dev < SNDRV_CARDS; dev++)
+ snd_card_free(snd_sb16_legacy[dev]);
+}
+
+module_init(alsa_card_sb16_init)
+module_exit(alsa_card_sb16_exit)
diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
new file mode 100644
index 000000000000..b62920eead3d
--- /dev/null
+++ b/sound/isa/sb/sb16_csp.c
@@ -0,0 +1,1175 @@
+/*
+ * Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
+ * Takashi Iwai <tiwai@suse.de>
+ *
+ * SB16ASP/AWE32 CSP control
+ *
+ * CSP microcode loader:
+ * alsa-tools/sb16_csp/
+ *
+ * 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 <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/info.h>
+#include <sound/sb16_csp.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Uros Bizjak <uros@kss-loka.si>");
+MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor");
+MODULE_LICENSE("GPL");
+
+#ifdef SNDRV_LITTLE_ENDIAN
+#define CSP_HDR_VALUE(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
+#else
+#define CSP_HDR_VALUE(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
+#endif
+#define LE_SHORT(v) le16_to_cpu(v)
+#define LE_INT(v) le32_to_cpu(v)
+
+#define RIFF_HEADER CSP_HDR_VALUE('R', 'I', 'F', 'F')
+#define CSP__HEADER CSP_HDR_VALUE('C', 'S', 'P', ' ')
+#define LIST_HEADER CSP_HDR_VALUE('L', 'I', 'S', 'T')
+#define FUNC_HEADER CSP_HDR_VALUE('f', 'u', 'n', 'c')
+#define CODE_HEADER CSP_HDR_VALUE('c', 'o', 'd', 'e')
+#define INIT_HEADER CSP_HDR_VALUE('i', 'n', 'i', 't')
+#define MAIN_HEADER CSP_HDR_VALUE('m', 'a', 'i', 'n')
+
+/*
+ * RIFF data format
+ */
+typedef struct riff_header {
+ __u32 name;
+ __u32 len;
+} riff_header_t;
+
+typedef struct desc_header {
+ riff_header_t info;
+ __u16 func_nr;
+ __u16 VOC_type;
+ __u16 flags_play_rec;
+ __u16 flags_16bit_8bit;
+ __u16 flags_stereo_mono;
+ __u16 flags_rates;
+} desc_header_t;
+
+/*
+ * prototypes
+ */
+static void snd_sb_csp_free(snd_hwdep_t *hw);
+static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file);
+static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg);
+static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file);
+
+static int csp_detect(sb_t *chip, int *version);
+static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val);
+static int set_register(sb_t *chip, unsigned char reg, unsigned char val);
+static int read_register(sb_t *chip, unsigned char reg);
+static int set_mode_register(sb_t *chip, unsigned char mode);
+static int get_version(sb_t *chip);
+
+static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t __user * code);
+static int snd_sb_csp_unload(snd_sb_csp_t * p);
+static int snd_sb_csp_load_user(snd_sb_csp_t * p, const unsigned char __user *buf, int size, int load_flags);
+static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode);
+static int snd_sb_csp_check_version(snd_sb_csp_t * p);
+
+static int snd_sb_csp_use(snd_sb_csp_t * p);
+static int snd_sb_csp_unuse(snd_sb_csp_t * p);
+static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels);
+static int snd_sb_csp_stop(snd_sb_csp_t * p);
+static int snd_sb_csp_pause(snd_sb_csp_t * p);
+static int snd_sb_csp_restart(snd_sb_csp_t * p);
+
+static int snd_sb_qsound_build(snd_sb_csp_t * p);
+static void snd_sb_qsound_destroy(snd_sb_csp_t * p);
+static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p);
+
+static int init_proc_entry(snd_sb_csp_t * p, int device);
+static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer);
+
+/*
+ * Detect CSP chip and create a new instance
+ */
+int snd_sb_csp_new(sb_t *chip, int device, snd_hwdep_t ** rhwdep)
+{
+ snd_sb_csp_t *p;
+ int version, err;
+ snd_hwdep_t *hw;
+
+ if (rhwdep)
+ *rhwdep = NULL;
+
+ if (csp_detect(chip, &version))
+ return -ENODEV;
+
+ if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0)
+ return err;
+
+ if ((p = kcalloc(1, sizeof(*p), GFP_KERNEL)) == NULL) {
+ snd_device_free(chip->card, hw);
+ return -ENOMEM;
+ }
+ p->chip = chip;
+ p->version = version;
+
+ /* CSP operators */
+ p->ops.csp_use = snd_sb_csp_use;
+ p->ops.csp_unuse = snd_sb_csp_unuse;
+ p->ops.csp_autoload = snd_sb_csp_autoload;
+ p->ops.csp_start = snd_sb_csp_start;
+ p->ops.csp_stop = snd_sb_csp_stop;
+ p->ops.csp_qsound_transfer = snd_sb_csp_qsound_transfer;
+
+ init_MUTEX(&p->access_mutex);
+ sprintf(hw->name, "CSP v%d.%d", (version >> 4), (version & 0x0f));
+ hw->iface = SNDRV_HWDEP_IFACE_SB16CSP;
+ hw->private_data = p;
+ hw->private_free = snd_sb_csp_free;
+
+ /* operators - only write/ioctl */
+ hw->ops.open = snd_sb_csp_open;
+ hw->ops.ioctl = snd_sb_csp_ioctl;
+ hw->ops.release = snd_sb_csp_release;
+
+ /* create a proc entry */
+ init_proc_entry(p, device);
+ if (rhwdep)
+ *rhwdep = hw;
+ return 0;
+}
+
+/*
+ * free_private for hwdep instance
+ */
+static void snd_sb_csp_free(snd_hwdep_t *hwdep)
+{
+ snd_sb_csp_t *p = hwdep->private_data;
+ if (p) {
+ if (p->running & SNDRV_SB_CSP_ST_RUNNING)
+ snd_sb_csp_stop(p);
+ kfree(p);
+ }
+}
+
+/* ------------------------------ */
+
+/*
+ * open the device exclusively
+ */
+static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file)
+{
+ snd_sb_csp_t *p = hw->private_data;
+ return (snd_sb_csp_use(p));
+}
+
+/*
+ * ioctl for hwdep device:
+ */
+static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ snd_sb_csp_t *p = hw->private_data;
+ snd_sb_csp_info_t info;
+ snd_sb_csp_start_t start_info;
+ int err;
+
+ snd_assert(p != NULL, return -EINVAL);
+
+ if (snd_sb_csp_check_version(p))
+ return -ENODEV;
+
+ switch (cmd) {
+ /* get information */
+ case SNDRV_SB_CSP_IOCTL_INFO:
+ *info.codec_name = *p->codec_name;
+ info.func_nr = p->func_nr;
+ info.acc_format = p->acc_format;
+ info.acc_channels = p->acc_channels;
+ info.acc_width = p->acc_width;
+ info.acc_rates = p->acc_rates;
+ info.csp_mode = p->mode;
+ info.run_channels = p->run_channels;
+ info.run_width = p->run_width;
+ info.version = p->version;
+ info.state = p->running;
+ if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+ err = -EFAULT;
+ else
+ err = 0;
+ break;
+
+ /* load CSP microcode */
+ case SNDRV_SB_CSP_IOCTL_LOAD_CODE:
+ err = (p->running & SNDRV_SB_CSP_ST_RUNNING ?
+ -EBUSY : snd_sb_csp_riff_load(p, (snd_sb_csp_microcode_t __user *) arg));
+ break;
+ case SNDRV_SB_CSP_IOCTL_UNLOAD_CODE:
+ err = (p->running & SNDRV_SB_CSP_ST_RUNNING ?
+ -EBUSY : snd_sb_csp_unload(p));
+ break;
+
+ /* change CSP running state */
+ case SNDRV_SB_CSP_IOCTL_START:
+ if (copy_from_user(&start_info, (void __user *) arg, sizeof(start_info)))
+ err = -EFAULT;
+ else
+ err = snd_sb_csp_start(p, start_info.sample_width, start_info.channels);
+ break;
+ case SNDRV_SB_CSP_IOCTL_STOP:
+ err = snd_sb_csp_stop(p);
+ break;
+ case SNDRV_SB_CSP_IOCTL_PAUSE:
+ err = snd_sb_csp_pause(p);
+ break;
+ case SNDRV_SB_CSP_IOCTL_RESTART:
+ err = snd_sb_csp_restart(p);
+ break;
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ return err;
+}
+
+/*
+ * close the device
+ */
+static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file)
+{
+ snd_sb_csp_t *p = hw->private_data;
+ return (snd_sb_csp_unuse(p));
+}
+
+/* ------------------------------ */
+
+/*
+ * acquire device
+ */
+static int snd_sb_csp_use(snd_sb_csp_t * p)
+{
+ down(&p->access_mutex);
+ if (p->used) {
+ up(&p->access_mutex);
+ return -EAGAIN;
+ }
+ p->used++;
+ up(&p->access_mutex);
+
+ return 0;
+
+}
+
+/*
+ * release device
+ */
+static int snd_sb_csp_unuse(snd_sb_csp_t * p)
+{
+ down(&p->access_mutex);
+ p->used--;
+ up(&p->access_mutex);
+
+ return 0;
+}
+
+/*
+ * load microcode via ioctl:
+ * code is user-space pointer
+ */
+static int snd_sb_csp_riff_load(snd_sb_csp_t * p, snd_sb_csp_microcode_t __user * mcode)
+{
+ snd_sb_csp_mc_header_t info;
+
+ unsigned char __user *data_ptr;
+ unsigned char __user *data_end;
+ unsigned short func_nr = 0;
+
+ riff_header_t file_h, item_h, code_h;
+ __u32 item_type;
+ desc_header_t funcdesc_h;
+
+ unsigned long flags;
+ int err;
+
+ if (copy_from_user(&info, mcode, sizeof(info)))
+ return -EFAULT;
+ data_ptr = mcode->data;
+
+ if (copy_from_user(&file_h, data_ptr, sizeof(file_h)))
+ return -EFAULT;
+ if ((file_h.name != RIFF_HEADER) ||
+ (LE_INT(file_h.len) >= SNDRV_SB_CSP_MAX_MICROCODE_FILE_SIZE - sizeof(file_h))) {
+ snd_printd("%s: Invalid RIFF header\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ data_ptr += sizeof(file_h);
+ data_end = data_ptr + LE_INT(file_h.len);
+
+ if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
+ return -EFAULT;
+ if (item_type != CSP__HEADER) {
+ snd_printd("%s: Invalid RIFF file type\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ data_ptr += sizeof (item_type);
+
+ for (; data_ptr < data_end; data_ptr += LE_INT(item_h.len)) {
+ if (copy_from_user(&item_h, data_ptr, sizeof(item_h)))
+ return -EFAULT;
+ data_ptr += sizeof(item_h);
+ if (item_h.name != LIST_HEADER)
+ continue;
+
+ if (copy_from_user(&item_type, data_ptr, sizeof(item_type)))
+ return -EFAULT;
+ switch (item_type) {
+ case FUNC_HEADER:
+ if (copy_from_user(&funcdesc_h, data_ptr + sizeof(item_type), sizeof(funcdesc_h)))
+ return -EFAULT;
+ func_nr = LE_SHORT(funcdesc_h.func_nr);
+ break;
+ case CODE_HEADER:
+ if (func_nr != info.func_req)
+ break; /* not required function, try next */
+ data_ptr += sizeof(item_type);
+
+ /* destroy QSound mixer element */
+ if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
+ snd_sb_qsound_destroy(p);
+ }
+ /* Clear all flags */
+ p->running = 0;
+ p->mode = 0;
+
+ /* load microcode blocks */
+ for (;;) {
+ if (data_ptr >= data_end)
+ return -EINVAL;
+ if (copy_from_user(&code_h, data_ptr, sizeof(code_h)))
+ return -EFAULT;
+
+ /* init microcode blocks */
+ if (code_h.name != INIT_HEADER)
+ break;
+ data_ptr += sizeof(code_h);
+ err = snd_sb_csp_load_user(p, data_ptr, LE_INT(code_h.len),
+ SNDRV_SB_CSP_LOAD_INITBLOCK);
+ if (err)
+ return err;
+ data_ptr += LE_INT(code_h.len);
+ }
+ /* main microcode block */
+ if (copy_from_user(&code_h, data_ptr, sizeof(code_h)))
+ return -EFAULT;
+
+ if (code_h.name != MAIN_HEADER) {
+ snd_printd("%s: Missing 'main' microcode\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ data_ptr += sizeof(code_h);
+ err = snd_sb_csp_load_user(p, data_ptr,
+ LE_INT(code_h.len), 0);
+ if (err)
+ return err;
+
+ /* fill in codec header */
+ strlcpy(p->codec_name, info.codec_name, sizeof(p->codec_name));
+ p->func_nr = func_nr;
+ p->mode = LE_SHORT(funcdesc_h.flags_play_rec);
+ switch (LE_SHORT(funcdesc_h.VOC_type)) {
+ case 0x0001: /* QSound decoder */
+ if (LE_SHORT(funcdesc_h.flags_play_rec) == SNDRV_SB_CSP_MODE_DSP_WRITE) {
+ if (snd_sb_qsound_build(p) == 0)
+ /* set QSound flag and clear all other mode flags */
+ p->mode = SNDRV_SB_CSP_MODE_QSOUND;
+ }
+ p->acc_format = 0;
+ break;
+ case 0x0006: /* A Law codec */
+ p->acc_format = SNDRV_PCM_FMTBIT_A_LAW;
+ break;
+ case 0x0007: /* Mu Law codec */
+ p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW;
+ break;
+ case 0x0011: /* what Creative thinks is IMA ADPCM codec */
+ case 0x0200: /* Creative ADPCM codec */
+ p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM;
+ break;
+ case 201: /* Text 2 Speech decoder */
+ /* TODO: Text2Speech handling routines */
+ p->acc_format = 0;
+ break;
+ case 0x0202: /* Fast Speech 8 codec */
+ case 0x0203: /* Fast Speech 10 codec */
+ p->acc_format = SNDRV_PCM_FMTBIT_SPECIAL;
+ break;
+ default: /* other codecs are unsupported */
+ p->acc_format = p->acc_width = p->acc_rates = 0;
+ p->mode = 0;
+ snd_printd("%s: Unsupported CSP codec type: 0x%04x\n",
+ __FUNCTION__,
+ LE_SHORT(funcdesc_h.VOC_type));
+ return -EINVAL;
+ }
+ p->acc_channels = LE_SHORT(funcdesc_h.flags_stereo_mono);
+ p->acc_width = LE_SHORT(funcdesc_h.flags_16bit_8bit);
+ p->acc_rates = LE_SHORT(funcdesc_h.flags_rates);
+
+ /* Decouple CSP from IRQ and DMAREQ lines */
+ spin_lock_irqsave(&p->chip->reg_lock, flags);
+ set_mode_register(p->chip, 0xfc);
+ set_mode_register(p->chip, 0x00);
+ spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+
+ /* finished loading successfully */
+ p->running = SNDRV_SB_CSP_ST_LOADED; /* set LOADED flag */
+ return 0;
+ }
+ }
+ snd_printd("%s: Function #%d not found\n", __FUNCTION__, info.func_req);
+ return -EINVAL;
+}
+
+/*
+ * unload CSP microcode
+ */
+static int snd_sb_csp_unload(snd_sb_csp_t * p)
+{
+ if (p->running & SNDRV_SB_CSP_ST_RUNNING)
+ return -EBUSY;
+ if (!(p->running & SNDRV_SB_CSP_ST_LOADED))
+ return -ENXIO;
+
+ /* clear supported formats */
+ p->acc_format = 0;
+ p->acc_channels = p->acc_width = p->acc_rates = 0;
+ /* destroy QSound mixer element */
+ if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
+ snd_sb_qsound_destroy(p);
+ }
+ /* clear all flags */
+ p->running = 0;
+ p->mode = 0;
+ return 0;
+}
+
+/*
+ * send command sequence to DSP
+ */
+static inline int command_seq(sb_t *chip, const unsigned char *seq, int size)
+{
+ int i;
+ for (i = 0; i < size; i++) {
+ if (!snd_sbdsp_command(chip, seq[i]))
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * set CSP codec parameter
+ */
+static int set_codec_parameter(sb_t *chip, unsigned char par, unsigned char val)
+{
+ unsigned char dsp_cmd[3];
+
+ dsp_cmd[0] = 0x05; /* CSP set codec parameter */
+ dsp_cmd[1] = val; /* Parameter value */
+ dsp_cmd[2] = par; /* Parameter */
+ command_seq(chip, dsp_cmd, 3);
+ snd_sbdsp_command(chip, 0x03); /* DSP read? */
+ if (snd_sbdsp_get_byte(chip) != par)
+ return -EIO;
+ return 0;
+}
+
+/*
+ * set CSP register
+ */
+static int set_register(sb_t *chip, unsigned char reg, unsigned char val)
+{
+ unsigned char dsp_cmd[3];
+
+ dsp_cmd[0] = 0x0e; /* CSP set register */
+ dsp_cmd[1] = reg; /* CSP Register */
+ dsp_cmd[2] = val; /* value */
+ return command_seq(chip, dsp_cmd, 3);
+}
+
+/*
+ * read CSP register
+ * return < 0 -> error
+ */
+static int read_register(sb_t *chip, unsigned char reg)
+{
+ unsigned char dsp_cmd[2];
+
+ dsp_cmd[0] = 0x0f; /* CSP read register */
+ dsp_cmd[1] = reg; /* CSP Register */
+ command_seq(chip, dsp_cmd, 2);
+ return snd_sbdsp_get_byte(chip); /* Read DSP value */
+}
+
+/*
+ * set CSP mode register
+ */
+static int set_mode_register(sb_t *chip, unsigned char mode)
+{
+ unsigned char dsp_cmd[2];
+
+ dsp_cmd[0] = 0x04; /* CSP set mode register */
+ dsp_cmd[1] = mode; /* mode */
+ return command_seq(chip, dsp_cmd, 2);
+}
+
+/*
+ * Detect CSP
+ * return 0 if CSP exists.
+ */
+static int csp_detect(sb_t *chip, int *version)
+{
+ unsigned char csp_test1, csp_test2;
+ unsigned long flags;
+ int result = -ENODEV;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+
+ set_codec_parameter(chip, 0x00, 0x00);
+ set_mode_register(chip, 0xfc); /* 0xfc = ?? */
+
+ csp_test1 = read_register(chip, 0x83);
+ set_register(chip, 0x83, ~csp_test1);
+ csp_test2 = read_register(chip, 0x83);
+ if (csp_test2 != (csp_test1 ^ 0xff))
+ goto __fail;
+
+ set_register(chip, 0x83, csp_test1);
+ csp_test2 = read_register(chip, 0x83);
+ if (csp_test2 != csp_test1)
+ goto __fail;
+
+ set_mode_register(chip, 0x00); /* 0x00 = ? */
+
+ *version = get_version(chip);
+ snd_sbdsp_reset(chip); /* reset DSP after getversion! */
+ if (*version >= 0x10 && *version <= 0x1f)
+ result = 0; /* valid version id */
+
+ __fail:
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return result;
+}
+
+/*
+ * get CSP version number
+ */
+static int get_version(sb_t *chip)
+{
+ unsigned char dsp_cmd[2];
+
+ dsp_cmd[0] = 0x08; /* SB_DSP_!something! */
+ dsp_cmd[1] = 0x03; /* get chip version id? */
+ command_seq(chip, dsp_cmd, 2);
+
+ return (snd_sbdsp_get_byte(chip));
+}
+
+/*
+ * check if the CSP version is valid
+ */
+static int snd_sb_csp_check_version(snd_sb_csp_t * p)
+{
+ if (p->version < 0x10 || p->version > 0x1f) {
+ snd_printd("%s: Invalid CSP version: 0x%x\n", __FUNCTION__, p->version);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * download microcode to CSP (microcode should have one "main" block).
+ */
+static int snd_sb_csp_load(snd_sb_csp_t * p, const unsigned char *buf, int size, int load_flags)
+{
+ int status, i;
+ int err;
+ int result = -EIO;
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->chip->reg_lock, flags);
+ snd_sbdsp_command(p->chip, 0x01); /* CSP download command */
+ if (snd_sbdsp_get_byte(p->chip)) {
+ snd_printd("%s: Download command failed\n", __FUNCTION__);
+ goto __fail;
+ }
+ /* Send CSP low byte (size - 1) */
+ snd_sbdsp_command(p->chip, (unsigned char)(size - 1));
+ /* Send high byte */
+ snd_sbdsp_command(p->chip, (unsigned char)((size - 1) >> 8));
+ /* send microcode sequence */
+ /* load from kernel space */
+ while (size--) {
+ if (!snd_sbdsp_command(p->chip, *buf++))
+ goto __fail;
+ }
+ if (snd_sbdsp_get_byte(p->chip))
+ goto __fail;
+
+ if (load_flags & SNDRV_SB_CSP_LOAD_INITBLOCK) {
+ i = 0;
+ /* some codecs (FastSpeech) take some time to initialize */
+ while (1) {
+ snd_sbdsp_command(p->chip, 0x03);
+ status = snd_sbdsp_get_byte(p->chip);
+ if (status == 0x55 || ++i >= 10)
+ break;
+ udelay (10);
+ }
+ if (status != 0x55) {
+ snd_printd("%s: Microcode initialization failed\n", __FUNCTION__);
+ goto __fail;
+ }
+ } else {
+ /*
+ * Read mixer register SB_DSP4_DMASETUP after loading 'main' code.
+ * Start CSP chip if no 16bit DMA channel is set - some kind
+ * of autorun or perhaps a bugfix?
+ */
+ spin_lock(&p->chip->mixer_lock);
+ status = snd_sbmixer_read(p->chip, SB_DSP4_DMASETUP);
+ spin_unlock(&p->chip->mixer_lock);
+ if (!(status & (SB_DMASETUP_DMA7 | SB_DMASETUP_DMA6 | SB_DMASETUP_DMA5))) {
+ err = (set_codec_parameter(p->chip, 0xaa, 0x00) ||
+ set_codec_parameter(p->chip, 0xff, 0x00));
+ snd_sbdsp_reset(p->chip); /* really! */
+ if (err)
+ goto __fail;
+ set_mode_register(p->chip, 0xc0); /* c0 = STOP */
+ set_mode_register(p->chip, 0x70); /* 70 = RUN */
+ }
+ }
+ result = 0;
+
+ __fail:
+ spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+ return result;
+}
+
+static int snd_sb_csp_load_user(snd_sb_csp_t * p, const unsigned char __user *buf, int size, int load_flags)
+{
+ int err = -ENOMEM;
+ unsigned char *kbuf = kmalloc(size, GFP_KERNEL);
+ if (kbuf) {
+ if (copy_from_user(kbuf, buf, size))
+ err = -EFAULT;
+ else
+ err = snd_sb_csp_load(p, kbuf, size, load_flags);
+ kfree(kbuf);
+ }
+ return err;
+}
+
+#include "sb16_csp_codecs.h"
+
+/*
+ * autoload hardware codec if necessary
+ * return 0 if CSP is loaded and ready to run (p->running != 0)
+ */
+static int snd_sb_csp_autoload(snd_sb_csp_t * p, int pcm_sfmt, int play_rec_mode)
+{
+ unsigned long flags;
+ int err = 0;
+
+ /* if CSP is running or manually loaded then exit */
+ if (p->running & (SNDRV_SB_CSP_ST_RUNNING | SNDRV_SB_CSP_ST_LOADED))
+ return -EBUSY;
+
+ /* autoload microcode only if requested hardware codec is not already loaded */
+ if (((1 << pcm_sfmt) & p->acc_format) && (play_rec_mode & p->mode)) {
+ p->running = SNDRV_SB_CSP_ST_AUTO;
+ } else {
+ switch (pcm_sfmt) {
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ err = snd_sb_csp_load(p, &mulaw_main[0], sizeof(mulaw_main), 0);
+ p->acc_format = SNDRV_PCM_FMTBIT_MU_LAW;
+ p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE;
+ break;
+ case SNDRV_PCM_FORMAT_A_LAW:
+ err = snd_sb_csp_load(p, &alaw_main[0], sizeof(alaw_main), 0);
+ p->acc_format = SNDRV_PCM_FMTBIT_A_LAW;
+ p->mode = SNDRV_SB_CSP_MODE_DSP_READ | SNDRV_SB_CSP_MODE_DSP_WRITE;
+ break;
+ case SNDRV_PCM_FORMAT_IMA_ADPCM:
+ err = snd_sb_csp_load(p, &ima_adpcm_init[0], sizeof(ima_adpcm_init),
+ SNDRV_SB_CSP_LOAD_INITBLOCK);
+ if (err)
+ break;
+ if (play_rec_mode == SNDRV_SB_CSP_MODE_DSP_WRITE) {
+ err = snd_sb_csp_load(p, &ima_adpcm_playback[0],
+ sizeof(ima_adpcm_playback), 0);
+ p->mode = SNDRV_SB_CSP_MODE_DSP_WRITE;
+ } else {
+ err = snd_sb_csp_load(p, &ima_adpcm_capture[0],
+ sizeof(ima_adpcm_capture), 0);
+ p->mode = SNDRV_SB_CSP_MODE_DSP_READ;
+ }
+ p->acc_format = SNDRV_PCM_FMTBIT_IMA_ADPCM;
+ break;
+ default:
+ /* Decouple CSP from IRQ and DMAREQ lines */
+ if (p->running & SNDRV_SB_CSP_ST_AUTO) {
+ spin_lock_irqsave(&p->chip->reg_lock, flags);
+ set_mode_register(p->chip, 0xfc);
+ set_mode_register(p->chip, 0x00);
+ spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+ p->running = 0; /* clear autoloaded flag */
+ }
+ return -EINVAL;
+ }
+ if (err) {
+ p->acc_format = 0;
+ p->acc_channels = p->acc_width = p->acc_rates = 0;
+
+ p->running = 0; /* clear autoloaded flag */
+ p->mode = 0;
+ return (err);
+ } else {
+ p->running = SNDRV_SB_CSP_ST_AUTO; /* set autoloaded flag */
+ p->acc_width = SNDRV_SB_CSP_SAMPLE_16BIT; /* only 16 bit data */
+ p->acc_channels = SNDRV_SB_CSP_MONO | SNDRV_SB_CSP_STEREO;
+ p->acc_rates = SNDRV_SB_CSP_RATE_ALL; /* HW codecs accept all rates */
+ }
+
+ }
+ return (p->running & SNDRV_SB_CSP_ST_AUTO) ? 0 : -ENXIO;
+}
+
+/*
+ * start CSP
+ */
+static int snd_sb_csp_start(snd_sb_csp_t * p, int sample_width, int channels)
+{
+ unsigned char s_type; /* sample type */
+ unsigned char mixL, mixR;
+ int result = -EIO;
+ unsigned long flags;
+
+ if (!(p->running & (SNDRV_SB_CSP_ST_LOADED | SNDRV_SB_CSP_ST_AUTO))) {
+ snd_printd("%s: Microcode not loaded\n", __FUNCTION__);
+ return -ENXIO;
+ }
+ if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
+ snd_printd("%s: CSP already running\n", __FUNCTION__);
+ return -EBUSY;
+ }
+ if (!(sample_width & p->acc_width)) {
+ snd_printd("%s: Unsupported PCM sample width\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (!(channels & p->acc_channels)) {
+ snd_printd("%s: Invalid number of channels\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ /* Mute PCM volume */
+ spin_lock_irqsave(&p->chip->mixer_lock, flags);
+ mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+ mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+
+ spin_lock(&p->chip->reg_lock);
+ set_mode_register(p->chip, 0xc0); /* c0 = STOP */
+ set_mode_register(p->chip, 0x70); /* 70 = RUN */
+
+ s_type = 0x00;
+ if (channels == SNDRV_SB_CSP_MONO)
+ s_type = 0x11; /* 000n 000n (n = 1 if mono) */
+ if (sample_width == SNDRV_SB_CSP_SAMPLE_8BIT)
+ s_type |= 0x22; /* 00dX 00dX (d = 1 if 8 bit samples) */
+
+ if (set_codec_parameter(p->chip, 0x81, s_type)) {
+ snd_printd("%s: Set sample type command failed\n", __FUNCTION__);
+ goto __fail;
+ }
+ if (set_codec_parameter(p->chip, 0x80, 0x00)) {
+ snd_printd("%s: Codec start command failed\n", __FUNCTION__);
+ goto __fail;
+ }
+ p->run_width = sample_width;
+ p->run_channels = channels;
+
+ p->running |= SNDRV_SB_CSP_ST_RUNNING;
+
+ if (p->mode & SNDRV_SB_CSP_MODE_QSOUND) {
+ set_codec_parameter(p->chip, 0xe0, 0x01);
+ /* enable QSound decoder */
+ set_codec_parameter(p->chip, 0x00, 0xff);
+ set_codec_parameter(p->chip, 0x01, 0xff);
+ p->running |= SNDRV_SB_CSP_ST_QSOUND;
+ /* set QSound startup value */
+ snd_sb_csp_qsound_transfer(p);
+ }
+ result = 0;
+
+ __fail:
+ spin_unlock(&p->chip->reg_lock);
+
+ /* restore PCM volume */
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+ spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
+
+ return result;
+}
+
+/*
+ * stop CSP
+ */
+static int snd_sb_csp_stop(snd_sb_csp_t * p)
+{
+ int result;
+ unsigned char mixL, mixR;
+ unsigned long flags;
+
+ if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
+ return 0;
+
+ /* Mute PCM volume */
+ spin_lock_irqsave(&p->chip->mixer_lock, flags);
+ mixL = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV);
+ mixR = snd_sbmixer_read(p->chip, SB_DSP4_PCM_DEV + 1);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL & 0x7);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR & 0x7);
+
+ spin_lock(&p->chip->reg_lock);
+ if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+ set_codec_parameter(p->chip, 0xe0, 0x01);
+ /* disable QSound decoder */
+ set_codec_parameter(p->chip, 0x00, 0x00);
+ set_codec_parameter(p->chip, 0x01, 0x00);
+
+ p->running &= ~SNDRV_SB_CSP_ST_QSOUND;
+ }
+ result = set_mode_register(p->chip, 0xc0); /* c0 = STOP */
+ spin_unlock(&p->chip->reg_lock);
+
+ /* restore PCM volume */
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV, mixL);
+ snd_sbmixer_write(p->chip, SB_DSP4_PCM_DEV + 1, mixR);
+ spin_unlock_irqrestore(&p->chip->mixer_lock, flags);
+
+ if (!(result))
+ p->running &= ~(SNDRV_SB_CSP_ST_PAUSED | SNDRV_SB_CSP_ST_RUNNING);
+ return result;
+}
+
+/*
+ * pause CSP codec and hold DMA transfer
+ */
+static int snd_sb_csp_pause(snd_sb_csp_t * p)
+{
+ int result;
+ unsigned long flags;
+
+ if (!(p->running & SNDRV_SB_CSP_ST_RUNNING))
+ return -EBUSY;
+
+ spin_lock_irqsave(&p->chip->reg_lock, flags);
+ result = set_codec_parameter(p->chip, 0x80, 0xff);
+ spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+ if (!(result))
+ p->running |= SNDRV_SB_CSP_ST_PAUSED;
+
+ return result;
+}
+
+/*
+ * restart CSP codec and resume DMA transfer
+ */
+static int snd_sb_csp_restart(snd_sb_csp_t * p)
+{
+ int result;
+ unsigned long flags;
+
+ if (!(p->running & SNDRV_SB_CSP_ST_PAUSED))
+ return -EBUSY;
+
+ spin_lock_irqsave(&p->chip->reg_lock, flags);
+ result = set_codec_parameter(p->chip, 0x80, 0x00);
+ spin_unlock_irqrestore(&p->chip->reg_lock, flags);
+ if (!(result))
+ p->running &= ~SNDRV_SB_CSP_ST_PAUSED;
+
+ return result;
+}
+
+/* ------------------------------ */
+
+/*
+ * QSound mixer control for PCM
+ */
+
+static int snd_sb_qsound_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_sb_qsound_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = p->q_enabled ? 1 : 0;
+ return 0;
+}
+
+static int snd_sb_qsound_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int change;
+ unsigned char nval;
+
+ nval = ucontrol->value.integer.value[0] & 0x01;
+ spin_lock_irqsave(&p->q_lock, flags);
+ change = p->q_enabled != nval;
+ p->q_enabled = nval;
+ spin_unlock_irqrestore(&p->q_lock, flags);
+ return change;
+}
+
+static int snd_sb_qsound_space_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
+ return 0;
+}
+
+static int snd_sb_qsound_space_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->q_lock, flags);
+ ucontrol->value.integer.value[0] = p->qpos_left;
+ ucontrol->value.integer.value[1] = p->qpos_right;
+ spin_unlock_irqrestore(&p->q_lock, flags);
+ return 0;
+}
+
+static int snd_sb_qsound_space_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ snd_sb_csp_t *p = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int change;
+ unsigned char nval1, nval2;
+
+ nval1 = ucontrol->value.integer.value[0];
+ if (nval1 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT)
+ nval1 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
+ nval2 = ucontrol->value.integer.value[1];
+ if (nval2 > SNDRV_SB_CSP_QSOUND_MAX_RIGHT)
+ nval2 = SNDRV_SB_CSP_QSOUND_MAX_RIGHT;
+ spin_lock_irqsave(&p->q_lock, flags);
+ change = p->qpos_left != nval1 || p->qpos_right != nval2;
+ p->qpos_left = nval1;
+ p->qpos_right = nval2;
+ p->qpos_changed = change;
+ spin_unlock_irqrestore(&p->q_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_sb_qsound_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "3D Control - Switch",
+ .info = snd_sb_qsound_switch_info,
+ .get = snd_sb_qsound_switch_get,
+ .put = snd_sb_qsound_switch_put
+};
+
+static snd_kcontrol_new_t snd_sb_qsound_space = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "3D Control - Space",
+ .info = snd_sb_qsound_space_info,
+ .get = snd_sb_qsound_space_get,
+ .put = snd_sb_qsound_space_put
+};
+
+static int snd_sb_qsound_build(snd_sb_csp_t * p)
+{
+ snd_card_t * card;
+ int err;
+
+ snd_assert(p != NULL, return -EINVAL);
+
+ card = p->chip->card;
+ p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2;
+ p->qpos_changed = 0;
+
+ spin_lock_init(&p->q_lock);
+
+ if ((err = snd_ctl_add(card, p->qsound_switch = snd_ctl_new1(&snd_sb_qsound_switch, p))) < 0)
+ goto __error;
+ if ((err = snd_ctl_add(card, p->qsound_space = snd_ctl_new1(&snd_sb_qsound_space, p))) < 0)
+ goto __error;
+
+ return 0;
+
+ __error:
+ snd_sb_qsound_destroy(p);
+ return err;
+}
+
+static void snd_sb_qsound_destroy(snd_sb_csp_t * p)
+{
+ snd_card_t * card;
+ unsigned long flags;
+
+ snd_assert(p != NULL, return);
+
+ card = p->chip->card;
+
+ down_write(&card->controls_rwsem);
+ if (p->qsound_switch)
+ snd_ctl_remove(card, p->qsound_switch);
+ if (p->qsound_space)
+ snd_ctl_remove(card, p->qsound_space);
+ up_write(&card->controls_rwsem);
+
+ /* cancel pending transfer of QSound parameters */
+ spin_lock_irqsave (&p->q_lock, flags);
+ p->qpos_changed = 0;
+ spin_unlock_irqrestore (&p->q_lock, flags);
+}
+
+/*
+ * Transfer qsound parameters to CSP,
+ * function should be called from interrupt routine
+ */
+static int snd_sb_csp_qsound_transfer(snd_sb_csp_t * p)
+{
+ int err = -ENXIO;
+
+ spin_lock(&p->q_lock);
+ if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+ set_codec_parameter(p->chip, 0xe0, 0x01);
+ /* left channel */
+ set_codec_parameter(p->chip, 0x00, p->qpos_left);
+ set_codec_parameter(p->chip, 0x02, 0x00);
+ /* right channel */
+ set_codec_parameter(p->chip, 0x00, p->qpos_right);
+ set_codec_parameter(p->chip, 0x03, 0x00);
+ err = 0;
+ }
+ p->qpos_changed = 0;
+ spin_unlock(&p->q_lock);
+ return err;
+}
+
+/* ------------------------------ */
+
+/*
+ * proc interface
+ */
+static int init_proc_entry(snd_sb_csp_t * p, int device)
+{
+ char name[16];
+ snd_info_entry_t *entry;
+ sprintf(name, "cspD%d", device);
+ if (! snd_card_proc_new(p->chip->card, name, &entry))
+ snd_info_set_text_ops(entry, p, 1024, info_read);
+ return 0;
+}
+
+static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
+{
+ snd_sb_csp_t *p = entry->private_data;
+
+ snd_iprintf(buffer, "Creative Signal Processor [v%d.%d]\n", (p->version >> 4), (p->version & 0x0f));
+ snd_iprintf(buffer, "State: %cx%c%c%c\n", ((p->running & SNDRV_SB_CSP_ST_QSOUND) ? 'Q' : '-'),
+ ((p->running & SNDRV_SB_CSP_ST_PAUSED) ? 'P' : '-'),
+ ((p->running & SNDRV_SB_CSP_ST_RUNNING) ? 'R' : '-'),
+ ((p->running & SNDRV_SB_CSP_ST_LOADED) ? 'L' : '-'));
+ if (p->running & SNDRV_SB_CSP_ST_LOADED) {
+ snd_iprintf(buffer, "Codec: %s [func #%d]\n", p->codec_name, p->func_nr);
+ snd_iprintf(buffer, "Sample rates: ");
+ if (p->acc_rates == SNDRV_SB_CSP_RATE_ALL) {
+ snd_iprintf(buffer, "All\n");
+ } else {
+ snd_iprintf(buffer, "%s%s%s%s\n",
+ ((p->acc_rates & SNDRV_SB_CSP_RATE_8000) ? "8000Hz " : ""),
+ ((p->acc_rates & SNDRV_SB_CSP_RATE_11025) ? "11025Hz " : ""),
+ ((p->acc_rates & SNDRV_SB_CSP_RATE_22050) ? "22050Hz " : ""),
+ ((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : ""));
+ }
+ if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
+ snd_iprintf(buffer, "QSound decoder %sabled\n",
+ p->q_enabled ? "en" : "dis");
+ } else {
+ snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n",
+ p->acc_format,
+ ((p->acc_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? "16bit" : "-"),
+ ((p->acc_width & SNDRV_SB_CSP_SAMPLE_8BIT) ? "8bit" : "-"),
+ ((p->acc_channels & SNDRV_SB_CSP_MONO) ? "mono" : "-"),
+ ((p->acc_channels & SNDRV_SB_CSP_STEREO) ? "stereo" : "-"),
+ ((p->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) ? "playback" : "-"),
+ ((p->mode & SNDRV_SB_CSP_MODE_DSP_READ) ? "capture" : "-"));
+ }
+ }
+ if (p->running & SNDRV_SB_CSP_ST_AUTO) {
+ snd_iprintf(buffer, "Autoloaded Mu-Law, A-Law or Ima-ADPCM hardware codec\n");
+ }
+ if (p->running & SNDRV_SB_CSP_ST_RUNNING) {
+ snd_iprintf(buffer, "Processing %dbit %s PCM samples\n",
+ ((p->run_width & SNDRV_SB_CSP_SAMPLE_16BIT) ? 16 : 8),
+ ((p->run_channels & SNDRV_SB_CSP_MONO) ? "mono" : "stereo"));
+ }
+ if (p->running & SNDRV_SB_CSP_ST_QSOUND) {
+ snd_iprintf(buffer, "Qsound position: left = 0x%x, right = 0x%x\n",
+ p->qpos_left, p->qpos_right);
+ }
+}
+
+/* */
+
+EXPORT_SYMBOL(snd_sb_csp_new);
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_sb_csp_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_sb_csp_exit(void)
+{
+}
+
+module_init(alsa_sb_csp_init)
+module_exit(alsa_sb_csp_exit)
diff --git a/sound/isa/sb/sb16_csp_codecs.h b/sound/isa/sb/sb16_csp_codecs.h
new file mode 100644
index 000000000000..f0e8b0dcb572
--- /dev/null
+++ b/sound/isa/sb/sb16_csp_codecs.h
@@ -0,0 +1,949 @@
+/*
+ * Copyright (c) 1994 Creative Technology Ltd.
+ * Microcode files for SB16 Advanced Signal Processor
+ *
+ * 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
+ *
+ */
+
+static unsigned char mulaw_main[] = {
+ 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+ 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44,
+ 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+ 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+ 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+ 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+ 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
+ 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b,
+ 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80,
+ 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b,
+ 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80,
+ 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80,
+ 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82,
+ 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19,
+ 0xa2, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19,
+ 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19,
+ 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24,
+ 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44,
+ 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
+ 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39,
+ 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef,
+ 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1,
+ 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80,
+ 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54,
+ 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0,
+ 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a,
+ 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+ 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44,
+ 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80,
+ 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4,
+ 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4,
+ 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4,
+ 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4,
+ 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+ 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+ 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4,
+ 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4,
+ 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4,
+ 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80,
+ 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4,
+ 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b,
+ 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44,
+ 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4,
+ 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88,
+ 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39,
+ 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+ 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39,
+ 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+ 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02,
+ 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+ 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88,
+ 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19,
+ 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab,
+ 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0,
+ 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+ 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84,
+ 0x03, 0x03, 0x04, 0x49, 0x08, 0xc2, 0x00, 0x54,
+ 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44,
+ 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+ 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+ 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+ 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84,
+ 0x3e, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+ 0x08, 0x01, 0x00, 0x44, 0x6c, 0x00, 0x51, 0x8b,
+ 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b,
+ 0x04, 0x21, 0x00, 0x84, 0xfd, 0x00, 0x51, 0x8b,
+ 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44,
+ 0xfe, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
+ 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
+ 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19,
+ 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
+ 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+ 0xc7, 0x20, 0x04, 0x19, 0x5e, 0x00, 0x71, 0x8b,
+ 0xcf, 0x00, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
+ 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19,
+ 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19,
+ 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19,
+ 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+ 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19,
+ 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19,
+ 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a,
+ 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
+ 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f,
+ 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e,
+ 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22,
+ 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80,
+ 0x08, 0xa8, 0x00, 0x44, 0x20, 0x31, 0x49, 0x5c,
+ 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e,
+ 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0,
+ 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b,
+ 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02,
+ 0xcb, 0x00, 0xa8, 0x58, 0xb0, 0x05, 0xf3, 0x80,
+ 0x20, 0x04, 0xa8, 0x10, 0x00, 0x00, 0x10, 0x39,
+ 0xb0, 0x00, 0xe0, 0x8b, 0x20, 0x01, 0x00, 0x80,
+ 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02,
+ 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80,
+ 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0,
+ 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e,
+ 0xff, 0xff, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22,
+ 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80,
+ 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80,
+ 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a,
+ 0x08, 0x83, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80,
+ 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39,
+ 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a,
+ 0x0c, 0xa3, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82,
+ 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80,
+ 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45,
+ 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+ 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a,
+ 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a,
+ 0x00, 0x73, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+ 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+ 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0,
+ 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02,
+ 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80,
+ 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50,
+ 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80,
+ 0x00, 0xf3, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a,
+ 0x00, 0xd3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+ 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+ 0x00, 0x00, 0xb3, 0x10, 0xcc, 0x20, 0x00, 0x39,
+ 0x00, 0x00, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44,
+ 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x71, 0xc0,
+ 0x00, 0x30, 0x71, 0xc0, 0x00, 0xf3, 0x00, 0x44,
+ 0x20, 0x01, 0x00, 0x80, 0xff, 0xff, 0x62, 0x8b,
+ 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+ 0x20, 0x00, 0x7a, 0x80, 0x20, 0xe1, 0x09, 0x5c,
+ 0x82, 0x00, 0x09, 0x2f, 0x80, 0x4a, 0x09, 0x8e,
+ 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80,
+ 0x00, 0x00, 0x7a, 0xcb, 0x03, 0x00, 0xa8, 0x18,
+ 0x00, 0x00, 0x10, 0x39, 0x08, 0x04, 0xea, 0x10,
+ 0x08, 0x04, 0x7a, 0x10, 0x20, 0x00, 0x00, 0x80,
+ 0x40, 0x00, 0x21, 0xcb, 0x0c, 0x00, 0xe8, 0x10,
+ 0x00, 0x00, 0x41, 0x02, 0x0c, 0x00, 0xeb, 0x10,
+ 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02,
+ 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19,
+ 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a,
+ 0x0c, 0xe8, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a,
+ 0x03, 0x00, 0x48, 0x0a, 0x00, 0xb8, 0x04, 0x54,
+ 0xc3, 0x00, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0x44,
+ 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0xb8, 0x04, 0x54,
+ 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a,
+ 0x09, 0x00, 0x48, 0x0a, 0x00, 0x68, 0x04, 0x54,
+ 0xc9, 0x00, 0x04, 0x19, 0x0c, 0x68, 0x00, 0x44,
+ 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0x68, 0x04, 0x54,
+ 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a,
+ 0x06, 0x00, 0x48, 0x0a, 0x00, 0x78, 0x04, 0x54,
+ 0xc6, 0x00, 0x04, 0x19, 0x0c, 0x78, 0x00, 0x44,
+ 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0x78, 0x04, 0x54,
+ 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
+ 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xe8, 0x04, 0x54,
+ 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xe8, 0x00, 0x44,
+ 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xe8, 0x04, 0x54,
+ 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+ 0x20, 0x10, 0x71, 0x8b, 0x09, 0x3f, 0x07, 0x00
+};
+
+static unsigned char alaw_main[] = {
+ 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+ 0x00, 0xb1, 0x00, 0x44, 0x00, 0x61, 0x00, 0x44,
+ 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+ 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+ 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+ 0x50, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+ 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
+ 0x20, 0x01, 0x09, 0x0e, 0x20, 0x00, 0x71, 0x8b,
+ 0xa8, 0x01, 0xa8, 0x80, 0x88, 0x01, 0xa8, 0x80,
+ 0xa8, 0x00, 0x00, 0x80, 0xd2, 0x00, 0x71, 0x8b,
+ 0x88, 0x00, 0xa8, 0x80, 0xa8, 0x04, 0xb3, 0x80,
+ 0x20, 0x07, 0xb3, 0x80, 0x88, 0x03, 0xb1, 0x80,
+ 0xc0, 0x00, 0x09, 0x5c, 0xc2, 0x01, 0x00, 0x82,
+ 0xa1, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x04, 0x19,
+ 0x21, 0x20, 0x71, 0x8b, 0xcf, 0x00, 0x04, 0x19,
+ 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x00, 0x04, 0x19,
+ 0x00, 0x40, 0x00, 0x14, 0x08, 0x40, 0x04, 0x24,
+ 0x00, 0x00, 0x34, 0x49, 0x0c, 0x40, 0x00, 0x44,
+ 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
+ 0x32, 0x00, 0x09, 0x5c, 0x00, 0x00, 0x0c, 0x39,
+ 0x00, 0x00, 0x40, 0x45, 0x40, 0x40, 0x09, 0xef,
+ 0xff, 0x20, 0x09, 0xcf, 0x00, 0x04, 0x63, 0xa1,
+ 0x50, 0x03, 0x33, 0x80, 0x00, 0x04, 0xa3, 0x80,
+ 0x00, 0xff, 0xc2, 0x8b, 0x00, 0xd0, 0x04, 0x54,
+ 0x04, 0xe0, 0x00, 0xc4, 0x20, 0x03, 0x80, 0xc0,
+ 0x30, 0x00, 0x00, 0x88, 0x00, 0x00, 0x7a, 0x0a,
+ 0xd0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+ 0xc0, 0x00, 0x00, 0x99, 0x00, 0x60, 0x00, 0x44,
+ 0x00, 0xff, 0xc2, 0x8b, 0x20, 0x00, 0x00, 0x80,
+ 0x00, 0x0d, 0x42, 0x8b, 0x08, 0x32, 0x00, 0xc4,
+ 0x00, 0x0e, 0x42, 0x8b, 0x00, 0xa2, 0x00, 0xc4,
+ 0x00, 0x1e, 0x42, 0x8b, 0x0c, 0xb2, 0x00, 0xc4,
+ 0x00, 0x8e, 0x42, 0x8b, 0x00, 0x62, 0x00, 0xc4,
+ 0x00, 0x9e, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+ 0x00, 0xbe, 0x42, 0x8b, 0x08, 0x52, 0x00, 0xc4,
+ 0x00, 0x04, 0x42, 0x8b, 0x04, 0x72, 0x00, 0xc4,
+ 0x00, 0x24, 0x42, 0x8b, 0x00, 0xd2, 0x00, 0xc4,
+ 0x00, 0x55, 0x42, 0x8b, 0x00, 0x60, 0x00, 0xc4,
+ 0x00, 0x00, 0x40, 0x45, 0x20, 0x01, 0x79, 0x80,
+ 0x00, 0x30, 0x42, 0x8b, 0x08, 0x82, 0x00, 0xc4,
+ 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x71, 0x8b,
+ 0x40, 0x01, 0x00, 0x80, 0x00, 0x60, 0x00, 0x44,
+ 0xff, 0x00, 0xe2, 0xab, 0x00, 0xb2, 0x00, 0xc4,
+ 0x0f, 0xf2, 0xa8, 0xa8, 0x20, 0x00, 0xb1, 0x88,
+ 0x00, 0x00, 0x41, 0x02, 0x4d, 0xf2, 0x00, 0x39,
+ 0xc0, 0x01, 0x00, 0x82, 0x00, 0x60, 0x00, 0x44,
+ 0x0d, 0xf2, 0xa3, 0xa8, 0x4d, 0xf2, 0x00, 0x39,
+ 0x00, 0x60, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+ 0x20, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x02,
+ 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+ 0xff, 0x00, 0xe2, 0xab, 0xa0, 0x00, 0x00, 0x88,
+ 0x00, 0x00, 0x61, 0x10, 0x4d, 0xf2, 0x04, 0x19,
+ 0x00, 0x60, 0x00, 0x44, 0xff, 0x20, 0xe2, 0xab,
+ 0x60, 0x00, 0x00, 0x88, 0x00, 0x00, 0x71, 0xc0,
+ 0x4d, 0xf2, 0x04, 0x19, 0x00, 0x60, 0x00, 0x44,
+ 0x00, 0x00, 0x79, 0x80, 0x00, 0xe2, 0x00, 0x84,
+ 0x03, 0x03, 0x04, 0x49, 0x04, 0xc2, 0x00, 0x54,
+ 0x00, 0x60, 0x04, 0x64, 0x00, 0x60, 0x00, 0x44,
+ 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+ 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+ 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+ 0x00, 0x20, 0xe2, 0x8b, 0x0c, 0xf2, 0x00, 0x84,
+ 0xbe, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+ 0x08, 0x01, 0x00, 0x44, 0xec, 0x00, 0x51, 0x8b,
+ 0xc0, 0x20, 0x00, 0x39, 0x00, 0x02, 0xe2, 0x8b,
+ 0x04, 0x21, 0x00, 0x84, 0x3f, 0x00, 0x51, 0x8b,
+ 0xc2, 0x20, 0x00, 0x39, 0x00, 0x11, 0x00, 0x44,
+ 0x3d, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
+ 0xe5, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
+ 0x00, 0x00, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x19,
+ 0xcb, 0x20, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
+ 0xc3, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+ 0xc7, 0x20, 0x04, 0x19, 0xde, 0x00, 0x51, 0x8b,
+ 0xcf, 0x00, 0x00, 0x39, 0x00, 0x01, 0xb1, 0x80,
+ 0xc4, 0x20, 0x04, 0x19, 0xc6, 0x20, 0x04, 0x19,
+ 0xc8, 0x20, 0x04, 0x19, 0xca, 0x20, 0x04, 0x19,
+ 0x20, 0x00, 0x71, 0x8b, 0xcc, 0x20, 0x04, 0x19,
+ 0x03, 0x00, 0x04, 0x49, 0x00, 0x60, 0x00, 0x44,
+ 0x09, 0x04, 0x61, 0xa8, 0xc1, 0x00, 0x04, 0x19,
+ 0x0b, 0x04, 0x61, 0xa8, 0xca, 0x00, 0x04, 0x19,
+ 0x04, 0x60, 0x00, 0xd4, 0x0d, 0x00, 0x61, 0x0a,
+ 0x90, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
+ 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x40, 0x09, 0x8f,
+ 0x00, 0x01, 0x00, 0x45, 0x82, 0x00, 0x09, 0x2e,
+ 0x80, 0x40, 0x09, 0xcf, 0x02, 0x00, 0x61, 0x22,
+ 0x43, 0x25, 0x61, 0x22, 0x40, 0x33, 0x00, 0x80,
+ 0x08, 0x48, 0x00, 0x44, 0x20, 0xb1, 0x49, 0x5c,
+ 0x92, 0x00, 0x09, 0x4e, 0x02, 0x03, 0x09, 0x2e,
+ 0x00, 0x00, 0xa3, 0x02, 0xc0, 0x00, 0x71, 0xc0,
+ 0x20, 0x00, 0xeb, 0x80, 0x00, 0x04, 0xc2, 0x8b,
+ 0x20, 0x04, 0x61, 0x80, 0x00, 0x04, 0x7a, 0x02,
+ 0xc0, 0x00, 0x00, 0x82, 0x0c, 0xc3, 0x08, 0x49,
+ 0xb0, 0x01, 0xf3, 0x80, 0x00, 0x00, 0x10, 0x39,
+ 0x20, 0x00, 0x0c, 0x89, 0x0c, 0x88, 0x08, 0x49,
+ 0x03, 0x00, 0xa8, 0x18, 0x00, 0x00, 0x10, 0x39,
+ 0xbd, 0xff, 0x62, 0x8b, 0x20, 0x01, 0x00, 0x80,
+ 0x00, 0x00, 0x63, 0xcb, 0x00, 0x00, 0x7a, 0x02,
+ 0x40, 0x00, 0x01, 0x5b, 0x20, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0x13, 0x80,
+ 0x20, 0x00, 0x7a, 0x80, 0xe0, 0x21, 0x00, 0xc0,
+ 0x08, 0x00, 0x08, 0x49, 0x10, 0x41, 0x09, 0x8e,
+ 0xae, 0xae, 0x62, 0x8b, 0x00, 0x04, 0x61, 0x22,
+ 0x00, 0x03, 0x00, 0x45, 0x22, 0x01, 0x33, 0x80,
+ 0x20, 0x01, 0xa3, 0x02, 0x00, 0x00, 0x7a, 0x80,
+ 0xc0, 0x00, 0x00, 0x82, 0x07, 0x20, 0x40, 0x0a,
+ 0x08, 0xa3, 0x00, 0x84, 0x40, 0x21, 0x00, 0x80,
+ 0x40, 0x05, 0x93, 0x10, 0xc7, 0x20, 0x00, 0x39,
+ 0x00, 0x00, 0x40, 0x45, 0x07, 0x20, 0x40, 0x0a,
+ 0x0c, 0x93, 0x00, 0x84, 0x08, 0x00, 0x00, 0x82,
+ 0x0c, 0x24, 0x61, 0x50, 0x40, 0x01, 0x00, 0x80,
+ 0xc7, 0x20, 0x00, 0x39, 0x00, 0x00, 0x40, 0x45,
+ 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+ 0x42, 0x01, 0x09, 0x0e, 0x02, 0x20, 0x61, 0x0a,
+ 0x00, 0x01, 0x00, 0x45, 0x0c, 0x20, 0x60, 0x0a,
+ 0x00, 0xc3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+ 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+ 0x00, 0x24, 0x71, 0xc0, 0x20, 0x33, 0x33, 0xc0,
+ 0xe0, 0x01, 0xa3, 0x82, 0x22, 0x03, 0x7a, 0x02,
+ 0xc3, 0x01, 0xa3, 0x82, 0x20, 0x01, 0x33, 0x80,
+ 0x00, 0x00, 0x7a, 0x80, 0xc2, 0x01, 0xb3, 0x50,
+ 0xcc, 0x20, 0x00, 0x39, 0x00, 0x00, 0x71, 0x80,
+ 0x00, 0x08, 0x00, 0x44, 0x0c, 0x20, 0x60, 0x0a,
+ 0x00, 0xf3, 0x00, 0x84, 0x00, 0x04, 0xb1, 0x80,
+ 0x00, 0x00, 0x06, 0x39, 0x0c, 0x61, 0x04, 0xd4,
+ 0x00, 0x00, 0x71, 0xc0, 0x00, 0x00, 0x93, 0x10,
+ 0xcc, 0x20, 0x00, 0x39, 0x00, 0x08, 0x00, 0x44,
+ 0xcc, 0x20, 0x00, 0x39, 0x00, 0x20, 0x00, 0xc0,
+ 0x00, 0x30, 0x71, 0xc0, 0x00, 0x08, 0x00, 0x44,
+ 0x20, 0x01, 0x00, 0x80, 0xae, 0xae, 0x62, 0x8b,
+ 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+ 0x20, 0x00, 0x7a, 0x80, 0x20, 0xa1, 0x49, 0x5c,
+ 0x82, 0x00, 0x09, 0x6e, 0x80, 0x4a, 0x09, 0x8e,
+ 0xe0, 0x01, 0xb3, 0x82, 0x20, 0x04, 0xa3, 0x80,
+ 0x00, 0x00, 0x7a, 0xcb, 0x28, 0x04, 0xea, 0x10,
+ 0x0c, 0x04, 0x7a, 0x10, 0x70, 0x00, 0xc0, 0x8b,
+ 0x00, 0x00, 0x10, 0x39, 0x90, 0x03, 0x00, 0x80,
+ 0x40, 0x00, 0x21, 0x5b, 0x90, 0x00, 0x61, 0x80,
+ 0x0c, 0x8a, 0x08, 0x49, 0x00, 0x00, 0x1c, 0x19,
+ 0x40, 0x00, 0x08, 0x5b, 0x08, 0x00, 0x08, 0x49,
+ 0x20, 0x02, 0x00, 0x80, 0x03, 0x00, 0xa8, 0x18,
+ 0x00, 0x00, 0x14, 0x19, 0x40, 0x00, 0x21, 0xcb,
+ 0x00, 0x00, 0x41, 0x02, 0x00, 0x00, 0xeb, 0x80,
+ 0xf2, 0x01, 0x00, 0x82, 0x40, 0x21, 0x33, 0x02,
+ 0x08, 0x20, 0x61, 0x0a, 0xc4, 0x00, 0x04, 0x19,
+ 0xc7, 0x00, 0x00, 0x99, 0x02, 0x00, 0x61, 0x0a,
+ 0x0c, 0x0a, 0x04, 0x14, 0x01, 0x00, 0x61, 0x0a,
+ 0x03, 0x00, 0x48, 0x0a, 0x00, 0x58, 0x04, 0x54,
+ 0xc3, 0x00, 0x04, 0x19, 0x0c, 0x58, 0x00, 0x44,
+ 0x08, 0x00, 0xc8, 0x0a, 0x0c, 0x58, 0x04, 0x54,
+ 0xc8, 0x00, 0x04, 0x19, 0x0a, 0x00, 0x61, 0x0a,
+ 0x09, 0x00, 0x48, 0x0a, 0x00, 0xc8, 0x04, 0x54,
+ 0xc9, 0x00, 0x04, 0x19, 0x0c, 0xc8, 0x00, 0x44,
+ 0x0b, 0x00, 0xc8, 0x0a, 0x0c, 0xc8, 0x04, 0x54,
+ 0xcb, 0x00, 0x04, 0x19, 0x04, 0x00, 0x61, 0x0a,
+ 0x06, 0x00, 0x48, 0x0a, 0x00, 0xd8, 0x04, 0x54,
+ 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xd8, 0x00, 0x44,
+ 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xd8, 0x04, 0x54,
+ 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
+ 0x0c, 0x00, 0x48, 0x0a, 0x00, 0x0a, 0x04, 0x54,
+ 0xcc, 0x00, 0x04, 0x19, 0x0c, 0x0a, 0x00, 0x44,
+ 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0x0a, 0x04, 0x54,
+ 0xce, 0x00, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+ 0x20, 0x10, 0x71, 0x8b, 0x08, 0x42, 0x06, 0x00
+};
+
+
+static unsigned char ima_adpcm_init[] = {
+ 0x00, 0x10, 0x00, 0x44, 0x00, 0x00, 0x40, 0x45,
+ 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45,
+ 0x00, 0x00, 0x40, 0x45, 0xaa, 0xaa, 0x71, 0x8b,
+ 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+ 0xff, 0x6e, 0x21, 0x49, 0xff, 0x0f, 0xd4, 0x49,
+ 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+ 0x50, 0x05, 0xb1, 0x80, 0x62, 0x00, 0x19, 0x0e,
+ 0x21, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xb0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x40, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x60, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x50, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x70, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xc0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xe0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xd0, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x02, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x22, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x32, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xa2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xb2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x62, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xc2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xf2, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x11, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xa1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x61, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xe1, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x13, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xb3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xc3, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x18, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x68, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x0a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x4a, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x29, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x79, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x9b, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x14, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xf4, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xe6, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xe5, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xd7, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x2e, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x9d, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xef, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xb2, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x33, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x2a, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x3b, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x46, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x2c, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xdd, 0x20, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x01, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x9a, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x16, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x8e, 0x10, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xc2, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xc9, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x3c, 0x30, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x81, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xd4, 0x80, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x10, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x34, 0xa0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x02, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x75, 0x90, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x9a, 0xb0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x12, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x0d, 0x40, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x3c, 0x60, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xe7, 0x50, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x0e, 0x70, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xff, 0xc0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xc8, 0xd0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x57, 0xf0, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xc8, 0x22, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xb0, 0x32, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xdd, 0x82, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x90, 0xb2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x8a, 0x62, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xce, 0x72, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xa5, 0xd2, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x97, 0x21, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xa2, 0xa1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x5c, 0x41, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xfe, 0xc1, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x7a, 0x23, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x78, 0x93, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x67, 0x73, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x17, 0x28, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x88, 0x48, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xdb, 0xf8, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x2b, 0xba, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xf1, 0x09, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xdc, 0x69, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x19, 0x8b, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0xff, 0xfb, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x20, 0x00, 0x71, 0x8b, 0x88, 0x00, 0x00, 0x80,
+ 0x52, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0xff, 0xff, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
+ 0xc2, 0x00, 0x00, 0x82, 0x10, 0x00, 0x71, 0x8b,
+ 0xc2, 0x00, 0x00, 0x82, 0x80, 0x00, 0x71, 0x8b,
+ 0xc2, 0x00, 0x00, 0x82, 0x90, 0x00, 0x71, 0x8b,
+ 0xc2, 0x00, 0x00, 0x82, 0x40, 0x00, 0x71, 0x8b,
+ 0xc2, 0x00, 0x00, 0x82, 0xff, 0xff, 0x71, 0x8b,
+ 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
+ 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x00, 0x00, 0x82,
+ 0x10, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0x80, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0x90, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0x40, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0xff, 0xfb, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0x00, 0x04, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0x4a, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0x00, 0x00, 0x71, 0x8b, 0xc2, 0x00, 0x00, 0x82,
+ 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x30, 0x04, 0x19,
+ 0x10, 0x00, 0x09, 0x4f, 0xc2, 0x01, 0x00, 0x82,
+ 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+ 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+ 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+ 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+ 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+ 0xc2, 0x01, 0x00, 0x82, 0xc2, 0x01, 0x00, 0x82,
+ 0x00, 0x10, 0x71, 0x8b, 0xc1, 0x30, 0x04, 0x19,
+ 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09,
+ 0xcf, 0x30, 0x00, 0x09, 0x00, 0x00, 0x34, 0x49,
+ 0x00, 0x08, 0x00, 0x44, 0xc8, 0x54, 0x11, 0x00
+};
+
+static unsigned char ima_adpcm_playback[] = {
+ 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+ 0x0c, 0x50, 0x00, 0x44, 0x00, 0x70, 0x00, 0x44,
+ 0x04, 0x70, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+ 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+ 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+ 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0d, 0xd4, 0x49,
+ 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+ 0x50, 0x01, 0xb1, 0x80, 0x00, 0x01, 0xb1, 0x80,
+ 0xc9, 0x20, 0x04, 0x19, 0x51, 0x00, 0x71, 0x8b,
+ 0xcd, 0x00, 0x04, 0x19, 0xe4, 0x20, 0x71, 0x8b,
+ 0xcf, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b,
+ 0xcb, 0x20, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+ 0xc4, 0x20, 0x04, 0x19, 0x65, 0x00, 0x51, 0x8b,
+ 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
+ 0xc2, 0x30, 0x04, 0x19, 0x00, 0x00, 0x63, 0x80,
+ 0xc1, 0xa0, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
+ 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
+ 0x04, 0x40, 0x00, 0x14, 0x0c, 0x40, 0x00, 0x14,
+ 0x00, 0x04, 0x61, 0xa8, 0x02, 0x04, 0x61, 0xa8,
+ 0x04, 0x60, 0x04, 0x24, 0x00, 0x00, 0x34, 0x49,
+ 0x00, 0x50, 0x00, 0x44, 0x44, 0x04, 0x04, 0x39,
+ 0x00, 0x00, 0x40, 0x45, 0x00, 0x00, 0x40, 0x45,
+ 0x0f, 0x00, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45,
+ 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf,
+ 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80,
+ 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b,
+ 0x08, 0xf0, 0x04, 0x54, 0x0c, 0xd0, 0x00, 0xc4,
+ 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88,
+ 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82,
+ 0x08, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99,
+ 0x08, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b,
+ 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b,
+ 0x00, 0xa2, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b,
+ 0x0c, 0x92, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b,
+ 0x04, 0x62, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b,
+ 0x0c, 0x52, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b,
+ 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b,
+ 0x00, 0xc2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b,
+ 0x00, 0xf2, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b,
+ 0x00, 0x91, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b,
+ 0x08, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b,
+ 0x08, 0xe2, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+ 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b,
+ 0x00, 0x92, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+ 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80,
+ 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+ 0x0c, 0x42, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8,
+ 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02,
+ 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82,
+ 0x08, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8,
+ 0x4d, 0xf2, 0x00, 0x39, 0x08, 0x50, 0x00, 0x44,
+ 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88,
+ 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19,
+ 0x08, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+ 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10,
+ 0x4d, 0xf2, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44,
+ 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88,
+ 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19,
+ 0x08, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a,
+ 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a,
+ 0x04, 0xd2, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80,
+ 0xc1, 0x30, 0x04, 0x19, 0x08, 0x50, 0x00, 0x44,
+ 0x00, 0x00, 0x79, 0x80, 0x00, 0xa1, 0x00, 0x84,
+ 0xb5, 0x00, 0x51, 0x8b, 0xcf, 0x00, 0x00, 0x39,
+ 0x00, 0x01, 0xb1, 0x80, 0x88, 0x00, 0x04, 0x19,
+ 0x8a, 0x00, 0x04, 0x19, 0xc8, 0x20, 0x04, 0x19,
+ 0xca, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19,
+ 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
+ 0xb0, 0x00, 0x71, 0x8b, 0x8c, 0x00, 0x04, 0x19,
+ 0x8e, 0x00, 0x04, 0x19, 0x10, 0x00, 0x71, 0x8b,
+ 0xc4, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
+ 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
+ 0x03, 0x03, 0x04, 0x49, 0x04, 0x81, 0x00, 0x54,
+ 0x08, 0x50, 0x04, 0x64, 0x08, 0x50, 0x00, 0x44,
+ 0x00, 0x00, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+ 0x03, 0x00, 0x04, 0x49, 0x08, 0x50, 0x00, 0x44,
+ 0x20, 0x01, 0x63, 0x80, 0x00, 0x00, 0x06, 0x19,
+ 0x00, 0x02, 0xe2, 0x8b, 0x08, 0x41, 0x00, 0x84,
+ 0x65, 0x00, 0x51, 0x8b, 0xc2, 0x20, 0x00, 0x39,
+ 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19,
+ 0x08, 0x61, 0x00, 0x44, 0x2d, 0x00, 0x51, 0x8b,
+ 0xc2, 0x20, 0x00, 0x39, 0x00, 0x00, 0xb1, 0x80,
+ 0xc1, 0xa0, 0x04, 0x19, 0x03, 0x00, 0x04, 0x49,
+ 0x08, 0x50, 0x00, 0x44, 0x02, 0x20, 0x61, 0x0a,
+ 0x00, 0x01, 0x00, 0x45, 0x02, 0x30, 0x61, 0x0a,
+ 0x04, 0x03, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
+ 0x04, 0x71, 0x00, 0xc4, 0x00, 0x13, 0x00, 0x44,
+ 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+ 0x00, 0x49, 0x00, 0xc4, 0xca, 0x20, 0x04, 0x19,
+ 0x4a, 0x04, 0x04, 0x19, 0xff, 0x00, 0xe2, 0x8b,
+ 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19,
+ 0x0c, 0x2b, 0x08, 0x44, 0x8e, 0x00, 0x04, 0x19,
+ 0x03, 0x30, 0x61, 0x0a, 0xc8, 0x20, 0x00, 0x39,
+ 0x48, 0x04, 0x00, 0x39, 0x0a, 0x30, 0x61, 0x0a,
+ 0x0c, 0xf9, 0x08, 0x44, 0xcd, 0x10, 0x04, 0x19,
+ 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19,
+ 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44,
+ 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+ 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19,
+ 0xca, 0x30, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44,
+ 0x42, 0x0a, 0x09, 0x0e, 0x00, 0x01, 0x33, 0x11,
+ 0x8c, 0x01, 0xa3, 0x80, 0x00, 0x01, 0x7a, 0x10,
+ 0x80, 0x05, 0xb1, 0x80, 0x05, 0xb0, 0xe0, 0x18,
+ 0x00, 0x93, 0x00, 0x84, 0x00, 0x79, 0x08, 0x44,
+ 0x00, 0x04, 0x79, 0x80, 0x00, 0x49, 0x00, 0xc4,
+ 0x0c, 0x1b, 0x08, 0x44, 0x88, 0x00, 0x04, 0x19,
+ 0x8a, 0x00, 0x00, 0x99, 0x0c, 0xd9, 0x08, 0x44,
+ 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b,
+ 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b,
+ 0x00, 0x43, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
+ 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x43, 0x00, 0x84,
+ 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19,
+ 0x0c, 0xa8, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a,
+ 0x00, 0xd3, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
+ 0x04, 0x63, 0x00, 0xc4, 0x08, 0xf3, 0x00, 0x44,
+ 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+ 0x00, 0x49, 0x00, 0xc4, 0x20, 0x00, 0x04, 0x19,
+ 0xff, 0x00, 0xe2, 0x8b, 0x0c, 0xf9, 0x08, 0x44,
+ 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
+ 0x0c, 0x2b, 0x08, 0x44, 0x8c, 0x00, 0x04, 0x19,
+ 0x8e, 0x00, 0x04, 0x19, 0x03, 0x30, 0x61, 0x0a,
+ 0xc8, 0x20, 0x00, 0x39, 0xca, 0x20, 0x00, 0x39,
+ 0x48, 0x04, 0x00, 0x39, 0x4a, 0x04, 0x00, 0x39,
+ 0x0c, 0xd9, 0x08, 0x44, 0x0c, 0x5a, 0x00, 0x44,
+ 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+ 0x00, 0x49, 0x00, 0xc4, 0xc3, 0x30, 0x04, 0x19,
+ 0x0c, 0xd9, 0x08, 0x44, 0x42, 0x0a, 0x09, 0x0e,
+ 0x05, 0xb0, 0xe0, 0x18, 0x00, 0x18, 0x00, 0x84,
+ 0x00, 0x79, 0x08, 0x44, 0x00, 0x04, 0x79, 0x80,
+ 0x00, 0x49, 0x00, 0xc4, 0x0c, 0x1b, 0x08, 0x44,
+ 0x80, 0x01, 0x00, 0x80, 0x0c, 0xd9, 0x08, 0x44,
+ 0x42, 0x0a, 0x09, 0x0e, 0x80, 0x00, 0x71, 0x8b,
+ 0xc0, 0x04, 0xb1, 0x82, 0x10, 0x00, 0xe0, 0x0b,
+ 0x00, 0x88, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
+ 0x01, 0x30, 0xc8, 0x0a, 0x00, 0x88, 0x00, 0x84,
+ 0x00, 0x00, 0xb1, 0x80, 0xc2, 0x30, 0x04, 0x19,
+ 0x00, 0x01, 0x00, 0x11, 0x00, 0x0f, 0xe2, 0x8b,
+ 0x00, 0x00, 0x41, 0xcb, 0x8c, 0x00, 0x00, 0x80,
+ 0x00, 0x00, 0x48, 0xcb, 0x20, 0x00, 0x7a, 0x80,
+ 0x80, 0x01, 0x00, 0x80, 0x82, 0x0c, 0x09, 0x6e,
+ 0x03, 0x08, 0x09, 0x0e, 0x80, 0x40, 0x09, 0xcf,
+ 0x00, 0x01, 0x71, 0xc2, 0x00, 0x08, 0xc2, 0x1b,
+ 0x04, 0xb8, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
+ 0x20, 0x01, 0xf0, 0x80, 0x00, 0x01, 0xc2, 0x1b,
+ 0x04, 0x48, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
+ 0x20, 0x01, 0xf0, 0x80, 0x00, 0x02, 0xc2, 0x1b,
+ 0x04, 0x68, 0x00, 0xc4, 0x20, 0x05, 0xa8, 0x80,
+ 0x20, 0x01, 0xf0, 0x80, 0x20, 0x03, 0xa8, 0x80,
+ 0x00, 0x01, 0x00, 0x11, 0x00, 0x04, 0xc2, 0x8b,
+ 0x08, 0x78, 0x00, 0xc4, 0x00, 0x00, 0xe9, 0x80,
+ 0x05, 0xb0, 0xa8, 0x18, 0x00, 0x00, 0x4a, 0xcb,
+ 0x20, 0x00, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
+ 0x40, 0x01, 0x00, 0x80, 0xc4, 0x00, 0x04, 0x19,
+ 0xb0, 0x00, 0xe2, 0x8b, 0x06, 0x20, 0xa8, 0x0a,
+ 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x08, 0x09, 0x2e,
+ 0x00, 0x01, 0xa8, 0x02, 0x0c, 0xf9, 0x08, 0x44,
+ 0xcd, 0x10, 0x04, 0x19, 0x0c, 0x2b, 0x08, 0x44,
+ 0x03, 0x08, 0x09, 0x0e, 0x9a, 0x25, 0xb1, 0x60,
+ 0xa2, 0x0e, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0f,
+ 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
+ 0x00, 0x00, 0x61, 0xcb, 0x80, 0x01, 0x00, 0x80,
+ 0x03, 0x00, 0x09, 0x0f, 0x00, 0x01, 0x71, 0xc2,
+ 0x00, 0x08, 0xc2, 0x1b, 0x0c, 0x2a, 0x00, 0xc4,
+ 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
+ 0x00, 0x01, 0xc2, 0x1b, 0x0c, 0x1a, 0x00, 0xc4,
+ 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
+ 0x00, 0x02, 0xc2, 0x1b, 0x0c, 0x3a, 0x00, 0xc4,
+ 0x20, 0x05, 0xa8, 0x80, 0x20, 0x01, 0xf0, 0x80,
+ 0x20, 0x03, 0xa8, 0x80, 0x00, 0x01, 0x00, 0x11,
+ 0x00, 0x04, 0xc2, 0x8b, 0x04, 0xaa, 0x00, 0xc4,
+ 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
+ 0x00, 0x00, 0x4a, 0xcb, 0x20, 0x00, 0xa8, 0x22,
+ 0xd0, 0x01, 0x00, 0x82, 0x40, 0x01, 0x00, 0x80,
+ 0xc7, 0x00, 0x04, 0x19, 0xb0, 0x00, 0xe2, 0x8b,
+ 0x06, 0x20, 0xa8, 0x0a, 0x2f, 0x10, 0x61, 0x0a,
+ 0xf1, 0x08, 0x09, 0x2e, 0x00, 0x01, 0xa8, 0x02,
+ 0x0c, 0xf9, 0x08, 0x44, 0xcf, 0x10, 0x04, 0x19,
+ 0x0c, 0x2b, 0x08, 0x44, 0x9f, 0x35, 0xb1, 0x60,
+ 0x03, 0x08, 0x09, 0x0e, 0x00, 0x01, 0x71, 0x82,
+ 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x61, 0xcb,
+ 0x80, 0x01, 0x00, 0x80, 0xe4, 0x20, 0x71, 0x8b,
+ 0x00, 0x01, 0x00, 0x45, 0x90, 0x40, 0x09, 0x8f,
+ 0x00, 0x05, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+ 0x08, 0x19, 0x04, 0xd4, 0x93, 0x00, 0x01, 0x4f,
+ 0xe7, 0x00, 0x01, 0x6f, 0x0d, 0x30, 0x61, 0x0a,
+ 0x20, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82,
+ 0x02, 0x04, 0x61, 0xa8, 0xc2, 0x00, 0x00, 0x82,
+ 0xcd, 0x30, 0x00, 0x09, 0x02, 0x00, 0x00, 0x02,
+ 0x02, 0x00, 0x00, 0x02, 0xc0, 0x80, 0x00, 0x09,
+ 0x20, 0x00, 0x09, 0x49, 0x0f, 0x30, 0x61, 0x0a,
+ 0x0d, 0x30, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4,
+ 0x00, 0x80, 0xc8, 0x0a, 0x00, 0x29, 0x00, 0xc4,
+ 0x00, 0x04, 0xb1, 0x80, 0x00, 0x00, 0x06, 0x39,
+ 0xc9, 0x20, 0x04, 0x39, 0x00, 0x39, 0x00, 0x44,
+ 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+ 0x00, 0x04, 0xb1, 0x80, 0xc9, 0x20, 0x04, 0x39,
+ 0x00, 0x39, 0x00, 0x44, 0x09, 0x20, 0x23, 0x0a,
+ 0x00, 0x00, 0x06, 0x19, 0xc9, 0x20, 0x04, 0x19,
+ 0x00, 0x00, 0x40, 0x45, 0x02, 0x00, 0x61, 0x0a,
+ 0x0c, 0xb9, 0x04, 0x14, 0x04, 0x00, 0x61, 0x0a,
+ 0x06, 0x00, 0x48, 0x0a, 0x00, 0xa9, 0x04, 0x54,
+ 0xc6, 0x00, 0x04, 0x19, 0x0c, 0xa9, 0x00, 0x44,
+ 0x05, 0x00, 0xc8, 0x0a, 0x0c, 0xa9, 0x04, 0x54,
+ 0xc5, 0x00, 0x04, 0x19, 0x07, 0x00, 0x61, 0x0a,
+ 0x0c, 0x00, 0x48, 0x0a, 0x00, 0xb9, 0x04, 0x54,
+ 0xcc, 0x00, 0x04, 0x19, 0x0c, 0xb9, 0x00, 0x44,
+ 0x0e, 0x00, 0xc8, 0x0a, 0x0c, 0xb9, 0x04, 0x54,
+ 0xce, 0x00, 0x04, 0x19, 0x0c, 0x5a, 0x00, 0x44,
+ 0x82, 0x0d, 0x09, 0x2e, 0x80, 0x40, 0x09, 0xcf,
+ 0x00, 0xdf, 0x71, 0x8b, 0x80, 0x01, 0x00, 0x80,
+ 0x02, 0xc1, 0x00, 0x22, 0x03, 0xc1, 0x00, 0x22,
+ 0x00, 0x01, 0x65, 0x80, 0xd2, 0x05, 0x65, 0x82,
+ 0x40, 0x21, 0x00, 0x80, 0xd3, 0x03, 0x00, 0x82,
+ 0x40, 0x33, 0x00, 0x80, 0x0c, 0x5a, 0x00, 0x44,
+ 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
+ 0x08, 0xd9, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f,
+ 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a,
+ 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02,
+ 0x02, 0x00, 0x00, 0x03, 0xcf, 0x30, 0x00, 0x09,
+ 0x20, 0x00, 0x09, 0x49, 0x00, 0x04, 0x63, 0x80,
+ 0x04, 0xd9, 0x00, 0x44, 0x00, 0x04, 0xb1, 0x80,
+ 0x00, 0x00, 0x00, 0x46, 0x02, 0x30, 0x61, 0x0a,
+ 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19,
+ 0x00, 0x00, 0x00, 0x46, 0x0e, 0x10, 0xc8, 0x0a,
+ 0x0c, 0x0b, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
+ 0x04, 0x2b, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
+ 0x04, 0x2b, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
+ 0x00, 0x00, 0x00, 0x46, 0x00, 0x10, 0xa8, 0x18,
+ 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
+ 0x00, 0x00, 0x00, 0x46, 0x00, 0x04, 0x33, 0x80,
+ 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80,
+ 0x20, 0x01, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+ 0x20, 0x00, 0x7a, 0x80, 0x20, 0x03, 0x00, 0x80,
+ 0x00, 0x00, 0x00, 0x46, 0x16, 0xce, 0x11, 0x00
+};
+
+static unsigned char ima_adpcm_capture[] = {
+ 0x00, 0x10, 0x00, 0x44, 0x08, 0x00, 0x00, 0x44,
+ 0x00, 0x70, 0x00, 0x44, 0x08, 0xd0, 0x00, 0x44,
+ 0x00, 0xf0, 0x00, 0x44, 0x0d, 0xf2, 0x61, 0xa8,
+ 0x44, 0x04, 0x04, 0x19, 0x00, 0x00, 0x40, 0x45,
+ 0x00, 0x04, 0x63, 0x80, 0x00, 0x00, 0x06, 0x39,
+ 0xff, 0x2e, 0x21, 0x49, 0xff, 0x0c, 0xd4, 0x49,
+ 0x40, 0x49, 0x39, 0xac, 0x55, 0x55, 0x71, 0x8b,
+ 0x50, 0x01, 0xb1, 0x80, 0x00, 0x00, 0x71, 0x8b,
+ 0xc2, 0x30, 0x04, 0x19, 0xc0, 0xa0, 0x04, 0x19,
+ 0xc2, 0xa0, 0x04, 0x19, 0x89, 0x00, 0x71, 0x8b,
+ 0xc8, 0x30, 0x04, 0x19, 0x71, 0x00, 0x71, 0x8b,
+ 0xcd, 0x00, 0x04, 0x19, 0xcf, 0x00, 0x04, 0x19,
+ 0x80, 0x00, 0x71, 0x8b, 0xcb, 0x20, 0x04, 0x19,
+ 0x20, 0x00, 0x71, 0x8b, 0xc4, 0x20, 0x04, 0x19,
+ 0x47, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+ 0x00, 0x00, 0x63, 0x80, 0xc1, 0xa0, 0x04, 0x19,
+ 0x93, 0x00, 0x01, 0x4f, 0xcd, 0x30, 0x00, 0x09,
+ 0xcf, 0x30, 0x00, 0x09, 0x0c, 0x40, 0x00, 0x14,
+ 0x00, 0x60, 0x00, 0x14, 0x00, 0x04, 0x61, 0xa8,
+ 0x02, 0x04, 0x61, 0xa8, 0x0c, 0x60, 0x04, 0x24,
+ 0x00, 0x00, 0x34, 0x49, 0x08, 0x50, 0x00, 0x44,
+ 0x44, 0x04, 0x04, 0x39, 0x00, 0x00, 0x40, 0x45,
+ 0x08, 0x30, 0x61, 0x0a, 0x05, 0xb0, 0xe8, 0x18,
+ 0x0c, 0xc0, 0x04, 0x54, 0xc8, 0x30, 0x04, 0x19,
+ 0x09, 0x04, 0x00, 0xa8, 0x0b, 0x04, 0x00, 0xa8,
+ 0x00, 0x00, 0x40, 0x45, 0x09, 0x04, 0x61, 0xa8,
+ 0xc1, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8,
+ 0xca, 0x00, 0x04, 0x19, 0x0d, 0x00, 0x61, 0x0a,
+ 0x00, 0x01, 0x00, 0x45, 0x0f, 0x00, 0x61, 0x0a,
+ 0x00, 0x40, 0x09, 0x8f, 0x00, 0x01, 0x00, 0x45,
+ 0x40, 0x40, 0x09, 0xef, 0xff, 0x20, 0x09, 0xcf,
+ 0x00, 0x04, 0x63, 0xa1, 0x50, 0x03, 0x33, 0x80,
+ 0x00, 0x04, 0xa3, 0x80, 0x00, 0xff, 0xc2, 0x8b,
+ 0x0c, 0x12, 0x04, 0x54, 0x08, 0x12, 0x00, 0xc4,
+ 0x20, 0x03, 0x80, 0xc0, 0x30, 0x00, 0x00, 0x88,
+ 0x00, 0x00, 0x7a, 0x0a, 0xd0, 0x01, 0x00, 0x82,
+ 0x04, 0x50, 0x00, 0x44, 0xc0, 0x00, 0x00, 0x99,
+ 0x04, 0x50, 0x00, 0x44, 0x00, 0xff, 0xc2, 0x8b,
+ 0x20, 0x00, 0x00, 0x80, 0x00, 0x0d, 0x42, 0x8b,
+ 0x04, 0x42, 0x00, 0xc4, 0x00, 0x0e, 0x42, 0x8b,
+ 0x08, 0x52, 0x00, 0xc4, 0x00, 0x1e, 0x42, 0x8b,
+ 0x00, 0xe2, 0x00, 0xc4, 0x00, 0x8e, 0x42, 0x8b,
+ 0x08, 0xd2, 0x00, 0xc4, 0x00, 0x9e, 0x42, 0x8b,
+ 0x04, 0xf2, 0x00, 0xc4, 0x00, 0xbe, 0x42, 0x8b,
+ 0x04, 0xf2, 0x00, 0xc4, 0x00, 0x04, 0x42, 0x8b,
+ 0x04, 0x11, 0x00, 0xc4, 0x00, 0x24, 0x42, 0x8b,
+ 0x0c, 0x61, 0x00, 0xc4, 0x00, 0x55, 0x42, 0x8b,
+ 0x04, 0x50, 0x00, 0xc4, 0x00, 0x3f, 0x42, 0x8b,
+ 0x0c, 0x01, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+ 0x20, 0x01, 0x79, 0x80, 0x00, 0x30, 0x42, 0x8b,
+ 0x04, 0x62, 0x00, 0xc4, 0x00, 0x00, 0x40, 0x45,
+ 0x00, 0x00, 0x71, 0x8b, 0x40, 0x01, 0x00, 0x80,
+ 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+ 0x08, 0xc2, 0x00, 0xc4, 0x0f, 0xf2, 0xa8, 0xa8,
+ 0x20, 0x00, 0xb1, 0x88, 0x00, 0x00, 0x41, 0x02,
+ 0x4d, 0xf2, 0x00, 0x39, 0xc0, 0x01, 0x00, 0x82,
+ 0x04, 0x50, 0x00, 0x44, 0x0d, 0xf2, 0xa3, 0xa8,
+ 0x4d, 0xf2, 0x00, 0x39, 0x04, 0x50, 0x00, 0x44,
+ 0xff, 0x00, 0xe2, 0xab, 0x20, 0x00, 0x00, 0x88,
+ 0x00, 0x00, 0x61, 0x02, 0x4d, 0xf2, 0x04, 0x19,
+ 0x04, 0x50, 0x00, 0x44, 0xff, 0x00, 0xe2, 0xab,
+ 0xa0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x61, 0x10,
+ 0x4d, 0xf2, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44,
+ 0xff, 0x20, 0xe2, 0xab, 0x60, 0x00, 0x00, 0x88,
+ 0x00, 0x00, 0x71, 0xc0, 0x4d, 0xf2, 0x04, 0x19,
+ 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x7a, 0x0a,
+ 0x20, 0x01, 0xf0, 0x80, 0x01, 0xa0, 0x41, 0x0a,
+ 0x00, 0x11, 0x00, 0xc4, 0x20, 0x01, 0xf0, 0x80,
+ 0xc1, 0x30, 0x04, 0x19, 0x04, 0x50, 0x00, 0x44,
+ 0x00, 0x00, 0x79, 0x80, 0x0c, 0x41, 0x00, 0x84,
+ 0x89, 0x00, 0x71, 0x8b, 0xc8, 0x30, 0x04, 0x19,
+ 0x97, 0x00, 0x71, 0x8b, 0xcd, 0x00, 0x00, 0x39,
+ 0x00, 0x01, 0xb1, 0x80, 0x80, 0x00, 0x04, 0x19,
+ 0x82, 0x00, 0x04, 0x19, 0xc1, 0x20, 0x04, 0x19,
+ 0xc3, 0x20, 0x04, 0x19, 0xc2, 0x30, 0x04, 0x19,
+ 0xcd, 0x10, 0x04, 0x19, 0xcf, 0x10, 0x04, 0x19,
+ 0xb0, 0x00, 0x71, 0x8b, 0x84, 0x00, 0x04, 0x19,
+ 0x86, 0x00, 0x04, 0x19, 0x80, 0x00, 0x71, 0x8b,
+ 0xcb, 0x20, 0x04, 0x19, 0x93, 0x00, 0x01, 0x4f,
+ 0xcd, 0x30, 0x00, 0x09, 0xcf, 0x30, 0x00, 0x09,
+ 0x03, 0x02, 0x04, 0x49, 0x08, 0x41, 0x00, 0x14,
+ 0x04, 0x50, 0x00, 0x44, 0x00, 0x00, 0x63, 0x80,
+ 0x00, 0x00, 0x06, 0x19, 0x03, 0x00, 0x04, 0x49,
+ 0x04, 0x50, 0x00, 0x44, 0x20, 0x01, 0x63, 0x80,
+ 0x00, 0x00, 0x06, 0x19, 0x00, 0x20, 0xe2, 0x8b,
+ 0x00, 0xc1, 0x00, 0x84, 0x47, 0x00, 0x51, 0x8b,
+ 0xc0, 0x20, 0x00, 0x39, 0x00, 0x00, 0x63, 0x80,
+ 0xc1, 0xa0, 0x04, 0x19, 0x00, 0xe1, 0x00, 0x44,
+ 0xbd, 0x00, 0x51, 0x8b, 0xc0, 0x20, 0x00, 0x39,
+ 0x00, 0x00, 0xb1, 0x80, 0xc1, 0xa0, 0x04, 0x19,
+ 0x03, 0x00, 0x04, 0x49, 0x04, 0x50, 0x00, 0x44,
+ 0x00, 0x20, 0x61, 0x0a, 0x00, 0x01, 0x00, 0x45,
+ 0x02, 0x30, 0x61, 0x0a, 0x0c, 0x83, 0x00, 0xc4,
+ 0x0c, 0x78, 0x08, 0x44, 0x04, 0x5a, 0x08, 0x44,
+ 0xb2, 0x00, 0x09, 0x4f, 0x10, 0x42, 0x09, 0x8e,
+ 0x05, 0xb0, 0xe0, 0x18, 0x04, 0x23, 0x00, 0x84,
+ 0x0c, 0x01, 0x00, 0x11, 0x08, 0x05, 0x61, 0x10,
+ 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44,
+ 0xb2, 0x00, 0x09, 0x4f, 0x80, 0x00, 0x71, 0x8b,
+ 0xc0, 0x00, 0x00, 0x82, 0x0c, 0x01, 0x33, 0x10,
+ 0x28, 0x01, 0xa3, 0x10, 0x00, 0x01, 0x7a, 0x80,
+ 0x8c, 0x01, 0x00, 0x80, 0x02, 0x30, 0x61, 0x0a,
+ 0x20, 0x00, 0x04, 0x19, 0x0c, 0x83, 0x00, 0xc4,
+ 0x05, 0xb0, 0xc8, 0x18, 0x08, 0x43, 0x00, 0xc4,
+ 0x01, 0x30, 0xc8, 0x0a, 0x0c, 0x38, 0x00, 0xc4,
+ 0x08, 0x88, 0x00, 0x44, 0x0c, 0x78, 0x08, 0x44,
+ 0x04, 0x5a, 0x08, 0x44, 0x00, 0x00, 0xa3, 0x18,
+ 0x80, 0x00, 0x04, 0x19, 0x0b, 0x04, 0x61, 0xa8,
+ 0xc3, 0x20, 0x00, 0x39, 0xc3, 0x30, 0x04, 0x19,
+ 0x0f, 0x10, 0x61, 0x0a, 0xca, 0x30, 0x04, 0x19,
+ 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39,
+ 0xd1, 0x00, 0x09, 0x4f, 0x00, 0x04, 0x61, 0x02,
+ 0x08, 0x63, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a,
+ 0x20, 0x00, 0x00, 0x39, 0xa3, 0x00, 0x09, 0x4f,
+ 0x00, 0x04, 0x61, 0x02, 0x00, 0x48, 0x08, 0x44,
+ 0x08, 0x88, 0x00, 0x44, 0x02, 0x30, 0x61, 0x0a,
+ 0x00, 0x08, 0x00, 0xc4, 0x0c, 0x78, 0x08, 0x44,
+ 0x04, 0x5a, 0x08, 0x44, 0xb2, 0x00, 0x09, 0x0f,
+ 0x10, 0x40, 0x09, 0x8e, 0x00, 0x00, 0x68, 0x5b,
+ 0x20, 0x04, 0xb1, 0x80, 0x02, 0x00, 0x61, 0x5b,
+ 0x88, 0x03, 0x7a, 0x80, 0xac, 0x01, 0x00, 0x80,
+ 0x05, 0xb0, 0xe0, 0x18, 0x00, 0xd3, 0x00, 0x84,
+ 0x00, 0x49, 0x08, 0x44, 0x00, 0x48, 0x08, 0x44,
+ 0xb2, 0x00, 0x09, 0x0f, 0x80, 0x00, 0x71, 0x8b,
+ 0xc0, 0x00, 0x00, 0x82, 0x02, 0x30, 0x61, 0x0a,
+ 0x00, 0x08, 0x00, 0xc4, 0x05, 0xb0, 0xc8, 0x18,
+ 0x0c, 0x18, 0x00, 0xc4, 0x01, 0x30, 0xc8, 0x0a,
+ 0x0c, 0x38, 0x00, 0xc4, 0x08, 0x88, 0x00, 0x44,
+ 0x0c, 0x78, 0x08, 0x44, 0x00, 0x00, 0x61, 0x18,
+ 0x20, 0x05, 0xb1, 0x80, 0x00, 0x00, 0x68, 0xcb,
+ 0x80, 0x00, 0x04, 0x19, 0x0d, 0x10, 0x61, 0x0a,
+ 0xc3, 0x30, 0x04, 0x19, 0x0b, 0x04, 0x41, 0xa8,
+ 0x09, 0x04, 0x41, 0xa8, 0xe1, 0x20, 0x00, 0x39,
+ 0x08, 0x38, 0x00, 0x44, 0x03, 0x30, 0x41, 0x0a,
+ 0x20, 0x04, 0xb1, 0x80, 0x00, 0x48, 0x08, 0x44,
+ 0x08, 0x88, 0x00, 0x44, 0x00, 0x00, 0xb1, 0x80,
+ 0xc2, 0x30, 0x04, 0x19, 0x0c, 0xb8, 0x00, 0xd4,
+ 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
+ 0x0c, 0xb8, 0x00, 0xc4, 0x93, 0x00, 0x01, 0x4f,
+ 0xe7, 0x00, 0x01, 0x6f, 0x0f, 0x30, 0x61, 0x0a,
+ 0x20, 0x00, 0x00, 0x88, 0x02, 0x00, 0x61, 0x02,
+ 0x41, 0x04, 0x04, 0x19, 0x02, 0x04, 0x61, 0x02,
+ 0x43, 0x04, 0x04, 0x39, 0xcf, 0x30, 0x00, 0x09,
+ 0x20, 0x00, 0x09, 0x49, 0x00, 0x59, 0x00, 0x44,
+ 0x93, 0x00, 0x01, 0x4f, 0xe7, 0x00, 0x01, 0x6f,
+ 0x0d, 0x30, 0x61, 0x0a, 0x20, 0x00, 0x61, 0x88,
+ 0xc2, 0x00, 0x00, 0x82, 0xc2, 0x03, 0x00, 0x82,
+ 0xcd, 0x30, 0x00, 0x09, 0x20, 0x00, 0x09, 0x49,
+ 0x0f, 0x30, 0x61, 0x0a, 0x0d, 0x30, 0xc8, 0x0a,
+ 0x0c, 0x58, 0x00, 0x84, 0x02, 0x30, 0x61, 0x0a,
+ 0x05, 0xb0, 0xa8, 0x18, 0xc2, 0x30, 0x04, 0x19,
+ 0x00, 0x00, 0x00, 0x46, 0x90, 0x40, 0x09, 0x8f,
+ 0x12, 0x04, 0x09, 0x6e, 0x03, 0x00, 0x09, 0x0e,
+ 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
+ 0x00, 0x00, 0x61, 0xcb, 0x80, 0x04, 0xb1, 0x80,
+ 0x00, 0x01, 0xe0, 0x60, 0x0c, 0xd8, 0x04, 0x14,
+ 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b,
+ 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2,
+ 0x20, 0x00, 0xc0, 0x80, 0x08, 0x0a, 0x04, 0x54,
+ 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b,
+ 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+ 0x20, 0x00, 0xc0, 0x80, 0x0c, 0x2a, 0x04, 0x54,
+ 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b,
+ 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+ 0x20, 0x00, 0xc0, 0x80, 0x08, 0x3a, 0x04, 0x54,
+ 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b,
+ 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82,
+ 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11,
+ 0x40, 0x00, 0xc2, 0x8b, 0x00, 0xaa, 0x00, 0xc4,
+ 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
+ 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
+ 0xf0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a,
+ 0x2d, 0x10, 0x61, 0x0a, 0xd1, 0x00, 0x09, 0x2e,
+ 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a,
+ 0x0c, 0xba, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
+ 0x04, 0x4a, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
+ 0x04, 0x4a, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
+ 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18,
+ 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
+ 0x03, 0x00, 0x09, 0x0e, 0x9a, 0x01, 0x00, 0x60,
+ 0x32, 0x00, 0x09, 0x2e, 0x00, 0x00, 0x00, 0x46,
+ 0x00, 0x01, 0x71, 0x82, 0x20, 0x01, 0x00, 0x80,
+ 0x00, 0x00, 0x61, 0xcb, 0x80, 0x24, 0xb1, 0xc0,
+ 0x00, 0x31, 0xe0, 0x60, 0x0c, 0xca, 0x04, 0x14,
+ 0x00, 0x01, 0xeb, 0x80, 0x40, 0x00, 0x52, 0x1b,
+ 0x80, 0x00, 0x79, 0x80, 0xc0, 0x01, 0x71, 0xc2,
+ 0x20, 0x00, 0xc0, 0x80, 0x08, 0xda, 0x04, 0x54,
+ 0xc0, 0x04, 0xa8, 0x82, 0x80, 0x00, 0x72, 0x1b,
+ 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+ 0x20, 0x00, 0xc0, 0x80, 0x0c, 0xfa, 0x04, 0x54,
+ 0xc0, 0x04, 0xa8, 0x82, 0x10, 0x00, 0x72, 0x1b,
+ 0x80, 0x00, 0x00, 0x80, 0x00, 0x01, 0xf0, 0x80,
+ 0x20, 0x00, 0xc0, 0x80, 0x08, 0x29, 0x04, 0x54,
+ 0xc0, 0x04, 0xa8, 0x82, 0x20, 0x00, 0x72, 0x1b,
+ 0x80, 0x00, 0x00, 0x80, 0xc0, 0x03, 0xf0, 0x82,
+ 0x20, 0x00, 0xa0, 0x80, 0x00, 0x01, 0x00, 0x11,
+ 0x40, 0x00, 0xc2, 0x8b, 0x00, 0x39, 0x00, 0xc4,
+ 0x00, 0x00, 0xe9, 0x80, 0x05, 0xb0, 0xa8, 0x18,
+ 0x00, 0x01, 0xa8, 0x22, 0xd0, 0x01, 0x00, 0x82,
+ 0xb0, 0x00, 0xe2, 0x1b, 0x06, 0x20, 0xa8, 0x0a,
+ 0x2f, 0x10, 0x61, 0x0a, 0xf1, 0x00, 0x09, 0x2e,
+ 0x00, 0x01, 0xa8, 0x02, 0x0e, 0x10, 0xc8, 0x0a,
+ 0x0c, 0xa9, 0x04, 0x14, 0x0e, 0x10, 0x61, 0x0a,
+ 0x04, 0x99, 0x00, 0x44, 0x0c, 0x10, 0xc8, 0x0a,
+ 0x04, 0x99, 0x04, 0x54, 0x0c, 0x10, 0x61, 0x0a,
+ 0xd0, 0x01, 0x00, 0x82, 0x00, 0x10, 0xa8, 0x18,
+ 0xa0, 0x00, 0x00, 0x88, 0x00, 0x01, 0x71, 0x82,
+ 0x9f, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x46,
+ 0x00, 0x00, 0x33, 0x80, 0x00, 0x00, 0x83, 0x80,
+ 0x20, 0x00, 0x7a, 0x80, 0x20, 0x07, 0x33, 0x80,
+ 0x00, 0x00, 0x83, 0x80, 0x20, 0x04, 0x7a, 0x80,
+ 0x20, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x46,
+ 0x02, 0x00, 0x61, 0x0a, 0x04, 0x1b, 0x04, 0x14,
+ 0x01, 0x00, 0x61, 0x0a, 0x03, 0x00, 0x48, 0x0a,
+ 0x0c, 0x79, 0x04, 0x54, 0xc3, 0x00, 0x04, 0x19,
+ 0x04, 0xc9, 0x00, 0x44, 0x08, 0x00, 0xc8, 0x0a,
+ 0x04, 0xc9, 0x04, 0x54, 0xc8, 0x00, 0x04, 0x19,
+ 0x0a, 0x00, 0x61, 0x0a, 0x09, 0x00, 0x48, 0x0a,
+ 0x0c, 0xe9, 0x04, 0x54, 0xc9, 0x00, 0x04, 0x19,
+ 0x04, 0xd9, 0x00, 0x44, 0x0b, 0x00, 0xc8, 0x0a,
+ 0x04, 0xd9, 0x04, 0x54, 0xcb, 0x00, 0x04, 0x19,
+ 0x04, 0x00, 0x61, 0x0a, 0x06, 0x00, 0x48, 0x0a,
+ 0x0c, 0xf9, 0x04, 0x54, 0xc6, 0x00, 0x04, 0x19,
+ 0x04, 0x0b, 0x00, 0x44, 0x05, 0x00, 0xc8, 0x0a,
+ 0x04, 0x0b, 0x04, 0x54, 0xc5, 0x00, 0x04, 0x19,
+ 0x07, 0x00, 0x61, 0x0a, 0x0c, 0x00, 0x48, 0x0a,
+ 0x0c, 0x2b, 0x04, 0x54, 0xcc, 0x00, 0x04, 0x19,
+ 0x04, 0x1b, 0x00, 0x44, 0x0e, 0x00, 0xc8, 0x0a,
+ 0x04, 0x1b, 0x04, 0x54, 0xce, 0x00, 0x04, 0x19,
+ 0x00, 0x00, 0x40, 0x45, 0x92, 0x20, 0x71, 0x8b,
+ 0xa6, 0xc5, 0x11, 0x00
+};
diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c
new file mode 100644
index 000000000000..a6a0fa516268
--- /dev/null
+++ b/sound/isa/sb/sb16_main.c
@@ -0,0 +1,916 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Routines for control of 16-bit SoundBlaster cards and clones
+ * Note: This is very ugly hardware which uses one 8-bit DMA channel and
+ * second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't
+ * transfer 16-bit samples and 16-bit DMA channels can't transfer
+ * 8-bit samples. This make full duplex more complicated than
+ * can be... People, don't buy these soundcards for full 16-bit
+ * duplex!!!
+ * Note: 16-bit wide is assigned to first direction which made request.
+ * With full duplex - playback is preferred with abstract layer.
+ *
+ * Note: Some chip revisions have hardware bug. Changing capture
+ * channel from full-duplex 8bit DMA to 16bit DMA will block
+ * 16bit DMA transfers from DSP chip (capture) until 8bit transfer
+ * to DSP chip (playback) starts. This bug can be avoided with
+ * "16bit DMA Allocation" setting set to Playback or Capture.
+ *
+ *
+ * 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 <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/sb16_csp.h>
+#include <sound/mpu401.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_SND_SB16_CSP
+static void snd_sb16_csp_playback_prepare(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+ if (chip->hardware == SB_HW_16CSP) {
+ snd_sb_csp_t *csp = chip->csp;
+
+ if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+ /* manually loaded codec */
+ if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) &&
+ ((1U << runtime->format) == csp->acc_format)) {
+ /* Supported runtime PCM format for playback */
+ if (csp->ops.csp_use(csp) == 0) {
+ /* If CSP was successfully acquired */
+ goto __start_CSP;
+ }
+ } else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) {
+ /* QSound decoder is loaded and enabled */
+ if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) {
+ /* Only for simple PCM formats */
+ if (csp->ops.csp_use(csp) == 0) {
+ /* If CSP was successfully acquired */
+ goto __start_CSP;
+ }
+ }
+ }
+ } else if (csp->ops.csp_use(csp) == 0) {
+ /* Acquire CSP and try to autoload hardware codec */
+ if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) {
+ /* Unsupported format, release CSP */
+ csp->ops.csp_unuse(csp);
+ } else {
+ __start_CSP:
+ /* Try to start CSP */
+ if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ?
+ SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT,
+ (runtime->channels > 1) ?
+ SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) {
+ /* Failed, release CSP */
+ csp->ops.csp_unuse(csp);
+ } else {
+ /* Success, CSP acquired and running */
+ chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE;
+ }
+ }
+ }
+ }
+}
+
+static void snd_sb16_csp_capture_prepare(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+ if (chip->hardware == SB_HW_16CSP) {
+ snd_sb_csp_t *csp = chip->csp;
+
+ if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+ /* manually loaded codec */
+ if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) &&
+ ((1U << runtime->format) == csp->acc_format)) {
+ /* Supported runtime PCM format for capture */
+ if (csp->ops.csp_use(csp) == 0) {
+ /* If CSP was successfully acquired */
+ goto __start_CSP;
+ }
+ }
+ } else if (csp->ops.csp_use(csp) == 0) {
+ /* Acquire CSP and try to autoload hardware codec */
+ if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) {
+ /* Unsupported format, release CSP */
+ csp->ops.csp_unuse(csp);
+ } else {
+ __start_CSP:
+ /* Try to start CSP */
+ if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ?
+ SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT,
+ (runtime->channels > 1) ?
+ SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) {
+ /* Failed, release CSP */
+ csp->ops.csp_unuse(csp);
+ } else {
+ /* Success, CSP acquired and running */
+ chip->open = SNDRV_SB_CSP_MODE_DSP_READ;
+ }
+ }
+ }
+ }
+}
+
+static void snd_sb16_csp_update(sb_t *chip)
+{
+ if (chip->hardware == SB_HW_16CSP) {
+ snd_sb_csp_t *csp = chip->csp;
+
+ if (csp->qpos_changed) {
+ spin_lock(&chip->reg_lock);
+ csp->ops.csp_qsound_transfer (csp);
+ spin_unlock(&chip->reg_lock);
+ }
+ }
+}
+
+static void snd_sb16_csp_playback_open(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+ /* CSP decoders (QSound excluded) support only 16bit transfers */
+ if (chip->hardware == SB_HW_16CSP) {
+ snd_sb_csp_t *csp = chip->csp;
+
+ if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+ /* manually loaded codec */
+ if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) {
+ runtime->hw.formats |= csp->acc_format;
+ }
+ } else {
+ /* autoloaded codecs */
+ runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+ SNDRV_PCM_FMTBIT_IMA_ADPCM;
+ }
+ }
+}
+
+static void snd_sb16_csp_playback_close(sb_t *chip)
+{
+ if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) {
+ snd_sb_csp_t *csp = chip->csp;
+
+ if (csp->ops.csp_stop(csp) == 0) {
+ csp->ops.csp_unuse(csp);
+ chip->open = 0;
+ }
+ }
+}
+
+static void snd_sb16_csp_capture_open(sb_t *chip, snd_pcm_runtime_t *runtime)
+{
+ /* CSP coders support only 16bit transfers */
+ if (chip->hardware == SB_HW_16CSP) {
+ snd_sb_csp_t *csp = chip->csp;
+
+ if (csp->running & SNDRV_SB_CSP_ST_LOADED) {
+ /* manually loaded codec */
+ if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) {
+ runtime->hw.formats |= csp->acc_format;
+ }
+ } else {
+ /* autoloaded codecs */
+ runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+ SNDRV_PCM_FMTBIT_IMA_ADPCM;
+ }
+ }
+}
+
+static void snd_sb16_csp_capture_close(sb_t *chip)
+{
+ if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) {
+ snd_sb_csp_t *csp = chip->csp;
+
+ if (csp->ops.csp_stop(csp) == 0) {
+ csp->ops.csp_unuse(csp);
+ chip->open = 0;
+ }
+ }
+}
+#else
+#define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/
+#define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/
+#define snd_sb16_csp_update(chip) /*nop*/
+#define snd_sb16_csp_playback_open(chip, runtime) /*nop*/
+#define snd_sb16_csp_playback_close(chip) /*nop*/
+#define snd_sb16_csp_capture_open(chip, runtime) /*nop*/
+#define snd_sb16_csp_capture_close(chip) /*nop*/
+#endif
+
+
+static void snd_sb16_setup_rate(sb_t *chip,
+ unsigned short rate,
+ int channel)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16))
+ snd_sb_ack_16bit(chip);
+ else
+ snd_sb_ack_8bit(chip);
+ if (!(chip->mode & SB_RATE_LOCK)) {
+ chip->locked_rate = rate;
+ snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN);
+ snd_sbdsp_command(chip, rate >> 8);
+ snd_sbdsp_command(chip, rate & 0xff);
+ snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT);
+ snd_sbdsp_command(chip, rate >> 8);
+ snd_sbdsp_command(chip, rate & 0xff);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+static int snd_sb16_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sb16_hw_free(snd_pcm_substream_t * substream)
+{
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int snd_sb16_playback_prepare(snd_pcm_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned char format;
+ unsigned int size, count, dma;
+
+ snd_sb16_csp_playback_prepare(chip, runtime);
+ if (snd_pcm_format_unsigned(runtime->format) > 0) {
+ format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO;
+ } else {
+ format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO;
+ }
+
+ snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK);
+ size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
+ dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16;
+ snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
+
+ count = snd_pcm_lib_period_bytes(substream);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->mode & SB_MODE_PLAYBACK_16) {
+ count >>= 1;
+ count--;
+ snd_sbdsp_command(chip, SB_DSP4_OUT16_AI);
+ snd_sbdsp_command(chip, format);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+ } else {
+ count--;
+ snd_sbdsp_command(chip, SB_DSP4_OUT8_AI);
+ snd_sbdsp_command(chip, format);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0;
+}
+
+static int snd_sb16_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ int result = 0;
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ chip->mode |= SB_RATE_LOCK_PLAYBACK;
+ snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF);
+ /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */
+ if (chip->mode & SB_RATE_LOCK_CAPTURE)
+ snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+ chip->mode &= ~SB_RATE_LOCK_PLAYBACK;
+ break;
+ default:
+ result = -EINVAL;
+ }
+ spin_unlock(&chip->reg_lock);
+ return result;
+}
+
+static int snd_sb16_capture_prepare(snd_pcm_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned char format;
+ unsigned int size, count, dma;
+
+ snd_sb16_csp_capture_prepare(chip, runtime);
+ if (snd_pcm_format_unsigned(runtime->format) > 0) {
+ format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO;
+ } else {
+ format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO;
+ }
+ snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE);
+ size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
+ dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16;
+ snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
+
+ count = snd_pcm_lib_period_bytes(substream);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (chip->mode & SB_MODE_CAPTURE_16) {
+ count >>= 1;
+ count--;
+ snd_sbdsp_command(chip, SB_DSP4_IN16_AI);
+ snd_sbdsp_command(chip, format);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+ } else {
+ count--;
+ snd_sbdsp_command(chip, SB_DSP4_IN8_AI);
+ snd_sbdsp_command(chip, format);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0;
+}
+
+static int snd_sb16_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ int result = 0;
+
+ spin_lock(&chip->reg_lock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ chip->mode |= SB_RATE_LOCK_CAPTURE;
+ snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF);
+ /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */
+ if (chip->mode & SB_RATE_LOCK_PLAYBACK)
+ snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON);
+ chip->mode &= ~SB_RATE_LOCK_CAPTURE;
+ break;
+ default:
+ result = -EINVAL;
+ }
+ spin_unlock(&chip->reg_lock);
+ return result;
+}
+
+irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ sb_t *chip = dev_id;
+ unsigned char status;
+ int ok;
+
+ spin_lock(&chip->mixer_lock);
+ status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
+ spin_unlock(&chip->mixer_lock);
+ if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi_callback)
+ chip->rmidi_callback(irq, chip->rmidi->private_data, regs);
+ if (status & SB_IRQTYPE_8BIT) {
+ ok = 0;
+ if (chip->mode & SB_MODE_PLAYBACK_8) {
+ snd_pcm_period_elapsed(chip->playback_substream);
+ snd_sb16_csp_update(chip);
+ ok++;
+ }
+ if (chip->mode & SB_MODE_CAPTURE_8) {
+ snd_pcm_period_elapsed(chip->capture_substream);
+ ok++;
+ }
+ spin_lock(&chip->reg_lock);
+ if (!ok)
+ snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+ snd_sb_ack_8bit(chip);
+ spin_unlock(&chip->reg_lock);
+ }
+ if (status & SB_IRQTYPE_16BIT) {
+ ok = 0;
+ if (chip->mode & SB_MODE_PLAYBACK_16) {
+ snd_pcm_period_elapsed(chip->playback_substream);
+ snd_sb16_csp_update(chip);
+ ok++;
+ }
+ if (chip->mode & SB_MODE_CAPTURE_16) {
+ snd_pcm_period_elapsed(chip->capture_substream);
+ ok++;
+ }
+ spin_lock(&chip->reg_lock);
+ if (!ok)
+ snd_sbdsp_command(chip, SB_DSP_DMA16_OFF);
+ snd_sb_ack_16bit(chip);
+ spin_unlock(&chip->reg_lock);
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+
+ */
+
+static snd_pcm_uframes_t snd_sb16_playback_pointer(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int dma;
+ size_t ptr;
+
+ dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16;
+ ptr = snd_dma_pointer(dma, chip->p_dma_size);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_sb16_capture_pointer(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int dma;
+ size_t ptr;
+
+ dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16;
+ ptr = snd_dma_pointer(dma, chip->c_dma_size);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static snd_pcm_hardware_t snd_sb16_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = 0,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+ .rate_min = 4000,
+ .rate_max = 44100,
+ .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,
+};
+
+static snd_pcm_hardware_t snd_sb16_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = 0,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+ .rate_min = 4000,
+ .rate_max = 44100,
+ .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,
+};
+
+/*
+ * open/close
+ */
+
+static int snd_sb16_playback_open(snd_pcm_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ spin_lock_irqsave(&chip->open_lock, flags);
+ if (chip->mode & SB_MODE_PLAYBACK) {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return -EAGAIN;
+ }
+ runtime->hw = snd_sb16_playback;
+
+ /* skip if 16 bit DMA was reserved for capture */
+ if (chip->force_mode16 & SB_MODE_CAPTURE_16)
+ goto __skip_16bit;
+
+ if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_CAPTURE_16)) {
+ chip->mode |= SB_MODE_PLAYBACK_16;
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+ /* Vibra16X hack */
+ if (chip->dma16 <= 3) {
+ runtime->hw.buffer_bytes_max =
+ runtime->hw.period_bytes_max = 64 * 1024;
+ } else {
+ snd_sb16_csp_playback_open(chip, runtime);
+ }
+ goto __open_ok;
+ }
+
+ __skip_16bit:
+ if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_CAPTURE_8)) {
+ chip->mode |= SB_MODE_PLAYBACK_8;
+ /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */
+ if (chip->dma16 < 0) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+ chip->mode |= SB_MODE_PLAYBACK_16;
+ } else {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8;
+ }
+ runtime->hw.buffer_bytes_max =
+ runtime->hw.period_bytes_max = 64 * 1024;
+ goto __open_ok;
+ }
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return -EAGAIN;
+
+ __open_ok:
+ if (chip->hardware == SB_HW_ALS100)
+ runtime->hw.rate_max = 48000;
+ if (chip->mode & SB_RATE_LOCK)
+ runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
+ chip->playback_substream = substream;
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return 0;
+}
+
+static int snd_sb16_playback_close(snd_pcm_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+
+ snd_sb16_csp_playback_close(chip);
+ spin_lock_irqsave(&chip->open_lock, flags);
+ chip->playback_substream = NULL;
+ chip->mode &= ~SB_MODE_PLAYBACK;
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return 0;
+}
+
+static int snd_sb16_capture_open(snd_pcm_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+
+ spin_lock_irqsave(&chip->open_lock, flags);
+ if (chip->mode & SB_MODE_CAPTURE) {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return -EAGAIN;
+ }
+ runtime->hw = snd_sb16_capture;
+
+ /* skip if 16 bit DMA was reserved for playback */
+ if (chip->force_mode16 & SB_MODE_PLAYBACK_16)
+ goto __skip_16bit;
+
+ if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) {
+ chip->mode |= SB_MODE_CAPTURE_16;
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+ /* Vibra16X hack */
+ if (chip->dma16 <= 3) {
+ runtime->hw.buffer_bytes_max =
+ runtime->hw.period_bytes_max = 64 * 1024;
+ } else {
+ snd_sb16_csp_capture_open(chip, runtime);
+ }
+ goto __open_ok;
+ }
+
+ __skip_16bit:
+ if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) {
+ chip->mode |= SB_MODE_CAPTURE_8;
+ /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */
+ if (chip->dma16 < 0) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE;
+ chip->mode |= SB_MODE_CAPTURE_16;
+ } else {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8;
+ }
+ runtime->hw.buffer_bytes_max =
+ runtime->hw.period_bytes_max = 64 * 1024;
+ goto __open_ok;
+ }
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return -EAGAIN;
+
+ __open_ok:
+ if (chip->hardware == SB_HW_ALS100)
+ runtime->hw.rate_max = 48000;
+ if (chip->mode & SB_RATE_LOCK)
+ runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
+ chip->capture_substream = substream;
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return 0;
+}
+
+static int snd_sb16_capture_close(snd_pcm_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+
+ snd_sb16_csp_capture_close(chip);
+ spin_lock_irqsave(&chip->open_lock, flags);
+ chip->capture_substream = NULL;
+ chip->mode &= ~SB_MODE_CAPTURE;
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return 0;
+}
+
+/*
+ * DMA control interface
+ */
+
+static int snd_sb16_set_dma_mode(sb_t *chip, int what)
+{
+ if (chip->dma8 < 0 || chip->dma16 < 0) {
+ snd_assert(what == 0, return -EINVAL);
+ return 0;
+ }
+ if (what == 0) {
+ chip->force_mode16 = 0;
+ } else if (what == 1) {
+ chip->force_mode16 = SB_MODE_PLAYBACK_16;
+ } else if (what == 2) {
+ chip->force_mode16 = SB_MODE_CAPTURE_16;
+ } else {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int snd_sb16_get_dma_mode(sb_t *chip)
+{
+ if (chip->dma8 < 0 || chip->dma16 < 0)
+ return 0;
+ switch (chip->force_mode16) {
+ case SB_MODE_PLAYBACK_16:
+ return 1;
+ case SB_MODE_CAPTURE_16:
+ return 2;
+ default:
+ return 0;
+ }
+}
+
+static int snd_sb16_dma_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[3] = {
+ "Auto", "Playback", "Capture"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2)
+ uinfo->value.enumerated.item = 2;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_sb16_dma_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return 0;
+}
+
+static int snd_sb16_dma_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *chip = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ unsigned char nval, oval;
+ int change;
+
+ if ((nval = ucontrol->value.enumerated.item[0]) > 2)
+ return -EINVAL;
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ oval = snd_sb16_get_dma_mode(chip);
+ change = nval != oval;
+ snd_sb16_set_dma_mode(chip, nval);
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return change;
+}
+
+static snd_kcontrol_new_t snd_sb16_dma_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "16-bit DMA Allocation",
+ .info = snd_sb16_dma_control_info,
+ .get = snd_sb16_dma_control_get,
+ .put = snd_sb16_dma_control_put
+};
+
+/*
+ * Initialization part
+ */
+
+int snd_sb16dsp_configure(sb_t * chip)
+{
+ unsigned long flags;
+ unsigned char irqreg = 0, dmareg = 0, mpureg;
+ unsigned char realirq, realdma, realmpureg;
+ /* note: mpu register should be present only on SB16 Vibra soundcards */
+
+ // printk("codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16);
+ spin_lock_irqsave(&chip->mixer_lock, flags);
+ mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06;
+ spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ switch (chip->irq) {
+ case 2:
+ case 9:
+ irqreg |= SB_IRQSETUP_IRQ9;
+ break;
+ case 5:
+ irqreg |= SB_IRQSETUP_IRQ5;
+ break;
+ case 7:
+ irqreg |= SB_IRQSETUP_IRQ7;
+ break;
+ case 10:
+ irqreg |= SB_IRQSETUP_IRQ10;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (chip->dma8 >= 0) {
+ switch (chip->dma8) {
+ case 0:
+ dmareg |= SB_DMASETUP_DMA0;
+ break;
+ case 1:
+ dmareg |= SB_DMASETUP_DMA1;
+ break;
+ case 3:
+ dmareg |= SB_DMASETUP_DMA3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) {
+ switch (chip->dma16) {
+ case 5:
+ dmareg |= SB_DMASETUP_DMA5;
+ break;
+ case 6:
+ dmareg |= SB_DMASETUP_DMA6;
+ break;
+ case 7:
+ dmareg |= SB_DMASETUP_DMA7;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ switch (chip->mpu_port) {
+ case 0x300:
+ mpureg |= 0x04;
+ break;
+ case 0x330:
+ mpureg |= 0x00;
+ break;
+ default:
+ mpureg |= 0x02; /* disable MPU */
+ }
+ spin_lock_irqsave(&chip->mixer_lock, flags);
+
+ snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg);
+ realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP);
+
+ snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg);
+ realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP);
+
+ snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg);
+ realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP);
+
+ spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ if ((~realirq) & irqreg || (~realdma) & dmareg) {
+ snd_printk("SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port);
+ snd_printk("SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg);
+ snd_printk("SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static snd_pcm_ops_t snd_sb16_playback_ops = {
+ .open = snd_sb16_playback_open,
+ .close = snd_sb16_playback_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sb16_hw_params,
+ .hw_free = snd_sb16_hw_free,
+ .prepare = snd_sb16_playback_prepare,
+ .trigger = snd_sb16_playback_trigger,
+ .pointer = snd_sb16_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_sb16_capture_ops = {
+ .open = snd_sb16_capture_open,
+ .close = snd_sb16_capture_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sb16_hw_params,
+ .hw_free = snd_sb16_hw_free,
+ .prepare = snd_sb16_capture_prepare,
+ .trigger = snd_sb16_capture_trigger,
+ .pointer = snd_sb16_capture_pointer,
+};
+
+static void snd_sb16dsp_pcm_free(snd_pcm_t *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_sb16dsp_pcm(sb_t * chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_card_t *card = chip->card;
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm)) < 0)
+ return err;
+ sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
+ pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+ pcm->private_data = chip;
+ pcm->private_free = snd_sb16dsp_pcm_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops);
+
+ if (chip->dma16 >= 0 && chip->dma8 != chip->dma16)
+ snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip));
+ else
+ pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_isa_data(),
+ 64*1024, 128*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+const snd_pcm_ops_t *snd_sb16dsp_get_pcm_ops(int direction)
+{
+ return direction == SNDRV_PCM_STREAM_PLAYBACK ?
+ &snd_sb16_playback_ops : &snd_sb16_capture_ops;
+}
+
+EXPORT_SYMBOL(snd_sb16dsp_pcm);
+EXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops);
+EXPORT_SYMBOL(snd_sb16dsp_configure);
+EXPORT_SYMBOL(snd_sb16dsp_interrupt);
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_sb16_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_sb16_exit(void)
+{
+}
+
+module_init(alsa_sb16_init)
+module_exit(alsa_sb16_exit)
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
new file mode 100644
index 000000000000..e2cbc4202b3d
--- /dev/null
+++ b/sound/isa/sb/sb8.c
@@ -0,0 +1,223 @@
+/*
+ * Driver for SoundBlaster 1.0/2.0/Pro soundcards and compatible
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ * 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 <sound/driver.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/opl3.h>
+#define SNDRV_LEGACY_AUTO_PROBE
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */
+static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3 */
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Sound Blaster soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard.");
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for SB8 driver.");
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for SB8 driver.");
+module_param_array(dma8, int, NULL, 0444);
+MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver.");
+
+struct snd_sb8 {
+ struct resource *fm_res; /* used to block FM i/o region for legacy cards */
+};
+
+static snd_card_t *snd_sb8_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ sb_t *chip = dev_id;
+
+ if (chip->open & SB_OPEN_PCM) {
+ return snd_sb8dsp_interrupt(chip);
+ } else {
+ return snd_sb8dsp_midi_interrupt(chip);
+ }
+}
+
+static void snd_sb8_free(snd_card_t *card)
+{
+ struct snd_sb8 *acard = (struct snd_sb8 *)card->private_data;
+
+ if (acard == NULL)
+ return;
+ if (acard->fm_res) {
+ release_resource(acard->fm_res);
+ kfree_nocheck(acard->fm_res);
+ }
+}
+
+static int __init snd_sb8_probe(int dev)
+{
+ sb_t *chip;
+ snd_card_t *card;
+ struct snd_sb8 *acard;
+ opl3_t *opl3;
+ int err;
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE,
+ sizeof(struct snd_sb8));
+ if (card == NULL)
+ return -ENOMEM;
+ acard = (struct snd_sb8 *)card->private_data;
+ card->private_free = snd_sb8_free;
+
+ /* block the 0x388 port to avoid PnP conflicts */
+ acard->fm_res = request_region(0x388, 4, "SoundBlaster FM");
+
+ if ((err = snd_sbdsp_create(card, port[dev], irq[dev],
+ snd_sb8_interrupt,
+ dma8[dev],
+ -1,
+ SB_HW_AUTO,
+ &chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if (chip->hardware >= SB_HW_16) {
+ snd_card_free(card);
+ if (chip->hardware == SB_HW_ALS100)
+ snd_printdd("ALS100 chip detected at 0x%lx, try snd-als100 module\n",
+ port[dev]);
+ else
+ snd_printdd("SB 16 chip detected at 0x%lx, try snd-sb16 module\n",
+ port[dev]);
+ return -ENODEV;
+ }
+
+ if ((err = snd_sb8dsp_pcm(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if ((err = snd_sbmixer_new(chip)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ if (chip->hardware == SB_HW_10 || chip->hardware == SB_HW_20) {
+ if ((err = snd_opl3_create(card, chip->port + 8, 0,
+ OPL3_HW_AUTO, 1,
+ &opl3)) < 0) {
+ snd_printk(KERN_ERR "sb8: no OPL device at 0x%lx\n", chip->port + 8);
+ }
+ } else {
+ if ((err = snd_opl3_create(card, chip->port, chip->port + 2,
+ OPL3_HW_AUTO, 1,
+ &opl3)) < 0) {
+ snd_printk(KERN_ERR "sb8: no OPL device at 0x%lx-0x%lx\n",
+ chip->port, chip->port + 2);
+ }
+ }
+ if (err >= 0) {
+ if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ }
+
+ if ((err = snd_sb8dsp_midi(chip, 0, NULL)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ strcpy(card->driver, chip->hardware == SB_HW_PRO ? "SB Pro" : "SB8");
+ strcpy(card->shortname, chip->name);
+ sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
+ chip->name,
+ chip->port,
+ irq[dev], dma8[dev]);
+ if ((err = snd_card_register(card)) < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ snd_sb8_cards[dev] = card;
+ return 0;
+}
+
+static int __init snd_card_sb8_legacy_auto_probe(unsigned long xport)
+{
+ static int dev;
+ int res;
+
+ for ( ; dev < SNDRV_CARDS; dev++) {
+ if (!enable[dev] || port[dev] != SNDRV_AUTO_PORT)
+ continue;
+ port[dev] = xport;
+ res = snd_sb8_probe(dev);
+ if (res < 0)
+ port[dev] = SNDRV_AUTO_PORT;
+ return res;
+ }
+ return -ENODEV;
+}
+
+static int __init alsa_card_sb8_init(void)
+{
+ static unsigned long possible_ports[] = {0x220, 0x240, 0x260, -1};
+ int dev, cards, i;
+
+ for (dev = cards = 0; dev < SNDRV_CARDS && enable[dev]; dev++) {
+ if (port[dev] == SNDRV_AUTO_PORT)
+ continue;
+ if (snd_sb8_probe(dev) >= 0)
+ cards++;
+ }
+ i = snd_legacy_auto_probe(possible_ports, snd_card_sb8_legacy_auto_probe);
+ if (i > 0)
+ cards += i;
+
+ if (!cards) {
+#ifdef MODULE
+ snd_printk(KERN_ERR "Sound Blaster soundcard not found or device busy\n");
+#endif
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __exit alsa_card_sb8_exit(void)
+{
+ int idx;
+
+ for (idx = 0; idx < SNDRV_CARDS; idx++)
+ snd_card_free(snd_sb8_cards[idx]);
+}
+
+module_init(alsa_card_sb8_init)
+module_exit(alsa_card_sb8_exit)
diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c
new file mode 100644
index 000000000000..87c9b1ba06cf
--- /dev/null
+++ b/sound/isa/sb/sb8_main.c
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Uros Bizjak <uros@kss-loka.si>
+ *
+ * Routines for control of 8-bit SoundBlaster cards and clones
+ * Please note: I don't have access to old SB8 soundcards.
+ *
+ *
+ * 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
+ *
+ * --
+ *
+ * Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
+ * DSP can't respond to commands whilst in "high speed" mode. Caused
+ * glitching during playback. Fixed.
+ *
+ * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak <uros@kss-loka.si>
+ * Cleaned up and rewrote lowlevel routines.
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Uros Bizjak <uros@kss-loka.si>");
+MODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones");
+MODULE_LICENSE("GPL");
+
+#define SB8_CLOCK 1000000
+#define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v))
+#define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v))
+
+static ratnum_t clock = {
+ .num = SB8_CLOCK,
+ .den_min = 1,
+ .den_max = 256,
+ .den_step = 1,
+};
+
+static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = {
+ .nrats = 1,
+ .rats = &clock,
+};
+
+static ratnum_t stereo_clocks[] = {
+ {
+ .num = SB8_CLOCK,
+ .den_min = SB8_DEN(22050),
+ .den_max = SB8_DEN(22050),
+ .den_step = 1,
+ },
+ {
+ .num = SB8_CLOCK,
+ .den_min = SB8_DEN(11025),
+ .den_max = SB8_DEN(11025),
+ .den_step = 1,
+ }
+};
+
+static int snd_sb8_hw_constraint_rate_channels(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ snd_interval_t *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ if (c->min > 1) {
+ unsigned int num = 0, den = 0;
+ int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE),
+ 2, stereo_clocks, &num, &den);
+ if (err >= 0 && den) {
+ params->rate_num = num;
+ params->rate_den = den;
+ }
+ return err;
+ }
+ return 0;
+}
+
+static int snd_sb8_hw_constraint_channels_rate(snd_pcm_hw_params_t *params,
+ snd_pcm_hw_rule_t *rule)
+{
+ snd_interval_t *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) {
+ snd_interval_t t = { .min = 1, .max = 1 };
+ return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t);
+ }
+ return 0;
+}
+
+static int snd_sb8_playback_prepare(snd_pcm_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int mixreg, rate, size, count;
+
+ rate = runtime->rate;
+ switch (chip->hardware) {
+ case SB_HW_PRO:
+ if (runtime->channels > 1) {
+ snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+ chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
+ break;
+ }
+ /* fallthru */
+ case SB_HW_201:
+ if (rate > 23000) {
+ chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;
+ break;
+ }
+ /* fallthru */
+ case SB_HW_20:
+ chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
+ break;
+ case SB_HW_10:
+ chip->playback_format = SB_DSP_OUTPUT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
+ count = chip->p_period_size = snd_pcm_lib_period_bytes(substream);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
+ if (runtime->channels > 1) {
+ /* set playback stereo mode */
+ spin_lock(&chip->mixer_lock);
+ mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
+ snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02);
+ spin_unlock(&chip->mixer_lock);
+
+ /* Soundblaster hardware programming reference guide, 3-23 */
+ snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
+ runtime->dma_area[0] = 0x80;
+ snd_dma_program(chip->dma8, runtime->dma_addr, 1, DMA_MODE_WRITE);
+ /* force interrupt */
+ chip->mode = SB_MODE_HALT;
+ snd_sbdsp_command(chip, SB_DSP_OUTPUT);
+ snd_sbdsp_command(chip, 0);
+ snd_sbdsp_command(chip, 0);
+ }
+ snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+ if (runtime->channels > 1) {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+ spin_lock(&chip->mixer_lock);
+ /* save output filter status and turn it off */
+ mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT);
+ snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20);
+ spin_unlock(&chip->mixer_lock);
+ /* just use force_mode16 for temporary storate... */
+ chip->force_mode16 = mixreg;
+ } else {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den);
+ }
+ if (chip->playback_format != SB_DSP_OUTPUT) {
+ count--;
+ snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_dma_program(chip->dma8, runtime->dma_addr,
+ size, DMA_MODE_WRITE | DMA_AUTOINIT);
+ return 0;
+}
+
+static int snd_sb8_playback_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int count;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_sbdsp_command(chip, chip->playback_format);
+ if (chip->playback_format == SB_DSP_OUTPUT) {
+ count = chip->p_period_size - 1;
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) {
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_sbdsp_reset(chip);
+ if (runtime->channels > 1) {
+ spin_lock(&chip->mixer_lock);
+ /* restore output filter and set hardware to mono mode */
+ snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02);
+ spin_unlock(&chip->mixer_lock);
+ }
+ } else {
+ snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+ }
+ snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_PLAYBACK_8 : SB_MODE_HALT;
+ return 0;
+}
+
+static int snd_sb8_hw_params(snd_pcm_substream_t * substream,
+ snd_pcm_hw_params_t * hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_sb8_hw_free(snd_pcm_substream_t * substream)
+{
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int snd_sb8_capture_prepare(snd_pcm_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned int mixreg, rate, size, count;
+
+ rate = runtime->rate;
+ switch (chip->hardware) {
+ case SB_HW_PRO:
+ if (runtime->channels > 1) {
+ snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL);
+ chip->capture_format = SB_DSP_HI_INPUT_AUTO;
+ break;
+ }
+ chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO;
+ break;
+ case SB_HW_201:
+ if (rate > 13000) {
+ chip->capture_format = SB_DSP_HI_INPUT_AUTO;
+ break;
+ }
+ /* fallthru */
+ case SB_HW_20:
+ chip->capture_format = SB_DSP_LO_INPUT_AUTO;
+ break;
+ case SB_HW_10:
+ chip->capture_format = SB_DSP_INPUT;
+ break;
+ default:
+ return -EINVAL;
+ }
+ size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
+ count = chip->c_period_size = snd_pcm_lib_period_bytes(substream);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+ if (runtime->channels > 1)
+ snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT);
+ snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
+ if (runtime->channels > 1) {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
+ spin_lock(&chip->mixer_lock);
+ /* save input filter status and turn it off */
+ mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT);
+ snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20);
+ spin_unlock(&chip->mixer_lock);
+ /* just use force_mode16 for temporary storate... */
+ chip->force_mode16 = mixreg;
+ } else {
+ snd_sbdsp_command(chip, 256 - runtime->rate_den);
+ }
+ if (chip->capture_format != SB_DSP_OUTPUT) {
+ count--;
+ snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ snd_dma_program(chip->dma8, runtime->dma_addr,
+ size, DMA_MODE_READ | DMA_AUTOINIT);
+ return 0;
+}
+
+static int snd_sb8_capture_trigger(snd_pcm_substream_t * substream,
+ int cmd)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ unsigned int count;
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_sbdsp_command(chip, chip->capture_format);
+ if (chip->capture_format == SB_DSP_INPUT) {
+ count = chip->c_period_size - 1;
+ snd_sbdsp_command(chip, count & 0xff);
+ snd_sbdsp_command(chip, count >> 8);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) {
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ snd_sbdsp_reset(chip);
+ if (runtime->channels > 1) {
+ /* restore input filter status */
+ spin_lock(&chip->mixer_lock);
+ snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16);
+ spin_unlock(&chip->mixer_lock);
+ /* set hardware to mono mode */
+ snd_sbdsp_command(chip, SB_DSP_MONO_8BIT);
+ }
+ } else {
+ snd_sbdsp_command(chip, SB_DSP_DMA8_OFF);
+ }
+ snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_CAPTURE_8 : SB_MODE_HALT;
+ return 0;
+}
+
+irqreturn_t snd_sb8dsp_interrupt(sb_t *chip)
+{
+ snd_pcm_substream_t *substream;
+ snd_pcm_runtime_t *runtime;
+
+#if 0
+ snd_printk("sb8: interrupt\n");
+#endif
+ snd_sb_ack_8bit(chip);
+ switch (chip->mode) {
+ case SB_MODE_PLAYBACK_8: /* ok.. playback is active */
+ substream = chip->playback_substream;
+ runtime = substream->runtime;
+ if (chip->playback_format == SB_DSP_OUTPUT)
+ snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START);
+ snd_pcm_period_elapsed(substream);
+ break;
+ case SB_MODE_CAPTURE_8:
+ substream = chip->capture_substream;
+ runtime = substream->runtime;
+ if (chip->capture_format == SB_DSP_INPUT)
+ snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START);
+ snd_pcm_period_elapsed(substream);
+ break;
+ }
+ return IRQ_HANDLED;
+}
+
+static snd_pcm_uframes_t snd_sb8_playback_pointer(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (chip->mode != SB_MODE_PLAYBACK_8)
+ return 0;
+ ptr = snd_dma_pointer(chip->dma8, chip->p_dma_size);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_sb8_capture_pointer(snd_pcm_substream_t * substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ size_t ptr;
+
+ if (chip->mode != SB_MODE_CAPTURE_8)
+ return 0;
+ ptr = snd_dma_pointer(chip->dma8, chip->c_dma_size);
+ return bytes_to_frames(substream->runtime, ptr);
+}
+
+/*
+
+ */
+
+static snd_pcm_hardware_t snd_sb8_playback =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_U8,
+ .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050),
+ .rate_min = 4000,
+ .rate_max = 23000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 64,
+ .period_bytes_max = 65536,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+static snd_pcm_hardware_t snd_sb8_capture =
+{
+ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID),
+ .formats = SNDRV_PCM_FMTBIT_U8,
+ .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_11025),
+ .rate_min = 4000,
+ .rate_max = 13000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = 65536,
+ .period_bytes_min = 64,
+ .period_bytes_max = 65536,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .fifo_size = 0,
+};
+
+/*
+ *
+ */
+
+static int snd_sb8_open(snd_pcm_substream_t *substream)
+{
+ sb_t *chip = snd_pcm_substream_chip(substream);
+ snd_pcm_runtime_t *runtime = substream->runtime;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->open_lock, flags);
+ if (chip->open) {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return -EAGAIN;
+ }
+ chip->open |= SB_OPEN_PCM;
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ chip->playback_substream = substream;
+ runtime->hw = snd_sb8_playback;
+ } else {
+ chip->capture_substream = substream;
+ runtime->hw = snd_sb8_capture;
+ }
+ switch (chip->hardware) {
+ case SB_HW_PRO:
+ runtime->hw.rate_max = 44100;
+ runtime->hw.channels_max = 2;
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ snd_sb8_hw_constraint_rate_channels, NULL,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ snd_sb8_hw_constraint_channels_rate, NULL,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ break;
+ case SB_HW_201:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ runtime->hw.rate_max = 44100;
+ } else {
+ runtime->hw.rate_max = 15000;
+ }
+ default:
+ break;
+ }
+ snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ &hw_constraints_clock);
+ return 0;
+}
+
+static int snd_sb8_close(snd_pcm_substream_t *substream)
+{
+ unsigned long flags;
+ sb_t *chip = snd_pcm_substream_chip(substream);
+
+ chip->playback_substream = NULL;
+ chip->capture_substream = NULL;
+ spin_lock_irqsave(&chip->open_lock, flags);
+ chip->open &= ~SB_OPEN_PCM;
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return 0;
+}
+
+/*
+ * Initialization part
+ */
+
+static snd_pcm_ops_t snd_sb8_playback_ops = {
+ .open = snd_sb8_open,
+ .close = snd_sb8_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sb8_hw_params,
+ .hw_free = snd_sb8_hw_free,
+ .prepare = snd_sb8_playback_prepare,
+ .trigger = snd_sb8_playback_trigger,
+ .pointer = snd_sb8_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_sb8_capture_ops = {
+ .open = snd_sb8_open,
+ .close = snd_sb8_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sb8_hw_params,
+ .hw_free = snd_sb8_hw_free,
+ .prepare = snd_sb8_capture_prepare,
+ .trigger = snd_sb8_capture_trigger,
+ .pointer = snd_sb8_capture_pointer,
+};
+
+static void snd_sb8dsp_pcm_free(snd_pcm_t *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int snd_sb8dsp_pcm(sb_t *chip, int device, snd_pcm_t ** rpcm)
+{
+ snd_card_t *card = chip->card;
+ snd_pcm_t *pcm;
+ int err;
+
+ if (rpcm)
+ *rpcm = NULL;
+ if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0)
+ return err;
+ sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff);
+ pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
+ pcm->private_data = chip;
+ pcm->private_free = snd_sb8dsp_pcm_free;
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops);
+
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+ snd_dma_isa_data(),
+ 64*1024, 64*1024);
+
+ if (rpcm)
+ *rpcm = pcm;
+ return 0;
+}
+
+EXPORT_SYMBOL(snd_sb8dsp_pcm);
+EXPORT_SYMBOL(snd_sb8dsp_interrupt);
+ /* sb8_midi.c */
+EXPORT_SYMBOL(snd_sb8dsp_midi_interrupt);
+EXPORT_SYMBOL(snd_sb8dsp_midi);
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_sb8_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_sb8_exit(void)
+{
+}
+
+module_init(alsa_sb8_init)
+module_exit(alsa_sb8_exit)
diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c
new file mode 100644
index 000000000000..d2c633a40e74
--- /dev/null
+++ b/sound/isa/sb/sb8_midi.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Routines for control of SoundBlaster cards - MIDI interface
+ *
+ * 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
+ *
+ * --
+ *
+ * Sun May 9 22:54:38 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk>
+ * Fixed typo in snd_sb8dsp_midi_new_device which prevented midi from
+ * working.
+ *
+ * Sun May 11 12:34:56 UTC 2003 Clemens Ladisch <clemens@ladisch.de>
+ * Added full duplex UART mode for DSP version 2.0 and later.
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+
+/*
+
+ */
+
+irqreturn_t snd_sb8dsp_midi_interrupt(sb_t * chip)
+{
+ snd_rawmidi_t *rmidi;
+ int max = 64;
+ char byte;
+
+ if (chip == NULL || (rmidi = chip->rmidi) == NULL) {
+ inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */
+ return IRQ_NONE;
+ }
+ spin_lock(&chip->midi_input_lock);
+ while (max-- > 0) {
+ if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
+ byte = inb(SBP(chip, READ));
+ if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
+ snd_rawmidi_receive(chip->midi_substream_input, &byte, 1);
+ }
+ }
+ }
+ spin_unlock(&chip->midi_input_lock);
+ return IRQ_HANDLED;
+}
+
+/*
+
+ */
+
+static int snd_sb8dsp_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip;
+ unsigned int valid_open_flags;
+
+ chip = substream->rmidi->private_data;
+ valid_open_flags = chip->hardware >= SB_HW_20
+ ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0;
+ spin_lock_irqsave(&chip->open_lock, flags);
+ if (chip->open & ~valid_open_flags) {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return -EAGAIN;
+ }
+ chip->open |= SB_OPEN_MIDI_INPUT;
+ chip->midi_substream_input = substream;
+ if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ snd_sbdsp_reset(chip); /* reset DSP */
+ if (chip->hardware >= SB_HW_20)
+ snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
+ } else {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ }
+ return 0;
+}
+
+static int snd_sb8dsp_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip;
+ unsigned int valid_open_flags;
+
+ chip = substream->rmidi->private_data;
+ valid_open_flags = chip->hardware >= SB_HW_20
+ ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0;
+ spin_lock_irqsave(&chip->open_lock, flags);
+ if (chip->open & ~valid_open_flags) {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ return -EAGAIN;
+ }
+ chip->open |= SB_OPEN_MIDI_OUTPUT;
+ chip->midi_substream_output = substream;
+ if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ snd_sbdsp_reset(chip); /* reset DSP */
+ if (chip->hardware >= SB_HW_20)
+ snd_sbdsp_command(chip, SB_DSP_MIDI_UART_IRQ);
+ } else {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ }
+ return 0;
+}
+
+static int snd_sb8dsp_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip;
+
+ chip = substream->rmidi->private_data;
+ spin_lock_irqsave(&chip->open_lock, flags);
+ chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER);
+ chip->midi_substream_input = NULL;
+ if (!(chip->open & SB_OPEN_MIDI_OUTPUT)) {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ snd_sbdsp_reset(chip); /* reset DSP */
+ } else {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ }
+ return 0;
+}
+
+static int snd_sb8dsp_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip;
+
+ chip = substream->rmidi->private_data;
+ spin_lock_irqsave(&chip->open_lock, flags);
+ chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER);
+ chip->midi_substream_output = NULL;
+ if (!(chip->open & SB_OPEN_MIDI_INPUT)) {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ snd_sbdsp_reset(chip); /* reset DSP */
+ } else {
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ }
+ return 0;
+}
+
+static void snd_sb8dsp_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ unsigned long flags;
+ sb_t *chip;
+
+ chip = substream->rmidi->private_data;
+ spin_lock_irqsave(&chip->open_lock, flags);
+ if (up) {
+ if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) {
+ if (chip->hardware < SB_HW_20)
+ snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
+ chip->open |= SB_OPEN_MIDI_INPUT_TRIGGER;
+ }
+ } else {
+ if (chip->open & SB_OPEN_MIDI_INPUT_TRIGGER) {
+ if (chip->hardware < SB_HW_20)
+ snd_sbdsp_command(chip, SB_DSP_MIDI_INPUT_IRQ);
+ chip->open &= ~SB_OPEN_MIDI_INPUT_TRIGGER;
+ }
+ }
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+}
+
+static void snd_sb8dsp_midi_output_write(snd_rawmidi_substream_t * substream)
+{
+ unsigned long flags;
+ sb_t *chip;
+ char byte;
+ int max = 32;
+
+ /* how big is Tx FIFO? */
+ chip = substream->rmidi->private_data;
+ while (max-- > 0) {
+ spin_lock_irqsave(&chip->open_lock, flags);
+ if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) {
+ chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
+ del_timer(&chip->midi_timer);
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ break;
+ }
+ if (chip->hardware >= SB_HW_20) {
+ int timeout = 8;
+ while ((inb(SBP(chip, STATUS)) & 0x80) != 0 && --timeout > 0)
+ ;
+ if (timeout == 0) {
+ /* Tx FIFO full - try again later */
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ break;
+ }
+ outb(byte, SBP(chip, WRITE));
+ } else {
+ snd_sbdsp_command(chip, SB_DSP_MIDI_OUTPUT);
+ snd_sbdsp_command(chip, byte);
+ }
+ snd_rawmidi_transmit_ack(substream, 1);
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ }
+}
+
+static void snd_sb8dsp_midi_output_timer(unsigned long data)
+{
+ snd_rawmidi_substream_t * substream = (snd_rawmidi_substream_t *) data;
+ sb_t * chip = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&chip->open_lock, flags);
+ chip->midi_timer.expires = 1 + jiffies;
+ add_timer(&chip->midi_timer);
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+ snd_sb8dsp_midi_output_write(substream);
+}
+
+static void snd_sb8dsp_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+ unsigned long flags;
+ sb_t *chip;
+
+ chip = substream->rmidi->private_data;
+ spin_lock_irqsave(&chip->open_lock, flags);
+ if (up) {
+ if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) {
+ init_timer(&chip->midi_timer);
+ chip->midi_timer.function = snd_sb8dsp_midi_output_timer;
+ chip->midi_timer.data = (unsigned long) substream;
+ chip->midi_timer.expires = 1 + jiffies;
+ add_timer(&chip->midi_timer);
+ chip->open |= SB_OPEN_MIDI_OUTPUT_TRIGGER;
+ }
+ } else {
+ if (chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER) {
+ chip->open &= ~SB_OPEN_MIDI_OUTPUT_TRIGGER;
+ }
+ }
+ spin_unlock_irqrestore(&chip->open_lock, flags);
+
+ if (up)
+ snd_sb8dsp_midi_output_write(substream);
+}
+
+/*
+
+ */
+
+static snd_rawmidi_ops_t snd_sb8dsp_midi_output =
+{
+ .open = snd_sb8dsp_midi_output_open,
+ .close = snd_sb8dsp_midi_output_close,
+ .trigger = snd_sb8dsp_midi_output_trigger,
+};
+
+static snd_rawmidi_ops_t snd_sb8dsp_midi_input =
+{
+ .open = snd_sb8dsp_midi_input_open,
+ .close = snd_sb8dsp_midi_input_close,
+ .trigger = snd_sb8dsp_midi_input_trigger,
+};
+
+int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi)
+{
+ snd_rawmidi_t *rmidi;
+ int err;
+
+ if (rrawmidi)
+ *rrawmidi = NULL;
+ if ((err = snd_rawmidi_new(chip->card, "SB8 MIDI", device, 1, 1, &rmidi)) < 0)
+ return err;
+ strcpy(rmidi->name, "SB8 MIDI");
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_sb8dsp_midi_output);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_sb8dsp_midi_input);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT;
+ if (chip->hardware >= SB_HW_20)
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = chip;
+ chip->rmidi = rmidi;
+ if (rrawmidi)
+ *rrawmidi = rmidi;
+ return 0;
+}
diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
new file mode 100644
index 000000000000..5b6bde213ea0
--- /dev/null
+++ b/sound/isa/sb/sb_common.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Uros Bizjak <uros@kss-loka.si>
+ *
+ * Lowlevel routines for control of Sound Blaster cards
+ *
+ * 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 <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/initval.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards");
+MODULE_LICENSE("GPL");
+
+#define BUSY_LOOPS 100000
+
+#undef IO_DEBUG
+
+int snd_sbdsp_command(sb_t *chip, unsigned char val)
+{
+ int i;
+#ifdef IO_DEBUG
+ snd_printk("command 0x%x\n", val);
+#endif
+ for (i = BUSY_LOOPS; i; i--)
+ if ((inb(SBP(chip, STATUS)) & 0x80) == 0) {
+ outb(val, SBP(chip, COMMAND));
+ return 1;
+ }
+ snd_printd("%s [0x%lx]: timeout (0x%x)\n", __FUNCTION__, chip->port, val);
+ return 0;
+}
+
+int snd_sbdsp_get_byte(sb_t *chip)
+{
+ int val;
+ int i;
+ for (i = BUSY_LOOPS; i; i--) {
+ if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
+ val = inb(SBP(chip, READ));
+#ifdef IO_DEBUG
+ snd_printk("get_byte 0x%x\n", val);
+#endif
+ return val;
+ }
+ }
+ snd_printd("%s [0x%lx]: timeout\n", __FUNCTION__, chip->port);
+ return -ENODEV;
+}
+
+int snd_sbdsp_reset(sb_t *chip)
+{
+ int i;
+
+ outb(1, SBP(chip, RESET));
+ udelay(10);
+ outb(0, SBP(chip, RESET));
+ udelay(30);
+ for (i = BUSY_LOOPS; i; i--)
+ if (inb(SBP(chip, DATA_AVAIL)) & 0x80) {
+ if (inb(SBP(chip, READ)) == 0xaa)
+ return 0;
+ else
+ break;
+ }
+ snd_printdd("%s [0x%lx] failed...\n", __FUNCTION__, chip->port);
+ return -ENODEV;
+}
+
+static int snd_sbdsp_version(sb_t * chip)
+{
+ unsigned int result = -ENODEV;
+
+ snd_sbdsp_command(chip, SB_DSP_GET_VERSION);
+ result = (short) snd_sbdsp_get_byte(chip) << 8;
+ result |= (short) snd_sbdsp_get_byte(chip);
+ return result;
+}
+
+static int snd_sbdsp_probe(sb_t * chip)
+{
+ int version;
+ int major, minor;
+ char *str;
+ unsigned long flags;
+
+ /*
+ * initialization sequence
+ */
+
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ if (snd_sbdsp_reset(chip) < 0) {
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return -ENODEV;
+ }
+ version = snd_sbdsp_version(chip);
+ if (version < 0) {
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ return -ENODEV;
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+ major = version >> 8;
+ minor = version & 0xff;
+ snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
+ chip->port, major, minor);
+
+ switch (chip->hardware) {
+ case SB_HW_AUTO:
+ switch (major) {
+ case 1:
+ chip->hardware = SB_HW_10;
+ str = "1.0";
+ break;
+ case 2:
+ if (minor) {
+ chip->hardware = SB_HW_201;
+ str = "2.01+";
+ } else {
+ chip->hardware = SB_HW_20;
+ str = "2.0";
+ }
+ break;
+ case 3:
+ chip->hardware = SB_HW_PRO;
+ str = "Pro";
+ break;
+ case 4:
+ chip->hardware = SB_HW_16;
+ str = "16";
+ break;
+ default:
+ snd_printk("SB [0x%lx]: unknown DSP chip version %i.%i\n",
+ chip->port, major, minor);
+ return -ENODEV;
+ }
+ break;
+ case SB_HW_ALS100:
+ str = "16 (ALS-100)";
+ break;
+ case SB_HW_ALS4000:
+ str = "16 (ALS-4000)";
+ break;
+ case SB_HW_DT019X:
+ str = "(DT019X/ALS007)";
+ break;
+ default:
+ return -ENODEV;
+ }
+ sprintf(chip->name, "Sound Blaster %s", str);
+ chip->version = (major << 8) | minor;
+ return 0;
+}
+
+static int snd_sbdsp_free(sb_t *chip)
+{
+ if (chip->res_port) {
+ release_resource(chip->res_port);
+ kfree_nocheck(chip->res_port);
+ }
+ if (chip->irq >= 0)
+ free_irq(chip->irq, (void *) chip);
+#ifdef CONFIG_ISA
+ if (chip->dma8 >= 0) {
+ disable_dma(chip->dma8);
+ free_dma(chip->dma8);
+ }
+ if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) {
+ disable_dma(chip->dma16);
+ free_dma(chip->dma16);
+ }
+#endif
+ kfree(chip);
+ return 0;
+}
+
+static int snd_sbdsp_dev_free(snd_device_t *device)
+{
+ sb_t *chip = device->device_data;
+ return snd_sbdsp_free(chip);
+}
+
+int snd_sbdsp_create(snd_card_t *card,
+ unsigned long port,
+ int irq,
+ irqreturn_t (*irq_handler)(int, void *, struct pt_regs *),
+ int dma8,
+ int dma16,
+ unsigned short hardware,
+ sb_t **r_chip)
+{
+ sb_t *chip;
+ int err;
+ static snd_device_ops_t ops = {
+ .dev_free = snd_sbdsp_dev_free,
+ };
+
+ snd_assert(r_chip != NULL, return -EINVAL);
+ *r_chip = NULL;
+ chip = kcalloc(1, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+ spin_lock_init(&chip->reg_lock);
+ spin_lock_init(&chip->open_lock);
+ spin_lock_init(&chip->midi_input_lock);
+ spin_lock_init(&chip->mixer_lock);
+ chip->irq = -1;
+ chip->dma8 = -1;
+ chip->dma16 = -1;
+ chip->port = port;
+
+ if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ?
+ SA_INTERRUPT | SA_SHIRQ : SA_INTERRUPT,
+ "SoundBlaster", (void *) chip)) {
+ snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq);
+ snd_sbdsp_free(chip);
+ return -EBUSY;
+ }
+ chip->irq = irq;
+
+ if (hardware == SB_HW_ALS4000)
+ goto __skip_allocation;
+
+ if ((chip->res_port = request_region(port, 16, "SoundBlaster")) == NULL) {
+ snd_printk(KERN_ERR "sb: can't grab port 0x%lx\n", port);
+ snd_sbdsp_free(chip);
+ return -EBUSY;
+ }
+
+#ifdef CONFIG_ISA
+ if (dma8 >= 0 && request_dma(dma8, "SoundBlaster - 8bit")) {
+ snd_printk(KERN_ERR "sb: can't grab DMA8 %d\n", dma8);
+ snd_sbdsp_free(chip);
+ return -EBUSY;
+ }
+ chip->dma8 = dma8;
+ if (dma16 >= 0) {
+ if (hardware != SB_HW_ALS100 && (dma16 < 5 || dma16 > 7)) {
+ /* no duplex */
+ dma16 = -1;
+ } else if (request_dma(dma16, "SoundBlaster - 16bit")) {
+ snd_printk(KERN_ERR "sb: can't grab DMA16 %d\n", dma16);
+ snd_sbdsp_free(chip);
+ return -EBUSY;
+ }
+ }
+ chip->dma16 = dma16;
+#endif
+
+ __skip_allocation:
+ chip->card = card;
+ chip->hardware = hardware;
+ if ((err = snd_sbdsp_probe(chip)) < 0) {
+ snd_sbdsp_free(chip);
+ return err;
+ }
+ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+ snd_sbdsp_free(chip);
+ return err;
+ }
+ *r_chip = chip;
+ return 0;
+}
+
+EXPORT_SYMBOL(snd_sbdsp_command);
+EXPORT_SYMBOL(snd_sbdsp_get_byte);
+EXPORT_SYMBOL(snd_sbdsp_reset);
+EXPORT_SYMBOL(snd_sbdsp_create);
+/* sb_mixer.c */
+EXPORT_SYMBOL(snd_sbmixer_write);
+EXPORT_SYMBOL(snd_sbmixer_read);
+EXPORT_SYMBOL(snd_sbmixer_new);
+EXPORT_SYMBOL(snd_sbmixer_add_ctl);
+
+/*
+ * INIT part
+ */
+
+static int __init alsa_sb_common_init(void)
+{
+ return 0;
+}
+
+static void __exit alsa_sb_common_exit(void)
+{
+}
+
+module_init(alsa_sb_common_init)
+module_exit(alsa_sb_common_exit)
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
new file mode 100644
index 000000000000..cc5a2c6dec16
--- /dev/null
+++ b/sound/isa/sb/sb_mixer.c
@@ -0,0 +1,844 @@
+/*
+ * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ * Routines for Sound Blaster mixer control
+ *
+ *
+ * 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 <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/control.h>
+
+#undef IO_DEBUG
+
+void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data)
+{
+ outb(reg, SBP(chip, MIXER_ADDR));
+ udelay(10);
+ outb(data, SBP(chip, MIXER_DATA));
+ udelay(10);
+#ifdef IO_DEBUG
+ snd_printk("mixer_write 0x%x 0x%x\n", reg, data);
+#endif
+}
+
+unsigned char snd_sbmixer_read(sb_t *chip, unsigned char reg)
+{
+ unsigned char result;
+
+ outb(reg, SBP(chip, MIXER_ADDR));
+ udelay(10);
+ result = inb(SBP(chip, MIXER_DATA));
+ udelay(10);
+#ifdef IO_DEBUG
+ snd_printk("mixer_read 0x%x 0x%x\n", reg, result);
+#endif
+ return result;
+}
+
+/*
+ * Single channel mixer element
+ */
+
+static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_sbmixer_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 16) & 0xff;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ unsigned char val;
+
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ val = (snd_sbmixer_read(sb, reg) >> shift) & mask;
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ ucontrol->value.integer.value[0] = val;
+ return 0;
+}
+
+static int snd_sbmixer_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int reg = kcontrol->private_value & 0xff;
+ int shift = (kcontrol->private_value >> 16) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int change;
+ unsigned char val, oval;
+
+ val = (ucontrol->value.integer.value[0] & mask) << shift;
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ oval = snd_sbmixer_read(sb, reg);
+ val = (oval & ~(mask << shift)) | val;
+ change = val != oval;
+ if (change)
+ snd_sbmixer_write(sb, reg, val);
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ return change;
+}
+
+/*
+ * Double channel mixer element
+ */
+
+static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+
+ uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 2;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mask;
+ return 0;
+}
+
+static int snd_sbmixer_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int left_shift = (kcontrol->private_value >> 16) & 0x07;
+ int right_shift = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ unsigned char left, right;
+
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;
+ right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ ucontrol->value.integer.value[0] = left;
+ ucontrol->value.integer.value[1] = right;
+ return 0;
+}
+
+static int snd_sbmixer_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int left_reg = kcontrol->private_value & 0xff;
+ int right_reg = (kcontrol->private_value >> 8) & 0xff;
+ int left_shift = (kcontrol->private_value >> 16) & 0x07;
+ int right_shift = (kcontrol->private_value >> 19) & 0x07;
+ int mask = (kcontrol->private_value >> 24) & 0xff;
+ int change;
+ unsigned char left, right, oleft, oright;
+
+ left = (ucontrol->value.integer.value[0] & mask) << left_shift;
+ right = (ucontrol->value.integer.value[1] & mask) << right_shift;
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ if (left_reg == right_reg) {
+ oleft = snd_sbmixer_read(sb, left_reg);
+ left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;
+ change = left != oleft;
+ if (change)
+ snd_sbmixer_write(sb, left_reg, left);
+ } else {
+ oleft = snd_sbmixer_read(sb, left_reg);
+ oright = snd_sbmixer_read(sb, right_reg);
+ left = (oleft & ~(mask << left_shift)) | left;
+ right = (oright & ~(mask << right_shift)) | right;
+ change = left != oleft || right != oright;
+ if (change) {
+ snd_sbmixer_write(sb, left_reg, left);
+ snd_sbmixer_write(sb, right_reg, right);
+ }
+ }
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ return change;
+}
+
+/*
+ * DT-019x / ALS-007 capture/input switch
+ */
+
+static int snd_dt019x_input_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[5] = {
+ "CD", "Mic", "Line", "Synth", "Master"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 5;
+ if (uinfo->value.enumerated.item > 4)
+ uinfo->value.enumerated.item = 4;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+static int snd_dt019x_input_sw_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ unsigned char oval;
+
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ switch (oval & 0x07) {
+ case SB_DT019X_CAP_CD:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ case SB_DT019X_CAP_MIC:
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ case SB_DT019X_CAP_LINE:
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ case SB_DT019X_CAP_MAIN:
+ ucontrol->value.enumerated.item[0] = 4;
+ break;
+ /* To record the synth on these cards you must record the main. */
+ /* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */
+ /* duplicate case labels if left uncommented. */
+ /* case SB_DT019X_CAP_SYNTH:
+ * ucontrol->value.enumerated.item[0] = 3;
+ * break;
+ */
+ default:
+ ucontrol->value.enumerated.item[0] = 4;
+ break;
+ }
+ return 0;
+}
+
+static int snd_dt019x_input_sw_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int change;
+ unsigned char nval, oval;
+
+ if (ucontrol->value.enumerated.item[0] > 4)
+ return -EINVAL;
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 0:
+ nval = SB_DT019X_CAP_CD;
+ break;
+ case 1:
+ nval = SB_DT019X_CAP_MIC;
+ break;
+ case 2:
+ nval = SB_DT019X_CAP_LINE;
+ break;
+ case 3:
+ nval = SB_DT019X_CAP_SYNTH;
+ break;
+ case 4:
+ nval = SB_DT019X_CAP_MAIN;
+ break;
+ default:
+ nval = SB_DT019X_CAP_MAIN;
+ }
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);
+ change = nval != oval;
+ if (change)
+ snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ return change;
+}
+
+/*
+ * SBPRO input multiplexer
+ */
+
+static int snd_sb8mixer_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ static char *texts[3] = {
+ "Mic", "CD", "Line"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 3;
+ if (uinfo->value.enumerated.item > 2)
+ uinfo->value.enumerated.item = 2;
+ strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+ return 0;
+}
+
+
+static int snd_sb8mixer_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ unsigned char oval;
+
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ switch ((oval >> 0x01) & 0x03) {
+ case SB_DSP_MIXS_CD:
+ ucontrol->value.enumerated.item[0] = 1;
+ break;
+ case SB_DSP_MIXS_LINE:
+ ucontrol->value.enumerated.item[0] = 2;
+ break;
+ default:
+ ucontrol->value.enumerated.item[0] = 0;
+ break;
+ }
+ return 0;
+}
+
+static int snd_sb8mixer_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int change;
+ unsigned char nval, oval;
+
+ if (ucontrol->value.enumerated.item[0] > 2)
+ return -EINVAL;
+ switch (ucontrol->value.enumerated.item[0]) {
+ case 1:
+ nval = SB_DSP_MIXS_CD;
+ break;
+ case 2:
+ nval = SB_DSP_MIXS_LINE;
+ break;
+ default:
+ nval = SB_DSP_MIXS_MIC;
+ }
+ nval <<= 1;
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);
+ nval |= oval & ~0x06;
+ change = nval != oval;
+ if (change)
+ snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ return change;
+}
+
+/*
+ * SB16 input switch
+ */
+
+static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 4;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int snd_sb16mixer_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int reg1 = kcontrol->private_value & 0xff;
+ int reg2 = (kcontrol->private_value >> 8) & 0xff;
+ int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+ int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+ unsigned char val1, val2;
+
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ val1 = snd_sbmixer_read(sb, reg1);
+ val2 = snd_sbmixer_read(sb, reg2);
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;
+ ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;
+ ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;
+ ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01;
+ return 0;
+}
+
+static int snd_sb16mixer_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+ sb_t *sb = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+ int reg1 = kcontrol->private_value & 0xff;
+ int reg2 = (kcontrol->private_value >> 8) & 0xff;
+ int left_shift = (kcontrol->private_value >> 16) & 0x0f;
+ int right_shift = (kcontrol->private_value >> 24) & 0x0f;
+ int change;
+ unsigned char val1, val2, oval1, oval2;
+
+ spin_lock_irqsave(&sb->mixer_lock, flags);
+ oval1 = snd_sbmixer_read(sb, reg1);
+ oval2 = snd_sbmixer_read(sb, reg2);
+ val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));
+ val2 = oval2 & ~((1 << left_shift) | (1 << right_shift));
+ val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;
+ val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;
+ val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;
+ val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;
+ change = val1 != oval1 || val2 != oval2;
+ if (change) {
+ snd_sbmixer_write(sb, reg1, val1);
+ snd_sbmixer_write(sb, reg2, val2);
+ }
+ spin_unlock_irqrestore(&sb->mixer_lock, flags);
+ return change;
+}
+
+
+/*
+ */
+/*
+ */
+int snd_sbmixer_add_ctl(sb_t *chip, const char *name, int index, int type, unsigned long value)
+{
+ static snd_kcontrol_new_t newctls[] = {
+ [SB_MIX_SINGLE] = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_sbmixer_info_single,
+ .get = snd_sbmixer_get_single,
+ .put = snd_sbmixer_put_single,
+ },
+ [SB_MIX_DOUBLE] = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_sbmixer_info_double,
+ .get = snd_sbmixer_get_double,
+ .put = snd_sbmixer_put_double,
+ },
+ [SB_MIX_INPUT_SW] = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_sb16mixer_info_input_sw,
+ .get = snd_sb16mixer_get_input_sw,
+ .put = snd_sb16mixer_put_input_sw,
+ },
+ [SB_MIX_CAPTURE_PRO] = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_sb8mixer_info_mux,
+ .get = snd_sb8mixer_get_mux,
+ .put = snd_sb8mixer_put_mux,
+ },
+ [SB_MIX_CAPTURE_DT019X] = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .info = snd_dt019x_input_sw_info,
+ .get = snd_dt019x_input_sw_get,
+ .put = snd_dt019x_input_sw_put,
+ },
+ };
+ snd_kcontrol_t *ctl;
+ int err;
+
+ ctl = snd_ctl_new1(&newctls[type], chip);
+ if (! ctl)
+ return -ENOMEM;
+ strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
+ ctl->id.index = index;
+ ctl->private_value = value;
+ if ((err = snd_ctl_add(chip->card, ctl)) < 0) {
+ snd_ctl_free_one(ctl);
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * SB 2.0 specific mixer elements
+ */
+
+static struct sbmix_elem snd_sb20_ctl_master_play_vol =
+ SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7);
+static struct sbmix_elem snd_sb20_ctl_pcm_play_vol =
+ SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3);
+static struct sbmix_elem snd_sb20_ctl_synth_play_vol =
+ SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7);
+static struct sbmix_elem snd_sb20_ctl_cd_play_vol =
+ SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7);
+
+static struct sbmix_elem *snd_sb20_controls[] = {
+ &snd_sb20_ctl_master_play_vol,
+ &snd_sb20_ctl_pcm_play_vol,
+ &snd_sb20_ctl_synth_play_vol,
+ &snd_sb20_ctl_cd_play_vol
+};
+
+static unsigned char snd_sb20_init_values[][2] = {
+ { SB_DSP20_MASTER_DEV, 0 },
+ { SB_DSP20_FM_DEV, 0 },
+};
+
+/*
+ * SB Pro specific mixer elements
+ */
+static struct sbmix_elem snd_sbpro_ctl_master_play_vol =
+ SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_pcm_play_vol =
+ SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_pcm_play_filter =
+ SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1);
+static struct sbmix_elem snd_sbpro_ctl_synth_play_vol =
+ SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_cd_play_vol =
+ SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_line_play_vol =
+ SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7);
+static struct sbmix_elem snd_sbpro_ctl_mic_play_vol =
+ SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3);
+static struct sbmix_elem snd_sbpro_ctl_capture_source =
+ {
+ .name = "Capture Source",
+ .type = SB_MIX_CAPTURE_PRO
+ };
+static struct sbmix_elem snd_sbpro_ctl_capture_filter =
+ SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1);
+static struct sbmix_elem snd_sbpro_ctl_capture_low_filter =
+ SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1);
+
+static struct sbmix_elem *snd_sbpro_controls[] = {
+ &snd_sbpro_ctl_master_play_vol,
+ &snd_sbpro_ctl_pcm_play_vol,
+ &snd_sbpro_ctl_pcm_play_filter,
+ &snd_sbpro_ctl_synth_play_vol,
+ &snd_sbpro_ctl_cd_play_vol,
+ &snd_sbpro_ctl_line_play_vol,
+ &snd_sbpro_ctl_mic_play_vol,
+ &snd_sbpro_ctl_capture_source,
+ &snd_sbpro_ctl_capture_filter,
+ &snd_sbpro_ctl_capture_low_filter
+};
+
+static unsigned char snd_sbpro_init_values[][2] = {
+ { SB_DSP_MASTER_DEV, 0 },
+ { SB_DSP_PCM_DEV, 0 },
+ { SB_DSP_FM_DEV, 0 },
+};
+
+/*
+ * SB16 specific mixer elements
+ */
+static struct sbmix_elem snd_sb16_ctl_master_play_vol =
+ SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_3d_enhance_switch =
+ SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1);
+static struct sbmix_elem snd_sb16_ctl_tone_bass =
+ SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15);
+static struct sbmix_elem snd_sb16_ctl_tone_treble =
+ SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15);
+static struct sbmix_elem snd_sb16_ctl_pcm_play_vol =
+ SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_synth_capture_route =
+ SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5);
+static struct sbmix_elem snd_sb16_ctl_synth_play_vol =
+ SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_cd_capture_route =
+ SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1);
+static struct sbmix_elem snd_sb16_ctl_cd_play_switch =
+ SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1);
+static struct sbmix_elem snd_sb16_ctl_cd_play_vol =
+ SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_line_capture_route =
+ SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3);
+static struct sbmix_elem snd_sb16_ctl_line_play_switch =
+ SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1);
+static struct sbmix_elem snd_sb16_ctl_line_play_vol =
+ SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_mic_capture_route =
+ SB16_INPUT_SW("Mic Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0);
+static struct sbmix_elem snd_sb16_ctl_mic_play_switch =
+ SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1);
+static struct sbmix_elem snd_sb16_ctl_mic_play_vol =
+ SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31);
+static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol =
+ SB_SINGLE("PC Speaker Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
+static struct sbmix_elem snd_sb16_ctl_capture_vol =
+ SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3);
+static struct sbmix_elem snd_sb16_ctl_play_vol =
+ SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3);
+static struct sbmix_elem snd_sb16_ctl_auto_mic_gain =
+ SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1);
+
+static struct sbmix_elem *snd_sb16_controls[] = {
+ &snd_sb16_ctl_master_play_vol,
+ &snd_sb16_ctl_3d_enhance_switch,
+ &snd_sb16_ctl_tone_bass,
+ &snd_sb16_ctl_tone_treble,
+ &snd_sb16_ctl_pcm_play_vol,
+ &snd_sb16_ctl_synth_capture_route,
+ &snd_sb16_ctl_synth_play_vol,
+ &snd_sb16_ctl_cd_capture_route,
+ &snd_sb16_ctl_cd_play_switch,
+ &snd_sb16_ctl_cd_play_vol,
+ &snd_sb16_ctl_line_capture_route,
+ &snd_sb16_ctl_line_play_switch,
+ &snd_sb16_ctl_line_play_vol,
+ &snd_sb16_ctl_mic_capture_route,
+ &snd_sb16_ctl_mic_play_switch,
+ &snd_sb16_ctl_mic_play_vol,
+ &snd_sb16_ctl_pc_speaker_vol,
+ &snd_sb16_ctl_capture_vol,
+ &snd_sb16_ctl_play_vol,
+ &snd_sb16_ctl_auto_mic_gain
+};
+
+static unsigned char snd_sb16_init_values[][2] = {
+ { SB_DSP4_MASTER_DEV + 0, 0 },
+ { SB_DSP4_MASTER_DEV + 1, 0 },
+ { SB_DSP4_PCM_DEV + 0, 0 },
+ { SB_DSP4_PCM_DEV + 1, 0 },
+ { SB_DSP4_SYNTH_DEV + 0, 0 },
+ { SB_DSP4_SYNTH_DEV + 1, 0 },
+ { SB_DSP4_INPUT_LEFT, 0 },
+ { SB_DSP4_INPUT_RIGHT, 0 },
+ { SB_DSP4_OUTPUT_SW, 0 },
+ { SB_DSP4_SPEAKER_DEV, 0 },
+};
+
+/*
+ * DT019x specific mixer elements
+ */
+static struct sbmix_elem snd_dt019x_ctl_master_play_vol =
+ SB_DOUBLE("Master Playback Volume", SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_pcm_play_vol =
+ SB_DOUBLE("PCM Playback Volume", SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_synth_play_vol =
+ SB_DOUBLE("Synth Playback Volume", SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_cd_play_vol =
+ SB_DOUBLE("CD Playback Volume", SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_mic_play_vol =
+ SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7);
+static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol =
+ SB_SINGLE("PC Speaker Volume", SB_DT019X_SPKR_DEV, 0, 7);
+static struct sbmix_elem snd_dt019x_ctl_line_play_vol =
+ SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15);
+static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch =
+ SB_DOUBLE("PCM Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2,1, 1);
+static struct sbmix_elem snd_dt019x_ctl_synth_play_switch =
+ SB_DOUBLE("Synth Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4,3, 1);
+static struct sbmix_elem snd_dt019x_ctl_capture_source =
+ {
+ .name = "Capture Source",
+ .type = SB_MIX_CAPTURE_DT019X
+ };
+
+static struct sbmix_elem *snd_dt019x_controls[] = {
+ &snd_dt019x_ctl_master_play_vol,
+ &snd_dt019x_ctl_pcm_play_vol,
+ &snd_dt019x_ctl_synth_play_vol,
+ &snd_dt019x_ctl_cd_play_vol,
+ &snd_dt019x_ctl_mic_play_vol,
+ &snd_dt019x_ctl_pc_speaker_vol,
+ &snd_dt019x_ctl_line_play_vol,
+ &snd_sb16_ctl_mic_play_switch,
+ &snd_sb16_ctl_cd_play_switch,
+ &snd_sb16_ctl_line_play_switch,
+ &snd_dt019x_ctl_pcm_play_switch,
+ &snd_dt019x_ctl_synth_play_switch,
+ &snd_dt019x_ctl_capture_source
+};
+
+static unsigned char snd_dt019x_init_values[][2] = {
+ { SB_DT019X_MASTER_DEV, 0 },
+ { SB_DT019X_PCM_DEV, 0 },
+ { SB_DT019X_SYNTH_DEV, 0 },
+ { SB_DT019X_CD_DEV, 0 },
+ { SB_DT019X_MIC_DEV, 0 }, /* Includes PC-speaker in high nibble */
+ { SB_DT019X_LINE_DEV, 0 },
+ { SB_DSP4_OUTPUT_SW, 0 },
+ { SB_DT019X_OUTPUT_SW2, 0 },
+ { SB_DT019X_CAPTURE_SW, 0x06 },
+};
+
+/*
+ * ALS4000 specific mixer elements
+ */
+/* FIXME: SB_ALS4000_MONO_IO_CTRL needs output select ctrl ! */
+static struct sbmix_elem snd_als4000_ctl_mono_output_switch =
+ SB_SINGLE("Mono Output Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1);
+/* FIXME: mono input switch also available on DT019X ? */
+static struct sbmix_elem snd_als4000_ctl_mono_input_switch =
+ SB_SINGLE("Mono Input Switch", SB_DT019X_OUTPUT_SW2, 0, 1);
+static struct sbmix_elem snd_als4000_ctl_mic_20db_boost =
+ SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03);
+static struct sbmix_elem snd_als4000_ctl_mixer_out_to_in =
+ SB_SINGLE("Mixer Out To In", SB_ALS4000_MIC_IN_GAIN, 7, 0x01);
+/* FIXME: 3D needs much more sophisticated controls, many more features ! */
+static struct sbmix_elem snd_als4000_ctl_3d_output_switch =
+ SB_SINGLE("3D Output Switch", SB_ALS4000_3D_SND_FX, 6, 0x01);
+static struct sbmix_elem snd_als4000_ctl_3d_output_ratio =
+ SB_SINGLE("3D Output Ratio", SB_ALS4000_3D_SND_FX, 0, 0x07);
+static struct sbmix_elem snd_als4000_ctl_3d_poweroff_switch =
+ SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01);
+static struct sbmix_elem snd_als4000_ctl_3d_delay =
+ SB_SINGLE("3D Delay", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f);
+#if NOT_AVAILABLE
+static struct sbmix_elem snd_als4000_ctl_fmdac =
+ SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01);
+static struct sbmix_elem snd_als4000_ctl_qsound =
+ SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f);
+#endif
+
+static struct sbmix_elem *snd_als4000_controls[] = {
+ &snd_sb16_ctl_master_play_vol,
+ &snd_dt019x_ctl_pcm_play_switch,
+ &snd_sb16_ctl_pcm_play_vol,
+ &snd_sb16_ctl_synth_capture_route,
+ &snd_dt019x_ctl_synth_play_switch,
+ &snd_sb16_ctl_synth_play_vol,
+ &snd_sb16_ctl_cd_capture_route,
+ &snd_sb16_ctl_cd_play_switch,
+ &snd_sb16_ctl_cd_play_vol,
+ &snd_sb16_ctl_line_capture_route,
+ &snd_sb16_ctl_line_play_switch,
+ &snd_sb16_ctl_line_play_vol,
+ &snd_sb16_ctl_mic_capture_route,
+ &snd_als4000_ctl_mic_20db_boost,
+ &snd_sb16_ctl_auto_mic_gain,
+ &snd_sb16_ctl_mic_play_switch,
+ &snd_sb16_ctl_mic_play_vol,
+ &snd_sb16_ctl_pc_speaker_vol,
+ &snd_sb16_ctl_capture_vol,
+ &snd_sb16_ctl_play_vol,
+ &snd_als4000_ctl_mono_output_switch,
+ &snd_als4000_ctl_mono_input_switch,
+ &snd_als4000_ctl_mixer_out_to_in,
+ &snd_als4000_ctl_3d_output_switch,
+ &snd_als4000_ctl_3d_output_ratio,
+ &snd_als4000_ctl_3d_delay,
+ &snd_als4000_ctl_3d_poweroff_switch,
+#if NOT_AVAILABLE
+ &snd_als4000_ctl_fmdac,
+ &snd_als4000_ctl_qsound,
+#endif
+};
+
+static unsigned char snd_als4000_init_values[][2] = {
+ { SB_DSP4_MASTER_DEV + 0, 0 },
+ { SB_DSP4_MASTER_DEV + 1, 0 },
+ { SB_DSP4_PCM_DEV + 0, 0 },
+ { SB_DSP4_PCM_DEV + 1, 0 },
+ { SB_DSP4_SYNTH_DEV + 0, 0 },
+ { SB_DSP4_SYNTH_DEV + 1, 0 },
+ { SB_DSP4_SPEAKER_DEV, 0 },
+ { SB_DSP4_OUTPUT_SW, 0 },
+ { SB_DSP4_INPUT_LEFT, 0 },
+ { SB_DSP4_INPUT_RIGHT, 0 },
+ { SB_DT019X_OUTPUT_SW2, 0 },
+ { SB_ALS4000_MIC_IN_GAIN, 0 },
+};
+
+
+/*
+ */
+static int snd_sbmixer_init(sb_t *chip,
+ struct sbmix_elem **controls,
+ int controls_count,
+ unsigned char map[][2],
+ int map_count,
+ char *name)
+{
+ unsigned long flags;
+ snd_card_t *card = chip->card;
+ int idx, err;
+
+ /* mixer reset */
+ spin_lock_irqsave(&chip->mixer_lock, flags);
+ snd_sbmixer_write(chip, 0x00, 0x00);
+ spin_unlock_irqrestore(&chip->mixer_lock, flags);
+
+ /* mute and zero volume channels */
+ for (idx = 0; idx < map_count; idx++) {
+ spin_lock_irqsave(&chip->mixer_lock, flags);
+ snd_sbmixer_write(chip, map[idx][0], map[idx][1]);
+ spin_unlock_irqrestore(&chip->mixer_lock, flags);
+ }
+
+ for (idx = 0; idx < controls_count; idx++) {
+ if ((err = snd_sbmixer_add_ctl_elem(chip, controls[idx])) < 0)
+ return err;
+ }
+ snd_component_add(card, name);
+ strcpy(card->mixername, name);
+ return 0;
+}
+
+int snd_sbmixer_new(sb_t *chip)
+{
+ snd_card_t * card;
+ int err;
+
+ snd_assert(chip != NULL && chip->card != NULL, return -EINVAL);
+
+ card = chip->card;
+
+ switch (chip->hardware) {
+ case SB_HW_10:
+ return 0; /* no mixer chip on SB1.x */
+ case SB_HW_20:
+ case SB_HW_201:
+ if ((err = snd_sbmixer_init(chip,
+ snd_sb20_controls,
+ ARRAY_SIZE(snd_sb20_controls),
+ snd_sb20_init_values,
+ ARRAY_SIZE(snd_sb20_init_values),
+ "CTL1335")) < 0)
+ return err;
+ break;
+ case SB_HW_PRO:
+ if ((err = snd_sbmixer_init(chip,
+ snd_sbpro_controls,
+ ARRAY_SIZE(snd_sbpro_controls),
+ snd_sbpro_init_values,
+ ARRAY_SIZE(snd_sbpro_init_values),
+ "CTL1345")) < 0)
+ return err;
+ break;
+ case SB_HW_16:
+ case SB_HW_ALS100:
+ if ((err = snd_sbmixer_init(chip,
+ snd_sb16_controls,
+ ARRAY_SIZE(snd_sb16_controls),
+ snd_sb16_init_values,
+ ARRAY_SIZE(snd_sb16_init_values),
+ "CTL1745")) < 0)
+ return err;
+ break;
+ case SB_HW_ALS4000:
+ if ((err = snd_sbmixer_init(chip,
+ snd_als4000_controls,
+ ARRAY_SIZE(snd_als4000_controls),
+ snd_als4000_init_values,
+ ARRAY_SIZE(snd_als4000_init_values),
+ "ALS4000")) < 0)
+ return err;
+ break;
+ case SB_HW_DT019X:
+ if ((err = snd_sbmixer_init(chip,
+ snd_dt019x_controls,
+ ARRAY_SIZE(snd_dt019x_controls),
+ snd_dt019x_init_values,
+ ARRAY_SIZE(snd_dt019x_init_values),
+ "DT019X")) < 0)
+ break;
+ default:
+ strcpy(card->mixername, "???");
+ }
+ return 0;
+}
diff --git a/sound/isa/sb/sbawe.c b/sound/isa/sb/sbawe.c
new file mode 100644
index 000000000000..2ec52a3473a2
--- /dev/null
+++ b/sound/isa/sb/sbawe.c
@@ -0,0 +1,2 @@
+#define SNDRV_SBAWE
+#include "sb16.c"