changeset 10913:1d1ed05d0838

PSARC 2009/519 audioemu10k device driver 6539690 add sound driver for EMU10K chip
author Garrett D'Amore <gdamore@opensolaris.org>
date Thu, 29 Oct 2009 21:38:34 -0700
parents bb04b6e33d44
children 185115acf63e
files usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNWaudioemu10k/Makefile usr/src/pkgdefs/SUNWaudioemu10k/depend usr/src/pkgdefs/SUNWaudioemu10k/pkginfo.tmpl usr/src/pkgdefs/SUNWaudioemu10k/postinstall.tmpl usr/src/pkgdefs/SUNWaudioemu10k/preremove.tmpl usr/src/pkgdefs/SUNWaudioemu10k/prototype_com usr/src/pkgdefs/SUNWaudioemu10k/prototype_i386 usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/audio/ac97/ac97.c usr/src/uts/common/io/audio/drv/audioemu10k/audioemu10k.c usr/src/uts/common/io/audio/drv/audioemu10k/audioemu10k.h usr/src/uts/common/io/audio/drv/audioemu10k/dsp/asm10k.c usr/src/uts/common/io/audio/drv/audioemu10k/dsp/emu10k.dsp usr/src/uts/common/io/audio/drv/audioemu10k/dsp/emu10k1.mac usr/src/uts/common/io/audio/drv/audioemu10k/dsp/emu10k2.mac usr/src/uts/common/sys/audio/ac97.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/audioemu10k/Makefile
diffstat 20 files changed, 4986 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/pkgdefs/Makefile	Fri Oct 30 10:52:42 2009 +0800
+++ b/usr/src/pkgdefs/Makefile	Thu Oct 29 21:38:34 2009 -0700
@@ -112,6 +112,7 @@
 	SUNWatge \
 	SUNWatu \
 	SUNWaudiocmi \
+	SUNWaudioemu10k \
 	SUNWaudiohd \
 	SUNWaudiosolo \
 	SUNWaudiovia97 \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudioemu10k/Makefile	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+TMPLFILES += postinstall preremove
+
+.KEEP_STATE:
+
+all: $(FILES) depend
+install: all pkg
+
+include ../Makefile.targ
+include ../Makefile.prtarg
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudioemu10k/depend	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,50 @@
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# This package information file defines software dependencies associated
+# with the pkg.  You can define three types of pkg dependencies with this file:
+#	 P indicates a prerequisite for installation
+#	 I indicates an incompatible package
+#	 R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# 	(<arch>)<version>
+# 	(<arch>)<version>
+# 	...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar	Core Architecture, (Root)
+P SUNWcakr	Core Solaris Kernel Architecture (Root)
+P SUNWkvm	Core Architecture, (Kvm)
+P SUNWcsr	Core Solaris, (Root)
+P SUNWckr	Core Solaris Kernel (Root)
+P SUNWcnetr	Core Solaris Network Infrastructure (Root)
+P SUNWcsu	Core Solaris, (Usr)
+P SUNWcsd	Core Solaris Devices
+P SUNWcsl	Core Solaris Libraries
+P SUNWaudd	Audio Drivers
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudioemu10k/pkginfo.tmpl	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,57 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWaudioemu10k"
+NAME="Creative EMU10K Audio Driver"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+CATEGORY="system"
+DESC="SunOS audio device driver for Creative EMU10K"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none preserve"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
+#VSTOCK="<reserved by Release Engineering for package part #>"
+#ISTATES="<developer defined>"
+#RSTATES='<developer defined>'
+#ULIMIT="<developer defined>"
+#ORDER="<developer defined>"
+#PSTAMP="<developer defined>"
+#INTONLY="<developer defined>"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudioemu10k/postinstall.tmpl	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,36 @@
+#! /bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# SUNWaudioemu postinstall script
+
+include drv_utils
+
+DRVALIASES="\
+	\"pci1102,2\"	\
+	\"pci1102,4\"	\
+	\"pci1102,8\"	\
+	"
+
+pkg_drvadd -i "${DRVALIASES}" audioemu10k || exit 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudioemu10k/preremove.tmpl	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,30 @@
+#! /bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# SUNWaudioemu10k preremove script
+
+include drv_utils
+
+pkg_drvrem audioemu10k || exit 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudioemu10k/prototype_com	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+i postinstall
+i preremove
+#
+# source locations relative to the prototype file
+#
+# SUNWaudioemu10k
+#
+d none kernel 755 root sys
+d none kernel/drv 755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudioemu10k/prototype_i386	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are i386 specific here
+#
+# source locations relative to the prototype file
+#
+# SUNWaudioemu10k
+#
+f none kernel/drv/audioemu10k 755 root sys
+d none kernel/drv/amd64 755 root sys
+f none kernel/drv/amd64/audioemu10k 755 root sys
--- a/usr/src/uts/common/Makefile.files	Fri Oct 30 10:52:42 2009 +0800
+++ b/usr/src/uts/common/Makefile.files	Thu Oct 29 21:38:34 2009 -0700
@@ -433,6 +433,8 @@
 	   audio_grc3.o audio_output.o audio_input.o \
 	   audio_oss.o audio_sun.o
 
+AUDIOEMU10K_OBJS += audioemu10k.o
+
 AUDIOENS_OBJS += audioens.o
 
 AUDIOVIA823X_OBJS += audiovia823x.o
--- a/usr/src/uts/common/Makefile.rules	Fri Oct 30 10:52:42 2009 +0800
+++ b/usr/src/uts/common/Makefile.rules	Thu Oct 29 21:38:34 2009 -0700
@@ -580,6 +580,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/audio/drv/audioemu10k/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/audio/drv/audio1575/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1896,6 +1900,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/audio/drv/audioens/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/audio/drv/audioemu10k/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/audio/drv/audiohd/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- a/usr/src/uts/common/io/audio/ac97/ac97.c	Fri Oct 30 10:52:42 2009 +0800
+++ b/usr/src/uts/common/io/audio/ac97/ac97.c	Thu Oct 29 21:38:34 2009 -0700
@@ -1217,12 +1217,6 @@
 		WR(AC97_VENDOR_REGISTER_11, 8);
 		break;
 
-	case AC97_CODEC_EM28028:
-		ac_wr(ac, AC97_EXTENDED_AUDIO_STAT_CTRL_REGISTER,
-		    (ac_rd(ac, AC97_EXTENDED_AUDIO_STAT_CTRL_REGISTER) &
-		    ~3800) | 0xE0);
-		break;
-
 	case AC97_CODEC_AD1886:
 		/* jack sense */
 		WR(AC97_VENDOR_REGISTER_13,
@@ -1259,7 +1253,7 @@
 	case AC97_CODEC_VT1612A:
 	case AC97_CODEC_VT1617A:
 	case AC97_CODEC_VT1616:
-		/* Turn off Center, Surround, and LFE DACs */
+		/* Turn on Center, Surround, and LFE DACs */
 		ac_clr(ac, AC97_EXTENDED_AUDIO_STAT_CTRL_REGISTER,
 		    EASCR_PRI | EASCR_PRJ | EASCR_PRK);
 		WR(AC97_VENDOR_REGISTER_01, 0x0230);
@@ -1829,11 +1823,18 @@
 	{ AC97_VENDOR_CMI,	"C-Media" },
 	{ AC97_VENDOR_CRY,	"Cirrus Logic" },
 	{ AC97_VENDOR_CXT,	"Conexant" },
+	{ AC97_VENDOR_EMC,	"eMicro" },
 	{ AC97_VENDOR_ESS,	"ESS Technology" },
 	{ AC97_VENDOR_EV,	"Ectiva" },
+	{ AC97_VENDOR_HRS,	"Intersil" },
 	{ AC97_VENDOR_ICE,	"ICEnsemble" },
+	{ AC97_VENDOR_ITE,	"ITE, Inc." },
+	{ AC97_VENDOR_NSC,	"National Semiconductor" },
+	{ AC97_VENDOR_PSC,	"Philips Semiconductor" },
+	{ AC97_VENDOR_SIL,	"Silicon Laboratories" },
 	{ AC97_VENDOR_ST,	"SigmaTel" },
 	{ AC97_VENDOR_TRA,	"TriTech", },
+	{ AC97_VENDOR_TXN,	"Texas Instruments", },
 	{ AC97_VENDOR_VIA,	"VIA Technologies" },
 	{ AC97_VENDOR_WML,	"Wolfson" },
 	{ AC97_VENDOR_YMH,	"Yamaha" },
@@ -1892,6 +1893,7 @@
 	{ AC97_CODEC_WM9704,	"WM9704" },
 	{ AC97_CODEC_ES1921,	"ES1921" },
 	{ AC97_CODEC_ICE1232,	"ICE1232/VT1611A" },
+	{ AC97_CODEC_LM4550,	"LM4550" },
 	{ AC97_CODEC_VT1612A,	"VT1612A" },
 	{ AC97_CODEC_VT1616,	"VT1616" },
 	{ AC97_CODEC_VT1616A,	"VT1616A" },
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/audio/drv/audioemu10k/audioemu10k.c	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,2616 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (C) 4Front Technologies 1996-2009.
+ */
+
+/*
+ * Purpose: Driver for the Creative Sound Blaster Live! and Audigy/2/4
+ * sound cards
+ */
+
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/pci.h>
+#include <sys/note.h>
+#include <sys/stdbool.h>
+#include <sys/audio/audio_driver.h>
+#include <sys/audio/ac97.h>
+
+#include "audioemu10k.h"
+#include <sys/promif.h>
+
+/*
+ * Include the DSP files for emu10k1 (Live!) and emu10k2 (Audigy)
+ */
+#include "emu10k_gpr.h"
+#include "emu10k1_dsp.h"
+#include "emu10k2_dsp.h"
+
+static struct ddi_device_acc_attr dev_attr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_STRUCTURE_LE_ACC,
+	DDI_STRICTORDER_ACC
+};
+
+static struct ddi_device_acc_attr buf_attr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_NEVERSWAP_ACC,
+	DDI_STRICTORDER_ACC
+};
+
+
+/*
+ * EMU10K routing stuff.
+ */
+#define	MAX_SENDS		4
+#define	SEND_L			0
+#define	SEND_R			1
+#define	SEND_SURRL		2
+#define	SEND_SURRR		3
+#define	SEND_CEN		4
+#define	SEND_LFE		5
+#define	SEND_SIDEL		6
+#define	SEND_SIDER		7
+
+#define	SPDIF_L			20
+#define	SPDIF_R			21
+
+/*
+ * Recording sources... we start from 16 to ensure that the
+ * record sources don't collide with AC'97 record sources in
+ * the control value.
+ */
+#define	INPUT_AC97		1
+#define	INPUT_SPD1		2
+#define	INPUT_SPD2		3
+#define	INPUT_DIGCD		4
+#define	INPUT_AUX2		5
+#define	INPUT_LINE2		6
+#define	INPUT_STEREOMIX		7
+
+static uint8_t front_routing[MAX_SENDS] = {
+	SEND_L, SEND_R, 0x3f, 0x3f
+};
+static uint8_t surr_routing[MAX_SENDS] = {
+	SEND_SURRL, SEND_SURRR, 0x3f, 0x3f
+};
+static uint8_t clfe_routing[MAX_SENDS] = {
+	SEND_CEN, SEND_LFE, 0x3f, 0x3f
+};
+static uint8_t side_routing[MAX_SENDS] = {
+	SEND_SIDEL, SEND_SIDER, 0x3f, 0x3f
+};
+static uint8_t no_routing[MAX_SENDS] = {
+	0x3f, 0x3f, 0x3f, 0x3f
+};
+
+/*
+ * SB Live! cannot do DMA above 2G addresses. Audigy/2/4 have special 8k page
+ * mode that supports high addresses.  However, we should not need this except
+ * on SPARC.  For simplicity's sake, we are only delivering this driver for
+ * x86 platforms.  If SPARC support is desired, then the code will have to
+ * be modified to support full 32-bit addressing.  (And again, SB Live!
+ * can't do it anyway.)
+ */
+
+static ddi_dma_attr_t dma_attr_buf = {
+	DMA_ATTR_V0,		/* Version */
+	0x00000000ULL,		/* Address low */
+	0x7ffffff0ULL,		/* Address high */
+	0xffffffffULL,		/* Counter max */
+	1ULL,			/* Default byte align */
+	0x7f,			/* Burst size */
+	0x1,			/* Minimum xfer size */
+	0xffffffffULL,		/* Maximum xfer size */
+	0xffffffffULL,		/* Max segment size */
+	1,			/* S/G list length */
+	1,			/* Granularity */
+	0			/* Flag */
+};
+
+static int emu10k_attach(dev_info_t *);
+static int emu10k_resume(dev_info_t *);
+static int emu10k_detach(emu10k_devc_t *);
+static int emu10k_suspend(emu10k_devc_t *);
+
+static int emu10k_open(void *, int, unsigned *, unsigned *, caddr_t *);
+static void emu10k_close(void *);
+static int emu10k_start(void *);
+static void emu10k_stop(void *);
+static int emu10k_format(void *);
+static int emu10k_channels(void *);
+static int emu10k_rate(void *);
+static uint64_t emu10k_count(void *);
+static void emu10k_sync(void *, unsigned);
+static size_t emu10k_qlen(void *);
+static void emu10k_chinfo(void *, int, unsigned *, unsigned *);
+
+static uint16_t emu10k_read_ac97(void *, uint8_t);
+static void emu10k_write_ac97(void *, uint8_t, uint16_t);
+static int emu10k_alloc_port(emu10k_devc_t *, int);
+static void emu10k_destroy(emu10k_devc_t *);
+static int emu10k_setup_intrs(emu10k_devc_t *);
+static int emu10k_hwinit(emu10k_devc_t *);
+static uint_t emu10k_intr(caddr_t, caddr_t);
+static void emu10k_init_effects(emu10k_devc_t *);
+
+static audio_engine_ops_t emu10k_engine_ops = {
+	AUDIO_ENGINE_VERSION,
+	emu10k_open,
+	emu10k_close,
+	emu10k_start,
+	emu10k_stop,
+	emu10k_count,
+	emu10k_format,
+	emu10k_channels,
+	emu10k_rate,
+	emu10k_sync,
+	emu10k_qlen,
+	emu10k_chinfo
+};
+
+static uint16_t
+emu10k_read_ac97(void *arg, uint8_t index)
+{
+	emu10k_devc_t *devc = arg;
+	int dtemp = 0, i;
+
+	mutex_enter(&devc->mutex);
+	OUTB(devc, index, devc->regs + 0x1e);
+	for (i = 0; i < 10000; i++)
+		if (INB(devc, devc->regs + 0x1e) & 0x80)
+			break;
+
+	if (i == 1000) {
+		mutex_exit(&devc->mutex);
+		return (0);			/* Timeout */
+	}
+	dtemp = INW(devc, devc->regs + 0x1c);
+
+	mutex_exit(&devc->mutex);
+
+	return (dtemp & 0xffff);
+}
+
+static void
+emu10k_write_ac97(void *arg, uint8_t index, uint16_t data)
+{
+	emu10k_devc_t *devc = arg;
+	int i;
+
+	mutex_enter(&devc->mutex);
+
+	OUTB(devc, index, devc->regs + 0x1e);
+	for (i = 0; i < 10000; i++)
+		if (INB(devc, devc->regs + 0x1e) & 0x80)
+			break;
+	OUTW(devc, data, devc->regs + 0x1c);
+
+	mutex_exit(&devc->mutex);
+}
+
+static uint32_t
+emu10k_read_reg(emu10k_devc_t *devc, int reg, int chn)
+{
+	uint32_t ptr, ptr_addr_mask, val, mask, size, offset;
+
+	ptr_addr_mask = (devc->feature_mask &
+	    (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) ?
+	    0x0fff0000 : 0x07ff0000;
+	ptr = ((reg << 16) & ptr_addr_mask) | (chn & 0x3f);
+	OUTL(devc, ptr, devc->regs + 0x00);	/* Pointer */
+	val = INL(devc, devc->regs + 0x04);	/* Data */
+	if (reg & 0xff000000) {
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		val &= mask;
+		val >>= offset;
+	}
+
+	return (val);
+}
+
+static void
+emu10k_write_reg(emu10k_devc_t *devc, int reg, int chn, uint32_t value)
+{
+	uint32_t ptr, ptr_addr_mask, mask, size, offset;
+
+	ptr_addr_mask = (devc->feature_mask &
+	    (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) ?
+	    0x0fff0000 : 0x07ff0000;
+	ptr = ((reg << 16) & ptr_addr_mask) | (chn & 0x3f);
+	OUTL(devc, ptr, devc->regs + 0x00);	/* Pointer */
+	if (reg & 0xff000000) {
+		size = (reg >> 24) & 0x3f;
+		offset = (reg >> 16) & 0x1f;
+		mask = ((1 << size) - 1) << offset;
+		value <<= offset;
+		value &= mask;
+		value |= INL(devc, devc->regs + 0x04) & ~mask;	/* data */
+	}
+	OUTL(devc, value, devc->regs + 0x04);	/* Data */
+}
+
+static void
+emu10k_write_routing(emu10k_devc_t *devc, int voice, unsigned char *routing)
+{
+	int i;
+
+	ASSERT(routing != NULL);
+
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
+		unsigned int srda = 0;
+
+		for (i = 0; i < 4; i++)
+			srda |= routing[i] << (i * 8);
+
+		emu10k_write_reg(devc, SRDA, voice, srda);
+	} else {
+		int fxrt = 0;
+
+		for (i = 0; i < 4; i++)
+			fxrt |= routing[i] << ((i * 4) + 16);
+		emu10k_write_reg(devc, FXRT, voice, fxrt);
+	}
+}
+
+static void
+emu10k_write_efx(emu10k_devc_t *devc, int reg, unsigned int value)
+{
+	emu10k_write_reg(devc, reg, 0, value);
+}
+
+static uint_t
+emu10k_intr(caddr_t argp, caddr_t nocare)
+{
+	emu10k_devc_t *devc = (void *) argp;
+	emu10k_portc_t *portc;
+	uint32_t status;
+	audio_engine_t *cons = NULL, *prod = NULL;
+
+
+	_NOTE(ARGUNUSED (nocare));
+
+	mutex_enter(&devc->mutex);
+	if (devc->suspended) {
+		mutex_exit(&devc->mutex);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	status = INL(devc, devc->regs + INTPEND);
+
+	if (status == 0) {
+		mutex_exit(&devc->mutex);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	if (status & INT_CL) {	/* channel loop */
+		emu10k_write_reg(devc, CLIPL, 0, (1U << 8));
+		OUTL(devc, INT_CL, devc->regs + INTPEND);
+		portc = devc->portc[EMU10K_PLAY];
+		if (portc->active) {
+			cons = portc->engine;
+		}
+	}
+	if (status & (INT_AF|INT_AH|INT_MF|INT_MH)) {	/* ADC interrupt */
+		OUTL(devc, INT_AF|INT_AH|INT_MF|INT_MH, devc->regs + INTPEND);
+		portc = devc->portc[EMU10K_REC];
+		if (portc->active) {
+			prod = portc->engine;
+		}
+	}
+
+	mutex_exit(&devc->mutex);
+
+	if (cons) {
+		audio_engine_consume(cons);
+	}
+	if (prod) {
+		audio_engine_produce(prod);
+	}
+
+	return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * Audio routines
+ */
+
+static void
+emu10k_update_output_volume(emu10k_portc_t *portc, int voice, int chn)
+{
+	emu10k_devc_t *devc = portc->devc;
+	unsigned int tmp;
+	unsigned char send[2];
+
+	/*
+	 * Each voice operator of EMU10k has 4 sends (0=left, 1=right,
+	 * 2=surround_left, 3=surround_right). The original OSS driver
+	 * used all of them to spread stereo output to two different
+	 * speaker pairs. This Boomer version uses only the first two
+	 * sends. The other sends are set to 0.
+	 *
+	 * Boomer uses multiple voice pairs to play multichannel
+	 * audio. This function is used to update only one of these
+	 * pairs.
+	 */
+
+	send[0] = 0xff;		/* Max */
+	send[1] = 0xff;		/* Max */
+
+	/* Analog voice */
+	if (chn == LEFT_CH) {
+		send[1] = 0;
+	} else {
+		send[0] = 0;
+	}
+
+	tmp = emu10k_read_reg(devc, PTAB, voice) & 0xffff0000;
+	emu10k_write_reg(devc, PTAB, voice, tmp | (send[0] << 8) | send[1]);
+}
+
+static void
+emu10k_setup_voice(emu10k_portc_t *portc, int voice, int chn, int buf_offset)
+{
+	emu10k_devc_t *devc = portc->devc;
+	unsigned int nCRA = 0;
+
+	unsigned int loop_start, loop_end, buf_size;
+
+	int sz;
+	int start_pos;
+
+	emu10k_write_reg(devc, VEDS, voice, 0x0);	/* OFF */
+	emu10k_write_reg(devc, VTFT, voice, 0xffff);
+	emu10k_write_reg(devc, CVCF, voice, 0xffff);
+
+	sz = 2;			/* Shift value for 16 bits stereo */
+
+	/* Size of one stereo sub buffer */
+	buf_size = (portc->buf_size / portc->channels) * 2;
+	loop_start = (portc->memptr + buf_offset) >> sz;
+	loop_end = (portc->memptr + buf_offset + buf_size) >> sz;
+
+	/* set stereo */
+	emu10k_write_reg(devc, CPF, voice, 0x8000);
+
+	nCRA = 28;			/* Stereo (16 bits) */
+	start_pos = loop_start + nCRA;
+
+	/* SDL, ST, CA */
+
+	emu10k_write_reg(devc, SDL, voice, loop_end);
+	emu10k_write_reg(devc, SCSA, voice, loop_start);
+	emu10k_write_reg(devc, PTAB, voice, 0);
+
+	emu10k_update_output_volume(portc, voice, chn);	/* Set volume */
+
+	emu10k_write_reg(devc, QKBCA, voice, start_pos);
+
+	emu10k_write_reg(devc, Z1, voice, 0);
+	emu10k_write_reg(devc, Z2, voice, 0);
+
+	/* This is really a physical address */
+	emu10k_write_reg(devc, MAPA, voice,
+	    0x1fff | (devc->silence_paddr << 1));
+	emu10k_write_reg(devc, MAPB, voice,
+	    0x1fff | (devc->silence_paddr << 1));
+
+	emu10k_write_reg(devc, VTFT, voice, 0x0000ffff);
+	emu10k_write_reg(devc, CVCF, voice, 0x0000ffff);
+	emu10k_write_reg(devc, MEHA, voice, 0);
+	emu10k_write_reg(devc, MEDS, voice, 0x7f);
+	emu10k_write_reg(devc, MLV, voice, 0x8000);
+	emu10k_write_reg(devc, VLV, voice, 0x8000);
+	emu10k_write_reg(devc, VFM, voice, 0);
+	emu10k_write_reg(devc, TMFQ, voice, 0);
+	emu10k_write_reg(devc, VVFQ, voice, 0);
+	emu10k_write_reg(devc, MEV, voice, 0x8000);
+	emu10k_write_reg(devc, VEHA, voice, 0x7f7f);	/* OK */
+	/* No volume envelope delay (OK) */
+	emu10k_write_reg(devc, VEV, voice, 0x8000);
+	emu10k_write_reg(devc, PEFE_FILTERAMOUNT, voice, 0x7f);
+	emu10k_write_reg(devc, PEFE_PITCHAMOUNT, voice, 0x00);
+}
+
+static void
+emu10k_setup_silence(emu10k_portc_t *portc, int voice)
+{
+	emu10k_devc_t *devc = portc->devc;
+
+	emu10k_write_reg(devc, VEDS, voice, 0x0);	/* OFF */
+	emu10k_write_reg(devc, VTFT, voice, 0xffff);
+	emu10k_write_reg(devc, CVCF, voice, 0xffff);
+
+	/* set stereo */
+	emu10k_write_reg(devc, CPF, voice, 0x8000);
+
+	/* SDL, ST, CA */
+	emu10k_write_reg(devc, SDL, voice, portc->fragfr);
+	emu10k_write_reg(devc, SCSA, voice, 0);
+	emu10k_write_reg(devc, PTAB, voice, 0);
+	emu10k_write_reg(devc, QKBCA, voice, 0);
+
+	emu10k_write_reg(devc, Z1, voice, 0);
+	emu10k_write_reg(devc, Z2, voice, 0);
+
+	/* This is really a physical address */
+	emu10k_write_reg(devc, MAPA, voice,
+	    0x1fff | (devc->silence_paddr << 1));
+	emu10k_write_reg(devc, MAPB, voice,
+	    0x1fff | (devc->silence_paddr << 1));
+
+	emu10k_write_reg(devc, VTFT, voice, 0x0000ffff);
+	emu10k_write_reg(devc, CVCF, voice, 0x0000ffff);
+	emu10k_write_reg(devc, MEHA, voice, 0);
+	emu10k_write_reg(devc, MEDS, voice, 0x7f);
+	emu10k_write_reg(devc, MLV, voice, 0x8000);
+	emu10k_write_reg(devc, VLV, voice, 0x8000);
+	emu10k_write_reg(devc, VFM, voice, 0);
+	emu10k_write_reg(devc, TMFQ, voice, 0);
+	emu10k_write_reg(devc, VVFQ, voice, 0);
+	emu10k_write_reg(devc, MEV, voice, 0x8000);
+	emu10k_write_reg(devc, VEHA, voice, 0x7f7f);	/* OK */
+	/* No volume envelope delay (OK) */
+	emu10k_write_reg(devc, VEV, voice, 0x8000);
+	emu10k_write_reg(devc, PEFE_FILTERAMOUNT, voice, 0x7f);
+	emu10k_write_reg(devc, PEFE_PITCHAMOUNT, voice, 0x00);
+}
+
+int
+emu10k_open(void *arg, int flag,
+    unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp)
+{
+	emu10k_portc_t *portc = arg;
+	emu10k_devc_t *devc = portc->devc;
+
+	_NOTE(ARGUNUSED(flag));
+
+	portc->started = B_FALSE;
+	portc->active = B_FALSE;
+	*fragfrp = portc->fragfr;
+	*nfragsp = portc->nfrags;
+	*bufp = portc->buf_kaddr;
+
+	mutex_enter(&devc->mutex);
+	if (!devc->suspended)
+		portc->reset_port(portc);
+	portc->count = 0;
+	mutex_exit(&devc->mutex);
+
+	return (0);
+}
+
+void
+emu10k_close(void *arg)
+{
+	emu10k_portc_t *portc = arg;
+	emu10k_devc_t *devc = portc->devc;
+
+	mutex_enter(&devc->mutex);
+	if (!devc->suspended)
+		portc->stop_port(portc);
+	portc->started = B_FALSE;
+	mutex_exit(&devc->mutex);
+}
+
+int
+emu10k_start(void *arg)
+{
+	emu10k_portc_t *portc = arg;
+	emu10k_devc_t *devc = portc->devc;
+
+	mutex_enter(&devc->mutex);
+	if (!portc->started) {
+		if (!devc->suspended)
+			portc->start_port(portc);
+		portc->started = B_TRUE;
+	}
+	mutex_exit(&devc->mutex);
+	return (0);
+}
+
+void
+emu10k_stop(void *arg)
+{
+	emu10k_portc_t *portc = arg;
+	emu10k_devc_t *devc = portc->devc;
+
+	mutex_enter(&devc->mutex);
+	if (portc->started) {
+		if (!devc->suspended)
+			portc->stop_port(portc);
+		portc->started = B_FALSE;
+	}
+	mutex_exit(&devc->mutex);
+}
+
+int
+emu10k_format(void *arg)
+{
+	_NOTE(ARGUNUSED(arg));
+
+	return (AUDIO_FORMAT_S16_LE);
+}
+
+int
+emu10k_channels(void *arg)
+{
+	emu10k_portc_t *portc = arg;
+
+	return (portc->channels);
+}
+
+int
+emu10k_rate(void *arg)
+{
+	_NOTE(ARGUNUSED(arg));
+
+	return (SAMPLE_RATE);
+}
+
+void
+emu10k_sync(void *arg, unsigned nframes)
+{
+	emu10k_portc_t *portc = arg;
+	_NOTE(ARGUNUSED(nframes));
+
+	(void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir);
+}
+
+size_t
+emu10k_qlen(void *arg)
+{
+	_NOTE(ARGUNUSED (arg));
+	return (0);
+}
+
+uint64_t
+emu10k_count(void *arg)
+{
+	emu10k_portc_t *portc = arg;
+	emu10k_devc_t *devc = portc->devc;
+	uint64_t count;
+
+	mutex_enter(&devc->mutex);
+	if (!devc->suspended)
+		portc->update_port(portc);
+	count = portc->count;
+	mutex_exit(&devc->mutex);
+
+	return (count);
+}
+
+static void
+emu10k_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
+{
+	emu10k_portc_t *portc = arg;
+
+	*offset = portc->nframes * (chan / 2) * 2 + (chan % 2);
+	*incr = 2;
+}
+
+/* private implementation bits */
+
+static void
+emu10k_set_loop_stop(emu10k_devc_t *devc, int voice, int s)
+{
+	unsigned int tmp;
+	int offs, bit;
+
+	offs = voice / 32;
+	bit = voice % 32;
+	s = !!s;
+
+	tmp = emu10k_read_reg(devc, SOLL + offs, 0);
+	tmp &= ~(1 << bit);
+
+	if (s)
+		tmp |= (1 << bit);
+	emu10k_write_reg(devc, SOLL + offs, 0, tmp);
+}
+
+static unsigned int
+emu10k_rate_to_pitch(unsigned int rate)
+{
+	static unsigned int logMagTable[128] = {
+		0x00000, 0x02dfc, 0x05b9e, 0x088e6,
+		0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
+		0x1663f, 0x1918a, 0x1bc84, 0x1e72e,
+		0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
+		0x2b803, 0x2e0e8, 0x30985, 0x331db,
+		0x359eb, 0x381b6, 0x3a93d, 0x3d081,
+		0x3f782, 0x41e42, 0x444c1, 0x46b01,
+		0x49101, 0x4b6c4, 0x4dc49, 0x50191,
+		0x5269e, 0x54b6f, 0x57006, 0x59463,
+		0x5b888, 0x5dc74, 0x60029, 0x623a7,
+		0x646ee, 0x66a00, 0x68cdd, 0x6af86,
+		0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
+		0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5,
+		0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
+		0x86082, 0x88089, 0x8a064, 0x8c014,
+		0x8df98, 0x8fef1, 0x91e20, 0x93d26,
+		0x95c01, 0x97ab4, 0x9993e, 0x9b79f,
+		0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
+		0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537,
+		0xac241, 0xadf26, 0xafbe7, 0xb1885,
+		0xb3500, 0xb5157, 0xb6d8c, 0xb899f,
+		0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
+		0xc1404, 0xc2f50, 0xc4a7b, 0xc6587,
+		0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
+		0xceaec, 0xd053f, 0xd1f73, 0xd398a,
+		0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
+		0xdba4a, 0xdd3b4, 0xded03, 0xe0636,
+		0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
+		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08,
+		0xee44c, 0xefc78, 0xf148a, 0xf2c83,
+		0xf4463, 0xf5c2a, 0xf73da, 0xf8b71,
+		0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
+	};
+	static char logSlopeTable[128] = {
+		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
+		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
+		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
+		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
+		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
+		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
+		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
+		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
+		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
+		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
+		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
+		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
+		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
+		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
+		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
+		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
+	};
+	int i;
+
+	if (rate == 0)
+		return (0);			/* Bail out if no leading "1" */
+	rate *= 11185;		/* Scale 48000 to 0x20002380 */
+	for (i = 31; i > 0; i--) {
+		if (rate & 0x80000000) {	/* Detect leading "1" */
+			return (((unsigned int) (i - 15) << 20) +
+			    logMagTable[0x7f & (rate >> 24)] +
+			    (0x7f & (rate >> 17)) *
+			    logSlopeTable[0x7f & (rate >> 24)]);
+		}
+		rate <<= 1;
+	}
+
+	return (0);			/* Should never reach this point */
+}
+
+static unsigned int
+emu10k_rate_to_linearpitch(unsigned int rate)
+{
+	rate = (rate << 8) / 375;
+	return (rate >> 1) + (rate & 1);
+}
+
+static void
+emu10k_prepare_voice(emu10k_devc_t *devc, int voice)
+{
+	unsigned int sample, initial_pitch, pitch_target;
+	unsigned int cra, cs, ccis, i;
+
+	/* setup CCR regs */
+	cra = 64;
+	cs = 4;			/* Stereo */
+	ccis = 28;		/* Stereo */
+	sample = 0;		/* 16 bit silence */
+
+	for (i = 0; i < cs; i++)
+		emu10k_write_reg(devc, CD0 + i, voice, sample);
+
+	emu10k_write_reg(devc, CCR_CACHEINVALIDSIZE, voice, 0);
+	emu10k_write_reg(devc, CCR_READADDRESS, voice, cra);
+	emu10k_write_reg(devc, CCR_CACHEINVALIDSIZE, voice, ccis);
+
+	/* Set current pitch */
+	emu10k_write_reg(devc, IFA, voice, 0xff00);
+	emu10k_write_reg(devc, VTFT, voice, 0xffffffff);
+	emu10k_write_reg(devc, CVCF, voice, 0xffffffff);
+	emu10k_set_loop_stop(devc, voice, 0);
+
+	pitch_target = emu10k_rate_to_linearpitch(SAMPLE_RATE);
+	initial_pitch = emu10k_rate_to_pitch(SAMPLE_RATE) >> 8;
+	emu10k_write_reg(devc, PTRX_PITCHTARGET, voice, pitch_target);
+	emu10k_write_reg(devc, CPF_CURRENTPITCH, voice, pitch_target);
+	emu10k_write_reg(devc, IP, voice, initial_pitch);
+}
+
+static void
+emu10k_stop_voice(emu10k_devc_t *devc, int voice)
+{
+	emu10k_write_reg(devc, IFA, voice, 0xffff);
+	emu10k_write_reg(devc, VTFT, voice, 0xffff);
+	emu10k_write_reg(devc, PTRX_PITCHTARGET, voice, 0);
+	emu10k_write_reg(devc, CPF_CURRENTPITCH, voice, 0);
+	emu10k_write_reg(devc, IP, voice, 0);
+	emu10k_set_loop_stop(devc, voice, 1);
+}
+
+static void
+emu10k_reset_pair(emu10k_portc_t *portc, int voice, uint8_t *routing,
+    int buf_offset)
+{
+	emu10k_devc_t *devc = portc->devc;
+
+	/* Left channel */
+	/* Intial filter cutoff and attenuation */
+	emu10k_write_reg(devc, IFA, voice, 0xffff);
+	/* Volume envelope decay and sustain */
+	emu10k_write_reg(devc, VEDS, voice, 0x0);
+	/* Volume target and Filter cutoff target */
+	emu10k_write_reg(devc, VTFT, voice, 0xffff);
+	/* Pitch target and sends A and B */
+	emu10k_write_reg(devc, PTAB, voice, 0x0);
+
+	/* The same for right channel */
+	emu10k_write_reg(devc, IFA, voice + 1, 0xffff);
+	emu10k_write_reg(devc, VEDS, voice + 1, 0x0);
+	emu10k_write_reg(devc, VTFT, voice + 1, 0xffff);
+	emu10k_write_reg(devc, PTAB, voice + 1, 0x0);
+
+	/* now setup the voices and go! */
+	emu10k_setup_voice(portc, voice, LEFT_CH, buf_offset);
+	emu10k_setup_voice(portc, voice + 1, RIGHT_CH, buf_offset);
+
+	emu10k_write_routing(devc, voice, routing);
+	emu10k_write_routing(devc, voice + 1, routing);
+}
+
+void
+emu10k_start_play(emu10k_portc_t *portc)
+{
+	emu10k_devc_t *devc = portc->devc;
+
+	ASSERT(mutex_owned(&devc->mutex));
+	emu10k_prepare_voice(devc, 0);
+	emu10k_prepare_voice(devc, 1);
+
+	emu10k_prepare_voice(devc, 2);
+	emu10k_prepare_voice(devc, 3);
+
+	emu10k_prepare_voice(devc, 4);
+	emu10k_prepare_voice(devc, 5);
+
+	emu10k_prepare_voice(devc, 6);
+	emu10k_prepare_voice(devc, 7);
+
+	emu10k_prepare_voice(devc, 8);
+	emu10k_prepare_voice(devc, 9);
+
+	/* arrange to receive full loop interrupts on channel 8 */
+	emu10k_write_reg(devc, CLIEL, 0, (1U << 8));
+
+	/* initialize our position counter... */
+	portc->pos =
+	    (emu10k_read_reg(devc, QKBCA, 0) & 0xffffff) -
+	    (portc->memptr >> 2);
+
+	/* Trigger playback on all voices */
+	emu10k_write_reg(devc, VEDS, 0, 0x7f7f);
+	emu10k_write_reg(devc, VEDS, 1, 0x7f7f);
+	emu10k_write_reg(devc, VEDS, 2, 0x7f7f);
+	emu10k_write_reg(devc, VEDS, 3, 0x7f7f);
+	emu10k_write_reg(devc, VEDS, 4, 0x7f7f);
+	emu10k_write_reg(devc, VEDS, 5, 0x7f7f);
+	emu10k_write_reg(devc, VEDS, 6, 0x7f7f);
+	emu10k_write_reg(devc, VEDS, 7, 0x7f7f);
+	emu10k_write_reg(devc, VEDS, 8, 0x7f7f);
+	emu10k_write_reg(devc, VEDS, 9, 0x7f7f);
+
+	portc->active = B_TRUE;
+}
+
+void
+emu10k_stop_play(emu10k_portc_t *portc)
+{
+	emu10k_devc_t *devc = portc->devc;
+
+	emu10k_stop_voice(devc, 0);
+	emu10k_stop_voice(devc, 1);
+	emu10k_stop_voice(devc, 2);
+	emu10k_stop_voice(devc, 3);
+	emu10k_stop_voice(devc, 4);
+	emu10k_stop_voice(devc, 5);
+	emu10k_stop_voice(devc, 6);
+	emu10k_stop_voice(devc, 7);
+	emu10k_stop_voice(devc, 8);
+	emu10k_stop_voice(devc, 9);
+
+	portc->active = B_FALSE;
+}
+
+void
+emu10k_reset_play(emu10k_portc_t *portc)
+{
+	emu10k_devc_t *devc = portc->devc;
+	uint32_t offs;
+
+	offs = (portc->buf_size / portc->channels) * 2;
+
+	if (devc->feature_mask & SB_71) {
+		emu10k_reset_pair(portc, 0, front_routing, 0);
+		emu10k_reset_pair(portc, 2, clfe_routing, offs);
+		emu10k_reset_pair(portc, 4, surr_routing, 2 * offs);
+		emu10k_reset_pair(portc, 6, side_routing, 3 * offs);
+	} else if (devc->feature_mask & SB_51) {
+		emu10k_reset_pair(portc, 0, front_routing, 0);
+		emu10k_reset_pair(portc, 2, clfe_routing, offs);
+		emu10k_reset_pair(portc, 4, surr_routing, 2 * offs);
+	} else {
+		emu10k_reset_pair(portc, 0, front_routing, 0);
+		emu10k_reset_pair(portc, 2, surr_routing, offs);
+	}
+	emu10k_setup_silence(portc, 8);
+	emu10k_setup_silence(portc, 9);
+
+	/*
+	 * This way we can use voices 8 and 9 for timing, we have
+	 * programmed them to be just the size of a single fragment,
+	 * that way when they loop we get a clean interrupt.
+	 */
+	emu10k_write_routing(devc, 8, no_routing);
+	emu10k_write_routing(devc, 9, no_routing);
+	portc->pos = 0;
+}
+
+uint32_t emu10k_vars[5];
+
+void
+emu10k_update_play(emu10k_portc_t *portc)
+{
+	emu10k_devc_t *devc = portc->devc;
+	uint32_t cnt, pos;
+
+	/*
+	 * Note: position is given as stereo samples, i.e. frames.
+	 */
+	pos = emu10k_read_reg(devc, QKBCA, 0) & 0xffffff;
+	pos -= (portc->memptr >> 2);
+
+	if (pos <= portc->pos) {
+		cnt = portc->nframes - portc->pos;
+		cnt += pos;
+	} else {
+		cnt = (pos - portc->pos);
+	}
+	if (portc->dopos) {
+		emu10k_vars[0] = portc->pos;
+		emu10k_vars[1] = pos;
+		emu10k_vars[2] = (uint32_t)portc->count;
+		emu10k_vars[3] = cnt;
+		portc->dopos = 0;
+	}
+	if (cnt > portc->nframes) {
+		printf("Got bogus count %u\n", cnt);
+		cnt = portc->fragfr;
+	}
+	ASSERT(cnt <= portc->nframes);
+	portc->count += cnt;
+	portc->pos = pos;
+}
+
+void
+emu10k_start_rec(emu10k_portc_t *portc)
+{
+	emu10k_devc_t *devc = portc->devc;
+	uint32_t tmp;
+
+	/* Intr enable */
+	OUTL(devc, INL(devc, devc->regs + IE) | IE_MB | IE_AB,
+	    devc->regs + IE);
+
+	tmp = 0;			/* setup 48Kz */
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL))
+		tmp |= 0x30;		/* Left/right channel enable */
+	else
+		tmp |= 0x18;		/* Left/right channel enable */
+	emu10k_write_reg(devc, ADCSR, 0, tmp);	/* GO */
+
+	portc->active = B_TRUE;
+}
+
+void
+emu10k_stop_rec(emu10k_portc_t *portc)
+{
+	emu10k_devc_t *devc = portc->devc;
+
+	ASSERT(mutex_owned(&devc->mutex));
+	emu10k_write_reg(devc, ADCSR, 0, 0);
+
+	portc->active = B_FALSE;
+}
+void
+emu10k_reset_rec(emu10k_portc_t *portc)
+{
+	emu10k_devc_t *devc = portc->devc;
+	uint32_t sz;
+
+	switch (portc->buf_size) {
+	case 4096:
+		sz = 15;
+		break;
+	case 8192:
+		sz = 19;
+		break;
+	case 16384:
+		sz = 23;
+		break;
+	case 32768:
+		sz = 27;
+		break;
+	case 65536:
+		sz = 31;
+		break;
+	}
+	emu10k_write_reg(devc, ADCBA, 0, portc->buf_paddr);
+	emu10k_write_reg(devc, ADCBS, 0, sz);
+	emu10k_write_reg(devc, ADCSR, 0, 0);	/* reset for phase */
+	portc->pos = 0;
+	OUTL(devc, INL(devc, devc->regs + IE) & ~(IE_MB | IE_AB),
+	    devc->regs + IE);
+}
+
+void
+emu10k_update_rec(emu10k_portc_t *portc)
+{
+	emu10k_devc_t *devc = portc->devc;
+	uint32_t cnt, pos;
+
+	/* given in bytes, we divide all counts by 4 to get samples */
+	pos = emu10k_read_reg(devc,
+	    (devc->feature_mask & SB_LIVE) ? MIDX : ADCIDX, 0);
+	if (pos <= portc->pos) {
+		cnt = ((portc->buf_size) - portc->pos) >> 2;
+		cnt += (pos >> 2);
+	} else {
+		cnt = ((pos - portc->pos) >> 2);
+	}
+	portc->count += cnt;
+	portc->pos = pos;
+}
+
+int
+emu10k_alloc_port(emu10k_devc_t *devc, int num)
+{
+	emu10k_portc_t *portc;
+	size_t len;
+	ddi_dma_cookie_t cookie;
+	uint_t count;
+	int dir;
+	unsigned caps;
+	audio_dev_t *adev;
+	int i, n;
+
+	adev = devc->adev;
+	portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
+	devc->portc[num] = portc;
+	portc->devc = devc;
+	portc->started = B_FALSE;
+
+	portc->memptr = devc->audio_memptr;
+	devc->audio_memptr += (DMABUF_SIZE + 4095) & ~4095;
+
+	switch (num) {
+	case EMU10K_REC:
+		portc->syncdir = DDI_DMA_SYNC_FORKERNEL;
+		caps = ENGINE_INPUT_CAP;
+		dir = DDI_DMA_READ;
+		portc->channels = 2;
+		portc->start_port = emu10k_start_rec;
+		portc->stop_port = emu10k_stop_rec;
+		portc->reset_port = emu10k_reset_rec;
+		portc->update_port = emu10k_update_rec;
+		/* This is the minimum record buffer size. */
+		portc->buf_size = 4096;
+		portc->nfrags = 2;
+		portc->nframes = 4096 / 4;
+		portc->fragfr = portc->nframes / portc->nfrags;
+		break;
+	case EMU10K_PLAY:
+		portc->syncdir = DDI_DMA_SYNC_FORDEV;
+		caps = ENGINE_OUTPUT_CAP;
+		dir = DDI_DMA_WRITE;
+		portc->channels = 8;
+		portc->start_port = emu10k_start_play;
+		portc->stop_port = emu10k_stop_play;
+		portc->reset_port = emu10k_reset_play;
+		portc->update_port = emu10k_update_play;
+		/* XXX: this could probably be tunable */
+		portc->nfrags = 2;
+		portc->fragfr = 288;
+		portc->nframes = portc->nfrags * portc->fragfr;
+		portc->buf_size = portc->nframes * portc->channels * 2;
+		break;
+	default:
+		return (DDI_FAILURE);
+	}
+
+	/*
+	 * Fragments that are not powers of two don't seem to work
+	 * at all with EMU10K.  For simplicity's sake, we eliminate
+	 * the question and fix the interrupt rate.  This is also the
+	 * logical minimum for record, which requires at least 4K for
+	 * the record size.
+	 */
+
+	if (portc->buf_size > DMABUF_SIZE) {
+		cmn_err(CE_NOTE, "Buffer size %d is too large (max %d)",
+		    (int)portc->buf_size, DMABUF_SIZE);
+		portc->buf_size = DMABUF_SIZE;
+	}
+
+	/* Alloc buffers */
+	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
+	    &portc->buf_dmah) != DDI_SUCCESS) {
+		audio_dev_warn(adev, "failed to allocate BUF handle");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_dma_mem_alloc(portc->buf_dmah, portc->buf_size,
+	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
+	    &portc->buf_kaddr, &len, &portc->buf_acch) != DDI_SUCCESS) {
+		audio_dev_warn(adev, "failed to allocate BUF memory");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_dma_addr_bind_handle(portc->buf_dmah, NULL, portc->buf_kaddr,
+	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP,
+	    NULL, &cookie, &count) != DDI_SUCCESS) {
+		audio_dev_warn(adev, "failed binding BUF DMA handle");
+		return (DDI_FAILURE);
+	}
+	portc->buf_paddr = cookie.dmac_address;
+
+	if ((devc->feature_mask & SB_LIVE) &&
+	    (portc->buf_paddr & 0x80000000)) {
+		audio_dev_warn(adev, "Got DMA buffer beyond 2G limit.");
+		return (DDI_FAILURE);
+	}
+
+	if (num == EMU10K_PLAY) {	/* Output device */
+		n = portc->memptr / 4096;
+		/*
+		 * Fill the page table
+		 */
+		for (i = 0; i < portc->buf_size / 4096; i++) {
+			FILL_PAGE_MAP_ENTRY(n + i,
+			    portc->buf_paddr + i * 4096);
+		}
+
+		(void) ddi_dma_sync(devc->pt_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
+	}
+
+	portc->engine = audio_engine_alloc(&emu10k_engine_ops, caps);
+	if (portc->engine == NULL) {
+		audio_dev_warn(adev, "audio_engine_alloc failed");
+		return (DDI_FAILURE);
+	}
+
+	audio_engine_set_private(portc->engine, portc);
+	audio_dev_add_engine(adev, portc->engine);
+
+	return (DDI_SUCCESS);
+}
+
+int
+emu10k_setup_intrs(emu10k_devc_t *devc)
+{
+	uint_t ipri;
+	int actual;
+	int rv;
+	ddi_intr_handle_t ih[1];
+
+	rv = ddi_intr_alloc(devc->dip, ih, DDI_INTR_TYPE_FIXED,
+	    0, 1, &actual, DDI_INTR_ALLOC_STRICT);
+	if ((rv != DDI_SUCCESS) || (actual != 1)) {
+		audio_dev_warn(devc->adev,
+		    "Can't alloc interrupt handle (rv %d actual %d)",
+		    rv, actual);
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_intr_get_pri(ih[0], &ipri) != DDI_SUCCESS) {
+		audio_dev_warn(devc->adev, "Can't get interrupt priority");
+		(void) ddi_intr_free(ih[0]);
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_intr_add_handler(ih[0], emu10k_intr, devc, NULL) !=
+	    DDI_SUCCESS) {
+		audio_dev_warn(devc->adev, "Can't add interrupt handler");
+		(void) ddi_intr_free(ih[0]);
+		return (DDI_FAILURE);
+	}
+
+	devc->ih = ih[0];
+	mutex_init(&devc->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
+	return (DDI_SUCCESS);
+}
+
+void
+emu10k_destroy(emu10k_devc_t *devc)
+{
+	if (devc->ih != NULL) {
+		(void) ddi_intr_disable(devc->ih);
+		(void) ddi_intr_remove_handler(devc->ih);
+		(void) ddi_intr_free(devc->ih);
+		mutex_destroy(&devc->mutex);
+	}
+
+	if (devc->ksp) {
+		kstat_delete(devc->ksp);
+	}
+
+	if (devc->silence_paddr) {
+		(void) ddi_dma_unbind_handle(devc->silence_dmah);
+	}
+	if (devc->silence_acch) {
+		ddi_dma_mem_free(&devc->silence_acch);
+	}
+	if (devc->silence_dmah) {
+		ddi_dma_free_handle(&devc->silence_dmah);
+	}
+
+	if (devc->pt_paddr) {
+		(void) ddi_dma_unbind_handle(devc->pt_dmah);
+	}
+	if (devc->pt_acch) {
+		ddi_dma_mem_free(&devc->pt_acch);
+	}
+	if (devc->pt_dmah) {
+		ddi_dma_free_handle(&devc->pt_dmah);
+	}
+
+
+	for (int i = 0; i < CTL_MAX; i++) {
+		emu10k_ctrl_t *ec = &devc->ctrls[i];
+		if (ec->ctrl != NULL) {
+			audio_dev_del_control(ec->ctrl);
+			ec->ctrl = NULL;
+		}
+	}
+
+	for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
+		emu10k_portc_t *portc = devc->portc[i];
+		if (!portc)
+			continue;
+		if (portc->engine) {
+			audio_dev_remove_engine(devc->adev, portc->engine);
+			audio_engine_free(portc->engine);
+		}
+		if (portc->buf_paddr) {
+			(void) ddi_dma_unbind_handle(portc->buf_dmah);
+		}
+		if (portc->buf_acch) {
+			ddi_dma_mem_free(&portc->buf_acch);
+		}
+		if (portc->buf_dmah) {
+			ddi_dma_free_handle(&portc->buf_dmah);
+		}
+		kmem_free(portc, sizeof (*portc));
+	}
+
+	if (devc->ac97 != NULL) {
+		ac97_free(devc->ac97);
+	}
+	if (devc->adev != NULL) {
+		audio_dev_free(devc->adev);
+	}
+	if (devc->regsh != NULL) {
+		ddi_regs_map_free(&devc->regsh);
+	}
+	if (devc->pcih != NULL) {
+		pci_config_teardown(&devc->pcih);
+	}
+
+	kmem_free(devc, sizeof (*devc));
+}
+
+static void
+emu10k_init_voice(emu10k_devc_t *devc, int voice)
+{
+	emu10k_set_loop_stop(devc, voice, 1);
+
+	emu10k_write_reg(devc, VEDS, voice, 0x0);
+	emu10k_write_reg(devc, IP, voice, 0x0);
+	emu10k_write_reg(devc, VTFT, voice, 0xffff);
+	emu10k_write_reg(devc, CVCF, voice, 0xffff);
+	emu10k_write_reg(devc, PTAB, voice, 0x0);
+	emu10k_write_reg(devc, CPF, voice, 0x0);
+	emu10k_write_reg(devc, CCR, voice, 0x0);
+	emu10k_write_reg(devc, SCSA, voice, 0x0);
+	emu10k_write_reg(devc, SDL, voice, 0x10);
+	emu10k_write_reg(devc, QKBCA, voice, 0x0);
+	emu10k_write_reg(devc, Z1, voice, 0x0);
+	emu10k_write_reg(devc, Z2, voice, 0x0);
+
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL))
+		emu10k_write_reg(devc, SRDA, voice, 0x03020100);
+	else
+		emu10k_write_reg(devc, FXRT, voice, 0x32100000);
+
+	emu10k_write_reg(devc, MEHA, voice, 0x0);
+	emu10k_write_reg(devc, MEDS, voice, 0x0);
+	emu10k_write_reg(devc, IFA, voice, 0xffff);
+	emu10k_write_reg(devc, PEFE, voice, 0x0);
+	emu10k_write_reg(devc, VFM, voice, 0x0);
+	emu10k_write_reg(devc, TMFQ, voice, 24);
+	emu10k_write_reg(devc, VVFQ, voice, 24);
+	emu10k_write_reg(devc, TMPE, voice, 0x0);
+	emu10k_write_reg(devc, VLV, voice, 0x0);
+	emu10k_write_reg(devc, MLV, voice, 0x0);
+	emu10k_write_reg(devc, VEHA, voice, 0x0);
+	emu10k_write_reg(devc, VEV, voice, 0x0);
+	emu10k_write_reg(devc, MEV, voice, 0x0);
+
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
+		emu10k_write_reg(devc, CSBA, voice, 0x0);
+		emu10k_write_reg(devc, CSDC, voice, 0x0);
+		emu10k_write_reg(devc, CSFE, voice, 0x0);
+		emu10k_write_reg(devc, CSHG, voice, 0x0);
+		emu10k_write_reg(devc, SRHE, voice, 0x3f3f3f3f);
+	}
+}
+
+static void
+emu10k_refresh_mixer(emu10k_devc_t *devc)
+{
+	uint32_t val;
+	uint32_t set;
+
+	for (int gpr = 0; gpr < MAX_GPR; gpr++) {
+		if (devc->gpr_shadow[gpr].valid) {
+			emu10k_write_reg(devc, gpr + GPR0, 0,
+			    devc->gpr_shadow[gpr].value);
+		}
+	}
+
+	set = devc->ctrls[CTL_JACK3].val;
+	if (devc->feature_mask & SB_INVSP) {
+		set = !set;
+	}
+
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
+		val = INL(devc, devc->regs + 0x18);
+		val &= ~A_IOCFG_GPOUT0;
+		val |= set ? 0x44 : 0x40;
+		OUTL(devc, val, devc->regs + 0x18);
+
+	} else if (devc->feature_mask & SB_LIVE) {
+		val = INL(devc, devc->regs + HCFG);
+		val &= ~HCFG_GPOUT0;
+		val |= set ? HCFG_GPOUT0 : 0;
+		OUTL(devc, val, devc->regs + HCFG);
+	}
+}
+
+int
+emu10k_hwinit(emu10k_devc_t *devc)
+{
+
+	unsigned int tmp, i;
+	unsigned int reg;
+
+	ASSERT(mutex_owned(&devc->mutex));
+
+	emu10k_write_reg(devc, AC97SLOT, 0, AC97SLOT_CENTER | AC97SLOT_LFE);
+
+	OUTL(devc, 0x00000000, devc->regs + 0x0c);	/* Intr disable */
+	OUTL(devc, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
+	    HCFG_MUTEBUTTONENABLE,
+	    devc->regs + HCFG);
+
+	emu10k_write_reg(devc, MBS, 0, 0x0);
+	emu10k_write_reg(devc, MBA, 0, 0x0);
+	emu10k_write_reg(devc, FXBS, 0, 0x0);
+	emu10k_write_reg(devc, FXBA, 0, 0x0);
+	emu10k_write_reg(devc, ADCBS, 0, 0x0);
+	emu10k_write_reg(devc, ADCBA, 0, 0x0);
+
+	OUTL(devc, 0, devc->regs + IE);
+	emu10k_write_reg(devc, CLIEL, 0, 0x0);
+	emu10k_write_reg(devc, CLIEH, 0, 0x0);
+	if (!(devc->feature_mask & SB_LIVE)) {
+		emu10k_write_reg(devc, HLIEL, 0, 0x0);
+		emu10k_write_reg(devc, HLIEH, 0, 0x0);
+	}
+	emu10k_write_reg(devc, CLIPL, 0, 0xffffffff);
+	emu10k_write_reg(devc, CLIPH, 0, 0xffffffff);
+	emu10k_write_reg(devc, SOLL, 0, 0xffffffff);
+	emu10k_write_reg(devc, SOLH, 0, 0xffffffff);
+
+
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
+		emu10k_write_reg(devc, SOC, 0, 0xf00);	/* ?? */
+		emu10k_write_reg(devc, AC97SLOT, 0, 0x3);	/* ?? */
+	}
+
+	for (i = 0; i < 64; i++)
+		emu10k_init_voice(devc, i);
+
+	emu10k_write_reg(devc, SCS0, 0, 0x2109204);
+	emu10k_write_reg(devc, SCS1, 0, 0x2109204);
+	emu10k_write_reg(devc, SCS2, 0, 0x2109204);
+
+	emu10k_write_reg(devc, PTBA, 0, devc->pt_paddr);
+	tmp = emu10k_read_reg(devc, PTBA, 0);
+
+	emu10k_write_reg(devc, TCBA, 0, 0x0);
+	emu10k_write_reg(devc, TCBS, 0, 0x4);
+
+	reg = 0;
+	if (devc->feature_mask & SB_71) {
+		reg = AC97SLOT_CENTER | AC97SLOT_LFE | AC97SLOT_REAR_LEFT |
+		    AC97SLOT_REAR_RIGHT;
+	} else if (devc->feature_mask & SB_51) {
+		reg = AC97SLOT_CENTER | AC97SLOT_LFE;
+	}
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL))
+		reg |= 0x40;
+	emu10k_write_reg(devc, AC97SLOT, 0, reg);
+
+	if (devc->feature_mask & SB_AUDIGY2) {
+		/* Enable analog outputs on Audigy2 */
+		int tmp;
+
+		/* Setup SRCMulti_I2S SamplingRate */
+		tmp = emu10k_read_reg(devc, EHC, 0);
+		tmp &= 0xfffff1ff;
+		tmp |= (0x2 << 9);
+		emu10k_write_reg(devc, EHC, 0, tmp);
+		/* emu10k_write_reg (devc, SOC, 0, 0x00000000); */
+
+		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+		OUTL(devc, 0x600000, devc->regs + 0x20);
+		OUTL(devc, 0x14, devc->regs + 0x24);
+
+		/* Setup SRCMulti Input Audio Enable */
+		OUTL(devc, 0x6E0000, devc->regs + 0x20);
+
+		OUTL(devc, 0xFF00FF00, devc->regs + 0x24);
+
+		/* Setup I2S ASRC Enable  (HC register) */
+		tmp = INL(devc, devc->regs + HCFG);
+		tmp |= 0x00000070;
+		OUTL(devc, tmp, devc->regs + HCFG);
+
+		/*
+		 * Unmute Analog now.  Set GPO6 to 1 for Apollo.
+		 * This has to be done after init ALice3 I2SOut beyond 48KHz.
+		 * So, sequence is important
+		 */
+		tmp = INL(devc, devc->regs + 0x18);
+		tmp |= 0x0040;
+
+		OUTL(devc, tmp, devc->regs + 0x18);
+	}
+
+	if (devc->feature_mask & SB_AUDIGY2VAL) {
+		/* Enable analog outputs on Audigy2 */
+		int tmp;
+
+		/* Setup SRCMulti_I2S SamplingRate */
+		tmp = emu10k_read_reg(devc, EHC, 0);
+		tmp &= 0xfffff1ff;
+		tmp |= (0x2 << 9);
+		emu10k_write_reg(devc, EHC, 0, tmp);
+
+		/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
+		OUTL(devc, 0x600000, devc->regs + 0x20);
+		OUTL(devc, 0x14, devc->regs + 0x24);
+
+		/* Setup SRCMulti Input Audio Enable */
+		OUTL(devc, 0x7B0000, devc->regs + 0x20);
+		OUTL(devc, 0xFF000000, devc->regs + 0x24);
+
+		/* SPDIF output enable */
+		OUTL(devc, 0x7A0000, devc->regs + 0x20);
+		OUTL(devc, 0xFF000000, devc->regs + 0x24);
+
+		tmp = INL(devc, devc->regs + 0x18) & ~0x8;
+		OUTL(devc, tmp, devc->regs + 0x18);
+	}
+
+	emu10k_write_reg(devc, SOLL, 0, 0xffffffff);
+	emu10k_write_reg(devc, SOLH, 0, 0xffffffff);
+
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
+		unsigned int mode = 0;
+
+		if (devc->feature_mask & (SB_AUDIGY2|SB_AUDIGY2VAL))
+			mode |= HCFG_AC3ENABLE_GPSPDIF | HCFG_AC3ENABLE_CDSPDIF;
+		OUTL(devc,
+		    HCFG_AUDIOENABLE | HCFG_AUTOMUTE |
+		    HCFG_JOYENABLE | A_HCFG_VMUTE |
+		    A_HCFG_AUTOMUTE | mode, devc->regs + HCFG);
+
+		OUTL(devc, INL(devc, devc->regs + 0x18) |
+		    0x0004, devc->regs + 0x18);	/* GPIO (S/PDIF enable) */
+
+
+		/* enable IR port */
+		tmp = INL(devc, devc->regs + 0x18);
+		OUTL(devc, tmp | A_IOCFG_GPOUT2, devc->regs + 0x18);
+		drv_usecwait(500);
+		OUTL(devc, tmp | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2,
+		    devc->regs + 0x18);
+		drv_usecwait(100);
+		OUTL(devc, tmp, devc->regs + 0x18);
+	} else {
+		OUTL(devc,
+		    HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK |
+		    HCFG_AUTOMUTE | HCFG_JOYENABLE, devc->regs + HCFG);
+	}
+
+
+	/* enable IR port */
+	tmp = INL(devc, devc->regs + HCFG);
+	OUTL(devc, tmp | HCFG_GPOUT2, devc->regs + HCFG);
+	drv_usecwait(500);
+	OUTL(devc, tmp | HCFG_GPOUT1 | HCFG_GPOUT2, devc->regs + HCFG);
+	drv_usecwait(100);
+	OUTL(devc, tmp, devc->regs + HCFG);
+
+
+	/*
+	 * Start by configuring for analog mode.
+	 */
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
+		reg = INL(devc, devc->regs + 0x18) & ~A_IOCFG_GPOUT0;
+		reg |= ((devc->feature_mask & SB_INVSP) ? 0x4 : 0);
+		OUTL(devc, reg, devc->regs + 0x18);
+	}
+	if (devc->feature_mask & SB_LIVE) {	/* SBLIVE */
+		reg = INL(devc, devc->regs + HCFG) & ~HCFG_GPOUT0;
+		reg |= ((devc->feature_mask & SB_INVSP) ? HCFG_GPOUT0 : 0);
+		OUTL(devc, reg, devc->regs + HCFG);
+	}
+
+	if (devc->feature_mask & SB_AUDIGY2VAL) {
+		OUTL(devc, INL(devc, devc->regs + 0x18) | 0x0060,
+		    devc->regs + 0x18);
+	} else if (devc->feature_mask & SB_AUDIGY2) {
+		OUTL(devc, INL(devc, devc->regs + 0x18) | 0x0040,
+		    devc->regs + 0x18);
+	} else if (devc->feature_mask & SB_AUDIGY) {
+		OUTL(devc, INL(devc, devc->regs + 0x18) | 0x0080,
+		    devc->regs + 0x18);
+	}
+
+	emu10k_init_effects(devc);
+
+	return (DDI_SUCCESS);
+}
+
+static const int db2lin_101[101] = {
+	0x00000000,
+	0x0024B53A, 0x002750CA, 0x002A1BC6, 0x002D198D, 0x00304DBA, 0x0033BC2A,
+	0x00376901, 0x003B58AF, 0x003F8FF1, 0x004413DF, 0x0048E9EA, 0x004E17E9,
+	0x0053A419, 0x0059952C, 0x005FF24E, 0x0066C32A, 0x006E0FFB, 0x0075E18D,
+	0x007E414F, 0x0087395B, 0x0090D482, 0x009B1E5B, 0x00A6234F, 0x00B1F0A7,
+	0x00BE94A1, 0x00CC1E7C, 0x00DA9E8D, 0x00EA2650, 0x00FAC881, 0x010C9931,
+	0x011FADDC, 0x01341D87, 0x014A00D8, 0x01617235, 0x017A8DE6, 0x01957233,
+	0x01B23F8D, 0x01D118B1, 0x01F222D4, 0x021585D1, 0x023B6C57, 0x0264041D,
+	0x028F7E19, 0x02BE0EBD, 0x02EFEE33, 0x032558A2, 0x035E8E7A, 0x039BD4BC,
+	0x03DD7551, 0x0423BF61, 0x046F07B5, 0x04BFA91B, 0x051604D5, 0x0572830D,
+	0x05D59354, 0x063FAD27, 0x06B15080, 0x072B0673, 0x07AD61CD, 0x0838FFCA,
+	0x08CE88D3, 0x096EB147, 0x0A1A3A53, 0x0AD1F2E0, 0x0B96B889, 0x0C6978A5,
+	0x0D4B316A, 0x0E3CF31B, 0x0F3FE155, 0x10553469, 0x117E3AD9, 0x12BC5AEA,
+	0x14111457, 0x157E0219, 0x1704DC5E, 0x18A77A97, 0x1A67D5B6, 0x1C480A87,
+	0x1E4A5C45, 0x2071374D, 0x22BF3412, 0x25371A37, 0x27DBE3EF, 0x2AB0C18F,
+	0x2DB91D6F, 0x30F89FFD, 0x34733433, 0x382D0C46, 0x3C2AA6BD, 0x4070D3D9,
+	0x4504BB66, 0x49EBE2F1, 0x4F2C346F, 0x54CC0565, 0x5AD21E86, 0x6145C3E7,
+	0x682EBDBD, 0x6F9561C4, 0x77829D4D,
+	0x7fffffff
+};
+
+static int
+emu10k_convert_fixpoint(int val)
+{
+	if (val < 0)
+		val = 0;
+	if (val > 100)
+		val = 100;
+	return (db2lin_101[val]);
+}
+
+static void
+emu10k_write_gpr(emu10k_devc_t *devc, int gpr, uint32_t value)
+{
+	ASSERT(gpr < MAX_GPR);
+	devc->gpr_shadow[gpr].valid = B_TRUE;
+	devc->gpr_shadow[gpr].value = value;
+	if (!devc->suspended) {
+		emu10k_write_reg(devc, gpr + GPR0, 0, value);
+	}
+}
+
+static int
+emu10k_set_stereo(void *arg, uint64_t val)
+{
+	emu10k_ctrl_t *ec = arg;
+	emu10k_devc_t *devc = ec->devc;
+	uint32_t left, right;
+
+	left = (val >> 8) & 0xff;
+	right = val & 0xff;
+	if ((left > 100) || (right > 100) || (val & ~(0xffff)))
+		return (EINVAL);
+
+	left = emu10k_convert_fixpoint(left);
+	right = emu10k_convert_fixpoint(right);
+
+	mutex_enter(&devc->mutex);
+	ec->val = val;
+
+	emu10k_write_gpr(devc, ec->gpr_num, left);
+	emu10k_write_gpr(devc, ec->gpr_num + 1, right);
+
+	mutex_exit(&devc->mutex);
+	return (0);
+}
+
+static int
+emu10k_set_mono(void *arg, uint64_t val)
+{
+	emu10k_ctrl_t *ec = arg;
+	emu10k_devc_t *devc = ec->devc;
+	uint32_t v;
+
+	if (val > 100)
+		return (EINVAL);
+
+	v = emu10k_convert_fixpoint(val & 0xff);
+
+	mutex_enter(&devc->mutex);
+	ec->val = val;
+	emu10k_write_gpr(devc, ec->gpr_num, v);
+	mutex_exit(&devc->mutex);
+	return (0);
+}
+
+static int
+emu10k_get_control(void *arg, uint64_t *val)
+{
+	emu10k_ctrl_t *ec = arg;
+	emu10k_devc_t *devc = ec->devc;
+
+	mutex_enter(&devc->mutex);
+	*val = ec->val;
+	mutex_exit(&devc->mutex);
+	return (0);
+}
+
+#define	PLAYCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
+#define	RECCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
+#define	MONCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
+#define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
+#define	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
+#define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
+#define	MONVOL	(MONCTL | AUDIO_CTRL_FLAG_MONVOL)
+
+static int
+emu10k_get_ac97src(void *arg, uint64_t *valp)
+{
+	ac97_ctrl_t *ctrl = arg;
+
+	return (ac97_control_get(ctrl, valp));
+}
+
+static int
+emu10k_set_ac97src(void *arg, uint64_t value)
+{
+	ac97_ctrl_t	*ctrl = arg;
+
+	return (ac97_control_set(ctrl, value));
+}
+
+static int
+emu10k_set_jack3(void *arg, uint64_t value)
+{
+	emu10k_ctrl_t	*ec = arg;
+	emu10k_devc_t	*devc = ec->devc;
+	uint32_t	set_val;
+	uint32_t	val;
+
+	set_val = ddi_ffs(value & 0xffffffffU);
+	set_val--;
+	mutex_enter(&devc->mutex);
+	switch (set_val) {
+	case 0:
+	case 1:
+		break;
+	default:
+		mutex_exit(&devc->mutex);
+		return (EINVAL);
+	}
+	ec->val = value;
+	/* center/lfe */
+	if (devc->feature_mask & SB_INVSP) {
+		set_val = !set_val;
+	}
+	if (!devc->suspended) {
+		if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
+			val = INL(devc, devc->regs + 0x18);
+			val &= ~A_IOCFG_GPOUT0;
+			val |= set_val ? 0x44 : 0x40;
+			OUTL(devc, val, devc->regs + 0x18);
+
+		} else if (devc->feature_mask & SB_LIVE) {
+			val = INL(devc, devc->regs + HCFG);
+			val &= ~HCFG_GPOUT0;
+			val |= set_val ? HCFG_GPOUT0 : 0;
+			OUTL(devc, val, devc->regs + HCFG);
+		}
+	}
+	mutex_exit(&devc->mutex);
+	return (0);
+}
+
+static int
+emu10k_set_recsrc(void *arg, uint64_t value)
+{
+	emu10k_ctrl_t	*ec = arg;
+	emu10k_devc_t	*devc = ec->devc;
+	uint32_t	set_val;
+
+	set_val = ddi_ffs(value & 0xffffffffU);
+	set_val--;
+
+	/*
+	 * We start assuming well set up AC'97 for stereomix recording.
+	 */
+	switch (set_val) {
+	case INPUT_AC97:
+	case INPUT_SPD1:
+	case INPUT_SPD2:
+	case INPUT_DIGCD:
+	case INPUT_AUX2:
+	case INPUT_LINE2:
+	case INPUT_STEREOMIX:
+		break;
+	default:
+		return (EINVAL);
+	}
+
+	mutex_enter(&devc->mutex);
+	ec->val = value;
+
+	emu10k_write_gpr(devc, GPR_REC_AC97, (set_val == INPUT_AC97));
+	emu10k_write_gpr(devc, GPR_REC_SPDIF1, (set_val == INPUT_SPD1));
+	emu10k_write_gpr(devc, GPR_REC_SPDIF2, (set_val == INPUT_SPD2));
+	emu10k_write_gpr(devc, GPR_REC_DIGCD, (set_val == INPUT_DIGCD));
+	emu10k_write_gpr(devc, GPR_REC_AUX2, (set_val == INPUT_AUX2));
+	emu10k_write_gpr(devc, GPR_REC_LINE2, (set_val == INPUT_LINE2));
+	emu10k_write_gpr(devc, GPR_REC_PCM, (set_val == INPUT_STEREOMIX));
+
+	mutex_exit(&devc->mutex);
+
+	return (0);
+}
+
+static void
+emu10k_create_stereo(emu10k_devc_t *devc, int ctl, int gpr,
+    const char *id, int flags, int defval)
+{
+	emu10k_ctrl_t *ec;
+	audio_ctrl_desc_t desc;
+
+	bzero(&desc, sizeof (desc));
+
+	ec = &devc->ctrls[ctl];
+	ec->devc = devc;
+	ec->gpr_num = gpr;
+
+	desc.acd_name = id;
+	desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
+	desc.acd_minvalue = 0;
+	desc.acd_maxvalue = 100;
+	desc.acd_flags = flags;
+
+	ec->val = (defval << 8) | defval;
+	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
+	    emu10k_get_control, emu10k_set_stereo, ec);
+
+	mutex_enter(&devc->mutex);
+	emu10k_write_gpr(devc, gpr, emu10k_convert_fixpoint(defval));
+	emu10k_write_gpr(devc, gpr + 1, emu10k_convert_fixpoint(defval));
+	mutex_exit(&devc->mutex);
+}
+
+static void
+emu10k_create_mono(emu10k_devc_t *devc, int ctl, int gpr,
+    const char *id, int flags, int defval)
+{
+	emu10k_ctrl_t *ec;
+	audio_ctrl_desc_t desc;
+
+	bzero(&desc, sizeof (desc));
+
+	ec = &devc->ctrls[ctl];
+	ec->devc = devc;
+	ec->gpr_num = gpr;
+
+	desc.acd_name = id;
+	desc.acd_type = AUDIO_CTRL_TYPE_MONO;
+	desc.acd_minvalue = 0;
+	desc.acd_maxvalue = 100;
+	desc.acd_flags = flags;
+
+	ec->val = defval;
+	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
+	    emu10k_get_control, emu10k_set_mono, ec);
+
+	mutex_enter(&devc->mutex);
+	emu10k_write_gpr(devc, gpr, emu10k_convert_fixpoint(defval));
+	mutex_exit(&devc->mutex);
+}
+
+/*
+ * AC'97 source.  The AC'97 PCM record channel is routed to our
+ * mixer.  While we could support the direct monitoring capability of
+ * the AC'97 part itself, this would not work correctly with outputs
+ * that are not routed via AC'97 (such as the Live Drive headphones
+ * or digital outputs.)  So we just offer the ability to select one
+ * AC'97 source, and then offer independent ability to either monitor
+ * or record from the AC'97 mixer's PCM record channel.
+ */
+static void
+emu10k_create_ac97src(emu10k_devc_t *devc)
+{
+	emu10k_ctrl_t *ec;
+	audio_ctrl_desc_t desc;
+	ac97_ctrl_t *ac;
+	const audio_ctrl_desc_t *acd;
+
+	bzero(&desc, sizeof (desc));
+
+	ec = &devc->ctrls[CTL_AC97SRC];
+	desc.acd_name = "ac97-source";
+	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
+	desc.acd_flags = RECCTL;
+	ec->devc = devc;
+	ac = ac97_control_find(devc->ac97, AUDIO_CTRL_ID_RECSRC);
+	if (ac == NULL) {
+		return;
+	}
+
+	acd = ac97_control_desc(ac);
+
+	for (int i = 0; i < 64; i++) {
+		const char *n;
+		if (((acd->acd_minvalue & (1ULL << i)) == 0) ||
+		    ((n = acd->acd_enum[i]) == NULL)) {
+			continue;
+		}
+		desc.acd_enum[i] = acd->acd_enum[i];
+		/* we suppress some port options */
+		if ((strcmp(n, AUDIO_PORT_STEREOMIX) == 0) ||
+		    (strcmp(n, AUDIO_PORT_MONOMIX) == 0) ||
+		    (strcmp(n, AUDIO_PORT_VIDEO) == 0)) {
+			continue;
+		}
+		desc.acd_minvalue |= (1ULL << i);
+		desc.acd_maxvalue |= (1ULL << i);
+	}
+
+	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
+	    emu10k_get_ac97src, emu10k_set_ac97src, ac);
+}
+
+/*
+ * Record source... this one is tricky.  While the chip will
+ * conceivably let us *mix* some of the audio streams for recording,
+ * the AC'97 inputs don't have this capability.  Offering it to users
+ * is likely to be confusing, so we offer a single record source
+ * selection option.  Its not ideal, but it ought to be good enough
+ * for the vast majority of users.
+ */
+static void
+emu10k_create_recsrc(emu10k_devc_t *devc)
+{
+	emu10k_ctrl_t *ec;
+	audio_ctrl_desc_t desc;
+	ac97_ctrl_t *ac;
+
+	bzero(&desc, sizeof (desc));
+
+	ec = &devc->ctrls[CTL_RECSRC];
+	desc.acd_name = AUDIO_CTRL_ID_RECSRC;
+	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
+	desc.acd_flags = RECCTL;
+	desc.acd_minvalue = 0;
+	desc.acd_maxvalue = 0;
+	bzero(desc.acd_enum, sizeof (desc.acd_enum));
+	ec->devc = devc;
+	ac = ac97_control_find(devc->ac97, AUDIO_CTRL_ID_RECSRC);
+
+	/* only low order bits set by AC'97 */
+	ASSERT(desc.acd_minvalue == desc.acd_maxvalue);
+	ASSERT((desc.acd_minvalue & ~0xffff) == 0);
+
+	/*
+	 * It would be really cool if we could detect whether these
+	 * options are all sensible on a given configuration.  Units
+	 * without live-drive support, and units without a physical
+	 * live-drive, simply can't do all these.
+	 */
+	if (ac != NULL) {
+		desc.acd_minvalue |= (1 << INPUT_AC97);
+		desc.acd_maxvalue |= (1 << INPUT_AC97);
+		desc.acd_enum[INPUT_AC97] = "ac97";
+		ec->val = (1 << INPUT_AC97);
+	} else {
+		/* next best guess */
+		ec->val = (1 << INPUT_LINE2);
+	}
+
+	desc.acd_minvalue |= (1 << INPUT_SPD1);
+	desc.acd_maxvalue |= (1 << INPUT_SPD1);
+	desc.acd_enum[INPUT_SPD1] = AUDIO_PORT_SPDIFIN;
+
+	desc.acd_minvalue |= (1 << INPUT_SPD2);
+	desc.acd_maxvalue |= (1 << INPUT_SPD2);
+	desc.acd_enum[INPUT_SPD2] = "spdif2-in";
+
+	desc.acd_minvalue |= (1 << INPUT_DIGCD);
+	desc.acd_maxvalue |= (1 << INPUT_DIGCD);
+	desc.acd_enum[INPUT_DIGCD] = "digital-cd";
+
+	desc.acd_minvalue |= (1 << INPUT_AUX2);
+	desc.acd_maxvalue |= (1 << INPUT_AUX2);
+	desc.acd_enum[INPUT_AUX2] = AUDIO_PORT_AUX2IN;
+
+	desc.acd_minvalue |= (1 << INPUT_LINE2);
+	desc.acd_maxvalue |= (1 << INPUT_LINE2);
+	desc.acd_enum[INPUT_LINE2] = "line2-in";
+
+	desc.acd_minvalue |= (1 << INPUT_STEREOMIX);
+	desc.acd_maxvalue |= (1 << INPUT_STEREOMIX);
+	desc.acd_enum[INPUT_STEREOMIX] = AUDIO_PORT_STEREOMIX;
+
+	emu10k_write_gpr(devc, GPR_REC_SPDIF1, 0);
+	emu10k_write_gpr(devc, GPR_REC_SPDIF2, 0);
+	emu10k_write_gpr(devc, GPR_REC_DIGCD, 0);
+	emu10k_write_gpr(devc, GPR_REC_AUX2, 0);
+	emu10k_write_gpr(devc, GPR_REC_LINE2, 0);
+	emu10k_write_gpr(devc, GPR_REC_PCM, 0);
+	emu10k_write_gpr(devc, GPR_REC_AC97, 1);
+
+	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
+	    emu10k_get_control, emu10k_set_recsrc, ec);
+}
+
+static void
+emu10k_create_jack3(emu10k_devc_t *devc)
+{
+	emu10k_ctrl_t *ec;
+	audio_ctrl_desc_t desc;
+
+	bzero(&desc, sizeof (desc));
+
+	ec = &devc->ctrls[CTL_JACK3];
+	desc.acd_name = AUDIO_CTRL_ID_JACK3;
+	desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
+	desc.acd_flags = AUDIO_CTRL_FLAG_RW;
+	desc.acd_minvalue = 0x3;
+	desc.acd_maxvalue = 0x3;
+	bzero(desc.acd_enum, sizeof (desc.acd_enum));
+	ec->devc = devc;
+	ec->val = 0x1;
+
+	desc.acd_enum[0] = AUDIO_PORT_CENLFE;
+	desc.acd_enum[1] = AUDIO_PORT_SPDIFOUT;
+
+	ec->ctrl = audio_dev_add_control(devc->adev, &desc,
+	    emu10k_get_control, emu10k_set_jack3, ec);
+}
+
+
+static void
+emu10k_create_controls(emu10k_devc_t *devc)
+{
+	ac97_t		*ac97;
+	ac97_ctrl_t	*ac;
+
+	emu10k_create_mono(devc, CTL_VOLUME, GPR_VOL_PCM,
+	    AUDIO_CTRL_ID_VOLUME, PCMVOL, 75);
+
+	emu10k_create_stereo(devc, CTL_FRONT, GPR_VOL_FRONT,
+	    AUDIO_CTRL_ID_FRONT, MAINVOL, 100);
+	emu10k_create_stereo(devc, CTL_SURROUND, GPR_VOL_SURR,
+	    AUDIO_CTRL_ID_SURROUND, MAINVOL, 100);
+	if (devc->feature_mask & (SB_51 | SB_71)) {
+		emu10k_create_mono(devc, CTL_CENTER, GPR_VOL_CEN,
+		    AUDIO_CTRL_ID_CENTER, MAINVOL, 100);
+		emu10k_create_mono(devc, CTL_LFE, GPR_VOL_LFE,
+		    AUDIO_CTRL_ID_LFE, MAINVOL, 100);
+	}
+	if (devc->feature_mask & SB_71) {
+		emu10k_create_stereo(devc, CTL_SIDE, GPR_VOL_SIDE,
+		    "side", MAINVOL, 100);
+	}
+
+	emu10k_create_stereo(devc, CTL_RECGAIN, GPR_VOL_REC,
+	    AUDIO_CTRL_ID_RECGAIN, RECVOL, 50);
+
+	emu10k_create_ac97src(devc);
+	emu10k_create_recsrc(devc);
+	/*
+	 * 5.1 devices have versa jack.  Note that from what we can
+	 * tell, none of the 7.1 devices have or need this versa jack,
+	 * as they all seem to have a dedicated digital I/O port.
+	 */
+	if ((devc->feature_mask & SB_51) &&
+	    !(devc->feature_mask & SB_AUDIGY2VAL)) {
+		emu10k_create_jack3(devc);
+	}
+
+	/* these ones AC'97 can manage directly */
+	ac97 = devc->ac97;
+
+	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_MICBOOST)) != NULL)
+		ac97_control_register(ac);
+	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_MICGAIN)) != NULL)
+		ac97_control_register(ac);
+
+	/* set any AC'97 analog outputs to full volume (no attenuation) */
+	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_FRONT)) != NULL)
+		ac97_control_set(ac, (100 << 8) | 100);
+	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_LINEOUT)) != NULL)
+		ac97_control_set(ac, (100 << 8) | 100);
+	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_SURROUND)) != NULL)
+		ac97_control_set(ac, (100 << 8) | 100);
+	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_CENTER)) != NULL)
+		ac97_control_set(ac, 100);
+	if ((ac = ac97_control_find(ac97, AUDIO_CTRL_ID_LFE)) != NULL)
+		ac97_control_set(ac, 100);
+
+	/* Monitor sources */
+	emu10k_create_stereo(devc, CTL_AC97, GPR_MON_AC97,
+	    "ac97-monitor", MONVOL, 0);
+	emu10k_create_stereo(devc, CTL_SPD1, GPR_MON_SPDIF1,
+	    AUDIO_PORT_SPDIFIN, MONVOL, 0);
+	emu10k_create_stereo(devc, CTL_DIGCD, GPR_MON_DIGCD,
+	    "digital-cd", MONVOL, 0);
+	emu10k_create_stereo(devc, CTL_SPD1, GPR_MON_SPDIF1,
+	    AUDIO_PORT_SPDIFIN, MONVOL, 0);
+
+	if ((devc->feature_mask & SB_NOEXP) == 0) {
+		/*
+		 * These ports are only available via an external
+		 * expansion box.  Don't expose them for cards  that
+		 * don't have support for it.
+		 */
+		emu10k_create_stereo(devc, CTL_HEADPH, GPR_VOL_HEADPH,
+		    AUDIO_CTRL_ID_HEADPHONE, MAINVOL, 100);
+		emu10k_create_stereo(devc, CTL_SPD2, GPR_MON_SPDIF2,
+		    "spdif2-in", MONVOL, 0);
+		emu10k_create_stereo(devc, CTL_LINE2, GPR_MON_LINE2,
+		    "line2-in", MONVOL, 0);
+		emu10k_create_stereo(devc, CTL_AUX2, GPR_MON_AUX2,
+		    AUDIO_PORT_AUX2IN, MONVOL, 0);
+	}
+}
+
+static void
+emu10k_load_dsp(emu10k_devc_t *devc, uint32_t *code, int ncode,
+    uint32_t *init, int ninit)
+{
+	int i;
+
+	if (ncode > 1024) {
+		audio_dev_warn(devc->adev, "DSP file size too big");
+		return;
+	}
+	if (ninit > MAX_GPR) {
+		audio_dev_warn(devc->adev, "Too many inits");
+		return;
+	}
+
+	/* Upload our DSP code */
+	for (i = 0; i < ncode; i++) {
+		emu10k_write_efx(devc, UC0 + i, code[i]);
+	}
+
+	/* Upload the initialization settings */
+	for (i = 0; i < ninit; i += 2) {
+		emu10k_write_reg(devc, init[i] + GPR0, 0, init[i + 1]);
+	}
+}
+
+#define	LIVE_NOP()					\
+	emu10k_write_efx(devc, UC0 + (pc * 2), 0x10040);	\
+	emu10k_write_efx(devc, UC0 + (pc * 2 + 1), 0x610040);	\
+	pc++
+#define	LIVE_ACC3(r, a, x, y) /* z=w+x+y */				\
+	emu10k_write_efx(devc, UC0 + (pc * 2), (x << 10) | y);		\
+	emu10k_write_efx(devc, UC0 + (pc * 2 + 1), (6 << 20) | (r << 10) | a); \
+	pc++
+
+#define	AUDIGY_ACC3(r, a, x, y) /* z=w+x+y */				\
+	emu10k_write_efx(devc, UC0 + (pc * 2), (x << 12) | y);		\
+	emu10k_write_efx(devc, UC0 + (pc * 2+1), (6 << 24) | (r << 12) | a); \
+	pc++
+#define	AUDIGY_NOP() AUDIGY_ACC3(0xc0, 0xc0, 0xc0, 0xc0)
+
+static void
+emu10k_init_effects(emu10k_devc_t *devc)
+{
+	int i;
+	unsigned short pc;
+
+	ASSERT(mutex_owned(&devc->mutex));
+
+	if (devc->feature_mask & (SB_AUDIGY|SB_AUDIGY2|SB_AUDIGY2VAL)) {
+		pc = 0;
+		for (i = 0; i < 512; i++) {
+			AUDIGY_NOP();
+		}
+
+		for (i = 0; i < 256; i++)
+			emu10k_write_efx(devc, GPR0 + i, 0);
+		emu10k_write_reg(devc, AUDIGY_DBG, 0, 0);
+		emu10k_load_dsp(devc,
+		    emu10k2_code,
+		    sizeof (emu10k2_code) / sizeof (emu10k2_code[0]),
+		    emu10k2_init,
+		    sizeof (emu10k2_init) / sizeof (emu10k2_init[0]));
+
+	} else {
+		pc = 0;
+		for (i = 0; i < 512; i++) {
+			LIVE_NOP();
+		}
+
+		for (i = 0; i < 256; i++)
+			emu10k_write_efx(devc, GPR0 + i, 0);
+		emu10k_write_reg(devc, DBG, 0, 0);
+		emu10k_load_dsp(devc,
+		    emu10k1_code,
+		    sizeof (emu10k1_code) / sizeof (emu10k1_code[0]),
+		    emu10k1_init,
+		    sizeof (emu10k1_init) / sizeof (emu10k1_init[0]));
+	}
+}
+
+/* mixer */
+
+static struct {
+	uint16_t	devid;
+	uint16_t	subid;
+	const char	*model;
+	const char	*prod;
+	unsigned	feature_mask;
+} emu10k_cards[] = {
+	{ 0x2, 0x0020, "CT4670", "Live! Value", SB_LIVE | SB_NOEXP },
+	{ 0x2, 0x0021, "CT4621", "Live!", SB_LIVE },
+	{ 0x2, 0x100a, "SB0220", "Live! 5.1 Digital",
+	    SB_LIVE | SB_51 | SB_NOEXP },
+	{ 0x2, 0x8022, "CT4780", "Live! Value", SB_LIVE },
+	{ 0x2, 0x8023, "CT4790", "PCI512", SB_LIVE | SB_NOEXP },
+	{ 0x2, 0x8026, "CT4830", "Live! Value", SB_LIVE },
+	{ 0x2, 0x8028, "CT4870", "Live! Value", SB_LIVE },
+	{ 0x2, 0x8031, "CT4831", "Live! Value", SB_LIVE },
+	{ 0x2, 0x8040, "CT4760", "Live!", SB_LIVE },
+	{ 0x2, 0x8051, "CT4850", "Live! Value", SB_LIVE },
+	{ 0x2, 0x8061, "SB0060", "Live! 5.1", SB_LIVE | SB_51 },
+	{ 0x2, 0x8064, "SB0100", "Live! 5.1", SB_LIVE | SB_51 },
+	{ 0x2, 0x8065, "SB0220", "Live! 5.1", SB_LIVE | SB_51 },
+	{ 0x2, 0x8066, "SB0228", "Live! 5.1", SB_LIVE | SB_51 },
+	{ 0x4, 0x0051, "SB0090", "Audigy", SB_AUDIGY | SB_51 },
+	{ 0x4, 0x0052, "SB0160", "Audigy ES", SB_AUDIGY | SB_51 },
+	{ 0x4, 0x0053, "SB0092", "Audigy", SB_AUDIGY | SB_51 },
+	{ 0x4, 0x1002, "SB0240P", "Audigy 2 Platinum",
+	    SB_AUDIGY2 | SB_71 | SB_INVSP },
+	{ 0x4, 0x1003, "SB0353", "Audigy 2 ZS", SB_AUDIGY2 | SB_71 | SB_INVSP },
+	{ 0x4, 0x1005, "SB0280", "Audigy 2 Platinum EX", SB_AUDIGY2 | SB_71 },
+	{ 0x4, 0x1007, "SB0240", "Audigy 2", SB_AUDIGY2 | SB_71 },
+	{ 0x4, 0x2001, "SB0360", "Audigy 2 ZS", SB_AUDIGY2 | SB_71 | SB_INVSP },
+	{ 0x4, 0x2002, "SB0350", "Audigy 2 ZS", SB_AUDIGY2 | SB_71 | SB_INVSP },
+	{ 0x4, 0x2006, "SB0350", "Audigy 2", SB_AUDIGY2 | SB_71 | SB_INVSP },
+	{ 0x4, 0x2007, "SB0380", "Audigy 4 Pro", SB_AUDIGY2 | SB_71 },
+	{ 0x8, 0x1001, "SB0400", "Audigy 2 Value",
+	    SB_AUDIGY2VAL | SB_71 | SB_NOEXP },
+	{ 0x8, 0x1021, "SB0610", "Audigy 4",
+	    SB_AUDIGY2VAL | SB_71 | SB_NOEXP },
+	{ 0x8, 0x2001, "SB0530", "Audigy 2 ZS Notebook",
+	    SB_AUDIGY2VAL | SB_71 },
+	{ 0, 0, NULL, NULL, 0 },
+};
+
+int
+emu10k_attach(dev_info_t *dip)
+{
+	uint16_t pci_command;
+	uint16_t subid;
+	uint16_t devid;
+	emu10k_devc_t *devc;
+	ddi_acc_handle_t pcih;
+	ddi_dma_cookie_t cookie;
+	uint_t count;
+	ulong_t len;
+	int i;
+	const char *name;
+	const char *model;
+	char namebuf[64];
+	int feature_mask;
+
+	devc = kmem_zalloc(sizeof (*devc), KM_SLEEP);
+	devc->dip = dip;
+	ddi_set_driver_private(dip, devc);
+
+	if ((devc->adev = audio_dev_alloc(dip, 0)) == NULL) {
+		cmn_err(CE_WARN, "audio_dev_alloc failed");
+		goto error;
+	}
+
+	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
+		audio_dev_warn(devc->adev, "pci_config_setup failed");
+		goto error;
+	}
+	devc->pcih = pcih;
+
+	devid = pci_config_get16(pcih, PCI_CONF_DEVID);
+	subid = pci_config_get16(pcih, PCI_CONF_SUBSYSID);
+
+	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
+	pci_command |= PCI_COMM_ME | PCI_COMM_IO;
+	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
+
+	if ((ddi_regs_map_setup(dip, 1, &devc->regs, 0, 0, &dev_attr,
+	    &devc->regsh)) != DDI_SUCCESS) {
+		audio_dev_warn(devc->adev, "failed to map registers");
+		goto error;
+	}
+
+	switch (devid) {
+	case PCI_DEVICE_ID_SBLIVE:
+		name = "Live!";
+		model = "CT????";
+		feature_mask = SB_LIVE;
+		break;
+
+	case PCI_DEVICE_ID_AUDIGYVALUE:
+		name = "Audigy 2 Value";
+		model = "SB????";
+		feature_mask = SB_AUDIGY2VAL;
+		break;
+
+	case PCI_DEVICE_ID_AUDIGY:
+		if (subid >= 0x1002 && subid <= 0x2005) {
+			name = "Audigy 2";
+			model = "SB????";
+			feature_mask = SB_AUDIGY2;
+		} else {
+			name = "Audigy";
+			model = "SB????";
+			feature_mask = SB_AUDIGY;
+		}
+		break;
+
+	default:
+		audio_dev_warn(devc->adev, "Unrecognized device");
+		goto error;
+	}
+
+	for (i = 0; emu10k_cards[i].prod; i++) {
+		if ((devid == emu10k_cards[i].devid) &&
+		    (subid == emu10k_cards[i].subid)) {
+			name = emu10k_cards[i].prod;
+			model = emu10k_cards[i].model;
+			feature_mask = emu10k_cards[i].feature_mask;
+			break;
+		}
+	}
+	devc->feature_mask = feature_mask;
+
+	(void) snprintf(namebuf, sizeof (namebuf), "Sound Blaster %s", name);
+
+	audio_dev_set_description(devc->adev, namebuf);
+	audio_dev_set_version(devc->adev, model);
+
+	if (emu10k_setup_intrs(devc) != DDI_SUCCESS)
+		goto error;
+
+	/* allocate static page table memory */
+
+	devc->max_mem = AUDIO_MEMSIZE;
+
+	/* SB Live/Audigy supports at most 32M of memory) */
+	if (devc->max_mem > 32 * 1024 * 1024)
+		devc->max_mem = 32 * 1024 * 1024;
+
+	devc->max_pages = devc->max_mem / 4096;
+	if (devc->max_pages < 1024)
+		devc->max_pages = 1024;
+
+	/* Allocate page table */
+	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
+	    &devc->pt_dmah) != DDI_SUCCESS) {
+		audio_dev_warn(devc->adev,
+		    "failed to allocate page table handle");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_dma_mem_alloc(devc->pt_dmah, devc->max_pages * 4,
+	    &dev_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
+	    &devc->pt_kaddr, &len, &devc->pt_acch) !=
+	    DDI_SUCCESS) {
+		audio_dev_warn(devc->adev,
+		    "failed to allocate memory for page table");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_dma_addr_bind_handle(devc->pt_dmah, NULL,
+	    devc->pt_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
+	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
+		audio_dev_warn(devc->adev,
+		    "failed binding page table DMA handle");
+		return (DDI_FAILURE);
+	}
+
+	devc->page_map = (void *)devc->pt_kaddr;
+	devc->pt_paddr = cookie.dmac_address;
+	bzero(devc->pt_kaddr, devc->max_pages * 4);
+
+	/* Allocate silent page */
+	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
+	    &devc->silence_dmah) != DDI_SUCCESS) {
+		audio_dev_warn(devc->adev,
+		    "failed to allocate silent page handle");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_dma_mem_alloc(devc->silence_dmah, 4096,
+	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
+	    &devc->silence_kaddr, &len,
+	    &devc->silence_acch) != DDI_SUCCESS) {
+		audio_dev_warn(devc->adev,
+		    "failed to allocate silent page memory");
+		return (DDI_FAILURE);
+	}
+
+	(void) ddi_dma_sync(devc->silence_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
+
+	if (ddi_dma_addr_bind_handle(devc->silence_dmah, NULL,
+	    devc->silence_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
+	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
+		audio_dev_warn(devc->adev,
+		    "failed binding silent page DMA handle");
+		return (DDI_FAILURE);
+	}
+
+	devc->silence_paddr = cookie.dmac_address;
+	bzero(devc->silence_kaddr, 4096);
+	devc->audio_memptr = 4096;	/* Skip the silence page */
+
+	for (i = 0; i < devc->max_pages; i++)
+		FILL_PAGE_MAP_ENTRY(i, devc->silence_paddr);
+
+	(void) ddi_dma_sync(devc->pt_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
+
+	devc->ac97 = ac97_allocate(devc->adev, dip,
+	    emu10k_read_ac97, emu10k_write_ac97, devc);
+	if (devc->ac97 == NULL) {
+		audio_dev_warn(devc->adev, "failed to allocate ac97 handle");
+		goto error;
+	}
+
+	ac97_probe_controls(devc->ac97);
+
+	/* allocate voice 0 for play */
+	if (emu10k_alloc_port(devc, EMU10K_REC) != DDI_SUCCESS)
+		goto error;
+
+	if (emu10k_alloc_port(devc, EMU10K_PLAY) != DDI_SUCCESS)
+		goto error;
+
+	/* now initialize the hardware */
+	mutex_enter(&devc->mutex);
+	if (emu10k_hwinit(devc) != DDI_SUCCESS) {
+		mutex_exit(&devc->mutex);
+		goto error;
+	}
+	mutex_exit(&devc->mutex);
+
+	emu10k_create_controls(devc);
+
+	/* set up kernel statistics */
+	if ((devc->ksp = kstat_create(EMU10K_NAME, ddi_get_instance(dip),
+	    EMU10K_NAME, "controller", KSTAT_TYPE_INTR,
+	    1, KSTAT_FLAG_PERSISTENT)) != NULL) {
+		kstat_install(devc->ksp);
+	}
+
+	if (audio_dev_register(devc->adev) != DDI_SUCCESS) {
+		audio_dev_warn(devc->adev, "unable to register audio device");
+		goto error;
+	}
+
+	(void) ddi_intr_enable(devc->ih);
+	ddi_report_dev(dip);
+
+	return (DDI_SUCCESS);
+
+error:
+	emu10k_destroy(devc);
+	return (DDI_FAILURE);
+}
+
+int
+emu10k_resume(dev_info_t *dip)
+{
+	emu10k_devc_t *devc;
+	emu10k_portc_t *portc;
+
+	devc = ddi_get_driver_private(dip);
+
+	for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
+		portc = devc->portc[i];
+		audio_engine_reset(portc->engine);
+	}
+
+	mutex_enter(&devc->mutex);
+	if (emu10k_hwinit(devc) != DDI_SUCCESS) {
+		mutex_exit(&devc->mutex);
+		/*
+		 * In case of failure, we leave the chip suspended,
+		 * but don't panic.  Audio service is not normally a a
+		 * critical service.
+		 */
+		audio_dev_warn(devc->adev, "FAILED to RESUME device");
+		return (DDI_SUCCESS);
+	}
+
+	emu10k_refresh_mixer(devc);
+
+	devc->suspended = B_FALSE;
+
+	for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
+
+		portc = devc->portc[i];
+
+		portc->stop_port(portc);
+
+		portc->dopos = 1;
+		if (portc->started) {
+			portc->reset_port(portc);
+			portc->start_port(portc);
+		}
+	}
+
+	mutex_exit(&devc->mutex);
+
+	/* resume ac97 */
+	ac97_resume(devc->ac97);
+
+	return (DDI_SUCCESS);
+}
+
+int
+emu10k_detach(emu10k_devc_t *devc)
+{
+	if (audio_dev_unregister(devc->adev) != DDI_SUCCESS)
+		return (DDI_FAILURE);
+
+	emu10k_destroy(devc);
+	return (DDI_SUCCESS);
+}
+
+int
+emu10k_suspend(emu10k_devc_t *devc)
+{
+	ac97_suspend(devc->ac97);
+
+	mutex_enter(&devc->mutex);
+
+	devc->suspended = B_TRUE;
+
+	emu10k_write_reg(devc, CLIEL, 0, 0);
+	emu10k_write_reg(devc, CLIEH, 0, 0);
+	if (!(devc->feature_mask & SB_LIVE)) {
+		emu10k_write_reg(devc, HLIEL, 0, 0x0);
+		emu10k_write_reg(devc, HLIEH, 0, 0x0);
+	}
+	OUTL(devc, 0, devc->regs + IE);	/* Intr enable (all off) */
+
+	for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
+		emu10k_portc_t *portc = devc->portc[i];
+		portc->stop_port(portc);
+	}
+
+	/* stop all voices */
+	for (int i = 0; i < 64; i++) {
+		emu10k_write_reg(devc, VEDS, i, 0);
+	}
+	for (int i = 0; i < 64; i++) {
+		emu10k_write_reg(devc, VTFT, i, 0);
+		emu10k_write_reg(devc, CVCF, i, 0);
+		emu10k_write_reg(devc, PTAB, i, 0);
+		emu10k_write_reg(devc, CPF, i, 0);
+	}
+	/*
+	 * Turn off the hardware
+	 */
+	OUTL(devc,
+	    HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
+	    HCFG_MUTEBUTTONENABLE, devc->regs + HCFG);
+
+	/* stop ADC recording */
+	emu10k_write_reg(devc, ADCSR, 0, 0x0);
+	emu10k_write_reg(devc, ADCBA, 0, 0x0);
+	emu10k_write_reg(devc, ADCBA, 0, 0x0);
+
+	emu10k_write_reg(devc, PTBA, 0, 0);
+
+	mutex_exit(&devc->mutex);
+
+	return (DDI_SUCCESS);
+}
+
+static int emu10k_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
+static int emu10k_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
+static int emu10k_ddi_quiesce(dev_info_t *);
+
+static struct dev_ops emu10k_dev_ops = {
+	DEVO_REV,			/* rev */
+	0,				/* refcnt */
+	NULL,				/* getinfo */
+	nulldev,			/* identify */
+	nulldev,			/* probe */
+	emu10k_ddi_attach,		/* attach */
+	emu10k_ddi_detach,		/* detach */
+	nodev,				/* reset */
+	NULL,				/* cb_ops */
+	NULL,				/* bus_ops */
+	NULL,				/* power */
+	emu10k_ddi_quiesce,		/* quiesce */
+};
+
+static struct modldrv emu10k_modldrv = {
+	&mod_driverops,			/* drv_modops */
+	"Creative EMU10K Audio",	/* linkinfo */
+	&emu10k_dev_ops,		/* dev_ops */
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	{ &emu10k_modldrv, NULL }
+};
+
+int
+_init(void)
+{
+	int rv;
+
+	audio_init_ops(&emu10k_dev_ops, EMU10K_NAME);
+	if ((rv = mod_install(&modlinkage)) != 0) {
+		audio_fini_ops(&emu10k_dev_ops);
+	}
+	return (rv);
+}
+
+int
+_fini(void)
+{
+	int rv;
+
+	if ((rv = mod_remove(&modlinkage)) == 0) {
+		audio_fini_ops(&emu10k_dev_ops);
+	}
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+emu10k_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	switch (cmd) {
+	case DDI_ATTACH:
+		return (emu10k_attach(dip));
+
+	case DDI_RESUME:
+		return (emu10k_resume(dip));
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+int
+emu10k_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	emu10k_devc_t *devc;
+
+	devc = ddi_get_driver_private(dip);
+
+	switch (cmd) {
+	case DDI_DETACH:
+		return (emu10k_detach(devc));
+
+	case DDI_SUSPEND:
+		return (emu10k_suspend(devc));
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+int
+emu10k_ddi_quiesce(dev_info_t *dip)
+{
+	emu10k_devc_t *devc;
+
+	devc = ddi_get_driver_private(dip);
+
+	/* stop all voices */
+	for (int i = 0; i < 64; i++) {
+		emu10k_write_reg(devc, VEDS, i, 0);
+	}
+	for (int i = 0; i < 64; i++) {
+		emu10k_write_reg(devc, VTFT, i, 0);
+		emu10k_write_reg(devc, CVCF, i, 0);
+		emu10k_write_reg(devc, PTAB, i, 0);
+		emu10k_write_reg(devc, CPF, i, 0);
+	}
+
+	/*
+	 * Turn off the hardware
+	 */
+	OUTL(devc, 0, devc->regs + IE);	/* Intr enable (all off) */
+	OUTL(devc,
+	    HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK |
+	    HCFG_MUTEBUTTONENABLE, devc->regs + HCFG);
+
+	/* stop ADC recording */
+	emu10k_write_reg(devc, ADCSR, 0, 0x0);
+	emu10k_write_reg(devc, ADCBA, 0, 0x0);
+	emu10k_write_reg(devc, ADCBA, 0, 0x0);
+
+	emu10k_write_reg(devc, PTBA, 0, 0);
+
+	return (DDI_SUCCESS);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/audio/drv/audioemu10k/audioemu10k.h	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,453 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Purpose: Definitions for the SB Live/Audigy driver
+ */
+/*
+ * Copyright (C) 4Front Technologies 1996-2009.
+ */
+#ifndef	EMU10K_H
+#define	EMU10K_H
+
+#define	PCI_VENDOR_ID_CREATIVE		0x1102
+#define	PCI_DEVICE_ID_SBLIVE		0x0002
+#define	PCI_DEVICE_ID_AUDIGY		0x0004
+#define	PCI_DEVICE_ID_AUDIGYVALUE	0x0008
+
+#define	SAMPLE_RATE		48000
+
+#define	EMU10K_NAME		"audioemu10k"
+
+#define	EMU10K_NUM_PORTC	2
+#define	EMU10K_PLAY		0
+#define	EMU10K_REC		1
+
+#define	EMU10K_NUM_FRAGS	(2*4)	/* Must be multiple of 2 */
+
+#define	EMU10K_MAX_INTRS	512
+#define	EMU10K_MIN_INTRS	10
+#define	EMU10K_INTRS		100
+
+#define	FRAGMENT_FRAMES		512
+
+#define	EMU10K1_MAGIC		0xe10001
+#define	EMU10K2_MAGIC		0xe10002
+
+/* Audio */
+
+#define	DMABUF_SIZE		(256 * 1024)
+
+#define	AUDIO_MAXVOICE		(2*EMU10K_NUM_PORTC)
+/* Audio buffer + silent page */
+#define	AUDIO_MEMSIZE		(EMU10K_NUM_PORTC*DMABUF_SIZE+4096)
+
+/* Wall clock register */
+#define	WC			0x10
+
+/* Hardware config register */
+#define	HCFG			0x14
+#define	HCFG_CODECFORMAT_MASK	0x00070000	/* CODEC format */
+#define	HCFG_CODECFORMAT_AC97	0x00000000	/* AC97 CODEC format */
+#define	HCFG_CODECFORMAT_I2S	0x00010000	/* I2S CODEC format */
+#define	HCFG_GPINPUT0		0x00004000	/* External pin112 */
+#define	HCFG_GPINPUT1		0x00002000	/* External pin110 */
+#define	HCFG_GPOUTPUT_MASK	0x00001c00	/* Controllable pins */
+#define	HCFG_GPOUT0		0x00001000	/* enable dig out on 5.1 */
+#define	HCFG_GPOUT1		0x00000800	/* IR */
+#define	HCFG_GPOUT2		0x00000400	/* IR */
+#define	HCFG_JOYENABLE		0x00000200	/* Internal joystick enable */
+#define	HCFG_PHASETRACKENABLE	0x00000100	/* Phase tracking enable */
+#define	HCFG_AC3ENABLE_MASK	0x0x0000e0	/* AC3 async input control */
+#define	HCFG_AC3ENABLE_ZVIDEO	0x00000080	/* Chan 0/1 replace ZVIDEO  */
+#define	HCFG_AC3ENABLE_CDSPDIF	0x00000040	/* Chan 0/1 replace CDSPDIF */
+#define	HCFG_AC3ENABLE_GPSPDIF	0x00000020	/* Chan 0/1 replace GPSPDIF */
+#define	HCFG_AUTOMUTE		0x00000010
+#define	HCFG_LOCKSOUNDCACHE	0x00000008
+#define	HCFG_LOCKTANKCACHE_MASK	0x00000004
+#define	HCFG_LOCKTANKCACHE	0x01020014
+#define	HCFG_MUTEBUTTONENABLE	0x00000002	/* Mute can clear audioenable */
+#define	HCFG_AUDIOENABLE	0x00000001	/* Codecs can send data */
+#define	A_HCFG_VMUTE		0x00004000
+#define	A_HCFG_AUTOMUTE		0x00008000
+#define	A_HCFG_XM		0x00040000	/* Xtended address mode */
+
+/*
+ * GPIO bit definitions (global register 0x18) for Audigy.
+ */
+
+#define	A_IOCFG_GPOUT0		0x0044	/* analog/digital? */
+#define	A_IOCFG_GPOUT1		0x0002	/* IR */
+#define	A_IOCFG_GPOUT2		0x0001	/* IR */
+
+/* Status bits (read only) */
+#define	GPIO_VERSAPLUGGED	0x2000	/* Center/LFE/digital */
+#define	GPIO_FRONTPLUGGED	0x4000
+#define	GPIO_REARPLUGGED	0x8000
+#define	GPIO_HEADPHPLUGGED	0x0100
+#define	GPIO_ANALOG_MUTE	0x0040
+#define	GPIO_DIGITAL_ENABLE	0x0004	/* Cen/lfe (0) or digital (1) switch */
+
+#define	FILL_PAGE_MAP_ENTRY(e, v)					\
+	ddi_put32(devc->pt_acch, devc->page_map + e, ((v) << 1) | (e));
+
+/*
+ * Audio block registers
+ */
+
+#define	CPF		0x000	/* DW:cnl   Current pitch and fraction */
+#define	CPF_CURRENTPITCH_MASK		0xffff0000
+#define	CPF_CURRENTPITCH		0x10100000
+#define	CPF_STEREO_MASK			0x00008000
+#define	CPF_STOP_MASK			0x00004000
+#define	CPF_FRACADDRESS_MASK		0x00003fff
+
+
+#define	PTAB		0x001	/* DW:cnl   Pitch target and sends A and B */
+#define	PTRX_PITCHTARGET_MASK		0xffff0000
+#define	PTRX_PITCHTARGET		0x10100001
+#define	PTRX_FXSENDAMOUNT_A_MASK	0x0000ff00
+#define	PTRX_FXSENDAMOUNT_A		0x08080001
+#define	PTRX_FXSENDAMOUNT_B_MASK	0x000000ff
+#define	PTRX_FXSENDAMOUNT_B		0x08000001
+
+
+#define	CVCF		0x002	/* DW:cnl   Curr vol and curr filter cutoff */
+#define	VTFT		0x003	/* DW:cnl   Volume tgt and filter cutoff tgt */
+#define	Z2		0x004	/* DW:cnl   Filter delay memory 2 */
+#define	Z1		0x005	/* DW:cnl   Filter delay memory 1 */
+#define	SCSA		0x006	/* DW:cnl   Send C and Start addr */
+#define	SDL		0x007	/* DW:cnl   Send D and Loop addr */
+#define	QKBCA		0x008	/* DW:cnl   Filter Q, ROM, etc */
+#define	CCR		0x009
+#define	CCR_CACHEINVALIDSIZE		0x07190009
+#define	CCR_CACHEINVALIDSIZE_MASK	0xfe000000
+#define	CCR_CACHELOOPFLAG		0x01000000
+#define	CCR_INTERLEAVEDSAMPLES		0x00800000
+#define	CCR_WORDSIZEDSAMPLES		0x00400000
+#define	CCR_READADDRESS			0x06100009
+#define	CCR_READADDRESS_MASK		0x003f0000
+#define	CCR_LOOPINVALSIZE		0x0000fe00
+#define	CCR_LOOPFLAG			0x00000100
+#define	CCR_CACHELOOPADDRHI		0x000000ff
+
+#define	CLP		0x00a
+#define	SRHE		0x07c
+#define	STHE		0x07d
+#define	SRDA		0x07e
+#define	STDA		0x07f
+#define	L_FXRT		0x00b
+#define	FXRT		0x00b	/* W:cnl */
+#define	MAPA		0x00c
+#define	MAPB		0x00d
+#define	VEV		0x010	/* W:cnl */
+#define	VEHA		0x011	/* W:cnl */
+#define	VEDS		0x012	/* W:cnl */
+#define	MLV		0x013	/* W:cnl */
+#define	MEV		0x014	/* W:cnl */
+#define	MEHA		0x015	/* W:cnl */
+#define	MEDS		0x016	/* W:cnl */
+#define	VLV		0x017	/* W:cnl */
+#define	IP		0x018	/* W:cnl */
+#define	IFA		0x019	/* W:cnl */
+#define	PEFE		0x01a	/* W:cnl */
+#define	PEFE_PITCHAMOUNT_MASK	0x0000ff00	/* Pitch envlope amount */
+#define	PEFE_PITCHAMOUNT	0x0808001a
+#define	PEFE_FILTERAMOUNT_MASK	0x000000ff	/* Filter envlope amount */
+#define	PEFE_FILTERAMOUNT	0x0800001a
+
+#define	VFM		0x01b	/* W:cnl */
+#define	TMFQ		0x01c	/* W:cnl */
+#define	VVFQ		0x01d	/* W:cnl */
+#define	TMPE		0x01e	/* W:cnl */
+#define	CD0		0x020	/* DW:cnl (16 registers) */
+#define	PTBA		0x040	/* DW:nocnl */
+#define	TCBA		0x041	/* DW:nocnl */
+#define	ADCSR		0x042	/* B:nocnl */
+#define	FXWC		0x043	/* DW:nocnl */
+#define	TCBS		0x044	/* B:nocnl */
+#define	MBA		0x045	/* DW:nocnl */
+#define	ADCBA		0x046	/* DW:nocnl */
+#define	FXBA		0x047	/* DW:nocnl */
+
+#define	MBS		0x049	/* B:nocnl */
+#define	ADCBS		0x04a	/* B:nocnl */
+#define	FXBS		0x04b	/* B:nocnl */
+#define	CSBA	0x4c
+#define	CSDC	0x4d
+#define	CSFE	0x4e
+#define	CSHG	0x4f
+#define	CDCS		0x050	/* DW:nocnl */
+#define	GPSCS		0x051	/* DW:nocnl */
+#define	DBG		0x052	/* DW:nocnl */
+#define	AUDIGY_DBG	0x053	/* DW:nocnl */
+#define	SCS0		0x054	/* DW:nocnl */
+#define	SCS1		0x055	/* DW:nocnl */
+#define	SCS2		0x056	/* DW:nocnl */
+#define	CLIEL		0x058	/* DW:nocnl */
+#define	CLIEH		0x059	/* DW:nocnl */
+#define	CLIPL		0x05a	/* DW:nocnl */
+#define	CLIPH		0x05b	/* DW:nocnl */
+#define	SOLL		0x05c	/* DW:nocnl */
+#define	SOLH		0x05d	/* DW:nocnl */
+#define	SOC		0x05e	/* DW:nocnl */
+#define	AC97SLOT	0x05f
+#define	AC97SLOT_REAR_RIGHT	0x01
+#define	AC97SLOT_REAR_LEFT	0x02
+#define	AC97SLOT_CENTER		0x10
+#define	AC97SLOT_LFE		0x20
+#define	CDSRCS		0x060	/* DW:nocnl */
+#define	GPSRCS		0x061	/* DW:nocnl */
+#define	ZVSRCS		0x062	/* DW:nocnl */
+#define	ADCIDX		0x063	/* W:nocnl */
+#define	MIDX		0x064	/* W:nocnl */
+#define	FXIDX		0x065	/* W:nocnl */
+
+/* Half loop interrupt registers (audigy only) */
+#define	HLIEL		0x066	/* DW:nocnl */
+#define	HLIEH		0x067	/* DW:nocnl */
+#define	HLIPL		0x068	/* DW:nocnl */
+#define	HLIPH		0x069	/* DW:nocnl */
+#define	GPR0	((devc->feature_mask&SB_LIVE)? 0x100:0x400)	/* DW:nocnl */
+#define	TMA0		0x300	/* Tank memory */
+#define	UC0	((devc->feature_mask&SB_LIVE) ? 0x400:0x600)	/* DSM ucode */
+
+/* Interrupt pending register */
+#define	INTPEND	0x08
+#define		INT_VI		0x00100000
+#define		INT_VD		0x00080000
+#define		INT_MU		0x00040000
+#define		INT_MF		0x00020000
+#define		INT_MH		0x00010000
+#define		INT_AF		0x00008000
+#define		INT_AH		0x00004000
+#define		INT_IT		0x00000200
+#define		INT_TX		0x00000100
+#define		INT_RX		0x00000080
+#define		INT_CL		0x00000040
+/* Interrupt enable register */
+#define	IE	0x0c
+#define		IE_VI		0x00000400
+#define		IE_VD		0x00000200
+#define		IE_MU		0x00000100
+#define		IE_MB		0x00000080
+#define		IE_AB		0x00000040
+#define		IE_IT		0x00000004
+#define		IE_TX		0x00000002
+#define		IE_RX		0x00000001
+
+/* Interval timer register */
+#define	TIMR		0x1a
+
+/* EMU10K2 MIDI UART */
+#define	MUADAT		0x070
+#define	MUACMD		0x071
+#define	MUASTAT		MUACMD
+
+/* EMU10K2 S/PDIF recording buffer */
+#define	SPRI		0x6a
+#define	SPRA		0x6b
+#define	SPRC		0x6c
+
+#define	EHC		0x76	/* Audigy 2 */
+
+#define	SRHE	0x07c
+#define	STHE	0x07d
+#define	SRDA	0x07e
+
+#define	ROM0		0x00000000	/* interpolation ROM 0 */
+#define	ROM1		0x02000000	/* interpolation ROM 1 */
+#define	ROM2		0x04000000	/* interpolation ROM 2 */
+#define	ROM3		0x06000000	/* interpolation ROM 3 */
+#define	ROM4		0x08000000	/* interpolation ROM 4 */
+#define	ROM5		0x0A000000	/* interpolation ROM 5 */
+#define	ROM6		0x0C000000	/* interpolation ROM 6 */
+#define	ROM7		0x0E000000	/* interpolation ROM 7 */
+#define	BYTESIZE	0x01000000	/* byte sound memory */
+
+#define	MAX_GPR	256
+
+/* See feature_mask below */
+#define	SB_LIVE		1
+#define	SB_AUDIGY	2
+#define	SB_AUDIGY2	4
+#define	SB_AUDIGY2VAL	8
+#define	SB_51		0x10
+#define	SB_71		0x20
+#define	SB_INVSP	0x40	/* invert shared spdif switch */
+#define	SB_NOEXP	0x80	/* no support for Live! Drive or expansion */
+
+#define	LEFT_CH		0
+#define	RIGHT_CH	1
+
+#ifdef	_KERNEL
+
+typedef struct _emu10k_devc_t emu10k_devc_t;
+typedef struct _emu10k_portc_t emu10k_portc_t;
+
+
+typedef enum {
+	CTL_VOLUME = 0,
+	CTL_FRONT,
+	CTL_SURROUND,
+	CTL_CENTER,
+	CTL_LFE,
+	CTL_SIDE,
+	CTL_HEADPH,
+
+	CTL_RECGAIN,
+	CTL_RECSRC,
+	CTL_AC97SRC,
+
+	/* monitor source values */
+	CTL_AC97,
+	CTL_DIGCD,
+	CTL_SPD1,
+	CTL_SPD2,
+	CTL_LINE2,
+	CTL_AUX2,
+
+	CTL_JACK3,
+
+	/* this one must be last */
+	CTL_MAX,
+} emu10k_ctrl_id_t;
+
+typedef struct _emu10k_ctrl {
+	emu10k_devc_t	*devc;
+	audio_ctrl_t	*ctrl;
+	int		gpr_num;
+	uint64_t	val;
+} emu10k_ctrl_t;
+
+typedef struct _emu10k_gpr {
+	boolean_t	valid;
+	uint32_t	value;
+} emu10k_gpr_t;
+
+struct _emu10k_portc_t {
+	emu10k_devc_t		*devc;
+	audio_engine_t		*engine;
+
+	/* Helper functions */
+	void			(*update_port)(emu10k_portc_t *);
+	void			(*reset_port)(emu10k_portc_t *);
+	void			(*stop_port)(emu10k_portc_t *);
+	void			(*start_port)(emu10k_portc_t *);
+
+	int			channels;
+
+	boolean_t		started;
+	boolean_t		active;
+	unsigned		fragfr;
+	unsigned		nframes;
+	unsigned		nfrags;
+	unsigned		fragsz;
+
+	ddi_dma_handle_t	buf_dmah;	/* dma for buffers */
+	ddi_acc_handle_t	buf_acch;
+	uint32_t		buf_paddr;
+	caddr_t			buf_kaddr;
+	size_t			buf_size;
+	/* Start of loop within the internal memory space */
+	uint32_t		memptr;
+	int			syncdir;
+	/* Position & timing */
+	uint64_t		count;
+	uint32_t		pos;
+	int		dopos;
+};
+
+struct _emu10k_devc_t {
+	dev_info_t		*dip;
+	audio_dev_t		*adev;
+	kstat_t			*ksp;
+	boolean_t		suspended;
+	ddi_acc_handle_t	pcih;
+	ddi_acc_handle_t	regsh;
+	caddr_t			regs;
+	kmutex_t		mutex;
+	ddi_intr_handle_t	ih;
+
+	/*
+	 * Page table
+	 */
+	ddi_dma_handle_t	pt_dmah;	/* dma for page_tablefers */
+	ddi_acc_handle_t	pt_acch;
+	uint32_t		pt_paddr;
+	caddr_t			pt_kaddr;
+	uint32_t		*page_map;	/* up to 8k ptrs to 4k pages */
+
+
+	/*
+	 * Silent page used by voices that don't play anything.
+	 */
+	ddi_dma_handle_t	silence_dmah;	/* dma for silencefers */
+	ddi_acc_handle_t	silence_acch;
+	uint32_t		silence_paddr;
+	caddr_t			silence_kaddr;
+
+	/*
+	 * Device feature mask tells which kind of features are
+	 * supported by the hardware. Audigy2/2val have multiple bits
+	 * set while Live! has just the SB_LIVE bits. So Features of
+	 * Audigy will be reported by Audigy2/val too.
+	 */
+	int			feature_mask;
+	int			max_mem, max_pages, nr_pages;
+	/*
+	 * Mixer
+	 */
+	ac97_t			*ac97;
+	ac97_ctrl_t		*ac97_recsrc;
+	uint32_t		ac97_stereomix;
+	emu10k_gpr_t		gpr_shadow[MAX_GPR];
+	emu10k_ctrl_t		ctrls[CTL_MAX];
+
+	/*
+	 * Audio
+	 */
+
+	int			audio_memptr;
+	int			*silent_page;
+
+	emu10k_portc_t		*portc[EMU10K_NUM_PORTC];
+};
+
+#define	INB(devc, reg)		ddi_get8(devc->regsh, (void *)(reg))
+#define	OUTB(devc, val, reg)	ddi_put8(devc->regsh, (void *)(reg), (val))
+
+#define	INW(devc, reg)		ddi_get16(devc->regsh, (void *)(reg))
+#define	OUTW(devc, val, reg)	ddi_put16(devc->regsh, (void *)(reg), (val))
+
+#define	INL(devc, reg)		ddi_get32(devc->regsh, (void *)(reg))
+#define	OUTL(devc, val, reg)	ddi_put32(devc->regsh, (void *)(reg), (val))
+
+#define	EMU10K_KIOP(X)	((kstat_intr_t *)(X->ksp->ks_data))
+
+#endif	/* _KERNEL */
+
+#endif /* EMU10K_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/audio/drv/audioemu10k/dsp/asm10k.c	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,1096 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Assembler for Emu10k1
+ */
+/*
+ * Copyright (C) 4Front Technologies 1996-2008.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/param.h>
+
+#include <sys/soundcard.h>
+
+#define	MAX_GPR	256
+#define	MAX_GPR_PARMS	60
+#define	MAX_CONST_PARMS	128
+#define	GPR_NAME_SIZE 32
+
+typedef struct {
+	char name[GPR_NAME_SIZE];
+	unsigned int num;
+	int type;
+	int def;
+} gpr_t;
+
+typedef struct {
+	unsigned int gpr;
+	unsigned int value;
+} const_t;
+
+typedef struct {
+	unsigned int ngpr;
+
+	gpr_t gpr[MAX_GPR_PARMS];
+} gpr_info;
+
+typedef struct {
+	unsigned int nconst;
+
+	const_t consts[MAX_CONST_PARMS];
+} const_info;
+
+typedef struct {
+	unsigned int code[1024];
+	gpr_info parms;
+	const_info consts;
+	int	ninit;
+	struct {
+		uint32_t	gpr;
+		uint32_t	value;
+		char		name[GPR_NAME_SIZE];
+	} init[MAX_GPR];
+} emu10k1_file;
+
+#define	MAX_NAME	64
+#define	MAX_SYMBOLS	1024
+
+static int parms_only = 0;
+static int is_audigy = 0;
+static int verbose = 0;
+
+static int gpr_base = 0x100;
+static int input_base = 0x10;
+static int output_base = 0x20;
+
+static char *progname;
+
+typedef struct {
+	char name[MAX_NAME];
+	int type;
+#define	SY_DUMMY	0
+#define	SY_GPR		1
+#define	SY_INPUT	2
+#define	SY_OUTPUT	3
+#define	SY_CONST	4
+#define	SY_FX		5
+#define	SY_ACCUM	6
+#define	SY_PARM		7
+	int arg;
+} sym_t;
+
+typedef struct {
+	char *name;
+	int opcode;
+} instruction_t;
+
+static char remarks[2048] = "";
+static char *banner =
+	"/*\n"
+	" * Note: This file was automatically generated by %s\n"
+	" * on %s.\n"
+	" */\n";
+
+/*
+ * Instructions.  Each instruction takes 4 arguments, R, A, X, and Y.
+ */
+static instruction_t instructions[] = {
+	{ "MACS",	0x0},	/* R = A + (X * Y >> 31); saturation */
+	{ "MACS1",	0x1},	/* R = A + (-X * Y >> 31); saturation */
+	{ "MACW",	0x2},	/* R = A + (X * Y >> 31); wraparound */
+	{ "MACW1",	0x3},	/* R = A + (-X * Y >> 31); wraparound */
+	{ "MACINTS",	0x4},	/* R = A + (X * Y); saturation */
+	{ "MACINTW",	0x5},	/* R = A + (X * Y); wraparound */
+	{ "SUM",	0x6},	/* R = A + X + Y; saturation */
+	{ "ACC3",	0x6},	/* R = A + X + Y; saturation */
+	{ "MACMV",	0x7},	/* R = A, acc += X * Y >> 31 */
+	{ "ANDXOR",	0x8},	/* R = (A & X) ^ Y */
+	{ "TSTNEG",	0x9},	/* R = (A >= Y) ? X : ~X */
+	{ "LIMIT",	0xa},	/* R = (A >= Y) ? X : Y */
+	{ "LIMIT1",	0xb},	/* R = (A < Y) ? X : Y */
+	{ "LOG",	0xc},	/* R = ... (log?) */
+	{ "EXP",	0xd},	/* R = ... (exp?) */
+	{ "INTERP",	0xe},	/* R = A + (X * (Y - A) >> 31) */
+	{ "SKIP",	0xf},	/* R, CCR, CC_TEST, COUNT */
+	{ NULL, 0}
+};
+
+#define	CHECK_COUNT(tokens, cnt, mincnt, maxcnt)			\
+	if (cnt < mincnt) {						\
+		error("Too few parameters for '%s' (have %d, min %d)",	\
+		    tokens[0], cnt - 1, mincnt - 1);			\
+		return;							\
+	}								\
+	if (cnt > maxcnt) {						\
+		error("Too many parameters for '%s' (have %d, max %d)",	\
+		    tokens[0], cnt - 1, maxcnt - 1);			\
+		return;							\
+	}
+
+static sym_t symtab[MAX_SYMBOLS];
+static int nsyms = 0;
+
+static int lineno = 0, errors = 0;
+static emu10k1_file fle;
+static int pc;
+
+static int ngpr = 0;
+static char *infile;
+
+static int
+getline(FILE *input, char **tokens)
+{
+	char *s, *ls;
+	static char *stmt = NULL, *lasts = NULL;
+	static char line[4096];
+	int cnt, tokcnt;
+
+	for (;;) {
+
+		if (stmt == NULL) {
+			if (fgets(line, sizeof (line), input) == NULL)
+				return (-1);
+			lineno++;
+
+			/*
+			 * Special handling for .' comments.  We use
+			 * .' as a keyword to ensure that entire
+			 * comment makes it through the C preprocessor
+			 * unmolested.  We also need to make sure *we*
+			 * don't molest it either.  The comment will
+			 * be exported to any resulting header,
+			 * allowing us to pass through copyright and
+			 * other information from the source file to
+			 * the resulting header.
+			 */
+			s = line;
+			s += strspn(s, " \t");
+			if ((strncmp(s, ".'", 2) == 0) &&
+			    (strchr(" \t\n", s[2]) != NULL)) {
+				/* chop off trailing new line */
+				(void) strtok(line, "\n");
+				tokens[0] = s;
+				s += 2;
+				s += strspn(s, " \t");
+				if ((s[0] == '\'') &&
+				    (s[strlen(s) - 1] == '\'')) {
+					s[strlen(s) - 1] = 0;
+					s++;
+				}
+				tokens[1] = s;
+				tokens[0][2] = 0;
+				tokens[2] = NULL;
+				stmt = NULL;
+				return (strlen(tokens[1]) ? 2 : 1);
+			}
+
+			/* strip off any C++ style comments that CPP missed */
+			if ((s = strstr(line, "//")) != NULL) {
+				*s = NULL;
+			}
+			stmt = strtok_r(line, ";\n", &lasts);
+		} else {
+			stmt = strtok_r(NULL, ";\n", &lasts);
+		}
+
+		if (stmt != NULL) {
+			break;
+		}
+	}
+
+	/*
+	 * Ok, we have a statement, lets tokenize it.  For
+	 * simplicities sake we convert "OPCODE(arg1, arg2)" into
+	 * "OPCODE arg1 arg2".  This means that commas and parens are
+	 * treated as whitespace.  This can lead to some really messed
+	 * up syntaxes that get assembled properly (such as nested
+	 * calls, empty arguments, etc.)  Hopefully people don't abuse
+	 * this.
+	 */
+	ls = NULL;
+	s = strtok_r(stmt, " \t\n(),", &ls);
+	cnt = 0;
+	tokcnt = 0;
+	while (cnt < 10) {
+		tokens[cnt++] = s;
+		if (s != NULL) {
+			tokcnt++;
+			s = strtok_r(NULL, " \t\n(),", &ls);
+		}
+	}
+	return (tokcnt);
+}
+
+static void
+error(char *msg, ...)
+{
+	va_list va;
+	char msgbuf[1024];
+
+	va_start(va, msg);
+	(void) vsnprintf(msgbuf, sizeof (msgbuf), msg, va);
+	va_end(va);
+
+	(void) fprintf(stderr, "Error: %s on line %d of %s\n", msgbuf, lineno,
+	    infile);
+	errors++;
+}
+
+static sym_t *
+find_symbol(char *name)
+{
+	int i;
+
+	for (i = 0; i < nsyms; i++)
+		if (strcmp(symtab[i].name, name) == 0) {
+			return (&symtab[i]);
+		}
+
+	return (NULL);
+}
+
+static void
+add_symbol(char *name, int type, int arg)
+{
+	sym_t *sym;
+
+	if (nsyms >= MAX_SYMBOLS) {
+		error("Symbol table full");
+		exit(-1);
+	}
+
+	if (find_symbol(name) != NULL) {
+		error("Dublicate symbol '%s'", name);
+		return;
+	}
+
+	if (strlen(name) >= MAX_NAME) {
+		error("Symbol name '%s' too long", name);
+		exit(-1);
+	}
+
+	sym = &symtab[nsyms++];
+
+	(void) strcpy(sym->name, name);
+	sym->type = type;
+	sym->arg = arg;
+}
+
+static void
+add_init(uint32_t gpr, uint32_t val, const char *name)
+{
+	int	n;
+
+	n = fle.ninit;
+	if (n >= MAX_GPR) {
+		error("Too many GPRs");
+		return;
+	}
+	fle.init[n].gpr = gpr;
+	fle.init[n].value = val;
+	if (name)
+		(void) strlcpy(fle.init[n].name, name,
+		    sizeof (fle.init[n].name));
+	fle.ninit++;
+}
+
+static void
+compile_gpr(char **tokens, int cnt)
+{
+	CHECK_COUNT(tokens, cnt, 2, 2);
+
+	if (ngpr >= MAX_GPR)
+		error("Too many GPR variables");
+
+	add_symbol(tokens[1], SY_GPR, gpr_base + ngpr++);
+}
+
+static void
+compile_rem(char **tokens, int cnt)
+{
+	int i;
+
+	(void) strlcat(remarks, " *", sizeof (remarks));
+	for (i = 1; i < cnt; i++) {
+		(void) strlcat(remarks, " ", sizeof (remarks));
+		(void) strlcat(remarks, tokens[i], sizeof (remarks));
+	}
+	(void) strlcat(remarks, "\n", sizeof (remarks));
+}
+
+static void
+declare_const(unsigned int gpr, char *value)
+{
+	int n, intv;
+	float v;
+
+	n = fle.consts.nconst;
+
+	if (n >= MAX_CONST_PARMS) {
+		error("Too many constant parameters");
+		return;
+	}
+
+	if (*value == 'I') {
+		if (sscanf(&value[1], "%g", &v) != 1) {
+			error("Bad floating point value (%s)", value);
+			return;
+		}
+		intv = (int)v;
+	} else if (*value == '0' && value[1] == 'x') {
+		if (sscanf(&value[2], "%x", (unsigned *)&intv) != 1) {
+			error("Bad hexadecimal value (%s)", value);
+			return;
+		}
+	} else {
+		if (sscanf(value, "%g", &v) != 1) {
+			error("Bad floating point value (%s)", value);
+			return;
+		}
+		intv = (int)(v * 0x7fffffff);
+	}
+
+	fle.consts.consts[n].gpr = gpr;
+	fle.consts.consts[n].value = intv;
+	fle.consts.nconst = n + 1;
+
+	add_init(gpr, intv, NULL);
+}
+
+static void
+compile_const(char **tokens, int cnt)
+{
+	CHECK_COUNT(tokens, cnt, 2, 3);
+	char *name = tokens[1];
+	char *value = tokens[2] ? tokens[2] : tokens[1];
+
+	if (ngpr >= MAX_GPR)
+		error("Too many GPR variables");
+
+	declare_const(ngpr, value);
+
+	add_symbol(name, SY_GPR, gpr_base + ngpr++);
+}
+
+static void
+compile_bool(char **tokens, int cnt)
+{
+	char *parm, *def;
+	int n, num;
+
+	CHECK_COUNT(tokens, cnt, 3, 3);
+
+	parm = tokens[1];
+	def = tokens[2];
+
+	n = fle.parms.ngpr;
+	if (n >= MAX_GPR_PARMS) {
+		error("Too many GPR parameters");
+		return;
+	}
+
+	if (sscanf(def, "%d", &num) != 1) {
+		error("Bad integer value near '%s'", def);
+		return;
+	}
+
+	(void) strcpy(fle.parms.gpr[n].name, parm);
+	fle.parms.gpr[n].num = ngpr;
+	fle.parms.gpr[n].def = num;
+	fle.parms.ngpr = n + 1;
+
+	add_init(ngpr, num, parm);
+
+	add_symbol(parm, SY_PARM, gpr_base + ngpr++);
+}
+
+static void
+compile_mono(char **tokens, int cnt)
+{
+	char *parm, *def;
+	int n, num;
+	char tmp[128];
+
+	CHECK_COUNT(tokens, cnt, 3, 3);
+
+	parm = tokens[1];
+	def = tokens[2];
+
+	n = fle.parms.ngpr;
+	if (n >= MAX_GPR_PARMS) {
+		error("Too many GPR parameters");
+		return;
+	}
+
+	if (sscanf(def, "%d", &num) != 1) {
+		error("Bad integer value near '%s'", def);
+		return;
+	}
+
+	(void) strcpy(fle.parms.gpr[n].name, parm);
+	fle.parms.gpr[n].num = ngpr;
+	fle.parms.gpr[n].def = num;
+	fle.parms.ngpr = n + 1;
+
+	add_init(ngpr, num, parm);
+
+	add_symbol(parm, SY_PARM, gpr_base + ngpr++);
+}
+
+static void
+compile_stereo(char **tokens, int cnt)
+{
+	char *parm, *def;
+	int n, num;
+	char tmp[128];
+
+	CHECK_COUNT(tokens, cnt, 3, 3);
+
+	parm = tokens[1];
+	def = tokens[2];
+
+	n = fle.parms.ngpr;
+	if (n >= MAX_GPR_PARMS) {
+		error("Too many GPR parameters");
+		return;
+	}
+
+	if (sscanf(def, "%d", &num) != 1) {
+		error("Bad integer value near '%s'", def);
+		return;
+	}
+
+	(void) strcpy(fle.parms.gpr[n].name, parm);
+	fle.parms.gpr[n].num = ngpr;
+	fle.parms.gpr[n].def = num | (num << 8);
+	fle.parms.ngpr = n + 1;
+
+	add_init(ngpr, num, parm);
+	add_init(ngpr + 1, num, NULL);
+
+	(void) sprintf(tmp, "%s_L", parm);
+	add_symbol(tmp, SY_PARM, gpr_base + ngpr++);
+	(void) sprintf(tmp, "%s_R", parm);
+	add_symbol(tmp, SY_PARM, gpr_base + ngpr++);
+}
+
+static void
+compile_input(char **tokens, int cnt)
+{
+	int num;
+
+	CHECK_COUNT(tokens, cnt, 3, 3);
+
+	if (sscanf(tokens[2], "%d", &num) != 1) {
+		error("Bad integer value near '%s'", tokens[2]);
+		return;
+	}
+
+	add_symbol(tokens[1], SY_INPUT, input_base + num);
+}
+
+static void
+compile_send(char **tokens, int cnt)
+{
+	int num;
+
+	CHECK_COUNT(tokens, cnt, 3, 3);
+
+	if (sscanf(tokens[2], "%d", &num) != 1) {
+		error("Bad integer near '%s'", tokens[2]);
+		return;
+	}
+
+	add_symbol(tokens[1], SY_FX, num);
+}
+
+static void
+compile_output(char **tokens, int cnt)
+{
+	int num;
+
+	CHECK_COUNT(tokens, cnt, 3, 3);
+
+	if (sscanf(tokens[2], "%d", &num) != 1) {
+		error("Bad integer value near '%s'", tokens[2]);
+		return;
+	}
+
+	add_symbol(tokens[1], SY_OUTPUT, output_base + num);
+}
+
+static void
+compile_directive(char **tokens, int cnt)
+{
+	if (strcmp(tokens[0], ".gpr") == 0) {
+		compile_gpr(tokens, cnt);
+		return;
+	}
+
+	if (strcmp(tokens[0], ".const") == 0) {
+		compile_const(tokens, cnt);
+		return;
+	}
+
+	if (strcmp(tokens[0], ".stereo") == 0) {
+		compile_stereo(tokens, cnt);
+		return;
+	}
+
+	if (strcmp(tokens[0], ".mono") == 0) {
+		compile_mono(tokens, cnt);
+		return;
+	}
+
+	if (strcmp(tokens[0], ".bool") == 0) {
+		compile_bool(tokens, cnt);
+		return;
+	}
+
+	if (strcmp(tokens[0], ".input") == 0) {
+		compile_input(tokens, cnt);
+		return;
+	}
+
+	if (strcmp(tokens[0], ".send") == 0) {
+		compile_send(tokens, cnt);
+		return;
+	}
+
+	if (strcmp(tokens[0], ".output") == 0) {
+		compile_output(tokens, cnt);
+		return;
+	}
+
+	if (strcmp(tokens[0], ".rem") == 0) {
+		compile_rem(tokens, cnt);
+		return;
+	}
+	if (strcmp(tokens[0], ".'") == 0) {
+		compile_rem(tokens, cnt);
+		return;
+	}
+
+	error("Unknown directive '%s'", tokens[0]);
+}
+
+static void
+compile_asm(char **tokens, int cnt)
+{
+	char *parms[4];
+	sym_t *symbols[4];
+#define	EMIT(o, r, a, x, y) \
+	fle.code[pc*2] =  ((x) << 10) | (y);			\
+	fle.code[pc*2+1] = ((o) << 20) | ((r) << 10) | a; pc++
+#define	EMIT_AUDIGY(o, r, a, x, y) \
+	fle.code[pc*2] =  ((x) << 12) | (y);			\
+	fle.code[pc*2+1] = ((o) << 24) | ((r) << 12) | a; pc++
+
+	int i, n = 0, nerr = 0;
+	int ninputs = 0;
+
+	CHECK_COUNT(tokens, cnt, 5, 5);
+
+	for (i = 0; i < 4; i++) {
+		if ((symbols[i] = find_symbol(tokens[i+1])) == NULL) {
+			(void) fprintf(stderr, "%s\n", tokens[i+1]);
+			nerr++;
+			error("Undefined symbol '%s'", tokens[i + 1]);
+			continue;
+		}
+
+		if (symbols[i]->type == SY_INPUT)
+			ninputs++;
+
+		if (symbols[i]->type == SY_ACCUM && i != 1)
+			error("Bad usage of 'accum' operand.");
+	}
+
+	if (nerr > 0)
+		return;
+
+	if (ninputs > 1) {
+		error("Attempt to access more than one input "
+		    "GPRs by the same instruction");
+	}
+
+	for (i = 0; instructions[i].name != NULL; i++)
+		if (strcasecmp(tokens[0], instructions[i].name) == 0)  {
+
+			if (is_audigy) {
+				EMIT_AUDIGY(instructions[i].opcode,
+				    symbols[0]->arg,
+				    symbols[1]->arg,
+				    symbols[2]->arg,
+				    symbols[3]->arg);
+			} else {
+				EMIT(instructions[i].opcode,
+				    symbols[0]->arg,
+				    symbols[1]->arg,
+				    symbols[2]->arg,
+				    symbols[3]->arg);
+			}
+
+			return;
+		}
+
+	error("Unrecognized instruction '%s'", tokens[0]);
+}
+
+static void
+init_compiler(void)
+{
+	char tmp[100];
+	int i;
+
+	(void) memset(&fle, 0, sizeof (fle));
+	/*
+	 * Initialize few predefined GPR parameter registers. These
+	 * definitions have to be in sync with the GPR_* macros in
+	 * <sblive.h>.
+	 */
+
+	/*
+	 * Make sure we start at gpr id 2 for now; 0 and 1 may be used
+	 * differently.
+	 */
+	add_symbol("NULL", SY_DUMMY, gpr_base + ngpr++);
+	add_symbol("NULL_", SY_DUMMY, gpr_base + ngpr++);
+
+	pc = 0;
+
+	if (is_audigy) {
+		/* Initialize the code array with NOPs (AUDIGY) */
+		for (i = 0; i < 512; i++) {
+			fle.code[i * 2 + 0] = (0xc0 << 12) | 0xc0;
+			fle.code[i * 2 + 1] =
+			    (0x06 << 24) | (0xc0 << 12) | 0xc0;
+		}
+
+		for (i = 0; i < 32; i++) {
+			(void) sprintf(tmp, "fx%d", i);
+			add_symbol(tmp, SY_FX, i);
+		}
+	} else {
+		/* Initialize the code array with NOPs (LIVE) */
+		for (i = 0; i < 512; i++) {
+			fle.code[i * 2 + 0] = 0x10040;
+			fle.code[i * 2 + 1] = 0x610040;
+		}
+
+		for (i = 0; i < 16; i++) {
+			(void) sprintf(tmp, "fx%d", i);
+			add_symbol(tmp, SY_FX, i);
+		}
+	}
+
+	/*
+	 * Constants
+	 */
+
+	if (is_audigy) {
+		/* Audigy symbols */
+		add_symbol("0", SY_CONST, 0x0c0);
+		add_symbol("1", SY_CONST, 0x0c1);
+		add_symbol("2", SY_CONST, 0x0c2);
+		add_symbol("3", SY_CONST, 0x0c3);
+		add_symbol("4", SY_CONST, 0x0c4);
+		add_symbol("8", SY_CONST, 0x0c5);
+		add_symbol("16", SY_CONST, 0x0c6);
+		add_symbol("32", SY_CONST, 0x0c7);
+		add_symbol("256", SY_CONST, 0x0c8);
+		add_symbol("65536", SY_CONST, 0x0c9);
+
+		add_symbol("2048", SY_CONST, 0x0ca);
+		add_symbol("0x800", SY_CONST, 0x0ca);
+
+		add_symbol("2^28", SY_CONST, 0x0cb);
+		add_symbol("0x10000000", SY_CONST, 0x0cb);
+
+		add_symbol("2^29", SY_CONST, 0x0cc);
+		add_symbol("0x20000000", SY_CONST, 0x0cc);
+
+		add_symbol("2^30", SY_CONST, 0x0cd);
+		add_symbol("0x40000000", SY_CONST, 0x0cd);
+
+		add_symbol("2^31", SY_CONST, 0x0ce);
+		add_symbol("0x80000000", SY_CONST, 0x0ce);
+
+		add_symbol("0x7fffffff", SY_CONST, 0x0cf);
+
+		add_symbol("0xffffffff", SY_CONST, 0x0d0);
+		add_symbol("-1", SY_CONST, 0x0d0);
+
+		add_symbol("0xfffffffe", SY_CONST, 0x0d1);
+		add_symbol("-2", SY_CONST, 0x0d1);
+
+		add_symbol("0xc0000000", SY_CONST, 0x0d2);
+
+		add_symbol("0x4f1bbcdc", SY_CONST, 0x0d3);
+
+		add_symbol("0x5a7ef9db", SY_CONST, 0x0d4);
+
+		add_symbol("0x100000", SY_CONST, 0x0d5);
+		add_symbol("accum", SY_ACCUM, 0x0d6);
+		add_symbol("CCR", SY_CONST, 0x0d7);
+
+		add_symbol("noise_L", SY_CONST, 0x0d8);
+		add_symbol("noise_R", SY_CONST, 0x0d9);
+		add_symbol("IRQREQ", SY_CONST, 0x0da);
+	} else {
+		/* SB Live symbols */
+		add_symbol("0", SY_CONST, 0x040);
+		add_symbol("1", SY_CONST, 0x041);
+		add_symbol("2", SY_CONST, 0x042);
+		add_symbol("3", SY_CONST, 0x043);
+		add_symbol("4", SY_CONST, 0x044);
+		add_symbol("8", SY_CONST, 0x045);
+		add_symbol("16", SY_CONST, 0x046);
+		add_symbol("32", SY_CONST, 0x047);
+		add_symbol("256", SY_CONST, 0x048);
+		add_symbol("65536", SY_CONST, 0x049);
+
+		add_symbol("2^23", SY_CONST, 0x04a);
+		add_symbol("0x80000", SY_CONST, 0x04a);
+
+		add_symbol("2^28", SY_CONST, 0x04b);
+		add_symbol("0x10000000", SY_CONST, 0x04b);
+
+		add_symbol("2^29", SY_CONST, 0x04c);
+		add_symbol("0x20000000", SY_CONST, 0x04c);
+
+		add_symbol("2^30", SY_CONST, 0x04d);
+		add_symbol("0x40000000", SY_CONST, 0x04d);
+
+		add_symbol("2^31", SY_CONST, 0x04e);
+		add_symbol("0x80000000", SY_CONST, 0x04e);
+
+		add_symbol("0x7fffffff", SY_CONST, 0x04f);
+
+		add_symbol("0xffffffff", SY_CONST, 0x050);
+		add_symbol("-1", SY_CONST, 0x050);
+
+		add_symbol("0xfffffffe", SY_CONST, 0x051);
+		add_symbol("-2", SY_CONST, 0x051);
+
+		add_symbol("accum", SY_ACCUM, 0x056);
+		add_symbol("CCR", SY_CONST, 0x057);
+
+		add_symbol("noise_L", SY_CONST, 0x058);
+		add_symbol("noise_R", SY_CONST, 0x059);
+		add_symbol("IRQREQ", SY_CONST, 0x05a);
+	}
+}
+
+static void
+produce_map(char *name)
+{
+	char fname[1024];
+	int i;
+	FILE *f;
+
+	if ((f = fopen(name, "w")) == NULL) {
+		perror(name);
+		return;
+	}
+
+	(void) fprintf(f, "%d\n", pc);
+
+	for (i = 0; i < nsyms; i++) {
+		(void) fprintf(f, "%04x %x %s\n",
+		    symtab[i].arg, symtab[i].type, symtab[i].name);
+	}
+
+	(void) fclose(f);
+	if (verbose) {
+		(void) fprintf(stderr,
+		    "No errors detected - Map written to %s\n", name);
+	}
+}
+
+static void
+produce_output(char *fname)
+{
+	int fd;
+
+	if ((fd = creat(fname, 0644)) == -1) {
+		perror(fname);
+		exit(-1);
+	}
+
+	if (write(fd, &fle, sizeof (fle)) != sizeof (fle)) {
+		perror(fname);
+		exit(-1);
+	}
+
+	if (verbose) {
+		(void) fprintf(stderr,
+		    "No errors detected - Binary written to %s\n",
+		    fname);
+	}
+
+	(void) close(fd);
+}
+
+static void
+produce_header(char *fname, char *prefix)
+{
+	FILE *f;
+	char *s;
+	char sname[MAXPATHLEN + 1];
+	char dname[MAXPATHLEN + 1];
+	int i;
+	clock_t now;
+	char when[128];
+
+	/* get basename */
+	if (prefix == NULL) {
+		s = strrchr(fname, '/');
+		s = (s == NULL) ? fname : s + 1;
+	} else {
+		s = prefix;
+	}
+	(void) strlcpy(sname, s, sizeof (sname));
+
+	/* strip off any extension */
+	s = strchr(sname, '.');
+	if (s != NULL) {
+		*s = 0;
+	}
+	if ((f = fopen(fname, "w")) == NULL) {
+		perror(fname);
+		return;
+	}
+
+	if (remarks[0] != 0) {
+		(void) fprintf(f, "/*\n%s */\n", remarks);
+	}
+	now = time(NULL);
+	strftime(when, sizeof (when), "%c", localtime(&now));
+	(void) fprintf(f, banner, progname, when);
+
+	(void) strlcpy(dname, prefix ? prefix : sname, sizeof (dname));
+	for (i = 0; dname[i]; i++) {
+		dname[i] = toupper(dname[i]);
+		if (!isalnum(dname[i])) {
+			dname[i] = '_';
+		}
+	}
+
+	for (i = 0; i < fle.parms.ngpr; i++) {
+		(void) fprintf(f, "#define\t%s_%s\t\t%d\n",
+		    dname, fle.parms.gpr[i].name, fle.parms.gpr[i].num);
+	}
+
+	(void) fprintf(f, "\n");
+
+	if (parms_only)
+		goto done;
+
+	(void) fprintf(f, "uint32_t %s_code[] = {\n", sname);
+
+	for (i = 0; i < pc * 2; i++) {
+		if (i == 0) {
+			(void) fprintf(f, "\t0x%08xU", fle.code[i]);
+		} else if ((i % 4) == 0) {
+			(void) fprintf(f, ",\n\t0x%08xU", fle.code[i]);
+		} else {
+			(void) fprintf(f, ", 0x%08xU", fle.code[i]);
+		}
+	}
+	(void) fprintf(f, "\n};\n");
+
+	(void) fprintf(f, "uint32_t %s_ninit = %d;\n", sname, fle.ninit);
+	(void) fprintf(f, "uint32_t %s_init[] = {\n", sname);
+
+	for (i = 0; i < fle.ninit; i++) {
+		if (fle.init[i].name[0]) {
+			(void) fprintf(f, "\t%u, 0x%x%s,\t/* %s */\n",
+			    fle.init[i].gpr, fle.init[i].value,
+			    fle.init[i].value >= 0x80000000U ? "U" : "",
+			    fle.init[i].name);
+		} else {
+			(void) fprintf(f, "\t%u, 0x%x%s,\n",
+			    fle.init[i].gpr, fle.init[i].value,
+			    fle.init[i].value >= 0x80000000U ? "U" : "");
+		}
+	}
+	(void) fprintf(f, "};\n");
+
+done:
+	(void) fclose(f);
+	if (verbose) {
+		(void) fprintf(stderr,
+		    "No errors detected - Header written to %s\n",
+		    fname);
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	char line[4096], *p, *s, *outfile;
+	char *iline;
+	int i;
+	FILE *input;
+	char *tokens[10];
+	int tokcnt;
+	char *mapfile = NULL;
+	char *header = NULL;
+	char *prefix = NULL;
+
+	outfile = NULL;
+	infile = NULL;
+	input = NULL;
+	progname = argv[0];
+
+	while ((i = getopt(argc, argv, "m:h:o:i:P:021v")) != EOF) {
+		switch (i) {
+		case 'o':
+			outfile = optarg;
+			break;
+		case 'i':
+			infile = strdup(optarg);
+			break;
+		case 'm':
+			mapfile = optarg;
+			break;
+		case 'P':
+			prefix = optarg;
+			break;
+		case 'h':
+			header = optarg;
+			break;
+		case '0':
+			parms_only = 1;
+			break;
+		case '2':
+			is_audigy = 1;
+			break;
+		case '1':
+			is_audigy = 0;
+			break;
+		case 'v':
+			verbose++;
+			break;
+		default:
+			(void) fprintf(stderr,
+			    "usage: %s [-m <map>] [-h <header>] "
+			    "[-o <binary>] [-i <source>] [-2|-1]",
+			    progname);
+			exit(-1);
+			break;
+		}
+	}
+
+	if ((outfile == NULL) && (mapfile == NULL) && (header == NULL)) {
+		outfile = "dsp.bin";
+	}
+
+	if (infile) {
+		input = fopen(infile, "r");
+		if (input == NULL) {
+			perror(infile);
+			exit(-1);
+		}
+	} else {
+		infile = strdup("<stdin>");
+		input = stdin;
+	}
+
+	if (is_audigy) {
+		gpr_base = 0x400;
+		input_base = 0x40;
+		output_base = 0x60;
+		if (verbose)
+			(void) fprintf(stderr, "Compiling for SB Audigy\n");
+	} else {
+		if (verbose)
+			(void) fprintf(stderr, "Compiling for SB Live\n");
+	}
+
+	init_compiler();
+
+	while ((tokcnt = getline(input, tokens)) != -1) {
+		/* skip empty lines */
+		if (tokcnt == 0) {
+			continue;
+		}
+
+		if (strcmp(tokens[0], "#") == 0) {
+			int	num;
+			if ((tokcnt >= 3) &&
+			    (sscanf(tokens[1], "%d", &num) == 1)) {
+				lineno = num;
+				free(infile);
+				infile = strdup(tokens[2]);
+				/* we don't want to count the # directive */
+				lineno--;
+			}
+
+			/* unknown # directive? muddle on... */
+			continue;
+		}
+		if (*tokens[0] == '.') {
+			compile_directive(tokens, tokcnt);
+		} else {
+			compile_asm(tokens, tokcnt);
+		}
+	}
+
+	if (lineno < 1) {
+		error("Empty input");
+	}
+
+	if (errors == 0) {
+		if (verbose) {
+			(void) fprintf(stderr,
+			    "%d instructions out of 512 assembled\n", pc);
+		}
+
+		if (outfile)
+			produce_output(outfile);
+		if (mapfile)
+			produce_map(mapfile);
+		if (header)
+			produce_header(header, prefix);
+	}
+
+	if (errors > 0) {
+		(void) fprintf(stderr, "%d errors - compile failed\n", errors);
+		exit(-1);
+	}
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/audio/drv/audioemu10k/dsp/emu10k.dsp	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,73 @@
+//
+// Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+// Use is subject to license terms.
+//
+// Copyright (C) 4Front Technologies 1996-2008.
+//
+// CDDL HEADER START
+//
+// The contents of this file are subject to the terms of the
+// Common Development and Distribution License (the "License").
+// You may not use this file except in compliance with the License.
+//
+// You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+// or http://www.opensolaris.org/os/licensing.
+// See the License for the specific language governing permissions
+// and limitations under the License.
+//
+// When distributing Covered Code, include this CDDL HEADER in each
+// file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+// If applicable, add the following below this CDDL HEADER, with the
+// fields enclosed by brackets "[]" replaced with your own identifying
+// information: Portions Copyright [yyyy] [name of copyright owner]
+//
+// CDDL HEADER END
+
+	// Master volume
+	.mono VOL_PCM		75
+	
+	// Monitor volumes
+	.stereo	MON_SPDIF1	0
+	.stereo	MON_SPDIF2	0
+	.stereo	MON_DIGCD	0
+	.stereo	MON_AUX2	0
+	.stereo	MON_LINE2	0
+	.stereo	MON_AC97	0
+
+	// Output levels for various channels
+	.stereo	VOL_FRONT	100
+	.stereo	VOL_SURR	100
+	.stereo	VOL_SIDE	100
+	.mono	VOL_CEN		100
+	.mono	VOL_LFE		100
+	.stereo	VOL_HEADPH	100
+
+	// Recording volume
+	.stereo VOL_REC		100
+
+	// Recording source enables
+	.bool	REC_SPDIF1	0
+	.bool	REC_SPDIF2	0
+	.bool	REC_DIGCD	0
+	.bool	REC_AC97	1
+	.bool	REC_AUX2	0
+	.bool	REC_LINE2	0
+	.bool	REC_PCM		0
+
+	// Sends
+	.send	FX_FRONT_L	0
+	.send	FX_FRONT_R	1
+	.send	FX_SURR_L	2
+	.send	FX_SURR_R	3
+	.send	FX_CEN		4
+	.send	FX_LFE		5
+	.send	FX_SIDE_L	6
+	.send	FX_SIDE_R	7
+	.send	FX_SPDIF_L	20
+	.send	FX_SPDIF_R	21
+
+#ifdef AUDIGY
+#include "emu10k2.mac"
+#else
+#include "emu10k1.mac"
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/audio/drv/audioemu10k/dsp/emu10k1.mac	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,154 @@
+//
+// Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+// Use is subject to license terms.
+//
+// Copyright (C) 4Front Technologies 1996-2008.
+//
+// CDDL HEADER START
+//
+// The contents of this file are subject to the terms of the
+// Common Development and Distribution License (the "License").
+// You may not use this file except in compliance with the License.
+//
+// You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+// or http://www.opensolaris.org/os/licensing.
+// See the License for the specific language governing permissions
+// and limitations under the License.
+//
+// When distributing Covered Code, include this CDDL HEADER in each
+// file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+// If applicable, add the following below this CDDL HEADER, with the
+// fields enclosed by brackets "[]" replaced with your own identifying
+// information: Portions Copyright [yyyy] [name of copyright owner]
+//
+// CDDL HEADER END
+
+	// Constants for EMU 10k1 (SB Live)
+
+	// Inputs
+	.input IN_AC97_L	0
+	.input IN_AC97_R	1
+	.input IN_DIGCD_L	2
+	.input IN_DIGCD_R	3
+	.input IN_ZV_L		4
+	.input IN_ZV_R		5
+	.input IN_SPDIF1_L	6	// TOSLink
+	.input IN_SPDIF1_R	7
+	.input IN_LINE2_L	8	// LiveDrive (Line/Mic In 1)
+	.input IN_LINE2_R	9
+	.input IN_SPDIF2_L	10	// LiveDrive (Coax S/PDIF input)
+	.input IN_SPDIF2_R	11
+	.input IN_AUX2_L	12	// LiveDrive (Line/Mic 2)
+	.input IN_AUX2_R	13
+
+	// Outputs
+	.output OUT_FRONT_L	0	// via AC'97
+	.output OUT_FRONT_R	1	// via AC'97
+	.output OUT_SPDIF_L	2
+	.output OUT_SPDIF_R	3
+	.output OUT_DCENTER	4	// Digital Center channel
+	.output OUT_DLFE	5	// Digital LFE
+	.output OUT_HEADPH_L	6	// LiveDrive headphone out
+	.output OUT_HEADPH_R	7
+	.output OUT_SURR_L	8	// Rear output
+	.output OUT_SURR_R	9
+	.output OUT_ADC_L	10	// Send to the ADC recording channel
+	.output OUT_ADC_R	11
+	.output OUT_MICREC	12	// Send to the mic recording buffer
+	.output OUT_AC97SURR_L	13	// AC97 Surround L
+	.output OUT_AC97SURR_R	14	// AC97 Surround R
+
+	.output OUT_ACENTER	17	// Analog center channel
+	.output OUT_ALFE	18	// Analog LFE output
+
+	// Temporaries
+	.gpr PCM_FRONT_L
+	.gpr PCM_FRONT_R
+	.gpr PCM_SURR_L
+	.gpr PCM_SURR_R
+	.gpr PCM_CEN
+	.gpr PCM_LFE
+	.gpr PCM_REC_L
+	.gpr PCM_REC_R
+
+	// Code
+
+	// Load up the PCM inputs.  We multiply each of them by 4, as
+	// otherwise they are too quiet.
+	MACINTS(PCM_FRONT_L, 0 FX_FRONT_L, 4)
+	MACINTS(PCM_FRONT_R, 0 FX_FRONT_R, 4)
+	MACINTS(PCM_SURR_L, 0, FX_SURR_L, 4)
+	MACINTS(PCM_SURR_R, 0, FX_SURR_R, 4)
+	MACINTS(PCM_CEN, 0, FX_CEN, 4)
+	MACINTS(PCM_LFE, 0, FX_LFE, 4)
+
+	// Apply PCM (wave) volume
+	MACS(PCM_FRONT_L, 0, PCM_FRONT_L, VOL_PCM)
+	MACS(PCM_FRONT_R, 0, PCM_FRONT_R, VOL_PCM)
+	MACS(PCM_SURR_L, 0, PCM_SURR_L, VOL_PCM)
+	MACS(PCM_SURR_R, 0, PCM_SURR_R, VOL_PCM)
+	MACS(PCM_CEN, 0, PCM_CEN, VOL_PCM)
+	MACS(PCM_LFE, 0, PCM_LFE, VOL_PCM)
+
+	// Mix any monitor sources into the front PCM
+	// AC'97 (includes Line-In, analog CD, and Mic)
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_AC97_L, MON_AC97_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_AC97_R, MON_AC97_R)
+	// DIGCD
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_DIGCD_L, MON_DIGCD_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_DIGCD_R, MON_DIGCD_R)
+	// SPDIF1
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_SPDIF1_L, MON_SPDIF1_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_SPDIF1_R, MON_SPDIF1_R)
+	// SPDIF2
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_SPDIF2_L, MON_SPDIF2_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_SPDIF2_R, MON_SPDIF2_R)
+	// Line2/Mic2 (Live! Drive)
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_LINE2_L, MON_LINE2_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_LINE2_R, MON_LINE2_R)
+	// Aux2 (Live! Drive)
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_AUX2_L, MON_AUX2_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_AUX2_R, MON_AUX2_R)
+
+	// Outputs
+	MACS(OUT_FRONT_L, 0, PCM_FRONT_L, VOL_FRONT_L)
+	MACS(OUT_FRONT_R, 0, PCM_FRONT_R, VOL_FRONT_R)
+	MACS(OUT_SPDIF_L, 0, PCM_FRONT_L, VOL_FRONT_L)
+	MACS(OUT_SPDIF_R, 0, PCM_FRONT_R, VOL_FRONT_R)
+	MACS(OUT_HEADPH_L, 0, PCM_FRONT_L, VOL_HEADPH_L)
+	MACS(OUT_HEADPH_R, 0, PCM_FRONT_R, VOL_HEADPH_R)
+	MACS(OUT_SURR_L, 0, PCM_SURR_L, VOL_SURR_L)
+	MACS(OUT_SURR_R, 0, PCM_SURR_R, VOL_SURR_R)
+	MACS(OUT_AC97SURR_L, 0, PCM_SURR_L, VOL_SURR_L)
+	MACS(OUT_AC97SURR_R, 0, PCM_SURR_R, VOL_SURR_R)
+	MACS(OUT_DCENTER, 0, PCM_CEN, VOL_CEN)
+	MACS(OUT_ACENTER, 0, PCM_CEN, VOL_CEN)
+	MACS(OUT_DLFE, 0, PCM_LFE, VOL_LFE)
+	MACS(OUT_ALFE, 0, PCM_LFE, VOL_LFE)
+
+	// Inputs (Recording) -- the source variables are treated as
+	// simple boolean enables.
+	MACINTS(PCM_REC_L, 0, IN_AC97_L, REC_AC97)
+	MACINTS(PCM_REC_R, 0, IN_AC97_R, REC_AC97)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_DIGCD_L, REC_DIGCD)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_DIGCD_R, REC_DIGCD)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_SPDIF1_L, REC_SPDIF1)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_SPDIF1_R, REC_SPDIF1)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_SPDIF2_L, REC_SPDIF2)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_SPDIF2_R, REC_SPDIF2)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_AUX2_L, REC_AUX2)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_AUX2_R, REC_AUX2)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_LINE2_L, REC_LINE2)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_LINE2_R, REC_LINE2)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, PCM_FRONT_L, REC_PCM)
+	MACINTS(PCM_REC_R, PCM_REC_R, PCM_FRONT_R, REC_PCM)
+
+	// Apply master record gain
+	MACS(OUT_ADC_L, 0, PCM_REC_L, VOL_REC_L)
+	MACS(OUT_ADC_R, 0, PCM_REC_R, VOL_REC_R)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/audio/drv/audioemu10k/dsp/emu10k2.mac	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,159 @@
+//
+// Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+// Use is subject to license terms.
+//
+// Copyright (C) 4Front Technologies 1996-2008.
+//
+// CDDL HEADER START
+//
+// The contents of this file are subject to the terms of the
+// Common Development and Distribution License (the "License").
+// You may not use this file except in compliance with the License.
+//
+// You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+// or http://www.opensolaris.org/os/licensing.
+// See the License for the specific language governing permissions
+// and limitations under the License.
+//
+// When distributing Covered Code, include this CDDL HEADER in each
+// file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+// If applicable, add the following below this CDDL HEADER, with the
+// fields enclosed by brackets "[]" replaced with your own identifying
+// information: Portions Copyright [yyyy] [name of copyright owner]
+//
+// CDDL HEADER END
+
+	// Constants for EMU 10k2 (SB Audigy)
+
+	// Inputs
+	.input IN_AC97_L	0
+	.input IN_AC97_R	1
+	.input IN_DIGCD_L	2
+	.input IN_DIGCD_R	3
+	.input IN_SPDIF1_L	4	// TOSLink
+	.input IN_SPDIF1_R	5
+	.input IN_MYSTERY_L	6
+	.input IN_MYSTERY_R	7
+	.input IN_LINE2_L	8	// LiveDrive
+	.input IN_LINE2_R	9
+	.input IN_SPDIF2_L	10	// Coaxial SPDIF
+	.input IN_SPDIF2_R	11
+	.input IN_AUX2_L	12	// LiveDrive
+	.input IN_AUX2_R	13
+
+	// Outputs
+	.output OUT_SPDIF_L	0
+	.output OUT_SPDIF_R	1
+	.output OUT_DCENTER	2	// Digital Center channel
+	.output OUT_DLFE	3	// Digital LFE
+	.output OUT_HEADPH_L	4	// LiveDrive headphone out
+	.output OUT_HEADPH_R	5
+	.output OUT_DSURR_L	6	// Surround output (digital)
+	.output OUT_DSURR_R	7
+	.output OUT_FRONT_L	8
+	.output OUT_FRONT_R	9
+	.output OUT_ACENTER	10	// Analog center channel
+	.output OUT_ALFE	11	// Analog LFE output
+	.output OUT_SIDE_L	12	// Side surround
+	.output OUT_SIDE_R	13
+	.output OUT_SURR_L	14	// Surround output
+	.output OUT_SURR_R	15
+	.output OUT_AC97_L	16	// Send to the AC97 front channel
+	.output OUT_AC97_R	17
+	.output OUT_ADC_L	22	// Send to the ADC recording channel
+	.output OUT_ADC_R	23
+	.output OUT_MICREC_L	24	// Send to the mic recording buffer
+	.output OUT_MICREC_R	25	// ??????? (maybe not in use at all)
+
+	// Temporaries
+	.gpr PCM_FRONT_L
+	.gpr PCM_FRONT_R
+	.gpr PCM_SURR_L
+	.gpr PCM_SURR_R
+	.gpr PCM_SIDE_L
+	.gpr PCM_SIDE_R
+	.gpr PCM_CEN
+	.gpr PCM_LFE
+	.gpr PCM_REC_L
+	.gpr PCM_REC_R
+	.gpr SPDIF_DELAY
+
+	// Code
+
+	// Load up the PCM inputs.
+	// We apply the PCM volume at the same time.
+	MACS(PCM_FRONT_L, 0, FX_FRONT_L, VOL_PCM)
+	MACS(PCM_FRONT_R, 0, FX_FRONT_R, VOL_PCM)
+	MACS(PCM_SURR_L, 0, FX_SURR_L, VOL_PCM)
+	MACS(PCM_SURR_R, 0, FX_SURR_R, VOL_PCM)
+	MACS(PCM_SIDE_L, 0, FX_SIDE_L, VOL_PCM)
+	MACS(PCM_SIDE_R, 0, FX_SIDE_R, VOL_PCM)
+	MACS(PCM_CEN, 0, FX_CEN, VOL_PCM)
+	MACS(PCM_LFE, 0, FX_LFE, VOL_PCM)
+
+	// Mix any monitor sources into the front PCM
+	// AC'97 (includes Line-In, analog CD, and Mic)
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_AC97_L, MON_AC97_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_AC97_R, MON_AC97_R)
+	// DIGCD
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_DIGCD_L, MON_DIGCD_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_DIGCD_R, MON_DIGCD_R)
+	// SPDIF1
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_SPDIF1_L, MON_SPDIF1_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_SPDIF1_R, MON_SPDIF1_R)
+	// SPDIF2
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_SPDIF2_L, MON_SPDIF2_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_SPDIF2_R, MON_SPDIF2_R)
+	// Line2/Mic2 (Live! Drive)
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_LINE2_L, MON_LINE2_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_LINE2_R, MON_LINE2_R)
+	// Aux2 (Live! Drive)
+	MACS(PCM_FRONT_L, PCM_FRONT_L, IN_AUX2_L, MON_AUX2_L)
+	MACS(PCM_FRONT_R, PCM_FRONT_R, IN_AUX2_R, MON_AUX2_R)
+
+	// Outputs
+	MACS(OUT_FRONT_L, 0, PCM_FRONT_L, VOL_FRONT_L)
+	MACS(OUT_FRONT_R, 0, PCM_FRONT_R, VOL_FRONT_R)
+	MACS(OUT_SPDIF_L, 0, PCM_FRONT_L, VOL_FRONT_L)
+	// delay SPDIF right channel one sample to fix a bug
+	ACC3(OUT_SPDIF_R, 0, 0, SPDIF_DELAY)
+	MACS(SPDIF_DELAY, 0, PCM_FRONT_R, VOL_FRONT_R)
+	MACS(OUT_HEADPH_L, 0, PCM_FRONT_L, VOL_HEADPH_L)
+	MACS(OUT_HEADPH_R, 0, PCM_FRONT_R, VOL_HEADPH_R)
+	MACS(OUT_SURR_L, 0, PCM_SURR_L, VOL_SURR_L)
+	MACS(OUT_SURR_R, 0, PCM_SURR_R, VOL_SURR_R)
+	MACS(OUT_DSURR_L, 0, PCM_SURR_L, VOL_SURR_L)
+	MACS(OUT_DSURR_R, 0, PCM_SURR_R, VOL_SURR_R)
+	MACS(OUT_SIDE_L, 0, PCM_SIDE_L, VOL_SIDE_L)
+	MACS(OUT_SIDE_R, 0, PCM_SIDE_R, VOL_SIDE_R)
+	MACS(OUT_ACENTER, 0, PCM_CEN, VOL_CEN)
+	MACS(OUT_DCENTER, 0, PCM_CEN, VOL_CEN)
+	MACS(OUT_ALFE, 0, PCM_LFE, VOL_LFE)
+	MACS(OUT_DLFE, 0, PCM_LFE, VOL_LFE)
+
+	// Inputs (Recording) -- the source variables are treated as
+	// simple boolean enables.
+	MACINTS(PCM_REC_L, PCM_REC_R, IN_AC97_L, REC_AC97)
+	MACINTS(PCM_REC_R, PCM_REC_L, IN_AC97_R, REC_AC97)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_DIGCD_L, REC_DIGCD)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_DIGCD_R, REC_DIGCD)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_SPDIF1_L, REC_SPDIF1)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_SPDIF1_R, REC_SPDIF1)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_SPDIF2_L, REC_SPDIF2)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_SPDIF2_R, REC_SPDIF2)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_AUX2_L, REC_AUX2)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_AUX2_R, REC_AUX2)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, IN_LINE2_L, REC_LINE2)
+	MACINTS(PCM_REC_R, PCM_REC_R, IN_LINE2_R, REC_LINE2)
+
+	MACINTS(PCM_REC_L, PCM_REC_L, PCM_FRONT_L, REC_PCM)
+	MACINTS(PCM_REC_R, PCM_REC_R, PCM_FRONT_R, REC_PCM)
+
+	// Apply master record gain
+	MACS(OUT_ADC_L, 0, PCM_REC_L, VOL_REC_L)
+	MACS(OUT_ADC_R, 0, PCM_REC_R, VOL_REC_R)
--- a/usr/src/uts/common/sys/audio/ac97.h	Fri Oct 30 10:52:42 2009 +0800
+++ b/usr/src/uts/common/sys/audio/ac97.h	Thu Oct 29 21:38:34 2009 -0700
@@ -515,9 +515,15 @@
 #define	AC97_VENDOR_EMC			0x454d4300	/* eMicro */
 #define	AC97_VENDOR_EV			0x000f8300	/* Ectiva */
 #define	AC97_VENDOR_ESS			0x45838300	/* ESS */
+#define	AC97_VENDOR_HRS			0x48525300	/* Intersil */
 #define	AC97_VENDOR_ICE			0x49434500	/* ICEnsemble */
+#define	AC97_VENDOR_ITE			0x49544500	/* ITE */
+#define	AC97_VENDOR_NSC			0x4e534300	/* National */
+#define	AC97_VENDOR_PSC			0x50534300	/* Philips */
+#define	AC97_VENDOR_SIL			0x53494c00	/* Silicon Labs */
 #define	AC97_VENDOR_ST			0x83847600	/* SigmaTel */
 #define	AC97_VENDOR_TRA			0x54524100	/* TriTech */
+#define	AC97_VENDOR_TXN			0x54584e00	/* TI */
 #define	AC97_VENDOR_VIA			0x56494100	/* VIA */
 #define	AC97_VENDOR_WML			0x574d4c00	/* Wolfson */
 #define	AC97_VENDOR_YMH			0x594d4800	/* Yamaha */
@@ -567,6 +573,7 @@
 #define	AC97_CODEC_ES1921		0x45838308
 #define	AC97_CODEC_EV1938		0x000f8384
 #define	AC97_CODEC_ICE1232		0x49434511
+#define	AC97_CODEC_LM4550		0x4e534350
 #define	AC97_CODEC_STAC9700		0x83847600
 #define	AC97_CODEC_STAC9701		0x83847601
 #define	AC97_CODEC_STAC9701_2		0xc250c250
--- a/usr/src/uts/intel/Makefile.intel.shared	Fri Oct 30 10:52:42 2009 +0800
+++ b/usr/src/uts/intel/Makefile.intel.shared	Thu Oct 29 21:38:34 2009 -0700
@@ -200,6 +200,7 @@
 DRV_KMODS	+= audio1575
 DRV_KMODS	+= audio810
 DRV_KMODS	+= audiocmi
+DRV_KMODS	+= audioemu10k
 DRV_KMODS	+= audioens
 DRV_KMODS	+= audiohd
 DRV_KMODS	+= audioixp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/audioemu10k/Makefile	Thu Oct 29 21:38:34 2009 -0700
@@ -0,0 +1,104 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/intel/audioemu10k/Makefile
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#	This makefile drives the production of the audioemu10k driver.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= audioemu10k
+OBJECTS		= $(AUDIOEMU10K_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(AUDIOEMU10K_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+DSP_SRCDIR	= $(UTSBASE)/common/io/audio/drv/audioemu10k/dsp
+DSP_HNAMES	= emu10k_gpr.h emu10k1_dsp.h emu10k2_dsp.h
+DSP_HDRS	= $(DSP_HNAMES:%=$(OBJS_DIR)/%)
+DSP_SNAMES	= emu10k.dsp emu10k1.mac emu10k2.mac
+DSP_SRCS	= $(DSP_SNAMES:%=$(DSP_SRCDIR)/%)
+ASM10K		= $(OBJS_DIR)/asm10k
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+LDFLAGS		+= -dy -Ndrv/audio -Nmisc/ac97
+
+$(OBJS_DIR)/%.o			:= CPPFLAGS += -I$(OBJS_DIR)
+$(OBJS_DIR)/%.ln		:= CPPFLAGS += -I$(OBJS_DIR)
+$(OBJS_DIR)/emu10k_gpr.h	:= ASM10KFLAGS = -v -0 -P gpr
+$(OBJS_DIR)/emu10k1_dsp.h	:= ASM10KFLAGS = -v -1 -P emu10k1
+$(OBJS_DIR)/emu10k2_dsp.h	:= MODEL10K = SBLIVE
+$(OBJS_DIR)/emu10k2_dsp.h	:= ASM10KFLAGS = -v -2 -P emu10k2
+$(OBJS_DIR)/emu10k2_dsp.h	:= MODEL10K = AUDIGY
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+$(BINARY):	$(OBJS_DIR)/asm10k $(DSP_HDRS)
+
+$(ASM10K):	$(DSP_SRCDIR)/asm10k.c
+	$(CC) $(CFLAGS) -o $@ $(DSP_SRCDIR)/asm10k.c
+
+$(DSP_HDRS): $(ASM10K) $(DSP_SRCS)
+	$(CPP) -D$(MODEL10K) -I$(DSP_SRCDIR) $(DSP_SRCDIR)/emu10k.dsp | \
+	$(OBJS_DIR)/asm10k $(ASM10KFLAGS) -h $@
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ