diff options
-rw-r--r-- | sys/dev/sound/pci/au88x0.c | 637 | ||||
-rw-r--r-- | sys/dev/sound/pci/au88x0.h | 174 | ||||
-rw-r--r-- | sys/modules/sound/driver/au88x0/Makefile | 9 |
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> |