summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/man4.i386/Makefile6
-rw-r--r--share/man/man4/man4.i386/auglx.440
-rw-r--r--share/man/man4/pci.46
-rw-r--r--sys/arch/i386/conf/GENERIC4
-rw-r--r--sys/arch/i386/conf/files.i3867
-rw-r--r--sys/arch/i386/pci/auglx.c1375
6 files changed, 1431 insertions, 7 deletions
diff --git a/share/man/man4/man4.i386/Makefile b/share/man/man4/man4.i386/Makefile
index ca02420c368..79c243d4abd 100644
--- a/share/man/man4/man4.i386/Makefile
+++ b/share/man/man4/man4.i386/Makefile
@@ -1,9 +1,9 @@
-# $OpenBSD: Makefile,v 1.66 2008/06/14 21:31:46 mbalmer Exp $
+# $OpenBSD: Makefile,v 1.67 2008/06/25 15:27:34 mbalmer Exp $
# from: @(#)Makefile 5.1 (Berkeley) 2/12/91
# Id: Makefile,v 1.4 1995/12/14 05:41:38 deraadt Exp $
-MAN= amdpcib.4 amdmsr.4 apm.4 autoconf.4 bios.4 cpu.4 elansc.4 esm.4 \
- geodesc.4 glxpcib.4 glxsb.4 gscpcib.4 gscpm.4 gus.4 ie.4 \
+MAN= amdpcib.4 amdmsr.4 apm.4 auglx.4 autoconf.4 bios.4 cpu.4 elansc.4 \
+ esm.4 geodesc.4 glxpcib.4 glxsb.4 gscpcib.4 gscpm.4 gus.4 ie.4 \
ichpcib.4 intro.4 ioapic.4 \
joy.4 le.4 lms.4 mcd.4 mem.4 mms.4 mtrr.4 npx.4 nvram.4 pas.4 \
pcibios.4 pctr.4 piixpcib.4 pss.4 sb.4 \
diff --git a/share/man/man4/man4.i386/auglx.4 b/share/man/man4/man4.i386/auglx.4
new file mode 100644
index 00000000000..e40d5223e6b
--- /dev/null
+++ b/share/man/man4/man4.i386/auglx.4
@@ -0,0 +1,40 @@
+.\" $OpenBSD: auglx.4,v 1.1 2008/06/25 15:27:34 mbalmer Exp $
+.\"
+.\" Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: June 25 2008 $
+.Dt AUGLX 4 i386
+.Os
+.Sh NAME
+.Nm auglx
+.Nd AMD Geode LX CS5536 integrated AC'97 audio device
+.Sh SYNOPSIS
+.Cd "auglx* at pci?
+.Cd "audio* at auglx?"
+.Sh DESCRIPTION
+The
+.Nm
+device driver supports the integrated AC'97 audio controller
+of the AMD Geode LX CS5536 companion chip.
+.Sh SEE ALSO
+.Xr ac97 4 ,
+.Xr audio 4 ,
+.Xr intro 4 ,
+.Xr pci 4
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Ox 4.4 .
diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4
index b8a697a3006..3028ab421b2 100644
--- a/share/man/man4/pci.4
+++ b/share/man/man4/pci.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pci.4,v 1.252 2008/06/09 06:08:44 jmc Exp $
+.\" $OpenBSD: pci.4,v 1.253 2008/06/25 15:27:34 mbalmer Exp $
.\" $NetBSD: pci.4,v 1.29 2000/04/01 00:32:23 tsarna Exp $
.\"
.\" Copyright (c) 2000 Theo de Raadt. All rights reserved.
@@ -31,7 +31,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: June 9 2008 $
+.Dd $Mdocdate: June 25 2008 $
.Dt PCI 4
.Os
.Sh NAME
@@ -328,6 +328,8 @@ VGA graphics cards
.El
.Ss Audio devices
.Bl -tag -width 10n -offset ind -compact
+.It Xr auglx 4
+AMD Geode LX CS5536 AC'97 audio device
.It Xr auich 4
ICH-based PCI audio device
.It Xr auixp 4
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC
index f3d65c21430..099a2ddf28d 100644
--- a/sys/arch/i386/conf/GENERIC
+++ b/sys/arch/i386/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.628 2008/06/20 05:13:01 mbalmer Exp $
+# $OpenBSD: GENERIC,v 1.629 2008/06/25 15:27:34 mbalmer Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -644,6 +644,7 @@ eso* at pci? # ESS Solo-1 PCI AudioDrive
sv* at pci? # S3 SonicVibes (S3 617)
neo* at pci? # NeoMagic 256AV/ZX
cmpci* at pci? # C-Media CMI8338/8738
+auglx* at pci? # CMD Geode CS5536 Audio
auich* at pci? flags 0x0000 # i82801 ICH AC'97 audio
auixp* at pci? # ATI IXP AC'97 Audio
autri* at pci? flags 0x0000 # Trident 4D WAVE
@@ -714,6 +715,7 @@ audio* at maestro?
audio* at esa?
audio* at yds?
audio* at emu?
+audio* at auglx?
bktr0 at pci?
diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386
index 5fc576aa8b5..a49a3394367 100644
--- a/sys/arch/i386/conf/files.i386
+++ b/sys/arch/i386/conf/files.i386
@@ -1,4 +1,4 @@
-# $OpenBSD: files.i386,v 1.180 2008/06/14 21:31:46 mbalmer Exp $
+# $OpenBSD: files.i386,v 1.181 2008/06/25 15:27:34 mbalmer Exp $
#
# new style config file for i386 architecture
#
@@ -140,6 +140,11 @@ device amdmsr
attach amdmsr at mainbus
file arch/i386/i386/amdmsr.c amdmsr needs-flag
+# AMD Geode CS5536 Audio
+device auglx: audio
+attach auglx at pci
+file arch/i386/pci/auglx.c auglx
+
# PCI-ISA bridge chipsets
device pcib: isabus
attach pcib at pci
diff --git a/sys/arch/i386/pci/auglx.c b/sys/arch/i386/pci/auglx.c
new file mode 100644
index 00000000000..9260682ab6d
--- /dev/null
+++ b/sys/arch/i386/pci/auglx.c
@@ -0,0 +1,1375 @@
+/* $OpenBSD: auglx.c,v 1.1 2008/06/25 15:27:34 mbalmer Exp $ */
+
+/*
+ * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * AMD CS5536 series AC'97 audio driver.
+ *
+ * The following datasheets were helpful in the development of this
+ * driver:
+ *
+ * AMD Geode LX Processors Data Book
+ * http://www.amd.com/files/connectivitysolutions/geode/geode_lx/\
+ * 33234F_LX_databook.pdf
+ *
+ * AMD Geode CS5536 Companion Device Data Book
+ * http://www.amd.com/files/connectivitysolutions/geode/geode_lx/\
+ * 33238G_cs5536_db.pdf
+ *
+ * Realtek ALC203 Two-Channel AC'97 2.3 Audio Codec
+ * ftp://202.65.194.211/pc/audio/ALC203_DataSheet_1.6.pdf
+ *
+ * This driver is inspired by the auich(4) and auixp(4) drivers, some
+ * of the hardware-independent functionality has been derived from them
+ * (e.g. memory allocation for the upper level, parameter setting).
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+#include <sys/audioio.h>
+
+#include <machine/bus.h>
+#include <machine/cpufunc.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+#include <dev/audio_if.h>
+#include <dev/mulaw.h>
+#include <dev/auconv.h>
+
+#include <dev/ic/ac97.h>
+
+#define AUGLX_ACC_BAR 0x10
+
+/* Standard GeodeLink Device (GLD) MSRs */
+#define ACC_GLD_MSR_CAP 0x51500000 /* Capabilities */
+#define ACC_GLD_MSR_CONFIG 0x51500001 /* Master configuration */
+#define ACC_GLD_MSR_SMI 0x51500002
+#define ACC_GLD_MSR_ERROR 0x51500003
+#define ACC_GLD_MSR_PM 0x51500004 /* Power management */
+#define ACC_GLD_MSR_DIAG 0x51500005 /* Diagnostics */
+
+/* ACC_GLD_MSR_SMI Bit Definitions */
+#define IRQ_SSMI_FLAG 0x0000000100000000
+#define IRQ_SSMI_EN 0x0000000000000001
+
+/* ACC_GLD_MSR_ERROR Bit Definitions */
+#define UNEXP_TYPE_ERR_FLAG 0x0000000100000000
+#define UNEXP_TYPE_ERR_EN 0x0000000000000001
+
+/* ACC Native Registers */
+#define ACC_GPIO_STATUS 0x00
+#define ACC_GPIO_CNTL 0x04
+#define ACC_CODEC_STATUS 0x08
+#define ACC_CODEC_CNTL 0x0c
+#define ACC_IRQ_STATUS 0x12
+#define ACC_ENGINE_CNTL 0x14
+#define ACC_BM0_CMD 0x20 /* Bus Master 0 Command */
+#define ACC_BM0_STATUS 0x21 /* Bus Master 0 IRQ Status */
+#define ACC_BM0_PRD 0x24 /* BM0 PRD Table Address */
+#define ACC_BM1_CMD 0x28 /* Bus Master 1 Command */
+#define ACC_BM1_STATUS 0x29 /* Bus Master 1 IRQ Status */
+#define ACC_BM1_PRD 0x2c /* BM1 PRD Table Address */
+#define ACC_BM2_CMD 0x30 /* Bus Master 2 Command */
+#define ACC_BM2_STATUS 0x31 /* Bus Master 2 IRQ Status */
+#define ACC_BM2_PRD 0x34 /* BM2 PRD Table Address */
+#define ACC_BM3_CMD 0x38 /* Bus Master 3 Command */
+#define ACC_BM3_STATUS 0x39 /* Bus Master 3 IRQ Status */
+#define ACC_BM3_PRD 0x3c /* BM3 PRD Table Address */
+#define ACC_BM4_CMD 0x40 /* Bus Master 4 Command */
+#define ACC_BM4_STATUS 0x41 /* Bus Master 4 IRQ Status */
+#define ACC_BM4_PRD 0x44 /* BM4 PRD Table Address */
+#define ACC_BM5_CMD 0x48 /* Bus Master 5 Command */
+#define ACC_BM5_STATUS 0x49 /* Bus Master 5 IRQ Status */
+#define ACC_BM5_PRD 0x4c /* BM5 PRD Table Address */
+#define ACC_BM6_CMD 0x50 /* Bus Master 6 Command */
+#define ACC_BM6_STATUS 0x51 /* Bus Master 6 IRQ Status */
+#define ACC_BM6_PRD 0x54 /* BM6 PRD Table Address */
+#define ACC_BM7_CMD 0x58 /* Bus Master 7 Command */
+#define ACC_BM7_STATUS 0x59 /* Bus Master 7 IRQ Status */
+#define ACC_BM7_PRD 0x5c /* BM7 PRD Table Address */
+#define ACC_BM0_PNTR 0x60 /* Bus Master 0 DMA Pointer */
+#define ACC_BM1_PNTR 0x64 /* Bus Master 1 DMA Pointer */
+#define ACC_BM2_PNTR 0x68 /* Bus Master 2 DMA Pointer */
+#define ACC_BM3_PNTR 0x6c /* Bus Master 3 DMA Pointer */
+#define ACC_BM4_PNTR 0x70 /* Bus Master 4 DMA Pointer */
+#define ACC_BM5_PNTR 0x74 /* Bus Master 5 DMA Pointer */
+#define ACC_BM6_PNTR 0x78 /* Bus Master 6 DMA Pointer */
+#define ACC_BM7_PNTR 0x7c /* Bus Master 7 DMA Pointer */
+
+/* ACC_IRQ_STATUS Bit Definitions */
+#define BM7_IRQ_STS 0x0200 /* Audio Bus Master 7 IRQ Status */
+#define BM6_IRQ_STS 0x0100 /* Audio Bus Master 6 IRQ Status */
+#define BM5_IRQ_STS 0x0080 /* Audio Bus Master 5 IRQ Status */
+#define BM4_IRQ_STS 0x0040 /* Audio Bus Master 4 IRQ Status */
+#define BM3_IRQ_STS 0x0020 /* Audio Bus Master 3 IRQ Status */
+#define BM2_IRQ_STS 0x0010 /* Audio Bus Master 2 IRQ Status */
+#define BM1_IRQ_STS 0x0008 /* Audio Bus Master 1 IRQ Status */
+#define BM0_IRQ_STS 0x0004 /* Audio Bus Master 0 IRQ Status */
+#define WU_IRQ_STS 0x0002 /* Codec GPIO Wakeup IRQ Status */
+#define IRQ_STS 0x0001 /* Codec GPIO IRQ Status */
+
+/* ACC_ENGINE_CNTL Bit Definitions */
+#define SSND_MODE 0x00000001 /* Surround Sound (5.1) Sync. Mode */
+
+/* ACC_BM[x]_CMD Bit Descriptions */
+#define BMx_CMD_RW 0x08 /* 0: Mem to codec, 1: codec to mem */
+#define BMx_CMD_BYTE_ORD 0x04 /* 0: Little Endian, 1: Big Endian */
+#define BMx_CMD_BM_CTL_DIS 0x00 /* Disable bus master */
+#define BMx_CMD_BM_CTL_EN 0x01 /* Enable bus master */
+#define BMx_CMD_BM_CTL_PAUSE 0x03 /* Pause bus master */
+
+/* ACC_BM[x]_STATUS Bit Definitions */
+#define BMx_BM_EOP_ERR 0x02 /* Bus master error */
+#define BMx_BM_EOP 0x01 /* End of page */
+
+/* ACC_CODEC_CNTL Bit Definitions */
+#define RW_CMD 0x80000000
+#define PD_PRIM 0x00200000
+#define PD_SEC 0x00100000
+#define LNK_SHTDOWN 0x00040000
+#define LNK_WRM_RST 0x00020000
+#define CMD_NEW 0x00010000
+
+/* ACC_CODEC_STATUS Bit Definitions */
+#define PRM_RDY_STS 0x00800000
+#define SEC_RDY_STS 0x00400000
+#define SDATAIN2_EN 0x00200000
+#define BM5_SEL 0x00100000
+#define BM4_SEL 0x00080000
+#define STS_NEW 0x00020000
+
+#define AUGLX_TOUT 1000 /* uSec */
+
+#define AUGLX_DMALIST_MAX 1
+#define AUGLX_DMASEG_MAX 65536
+
+struct auglx_prd {
+ u_int32_t base;
+ u_int32_t size;
+#define AUGLX_PRD_EOT 0x80000000
+#define AUGLX_PRD_EOP 0x40000000
+#define AUGLX_PRD_JMP 0x20000000
+};
+
+#define AUGLX_FIXED_RATE 48000
+
+struct auglx_dma {
+ bus_dmamap_t map;
+ caddr_t addr;
+ bus_dma_segment_t segs[AUGLX_DMALIST_MAX];
+ int nsegs;
+ size_t size;
+ struct auglx_dma *next;
+};
+
+struct auglx_softc {
+ struct device sc_dev;
+ void *sc_ih;
+
+ audio_device_t sc_audev;
+
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ bus_dma_tag_t sc_dmat;
+
+ /*
+ * The CS5536 ACC has eight bus masters to support 5.1 audio.
+ * This driver, however, only supports main playback and recording
+ * since I only have a Realtek ALC203 codec available for testing.
+ */
+ struct auglx_ring {
+ bus_dmamap_t sc_prd;
+ struct auglx_prd *sc_vprd;
+ int sc_nprd;
+
+ size_t sc_size;
+ int nsegs;
+ bus_dma_segment_t seg;
+
+ void (*intr)(void *);
+ void *arg;
+ } bm0, bm1; /* bm0: output, bm1: input */
+
+ struct auglx_dma *sc_dmas;
+
+ struct ac97_codec_if *codec_if;
+ struct ac97_host_if host_if;
+
+ /* power mgmt */
+ void *sc_powerhook;
+ int sc_suspend;
+ u_int16_t sc_ext_ctrl;
+
+ int sc_dmamap_flags;
+};
+
+#ifdef AUGLX_DEBUG
+#define DPRINTF(l,x) do { if (auglx_debug & (l)) printf x; } while(0)
+int auglx_debug = 0;
+#define AUGLX_DBG_ACC 0x0001
+#define AUGLX_DBG_DMA 0x0002
+#define AUGLX_DBG_IRQ 0x0004
+#else
+#define DPRINTF(x,y) /* nothing */
+#endif
+
+struct cfdriver auglx_cd = {
+ NULL, "auglx", DV_DULL
+};
+
+int auglx_open(void *, int);
+void auglx_close(void *);
+int auglx_query_encoding(void *, struct audio_encoding *);
+int auglx_set_params(void *, int, int, struct audio_params *,
+ struct audio_params *);
+int auglx_round_blocksize(void *, int);
+int auglx_halt_output(void *);
+int auglx_halt_input(void *);
+int auglx_getdev(void *, struct audio_device *);
+int auglx_set_port(void *, mixer_ctrl_t *);
+int auglx_get_port(void *, mixer_ctrl_t *);
+int auglx_query_devinfo(void *, mixer_devinfo_t *);
+void *auglx_allocm(void *, int, size_t, int, int);
+void auglx_freem(void *, void *, int);
+size_t auglx_round_buffersize(void *, int, size_t);
+paddr_t auglx_mappage(void *, void *, off_t, int);
+int auglx_get_props(void *);
+int auglx_trigger_output(void *, void *, void *, int, void (*)(void *),
+ void *, struct audio_params *);
+int auglx_trigger_input(void *, void *, void *, int, void (*)(void *),
+ void *, struct audio_params *);
+int auglx_alloc_cdata(struct auglx_softc *);
+int auglx_alloc_prd(struct auglx_softc *, size_t, struct auglx_ring *);
+void auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm);
+int auglx_allocmem(struct auglx_softc *, size_t, size_t, struct auglx_dma *);
+void auglx_freemem(struct auglx_softc *, struct auglx_dma *);
+void auglx_get_default_params(void *, int, struct audio_params *);
+void auglx_powerhook(int, void *);
+
+struct audio_hw_if auglx_hw_if = {
+ auglx_open,
+ auglx_close,
+ NULL, /* drain */
+ auglx_query_encoding,
+ auglx_set_params,
+ auglx_round_blocksize,
+ NULL, /* commit_setting */
+ NULL, /* init_output */
+ NULL, /* init_input */
+ NULL, /* start_output */
+ NULL, /* start_input */
+ auglx_halt_output,
+ auglx_halt_input,
+ NULL, /* speaker_ctl */
+ auglx_getdev,
+ NULL, /* getfd */
+ auglx_set_port,
+ auglx_get_port,
+ auglx_query_devinfo,
+ auglx_allocm,
+ auglx_freem,
+ auglx_round_buffersize,
+ auglx_mappage,
+ auglx_get_props,
+ auglx_trigger_output,
+ auglx_trigger_input,
+ auglx_get_default_params
+};
+
+int auglx_match(struct device *, void *, void *);
+void auglx_attach(struct device *, struct device *, void *);
+int auglx_intr(void *);
+
+int auglx_attach_codec(void *, struct ac97_codec_if *);
+int auglx_read_codec(void *, u_int8_t, u_int16_t *);
+int auglx_write_codec(void *, u_int8_t, u_int16_t);
+void auglx_reset_codec(void *);
+enum ac97_host_flags auglx_flags_codec(void *);
+
+struct cfattach auglx_ca = {
+ sizeof(struct auglx_softc), auglx_match, auglx_attach
+};
+
+const struct pci_matchid auglx_devices[] = {
+ { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_AUDIO }
+};
+
+int
+auglx_match(struct device *parent, void *match, void *aux)
+{
+ if (pci_matchbyid((struct pci_attach_args *)aux, auglx_devices,
+ sizeof(auglx_devices) / sizeof(auglx_devices[0])))
+ return 2;
+
+ return 0;
+}
+
+void
+auglx_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct auglx_softc *sc = (struct auglx_softc *)self;
+ struct pci_attach_args *pa = aux;
+ bus_size_t bar_size;
+ pci_intr_handle_t ih;
+ const char *intrstr;
+
+ if (pci_mapreg_map(pa, AUGLX_ACC_BAR, PCI_MAPREG_TYPE_IO, 0,
+ &sc->sc_iot, &sc->sc_ioh, NULL, &bar_size, 0)) {
+ printf(": can't map ACC I/O space\n");
+ return;
+ }
+
+ sc->sc_dmat = pa->pa_dmat;
+
+ if (pci_intr_map(pa, &ih)) {
+ printf(": can't map interrupt");
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size);
+ return;
+ }
+ intrstr = pci_intr_string(pa->pa_pc, ih);
+ sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO, auglx_intr,
+ sc, sc->sc_dev.dv_xname);
+ if (!sc->sc_ih) {
+ printf(": can't establish interrupt");
+ if (intrstr)
+ printf(" at %s", intrstr);
+ printf("\n");
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size);
+ return;
+ }
+
+ strlcpy(sc->sc_audev.name, "CS5536 AC97", sizeof sc->sc_audev.name);
+ snprintf(sc->sc_audev.version, sizeof sc->sc_audev.version, "0x%02x",
+ PCI_REVISION(pa->pa_class));
+ strlcpy(sc->sc_audev.config, sc->sc_dev.dv_xname,
+ sizeof sc->sc_audev.config);
+
+ printf(": %s, %s\n", intrstr, sc->sc_audev.name);
+
+ sc->host_if.arg = sc;
+ sc->host_if.attach = auglx_attach_codec;
+ sc->host_if.read = auglx_read_codec;
+ sc->host_if.write = auglx_write_codec;
+ sc->host_if.reset = auglx_reset_codec;
+ sc->host_if.flags = auglx_flags_codec;
+
+ if (ac97_attach(&sc->host_if) != 0) {
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size);
+ return;
+ }
+ audio_attach_mi(&auglx_hw_if, sc, &sc->sc_dev);
+
+ /* Watch for power changes */
+ sc->sc_suspend = PWR_RESUME;
+ sc->sc_powerhook = powerhook_establish(auglx_powerhook, sc);
+
+}
+
+/* Functions to communicate with the AC97 Codec via the ACC */
+int
+auglx_read_codec(void *v, u_int8_t reg, u_int16_t *val)
+{
+ struct auglx_softc *sc = v;
+ u_int32_t codec_cntl, codec_status;
+ int i;
+
+ codec_cntl = RW_CMD | ((u_int32_t)reg << 24) | CMD_NEW;
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl);
+
+ for (i = AUGLX_TOUT; i; i--) {
+ codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+ ACC_CODEC_CNTL);
+ if (!(codec_cntl & CMD_NEW))
+ break;
+ delay(1);
+ }
+ if (codec_cntl & CMD_NEW) {
+ printf("%s: codec read timeout after write\n",
+ sc->sc_dev.dv_xname);
+ return -1;
+ }
+
+ for (i = AUGLX_TOUT; i; i--) {
+ codec_status = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+ ACC_CODEC_STATUS);
+ if ((codec_status & STS_NEW) && (codec_status >> 24 == reg))
+ break;
+ delay(10);
+ }
+ if (i == 0) {
+ printf("%s: codec status read timeout, 0x%08x\n",
+ sc->sc_dev.dv_xname, codec_status);
+ return -1;
+ }
+
+ *val = codec_status & 0xffff;
+ DPRINTF(AUGLX_DBG_ACC, ("%s: read codec register 0x%02x: 0x%04x\n",
+ sc->sc_dev.dv_xname, reg, *val));
+ return 0;
+}
+
+int
+auglx_write_codec(void *v, u_int8_t reg, u_int16_t val)
+{
+ struct auglx_softc *sc = v;
+ u_int32_t codec_cntl;
+ int i;
+
+ DPRINTF(AUGLX_DBG_ACC, ("%s: write codec register 0x%02x: 0x%04x\n",
+ sc->sc_dev.dv_xname, reg, val));
+
+
+ codec_cntl = ((u_int32_t)reg << 24) | CMD_NEW | val;
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl);
+
+ for (i = AUGLX_TOUT; i; i--) {
+ codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+ ACC_CODEC_CNTL);
+ if (!(codec_cntl & CMD_NEW))
+ break;
+ delay(1);
+ }
+ if (codec_cntl & CMD_NEW) {
+ printf("%s: codec write timeout\n", sc->sc_dev.dv_xname);
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+auglx_attach_codec(void *v, struct ac97_codec_if *cif)
+{
+ struct auglx_softc *sc = v;
+
+ sc->codec_if = cif;
+ return 0;
+}
+
+void
+auglx_reset_codec(void *v)
+{
+ struct auglx_softc *sc = v;
+ u_int32_t codec_cntl;
+ int i;
+
+ codec_cntl = LNK_WRM_RST | CMD_NEW;
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl);
+
+ for (i = AUGLX_TOUT; i; i--) {
+ codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
+ ACC_CODEC_CNTL);
+ if (!(codec_cntl & CMD_NEW))
+ continue;
+ delay(1);
+ }
+ if (codec_cntl & CMD_NEW)
+ printf("%s: codec reset timeout\n", sc->sc_dev.dv_xname);
+}
+
+enum ac97_host_flags
+auglx_flags_codec(void *v)
+{
+ return 0;
+}
+
+/*
+ * Audio functions
+ */
+int
+auglx_open(void *v, int flags)
+{
+ return 0;
+}
+
+void
+auglx_close(void *v)
+{
+}
+
+
+int
+auglx_query_encoding(void *v, struct audio_encoding *aep)
+{
+ switch (aep->index) {
+ case 0:
+ strlcpy(aep->name, AudioEulinear, sizeof aep->name);
+ aep->encoding = AUDIO_ENCODING_ULINEAR;
+ aep->precision = 8;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return 0;
+ case 1:
+ strlcpy(aep->name, AudioEmulaw, sizeof aep->name);
+ aep->encoding = AUDIO_ENCODING_ULAW;
+ aep->precision = 8;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return 0;
+ case 2:
+ strlcpy(aep->name, AudioEalaw, sizeof aep->name);
+ aep->encoding = AUDIO_ENCODING_ALAW;
+ aep->precision = 8;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return 0;
+ case 3:
+ strlcpy(aep->name, AudioEslinear, sizeof aep->name);
+ aep->encoding = AUDIO_ENCODING_SLINEAR;
+ aep->precision = 8;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return 0;
+ case 4:
+ strlcpy(aep->name, AudioEslinear_le, sizeof aep->name);
+ aep->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ aep->precision = 16;
+ aep->flags = 0;
+ return 0;
+ case 5:
+ strlcpy(aep->name, AudioEulinear_le, sizeof aep->name);
+ aep->encoding = AUDIO_ENCODING_ULINEAR_LE;
+ aep->precision = 16;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return 0;
+ case 6:
+ strlcpy(aep->name, AudioEslinear_be, sizeof aep->name);
+ aep->encoding = AUDIO_ENCODING_SLINEAR_BE;
+ aep->precision = 16;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return 0;
+ case 7:
+ strlcpy(aep->name, AudioEulinear_be, sizeof aep->name);
+ aep->encoding = AUDIO_ENCODING_ULINEAR_BE;
+ aep->precision = 16;
+ aep->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ return 0;
+ default:
+ return EINVAL;
+ }
+}
+
+
+int
+auglx_set_params(void *v, int setmode, int usemode, struct audio_params *play,
+ struct audio_params *rec)
+{
+ struct auglx_softc *sc = v;
+ int error;
+ u_int orate;
+ u_int adj_rate;
+
+ if (setmode & AUMODE_PLAY) {
+ play->factor = 1;
+ play->sw_code = NULL;
+ switch(play->encoding) {
+ case AUDIO_ENCODING_ULAW:
+ switch (play->channels) {
+ case 1:
+ play->factor = 4;
+ play->sw_code = mulaw_to_slinear16_le_mts;
+ break;
+ case 2:
+ play->factor = 2;
+ play->sw_code = mulaw_to_slinear16_le;
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case AUDIO_ENCODING_SLINEAR_LE:
+ switch (play->precision) {
+ case 8:
+ switch (play->channels) {
+ case 1:
+ play->factor = 4;
+ play->sw_code = linear8_to_linear16_le_mts;
+ break;
+ case 2:
+ play->factor = 2;
+ play->sw_code = linear8_to_linear16_le;
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case 16:
+ switch (play->channels) {
+ case 1:
+ play->factor = 2;
+ play->sw_code = noswap_bytes_mts;
+ break;
+ case 2:
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+ case AUDIO_ENCODING_ULINEAR_LE:
+ switch (play->precision) {
+ case 8:
+ switch (play->channels) {
+ case 1:
+ play->factor = 4;
+ play->sw_code = ulinear8_to_linear16_le_mts;
+ break;
+ case 2:
+ play->factor = 2;
+ play->sw_code = ulinear8_to_linear16_le;
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case 16:
+ switch (play->channels) {
+ case 1:
+ play->factor = 2;
+ play->sw_code = change_sign16_le_mts;
+ break;
+ case 2:
+ play->sw_code = change_sign16_le;
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case AUDIO_ENCODING_ALAW:
+ switch (play->channels) {
+ case 1:
+ play->factor = 4;
+ play->sw_code = alaw_to_slinear16_le_mts;
+ break;
+ case 2:
+ play->factor = 2;
+ play->sw_code = alaw_to_slinear16_le;
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case AUDIO_ENCODING_SLINEAR_BE:
+ switch (play->precision) {
+ case 8:
+ switch (play->channels) {
+ case 1:
+ play->factor = 4;
+ play->sw_code = linear8_to_linear16_le_mts;
+ break;
+ case 2:
+ play->factor = 2;
+ play->sw_code = linear8_to_linear16_le;
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case 16:
+ switch (play->channels) {
+ case 1:
+ play->factor = 2;
+ play->sw_code = swap_bytes_mts;
+ break;
+ case 2:
+ play->sw_code = swap_bytes;
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case AUDIO_ENCODING_ULINEAR_BE:
+ switch (play->precision) {
+ case 8:
+ switch (play->channels) {
+ case 1:
+ play->factor = 4;
+ play->sw_code = ulinear8_to_linear16_le_mts;
+ break;
+ case 2:
+ play->factor = 2;
+ play->sw_code = ulinear8_to_linear16_le;
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case 16:
+ switch (play->channels) {
+ case 1:
+ play->factor = 2;
+ play->sw_code = swap_bytes_change_sign16_le_mts;
+ break;
+ case 2:
+ play->sw_code = swap_bytes_change_sign16_le;
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+
+ orate = adj_rate = play->sample_rate;
+
+ play->sample_rate = adj_rate;
+ error = ac97_set_rate(sc->codec_if,
+ AC97_REG_PCM_LFE_DAC_RATE, &play->sample_rate);
+ if (error)
+ return error;
+
+ play->sample_rate = adj_rate;
+ error = ac97_set_rate(sc->codec_if,
+ AC97_REG_PCM_SURR_DAC_RATE, &play->sample_rate);
+ if (error)
+ return error;
+
+ play->sample_rate = adj_rate;
+ error = ac97_set_rate(sc->codec_if,
+ AC97_REG_PCM_FRONT_DAC_RATE, &play->sample_rate);
+ if (error)
+ return error;
+
+ if (play->sample_rate == adj_rate)
+ play->sample_rate = orate;
+ }
+
+ if (setmode & AUMODE_RECORD) {
+ rec->factor = 1;
+ rec->sw_code = 0;
+ switch(rec->encoding) {
+ case AUDIO_ENCODING_ULAW:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = slinear16_to_mulaw_le_stm;
+ rec->factor = 4;
+ break;
+ case 2:
+ rec->sw_code = slinear16_to_mulaw_le;
+ rec->factor = 2;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_ALAW:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = slinear16_to_alaw_le_stm;
+ rec->factor = 4;
+ break;
+ case 2:
+ rec->sw_code = slinear16_to_alaw_le;
+ rec->factor = 2;
+ break;
+ }
+ break;
+ case AUDIO_ENCODING_SLINEAR_LE:
+ switch (rec->precision) {
+ case 8:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = linear16_to_linear8_le_stm;
+ rec->factor = 4;
+ break;
+ case 2:
+ rec->sw_code = linear16_to_linear8_le;
+ rec->factor = 2;
+ break;
+ }
+ break;
+ case 16:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = linear16_decimator;
+ rec->factor = 2;
+ break;
+ case 2:
+ break;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case AUDIO_ENCODING_ULINEAR_LE:
+ switch (rec->precision) {
+ case 8:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = linear16_to_ulinear8_le_stm;
+ rec->factor = 4;
+ break;
+ case 2:
+ rec->sw_code = linear16_to_ulinear8_le;
+ rec->factor = 2;
+ break;
+ }
+ break;
+ case 16:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = change_sign16_le_stm;
+ rec->factor = 2;
+ break;
+ case 2:
+ rec->sw_code = change_sign16_le;
+ break;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case AUDIO_ENCODING_SLINEAR_BE:
+ switch (rec->precision) {
+ case 8:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = linear16_to_linear8_le_stm;
+ rec->factor = 4;
+ break;
+ case 2:
+ rec->sw_code = linear16_to_linear8_le;
+ rec->factor = 2;
+ break;
+ }
+ break;
+ case 16:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = swap_bytes_stm;
+ rec->factor = 2;
+ break;
+ case 2:
+ rec->sw_code = swap_bytes;
+ break;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ case AUDIO_ENCODING_ULINEAR_BE:
+ switch (rec->precision) {
+ case 8:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = linear16_to_ulinear8_le_stm;
+ rec->factor = 4;
+ break;
+ case 2:
+ rec->sw_code = linear16_to_ulinear8_le;
+ rec->factor = 2;
+ break;
+ }
+ break;
+ case 16:
+ switch (rec->channels) {
+ case 1:
+ rec->sw_code = change_sign16_swap_bytes_le_stm;
+ rec->factor = 2;
+ break;
+ case 2:
+ rec->sw_code = change_sign16_swap_bytes_le;
+ break;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+
+ orate = rec->sample_rate;
+ error = ac97_set_rate(sc->codec_if, AC97_REG_PCM_LR_ADC_RATE,
+ &rec->sample_rate);
+ if (error)
+ return error;
+ rec->sample_rate = orate;
+ }
+
+ return 0;
+}
+
+int
+auglx_round_blocksize(void *v, int blk)
+{
+ return (blk + 0x3f) & ~0x3f;
+}
+
+int
+auglx_halt_output(void *v)
+{
+ struct auglx_softc *sc = v;
+
+ DPRINTF(AUGLX_DBG_DMA, ("%s: halt_output\n", sc->sc_dev.dv_xname));
+
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD, 0x00);
+ sc->bm0.intr = NULL;
+ return 0;
+}
+
+int
+auglx_halt_input(void *v)
+{
+ struct auglx_softc *sc = v;
+
+ DPRINTF(AUGLX_DBG_DMA,
+ ("%s: halt_input\n", sc->sc_dev.dv_xname));
+
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD, 0x00);
+ sc->bm1.intr = NULL;
+ return 0;
+}
+
+int
+auglx_getdev(void *v, struct audio_device *adp)
+{
+ struct auglx_softc *sc = v;
+ *adp = sc->sc_audev;
+ return 0;
+}
+
+int
+auglx_set_port(void *v, mixer_ctrl_t *cp)
+{
+ struct auglx_softc *sc = v;
+ return sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp);
+}
+
+int
+auglx_get_port(void *v, mixer_ctrl_t *cp)
+{
+ struct auglx_softc *sc = v;
+ return sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp);
+}
+
+int
+auglx_query_devinfo(void *v, mixer_devinfo_t *dp)
+{
+ struct auglx_softc *sc = v;
+ return sc->codec_if->vtbl->query_devinfo(sc->codec_if, dp);
+}
+
+void *
+auglx_allocm(void *v, int direction, size_t size, int pool, int flags)
+{
+ struct auglx_softc *sc = v;
+ struct auglx_dma *p;
+ int error;
+
+ DPRINTF(AUGLX_DBG_DMA, ("%s: request buffer of size %ld, dir %d\n",
+ sc->sc_dev.dv_xname, size, direction));
+
+ /* can only use 1 segment */
+ if (size > AUGLX_DMASEG_MAX) {
+ DPRINTF(AUGLX_DBG_DMA,
+ ("%s: requested buffer size too large: %d", \
+ sc->sc_dev.dv_xname, size));
+ return NULL;
+ }
+
+ p = malloc(sizeof(*p), pool, flags | M_ZERO);
+ if (!p)
+ return NULL;
+
+ error = auglx_allocmem(sc, size, PAGE_SIZE, p);
+ if (error) {
+ free(p, pool);
+ return NULL;
+ }
+
+ p->next = sc->sc_dmas;
+ sc->sc_dmas = p;
+
+ return p->addr;
+}
+
+void
+auglx_freem(void *v, void *ptr, int pool)
+{
+ struct auglx_softc *sc;
+ struct auglx_dma *p, **pp;
+
+ sc = v;
+ for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) {
+ if (p->addr == ptr) {
+ auglx_freemem(sc, p);
+ *pp = p->next;
+ free(p, pool);
+ return;
+ }
+ }
+}
+
+
+size_t
+auglx_round_buffersize(void *v, int direction, size_t size)
+{
+ if (size > AUGLX_DMASEG_MAX)
+ size = AUGLX_DMASEG_MAX;
+
+ return size;
+}
+
+paddr_t
+auglx_mappage(void *v, void *mem, off_t off, int prot)
+{
+ struct auglx_softc *sc = v;
+ struct auglx_dma *p;
+
+ if (off < 0)
+ return -1;
+
+ for (p = sc->sc_dmas; p && p->addr != mem; p = p->next);
+ if (!p)
+ return -1;
+
+ return bus_dmamem_mmap(sc->sc_dmat, p->segs, p->nsegs,
+ off, prot, BUS_DMA_WAITOK);
+}
+
+int
+auglx_get_props(void *v)
+{
+ return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
+}
+
+int
+auglx_intr(void *v)
+{
+ struct auglx_softc *sc = v;
+ u_int16_t irq_sts;
+ u_int8_t bm_sts;
+
+ irq_sts = bus_space_read_2(sc->sc_iot, sc->sc_ioh, ACC_IRQ_STATUS);
+ if (irq_sts == 0)
+ return 0;
+
+ if (irq_sts & BM0_IRQ_STS) {
+ bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+ ACC_BM0_STATUS);
+ if (sc->bm0.intr) {
+ sc->bm0.intr(sc->bm0.arg);
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD,
+ BMx_CMD_BM_CTL_EN);
+ }
+ } else if (irq_sts & BM1_IRQ_STS) {
+ bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
+ ACC_BM1_STATUS);
+ if (sc->bm1.intr) {
+ sc->bm1.intr(sc->bm1.arg);
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD,
+ BMx_CMD_RW | BMx_CMD_BM_CTL_EN);
+ }
+ } else {
+ DPRINTF(AUGLX_DBG_IRQ, ("%s: stray intr, status = 0x%04x\n",
+ sc->sc_dev.dv_xname, irq_sts));
+ return -1;
+ }
+ return 1;
+}
+
+int
+auglx_trigger_output(void *v, void *start, void *end, int blksize,
+ void (*intr)(void *), void *arg, struct audio_params *param)
+{
+ struct auglx_softc *sc = v;
+ struct auglx_dma *p;
+ size_t size;
+ u_int32_t addr;
+ int i, nprd;
+
+ size = (size_t)((caddr_t)end - (caddr_t)start);
+ DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_output, %p 0x%08x bytes, "
+ "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize));
+
+ for (p = sc->sc_dmas; p && p->addr != start; p = p->next);
+ if (!p) {
+ DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n",
+ sc->sc_dev.dv_xname));
+ return -1;
+ }
+
+ /* set up the PRDs */
+ nprd = size / blksize;
+ if (sc->bm0.sc_nprd != nprd + 1) {
+ if (sc->bm0.sc_nprd > 0)
+ auglx_free_prd(sc, &sc->bm0);
+ sc->bm0.sc_nprd = nprd + 1;
+ auglx_alloc_prd(sc,
+ sc->bm0.sc_nprd * sizeof(struct auglx_prd), &sc->bm0);
+ }
+ DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname,
+ nprd));
+ addr = p->segs->ds_addr;
+ for (i = 0; i < nprd; i++) {
+ sc->bm0.sc_vprd[i].base = addr;
+ sc->bm0.sc_vprd[i].size = blksize | AUGLX_PRD_EOP;
+ (char *)addr += blksize;
+ }
+ sc->bm0.sc_vprd[i].base = sc->bm0.sc_prd->dm_segs[0].ds_addr;
+ sc->bm0.sc_vprd[i].size = AUGLX_PRD_JMP;
+
+#ifdef AUGLX_DEBUG
+ for (i = 0; i < sc->bm0.sc_nprd; i++)
+ DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n",
+ sc->sc_dev.dv_xname, i, sc->bm0.sc_vprd[i].base,
+ sc->bm0.sc_vprd[i].size));
+#endif
+ sc->bm0.intr = intr;
+ sc->bm0.arg = arg;
+
+ /* Program the BM0 PRD register */
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM0_PRD,
+ sc->bm0.sc_prd->dm_segs[0].ds_addr);
+ /* Start Audio Bus Master 0 */
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD,
+ BMx_CMD_BM_CTL_EN);
+ return 0;
+}
+
+int
+auglx_trigger_input(void *v, void *start, void *end, int blksize,
+ void (*intr)(void *), void * arg, struct audio_params *param)
+{
+ struct auglx_softc *sc = v;
+ struct auglx_dma *p;
+ size_t size;
+ u_int32_t addr;
+ int i, nprd;
+
+ size = (size_t)((caddr_t)end - (caddr_t)start);
+ DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_input, %p 0x%08x bytes, "
+ "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize));
+
+ for (p = sc->sc_dmas; p && p->addr != start; p = p->next);
+ if (!p) {
+ DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n",
+ sc->sc_dev.dv_xname));
+ return -1;
+ }
+
+ /* set up the PRDs */
+ nprd = size / blksize;
+ if (sc->bm1.sc_nprd != nprd + 1) {
+ if (sc->bm1.sc_nprd > 0)
+ auglx_free_prd(sc, &sc->bm1);
+ sc->bm1.sc_nprd = nprd + 1;
+ auglx_alloc_prd(sc,
+ sc->bm1.sc_nprd * sizeof(struct auglx_prd), &sc->bm1);
+ }
+ DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname,
+ nprd));
+ addr = p->segs->ds_addr;
+ for (i = 0; i < nprd; i++) {
+ sc->bm1.sc_vprd[i].base = addr;
+ sc->bm1.sc_vprd[i].size = blksize | AUGLX_PRD_EOP;
+ (char *)addr += blksize;
+ }
+ sc->bm1.sc_vprd[i].base = sc->bm1.sc_prd->dm_segs[0].ds_addr;
+ sc->bm1.sc_vprd[i].size = AUGLX_PRD_JMP;
+
+#ifdef AUGLX_DEBUG
+ for (i = 0; i < sc->bm1.sc_nprd; i++)
+ DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n",
+ sc->sc_dev.dv_xname, i, sc->bm1.sc_vprd[i].base,
+ sc->bm1.sc_vprd[i].size));
+#endif
+ sc->bm1.intr = intr;
+ sc->bm1.arg = arg;
+
+ /* Program the BM1 PRD register */
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM1_PRD,
+ sc->bm1.sc_prd->dm_segs[0].ds_addr);
+ /* Start Audio Bus Master 0 */
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD,
+ BMx_CMD_RW | BMx_CMD_BM_CTL_EN);
+ return 0;
+}
+
+int
+auglx_allocmem(struct auglx_softc *sc, size_t size, size_t align,
+ struct auglx_dma *p)
+{
+ int error;
+
+ p->size = size;
+ error = bus_dmamem_alloc(sc->sc_dmat, p->size, align, 0, p->segs, 1,
+ &p->nsegs, BUS_DMA_NOWAIT);
+ if (error) {
+ DPRINTF(AUGLX_DBG_DMA,
+ ("%s: bus_dmamem_alloc failed: error %d\n",
+ sc->sc_dev.dv_xname, error));
+ return error;
+ }
+
+ error = bus_dmamem_map(sc->sc_dmat, p->segs, 1, p->size, &p->addr,
+ BUS_DMA_NOWAIT | sc->sc_dmamap_flags);
+ if (error) {
+ DPRINTF(AUGLX_DBG_DMA,
+ ("%s: bus_dmamem_map failed: error %d\n",
+ sc->sc_dev.dv_xname, error));
+ goto free;
+ }
+
+ error = bus_dmamap_create(sc->sc_dmat, p->size, 1, p->size, 0,
+ BUS_DMA_NOWAIT, &p->map);
+ if (error) {
+ DPRINTF(AUGLX_DBG_DMA,
+ ("%s: bus_dmamap_create failed: error %d\n",
+ sc->sc_dev.dv_xname, error));
+ goto unmap;
+ }
+
+ error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, p->size, NULL,
+ BUS_DMA_NOWAIT);
+ if (error) {
+ DPRINTF(AUGLX_DBG_DMA,
+ ("%s: bus_dmamap_load failed: error %d\n",
+ sc->sc_dev.dv_xname, error));
+ goto destroy;
+ }
+ return 0;
+
+ destroy:
+ bus_dmamap_destroy(sc->sc_dmat, p->map);
+ unmap:
+ bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
+ free:
+ bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
+ return error;
+}
+
+void
+auglx_freemem(struct auglx_softc *sc, struct auglx_dma *p)
+{
+ bus_dmamap_unload(sc->sc_dmat, p->map);
+ bus_dmamap_destroy(sc->sc_dmat, p->map);
+ bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
+ bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
+}
+
+void
+auglx_get_default_params(void *addr, int mode, struct audio_params *params)
+{
+ ac97_get_default_params(params);
+}
+
+int
+auglx_alloc_prd(struct auglx_softc *sc, size_t size, struct auglx_ring *bm)
+{
+ int error, rseg;
+
+ /*
+ * Allocate PRD table structure, and create and load the
+ * DMA map for it.
+ */
+ if ((error = bus_dmamem_alloc(sc->sc_dmat, size,
+ PAGE_SIZE, 0, &bm->seg, 1, &rseg, 0)) != 0) {
+ printf("%s: unable to allocate PRD, error = %d\n",
+ sc->sc_dev.dv_xname, error);
+ goto fail_0;
+ }
+
+ if ((error = bus_dmamem_map(sc->sc_dmat, &bm->seg, rseg,
+ size, (caddr_t *)&bm->sc_vprd,
+ sc->sc_dmamap_flags)) != 0) {
+ printf("%s: unable to map PRD, error = %d\n",
+ sc->sc_dev.dv_xname, error);
+ goto fail_1;
+ }
+
+ if ((error = bus_dmamap_create(sc->sc_dmat, size,
+ 1, size, 0, 0, &bm->sc_prd)) != 0) {
+ printf("%s: unable to create PRD DMA map, "
+ "error = %d\n", sc->sc_dev.dv_xname, error);
+ goto fail_2;
+ }
+
+ if ((error = bus_dmamap_load(sc->sc_dmat, bm->sc_prd, bm->sc_vprd,
+ size, NULL, 0)) != 0) {
+ printf("%s: unable tp load control data DMA map, "
+ "error = %d\n", sc->sc_dev.dv_xname, error);
+ goto fail_3;
+ }
+
+ return 0;
+
+ fail_3:
+ bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd);
+ fail_2:
+ bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd,
+ sizeof(struct auglx_prd));
+ fail_1:
+ bus_dmamem_free(sc->sc_dmat, &bm->seg, rseg);
+ fail_0:
+ return error;
+}
+
+void
+auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm)
+{
+ bus_dmamap_unload(sc->sc_dmat, bm->sc_prd);
+ bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd);
+ bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd, bm->sc_size);
+ bus_dmamem_free(sc->sc_dmat, &bm->seg, bm->nsegs);
+}
+
+void
+auglx_powerhook(int why, void *self)
+{
+ struct auglx_softc *sc = self;
+
+ if (why != PWR_RESUME) {
+ /* Power down */
+ sc->sc_suspend = why;
+ auglx_read_codec(sc, AC97_REG_EXT_AUDIO_CTRL, &sc->sc_ext_ctrl);
+ } else {
+ /* Wake up */
+ if (sc->sc_suspend == PWR_RESUME) {
+ printf("%s: resume without suspend?\n",
+ sc->sc_dev.dv_xname);
+ sc->sc_suspend = why;
+ return;
+ }
+ sc->sc_suspend = why;
+ auglx_reset_codec(sc);
+ delay(1000);
+ (sc->codec_if->vtbl->restore_ports)(sc->codec_if);
+ auglx_write_codec(sc, AC97_REG_EXT_AUDIO_CTRL, sc->sc_ext_ctrl);
+ }
+}