summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authordes <des@FreeBSD.org>2003-06-01 11:58:46 +0000
committerdes <des@FreeBSD.org>2003-06-01 11:58:46 +0000
commit0e25b20be3d78e8c9078469b0519757c84976744 (patch)
tree10ac71fcce3aeae8b452a9cf36eec98b6c833fbf /sys
parente750473b958c8b10e8fa503c619919086e43eba9 (diff)
downloadFreeBSD-src-0e25b20be3d78e8c9078469b0519757c84976744.zip
FreeBSD-src-0e25b20be3d78e8c9078469b0519757c84976744.tar.gz
Add (but do not connect) a half-finished driver for Aureal Vortex cards.
The mixer works, pcm support is half done.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/sound/pci/au88x0.c637
-rw-r--r--sys/dev/sound/pci/au88x0.h174
-rw-r--r--sys/modules/sound/driver/au88x0/Makefile9
3 files changed, 820 insertions, 0 deletions
diff --git a/sys/dev/sound/pci/au88x0.c b/sys/dev/sound/pci/au88x0.c
new file mode 100644
index 0000000..3bf227b
--- /dev/null
+++ b/sys/dev/sound/pci/au88x0.c
@@ -0,0 +1,637 @@
+/*-
+ * Copyright (c) 2003 Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pcm/ac97.h>
+#include <dev/sound/pci/au88x0.h>
+
+#include <machine/bus.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+
+/***************************************************************************\
+ * *
+ * FORMATS AND CAPABILITIES *
+ * *
+\***************************************************************************/
+
+static u_int32_t au88x0_formats[] = {
+ AFMT_U8,
+ AFMT_STEREO | AFMT_U8,
+ AFMT_S16_LE,
+ AFMT_STEREO | AFMT_S16_LE,
+ 0
+};
+
+static struct pcmchan_caps au88x0_capabilities = {
+ 4000, /* minimum sample rate */
+ 48000, /* maximum sample rate */
+ au88x0_formats, /* supported formats */
+ 0 /* no particular capabilities */
+};
+
+
+/***************************************************************************\
+ * *
+ * CODEC INTERFACE *
+ * *
+\***************************************************************************/
+
+/*
+ * Read from the au88x0 register space
+ */
+#if 1
+/* all our writes are 32-bit */
+#define au88x0_read(aui, reg, n) \
+ bus_space_read_4((aui)->aui_spct, (aui)->aui_spch, (reg))
+#define au88x0_write(aui, reg, data, n) \
+ bus_space_write_4((aui)->aui_spct, (aui)->aui_spch, (reg), (data))
+#else
+static uint32_t
+au88x0_read(struct au88x0_info *aui, int reg, int size)
+{
+ uint32_t data;
+
+ switch (size) {
+ case 1:
+ data = bus_space_read_1(aui->aui_spct, aui->aui_spch, reg);
+ break;
+ case 2:
+ data = bus_space_read_2(aui->aui_spct, aui->aui_spch, reg);
+ break;
+ case 4:
+ data = bus_space_read_4(aui->aui_spct, aui->aui_spch, reg);
+ break;
+ default:
+ panic("unsupported read size %d", size);
+ }
+ return (data);
+}
+
+/*
+ * Write to the au88x0 register space
+ */
+static void
+au88x0_write(struct au88x0_info *aui, int reg, uint32_t data, int size)
+{
+
+ switch (size) {
+ case 1:
+ bus_space_write_1(aui->aui_spct, aui->aui_spch, reg, data);
+ break;
+ case 2:
+ bus_space_write_2(aui->aui_spct, aui->aui_spch, reg, data);
+ break;
+ case 4:
+ bus_space_write_4(aui->aui_spct, aui->aui_spch, reg, data);
+ break;
+ default:
+ panic("unsupported write size %d", size);
+ }
+}
+#endif
+
+/*
+ * Reset and initialize the codec
+ */
+static void
+au88x0_codec_init(struct au88x0_info *aui)
+{
+ uint32_t data;
+ int i;
+
+ /* wave that chicken */
+ au88x0_write(aui, AU88X0_CODEC_CONTROL, 0x8068, 4);
+ DELAY(AU88X0_SETTLE_DELAY);
+ au88x0_write(aui, AU88X0_CODEC_CONTROL, 0x00e8, 4);
+ DELAY(1000);
+ for (i = 0; i < 32; ++i) {
+ au88x0_write(aui, AU88X0_CODEC_CHANNEL + i * 4, 0, 4);
+ DELAY(AU88X0_SETTLE_DELAY);
+ }
+ au88x0_write(aui, AU88X0_CODEC_CONTROL, 0x00e8, 4);
+ DELAY(AU88X0_SETTLE_DELAY);
+
+ /* enable both codec channels */
+ data = au88x0_read(aui, AU88X0_CODEC_ENABLE, 4);
+ data |= (1 << (8 + 0)) | (1 << (8 + 1));
+ au88x0_write(aui, AU88X0_CODEC_ENABLE, data, 4);
+ DELAY(AU88X0_SETTLE_DELAY);
+}
+
+/*
+ * Wait for the codec to get ready to accept a register write
+ * Should be called at spltty
+ */
+static int
+au88x0_codec_wait(struct au88x0_info *aui)
+{
+ uint32_t data;
+ int i;
+
+ for (i = 0; i < AU88X0_RETRY_COUNT; ++i) {
+ data = au88x0_read(aui, AU88X0_CODEC_CONTROL, 4);
+ if (data & AU88X0_CDCTL_WROK)
+ return (0);
+ DELAY(AU88X0_SETTLE_DELAY);
+ }
+ device_printf(aui->aui_dev, "timeout while waiting for codec\n");
+ return (-1);
+}
+
+/*
+ * Read from the ac97 codec
+ */
+static int
+au88x0_codec_read(kobj_t obj, void *arg, int reg)
+{
+ struct au88x0_info *aui = arg;
+ uint32_t data;
+ int sl;
+
+ sl = spltty();
+ au88x0_codec_wait(aui);
+ au88x0_write(aui, AU88X0_CODEC_IO, AU88X0_CDIO_READ(reg), 4);
+ DELAY(1000);
+ data = au88x0_read(aui, AU88X0_CODEC_IO, 4);
+ splx(sl);
+ data &= AU88X0_CDIO_DATA_MASK;
+ data >>= AU88X0_CDIO_DATA_SHIFT;
+ return (data);
+}
+
+/*
+ * Write to the ac97 codec
+ */
+static int
+au88x0_codec_write(kobj_t obj, void *arg, int reg, uint32_t data)
+{
+ struct au88x0_info *aui = arg;
+ int sl;
+
+ sl = spltty();
+ au88x0_codec_wait(aui);
+ au88x0_write(aui, AU88X0_CODEC_IO, AU88X0_CDIO_WRITE(reg, data), 4);
+ splx(sl);
+ return 0;
+}
+
+/*
+ * Codec interface glue
+ */
+static kobj_method_t au88x0_ac97_methods[] = {
+ KOBJMETHOD(ac97_read, au88x0_codec_read),
+ KOBJMETHOD(ac97_write, au88x0_codec_write),
+ { 0, 0 }
+};
+AC97_DECLARE(au88x0_ac97);
+
+#define au88x0_channel(aui, dir) \
+ &(aui)->aui_chan[((dir) == PCMDIR_PLAY) ? 0 : 1]
+
+
+/***************************************************************************\
+ * *
+ * CHANNEL INTERFACE *
+ * *
+\***************************************************************************/
+
+/*
+ * Initialize a PCM channel
+ */
+static void *
+au88x0_chan_init(kobj_t obj, void *arg,
+ struct snd_dbuf *buf, struct pcm_channel *chan, int dir)
+{
+ struct au88x0_info *aui = arg;
+ struct au88x0_chan_info *auci = au88x0_channel(aui, dir);
+
+ if (sndbuf_alloc(buf, aui->aui_dmat, aui->aui_bufsize) == -1)
+ return (NULL);
+ auci->auci_aui = aui;
+ auci->auci_pcmchan = chan;
+ auci->auci_buf = buf;
+ auci->auci_dir = dir;
+ return (auci);
+}
+
+/*
+ * Set the data format for a PCM channel
+ */
+static int
+au88x0_chan_setformat(kobj_t obj, void *arg, u_int32_t format)
+{
+
+ /* XXX */
+ return (ENXIO);
+}
+
+/*
+ * Set the sample rate for a PCM channel
+ */
+static int
+au88x0_chan_setspeed(kobj_t obj, void *arg, u_int32_t speed)
+{
+
+ /* XXX */
+ return (speed);
+}
+
+/*
+ * Set the block size for a PCM channel
+ */
+static int
+au88x0_chan_setblocksize(kobj_t obj, void *arg, u_int32_t blocksize)
+{
+
+ /* XXX */
+ return (blocksize);
+}
+
+/*
+ * Initiate a data transfer
+ */
+static int
+au88x0_chan_trigger(kobj_t obj, void *arg, int trigger)
+{
+ struct au88x0_chan_info *auci = arg;
+
+ (void)auci;
+ switch (trigger) {
+ case PCMTRIG_START:
+ break;
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ break;
+ }
+ return (0);
+}
+
+/*
+ *
+ */
+static int
+au88x0_chan_getptr(kobj_t obj, void *arg)
+{
+
+ /* XXX */
+ return (0);
+}
+
+/*
+ * Return the capabilities of a PCM channel
+ */
+static struct pcmchan_caps *
+au88x0_chan_getcaps(kobj_t obj, void *arg)
+{
+
+ return (&au88x0_capabilities);
+}
+
+/*
+ * Channel interface glue
+ */
+static kobj_method_t au88x0_chan_methods[] = {
+ KOBJMETHOD(channel_init, au88x0_chan_init),
+ KOBJMETHOD(channel_setformat, au88x0_chan_setformat),
+ KOBJMETHOD(channel_setspeed, au88x0_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize, au88x0_chan_setblocksize),
+ KOBJMETHOD(channel_trigger, au88x0_chan_trigger),
+ KOBJMETHOD(channel_getptr, au88x0_chan_getptr),
+ KOBJMETHOD(channel_getcaps, au88x0_chan_getcaps),
+ { 0, 0 }
+};
+CHANNEL_DECLARE(au88x0_chan);
+
+
+/***************************************************************************\
+ * *
+ * INTERRUPT HANDLER *
+ * *
+\***************************************************************************/
+
+static void
+au88x0_intr(void *arg)
+{
+ struct au88x0_info *aui = arg;
+ int pending, source;
+
+ pending = au88x0_read(aui, AU88X0_IRQ_PENDING, 4);
+ if ((pending & AU88X0_IRQ_PENDING_BIT) == 0)
+ return;
+ source = au88x0_read(aui, AU88X0_IRQ_SOURCE, 4);
+ if (source & AU88X0_IRQ_FATAL_ERR)
+ device_printf(aui->aui_dev,
+ "fatal error interrupt received\n");
+ if (source & AU88X0_IRQ_PARITY_ERR)
+ device_printf(aui->aui_dev,
+ "parity error interrupt received\n");
+ /* XXX handle the others... */
+
+ /* acknowledge the interrupts we just handled */
+ au88x0_write(aui, AU88X0_IRQ_SOURCE, source, 4);
+ au88x0_read(aui, AU88X0_IRQ_SOURCE, 4);
+}
+
+
+/***************************************************************************\
+ * *
+ * INITIALIZATION *
+ * *
+\***************************************************************************/
+
+/*
+ * Reset and initialize the ADB and WT FIFOs
+ *
+ * - need to find out what the magic values 0x42000 and 0x2000 mean.
+ */
+static void
+au88x0_fifo_init(struct au88x0_info *aui)
+{
+ int i;
+
+ /* reset, then clear the ADB FIFOs */
+ for (i = 0; i < AU88X0_ADB_FIFOS; ++i)
+ au88x0_write(aui, AU88X0_ADB_FIFO_CTL + i * 4, 0x42000, 4);
+ for (i = 0; i < AU88X0_ADB_FIFOS * AU88X0_ADB_FIFO_SIZE; ++i)
+ au88x0_write(aui, AU88X0_ADB_FIFO_BASE + i * 4, 0, 4);
+
+ /* reset, then clear the WT FIFOs */
+ for (i = 0; i < AU88X0_WT_FIFOS; ++i)
+ au88x0_write(aui, AU88X0_WT_FIFO_CTL + i * 4, 0x42000, 4);
+ for (i = 0; i < AU88X0_WT_FIFOS * AU88X0_WT_FIFO_SIZE; ++i)
+ au88x0_write(aui, AU88X0_WT_FIFO_BASE + i * 4, 0, 4);
+}
+
+/*
+ * Hardware initialization
+ */
+static void
+au88x0_init(struct au88x0_info *aui)
+{
+
+ /* reset the chip */
+ au88x0_write(aui, AU88X0_CONTROL, 0xffffffff, 4);
+ DELAY(10000);
+
+ /* clear all interrupts */
+ au88x0_write(aui, AU88X0_IRQ_SOURCE, 0xffffffff, 4);
+ au88x0_read(aui, AU88X0_IRQ_SOURCE, 4);
+ au88x0_read(aui, AU88X0_IRQ_STATUS, 4);
+
+ /* initialize the codec */
+ au88x0_codec_init(aui);
+
+ /* initialize the fifos */
+ au88x0_fifo_init(aui);
+
+ /* initialize the DMA engine */
+ /* XXX chicken-waving! */
+ au88x0_write(aui, AU88X0_DMA_CONTROL, 0x1380000, 4);
+}
+
+/*
+ * Construct and set status string
+ */
+static void
+au88x0_set_status(device_t dev)
+{
+ char status[SND_STATUSLEN];
+ struct au88x0_info *aui;
+
+ aui = pcm_getdevinfo(dev);
+ snprintf(status, sizeof status, "at %s 0x%lx irq %ld",
+ (aui->aui_regtype == SYS_RES_IOPORT)? "io" : "memory",
+ rman_get_start(aui->aui_reg), rman_get_start(aui->aui_irq));
+ pcm_setstatus(dev, status);
+}
+
+
+/***************************************************************************\
+ * *
+ * PCI INTERFACE *
+ * *
+\***************************************************************************/
+
+/*
+ * Probe
+ */
+static int
+au88x0_pci_probe(device_t dev)
+{
+
+ switch (pci_get_devid(dev)) {
+ case AUREAL_VORTEX_2:
+ device_set_desc(dev, "Aureal Vortex 2");
+ return (0);
+ case AUREAL_VORTEX_ADVANTAGE:
+ device_set_desc(dev, "Aureal Vortex Advantage");
+ return (0);
+ default:
+ return (ENXIO);
+ }
+ return (0);
+}
+
+/*
+ * Attach
+ */
+static int
+au88x0_pci_attach(device_t dev)
+{
+ struct au88x0_info *aui = NULL;
+ uint32_t config;
+ int error;
+
+ if ((aui = malloc(sizeof *aui, M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) {
+ device_printf(dev, "failed to allocate softc\n");
+ return (ENXIO);
+ }
+ aui->aui_dev = dev;
+
+ /* Model-specific parameters */
+ aui->aui_model = pci_get_devid(dev);
+ switch (aui->aui_model) {
+ case AUREAL_VORTEX_1:
+ break;
+ case AUREAL_VORTEX_2:
+ case AUREAL_VORTEX_ADVANTAGE:
+ break;
+ default:
+ panic("%s() called for non-au88x0 device", __func__);
+ }
+
+ /* enable pio, mmio, bus-mastering dma */
+ config = pci_read_config(dev, PCIR_COMMAND, 2);
+ config |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
+ pci_write_config(dev, PCIR_COMMAND, config, 2);
+
+ /* register mapping */
+ config = pci_read_config(dev, PCIR_COMMAND, 2);
+ if (config & PCIM_CMD_MEMEN) {
+ /* try memory-mapped I/O */
+ aui->aui_regid = PCIR_MAPS;
+ aui->aui_regtype = SYS_RES_MEMORY;
+ aui->aui_reg = bus_alloc_resource(dev, aui->aui_regtype,
+ &aui->aui_regid, 0, ~0, 1, RF_ACTIVE);
+ }
+ if (aui->aui_reg == NULL && (config & PCIM_CMD_PORTEN)) {
+ /* fall back on port I/O */
+ aui->aui_regid = PCIR_MAPS;
+ aui->aui_regtype = SYS_RES_IOPORT;
+ aui->aui_reg = bus_alloc_resource(dev, aui->aui_regtype,
+ &aui->aui_regid, 0, ~0, 1, RF_ACTIVE);
+ }
+ if (aui->aui_reg == NULL) {
+ /* both mmio and pio failed... */
+ device_printf(dev, "failed to map registers\n");
+ goto failed;
+ }
+ aui->aui_spct = rman_get_bustag(aui->aui_reg);
+ aui->aui_spch = rman_get_bushandle(aui->aui_reg);
+
+ /* IRQ mapping */
+ aui->aui_irqid = 0;
+ aui->aui_irqtype = SYS_RES_IRQ;
+ aui->aui_irq = bus_alloc_resource(dev, aui->aui_irqtype,
+ &aui->aui_irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+ if (aui->aui_irq == 0) {
+ device_printf(dev, "failed to map IRQ\n");
+ goto failed;
+ }
+
+ /* install interrupt handler */
+ error = snd_setup_intr(dev, aui->aui_irq, 0, au88x0_intr,
+ aui, &aui->aui_irqh);
+ if (error != 0) {
+ device_printf(dev, "failed to install interrupt handler\n");
+ goto failed;
+ }
+
+ /* DMA mapping */
+ aui->aui_bufsize = pcm_getbuffersize(dev, AU88X0_BUFSIZE_MIN,
+ AU88X0_BUFSIZE_DFLT, AU88X0_BUFSIZE_MAX);
+ error = bus_dma_tag_create(NULL,
+ 2, 0, /* 16-bit alignment, no boundary */
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, /* restrict to 4GB */
+ NULL, NULL, /* no filter */
+ aui->aui_bufsize, 1, aui->aui_bufsize,
+ 0, &aui->aui_dmat);
+ if (error != 0) {
+ device_printf(dev, "failed to create DMA tag\n");
+ goto failed;
+ }
+
+ /* initialize the hardware */
+ au88x0_init(aui);
+
+ /* initialize the ac97 codec and mixer */
+ if ((aui->aui_ac97i = AC97_CREATE(dev, aui, au88x0_ac97)) == NULL) {
+ device_printf(dev, "failed to initialize ac97 codec\n");
+ goto failed;
+ }
+ if (mixer_init(dev, ac97_getmixerclass(), aui->aui_ac97i) != 0) {
+ device_printf(dev, "failed to initialize ac97 mixer\n");
+ goto failed;
+ }
+
+ /* register with the pcm driver */
+ if (pcm_register(dev, aui, 0, 0))
+ goto failed;
+#if 0
+ pcm_addchan(dev, PCMDIR_PLAY, &au88x0_chan_class, aui);
+ pcm_addchan(dev, PCMDIR_REC, &au88x0_chan_class, aui);
+#endif
+ au88x0_set_status(dev);
+
+ return (0);
+failed:
+ if (aui->aui_ac97i != NULL)
+ ac97_destroy(aui->aui_ac97i);
+ if (aui->aui_dmat)
+ bus_dma_tag_destroy(aui->aui_dmat);
+ if (aui->aui_irqh != NULL)
+ bus_teardown_intr(dev, aui->aui_irq, aui->aui_irqh);
+ if (aui->aui_irq)
+ bus_release_resource(dev, aui->aui_irqtype,
+ aui->aui_irqid, aui->aui_irq);
+ if (aui->aui_reg)
+ bus_release_resource(dev, aui->aui_regtype,
+ aui->aui_regid, aui->aui_reg);
+ free(aui, M_DEVBUF);
+ return (ENXIO);
+}
+
+/*
+ * Detach
+ */
+static int
+au88x0_pci_detach(device_t dev)
+{
+ struct au88x0_info *aui;
+ int error;
+
+ aui = pcm_getdevinfo(dev);
+ if ((error = pcm_unregister(dev)) != 0)
+ return (error);
+
+ /* release resources in reverse order */
+ bus_dma_tag_destroy(aui->aui_dmat);
+ bus_teardown_intr(dev, aui->aui_irq, aui->aui_irqh);
+ bus_release_resource(dev, aui->aui_irqtype,
+ aui->aui_irqid, aui->aui_irq);
+ bus_release_resource(dev, aui->aui_regtype,
+ aui->aui_regid, aui->aui_reg);
+ free(aui, M_DEVBUF);
+
+ return (0);
+}
+
+/*
+ * Driver glue
+ */
+static device_method_t au88x0_methods[] = {
+ DEVMETHOD(device_probe, au88x0_pci_probe),
+ DEVMETHOD(device_attach, au88x0_pci_attach),
+ DEVMETHOD(device_detach, au88x0_pci_detach),
+ { 0, 0 }
+};
+
+static driver_t au88x0_driver = {
+ "pcm",
+ au88x0_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(snd_au88x0, pci, au88x0_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(snd_au88x0, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
+MODULE_VERSION(snd_au88x0, 1);
diff --git a/sys/dev/sound/pci/au88x0.h b/sys/dev/sound/pci/au88x0.h
new file mode 100644
index 0000000..4b442ee
--- /dev/null
+++ b/sys/dev/sound/pci/au88x0.h
@@ -0,0 +1,174 @@
+/*-
+ * Copyright (c) 2003 Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AU88X0_H_INCLUDED
+#define _AU88X0_H_INCLUDED
+
+/*
+ * Channel information
+ */
+struct au88x0_chan_info {
+ struct au88x0_info *auci_aui;
+ struct pcm_channel *auci_pcmchan;
+ struct snd_dbuf *auci_buf;
+ int auci_dir;
+};
+
+/*
+ * Device information
+ */
+struct au88x0_info {
+ /* the device we're associated with */
+ device_t aui_dev;
+ uint32_t aui_model;
+
+ /* parameters */
+ bus_size_t aui_bufsize;
+
+ /* bus_space tag and handle */
+ bus_space_tag_t aui_spct;
+ bus_space_handle_t aui_spch;
+
+ /* register space */
+ int aui_regtype;
+ int aui_regid;
+ struct resource *aui_reg;
+
+ /* irq */
+ int aui_irqtype;
+ int aui_irqid;
+ struct resource *aui_irq;
+ void *aui_irqh;
+
+ /* dma */
+ bus_dma_tag_t aui_dmat;
+
+ /* codec */
+ struct ac97_info *aui_ac97i;
+
+ /* channels */
+ struct au88x0_chan_info aui_chan[2];
+};
+
+/*
+ * PCI IDs of supported cards
+ */
+#define AUREAL_VORTEX_1 0x000112eb /* 8820 (not supported) */
+#define AUREAL_VORTEX_2 0x000212eb /* 8830 */
+#define AUREAL_VORTEX_ADVANTAGE 0x000312eb /* 8810 */
+
+/*
+ * Common parameters
+ */
+#define AU88X0_SETTLE_DELAY 1000
+#define AU88X0_RETRY_COUNT 10
+#define AU88X0_BUFSIZE_MIN 0x1000
+#define AU88X0_BUFSIZE_DFLT 0x4000
+#define AU88X0_BUFSIZE_MAX 0x4000
+
+/*
+ * General control registers
+ */
+#define AU88X0_CONTROL 0x2a00c
+#define AU88X0_CTL_MIDI_ENABLE 0x0001
+#define AU88X0_CTL_GAME_ENABLE 0x0008
+#define AU88X0_CTL_IRQ_ENABLE 0x4000
+
+#define AU88X0_IRQ_SOURCE 0x2a000
+#define AU88X0_IRQ_MASK 0x2a004
+#define AU88X0_IRQ_FATAL_ERR 0x0001
+#define AU88X0_IRQ_PARITY_ERR 0x0002
+#define AU88X0_IRQ_REG_ERR 0x0004
+#define AU88X0_IRQ_FIFO_ERR 0x0008
+#define AU88X0_IRQ_DMA_ERR 0x0010
+#define AU88X0_IRQ_PCMOUT 0x0020
+#define AU88X0_IRQ_TIMER 0x1000
+#define AU88X0_IRQ_MIDI 0x2000
+#define AU88X0_IRQ_MODEM 0x4000
+#define AU88X0_IRQ_PENDING 0x2a008
+#define AU88X0_IRQ_PENDING_BIT 0x0001
+#define AU88X0_IRQ_STATUS 0x2919c
+#define AU88X0_DMA_CONTROL 0x27ae8
+
+/*
+ * Codec control registers
+ *
+ * AU88X0_CODEC_CHANNEL array of 32 32-bit words
+ *
+ * AU88X0_CODEC_CONTROL control register
+ *
+ * bit 16 ready
+ *
+ * AU88X0_CODEC_IO I/O register
+ *
+ * bits 0-15 contents of codec register
+ * bits 16-22 address of codec register
+ * bit 23 0 for read, 1 for write
+ */
+#define AU88X0_CODEC_CHANNEL 0x29080
+#define AU88X0_CODEC_CONTROL 0x29184
+#define AU88X0_CDCTL_WROK 0x00000100
+#define AU88X0_CODEC_IO 0x29188
+#define AU88X0_CDIO_DATA_SHIFT 0
+#define AU88X0_CDIO_DATA_MASK 0x0000ffff
+#define AU88X0_CDIO_ADDR_SHIFT 16
+#define AU88X0_CDIO_ADDR_MASK 0x007f0000
+#define AU88X0_CDIO_RDBIT 0x00000000
+#define AU88X0_CDIO_WRBIT 0x00800000
+#define AU88X0_CDIO_READ(a) (AU88X0_CDIO_RDBIT | \
+ (((a) << AU88X0_CDIO_ADDR_SHIFT) & AU88X0_CDIO_ADDR_MASK))
+#define AU88X0_CDIO_WRITE(a, d) (AU88X0_CDIO_WRBIT | \
+ (((a) << AU88X0_CDIO_ADDR_SHIFT) & AU88X0_CDIO_ADDR_MASK) | \
+ (((d) << AU88X0_CDIO_DATA_SHIFT) & AU88X0_CDIO_DATA_MASK))
+#define AU88X0_CODEC_ENABLE 0x29190
+
+/*
+ * FIFO and DMA contorl registers
+ *
+ * There are two sets of these, one for PCM audio (ADB) and one for
+ * wavetables (WT).
+ */
+#define AU88X0_ADB_FIFOS 32
+#define AU88X0_ADB_FIFO_CTL 0x16100
+#define AU88X0_ADB_FIFO_BASE 0x14000
+#define AU88X0_ADB_FIFO_SIZE 0x40
+#define AU8810_ADB_DMA_CTL 0x27180
+#define AU8820_ADB_DMA_CTL 0x10580
+#define AU8830_ADB_DMA_CTL 0x27a00
+
+#define AU88X0_WT_FIFOS 32
+#define AU88X0_WT_FIFO_CTL 0x16000
+#define AU88X0_WT_FIFO_BASE 0x10000
+#define AU88X0_WT_FIFO_SIZE 0x40
+#define AU8810_WT_DMA_CTL 0x27fd8
+#define AU8820_WT_DMA_CTL 0x10500
+#define AU8830_WT_DMA_CTL 0x27900
+
+#endif
diff --git a/sys/modules/sound/driver/au88x0/Makefile b/sys/modules/sound/driver/au88x0/Makefile
new file mode 100644
index 0000000..b00a90f4
--- /dev/null
+++ b/sys/modules/sound/driver/au88x0/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../../dev/sound/pci
+
+KMOD= snd_au88x0
+SRCS= device_if.h bus_if.h pci_if.h
+SRCS+= au88x0.c
+
+.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud