summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
authorGarrett D'Amore <gdamore@opensolaris.org>2009-07-23 02:01:56 -0700
committerGarrett D'Amore <gdamore@opensolaris.org>2009-07-23 02:01:56 -0700
commit5fbb41393be5d63f75952b1d72d4df2642d22557 (patch)
tree3d12f9d8ada902bf09343c1cc430d7ff634c4182 /usr
parentaa62bbf83397e6d4a8a3acf988f6540dc2ab13bf (diff)
downloadillumos-joyent-5fbb41393be5d63f75952b1d72d4df2642d22557.tar.gz
PSARC 2009/321 audiovia97
6849194 audiovia97 driver desired
Diffstat (limited to 'usr')
-rw-r--r--usr/src/pkgdefs/SUNWaudiovia97/Makefile36
-rw-r--r--usr/src/pkgdefs/SUNWaudiovia97/depend50
-rw-r--r--usr/src/pkgdefs/SUNWaudiovia97/pkginfo.tmpl57
-rw-r--r--usr/src/pkgdefs/SUNWaudiovia97/postinstall.tmpl34
-rw-r--r--usr/src/pkgdefs/SUNWaudiovia97/preremove.tmpl30
-rw-r--r--usr/src/pkgdefs/SUNWaudiovia97/prototype_com47
-rw-r--r--usr/src/pkgdefs/SUNWaudiovia97/prototype_i38646
-rw-r--r--usr/src/uts/common/Makefile.files2
-rw-r--r--usr/src/uts/common/Makefile.rules7
-rw-r--r--usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.c976
-rw-r--r--usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.h115
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared1
-rw-r--r--usr/src/uts/intel/audiovia97/Makefile91
13 files changed, 1492 insertions, 0 deletions
diff --git a/usr/src/pkgdefs/SUNWaudiovia97/Makefile b/usr/src/pkgdefs/SUNWaudiovia97/Makefile
new file mode 100644
index 0000000000..c131ff1b31
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWaudiovia97/Makefile
@@ -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
diff --git a/usr/src/pkgdefs/SUNWaudiovia97/depend b/usr/src/pkgdefs/SUNWaudiovia97/depend
new file mode 100644
index 0000000000..4ba634b877
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWaudiovia97/depend
@@ -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
diff --git a/usr/src/pkgdefs/SUNWaudiovia97/pkginfo.tmpl b/usr/src/pkgdefs/SUNWaudiovia97/pkginfo.tmpl
new file mode 100644
index 0000000000..1612eeb6bb
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWaudiovia97/pkginfo.tmpl
@@ -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="SUNWaudiovia97"
+NAME="Via 82C686 Audio Driver"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+CATEGORY="system"
+DESC="SunOS audio device driver for Via 82C686"
+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>"
diff --git a/usr/src/pkgdefs/SUNWaudiovia97/postinstall.tmpl b/usr/src/pkgdefs/SUNWaudiovia97/postinstall.tmpl
new file mode 100644
index 0000000000..798a8881b4
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWaudiovia97/postinstall.tmpl
@@ -0,0 +1,34 @@
+#! /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.
+#
+# SUNWaudiovia97 postinstall script
+
+include drv_utils
+
+AUDIOVIA97_ALIASES="\
+ \"pci1106,3058\" \
+ "
+
+pkg_drvadd -i "${AUDIOVIA97_ALIASES}" audiovia97 || exit 1
diff --git a/usr/src/pkgdefs/SUNWaudiovia97/preremove.tmpl b/usr/src/pkgdefs/SUNWaudiovia97/preremove.tmpl
new file mode 100644
index 0000000000..b1cfff749b
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWaudiovia97/preremove.tmpl
@@ -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.
+#
+# SUNWaudiovia97 preremove script
+
+include drv_utils
+
+pkg_drvrem audiovia97 || exit 1
diff --git a/usr/src/pkgdefs/SUNWaudiovia97/prototype_com b/usr/src/pkgdefs/SUNWaudiovia97/prototype_com
new file mode 100644
index 0000000000..3be0a1d3c0
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWaudiovia97/prototype_com
@@ -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
+#
+# SUNWaudiovia97
+#
+d none kernel 755 root sys
+d none kernel/drv 755 root sys
diff --git a/usr/src/pkgdefs/SUNWaudiovia97/prototype_i386 b/usr/src/pkgdefs/SUNWaudiovia97/prototype_i386
new file mode 100644
index 0000000000..78be1d3f27
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWaudiovia97/prototype_i386
@@ -0,0 +1,46 @@
+#
+# 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
+#
+# SUNWaudiovia97
+#
+f none kernel/drv/audiovia97 755 root sys
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index a3249651ee..bea36dc659 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -428,6 +428,8 @@ AUDIOENS_OBJS += audioens.o
AUDIOVIA823X_OBJS += audiovia823x.o
+AUDIOVIA97_OBJS += audiovia97.o
+
AUDIO1575_OBJS += audio1575.o
AUDIO810_OBJS += audio810.o
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index 13a75ca9c3..6bde281698 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -584,6 +584,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/audio/drv/audiovia823x/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/audio/drv/audiovia97/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/bfe/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1817,6 +1821,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/audio/drv/audiots/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/audio/drv/audiovia823x/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/audio/drv/audiovia97/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/bfe/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.c b/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.c
new file mode 100644
index 0000000000..4a97b175e4
--- /dev/null
+++ b/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.c
@@ -0,0 +1,976 @@
+/*
+ * 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 the VIA VT82C686A AC97 audio controller
+ */
+/*
+ *
+ * Copyright (C) 4Front Technologies 1996-2009.
+ */
+
+#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/audio/audio_driver.h>
+#include <sys/audio/ac97.h>
+
+#include "audiovia97.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
+};
+
+static ddi_dma_attr_t dma_attr_sgd = {
+ DMA_ATTR_V0, /* version number */
+ 0x00000000, /* low DMA address range */
+ 0xffffffff, /* high DMA address range */
+ 0x0000ffff, /* DMA counter register */
+ 8, /* DMA address alignment */
+ 0x3c, /* DMA burstsizes */
+ 8, /* min effective DMA size */
+ 0xffffffff, /* max DMA xfer size */
+ 0x00000fff, /* segment boundary */
+ 1, /* s/g length */
+ 8, /* granularity of device */
+ 0 /* Bus specific DMA flags */
+};
+
+static ddi_dma_attr_t dma_attr_buf = {
+ DMA_ATTR_V0, /* version number */
+ 0x00000000, /* low DMA address range */
+ 0xffffffff, /* high DMA address range */
+ 0xfffffffe, /* DMA counter register */
+ 4, /* DMA address alignment */
+ 0x3c, /* DMA burstsizes */
+ 4, /* min effective DMA size */
+ 0xffffffff, /* max DMA xfer size */
+ 0xffffffff, /* segment boundary */
+ 1, /* s/g length */
+ 4, /* granularity of device */
+ 0 /* Bus specific DMA flags */
+};
+
+static int via97_attach(dev_info_t *);
+static int via97_resume(dev_info_t *);
+static int via97_detach(via97_devc_t *);
+static int via97_suspend(via97_devc_t *);
+
+static int via97_open(void *, int, unsigned *, unsigned *, caddr_t *);
+static void via97_close(void *);
+static int via97_start(void *);
+static void via97_stop(void *);
+static int via97_format(void *);
+static int via97_channels(void *);
+static int via97_rate(void *);
+static uint64_t via97_count(void *);
+static void via97_sync(void *, unsigned);
+static size_t via97_qlen(void *);
+
+static uint16_t via97_read_ac97(void *, uint8_t);
+static void via97_write_ac97(void *, uint8_t, uint16_t);
+static int via97_alloc_port(via97_devc_t *, int);
+static void via97_start_port(via97_portc_t *);
+static void via97_stop_port(via97_portc_t *);
+static void via97_update_port(via97_portc_t *);
+static void via97_reset_port(via97_portc_t *);
+static void via97_destroy(via97_devc_t *);
+static int via97_setup_intrs(via97_devc_t *);
+static void via97_hwinit(via97_devc_t *);
+static uint_t via97_intr(caddr_t, caddr_t);
+
+static audio_engine_ops_t via97_engine_ops = {
+ AUDIO_ENGINE_VERSION,
+ via97_open,
+ via97_close,
+ via97_start,
+ via97_stop,
+ via97_count,
+ via97_format,
+ via97_channels,
+ via97_rate,
+ via97_sync,
+ via97_qlen
+};
+
+static uint16_t
+via97_read_ac97(void *arg, uint8_t index)
+{
+ via97_devc_t *devc = arg;
+ int tmp, addr, i;
+
+ /* Index has only 7 bits */
+ if (index > 0x7F)
+ return (0xffff);
+
+ mutex_enter(&devc->low_mutex);
+ addr = (index << 16) + CODEC_RD;
+ OUTL(devc, devc->base + AC97CODEC, addr);
+ drv_usecwait(100);
+
+ /* Check AC CODEC access time out */
+ for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
+ /* if send command over, break */
+ if (INL(devc, devc->base + AC97CODEC) & STA_VALID)
+ break;
+ drv_usecwait(50);
+ }
+ if (i == CODEC_TIMEOUT_COUNT) {
+ mutex_exit(&devc->low_mutex);
+ return (0xffff);
+ }
+
+ /* Check if Index still ours? If yes, return data, else return FAIL */
+ tmp = INL(devc, devc->base + AC97CODEC);
+ OUTB(devc, devc->base + AC97CODEC + 3, 0x02);
+ if (((tmp & CODEC_INDEX) >> 16) == index) {
+ mutex_exit(&devc->low_mutex);
+ return ((int)tmp & CODEC_DATA);
+ }
+ mutex_exit(&devc->low_mutex);
+ return (0xffff);
+}
+
+static void
+via97_write_ac97(void *arg, uint8_t index, uint16_t data)
+{
+ via97_devc_t *devc = arg;
+ int value = 0;
+ unsigned int i = 0;
+
+ mutex_enter(&devc->low_mutex);
+ value = (index << 16) + data;
+ OUTL(devc, devc->base + AC97CODEC, value);
+ drv_usecwait(100);
+
+ /* Check AC CODEC access time out */
+ for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
+ /* if send command over, break */
+ if (!(INL(devc, devc->base + AC97CODEC) & IN_CMD))
+ break;
+ drv_usecwait(50);
+ }
+ mutex_exit(&devc->low_mutex);
+}
+
+static uint_t
+via97_recintr(via97_devc_t *devc)
+{
+ int status;
+
+ status = INB(devc, devc->base + 0x10);
+
+ if (!(status & 0x01)) /* No interrupt */
+ return (B_FALSE);
+
+ audio_engine_produce(devc->portc[VIA97_REC_SGD_NUM]->engine);
+
+ OUTB(devc, devc->base + 0x10, status | 0x01); /* Ack */
+ return (B_TRUE);
+}
+
+static uint_t
+via97_playintr(via97_devc_t *devc)
+{
+ int status;
+
+ status = INB(devc, devc->base + 0x00);
+
+ if (!(status & 0x01)) /* No interrupt */
+ return (B_FALSE);
+
+ audio_engine_consume(devc->portc[VIA97_PLAY_SGD_NUM]->engine);
+
+ OUTB(devc, devc->base + 0x00, status | 0x01); /* Ack */
+ return (B_TRUE);
+}
+
+static uint_t
+via97_intr(caddr_t argp, caddr_t nocare)
+{
+ via97_devc_t *devc = (void *)argp;
+
+ _NOTE(ARGUNUSED(nocare));
+
+ if (devc->suspended) {
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ if (!via97_recintr(devc) && !via97_playintr(devc)) {
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ if (devc->ksp) {
+ VIA97_KIOP(devc)->intrs[KSTAT_INTR_HARD]++;
+ }
+
+ return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * Audio routines
+ */
+
+int
+via97_open(void *arg, int flag,
+ unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp)
+{
+ via97_portc_t *portc = arg;
+ via97_devc_t *devc = portc->devc;
+
+ _NOTE(ARGUNUSED(flag));
+
+ portc->started = B_FALSE;
+ portc->count = 0;
+ *fragfrp = portc->fragfr;
+ *nfragsp = VIA97_NUM_SGD;
+ *bufp = portc->buf_kaddr;
+
+ mutex_enter(&devc->mutex);
+ via97_reset_port(portc);
+ mutex_exit(&devc->mutex);
+
+ return (0);
+}
+
+void
+via97_close(void *arg)
+{
+ via97_portc_t *portc = arg;
+ via97_devc_t *devc = portc->devc;
+
+ mutex_enter(&devc->mutex);
+ via97_stop_port(portc);
+ portc->started = B_FALSE;
+ mutex_exit(&devc->mutex);
+}
+
+int
+via97_start(void *arg)
+{
+ via97_portc_t *portc = arg;
+ via97_devc_t *devc = portc->devc;
+
+ mutex_enter(&devc->mutex);
+ if (!portc->started) {
+ via97_start_port(portc);
+ portc->started = B_TRUE;
+ }
+ mutex_exit(&devc->mutex);
+ return (0);
+}
+
+void
+via97_stop(void *arg)
+{
+ via97_portc_t *portc = arg;
+ via97_devc_t *devc = portc->devc;
+
+ mutex_enter(&devc->mutex);
+ if (portc->started) {
+ via97_stop_port(portc);
+ portc->started = B_FALSE;
+ }
+ mutex_exit(&devc->mutex);
+}
+
+int
+via97_format(void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+
+ return (AUDIO_FORMAT_S16_LE);
+}
+
+int
+via97_channels(void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+
+ return (2);
+}
+
+int
+via97_rate(void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+
+ return (48000);
+}
+
+void
+via97_sync(void *arg, unsigned nframes)
+{
+ via97_portc_t *portc = arg;
+ _NOTE(ARGUNUSED(nframes));
+
+ (void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir);
+}
+
+size_t
+via97_qlen(void *arg)
+{
+ _NOTE(ARGUNUSED(arg));
+ return (0);
+}
+
+uint64_t
+via97_count(void *arg)
+{
+ via97_portc_t *portc = arg;
+ via97_devc_t *devc = portc->devc;
+ uint64_t val;
+
+ mutex_enter(&devc->mutex);
+ via97_update_port(portc);
+ /*
+ * The residual is in bytes. We have to convert to frames,
+ * and then subtract it from the fragment size to get the
+ * number of frames processed. Note that we have 16 bit
+ * stereo frames.
+ */
+ val = portc->count +
+ (portc->fragfr - (portc->resid / (2 * 2)));
+ mutex_exit(&devc->mutex);
+
+ return (val);
+}
+
+
+/* private implementation bits */
+
+void
+via97_start_port(via97_portc_t *portc)
+{
+ via97_devc_t *devc = portc->devc;
+
+ ASSERT(mutex_owned(&devc->mutex));
+
+ if (devc->suspended)
+ return;
+ OUTB(devc, portc->base + 0x01, 0x80); /* Start */
+}
+
+void
+via97_stop_port(via97_portc_t *portc)
+{
+ via97_devc_t *devc = portc->devc;
+
+ if (devc->suspended)
+ return;
+
+ OUTB(devc, portc->base + 0x01, 0x40); /* Stop */
+}
+
+void
+via97_update_port(via97_portc_t *portc)
+{
+/*
+ * Unfortunately the controller seems to raise interrupt about 32 bytes before
+ * the DMA pointer moves to a new fragment. This means that the bytes value
+ * returned will be bogus during few samples before
+ * the pointer wraps back to the beginning of buffer.
+ */
+ via97_devc_t *devc = portc->devc;
+ uint32_t frag, resid;
+ uint32_t n;
+
+ ASSERT(mutex_owned(&devc->mutex));
+ if (devc->suspended) {
+ portc->cur_frag = 0;
+ portc->resid = portc->fragsz;
+ n = 0;
+ } else {
+ resid = INL(devc, portc->base + 0x0c) & 0xffffff;
+ resid = portc->fragsz - resid;
+
+ frag =
+ ((INL(devc, portc->base + 0x04) - portc->sgd_paddr) / 8) -
+ 1;
+
+ portc->resid = resid;
+
+ if (frag >= portc->cur_frag) {
+ n = frag - portc->cur_frag;
+ } else {
+ n = frag + VIA97_NUM_SGD - portc->cur_frag;
+ }
+ portc->count += (n * portc->fragfr);
+ portc->cur_frag = frag;
+ }
+}
+
+void
+via97_reset_port(via97_portc_t *portc)
+{
+ via97_devc_t *devc = portc->devc;
+
+ portc->cur_frag = 0;
+ portc->resid = portc->fragsz;
+
+ if (devc->suspended)
+ return;
+
+ OUTB(devc, portc->base + 0x01, 0x40); /* Stop */
+ OUTL(devc, portc->base + 4, portc->sgd_paddr);
+ /* Set autostart at EOL, interrupt on FLAG, stereo, 16 bits */
+ OUTB(devc, portc->base + 0x02,
+ 0x81 | /* Set autostart at EOL, interrupt on FLAG */
+ 0x20 | /* 16 bits */
+ 0x10); /* Stereo */
+}
+
+int
+via97_alloc_port(via97_devc_t *devc, int num)
+{
+ via97_portc_t *portc;
+ size_t len;
+ ddi_dma_cookie_t cookie;
+ uint_t count;
+ int dir;
+ char *prop;
+ unsigned caps;
+ audio_dev_t *adev;
+ uint32_t *desc;
+ uint32_t paddr;
+
+ adev = devc->adev;
+ portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
+ devc->portc[num] = portc;
+ portc->devc = devc;
+ portc->started = B_FALSE;
+ portc->base = devc->base + num * 0x10;
+
+ switch (num) {
+ case VIA97_REC_SGD_NUM:
+ prop = "record-interrupts";
+ portc->syncdir = DDI_DMA_SYNC_FORKERNEL;
+ caps = ENGINE_INPUT_CAP;
+ dir = DDI_DMA_READ;
+ break;
+ case VIA97_PLAY_SGD_NUM:
+ prop = "play-interrupts";
+ portc->syncdir = DDI_DMA_SYNC_FORDEV;
+ caps = ENGINE_OUTPUT_CAP;
+ dir = DDI_DMA_WRITE;
+ break;
+ default:
+ return (DDI_FAILURE);
+ }
+
+ /* figure out fragment configuration */
+ portc->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, devc->dip,
+ DDI_PROP_DONTPASS, prop, VIA97_INTRS);
+
+ /* make sure the values are good */
+ if (portc->intrs < VIA97_MIN_INTRS) {
+ audio_dev_warn(adev, "%s too low, %d, reset to %d",
+ prop, portc->intrs, VIA97_INTRS);
+ portc->intrs = VIA97_INTRS;
+ } else if (portc->intrs > VIA97_MAX_INTRS) {
+ audio_dev_warn(adev, "%s too high, %d, reset to %d",
+ prop, portc->intrs, VIA97_INTRS);
+ portc->intrs = VIA97_INTRS;
+ }
+
+ portc->fragfr = 48000 / portc->intrs;
+ portc->fragsz = portc->fragfr * 2 * 2; /* 16 bit stereo frames */
+ portc->buf_size = portc->fragsz * VIA97_NUM_SGD;
+
+ /* first allocate up space for SGD list */
+ if (ddi_dma_alloc_handle(devc->dip, &dma_attr_sgd,
+ DDI_DMA_SLEEP, NULL, &portc->sgd_dmah) != DDI_SUCCESS) {
+ audio_dev_warn(adev, "failed to allocate SGD handle");
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_dma_mem_alloc(portc->sgd_dmah,
+ VIA97_NUM_SGD * 2 *sizeof (uint32_t), &dev_attr,
+ DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &portc->sgd_kaddr,
+ &len, &portc->sgd_acch) != DDI_SUCCESS) {
+ audio_dev_warn(adev, "failed to allocate SGD memory");
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_dma_addr_bind_handle(portc->sgd_dmah, NULL,
+ portc->sgd_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
+ DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
+ audio_dev_warn(adev, "failed binding SGD DMA handle");
+ return (DDI_FAILURE);
+ }
+ portc->sgd_paddr = cookie.dmac_address;
+
+ /* now 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,
+ &buf_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;
+
+ /* now wire descriptors up */
+ desc = (void *)portc->sgd_kaddr;
+ paddr = portc->buf_paddr;
+ for (int i = 0; i < VIA97_NUM_SGD; i++) {
+ uint32_t flags;
+
+ flags = 0x40000000 | portc->fragsz;
+
+ if (i == (VIA97_NUM_SGD - 1)) {
+ flags |= 0x80000000; /* EOL */
+ }
+
+ ddi_put32(portc->sgd_acch, desc++, paddr);
+ ddi_put32(portc->sgd_acch, desc++, flags);
+ paddr += portc->fragsz;
+ }
+
+ OUTL(devc, portc->base + 4, portc->sgd_paddr);
+ ddi_dma_sync(portc->sgd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
+
+ portc->engine = audio_engine_alloc(&via97_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
+via97_setup_intrs(via97_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], via97_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));
+ mutex_init(&devc->low_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
+ return (DDI_SUCCESS);
+}
+
+void
+via97_destroy(via97_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);
+ mutex_destroy(&devc->low_mutex);
+ }
+
+ if (devc->ksp) {
+ kstat_delete(devc->ksp);
+ }
+
+ for (int i = 0; i < VIA97_NUM_PORTC; i++) {
+ via97_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->sgd_paddr) {
+ (void) ddi_dma_unbind_handle(portc->sgd_dmah);
+ }
+ if (portc->sgd_acch) {
+ ddi_dma_mem_free(&portc->sgd_acch);
+ }
+ if (portc->sgd_dmah) {
+ ddi_dma_free_handle(&portc->sgd_dmah);
+ }
+ 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));
+}
+
+void
+via97_hwinit(via97_devc_t *devc)
+{
+ ddi_acc_handle_t pcih = devc->pcih;
+ uint32_t tmp;
+
+ /* Enable codec, etc */
+
+ pci_config_put8(pcih, 0x41, 0xc0);
+ drv_usecwait(10);
+ tmp = pci_config_get8(pcih, 0x41);
+ pci_config_put8(pcih, 0x41, tmp | 0x0c);
+ drv_usecwait(10);
+
+ /* disable game port/MIDI */
+ pci_config_put8(pcih, 0x42, 0x00);
+ /* disable FM io */
+ pci_config_put8(pcih, 0x48, 0x00);
+
+ /* Enable interrupt on FLAG and on EOL */
+ tmp = INB(devc, devc->base + 0x22);
+ OUTB(devc, devc->base + 0x22, tmp | 0x83);
+}
+
+int
+via97_attach(dev_info_t *dip)
+{
+ uint16_t pci_command, vendor, device;
+ via97_devc_t *devc;
+ ddi_acc_handle_t pcih;
+
+ 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;
+
+ vendor = pci_config_get16(pcih, PCI_CONF_VENID);
+ device = pci_config_get16(pcih, PCI_CONF_DEVID);
+ if (vendor != VIA_VENDOR_ID ||
+ device != VIA_82C686) {
+ audio_dev_warn(devc->adev, "Hardware not recognized "
+ "(vendor=%x, dev=%x)", vendor, device);
+ goto error;
+ }
+
+ 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->base, 0, 0, &dev_attr,
+ &devc->regsh)) != DDI_SUCCESS) {
+ audio_dev_warn(devc->adev, "failed to map registers");
+ goto error;
+ }
+
+ audio_dev_set_description(devc->adev, "VIA 82C686 Audio");
+
+ via97_hwinit(devc);
+
+ if ((via97_alloc_port(devc, VIA97_PLAY_SGD_NUM) != DDI_SUCCESS) ||
+ (via97_alloc_port(devc, VIA97_REC_SGD_NUM) != DDI_SUCCESS)) {
+ goto error;
+ }
+
+ if (via97_setup_intrs(devc) != DDI_SUCCESS) {
+ goto error;
+ }
+
+ devc->ac97 = ac97_alloc(dip, via97_read_ac97, via97_write_ac97, devc);
+ if (devc->ac97 == NULL) {
+ audio_dev_warn(devc->adev, "failed to allocate ac97 handle");
+ goto error;
+ }
+
+ if (ac97_init(devc->ac97, devc->adev) != DDI_SUCCESS) {
+ audio_dev_warn(devc->adev, "failed to init ac97");
+ goto error;
+ }
+
+ /* set up kernel statistics */
+ if ((devc->ksp = kstat_create(VIA97_NAME, ddi_get_instance(dip),
+ VIA97_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 with framework");
+ goto error;
+ }
+
+ (void) ddi_intr_enable(devc->ih);
+ ddi_report_dev(dip);
+
+ return (DDI_SUCCESS);
+
+error:
+ via97_destroy(devc);
+ return (DDI_FAILURE);
+}
+
+int
+via97_resume(dev_info_t *dip)
+{
+ via97_devc_t *devc;
+
+ devc = ddi_get_driver_private(dip);
+
+ via97_hwinit(devc);
+
+ /* allow ac97 operations again */
+ ac97_resume(devc->ac97);
+
+ mutex_enter(&devc->mutex);
+ devc->suspended = B_FALSE;
+ for (int i = 0; i < VIA97_NUM_PORTC; i++) {
+
+ via97_portc_t *portc = devc->portc[i];
+
+ if (portc->engine != NULL)
+ audio_engine_reset(portc->engine);
+
+ /* reset the port */
+ via97_reset_port(portc);
+
+ if (portc->started) {
+ via97_start_port(portc);
+ } else {
+ via97_stop_port(portc);
+ }
+ }
+ mutex_exit(&devc->mutex);
+ return (DDI_SUCCESS);
+}
+
+int
+via97_detach(via97_devc_t *devc)
+{
+ if (audio_dev_unregister(devc->adev) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ via97_destroy(devc);
+ return (DDI_SUCCESS);
+}
+
+int
+via97_suspend(via97_devc_t *devc)
+{
+ ac97_suspend(devc->ac97);
+
+ mutex_enter(&devc->mutex);
+ for (int i = 0; i < VIA97_NUM_PORTC; i++) {
+
+ via97_portc_t *portc = devc->portc[i];
+ via97_stop_port(portc);
+ }
+ devc->suspended = B_TRUE;
+ mutex_exit(&devc->mutex);
+ return (DDI_SUCCESS);
+}
+
+static int via97_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
+static int via97_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
+static int via97_ddi_quiesce(dev_info_t *);
+
+static struct dev_ops via97_dev_ops = {
+ DEVO_REV, /* rev */
+ 0, /* refcnt */
+ NULL, /* getinfo */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ via97_ddi_attach, /* attach */
+ via97_ddi_detach, /* detach */
+ nodev, /* reset */
+ NULL, /* cb_ops */
+ NULL, /* bus_ops */
+ NULL, /* power */
+ via97_ddi_quiesce, /* quiesce */
+};
+
+static struct modldrv via97_modldrv = {
+ &mod_driverops, /* drv_modops */
+ "Via 82C686 Audio", /* linkinfo */
+ &via97_dev_ops, /* dev_ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ { &via97_modldrv, NULL }
+};
+
+int
+_init(void)
+{
+ int rv;
+
+ audio_init_ops(&via97_dev_ops, VIA97_NAME);
+ if ((rv = mod_install(&modlinkage)) != 0) {
+ audio_fini_ops(&via97_dev_ops);
+ }
+ return (rv);
+}
+
+int
+_fini(void)
+{
+ int rv;
+
+ if ((rv = mod_remove(&modlinkage)) == 0) {
+ audio_fini_ops(&via97_dev_ops);
+ }
+ return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+via97_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_ATTACH:
+ return (via97_attach(dip));
+
+ case DDI_RESUME:
+ return (via97_resume(dip));
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+int
+via97_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ via97_devc_t *devc;
+
+ devc = ddi_get_driver_private(dip);
+
+ switch (cmd) {
+ case DDI_DETACH:
+ return (via97_detach(devc));
+
+ case DDI_SUSPEND:
+ return (via97_suspend(devc));
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+int
+via97_ddi_quiesce(dev_info_t *dip)
+{
+ via97_devc_t *devc;
+
+ devc = ddi_get_driver_private(dip);
+
+ for (int i = 0; i < VIA97_NUM_PORTC; i++) {
+
+ via97_portc_t *portc = devc->portc[i];
+ via97_stop_port(portc);
+ }
+
+ /*
+ * Turn off the hardware
+ */
+ OUTB(devc, devc->base + 0x01, 0x40);
+ OUTB(devc, devc->base + 0x11, 0x40);
+ OUTB(devc, devc->base + 0x02, 0);
+ OUTB(devc, devc->base + 0x12, 0);
+ OUTL(devc, devc->base + 0x04, 0);
+ OUTL(devc, devc->base + 0x14, 0);
+ OUTL(devc, devc->base + 0x22, 0);
+ return (DDI_SUCCESS);
+}
diff --git a/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.h b/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.h
new file mode 100644
index 0000000000..8a5cde5cb6
--- /dev/null
+++ b/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.h
@@ -0,0 +1,115 @@
+/*
+ * 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 VIA VT82C686A AC97 driver
+ */
+/*
+ * Copyright (C) 4Front Technologies 1996-2009.
+ */
+#ifndef AUDIOVIA97_H
+#define AUDIOVIA97_H
+
+#define VIA97_NAME "audiovia97"
+
+#define VIA97_NUM_PORTC 2
+#define VIA97_PLAY_SGD_NUM 0
+#define VIA97_REC_SGD_NUM 1
+#define VIA97_NUM_SGD 512 /* Max number of SGD entries (4k/8) */
+
+#define VIA_VENDOR_ID 0x1106
+#define VIA_82C686 0x3058
+
+#define VIA97_MAX_INTRS 256
+#define VIA97_MIN_INTRS 24
+#define VIA97_INTRS 175
+
+#define CODEC_TIMEOUT_COUNT 500
+#define AC97CODEC 0x80 /* Access AC97 Codec */
+#define IN_CMD 0x01000000 /* busy in sending */
+#define STA_VALID 0x02000000 /* 1:status data is valid */
+#define CODEC_RD 0x00800000 /* Read CODEC status */
+#define CODEC_INDEX 0x007F0000 /* Index of command register */
+#define CODEC_DATA 0x0000FFFF /* AC97 status register data */
+
+typedef struct _via97_devc_t via97_devc_t;
+typedef struct _via97_portc_t via97_portc_t;
+
+struct _via97_portc_t
+{
+ via97_devc_t *devc;
+ audio_engine_t *engine;
+
+ int started;
+ unsigned intrs;
+ unsigned fragfr;
+ unsigned fragsz;
+ unsigned cur_frag;
+ unsigned resid;
+ caddr_t base;
+
+ ddi_dma_handle_t sgd_dmah; /* dma for descriptors */
+ ddi_acc_handle_t sgd_acch;
+ uint32_t sgd_paddr;
+ caddr_t sgd_kaddr;
+
+ 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;
+ int syncdir;
+ uint64_t count;
+};
+
+struct _via97_devc_t
+{
+ dev_info_t *dip;
+ audio_dev_t *adev;
+ ac97_t *ac97;
+ kstat_t *ksp;
+
+ boolean_t suspended;
+ ddi_acc_handle_t pcih;
+ ddi_acc_handle_t regsh;
+ caddr_t base;
+ kmutex_t mutex; /* For normal locking */
+ kmutex_t low_mutex; /* For low level routines */
+ ddi_intr_handle_t ih;
+
+ via97_portc_t *portc[VIA97_NUM_PORTC];
+};
+
+#define INL(devc, reg) ddi_get32(devc->regsh, (void *)(reg))
+
+#define INB(devc, reg) ddi_get8(devc->regsh, (void *)(reg))
+
+#define OUTL(devc, reg, val) ddi_put32(devc->regsh, (void *)(reg), (val))
+
+#define OUTB(devc, reg, val) ddi_put8(devc->regsh, (void *)(reg), (val))
+#define VIA97_KIOP(X) ((kstat_intr_t *)(X->ksp->ks_data))
+
+#endif /* AUDIOVIA97_H */
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index 0502c5d8eb..ac16b73280 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -206,6 +206,7 @@ DRV_KMODS += audioixp
DRV_KMODS += audiopci
DRV_KMODS += audiots
DRV_KMODS += audiovia823x
+DRV_KMODS_32 += audiovia97
DRV_KMODS += bl
DRV_KMODS += bge
DRV_KMODS += bofi
diff --git a/usr/src/uts/intel/audiovia97/Makefile b/usr/src/uts/intel/audiovia97/Makefile
new file mode 100644
index 0000000000..4a9899f0c4
--- /dev/null
+++ b/usr/src/uts/intel/audiovia97/Makefile
@@ -0,0 +1,91 @@
+#
+# 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.
+#
+# uts/intel/audiovia97/Makefile
+#
+#
+# This makefile drives the production of the Via 82c686 audio driver
+# (audiovia97) kernel module.
+#
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = audiovia97
+OBJECTS = $(AUDIOVIA97_OBJS:%=$(OBJS_DIR)/%)
+LINTS = $(AUDIOVIA97_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Depends on drv/audio and misc/ac97
+#
+LDFLAGS += -dy -Ndrv/audio -Nmisc/ac97
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+LINT_TARGET = $(MODULE).lint
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Overrides
+#
+DEF_BUILDS = $(DEF_BUILDS32)
+ALL_BUILDS = $(ALL_BUILDS32)
+
+#
+# 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