changeset 9831:60c34c50144c

PSARC 2009/263 audiocmi driver 6510438 sol x86 audio should support CMedia chipset
author Garrett D'Amore <gdamore@opensolaris.org>
date Tue, 09 Jun 2009 11:09:06 -0700
parents eed00643bbdd
children 3569b6c7f56c
files usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNWaudiocmi/Makefile usr/src/pkgdefs/SUNWaudiocmi/depend usr/src/pkgdefs/SUNWaudiocmi/pkginfo.tmpl usr/src/pkgdefs/SUNWaudiocmi/postinstall.tmpl usr/src/pkgdefs/SUNWaudiocmi/preremove.tmpl usr/src/pkgdefs/SUNWaudiocmi/prototype_com usr/src/pkgdefs/SUNWaudiocmi/prototype_i386 usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/audiocmi/Makefile
diffstat 14 files changed, 1926 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/pkgdefs/Makefile	Tue Jun 09 07:24:43 2009 -0700
+++ b/usr/src/pkgdefs/Makefile	Tue Jun 09 11:09:06 2009 -0700
@@ -118,6 +118,7 @@
 	SUNWatheros \
 	SUNWatigfx \
 	SUNWatu \
+	SUNWaudiocmi \
 	SUNWaudiohd \
 	SUNWcakr.i \
 	SUNWcakrx.i \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudiocmi/Makefile	Tue Jun 09 11:09:06 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/SUNWaudiocmi/depend	Tue Jun 09 11:09:06 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/SUNWaudiocmi/pkginfo.tmpl	Tue Jun 09 11:09:06 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="SUNWaudiocmi"
+NAME="C-Media 8738 Family Audio Driver"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+CATEGORY="system"
+DESC="SunOS audio device driver for C-Media 8738"
+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/SUNWaudiocmi/postinstall.tmpl	Tue Jun 09 11:09:06 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.
+#
+# SUNWaudiocmi postinstall script
+
+include drv_utils
+
+AUDIOCMI_ALIASES="\
+	\"pci13f6,111\"	\
+	\"pci13f6,100\"	\
+	\"pci13f6,101\"	\
+	"
+
+pkg_drvadd -i "${AUDIOCMI_ALIASES}" audiocmi || exit 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudiocmi/preremove.tmpl	Tue Jun 09 11:09:06 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.
+#
+# SUNWaudiocmi preremove script
+
+include drv_utils
+
+pkg_drvrem audiocmi || exit 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWaudiocmi/prototype_com	Tue Jun 09 11:09:06 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
+#
+# SUNWaudiocmi
+#
+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/SUNWaudiocmi/prototype_i386	Tue Jun 09 11:09:06 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
+#
+# SUNWaudiocmi
+#
+f none kernel/drv/audiocmi 755 root sys
+d none kernel/drv/amd64 755 root sys
+f none kernel/drv/amd64/audiocmi 755 root sys
--- a/usr/src/uts/common/Makefile.files	Tue Jun 09 07:24:43 2009 -0700
+++ b/usr/src/uts/common/Makefile.files	Tue Jun 09 11:09:06 2009 -0700
@@ -434,6 +434,8 @@
 
 AUDIO810_OBJS += audio810.o
 
+AUDIOCMI_OBJS += audiocmi.o
+
 AUDIOHD_OBJS +=	audiohd.o
 
 AUDIOIXP_OBJS += audioixp.o
--- a/usr/src/uts/common/Makefile.rules	Tue Jun 09 07:24:43 2009 -0700
+++ b/usr/src/uts/common/Makefile.rules	Tue Jun 09 11:09:06 2009 -0700
@@ -556,6 +556,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/audio/drv/audiocmi/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/audio/drv/audiohd/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1761,6 +1765,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/audio/drv/audio810/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/audio/drv/audiocmi/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/audio/drv/audioens/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.c	Tue Jun 09 11:09:06 2009 -0700
@@ -0,0 +1,1206 @@
+/*
+ * 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: Driver for CMEDIA CM8738 PCI audio controller.
+ */
+/*
+ * This file is part of Open Sound System
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This software is released under CDDL 1.0 source license.
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#include <sys/audio/audio_driver.h>
+#include <sys/note.h>
+#include <sys/pci.h>
+#include "audiocmi.h"
+
+/*
+ * Note: The original 4Front driver had support SPDIF, dual dac, and
+ * multichannel surround options.  However, we haven't got any cards
+ * with these advanced features available on them for testing, so
+ * we're just going to support basic analog stereo for now.
+ *
+ * Adding back support for the advanced features would be an
+ * interesting project for someone with access to suitable hardware.
+ *
+ * Note that each variant (CMI 8338, 8738-033, -037, -055, and 8768)
+ * seems to have significant differences in some of the registers.
+ * While programming these parts for basic stereo is pretty much the
+ * same on all parts, doing anything more than that can be
+ * sigificantly different for each part.
+ */
+
+static ddi_device_acc_attr_t acc_attr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_STRUCTURE_LE_ACC,
+	DDI_STRICTORDER_ACC
+};
+
+static ddi_device_acc_attr_t buf_attr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_NEVERSWAP_ACC,
+	DDI_STRICTORDER_ACC
+};
+
+static ddi_dma_attr_t dma_attr = {
+	DMA_ATTR_VERSION,	/* dma_attr_version */
+	0x0,			/* dma_attr_addr_lo */
+	0xffffffffU,		/* dma_attr_addr_hi */
+	0x3ffff,		/* dma_attr_count_max */
+	0x8,			/* dma_attr_align */
+	0x7f,			/* dma_attr_burstsizes */
+	0x1,			/* dma_attr_minxfer */
+	0x3ffff,		/* dma_attr_maxxfer */
+	0x3ffff,		/* dma_attr_seg */
+	0x1,			/* dma_attr_sgllen */
+	0x1,			/* dma_attr_granular */
+	0			/* dma_attr_flags */
+};
+
+static uint_t
+cmpci_intr(caddr_t arg1, caddr_t arg2)
+{
+	cmpci_dev_t	*dev = (void *)arg1;
+
+	uint32_t	intstat, intctrl, intclear;
+	void		(*cb0)(audio_engine_t *) = NULL;
+	void		(*cb1)(audio_engine_t *) = NULL;
+	uint_t		rv;
+
+	_NOTE(ARGUNUSED(arg2));
+
+	rv = DDI_INTR_UNCLAIMED;
+
+	mutex_enter(&dev->mutex);
+	if (dev->suspended) {
+		mutex_exit(&dev->mutex);
+		return (rv);
+	}
+
+	intclear = 0;
+	intstat = GET32(dev, REG_INTSTAT);
+	intctrl = GET32(dev, REG_INTCTRL);
+	if ((intstat & INTSTAT_CH0_INT) && (intctrl & INTCTRL_CH0_EN)) {
+		intclear |= INTCTRL_CH0_EN;
+		cb0 = dev->port[0].callb;
+	}
+	if ((intstat & INTSTAT_CH1_INT) && (intctrl & INTCTRL_CH1_EN)) {
+		intclear |= INTCTRL_CH1_EN;
+		cb1 = dev->port[1].callb;
+	}
+
+	/* toggle the bits that we are going to handle */
+	if (intclear) {
+		CLR32(dev, REG_INTCTRL, intclear);
+		SET32(dev, REG_INTCTRL, intclear);
+		rv = DDI_INTR_CLAIMED;
+
+		KSINTR(dev)->intrs[KSTAT_INTR_HARD]++;
+	}
+
+	mutex_exit(&dev->mutex);
+
+	if (cb0) {
+		(*cb0)(dev->port[0].engine);
+	}
+	if (cb1) {
+		(*cb1)(dev->port[1].engine);
+	}
+
+	return (rv);
+}
+
+static void
+cmpci_reset_port(cmpci_port_t *port)
+{
+	cmpci_dev_t *dev = port->dev;
+
+	if (dev->suspended)
+		return;
+
+	port->offset = 0;
+
+	/* reset channel */
+	SET32(dev, REG_FUNCTRL0, port->fc0_rst_bit);
+	drv_usecwait(10);
+	CLR32(dev, REG_FUNCTRL0, port->fc0_rst_bit);
+	drv_usecwait(10);
+
+	/* Set 48k 16-bit stereo -- these are just with all bits set. */
+	SET32(dev, REG_FUNCTRL1, port->fc1_rate_mask);
+	SET32(dev, REG_CHFORMAT, port->chformat_mask);
+
+	PUT32(dev, port->reg_paddr, port->paddr);
+	PUT16(dev, port->reg_bufsz, port->nframes - 1);
+	PUT16(dev, port->reg_fragsz, port->fragfr - 1);
+
+	/* Analog output */
+	if (port->capture) {
+		/* Analog capture */
+		SET32(dev, REG_FUNCTRL0, port->fc0_rec_bit);
+	} else {
+		CLR32(dev, REG_FUNCTRL0, port->fc0_rec_bit);
+	}
+}
+
+static void
+cmpci_start_port(cmpci_port_t *port)
+{
+	cmpci_dev_t	*dev = port->dev;
+
+	if (dev->suspended)
+		return;
+
+	SET32(dev, REG_FUNCTRL0, port->fc0_en_bit);
+	SET32(dev, REG_INTCTRL, port->int_en_bit);
+}
+
+static void
+cmpci_stop_port(cmpci_port_t *port)
+{
+	cmpci_dev_t	*dev = port->dev;
+
+	if (dev->suspended)
+		return;
+
+	CLR32(dev, REG_FUNCTRL0, port->fc0_en_bit);
+	CLR32(dev, REG_INTCTRL, port->int_en_bit);
+}
+
+static int
+cmpci_open(void *arg, int flag, uint_t *fragfrp, uint_t *nfp, caddr_t *bufp)
+{
+	cmpci_port_t *port = arg;
+	cmpci_dev_t *dev = port->dev;
+	int intrs;
+
+	mutex_enter(&dev->mutex);
+
+	if (flag & ENGINE_INPUT) {
+		intrs = dev->rintrs;
+	} else {
+		intrs = dev->pintrs;
+	}
+
+	/*
+	 * Calculate fragfr, nfrags, buf.
+	 *
+	 * 48 as minimum is chosen to ensure that we will have at
+	 * least 4 fragments.  512 is just an arbitrary limit, and at
+	 * the smallest frame size will result in no more than 176
+	 * fragments.
+	 */
+	intrs = min(512, max(48, intrs));
+
+	port->fragfr = (48000 / intrs);
+	port->nfrags = CMPCI_BUF_LEN / (port->fragfr * 4);
+	port->nframes = port->nfrags * port->fragfr;
+	port->count = 0;
+	port->bufsz = port->nframes * 4;
+
+	*fragfrp = port->fragfr;
+	*nfp = port->nfrags;
+	*bufp = port->kaddr;
+
+	port->open = B_TRUE;
+
+	cmpci_reset_port(port);
+	cmpci_start_port(port);
+
+	mutex_exit(&dev->mutex);
+	return (0);
+}
+
+static void
+cmpci_close(void *arg)
+{
+	cmpci_port_t *port = arg;
+	cmpci_dev_t *dev = port->dev;
+
+	mutex_enter(&dev->mutex);
+	port->open = B_FALSE;
+	cmpci_stop_port(port);
+	mutex_exit(&dev->mutex);
+}
+
+static void
+cmpci_update_port(cmpci_port_t *port)
+{
+	cmpci_dev_t	*dev = port->dev;
+	uint32_t	count;
+	uint32_t	offset;
+
+	if ((dev->suspended) || (!port->open))
+		return;
+
+	offset = GET32(dev, port->reg_paddr) - port->paddr;
+
+	/* check for wrap */
+	if (offset < port->offset) {
+		count = (port->bufsz - port->offset) + offset;
+	} else {
+		count = offset - port->offset;
+	}
+	port->count += count;
+	port->offset = offset;
+}
+
+static uint64_t
+cmpci_count(void *arg)
+{
+	cmpci_port_t	*port = arg;
+	cmpci_dev_t	*dev = port->dev;
+	uint64_t	count;
+
+	mutex_enter(&dev->mutex);
+	cmpci_update_port(port);
+
+	/* 4 is from 16-bit stereo */
+	count = port->count / 4;
+	mutex_exit(&dev->mutex);
+
+	/* NB: 2 because each sample is 2 bytes wide */
+	return (count);
+}
+
+
+static int
+cmpci_setup_interrupts(cmpci_dev_t *dev)
+{
+	int actual;
+	uint_t ipri;
+
+	if ((ddi_intr_alloc(dev->dip, &dev->ihandle, DDI_INTR_TYPE_FIXED,
+	    0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
+	    (actual != 1)) {
+		audio_dev_warn(dev->adev, "can't alloc intr handle");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_intr_get_pri(dev->ihandle, &ipri) != DDI_SUCCESS) {
+		audio_dev_warn(dev->adev,  "can't determine intr priority");
+		(void) ddi_intr_free(dev->ihandle);
+		dev->ihandle = NULL;
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_intr_add_handler(dev->ihandle, cmpci_intr, dev,
+	    NULL) != DDI_SUCCESS) {
+		audio_dev_warn(dev->adev, "can't add intr handler");
+		(void) ddi_intr_free(dev->ihandle);
+		dev->ihandle = NULL;
+		return (DDI_FAILURE);
+	}
+
+	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
+
+	return (DDI_SUCCESS);
+}
+
+
+#define	MASK(nbits)	((1 << (nbits)) - 1)
+#define	SCALE(val, nbits)	\
+	((uint8_t)((((val) * MASK(nbits)) / 100)) << (8 - (nbits)))
+
+#define	LEFT(dev, ctl)	min(((dev->controls[ctl].value) >> 8), 100)
+#define	RIGHT(dev, ctl)	min(((dev->controls[ctl].value) & 0xff), 100)
+#define	MONO(dev, ctl)	min(dev->controls[ctl].value, 100)
+
+static void
+cmpci_setmixer(cmpci_dev_t *dev, uint8_t idx, uint8_t val)
+{
+	PUT8(dev, REG_IDXADDR, idx);
+	PUT8(dev, REG_IDXDATA, val);
+}
+
+static uint8_t
+cmpci_getmixer(cmpci_dev_t *dev, uint8_t idx)
+{
+	PUT8(dev, REG_IDXADDR, idx);
+	return (GET8(dev, REG_IDXDATA));
+}
+
+
+static void
+cmpci_configure_mixer(cmpci_dev_t *dev)
+{
+	uint64_t	left, right;
+	uint8_t		outmix;
+	uint8_t		inmix[2];
+	uint64_t	recsrcs;
+	uint64_t	monsrcs;
+
+	if (dev->suspended)
+		return;
+
+	/* reset all mix values */
+	outmix = inmix[0] = inmix[1] = 0;
+
+	outmix = OUTMIX_MIC |
+	    OUTMIX_CD_R | OUTMIX_CD_L | OUTMIX_LINE_R | OUTMIX_LINE_L;
+
+	inmix[0] = INMIX_LINE_L | INMIX_CD_L | INMIX_MIC;
+	inmix[1] = INMIX_LINE_R | INMIX_CD_R | INMIX_MIC;
+
+	recsrcs = dev->controls[CTL_RECSRCS].value;
+	monsrcs = dev->controls[CTL_MONSRCS].value;
+
+	/* program PCM volume */
+	left = MONO(dev, CTL_VOLUME);
+	if (left) {
+		/* left and right are the same */
+		cmpci_setmixer(dev, IDX_VOICE_LEFT, SCALE(left, 5));
+		cmpci_setmixer(dev, IDX_VOICE_RIGHT, SCALE(left, 5));
+		CLR8(dev, REG_MIX2, MIX2_WSMUTE);
+	} else {
+		cmpci_setmixer(dev, IDX_VOICE_LEFT, 0);
+		cmpci_setmixer(dev, IDX_VOICE_RIGHT, 0);
+		SET8(dev, REG_MIX2, MIX2_WSMUTE);
+	}
+
+	left = LEFT(dev, CTL_LINEOUT);
+	right = RIGHT(dev, CTL_LINEOUT);
+
+	/* lineout/master volume - no separate mute */
+	cmpci_setmixer(dev, IDX_MASTER_LEFT, SCALE(left, 5));
+	cmpci_setmixer(dev, IDX_MASTER_RIGHT, SCALE(right, 5));
+
+	/* speaker volume - mute in extension register, but we don't use */
+	left = MONO(dev, CTL_SPEAKER);
+	cmpci_setmixer(dev, IDX_SPEAKER, SCALE(left, 2));
+
+	/* mic gain */
+	left = MONO(dev, CTL_MIC);
+	if (left) {
+		cmpci_setmixer(dev, IDX_MIC, SCALE(left, 5));
+		/* set record mic gain */
+		uint8_t v = GET8(dev, REG_MIX3);
+		v &= ~(0x7 << 1);
+		v |= ((left * 7) / 100) << 1;
+		PUT8(dev, REG_MIX3, v);
+		cmpci_setmixer(dev, 0x3f, SCALE(100, 2));
+		cmpci_setmixer(dev, 0x40, SCALE(100, 2));
+	} else {
+		cmpci_setmixer(dev, IDX_MIC, 0);
+		outmix &= ~OUTMIX_MIC;
+		inmix[0] &= ~INMIX_MIC;
+		inmix[1] &= ~INMIX_MIC;
+	}
+
+	/* line in */
+	left = LEFT(dev, CTL_LINEOUT);
+	right = RIGHT(dev, CTL_LINEOUT);
+	if (left) {
+		cmpci_setmixer(dev, IDX_LINEIN_LEFT, SCALE(left, 5));
+	} else {
+		cmpci_setmixer(dev, IDX_LINEIN_LEFT, 0);
+		inmix[0] &= ~INMIX_LINE_L;
+		outmix &= ~OUTMIX_LINE_L;
+	}
+	if (right) {
+		cmpci_setmixer(dev, IDX_LINEIN_RIGHT, SCALE(left, 5));
+	} else {
+		cmpci_setmixer(dev, IDX_LINEIN_RIGHT, 0);
+		inmix[1] &= ~INMIX_LINE_R;
+		outmix &= ~OUTMIX_LINE_R;
+	}
+
+	/* cd */
+	left = LEFT(dev, CTL_CD);
+	right = RIGHT(dev, CTL_CD);
+	if (left) {
+		cmpci_setmixer(dev, IDX_CDDA_LEFT, SCALE(left, 5));
+	} else {
+		cmpci_setmixer(dev, IDX_CDDA_LEFT, 0);
+		inmix[0] &= ~INMIX_CD_L;
+		outmix &= ~OUTMIX_CD_L;
+	}
+	if (right) {
+		cmpci_setmixer(dev, IDX_CDDA_RIGHT, SCALE(left, 5));
+	} else {
+		cmpci_setmixer(dev, IDX_CDDA_RIGHT, 0);
+		inmix[1] &= ~INMIX_CD_R;
+		outmix &= ~OUTMIX_CD_R;
+	}
+
+	/* aux - trickier because it doesn't use regular sbpro mixer */
+	left = LEFT(dev, CTL_AUX);
+	right = RIGHT(dev, CTL_AUX);
+	PUT8(dev, REG_VAUX, (((left * 15) / 100) << 4) | ((right * 15) / 100));
+	/* maybe enable recording */
+	if ((left || right) && (recsrcs & (1 << SRC_LINE))) {
+		SET8(dev, REG_MIX3, MIX3_RAUXREN | MIX3_RAUXLEN);
+	} else {
+		CLR8(dev, REG_MIX3, MIX3_RAUXREN | MIX3_RAUXLEN);
+	}
+	/* maybe enable monitoring */
+	if ((left || right) && (monsrcs & (1 << SRC_AUX))) {
+		CLR8(dev, REG_MIX3, MIX3_VAUXRM | MIX3_VAUXLM);
+	} else {
+		SET8(dev, REG_MIX3, MIX3_VAUXRM | MIX3_VAUXLM);
+	}
+
+	/* now do the recsrcs */
+	if ((recsrcs & (1 << SRC_MIC)) == 0) {
+		inmix[0] &= ~INMIX_MIC;
+		inmix[1] &= ~INMIX_MIC;
+	}
+	if ((recsrcs & (1 << SRC_LINE)) == 0) {
+		inmix[0] &= ~INMIX_LINE_L;
+		inmix[1] &= ~INMIX_LINE_R;
+	}
+	if ((recsrcs & (1 << SRC_CD)) == 0) {
+		inmix[0] &= ~INMIX_CD_L;
+		inmix[1] &= ~INMIX_CD_R;
+	}
+	if (recsrcs & (1 << SRC_MIX)) {
+		SET8(dev, REG_MIX2, MIX2_WAVEIN_L | MIX2_WAVEIN_R);
+	} else {
+		CLR8(dev, REG_MIX2, MIX2_WAVEIN_L | MIX2_WAVEIN_R);
+	}
+	cmpci_setmixer(dev, IDX_INMIX_L, inmix[0]);
+	cmpci_setmixer(dev, IDX_INMIX_R, inmix[1]);
+
+	/* now the monsrcs */
+	if ((monsrcs & (1 << SRC_MIC)) == 0) {
+		outmix &= ~OUTMIX_MIC;
+	}
+	if ((monsrcs & (1 << SRC_LINE)) == 0) {
+		outmix &= ~(OUTMIX_LINE_L | OUTMIX_LINE_R);
+	}
+	if ((monsrcs & (1 << SRC_CD)) == 0) {
+		outmix &= ~(OUTMIX_CD_L | OUTMIX_CD_R);
+	}
+	cmpci_setmixer(dev, IDX_OUTMIX, outmix);
+
+	/* micboost */
+	if (dev->controls[CTL_MICBOOST].value != 0) {
+		CLR8(dev, REG_MIX3, MIX3_MICGAINZ);
+		cmpci_setmixer(dev, IDX_EXTENSION,
+		    cmpci_getmixer(dev, IDX_EXTENSION) & ~0x1);
+	} else {
+		SET8(dev, REG_MIX3, MIX3_MICGAINZ);
+		cmpci_setmixer(dev, IDX_EXTENSION,
+		    cmpci_getmixer(dev, IDX_EXTENSION) | 0x1);
+	}
+}
+
+static int
+cmpci_set_ctrl(void *arg, uint64_t val)
+{
+	cmpci_ctrl_t *cc = arg;
+	cmpci_dev_t *dev = cc->dev;
+
+	/*
+	 * We don't bother to check for valid values - a bogus value
+	 * will give incorrect volumes, but is otherwise harmless.
+	 */
+	mutex_enter(&dev->mutex);
+	cc->value = val;
+	cmpci_configure_mixer(dev);
+	mutex_exit(&dev->mutex);
+
+	return (0);
+}
+
+static int
+cmpci_get_ctrl(void *arg, uint64_t *val)
+{
+	cmpci_ctrl_t *cc = arg;
+	cmpci_dev_t *dev = cc->dev;
+
+	mutex_enter(&dev->mutex);
+	*val = cc->value;
+	mutex_exit(&dev->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	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
+#define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
+#define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
+
+static void
+cmpci_alloc_ctrl(cmpci_dev_t *dev, uint32_t num, uint64_t val)
+{
+	audio_ctrl_desc_t	desc;
+	cmpci_ctrl_t		*cc;
+
+	cc = &dev->controls[num];
+	bzero(&desc, sizeof (desc));
+	cc->dev = dev;
+
+	switch (num) {
+	case CTL_VOLUME:
+		desc.acd_name = AUDIO_CTRL_ID_VOLUME;
+		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
+		desc.acd_minvalue = 0;
+		desc.acd_maxvalue = 100;
+		desc.acd_flags = PCMVOL;
+		break;
+
+	case CTL_LINEOUT:
+		desc.acd_name = AUDIO_CTRL_ID_LINEOUT;
+		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
+		desc.acd_minvalue = 0;
+		desc.acd_maxvalue = 100;
+		desc.acd_flags = MAINVOL;
+		break;
+
+	case CTL_SPEAKER:
+		desc.acd_name = AUDIO_CTRL_ID_SPEAKER;
+		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
+		desc.acd_minvalue = 0;
+		desc.acd_maxvalue = 100;
+		desc.acd_flags = MAINVOL;
+		break;
+
+	case CTL_MIC:
+		desc.acd_name = AUDIO_CTRL_ID_MIC;
+		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
+		desc.acd_minvalue = 0;
+		desc.acd_maxvalue = 100;
+		desc.acd_flags = RECVOL;
+		break;
+
+	case CTL_LINEIN:
+		desc.acd_name = AUDIO_CTRL_ID_LINEIN;
+		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
+		desc.acd_minvalue = 0;
+		desc.acd_maxvalue = 100;
+		desc.acd_flags = RECVOL;
+		break;
+
+	case CTL_CD:
+		desc.acd_name = AUDIO_CTRL_ID_CD;
+		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
+		desc.acd_minvalue = 0;
+		desc.acd_maxvalue = 100;
+		desc.acd_flags = RECVOL;
+		break;
+
+	case CTL_AUX:
+		desc.acd_name = AUDIO_CTRL_ID_AUX1IN;
+		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
+		desc.acd_minvalue = 0;
+		desc.acd_maxvalue = 100;
+		desc.acd_flags = RECVOL;
+		break;
+
+	case CTL_RECSRCS:
+		desc.acd_name = AUDIO_CTRL_ID_RECSRC;
+		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
+		desc.acd_enum[SRC_MIC] = AUDIO_PORT_MIC;
+		desc.acd_enum[SRC_LINE] = AUDIO_PORT_LINEIN;
+		desc.acd_enum[SRC_CD] = AUDIO_PORT_CD;
+		desc.acd_enum[SRC_AUX] = AUDIO_PORT_AUX1IN;
+		desc.acd_enum[SRC_MIX] = AUDIO_PORT_STEREOMIX;
+		desc.acd_minvalue = (1 << (SRC_MIX + 1)) - 1;
+		desc.acd_maxvalue = desc.acd_minvalue;
+		desc.acd_flags = RECCTL | AUDIO_CTRL_FLAG_MULTI;
+		break;
+
+	case CTL_MONSRCS:
+		desc.acd_name = AUDIO_CTRL_ID_MONSRC;
+		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
+		desc.acd_enum[SRC_MIC] = AUDIO_PORT_MIC;
+		desc.acd_enum[SRC_LINE] = AUDIO_PORT_LINEIN;
+		desc.acd_enum[SRC_CD] = AUDIO_PORT_CD;
+		desc.acd_enum[SRC_AUX] = AUDIO_PORT_AUX1IN;
+		desc.acd_minvalue = ((1 << (SRC_AUX + 1)) - 1);
+		desc.acd_maxvalue = desc.acd_minvalue;
+		desc.acd_flags = MONCTL | AUDIO_CTRL_FLAG_MULTI;
+		break;
+
+	case CTL_MICBOOST:
+		desc.acd_name = AUDIO_CTRL_ID_MICBOOST;
+		desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN;
+		desc.acd_minvalue = 0;
+		desc.acd_maxvalue = 1;
+		desc.acd_flags = RECCTL;
+		break;
+	}
+
+	cc->value = val;
+	cc->ctrl = audio_dev_add_control(dev->adev, &desc,
+	    cmpci_get_ctrl, cmpci_set_ctrl, cc);
+}
+
+static void
+cmpci_add_controls(cmpci_dev_t *dev)
+{
+	cmpci_alloc_ctrl(dev, CTL_VOLUME, 75);
+	cmpci_alloc_ctrl(dev, CTL_LINEOUT, 90 | (90 << 8));
+	cmpci_alloc_ctrl(dev, CTL_SPEAKER, 75);
+	cmpci_alloc_ctrl(dev, CTL_MIC, 32);
+	cmpci_alloc_ctrl(dev, CTL_LINEIN, 64 | (64 << 8));
+	cmpci_alloc_ctrl(dev, CTL_CD, 75 | (75 << 8));
+	cmpci_alloc_ctrl(dev, CTL_AUX, 75 | (75 << 8));
+	cmpci_alloc_ctrl(dev, CTL_RECSRCS, (1 << SRC_MIC));
+	cmpci_alloc_ctrl(dev, CTL_MONSRCS, 0);
+	cmpci_alloc_ctrl(dev, CTL_MICBOOST, 0);
+}
+
+static void
+cmpci_del_controls(cmpci_dev_t *dev)
+{
+	for (int i = 0; i < CTL_NUM; i++) {
+		if (dev->controls[i].ctrl) {
+			audio_dev_del_control(dev->controls[i].ctrl);
+			dev->controls[i].ctrl = NULL;
+		}
+	}
+}
+
+static void
+cmpci_reset(cmpci_dev_t *dev)
+{
+	/* Full reset */
+	SET32(dev, REG_MISC, MISC_RESET);
+	(void) GET32(dev, REG_MISC);
+	drv_usecwait(100);
+	CLR32(dev, REG_MISC, MISC_RESET);
+
+	/* reset all channels */
+	PUT32(dev, REG_FUNCTRL0, 0);
+
+	/* disable interrupts and such */
+	CLR32(dev, REG_FUNCTRL0, FUNCTRL0_CH0_EN | FUNCTRL0_CH1_EN);
+	CLR32(dev, REG_INTCTRL, INTCTRL_CH0_EN | INTCTRL_CH1_EN);
+
+	/* disable uart, joystick in Function Control Reg1 */
+	CLR32(dev, REG_FUNCTRL1, FUNCTRL1_UART_EN | FUNCTRL1_JYSTK_EN);
+
+	/*
+	 * Set DAC and ADC rates to 48 kHz - note that both rates have
+	 * all bits set in them, so we can do this with a simple "set".
+	 */
+	SET32(dev, REG_FUNCTRL1,
+	    FUNCTRL1_DAC_RATE_48K | FUNCTRL1_ADC_RATE_48K);
+
+	/* Set 16-bit stereo -- also these are just with all bits set. */
+	SET32(dev, REG_CHFORMAT, CHFORMAT_CH0_16ST | CHFORMAT_CH1_16ST);
+}
+
+static int
+cmpci_format(void *unused)
+{
+	_NOTE(ARGUNUSED(unused));
+	return (AUDIO_FORMAT_S16_LE);
+}
+
+static int
+cmpci_channels(void *unused)
+{
+	_NOTE(ARGUNUSED(unused));
+	return (2);
+}
+
+static int
+cmpci_rate(void *unused)
+{
+	_NOTE(ARGUNUSED(unused));
+	return (48000);
+}
+
+static void
+cmpci_sync(void *arg, unsigned nframes)
+{
+	cmpci_port_t *port = arg;
+
+	_NOTE(ARGUNUSED(nframes));
+
+	(void) ddi_dma_sync(port->dmah, 0, 0, port->sync_dir);
+}
+
+static size_t
+cmpci_qlen(void *unused)
+{
+	_NOTE(ARGUNUSED(unused));
+
+	return (0);
+}
+
+audio_engine_ops_t cmpci_engine_ops = {
+	AUDIO_ENGINE_VERSION,		/* version number */
+	cmpci_open,
+	cmpci_close,
+	NULL,		/* start */
+	NULL,		/* stop */
+	cmpci_count,
+	cmpci_format,
+	cmpci_channels,
+	cmpci_rate,
+	cmpci_sync,
+	cmpci_qlen,
+	NULL,
+};
+
+static int
+cmpci_init(cmpci_dev_t *dev)
+{
+	audio_dev_t	*adev = dev->adev;
+
+	dev->pintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip,
+	    DDI_PROP_DONTPASS, "play-interrupts", DEFINTS);
+
+	dev->rintrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip,
+	    DDI_PROP_DONTPASS, "record-interrupts", DEFINTS);
+
+	for (int i = 0; i < PORT_MAX; i++) {
+
+		cmpci_port_t *port;
+		unsigned dmaflags;
+		unsigned caps;
+		size_t rlen;
+		ddi_dma_cookie_t c;
+		unsigned ccnt;
+
+		port = &dev->port[i];
+		port->dev = dev;
+		port->num = i;
+
+		/*
+		 * Channel 0 is recording channel, unless we are in
+		 * dual DAC mode.  The reason for this is simple --
+		 * only channel "B" (which I presume to mean channel
+		 * 1) supports multichannel configuration.
+		 *
+		 * However, if we're going to use SPDIF recording,
+		 * then recording *must* occur on channel 1.  Yes, the
+		 * hardware is "strange".
+		 */
+
+		switch (i) {
+		case 0:
+			caps = ENGINE_INPUT_CAP;
+			dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
+			port->callb = audio_engine_produce;
+			port->reg_paddr = REG_CH0_PADDR;
+			port->reg_bufsz = REG_CH0_BUFSZ;
+			port->reg_fragsz = REG_CH0_FRAGSZ;
+			port->fc0_rst_bit = FUNCTRL0_CH0_RST;
+			port->fc0_rec_bit = FUNCTRL0_CH0_REC;
+			port->fc0_en_bit = FUNCTRL0_CH0_EN;
+			port->int_en_bit = INTCTRL_CH0_EN;
+			port->sync_dir = DDI_DMA_SYNC_FORKERNEL;
+			port->capture = B_TRUE;
+			port->fc1_rate_mask = FUNCTRL1_ADC_RATE_48K;
+			port->chformat_mask = CHFORMAT_CH0_16ST;
+			break;
+
+		case 1:
+			caps = ENGINE_OUTPUT_CAP;
+			dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
+			port->callb = audio_engine_consume;
+			port->reg_paddr = REG_CH1_PADDR;
+			port->reg_bufsz = REG_CH1_BUFSZ;
+			port->reg_fragsz = REG_CH1_FRAGSZ;
+			port->fc0_rst_bit = FUNCTRL0_CH1_RST;
+			port->fc0_rec_bit = FUNCTRL0_CH1_REC;
+			port->fc0_en_bit = FUNCTRL0_CH1_EN;
+			port->int_en_bit = INTCTRL_CH1_EN;
+			port->sync_dir = DDI_DMA_SYNC_FORDEV;
+			port->capture = B_FALSE;
+			port->fc1_rate_mask = FUNCTRL1_DAC_RATE_48K;
+			port->chformat_mask = CHFORMAT_CH1_16ST;
+			break;
+		}
+
+		if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_DONTWAIT,
+		    NULL, &port->dmah) != DDI_SUCCESS) {
+			audio_dev_warn(adev, "ch%d: dma hdl alloc failed", i);
+			return (DDI_FAILURE);
+		}
+		if (ddi_dma_mem_alloc(port->dmah, CMPCI_BUF_LEN, &buf_attr,
+		    DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL, &port->kaddr,
+		    &rlen, &port->acch) != DDI_SUCCESS) {
+			audio_dev_warn(adev, "ch%d: dma mem allcoc failed", i);
+			return (DDI_FAILURE);
+		}
+		bzero(port->kaddr, rlen);
+
+		if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr,
+		    rlen, dmaflags, DDI_DMA_DONTWAIT, NULL, &c, &ccnt) !=
+		    DDI_DMA_MAPPED) {
+			audio_dev_warn(adev, "ch%d: dma bind failed", i);
+			return (DDI_FAILURE);
+		}
+		port->paddr = c.dmac_address;
+
+		port->engine = audio_engine_alloc(&cmpci_engine_ops, caps);
+		if (port->engine == NULL) {
+			audio_dev_warn(adev, "ch%d: alloc engine failed", i);
+			return (DDI_FAILURE);
+		}
+		audio_engine_set_private(port->engine, port);
+		audio_dev_add_engine(adev, port->engine);
+	}
+
+	cmpci_add_controls(dev);
+
+	dev->ksp = kstat_create(ddi_driver_name(dev->dip),
+	    ddi_get_instance(dev->dip), ddi_driver_name(dev->dip),
+	    "controller", KSTAT_TYPE_INTR, 1, KSTAT_FLAG_PERSISTENT);
+	if (dev->ksp != NULL) {
+		kstat_install(dev->ksp);
+	}
+
+	cmpci_reset(dev);
+	cmpci_configure_mixer(dev);
+
+	if (audio_dev_register(adev) != DDI_SUCCESS) {
+		audio_dev_warn(adev, "audio_dev_register failed");
+		return (DDI_FAILURE);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+void
+cmpci_destroy(cmpci_dev_t *dev)
+{
+	if (dev->ihandle != NULL) {
+		(void) ddi_intr_disable(dev->ihandle);
+		(void) ddi_intr_remove_handler(dev->ihandle);
+		(void) ddi_intr_free(dev->ihandle);
+		mutex_destroy(&dev->mutex);
+	}
+
+	if (dev->ksp != NULL) {
+		kstat_delete(dev->ksp);
+	}
+
+	/* free up ports, including DMA resources for ports */
+	for (int i = 0; i < PORT_MAX; i++) {
+		cmpci_port_t	*port = &dev->port[i];
+
+		if (port->paddr != 0)
+			(void) ddi_dma_unbind_handle(port->dmah);
+		if (port->acch != NULL)
+			ddi_dma_mem_free(&port->acch);
+		if (port->dmah != NULL)
+			ddi_dma_free_handle(&port->dmah);
+
+		if (port->engine != NULL) {
+			audio_dev_remove_engine(dev->adev, port->engine);
+			audio_engine_free(port->engine);
+		}
+	}
+
+	if (dev->acch != NULL) {
+		ddi_regs_map_free(&dev->acch);
+	}
+
+	cmpci_del_controls(dev);
+
+	if (dev->adev != NULL) {
+		audio_dev_free(dev->adev);
+	}
+
+	kmem_free(dev, sizeof (*dev));
+}
+
+int
+cmpci_attach(dev_info_t *dip)
+{
+	uint16_t		vendor, device;
+	cmpci_dev_t		*dev;
+	ddi_acc_handle_t	pcih;
+	audio_dev_t		*adev;
+	uint32_t		val;
+
+	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
+		audio_dev_warn(NULL, "pci_config_setup failed");
+		return (DDI_FAILURE);
+	}
+
+	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
+	device = pci_config_get16(pcih, PCI_CONF_DEVID);
+
+	if (vendor != CMEDIA_VENDOR_ID ||
+	    ((device != CMEDIA_CM8738) && (device != CMEDIA_CM8338A) &&
+	    (device != CMEDIA_CM8338B))) {
+		pci_config_teardown(&pcih);
+		audio_dev_warn(NULL, "device not recognized");
+		return (DDI_FAILURE);
+	}
+
+	/* enable IO and Master accesses */
+	pci_config_put16(pcih, PCI_CONF_COMM,
+	    pci_config_get16(pcih, PCI_CONF_COMM) |
+	    PCI_COMM_MAE | PCI_COMM_IO);
+
+	pci_config_teardown(&pcih);
+
+	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
+	dev->dip = dip;
+
+	ddi_set_driver_private(dip, dev);
+
+	if ((adev = audio_dev_alloc(dip, 0)) == NULL) {
+		goto err_exit;
+	}
+	dev->adev = adev;
+
+	if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr,
+	    &dev->acch) != DDI_SUCCESS) {
+		audio_dev_warn(adev, "can't map registers");
+		goto err_exit;
+	}
+
+	/* setup some initial values */
+	audio_dev_set_description(adev, "C-Media PCI Audio");
+	switch (device) {
+	case CMEDIA_CM8738:
+		/* Crazy 8738 detection scheme */
+		val = GET32(dev, REG_INTCTRL) & 0x1F000000;
+		if (val == 0) {
+
+			if (GET32(dev, REG_CHFORMAT & 0x1F000000)) {
+				audio_dev_set_version(adev, "CM8738-037");
+			} else {
+				audio_dev_set_version(adev, "CM8738-033");
+			}
+		} else if (val & 0x0C000000) {
+			audio_dev_set_version(adev, "CMI8768");
+		} else if (val & 0x08000000) {
+			audio_dev_set_version(adev, "CMI8738-055");
+		} else if (val & 0x04000000) {
+			audio_dev_set_version(adev, "CMI8738-039");
+		} else {
+			audio_dev_set_version(adev, "CMI8738");
+		}
+		break;
+
+	case CMEDIA_CM8338A:
+		audio_dev_set_version(dev->adev, "CM8338A");
+		break;
+
+	case CMEDIA_CM8338B:
+		audio_dev_set_version(dev->adev, "CM8338B");
+		break;
+	}
+
+	if (cmpci_setup_interrupts(dev) != DDI_SUCCESS) {
+		audio_dev_warn(dev->adev, "can't register interrupts");
+		goto err_exit;
+	}
+
+	if (cmpci_init(dev) != DDI_SUCCESS) {
+		audio_dev_warn(dev->adev, "can't init device");
+		goto err_exit;
+	}
+
+	(void) ddi_intr_enable(dev->ihandle);
+	return (DDI_SUCCESS);
+
+err_exit:
+	cmpci_destroy(dev);
+	return (DDI_FAILURE);
+}
+
+static int
+cmpci_resume(cmpci_dev_t *dev)
+{
+	audio_engine_reset(dev->port[0].engine);
+	audio_engine_reset(dev->port[1].engine);
+
+	mutex_enter(&dev->mutex);
+	dev->suspended = B_FALSE;
+
+	cmpci_reset(dev);
+	/* wait one millisecond, to give reset a chance to get up */
+	drv_usecwait(1000);
+
+	cmpci_configure_mixer(dev);
+
+	for (int i = 0; i < PORT_MAX; i++) {
+		cmpci_port_t *port = &dev->port[i];
+
+		cmpci_reset_port(port);
+		if (port->open) {
+			cmpci_start_port(port);
+		}
+	}
+	mutex_exit(&dev->mutex);
+	return (DDI_SUCCESS);
+}
+
+static int
+cmpci_detach(cmpci_dev_t *dev)
+{
+	if (audio_dev_unregister(dev->adev) != DDI_SUCCESS)
+		return (DDI_FAILURE);
+
+	mutex_enter(&dev->mutex);
+
+	/* disable interrupts */
+	CLR32(dev, REG_INTCTRL, INTCTRL_CH1_EN | INTCTRL_CH0_EN);
+
+	/* disable channels */
+	PUT32(dev, REG_FUNCTRL0, 0);
+
+	mutex_exit(&dev->mutex);
+
+	cmpci_destroy(dev);
+
+	return (DDI_SUCCESS);
+}
+
+static int
+cmpci_suspend(cmpci_dev_t *dev)
+{
+	mutex_enter(&dev->mutex);
+
+	cmpci_update_port(&dev->port[0]);
+	cmpci_stop_port(&dev->port[0]);
+
+	cmpci_update_port(&dev->port[1]);
+	cmpci_stop_port(&dev->port[1]);
+
+	dev->suspended = B_TRUE;
+	mutex_exit(&dev->mutex);
+
+	return (DDI_SUCCESS);
+}
+
+static int
+cmpci_quiesce(dev_info_t *dip)
+{
+	cmpci_dev_t	*dev;
+
+	if ((dev = ddi_get_driver_private(dip)) == NULL) {
+		return (DDI_FAILURE);
+	}
+
+	/* disable interrupts */
+	PUT32(dev, REG_INTCTRL, 0);
+
+	/* disable channels */
+	PUT32(dev, REG_FUNCTRL0, 0);
+
+	return (DDI_SUCCESS);
+}
+
+static int
+cmpci_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	cmpci_dev_t *dev;
+
+	switch (cmd) {
+	case DDI_ATTACH:
+		return (cmpci_attach(dip));
+
+	case DDI_RESUME:
+		if ((dev = ddi_get_driver_private(dip)) == NULL) {
+			return (DDI_FAILURE);
+		}
+		return (cmpci_resume(dev));
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+static int
+cmpci_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	cmpci_dev_t *dev;
+
+	if ((dev = ddi_get_driver_private(dip)) == NULL) {
+		return (DDI_FAILURE);
+	}
+
+	switch (cmd) {
+	case DDI_DETACH:
+		return (cmpci_detach(dev));
+
+	case DDI_SUSPEND:
+		return (cmpci_suspend(dev));
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+static struct dev_ops cmpci_dev_ops = {
+	DEVO_REV,		/* rev */
+	0,			/* refcnt */
+	NULL,			/* getinfo */
+	nulldev,		/* identify */
+	nulldev,		/* probe */
+	cmpci_ddi_attach,	/* attach */
+	cmpci_ddi_detach,	/* detach */
+	nodev,			/* reset */
+	NULL,			/* cb_ops */
+	NULL,			/* bus_ops */
+	NULL,			/* power */
+	cmpci_quiesce,		/* quiesce */
+};
+
+static struct modldrv cmpci_modldrv = {
+	&mod_driverops,			/* drv_modops */
+	"C-Media PCI Audio",		/* linkinfo */
+	&cmpci_dev_ops,			/* dev_ops */
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	{ &cmpci_modldrv, NULL }
+};
+
+int
+_init(void)
+{
+	int	rv;
+
+	audio_init_ops(&cmpci_dev_ops, "audiocmi");
+	if ((rv = mod_install(&modlinkage)) != 0) {
+		audio_fini_ops(&cmpci_dev_ops);
+	}
+	return (rv);
+}
+
+int
+_fini(void)
+{
+	int	rv;
+	if ((rv = mod_remove(&modlinkage)) == 0) {
+		audio_fini_ops(&cmpci_dev_ops);
+	}
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/audio/drv/audiocmi/audiocmi.h	Tue Jun 09 11:09:06 2009 -0700
@@ -0,0 +1,324 @@
+/*
+ * 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: Driver for CMEDIA CM8738 PCI audio controller.
+ */
+/*
+ * This file is part of Open Sound System
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This software is released under CDDL 1.0 source license.
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#ifndef	_AUDIOCMI_H
+#define	_AUDIOCMI_H
+
+#define	CMEDIA_VENDOR_ID	0x13F6
+#define	CMEDIA_CM8738		0x0111
+#define	CMEDIA_CM8338A		0x0100
+#define	CMEDIA_CM8338B		0x0101
+
+/*
+ * CM8338 registers definition
+ */
+
+#define	REG_FUNCTRL0		0x00
+#define	REG_FUNCTRL1		0x04
+#define	REG_CHFORMAT		0x08
+#define	REG_INTCTRL		0x0C
+#define	REG_INTSTAT		0x10
+#define	REG_LEGACY		0x14
+#define	REG_MISC		0x18
+#define	REG_TDMAPOS		0x1C
+#define	REG_SBVER		0x20	/* 8 bit access only */
+#define	REG_IDXDATA		0x22	/* 8 bit access only */
+#define	REG_IDXADDR		0x23	/* 8 bit access only */
+#define	REG_MIX2		0x24
+#define	REG_MIX3		0x25
+#define	REG_VAUX		0x26
+#define	REG_CH0_PADDR		0x80	/* buffer address (32b) */
+#define	REG_CH0_BUFSZ		0x84	/* buffer size in samples (16b) */
+#define	REG_CH0_FRAGSZ		0x86	/* fragment size in samples (16b) */
+#define	REG_CH1_PADDR		0x88
+#define	REG_CH1_BUFSZ		0x8C
+#define	REG_CH1_FRAGSZ		0x8E
+
+#define	FUNCTRL0_CH1_RST	BIT(19)
+#define	FUNCTRL0_CH0_RST	BIT(18)
+#define	FUNCTRL0_CH1_EN		BIT(17)
+#define	FUNCTRL0_CH0_EN		BIT(16)
+#define	FUNCTRL0_CH1_PAUSE	BIT(3)
+#define	FUNCTRL0_CH0_PAUSE	BIT(2)
+#define	FUNCTRL0_CH1_REC	BIT(1)
+#define	FUNCTRL0_CH0_REC	BIT(0)
+
+#define	FUNCTRL1_DAC_RATE_MASK	(0x7 << 13)
+#define	FUNCTRL1_DAC_RATE_48K	(0x7 << 13)
+#define	FUNCTRL1_DAC_RATE_32K	(0x6 << 13)
+#define	FUNCTRL1_DAC_RATE_16K	(0x5 << 13)
+#define	FUNCTRL1_DAC_RATE_8K	(0x4 << 13)
+#define	FUNCTRL1_DAC_RATE_44K	(0x3 << 13)
+#define	FUNCTRL1_DAC_RATE_22K	(0x2 << 13)
+#define	FUNCTRL1_DAC_RATE_11K	(0x1 << 13)
+#define	FUNCTRL1_DAC_RATE_5K	(0x0 << 13)
+#define	FUNCTRL1_ADC_RATE_MASK	(0x7 << 10)
+#define	FUNCTRL1_ADC_RATE_48K	(0x7 << 10)
+#define	FUNCTRL1_ADC_RATE_32K	(0x6 << 10)
+#define	FUNCTRL1_ADC_RATE_16K	(0x5 << 10)
+#define	FUNCTRL1_ADC_RATE_8K	(0x4 << 10)
+#define	FUNCTRL1_ADC_RATE_44K	(0x3 << 10)
+#define	FUNCTRL1_ADC_RATE_22K	(0x2 << 10)
+#define	FUNCTRL1_ADC_RATE_11K	(0x1 << 10)
+#define	FUNCTRL1_ADC_RATE_5K	(0x0 << 10)
+#define	FUNCTRL1_INTRM		BIT(5)		/* enable MCB intr */
+#define	FUNCTRL1_BREQ		BIT(4)		/* bus master enable */
+#define	FUNCTRL1_VOICE_EN	BIT(3)
+#define	FUNCTRL1_UART_EN	BIT(2)
+#define	FUNCTRL1_JYSTK_EN	BIT(1)
+
+#define	CHFORMAT_CH1_MASK	(0x3 << 2)
+#define	CHFORMAT_CH1_16ST	(0x3 << 2)
+#define	CHFORMAT_CH1_16MO	(0x2 << 2)
+#define	CHFORMAT_CH1_8ST	(0x1 << 2)
+#define	CHFORMAT_CH1_8MO	(0x0 << 2)
+#define	CHFORMAT_CH0_MASK	(0x3 << 0)
+#define	CHFORMAT_CH0_16ST	(0x3 << 0)
+#define	CHFORMAT_CH0_16MO	(0x2 << 0)
+#define	CHFORMAT_CH0_8ST	(0x1 << 0)
+#define	CHFORMAT_CH0_8MO	(0x0 << 0)
+
+#define	INTCTRL_TDMA_EN		BIT(18)
+#define	INTCTRL_CH1_EN		BIT(17)
+#define	INTCTRL_CH0_EN		BIT(16)
+
+#define	INTSTAT_INTR		BIT(31)
+#define	INTSTAT_MCB_INT		BIT(26)
+#define	INTSTAT_UART_INT	BIT(16)
+#define	INTSTAT_LTDMA_INT	BIT(15)
+#define	INTSTAT_HTDMA_INT	BIT(14)
+#define	INTSTAT_LHBTOG		BIT(7)
+#define	INTSTAT_LEGDMA		BIT(6)
+#define	INTSTAT_LEGHIGH		BIT(5)
+#define	INTSTAT_LEGSTEREO	BIT(4)
+#define	INTSTAT_CH1_BUSY	BIT(3)
+#define	INTSTAT_CH0_BUSY	BIT(2)
+#define	INTSTAT_CH1_INT		BIT(1)
+#define	INTSTAT_CH0_INT		BIT(0)
+
+#define	MISC_PWD		BIT(31)	/* power down */
+#define	MISC_RESET		BIT(30)
+#define	MISC_ENDBDAC		BIT(23)	/* dual dac */
+#define	MISC_XCHGDAC		BIT(22)	/* swap front/rear dacs */
+#define	MISC_FM_EN		BIT(19)	/* enable legacy FM */
+
+#define	MIX2_FMMUTE		BIT(7)
+#define	MIX2_WSMUTE		BIT(6)
+#define	MIX2_WAVEIN_L		BIT(3)	/* for recording wave out */
+#define	MIX2_WAVEIN_R		BIT(2)	/* for recording wave out */
+
+#define	MIX3_RAUXREN		BIT(7)
+#define	MIX3_RAUXLEN		BIT(6)
+#define	MIX3_VAUXRM		BIT(5)	/* r-aux mute */
+#define	MIX3_VAUXLM		BIT(4)	/* l-aux mute */
+#define	MIX3_VADCMIC_MASK	(0x7 << 1)	/* rec mic volume */
+#define	MIX3_MICGAINZ		BIT(0)	/* mic gain */
+
+#define	VAUX_L_MASK		0xf0
+#define	VAUX_R_MASK		0x0f
+
+/* Indexes via SBINDEX */
+#define	IDX_MASTER_LEFT		0x30
+#define	IDX_MASTER_RIGHT	0x31
+#define	IDX_VOICE_LEFT		0x32	/* PCM volume */
+#define	IDX_VOICE_RIGHT		0x33
+#define	IDX_CDDA_LEFT		0x36
+#define	IDX_CDDA_RIGHT		0x37
+#define	IDX_LINEIN_LEFT		0x38
+#define	IDX_LINEIN_RIGHT	0x39
+#define	IDX_MIC			0x3A
+#define	IDX_SPEAKER		0x3B
+#define	IDX_OUTMIX		0x3C
+#define		OUTMIX_MIC	0x01
+#define		OUTMIX_CD_R	0x02
+#define		OUTMIX_CD_L	0x04
+#define		OUTMIX_LINE_R	0x08
+#define		OUTMIX_LINE_L	0x10
+#define	IDX_INMIX_L		0x3D
+#define	IDX_INMIX_R		0x3E
+#define		INMIX_LINE_R	0x08
+#define		INMIX_LINE_L	0x10
+#define		INMIX_CD_R	0x20
+#define		INMIX_CD_L	0x40
+#define		INMIX_MIC	0x01
+#define	IDX_IGAIN_L		0x3F
+#define	IDX_IGAIN_R		0x40
+#define	IDX_OGAIN_L		0x41
+#define	IDX_OGAIN_R		0x42
+#define	IDX_AGC			0x43
+#define	IDX_TREBLE_L		0x44
+#define	IDX_TREBLE_R		0x45
+#define	IDX_BASS_L		0x46
+#define	IDX_BASS_R		0x47
+
+
+#define	IDX_EXTENSION		0xf0
+
+#define	EXTENSION_VPHONE_MASK	(0x7 << 5)
+#define	EXTENSION_VPHONE_MUTE	BIT(4)
+#define	EXTENSION_BEEPER_MUTE	BIT(3)
+#define	EXTENSION_VADCMIC3	BIT(0)
+
+enum {
+	SRC_MIC = 0,
+	SRC_LINE,
+	SRC_CD,
+	SRC_AUX,
+	SRC_MIX,
+};
+
+enum {
+	CTL_VOLUME = 0,
+	CTL_LINEOUT,
+	CTL_SPEAKER,
+	CTL_MIC,
+	CTL_LINEIN,
+	CTL_CD,
+	CTL_AUX,
+	CTL_RECSRCS,
+	CTL_MONSRCS,
+	CTL_MICBOOST,
+	CTL_NUM
+};
+
+typedef struct cmpci_port cmpci_port_t;
+typedef struct cmpci_dev cmpci_dev_t;
+typedef struct cmpci_ctrl cmpci_ctrl_t;
+
+struct cmpci_ctrl {
+	cmpci_dev_t		*dev;
+	audio_ctrl_t		*ctrl;
+	uint64_t		value;
+};
+
+struct cmpci_port {
+	cmpci_dev_t		*dev;
+	audio_engine_t		*engine;
+	int			num;
+	ddi_acc_handle_t	acch;
+	ddi_dma_handle_t	dmah;
+	caddr_t			kaddr;
+	uint32_t		paddr;
+	unsigned		fragfr;
+	unsigned		nfrags;
+	unsigned		nframes;
+	unsigned		bufsz;
+
+	boolean_t		capture;
+	boolean_t		open;
+
+	/* registers & bit masks */
+	uint8_t			reg_paddr;
+	uint8_t			reg_bufsz;
+	uint8_t			reg_fragsz;
+
+	uint32_t		fc0_rst_bit;
+	uint32_t		fc0_rec_bit;
+	uint32_t		fc0_en_bit;
+	uint32_t		int_en_bit;
+	uint32_t		fc1_rate_mask;
+	uint32_t		chformat_mask;
+	int			sync_dir;
+
+	uint32_t		offset;	/* in bytes */
+	uint64_t		count;	/* in bytes */
+
+	void			(*callb)(audio_engine_t *);
+	cmpci_ctrl_t		controls[CTL_NUM];
+};
+
+#define	PORT_MAX	2
+
+struct cmpci_dev {
+	audio_dev_t		*adev;
+	dev_info_t		*dip;
+	ddi_acc_handle_t	acch;
+	caddr_t			regs;
+
+	int			pintrs;
+	int			rintrs;
+	ddi_intr_handle_t	ihandle;
+	kstat_t			*ksp;
+
+	int			model;
+#define	MDL_CM8738		1
+#define	MDL_CM8338A		2
+#define	MDL_CM8338B		3
+#define	MDL_CM8768		4
+	char			*chip_name;
+	int			chiprev;
+
+	boolean_t		suspended;
+
+	kmutex_t		mutex;
+	cmpci_port_t		port[PORT_MAX];
+	cmpci_ctrl_t		controls[CTL_NUM];
+};
+
+/*
+ * The hardware appears to be able to address up to 16-bits worth of samples,
+ * giving a total address space of 256K.  Note, however, that we will restrict
+ * this further when we do fragment and memory allocation.
+ */
+#define	CMPCI_BUF_LEN	(65536)
+#define	DEFINTS		175
+
+#define	GET8(dev, offset)	\
+	ddi_get8(dev->acch, (uint8_t *)(dev->regs + (offset)))
+#define	GET16(dev, offset)	\
+	ddi_get16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)))
+#define	GET32(dev, offset)	\
+	ddi_get32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)))
+#define	PUT8(dev, offset, v)	\
+	ddi_put8(dev->acch, (uint8_t *)(dev->regs + (offset)), v)
+#define	PUT16(dev, offset, v)	\
+	ddi_put16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)), v)
+#define	PUT32(dev, offset, v)	\
+	ddi_put32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)), v)
+
+#define	CLR8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) & ~(v))
+#define	SET8(dev, offset, v)	PUT8(dev, offset, GET8(dev, offset) | (v))
+#define	CLR16(dev, offset, v)	PUT16(dev, offset, GET16(dev, offset) & ~(v))
+#define	SET16(dev, offset, v)	PUT16(dev, offset, GET16(dev, offset) | (v))
+#define	CLR32(dev, offset, v)	PUT32(dev, offset, GET32(dev, offset) & ~(v))
+#define	SET32(dev, offset, v)	PUT32(dev, offset, GET32(dev, offset) | (v))
+
+#define	KSINTR(dev)	((kstat_intr_t *)((dev)->ksp->ks_data))
+
+#define	BIT(n)		(1U << (n))
+
+#endif	/* _AUDIOCMI_H */
--- a/usr/src/uts/intel/Makefile.intel.shared	Tue Jun 09 07:24:43 2009 -0700
+++ b/usr/src/uts/intel/Makefile.intel.shared	Tue Jun 09 11:09:06 2009 -0700
@@ -198,6 +198,7 @@
 DRV_KMODS	+= audio
 DRV_KMODS	+= audio1575
 DRV_KMODS	+= audio810
+DRV_KMODS	+= audiocmi
 DRV_KMODS	+= audioens
 DRV_KMODS	+= audiohd
 DRV_KMODS	+= audioixp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/audiocmi/Makefile	Tue Jun 09 11:09:06 2009 -0700
@@ -0,0 +1,81 @@
+#
+# 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/audiocmi/Makefile
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#	This makefile drives the production of the audiocmi driver.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= audiocmi
+OBJECTS		= $(AUDIOCMI_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(AUDIOCMI_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+#	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
+
+#
+#	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)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ