summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>2001-10-24 03:21:07 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>2001-10-24 03:21:07 +0000
commit531697a277cb3f86906355145e8cf8aecce46c11 (patch)
treefd6b190d48912961115b3d1b7a167fdff8098709
parent23911e81e10a1c7385d24dc4b166d6344587b1e0 (diff)
creative labs sb live! and (perhaps) pci512 driver.
from Yannick Montulet, via netbsd, w/ some minor fixens from meself.
-rw-r--r--sys/dev/pci/emuxki.c2079
-rw-r--r--sys/dev/pci/emuxkireg.h651
-rw-r--r--sys/dev/pci/emuxkivar.h259
-rw-r--r--sys/dev/pci/files.pci7
4 files changed, 2995 insertions, 1 deletions
diff --git a/sys/dev/pci/emuxki.c b/sys/dev/pci/emuxki.c
new file mode 100644
index 00000000000..4885be3a806
--- /dev/null
+++ b/sys/dev/pci/emuxki.c
@@ -0,0 +1,2079 @@
+/* $NetBSD: emuxki.c,v 1.1 2001/10/17 18:39:41 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Yannick Montulet.
+ *
+ * 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.
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/*
+ * Driver for Creative Labs SBLive! series and probably PCI512.
+ *
+ * Known bugs:
+ * - inversed stereo at ac97 codec level
+ * (XXX jdolecek - don't see the problem? maybe because auvia(4) has
+ * it swapped too?)
+ * - bass disapear when you plug rear jack-in on Cambridge FPS2000 speakers
+ * (and presumably all speakers that support front and rear jack-in)
+ *
+ * TODO:
+ * - Digital Outputs
+ * - (midi/mpu),joystick support
+ * - Single source recording
+ * - Multiple voices play (problem with /dev/audio architecture)
+ * - Multiple sources recording (Pb with audio(4))
+ * - Independant modification of each channel's parameters (via mixer ?)
+ * - DSP FX patches (to make fx like chipmunk)
+ */
+
+#include <sys/types.h>
+#include <sys/device.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/param.h>
+#include <sys/audioio.h>
+#include <sys/select.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/audio_if.h>
+#include <dev/audiovar.h>
+#include <dev/auconv.h>
+#include <dev/mulaw.h>
+#include <dev/ic/ac97.h>
+
+#include <dev/pci/emuxkireg.h>
+#include <dev/pci/emuxkivar.h>
+
+/* autconf goo */
+int emuxki_match __P((struct device *, void *, void *));
+void emuxki_attach __P((struct device *, struct device *, void *));
+int emuxki_detach __P((struct device *, int));
+int emuxki_scinit __P((struct emuxki_softc *sc));
+void emuxki_pci_shutdown __P((struct emuxki_softc *sc));
+
+/* dma mem mgmt */
+struct dmamem *emuxki_dmamem_alloc __P((bus_dma_tag_t, size_t, bus_size_t,
+ int, int, int));
+void emuxki_dmamem_free __P((struct dmamem *, int));
+void emuxki_dmamem_delete __P((struct dmamem *mem, int type));
+
+struct emuxki_mem *emuxki_mem_new __P((struct emuxki_softc *sc, int ptbidx,
+ size_t size, int type, int flags));
+void emuxki_mem_delete __P((struct emuxki_mem *mem, int type));
+
+/* Emu10k1 init & shutdown */
+int emuxki_init __P((struct emuxki_softc *));
+void emuxki_shutdown __P((struct emuxki_softc *));
+
+/* Emu10k1 mem mgmt */
+void *emuxki_pmem_alloc __P((struct emuxki_softc *, size_t,int,int));
+void *emuxki_rmem_alloc __P((struct emuxki_softc *, size_t,int,int));
+
+/*
+ * Emu10k1 channels funcs : There is no direct access to channels, everything
+ * is done through voices I will at least provide channel based fx params
+ * modification, later...
+ */
+
+/* Emu10k1 voice mgmt */
+struct emuxki_voice *emuxki_voice_new __P((struct emuxki_softc *, u_int8_t));
+void emuxki_voice_delete __P((struct emuxki_voice *));
+int emuxki_voice_set_audioparms
+ __P((struct emuxki_voice *, u_int8_t, u_int8_t, u_int32_t));
+/* emuxki_voice_set_fxparms will come later, it'll need channel distinction */
+int emuxki_voice_set_bufparms
+ __P((struct emuxki_voice *, void *, u_int32_t, u_int16_t));
+int emuxki_voice_set_stereo __P((struct emuxki_voice *voice, u_int8_t stereo));
+int emuxki_voice_dataloc_create __P((struct emuxki_voice *voice));
+void emuxki_voice_dataloc_destroy __P((struct emuxki_voice *voice));
+void emuxki_voice_commit_parms __P((struct emuxki_voice *));
+void emuxki_voice_recsrc_release
+ __P((struct emuxki_softc *sc, emuxki_recsrc_t source));
+u_int32_t emuxki_voice_curaddr __P((struct emuxki_voice *));
+int emuxki_set_vparms __P((struct emuxki_voice *voice, struct audio_params *p));
+int emuxki_voice_set_srate __P((struct emuxki_voice *voice, u_int32_t srate));
+void emuxki_voice_start
+ __P((struct emuxki_voice *, void (*) (void *), void *));
+void emuxki_voice_halt __P((struct emuxki_voice *));
+int emuxki_voice_channel_create __P((struct emuxki_voice *voice));
+void emuxki_voice_channel_destroy __P((struct emuxki_voice *voice));
+
+struct emuxki_channel *emuxki_channel_new
+ __P((struct emuxki_voice *voice, u_int8_t num));
+void emuxki_channel_delete __P((struct emuxki_channel *chan));
+void emuxki_channel_start __P((struct emuxki_channel *chan));
+void emuxki_channel_stop __P((struct emuxki_channel *chan));
+void emuxki_channel_commit_parms __P((struct emuxki_channel *chan));
+void emuxki_channel_set_bufparms
+ __P((struct emuxki_channel *chan, u_int32_t start, u_int32_t end));
+void emuxki_channel_set_srate
+ __P((struct emuxki_channel *chan, u_int32_t srate));
+void emuxki_channel_set_fxsend __P((struct emuxki_channel *chan,
+ struct emuxki_chanparms_fxsend *fxsend));
+void emuxki_chanparms_set_defaults __P((struct emuxki_channel *chan));
+
+void emuxki_resched_timer __P((struct emuxki_softc *sc));
+
+/*
+ * Emu10k1 stream mgmt : not done yet
+ */
+#if 0
+struct emuxki_stream *emuxki_stream_new __P((struct emu10k1 *));
+void emuxki_stream_delete __P((struct emuxki_stream *));
+int emuxki_stream_set_audio_params __P((struct emuxki_stream *, u_int8_t,
+ u_int8_t, u_int8_t, u_int16_t));
+void emuxki_stream_start __P((struct emuxki_stream *));
+void emuxki_stream_halt __P((struct emuxki_stream *));
+#endif
+
+/* fx interface */
+void emuxki_initfx __P((struct emuxki_softc *sc));
+void emuxki_dsp_addop __P((struct emuxki_softc *sc, u_int16_t *pc, u_int8_t op,
+ u_int16_t r, u_int16_t a, u_int16_t x, u_int16_t y));
+void emuxki_write_micro
+ __P((struct emuxki_softc *sc, u_int32_t pc, u_int32_t data));
+
+/* audio interface callbacks */
+
+int emuxki_open __P((void *, int));
+void emuxki_close __P((void *));
+
+int emuxki_query_encoding __P((void *, struct audio_encoding *));
+int emuxki_set_params __P((void *, int, int,
+ struct audio_params *,
+ struct audio_params *));
+
+size_t emuxki_round_buffersize __P((void *, int, size_t));
+
+int emuxki_trigger_output __P((void *, void *, void *, int,
+ void (*)(void *), void *,
+ struct audio_params *));
+int emuxki_trigger_input __P((void *, void *, void *, int,
+ void (*) (void *), void *,
+ struct audio_params *));
+int emuxki_halt_output __P((void *));
+int emuxki_halt_input __P((void *));
+
+int emuxki_getdev __P((void *, struct audio_device *));
+int emuxki_set_port __P((void *, mixer_ctrl_t *));
+int emuxki_get_port __P((void *, mixer_ctrl_t *));
+int emuxki_query_devinfo __P((void *, mixer_devinfo_t *));
+
+void *emuxki_allocm __P((void *, int, size_t, int, int));
+void emuxki_freem __P((void *, void *, int));
+
+int emuxki_mappage __P((void *, void *, int, int));
+int emuxki_get_props __P((void *));
+
+/* Interrupt handler */
+int emuxki_intr __P((void *));
+
+/* Emu10k1 AC97 interface callbacks */
+int emuxki_ac97_init __P((struct emuxki_softc *sc));
+int emuxki_ac97_attach __P((void *, struct ac97_codec_if *));
+int emuxki_ac97_read __P((void *, u_int8_t, u_int16_t *));
+int emuxki_ac97_write __P((void *, u_int8_t, u_int16_t));
+void emuxki_ac97_reset __P((void *));
+
+/*
+ * Autoconfig goo.
+ */
+struct cfdriver emu_cd = {
+ NULL, "emu", DV_DULL
+};
+
+struct cfattach emu_ca = {
+ sizeof(struct emuxki_softc),
+ emuxki_match,
+ emuxki_attach,
+ emuxki_detach,
+ NULL /* config activate */
+};
+
+struct audio_hw_if emuxki_hw_if = {
+ emuxki_open,
+ emuxki_close,
+ NULL, /* drain */
+ emuxki_query_encoding,
+ emuxki_set_params,
+ NULL, /* round blocksize */
+ NULL, /* commit settings */
+ NULL, /* init_output */
+ NULL, /* init_input */
+ NULL, /* start_output */
+ NULL, /* start_input */
+ emuxki_halt_output,
+ emuxki_halt_input,
+ NULL, /* speaker_ctl */
+ emuxki_getdev,
+ NULL, /* setfd */
+ emuxki_set_port,
+ emuxki_get_port,
+ emuxki_query_devinfo,
+ NULL, /* allocm_old */
+ emuxki_freem,
+ NULL, /* round_buffersize_old */
+ emuxki_mappage,
+ emuxki_get_props,
+ emuxki_trigger_output,
+ emuxki_trigger_input,
+ emuxki_allocm,
+ emuxki_round_buffersize
+};
+
+/*
+ * Dma memory mgmt
+ */
+
+void
+emuxki_dmamem_delete(struct dmamem *mem, int type)
+{
+ free(mem->segs, type);
+ free(mem, type);
+}
+
+struct dmamem *
+emuxki_dmamem_alloc(bus_dma_tag_t dmat, size_t size, bus_size_t align,
+ int nsegs, int type, int flags)
+{
+ struct dmamem *mem;
+ int bus_dma_flags;
+
+ /* Allocate memory for structure */
+ if ((mem = malloc(sizeof(*mem), type, flags)) == NULL)
+ return (NULL);
+ mem->dmat = dmat;
+ mem->size = size;
+ mem->align = align;
+ mem->nsegs = nsegs;
+ mem->bound = 0;
+
+ mem->segs = malloc(mem->nsegs * sizeof(*(mem->segs)), type, flags);
+ if (mem->segs == NULL) {
+ free(mem, type);
+ return (NULL);
+ }
+
+ bus_dma_flags = (flags & M_NOWAIT) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK;
+ if (bus_dmamem_alloc(dmat, mem->size, mem->align, mem->bound,
+ mem->segs, mem->nsegs, &(mem->rsegs),
+ bus_dma_flags)) {
+ emuxki_dmamem_delete(mem, type);
+ return (NULL);
+ }
+
+ if (bus_dmamem_map(dmat, mem->segs, mem->nsegs, mem->size,
+ &(mem->kaddr), bus_dma_flags | BUS_DMA_COHERENT)) {
+ bus_dmamem_free(dmat, mem->segs, mem->nsegs);
+ emuxki_dmamem_delete(mem, type);
+ return (NULL);
+ }
+
+ if (bus_dmamap_create(dmat, mem->size, mem->nsegs, mem->size,
+ mem->bound, bus_dma_flags, &(mem->map))) {
+ bus_dmamem_unmap(dmat, mem->kaddr, mem->size);
+ bus_dmamem_free(dmat, mem->segs, mem->nsegs);
+ emuxki_dmamem_delete(mem, type);
+ return (NULL);
+ }
+
+ if (bus_dmamap_load(dmat, mem->map, mem->kaddr,
+ mem->size, NULL, bus_dma_flags)) {
+ bus_dmamap_destroy(dmat, mem->map);
+ bus_dmamem_unmap(dmat, mem->kaddr, mem->size);
+ bus_dmamem_free(dmat, mem->segs, mem->nsegs);
+ emuxki_dmamem_delete(mem, type);
+ return (NULL);
+ }
+
+ return (mem);
+}
+
+void
+emuxki_dmamem_free(struct dmamem *mem, int type)
+{
+ bus_dmamap_unload(mem->dmat, mem->map);
+ bus_dmamap_destroy(mem->dmat, mem->map);
+ bus_dmamem_unmap(mem->dmat, mem->kaddr, mem->size);
+ bus_dmamem_free(mem->dmat, mem->segs, mem->nsegs);
+ emuxki_dmamem_delete(mem, type);
+}
+
+
+/*
+ * Autoconf device callbacks : attach and detach
+ */
+
+void
+emuxki_pci_shutdown(struct emuxki_softc *sc)
+{
+ if (sc->sc_ih != NULL)
+ pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
+ if (sc->sc_ios)
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
+}
+
+int
+emuxki_scinit(struct emuxki_softc *sc)
+{
+ int err;
+
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_HCFG,
+ EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK |
+ EMU_HCFG_MUTEBUTTONENABLE);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_INTE,
+ EMU_INTE_SAMPLERATER | EMU_INTE_PCIERRENABLE);
+
+ if ((err = emuxki_init(sc)))
+ return (err);
+
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_HCFG,
+ EMU_HCFG_AUDIOENABLE |
+ EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_AUTOMUTE);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_INTE,
+ bus_space_read_4(sc->sc_iot, sc->sc_ioh, EMU_INTE) |
+ EMU_INTE_VOLINCRENABLE | EMU_INTE_VOLDECRENABLE |
+ EMU_INTE_MUTEENABLE);
+
+ /* No multiple voice support for now */
+ sc->pvoice = sc->rvoice = NULL;
+
+ return (0);
+}
+
+int
+emuxki_ac97_init(struct emuxki_softc *sc)
+{
+ sc->hostif.arg = sc;
+ sc->hostif.attach = emuxki_ac97_attach;
+ sc->hostif.read = emuxki_ac97_read;
+ sc->hostif.write = emuxki_ac97_write;
+ sc->hostif.reset = emuxki_ac97_reset;
+ sc->hostif.flags = NULL;
+ return (ac97_attach(&(sc->hostif)));
+}
+
+int
+emuxki_match(struct device *parent, void *match, void *aux)
+{
+ struct pci_attach_args *pa = aux;
+
+ if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_CREATIVELABS &&
+ PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_CREATIVELABS_SBLIVE)
+ return (1);
+
+ return (0);
+}
+
+void
+emuxki_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct emuxki_softc *sc = (struct emuxki_softc *) self;
+ struct pci_attach_args *pa = aux;
+ pci_intr_handle_t ih;
+ const char *intrstr;
+
+ if (pci_mapreg_map(pa, EMU_PCI_CBIO, PCI_MAPREG_TYPE_IO, 0,
+ &(sc->sc_iot), &(sc->sc_ioh), &(sc->sc_iob), &(sc->sc_ios), 0)) {
+ printf(": can't map iospace\n");
+ return;
+ }
+
+ sc->sc_pc = pa->pa_pc;
+ sc->sc_dmat = pa->pa_dmat;
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
+ pci_conf_read(pa->pa_pc, pa->pa_tag,
+ (PCI_COMMAND_STATUS_REG) | PCI_COMMAND_MASTER_ENABLE));
+
+ if (pci_intr_map(pa, &ih)) {
+ printf(": couldn't map interrupt\n");
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
+ return;
+ }
+
+ intrstr = pci_intr_string(pa->pa_pc, ih);
+ sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO, emuxki_intr,
+ sc, sc->sc_dev.dv_xname);
+ if (sc->sc_ih == NULL) {
+ printf(": couldn't establish interrupt");
+ if (intrstr != NULL)
+ printf(" at %s", intrstr);
+ printf("\n");
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
+ return;
+ }
+ printf(": %s\n", sc->sc_dev.dv_xname, intrstr);
+
+ if (emuxki_scinit(sc) || emuxki_ac97_init(sc) ||
+ (sc->sc_audev = audio_attach_mi(&emuxki_hw_if, sc, self)) == NULL)
+ emuxki_pci_shutdown(sc);
+}
+
+int
+emuxki_detach(struct device *self, int flags)
+{
+ struct emuxki_softc *sc = (struct emuxki_softc *) self;
+ int err = 0;
+
+ if (sc->sc_audev != NULL) /* Test in case audio didn't attach */
+ err = config_detach(sc->sc_audev, 0);
+
+ /* All voices should be stopped now but add some code here if not */
+
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_HCFG,
+ EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK |
+ EMU_HCFG_MUTEBUTTONENABLE);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_INTE, 0);
+
+ emuxki_shutdown(sc);
+
+ emuxki_pci_shutdown(sc);
+
+ return (0);
+}
+
+
+/* Misc stuff relative to emu10k1 */
+
+static __inline u_int32_t
+emuxki_rate_to_pitch(u_int32_t rate)
+{
+ static const u_int32_t logMagTable[128] = {
+ 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3,
+ 0x13aa2, 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a,
+ 0x2655d, 0x28ed5, 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb,
+ 0x381b6, 0x3a93d, 0x3d081, 0x3f782, 0x41e42, 0x444c1, 0x46b01,
+ 0x49101, 0x4b6c4, 0x4dc49, 0x50191, 0x5269e, 0x54b6f, 0x57006,
+ 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, 0x646ee, 0x66a00,
+ 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, 0x759d4,
+ 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
+ 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20,
+ 0x93d26, 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec,
+ 0xa11d8, 0xa2f9d, 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241,
+ 0xadf26, 0xafbe7, 0xb1885, 0xb3500, 0xb5157, 0xb6d8c, 0xb899f,
+ 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, 0xc1404, 0xc2f50, 0xc4a7b,
+ 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, 0xceaec, 0xd053f,
+ 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, 0xdba4a,
+ 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
+ 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a,
+ 0xf2c83, 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57,
+ 0xfd1a7, 0xfe8df
+ };
+ static const u_int8_t logSlopeTable[128] = {
+ 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
+ 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
+ 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
+ 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
+ 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
+ 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
+ 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
+ 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
+ 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
+ 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
+ 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
+ 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
+ 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
+ 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
+ };
+ int8_t i;
+
+ if (rate == 0)
+ return 0; /* Bail out if no leading "1" */
+ rate *= 11185; /* Scale 48000 to 0x20002380 */
+ for (i = 31; i > 0; i--) {
+ if (rate & 0x80000000) { /* Detect leading "1" */
+ return (((u_int32_t) (i - 15) << 20) +
+ logMagTable[0x7f & (rate >> 24)] +
+ (0x7f & (rate >> 17)) *
+ logSlopeTable[0x7f & (rate >> 24)]);
+ }
+ rate <<= 1;
+ }
+
+ return 0; /* Should never reach this point */
+}
+
+/* Emu10k1 Low level */
+
+static __inline u_int32_t
+emuxki_read(struct emuxki_softc *sc, u_int16_t chano, u_int32_t reg)
+{
+ u_int32_t ptr, mask = 0xffffffff;
+ u_int8_t size, offset = 0;
+ int s;
+
+ ptr = ((((u_int32_t) reg) << 16) & EMU_PTR_ADDR_MASK) |
+ (chano & EMU_PTR_CHNO_MASK);
+ if (reg & 0xff000000) {
+ size = (reg >> 24) & 0x3f;
+ offset = (reg >> 16) & 0x1f;
+ mask = ((1 << size) - 1) << offset;
+ }
+
+ s = splaudio();
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_PTR, ptr);
+ ptr = (bus_space_read_4(sc->sc_iot, sc->sc_ioh, EMU_DATA) & mask)
+ >> offset;
+ splx(s);
+
+ return (ptr);
+}
+
+static __inline void
+emuxki_write(struct emuxki_softc *sc, u_int16_t chano,
+ u_int32_t reg, u_int32_t data)
+{
+ u_int32_t ptr, mask;
+ u_int8_t size, offset;
+ int s;
+
+ ptr = ((((u_int32_t) reg) << 16) & EMU_PTR_ADDR_MASK) |
+ (chano & EMU_PTR_CHNO_MASK);
+
+ /* BE CAREFUL WITH THAT AXE, EUGENE */
+ if (ptr == 0x52 || ptr == 0x53)
+ return;
+
+ if (reg & 0xff000000) {
+ size = (reg >> 24) & 0x3f;
+ offset = (reg >> 16) & 0x1f;
+ mask = ((1 << size) - 1) << offset;
+ data = ((data << offset) & mask) |
+ (emuxki_read(sc, chano, reg & 0xffff) & ~mask);
+ }
+
+ s = splaudio();
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_PTR, ptr);
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_DATA, data);
+ splx(s);
+}
+
+/* Microcode should this go in /sys/dev/microcode ? */
+
+void
+emuxki_write_micro(struct emuxki_softc *sc, u_int32_t pc, u_int32_t data)
+{
+ emuxki_write(sc, 0, EMU_MICROCODEBASE + pc, data);
+}
+
+void
+emuxki_dsp_addop(struct emuxki_softc *sc, u_int16_t *pc, u_int8_t op,
+ u_int16_t r, u_int16_t a, u_int16_t x, u_int16_t y)
+{
+ emuxki_write_micro(sc, *pc << 1,
+ ((x << 10) & EMU_DSP_LOWORD_OPX_MASK) |
+ (y & EMU_DSP_LOWORD_OPY_MASK));
+ emuxki_write_micro(sc, (*pc << 1) + 1,
+ ((op << 20) & EMU_DSP_HIWORD_OPCODE_MASK) |
+ ((r << 10) & EMU_DSP_HIWORD_RESULT_MASK) |
+ (a & EMU_DSP_HIWORD_OPA_MASK));
+ (*pc)++;
+}
+
+/* init and shutdown */
+
+void
+emuxki_initfx(struct emuxki_softc *sc)
+{
+ u_int16_t pc;
+
+ /* Set all GPRs to 0 */
+ for (pc = 0; pc < 256; pc++)
+ emuxki_write(sc, 0, EMU_DSP_GPR(pc), 0);
+ for (pc = 0; pc < 160; pc++) {
+ emuxki_write(sc, 0, EMU_TANKMEMDATAREGBASE + pc, 0);
+ emuxki_write(sc, 0, EMU_TANKMEMADDRREGBASE + pc, 0);
+ }
+ pc = 0;
+ /* AC97 Out (l/r) = AC97 In (l/r) + FX[0/1] * 4 */
+ emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_MACINTS,
+ EMU_DSP_OUTL(EMU_DSP_OUT_AC97),
+ EMU_DSP_CST(0),
+ EMU_DSP_FX(0), EMU_DSP_CST(4));
+ emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_MACINTS,
+ EMU_DSP_OUTR(EMU_DSP_OUT_AC97),
+ EMU_DSP_CST(0),
+ EMU_DSP_FX(1), EMU_DSP_CST(4));
+
+ /* Rear channel OUT (l/r) = FX[2/3] * 4 */
+#if 0
+ emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_MACINTS,
+ EMU_DSP_OUTL(EMU_DSP_OUT_RCHAN),
+ EMU_DSP_OUTL(EMU_DSP_OUT_AC97),
+ EMU_DSP_FX(0), EMU_DSP_CST(4));
+ emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_MACINTS,
+ EMU_DSP_OUTR(EMU_DSP_OUT_RCHAN),
+ EMU_DSP_OUTR(EMU_DSP_OUT_AC97),
+ EMU_DSP_FX(1), EMU_DSP_CST(4));
+#endif
+ /* zero out the rest of the microcode */
+ while (pc < 512)
+ emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_ACC3,
+ EMU_DSP_CST(0), EMU_DSP_CST(0),
+ EMU_DSP_CST(0), EMU_DSP_CST(0));
+
+ emuxki_write(sc, 0, EMU_DBG, 0); /* Is it really necessary ? */
+}
+
+int
+emuxki_init(struct emuxki_softc *sc)
+{
+ u_int16_t i;
+ u_int32_t spcs, *ptb;
+ bus_addr_t silentpage;
+
+ /* disable any channel interrupt */
+ emuxki_write(sc, 0, EMU_CLIEL, 0);
+ emuxki_write(sc, 0, EMU_CLIEH, 0);
+ emuxki_write(sc, 0, EMU_SOLEL, 0);
+ emuxki_write(sc, 0, EMU_SOLEH, 0);
+
+ /* Set recording buffers sizes to zero */
+ emuxki_write(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE);
+ emuxki_write(sc, 0, EMU_MICBA, 0);
+ emuxki_write(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE);
+ emuxki_write(sc, 0, EMU_FXBA, 0);
+ emuxki_write(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE);
+ emuxki_write(sc, 0, EMU_ADCBA, 0);
+
+ /* Initialize all channels to stopped and no effects */
+ for (i = 0; i < EMU_NUMCHAN; i++) {
+ emuxki_write(sc, i, EMU_CHAN_DCYSUSV, 0);
+ emuxki_write(sc, i, EMU_CHAN_IP, 0);
+ emuxki_write(sc, i, EMU_CHAN_VTFT, 0xffff);
+ emuxki_write(sc, i, EMU_CHAN_CVCF, 0xffff);
+ emuxki_write(sc, i, EMU_CHAN_PTRX, 0);
+ emuxki_write(sc, i, EMU_CHAN_CPF, 0);
+ emuxki_write(sc, i, EMU_CHAN_CCR, 0);
+ emuxki_write(sc, i, EMU_CHAN_PSST, 0);
+ emuxki_write(sc, i, EMU_CHAN_DSL, 0x10); /* Why 16 ? */
+ emuxki_write(sc, i, EMU_CHAN_CCCA, 0);
+ emuxki_write(sc, i, EMU_CHAN_Z1, 0);
+ emuxki_write(sc, i, EMU_CHAN_Z2, 0);
+ emuxki_write(sc, i, EMU_CHAN_FXRT, 0x32100000);
+ emuxki_write(sc, i, EMU_CHAN_ATKHLDM, 0);
+ emuxki_write(sc, i, EMU_CHAN_DCYSUSM, 0);
+ emuxki_write(sc, i, EMU_CHAN_IFATN, 0xffff);
+ emuxki_write(sc, i, EMU_CHAN_PEFE, 0);
+ emuxki_write(sc, i, EMU_CHAN_FMMOD, 0);
+ emuxki_write(sc, i, EMU_CHAN_TREMFRQ, 24);
+ emuxki_write(sc, i, EMU_CHAN_FM2FRQ2, 24);
+ emuxki_write(sc, i, EMU_CHAN_TEMPENV, 0);
+
+ /* these are last so OFF prevents writing */
+ emuxki_write(sc, i, EMU_CHAN_LFOVAL2, 0);
+ emuxki_write(sc, i, EMU_CHAN_LFOVAL1, 0);
+ emuxki_write(sc, i, EMU_CHAN_ATKHLDV, 0);
+ emuxki_write(sc, i, EMU_CHAN_ENVVOL, 0);
+ emuxki_write(sc, i, EMU_CHAN_ENVVAL, 0);
+ }
+
+ /* set digital outputs format */
+ spcs = (EMU_SPCS_CLKACCY_1000PPM | EMU_SPCS_SAMPLERATE_48 |
+ EMU_SPCS_CHANNELNUM_LEFT | EMU_SPCS_SOURCENUM_UNSPEC |
+ EMU_SPCS_GENERATIONSTATUS | 0x00001200 /* Cat code. */ |
+ 0x00000000 /* IEC-958 Mode */ | EMU_SPCS_EMPHASIS_NONE |
+ EMU_SPCS_COPYRIGHT);
+ emuxki_write(sc, 0, EMU_SPCS0, spcs);
+ emuxki_write(sc, 0, EMU_SPCS1, spcs);
+ emuxki_write(sc, 0, EMU_SPCS2, spcs);
+
+ /* Let's play with sound processor */
+ emuxki_initfx(sc);
+
+ /* Here is our Page Table */
+ if ((sc->ptb = emuxki_dmamem_alloc(sc->sc_dmat,
+ EMU_MAXPTE * sizeof(u_int32_t),
+ EMU_DMA_ALIGN, EMU_DMAMEM_NSEG,
+ M_DEVBUF, M_WAITOK)) == NULL)
+ return (ENOMEM);
+
+ /* This is necessary unless you like Metallic noise... */
+ if ((sc->silentpage = emuxki_dmamem_alloc(sc->sc_dmat, EMU_PTESIZE,
+ EMU_DMA_ALIGN, EMU_DMAMEM_NSEG, M_DEVBUF, M_WAITOK))==NULL){
+ emuxki_dmamem_free(sc->ptb, M_DEVBUF);
+ return (ENOMEM);
+ }
+
+ /* Zero out the silent page */
+ /* This might not be always true, it might be 128 for 8bit channels */
+ memset(KERNADDR(sc->silentpage), 0, DMASIZE(sc->silentpage));
+
+ /*
+ * Set all the PTB Entries to the silent page We shift the physical
+ * address by one and OR it with the page number. I don't know what
+ * the ORed index is for, might be a very useful unused feature...
+ */
+ silentpage = DMAADDR(sc->silentpage) << 1;
+ ptb = KERNADDR(sc->ptb);
+ for (i = 0; i < EMU_MAXPTE; i++)
+ ptb[i] = silentpage | i;
+
+ /* Write PTB address and set TCB to none */
+ emuxki_write(sc, 0, EMU_PTB, DMAADDR(sc->ptb));
+ emuxki_write(sc, 0, EMU_TCBS, 0); /* This means 16K TCB */
+ emuxki_write(sc, 0, EMU_TCB, 0); /* No TCB use for now */
+
+ /*
+ * Set channels MAPs to the silent page.
+ * I don't know what MAPs are for.
+ */
+ silentpage |= EMU_CHAN_MAP_PTI_MASK;
+ for (i = 0; i < EMU_NUMCHAN; i++) {
+ emuxki_write(sc, i, EMU_CHAN_MAPA, silentpage);
+ emuxki_write(sc, i, EMU_CHAN_MAPB, silentpage);
+ sc->channel[i] = NULL;
+ }
+
+ /* Init voices list */
+ LIST_INIT(&(sc->voices));
+
+ /* Timer is stopped */
+ sc->timerstate &= ~EMU_TIMER_STATE_ENABLED;
+ return (0);
+}
+
+void
+emuxki_shutdown(struct emuxki_softc *sc)
+{
+ u_int32_t i;
+
+ /* Disable any Channels interrupts */
+ emuxki_write(sc, 0, EMU_CLIEL, 0);
+ emuxki_write(sc, 0, EMU_CLIEH, 0);
+ emuxki_write(sc, 0, EMU_SOLEL, 0);
+ emuxki_write(sc, 0, EMU_SOLEH, 0);
+
+ /*
+ * Should do some voice(stream) stopping stuff here, that's what will
+ * stop and deallocate all channels.
+ */
+
+ /* Stop all channels */
+ /* XXX This shouldn't be necessary, I'll remove once everything works */
+ for (i = 0; i < EMU_NUMCHAN; i++)
+ emuxki_write(sc, i, EMU_CHAN_DCYSUSV, 0);
+ for (i = 0; i < EMU_NUMCHAN; i++) {
+ emuxki_write(sc, i, EMU_CHAN_VTFT, 0);
+ emuxki_write(sc, i, EMU_CHAN_CVCF, 0);
+ emuxki_write(sc, i, EMU_CHAN_PTRX, 0);
+ emuxki_write(sc, i, EMU_CHAN_CPF, 0);
+ }
+
+ /*
+ * Deallocate Emu10k1 caches and recording buffers. Again it will be
+ * removed because it will be done in voice shutdown.
+ */
+ emuxki_write(sc, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE);
+ emuxki_write(sc, 0, EMU_MICBA, 0);
+ emuxki_write(sc, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE);
+ emuxki_write(sc, 0, EMU_FXBA, 0);
+ emuxki_write(sc, 0, EMU_FXWC, 0);
+ emuxki_write(sc, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE);
+ emuxki_write(sc, 0, EMU_ADCBA, 0);
+
+ /*
+ * XXX I don't know yet how I will handle tank cache buffer,
+ * I don't even clearly know what it is for.
+ */
+ emuxki_write(sc, 0, EMU_TCB, 0); /* 16K again */
+ emuxki_write(sc, 0, EMU_TCBS, 0);
+
+ emuxki_write(sc, 0, EMU_DBG, 0x8000); /* necessary ? */
+
+ emuxki_dmamem_free(sc->silentpage, M_DEVBUF);
+ emuxki_dmamem_free(sc->ptb, M_DEVBUF);
+}
+
+/* Emu10k1 Memory managment */
+
+struct emuxki_mem *
+emuxki_mem_new(struct emuxki_softc *sc, int ptbidx,
+ size_t size, int type, int flags)
+{
+ struct emuxki_mem *mem;
+
+ if ((mem = malloc(sizeof(*mem), type, flags)) == NULL)
+ return (NULL);
+
+ mem->ptbidx = ptbidx;
+ if ((mem->dmamem = emuxki_dmamem_alloc(sc->sc_dmat, size, EMU_DMA_ALIGN,
+ EMU_DMAMEM_NSEG, type, flags)) == NULL) {
+ free(mem, type);
+ return (NULL);
+ }
+ return (mem);
+}
+
+void
+emuxki_mem_delete(struct emuxki_mem *mem, int type)
+{
+ emuxki_dmamem_free(mem->dmamem, type);
+ free(mem, type);
+}
+
+void *
+emuxki_pmem_alloc(struct emuxki_softc *sc, size_t size, int type, int flags)
+{
+ int i, j, s;
+ size_t numblocks;
+ struct emuxki_mem *mem;
+ u_int32_t *ptb, silentpage;
+
+ ptb = KERNADDR(sc->ptb);
+ silentpage = DMAADDR(sc->silentpage) << 1;
+ numblocks = size / EMU_PTESIZE;
+ if (size % EMU_PTESIZE)
+ numblocks++;
+
+ for (i = 0; i < EMU_MAXPTE; i++)
+ if ((ptb[i] & EMU_CHAN_MAP_PTE_MASK) == silentpage) {
+ /* We look for a free PTE */
+ s = splaudio();
+ for (j = 0; j < numblocks; j++)
+ if ((ptb[i + j] & EMU_CHAN_MAP_PTE_MASK)
+ != silentpage)
+ break;
+ if (j == numblocks) {
+ if ((mem = emuxki_mem_new(sc, i,
+ size, type, flags)) == NULL) {
+ splx(s);
+ return (NULL);
+ }
+ for (j = 0; j < numblocks; j++)
+ ptb[i + j] =
+ (((DMAADDR(mem->dmamem) +
+ j * EMU_PTESIZE)) << 1)
+ | (i + j);
+ LIST_INSERT_HEAD(&(sc->mem), mem, next);
+ splx(s);
+ return (KERNADDR(mem->dmamem));
+ } else
+ i += j;
+ splx(s);
+ }
+ return (NULL);
+}
+
+void *
+emuxki_rmem_alloc(struct emuxki_softc *sc, size_t size, int type, int flags)
+{
+ struct emuxki_mem *mem;
+ int s;
+
+ mem = emuxki_mem_new(sc, EMU_RMEM, size, type, flags);
+ if (mem == NULL)
+ return (NULL);
+
+ s = splaudio();
+ LIST_INSERT_HEAD(&(sc->mem), mem, next);
+ splx(s);
+
+ return (KERNADDR(mem->dmamem));
+}
+
+/*
+ * emuxki_channel_* : Channel managment functions
+ * emuxki_chanparms_* : Channel parameters modification functions
+ */
+
+/*
+ * is splaudio necessary here, can the same voice be manipulated by two
+ * different threads at a time ?
+ */
+void
+emuxki_chanparms_set_defaults(struct emuxki_channel *chan)
+{
+ chan->fxsend.a.level = chan->fxsend.b.level =
+ chan->fxsend.c.level = chan->fxsend.d.level = 0xff; /* max */
+ chan->fxsend.a.dest = 0x0;
+ chan->fxsend.b.dest = 0x1;
+ chan->fxsend.c.dest = 0x2;
+ chan->fxsend.d.dest = 0x3;
+
+ chan->pitch.intial = 0x0000; /* shouldn't it be 0xE000 ? */
+ chan->pitch.current = 0x0000; /* should it be 0x0400 */
+ chan->pitch.target = 0x0000; /* the unity pitch shift ? */
+ chan->pitch.envelope_amount = 0x00; /* none */
+
+ chan->initial_attenuation = 0x00; /* no attenuation */
+ chan->volume.current = 0x0000; /* no volume */
+ chan->volume.target = 0xffff;
+ chan->volume.envelope.current_state = 0x8000; /* 0 msec delay */
+ chan->volume.envelope.hold_time = 0x7f; /* 0 msec */
+ chan->volume.envelope.attack_time = 0x7F; /* 5.5msec */
+ chan->volume.envelope.sustain_level = 0x7F; /* full */
+ chan->volume.envelope.decay_time = 0x7F; /* 22msec */
+
+ chan->filter.initial_cutoff_frequency = 0xff; /* no filter */
+ chan->filter.current_cutoff_frequency = 0xffff; /* no filtering */
+ chan->filter.target_cutoff_frequency = 0xffff; /* no filtering */
+ chan->filter.lowpass_resonance_height = 0x0;
+ chan->filter.interpolation_ROM = 0x1; /* full band */
+ chan->filter.envelope_amount = 0x7f; /* none */
+ chan->filter.LFO_modulation_depth = 0x00; /* none */
+
+ chan->loop.start = 0x000000;
+ chan->loop.end = 0x000010; /* Why ? */
+
+ chan->modulation.envelope.current_state = 0x8000;
+ chan->modulation.envelope.hold_time = 0x00; /* 127 better ? */
+ chan->modulation.envelope.attack_time = 0x00; /* infinite */
+ chan->modulation.envelope.sustain_level = 0x00; /* off */
+ chan->modulation.envelope.decay_time = 0x7f; /* 22 msec */
+ chan->modulation.LFO_state = 0x8000;
+
+ chan->vibrato_LFO.state = 0x8000;
+ chan->vibrato_LFO.modulation_depth = 0x00; /* none */
+ chan->vibrato_LFO.vibrato_depth = 0x00;
+ chan->vibrato_LFO.frequency = 0x00; /* Why set to 24 when
+ * initialized ? */
+
+ chan->tremolo_depth = 0x00;
+}
+
+/* only call it at splaudio */
+struct emuxki_channel *
+emuxki_channel_new(struct emuxki_voice *voice, u_int8_t num)
+{
+ struct emuxki_channel *chan;
+
+ chan = malloc(sizeof(struct emuxki_channel), M_DEVBUF, M_WAITOK);
+ if (chan == NULL)
+ return (NULL);
+
+ chan->voice = voice;
+ chan->num = num;
+ emuxki_chanparms_set_defaults(chan);
+ chan->voice->sc->channel[num] = chan;
+ return (chan);
+}
+
+/* only call it at splaudio */
+void
+emuxki_channel_delete(struct emuxki_channel *chan)
+{
+ chan->voice->sc->channel[chan->num] = NULL;
+ free(chan, M_DEVBUF);
+}
+
+void
+emuxki_channel_set_fxsend(struct emuxki_channel *chan,
+ struct emuxki_chanparms_fxsend *fxsend)
+{
+ /* Could do a memcpy ...*/
+ chan->fxsend.a.level = fxsend->a.level;
+ chan->fxsend.b.level = fxsend->b.level;
+ chan->fxsend.c.level = fxsend->c.level;
+ chan->fxsend.d.level = fxsend->d.level;
+ chan->fxsend.a.dest = fxsend->a.dest;
+ chan->fxsend.b.dest = fxsend->b.dest;
+ chan->fxsend.c.dest = fxsend->c.dest;
+ chan->fxsend.d.dest = fxsend->d.dest;
+}
+
+void
+emuxki_channel_set_srate(struct emuxki_channel *chan, u_int32_t srate)
+{
+ chan->pitch.target = (srate << 8) / 375;
+ chan->pitch.target = (chan->pitch.target >> 1) +
+ (chan->pitch.target & 1);
+ chan->pitch.target &= 0xffff;
+ chan->pitch.current = chan->pitch.target;
+ chan->pitch.intial =
+ (emuxki_rate_to_pitch(srate) >> 8) & EMU_CHAN_IP_MASK;
+}
+
+/* voice params must be set before calling this */
+void
+emuxki_channel_set_bufparms(struct emuxki_channel *chan,
+ u_int32_t start, u_int32_t end)
+{
+ u_int8_t shift;
+ struct emuxki_voice *voice = chan->voice;
+
+ shift = voice->stereo + voice->b16;
+ chan->loop.start = start & EMU_CHAN_PSST_LOOPSTARTADDR_MASK;
+ chan->loop.end = end & EMU_CHAN_DSL_LOOPENDADDR_MASK;
+}
+
+void
+emuxki_channel_commit_parms(struct emuxki_channel *chan)
+{
+ struct emuxki_voice *voice = chan->voice;
+ struct emuxki_softc *sc = voice->sc;
+ u_int32_t start, mapval;
+ u_int8_t chano = chan->num;
+ int s;
+
+ start = chan->loop.start +
+ (voice->stereo ? 28 : 30) * (voice->b16 + 1);
+ mapval = DMAADDR(sc->silentpage) << 1 | EMU_CHAN_MAP_PTI_MASK;
+
+ s = splaudio();
+ emuxki_write(sc, chano, EMU_CHAN_CPF_STEREO, voice->stereo);
+ emuxki_write(sc, chano, EMU_CHAN_FXRT,
+ (chan->fxsend.d.dest << 28) | (chan->fxsend.c.dest << 24) |
+ (chan->fxsend.b.dest << 20) | (chan->fxsend.a.dest << 16));
+ emuxki_write(sc, chano, 0x10000000 | EMU_CHAN_PTRX,
+ (chan->fxsend.a.level << 8) | chan->fxsend.b.level);
+ emuxki_write(sc, chano, EMU_CHAN_DSL,
+ (chan->fxsend.d.level << 24) | chan->loop.end);
+ emuxki_write(sc, chano, EMU_CHAN_PSST,
+ (chan->fxsend.c.level << 24) | chan->loop.start);
+ emuxki_write(sc, chano, EMU_CHAN_CCCA,
+ (chan->filter.lowpass_resonance_height << 28) |
+ (chan->filter.interpolation_ROM << 25) |
+ (voice->b16 ? 0 : EMU_CHAN_CCCA_8BITSELECT) | start);
+ emuxki_write(sc, chano, EMU_CHAN_Z1, 0);
+ emuxki_write(sc, chano, EMU_CHAN_Z2, 0);
+ emuxki_write(sc, chano, EMU_CHAN_MAPA, mapval);
+ emuxki_write(sc, chano, EMU_CHAN_MAPB, mapval);
+ emuxki_write(sc, chano, EMU_CHAN_CVCF_CURRFILTER,
+ chan->filter.current_cutoff_frequency);
+ emuxki_write(sc, chano, EMU_CHAN_VTFT_FILTERTARGET,
+ chan->filter.target_cutoff_frequency);
+ emuxki_write(sc, chano, EMU_CHAN_ATKHLDM,
+ (chan->modulation.envelope.hold_time << 8) |
+ chan->modulation.envelope.attack_time);
+ emuxki_write(sc, chano, EMU_CHAN_DCYSUSM,
+ (chan->modulation.envelope.sustain_level << 8) |
+ chan->modulation.envelope.decay_time);
+ emuxki_write(sc, chano, EMU_CHAN_LFOVAL1,
+ chan->modulation.LFO_state);
+ emuxki_write(sc, chano, EMU_CHAN_LFOVAL2,
+ chan->vibrato_LFO.state);
+ emuxki_write(sc, chano, EMU_CHAN_FMMOD,
+ (chan->vibrato_LFO.modulation_depth << 8) |
+ chan->filter.LFO_modulation_depth);
+ emuxki_write(sc, chano, EMU_CHAN_TREMFRQ,
+ (chan->tremolo_depth << 8));
+ emuxki_write(sc, chano, EMU_CHAN_FM2FRQ2,
+ (chan->vibrato_LFO.vibrato_depth << 8) |
+ chan->vibrato_LFO.frequency);
+ emuxki_write(sc, chano, EMU_CHAN_ENVVAL,
+ chan->modulation.envelope.current_state);
+ emuxki_write(sc, chano, EMU_CHAN_ATKHLDV,
+ (chan->volume.envelope.hold_time << 8) |
+ chan->volume.envelope.attack_time);
+ emuxki_write(sc, chano, EMU_CHAN_ENVVOL,
+ chan->volume.envelope.current_state);
+ emuxki_write(sc, chano, EMU_CHAN_PEFE,
+ (chan->pitch.envelope_amount << 8) |
+ chan->filter.envelope_amount);
+ splx(s);
+}
+
+void
+emuxki_channel_start(struct emuxki_channel *chan)
+{
+ struct emuxki_voice *voice = chan->voice;
+ struct emuxki_softc *sc = voice->sc;
+ u_int8_t cache_sample, cache_invalid_size, chano = chan->num;
+ u_int32_t sample;
+ int s;
+
+ cache_sample = voice->stereo ? 4 : 2;
+ sample = voice->b16 ? 0x00000000 : 0x80808080;
+ cache_invalid_size = (voice->stereo ? 28 : 30) * (voice->b16 + 1);
+
+ s = splaudio();
+ while (cache_sample--) {
+ emuxki_write(sc, chano, EMU_CHAN_CD0 + cache_sample,
+ sample);
+ }
+ emuxki_write(sc, chano, EMU_CHAN_CCR_CACHEINVALIDSIZE, 0);
+ emuxki_write(sc, chano, EMU_CHAN_CCR_READADDRESS, 64);
+ emuxki_write(sc, chano, EMU_CHAN_CCR_CACHEINVALIDSIZE,
+ cache_invalid_size);
+ emuxki_write(sc, chano, EMU_CHAN_IFATN,
+ (chan->filter.target_cutoff_frequency << 8) |
+ chan->initial_attenuation);
+ emuxki_write(sc, chano, EMU_CHAN_VTFT_VOLUMETARGET,
+ chan->volume.target);
+ emuxki_write(sc, chano, EMU_CHAN_CVCF_CURRVOL,
+ chan->volume.current);
+ emuxki_write(sc, 0,
+ EMU_MKSUBREG(1, chano, EMU_SOLEL + (chano >> 5)),
+ 0); /* Clear stop on loop */
+ emuxki_write(sc, 0,
+ EMU_MKSUBREG(1, chano, EMU_CLIEL + (chano >> 5)),
+ 0); /* Clear loop interrupt */
+ emuxki_write(sc, chano, EMU_CHAN_DCYSUSV,
+ (chan->volume.envelope.sustain_level << 8) |
+ chan->volume.envelope.decay_time);
+ emuxki_write(sc, chano, EMU_CHAN_PTRX_PITCHTARGET,
+ chan->pitch.target);
+ emuxki_write(sc, chano, EMU_CHAN_CPF_PITCH,
+ chan->pitch.current);
+ emuxki_write(sc, chano, EMU_CHAN_IP, chan->pitch.intial);
+
+ splx(s);
+}
+
+void
+emuxki_channel_stop(struct emuxki_channel *chan)
+{
+ int s;
+ u_int8_t chano = chan->num;
+ struct emuxki_softc *sc = chan->voice->sc;
+
+ s = splaudio();
+ emuxki_write(sc, chano, EMU_CHAN_PTRX_PITCHTARGET, 0);
+ emuxki_write(sc, chano, EMU_CHAN_CPF_PITCH, 0);
+ emuxki_write(sc, chano, EMU_CHAN_IFATN_ATTENUATION, 0xff);
+ emuxki_write(sc, chano, EMU_CHAN_VTFT_VOLUMETARGET, 0);
+ emuxki_write(sc, chano, EMU_CHAN_CVCF_CURRVOL, 0);
+ emuxki_write(sc, chano, EMU_CHAN_IP, 0);
+ splx(s);
+}
+
+/*
+ * Voices managment
+ * emuxki_voice_dataloc : use(play or rec) independant dataloc union helpers
+ * emuxki_voice_channel_* : play part of dataloc union helpers
+ * emuxki_voice_recsrc_* : rec part of dataloc union helpers
+ */
+
+/* Allocate channels for voice in case of play voice */
+int
+emuxki_voice_channel_create(struct emuxki_voice *voice)
+{
+ struct emuxki_channel **channel = voice->sc->channel;
+ u_int8_t i, stereo = voice->stereo;
+ int s;
+
+ for (i = 0; i < EMU_NUMCHAN; i += stereo + 1) {
+ if ((stereo && (channel[i + 1] != NULL)) ||
+ (channel[i] != NULL)) /* Looking for free channels */
+ continue;
+ s = splaudio();
+ if (stereo) {
+ voice->dataloc.chan[1] =
+ emuxki_channel_new(voice, i + 1);
+ if (voice->dataloc.chan[1] == NULL) {
+ splx(s);
+ return (ENOMEM);
+ }
+ }
+ voice->dataloc.chan[0] = emuxki_channel_new(voice, i);
+ if (voice->dataloc.chan[0] == NULL) {
+ if (stereo) {
+ emuxki_channel_delete(voice->dataloc.chan[1]);
+ voice->dataloc.chan[1] = NULL;
+ }
+ splx(s);
+ return (ENOMEM);
+ }
+ splx(s);
+ return (0);
+ }
+ return (EAGAIN);
+}
+
+/* When calling this function we assume no one can access the voice */
+void
+emuxki_voice_channel_destroy(struct emuxki_voice *voice)
+{
+ emuxki_channel_delete(voice->dataloc.chan[0]);
+ voice->dataloc.chan[0] = NULL;
+ if (voice->stereo)
+ emuxki_channel_delete(voice->dataloc.chan[1]);
+ voice->dataloc.chan[1] = NULL;
+}
+
+/*
+ * Will come back when used in voice_dataloc_create
+ */
+#if 0
+int
+emuxki_recsrc_reserve(struct emuxki_voice *voice, emuxki_recsrc_t source)
+{
+ if (voice->emu->recsrc[source] != NULL)
+ return (EBUSY);
+ voice->emu->recsrc[source] = voice;
+ return (0);
+}
+#endif
+
+/* When calling this function we assume the voice is stopped */
+void
+emuxki_voice_recsrc_release(struct emuxki_softc *sc, emuxki_recsrc_t source)
+{
+ sc->recsrc[source] = NULL;
+}
+
+int
+emuxki_voice_dataloc_create(struct emuxki_voice *voice)
+{
+ int error;
+
+ if (voice->use & EMU_VOICE_USE_PLAY) {
+ if ((error = emuxki_voice_channel_create(voice)))
+ return (error);
+ } else {
+ /*
+ * Commented out because i don't know how to get the selected
+ * recording source
+ */
+#if 0
+ if (emuxki_recsrc_reserve(voice, recsrc))
+ return (EBUSY);
+ printf("Which rec src do i have to create!!!\n");
+#endif
+ return (EBUSY); /* just return an error, no real meaning */
+ }
+ return (0);
+}
+
+void
+emuxki_voice_dataloc_destroy(struct emuxki_voice *voice)
+{
+ if (voice->use & EMU_VOICE_USE_PLAY) {
+ if (voice->dataloc.chan[0] != NULL)
+ emuxki_voice_channel_destroy(voice);
+ } else {
+ if (voice->dataloc.source != EMU_RECSRC_NOTSET) {
+ emuxki_voice_recsrc_release(voice->sc,
+ voice->dataloc.source);
+ voice->dataloc.source = EMU_RECSRC_NOTSET;
+ }
+ }
+}
+
+struct emuxki_voice *
+emuxki_voice_new(struct emuxki_softc *sc, u_int8_t use)
+{
+ struct emuxki_voice *voice;
+ int s;
+
+ if ((voice = malloc(sizeof(*voice), M_DEVBUF, M_WAITOK)) == NULL)
+ return (NULL);
+ voice->sc = sc;
+ voice->use = use;
+ voice->state = !EMU_VOICE_STATE_STARTED;
+ voice->stereo = EMU_VOICE_STEREO_NOTSET;
+ voice->b16 = 0;
+ voice->sample_rate = 0;
+ if (use & EMU_VOICE_USE_PLAY)
+ voice->dataloc.chan[0] = voice->dataloc.chan[0] = NULL;
+ else
+ voice->dataloc.source = EMU_RECSRC_NOTSET;
+ voice->buffer = NULL;
+ voice->blksize = 0;
+ voice->trigblk = 0;
+ voice->blkmod = 0;
+ voice->inth = NULL;
+ voice->inthparam = NULL;
+
+ s = splaudio();
+ LIST_INSERT_HEAD((&sc->voices), voice, next);
+ splx(s);
+
+ return (voice);
+}
+
+void
+emuxki_voice_delete(struct emuxki_voice *voice)
+{
+ int s;
+
+ if (voice->state & EMU_VOICE_STATE_STARTED)
+ emuxki_voice_halt(voice);
+
+ s = splaudio();
+ LIST_REMOVE(voice, next);
+ splx(s);
+
+ emuxki_voice_dataloc_destroy(voice);
+ free(voice, M_DEVBUF);
+}
+
+int
+emuxki_voice_set_stereo(struct emuxki_voice *voice, u_int8_t stereo)
+{
+ int error;
+ struct emuxki_chanparms_fxsend fxsend;
+
+ emuxki_voice_dataloc_destroy(voice);
+ voice->stereo = stereo;
+ if ((error = emuxki_voice_dataloc_create(voice)))
+ return (error);
+ if (voice->use & EMU_VOICE_USE_PLAY) {
+ fxsend.a.dest = 0x0;
+ fxsend.b.dest = 0x1;
+ fxsend.c.dest = 0x2;
+ fxsend.d.dest = 0x3;
+ if (voice->stereo) {
+ fxsend.a.level = fxsend.c.level = 0xff;
+ fxsend.b.level = fxsend.d.level = 0x00;
+ emuxki_channel_set_fxsend(voice->dataloc.chan[0],
+ &fxsend);
+ fxsend.a.level = fxsend.c.level = 0x00;
+ fxsend.b.level = fxsend.d.level = 0xff;
+ emuxki_channel_set_fxsend(voice->dataloc.chan[1],
+ &fxsend);
+ } /* No else : default is good for mono */
+ }
+ return (0);
+}
+
+int
+emuxki_voice_set_srate(struct emuxki_voice *voice, u_int32_t srate)
+{
+ if (voice->use & EMU_VOICE_USE_PLAY) {
+ if ((srate < 4000) || (srate > 48000))
+ return (EINVAL);
+ voice->sample_rate = srate;
+ emuxki_channel_set_srate(voice->dataloc.chan[0], srate);
+ if (voice->stereo)
+ emuxki_channel_set_srate(voice->dataloc.chan[1],
+ srate);
+ } else {
+#ifdef EMUXKI_DEBUG
+ printf("Recording voice set_srate not implemented\n");
+#endif
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+emuxki_voice_set_audioparms(struct emuxki_voice *voice, u_int8_t stereo,
+ u_int8_t b16, u_int32_t srate)
+{
+ int error;
+
+ /*
+ * Audio driver tried to set recording AND playing params even if
+ * device opened in play or record only mode ==>
+ * modified emuxki_set_params.
+ * Stays here for now just in case ...
+ */
+ if (voice == NULL) {
+#ifdef EMUXKI_DEBUG
+ printf("warning: tried to set unallocated voice params !!\n");
+#endif
+ return (0);
+ }
+
+ if (voice->stereo == stereo && voice->b16 == b16 &&
+ voice->sample_rate == srate)
+ return (0);
+
+#ifdef EMUXKI_DEBUG
+ printf("Setting %s voice params : %s, %u bits, %u hz\n",
+ (voice->use & EMU_VOICE_USE_PLAY) ? "play" : "record",
+ stereo ? "stereo" : "mono", (b16 + 1) * 8, srate);
+#endif
+
+ if (voice->stereo != stereo) {
+ if ((error = emuxki_voice_set_stereo(voice, stereo)))
+ return (error);
+ }
+ voice->b16 = b16;
+ if (voice->sample_rate != srate)
+ emuxki_voice_set_srate(voice, srate);
+ return (0);
+}
+
+/* voice audio parms (see just before) must be set prior to this */
+int
+emuxki_voice_set_bufparms(struct emuxki_voice *voice, void *ptr,
+ u_int32_t bufsize, u_int16_t blksize)
+{
+ struct emuxki_mem *mem;
+ struct emuxki_channel **chan;
+ u_int32_t start, end;
+ u_int8_t sample_size;
+ int error = EFAULT;
+
+ LIST_FOREACH(mem, &voice->sc->mem, next) {
+ if (KERNADDR(mem->dmamem) != ptr)
+ continue;
+
+ voice->buffer = mem;
+ sample_size = (voice->b16 + 1) * (voice->stereo + 1);
+ voice->blksize = blksize / sample_size;
+ voice->trigblk = 0; /* This shouldn't be needed */
+ voice->blkmod = bufsize / blksize;
+ if (bufsize % blksize) /* This should not happen */
+ voice->blkmod++;
+ error = 0;
+
+ if (voice->use & EMU_VOICE_USE_PLAY) {
+ chan = voice->dataloc.chan;
+ start = mem->ptbidx << 12;
+ end = start + bufsize / sample_size;
+ emuxki_channel_set_bufparms(chan[0],
+ start, end);
+ if (voice->stereo)
+ emuxki_channel_set_bufparms(chan[1],
+ start, end);
+ voice->timerate = (u_int32_t) 48000 *
+ voice->blksize / voice->sample_rate;
+ if (voice->timerate < 5)
+ error = EINVAL;
+ } else {
+#ifdef EMUXKI_DEBUG
+ printf("Rec voice set bufparms not implemented\n");
+#endif
+ error = ENODEV;
+ }
+
+ break;
+ }
+
+ return (error);
+}
+
+void
+emuxki_voice_commit_parms(struct emuxki_voice *voice)
+{
+ if (voice->use & EMU_VOICE_USE_PLAY) {
+ emuxki_channel_commit_parms(voice->dataloc.chan[0]);
+ if (voice->stereo)
+ emuxki_channel_commit_parms(voice->dataloc.chan[1]);
+ }
+}
+
+u_int32_t
+emuxki_voice_curaddr(struct emuxki_voice *voice)
+{
+ if (voice->use & EMU_VOICE_USE_PLAY)
+ return (emuxki_read(voice->sc,
+ voice->dataloc.chan[0]->num,
+ EMU_CHAN_CCCA_CURRADDR) -
+ voice->dataloc.chan[0]->loop.start);
+ return (0);
+}
+
+void
+emuxki_resched_timer(struct emuxki_softc *sc)
+{
+ struct emuxki_voice *voice;
+ u_int16_t timerate = 1024;
+ u_int8_t active = 0;
+ int s;
+
+ s = splaudio();
+ LIST_FOREACH(voice, &sc->voices, next) {
+ if ((voice->use & EMU_VOICE_USE_PLAY) == 0 ||
+ (voice->state & EMU_VOICE_STATE_STARTED) == 0)
+ continue;
+ active = 1;
+ if (voice->timerate < timerate)
+ timerate = voice->timerate;
+ }
+
+ if (timerate & ~EMU_TIMER_RATE_MASK)
+ timerate = 0;
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_TIMER, timerate);
+ if (!active && (sc->timerstate & EMU_TIMER_STATE_ENABLED)) {
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_INTE,
+ bus_space_read_4(sc->sc_iot, sc->sc_ioh, EMU_INTE) &
+ ~EMU_INTE_INTERTIMERENB);
+ sc->timerstate &= ~EMU_TIMER_STATE_ENABLED;
+ } else if (active && !(sc->timerstate & EMU_TIMER_STATE_ENABLED)) {
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_INTE,
+ bus_space_read_4(sc->sc_iot, sc->sc_ioh, EMU_INTE) |
+ EMU_INTE_INTERTIMERENB);
+ sc->timerstate |= EMU_TIMER_STATE_ENABLED;
+ }
+ splx(s);
+}
+
+void
+emuxki_voice_start(struct emuxki_voice *voice,
+ void (*inth) (void *), void *inthparam)
+{
+ voice->inth = inth;
+ voice->inthparam = inthparam;
+ if (voice->use & EMU_VOICE_USE_PLAY) {
+ voice->trigblk = 1;
+ emuxki_channel_start(voice->dataloc.chan[0]);
+ if (voice->stereo)
+ emuxki_channel_start(voice->dataloc.chan[1]);
+ }
+#ifdef EMUXKI_DEBUG
+ else
+ printf("Recording voice start not implemented\n");
+#endif
+ voice->state |= EMU_VOICE_STATE_STARTED;
+ if (voice->use & EMU_VOICE_USE_PLAY)
+ emuxki_resched_timer(voice->sc);
+}
+
+void
+emuxki_voice_halt(struct emuxki_voice *voice)
+{
+ if (voice->use & EMU_VOICE_USE_PLAY) {
+ emuxki_channel_stop(voice->dataloc.chan[0]);
+ if (voice->stereo)
+ emuxki_channel_stop(voice->dataloc.chan[1]);
+ }
+#ifdef EMUXKI_DEBUG
+ else
+ printf("Recording voice halt not implemented\n");
+#endif
+ voice->state &= ~EMU_VOICE_STATE_STARTED;
+ if (voice->use & EMU_VOICE_USE_PLAY)
+ emuxki_resched_timer(voice->sc);
+}
+
+/*
+ * The interrupt handler
+ */
+int
+emuxki_intr(void *arg)
+{
+ struct emuxki_softc *sc = arg;
+ u_int32_t ipr, curblk, us = 0;
+ struct emuxki_voice *voice;
+
+ while ((ipr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, EMU_IPR))) {
+ if (ipr & EMU_IPR_INTERVALTIMER) {
+ LIST_FOREACH(voice, &sc->voices, next) {
+ if ((voice->use & EMU_VOICE_USE_PLAY)==0 ||
+ (voice->state &
+ EMU_VOICE_STATE_STARTED) == 0)
+ continue;
+
+ curblk = emuxki_voice_curaddr(voice) /
+ voice->blksize;
+ if (curblk == voice->trigblk) {
+ voice->inth(voice->inthparam);
+ voice->trigblk++;
+ voice->trigblk %= voice->blkmod;
+ }
+ }
+ us = 1;
+ }
+
+ /* Got interrupt */
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_IPR, ipr);
+ }
+
+ return (us);
+}
+
+
+/*
+ * Audio Architecture callbacks
+ */
+
+int
+emuxki_open(void *addr, int flags)
+{
+ struct emuxki_softc *sc = addr;
+
+#ifdef EMUXKI_DEBUG
+ printf("%s: emuxki_open called\n", sc->sc_dev.dv_xname);
+#endif
+
+ /*
+ * Multiple voice support would be added as soon as I find a way to
+ * trick the audio arch into supporting multiple voices.
+ * Or I might integrate a modified audio arch supporting
+ * multiple voices.
+ */
+
+ /*
+ * I did this because i have problems identifying the selected
+ * recording source(s) which is necessary when setting recording
+ * params This will be adressed very soon
+ */
+ if (flags & AUOPEN_READ)
+ return (EOPNOTSUPP);
+
+ if (flags & AUOPEN_WRITE) {
+ sc->pvoice = emuxki_voice_new(sc, EMU_VOICE_USE_PLAY);
+ if (sc->pvoice == NULL) {
+ if (flags & AUOPEN_READ)
+ emuxki_voice_delete(sc->rvoice);
+ return (EBUSY);
+ }
+ }
+
+ return (0);
+}
+
+void
+emuxki_close(void *addr)
+{
+ struct emuxki_softc *sc = addr;
+
+#ifdef EMUXKI_DEBUG
+ printf("%s: emu10K1_close called\n", sc->sc_dev.dv_xname);
+#endif
+
+ /* No multiple voice support for now */
+ if (sc->rvoice != NULL)
+ emuxki_voice_delete(sc->rvoice);
+ sc->rvoice = NULL;
+ if (sc->pvoice != NULL)
+ emuxki_voice_delete(sc->pvoice);
+ sc->pvoice = NULL;
+}
+
+int
+emuxki_query_encoding(void *addr, struct audio_encoding *fp)
+{
+#ifdef EMUXKI_DEBUG
+ struct emuxki_softc *sc = addr;
+
+ printf("%s: emuxki_query_encoding called\n", sc->sc_dev.dv_xname);
+#endif
+
+ switch (fp->index) {
+ case 0:
+ strcpy(fp->name, AudioEulinear);
+ fp->encoding = AUDIO_ENCODING_ULINEAR;
+ fp->precision = 8;
+ fp->flags = 0;
+ break;
+ case 1:
+ strcpy(fp->name, AudioEmulaw);
+ fp->encoding = AUDIO_ENCODING_ULAW;
+ fp->precision = 8;
+ fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+ case 2:
+ strcpy(fp->name, AudioEalaw);
+ fp->encoding = AUDIO_ENCODING_ALAW;
+ fp->precision = 8;
+ fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+ case 3:
+ strcpy(fp->name, AudioEslinear);
+ fp->encoding = AUDIO_ENCODING_SLINEAR;
+ fp->precision = 8;
+ fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+ case 4:
+ strcpy(fp->name, AudioEslinear_le);
+ fp->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ fp->precision = 16;
+ fp->flags = 0;
+ break;
+ case 5:
+ strcpy(fp->name, AudioEulinear_le);
+ fp->encoding = AUDIO_ENCODING_ULINEAR_LE;
+ fp->precision = 16;
+ fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+ case 6:
+ strcpy(fp->name, AudioEslinear_be);
+ fp->encoding = AUDIO_ENCODING_SLINEAR_BE;
+ fp->precision = 16;
+ fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+ case 7:
+ strcpy(fp->name, AudioEulinear_be);
+ fp->encoding = AUDIO_ENCODING_ULINEAR_BE;
+ fp->precision = 16;
+ fp->flags = AUDIO_ENCODINGFLAG_EMULATED;
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+int
+emuxki_set_vparms(struct emuxki_voice *voice, struct audio_params *p)
+{
+ u_int8_t b16, mode;
+
+ mode = (voice->use & EMU_VOICE_USE_PLAY) ?
+ AUMODE_PLAY : AUMODE_RECORD;
+ p->factor = 1;
+ p->sw_code = NULL;
+ if (p->channels != 1 && p->channels != 2)
+ return (EINVAL);/* Will change when streams come in use */
+
+ switch (p->encoding) {
+ case AUDIO_ENCODING_ULAW:
+ if (mode == AUMODE_PLAY) {
+ p->factor = 2;
+ p->sw_code = mulaw_to_slinear16_le;
+ b16 = 1;
+ } else {
+ p->sw_code = ulinear8_to_mulaw;
+ b16 = 0;
+ }
+ break;
+
+ case AUDIO_ENCODING_ALAW:
+ if (mode == AUMODE_PLAY) {
+ p->factor = 2;
+ p->sw_code = alaw_to_slinear16_le;
+ b16 = 1;
+ } else {
+ p->sw_code = ulinear8_to_alaw;
+ b16 = 0;
+ }
+ break;
+
+ case AUDIO_ENCODING_SLINEAR_LE:
+ if (p->precision == 8)
+ p->sw_code = change_sign8;
+ b16 = (p->precision == 16);
+ break;
+
+ case AUDIO_ENCODING_ULINEAR_LE:
+ if (p->precision == 16)
+ p->sw_code = change_sign16_le;
+ b16 = (p->precision == 16);
+ break;
+
+ case AUDIO_ENCODING_SLINEAR_BE:
+ if (p->precision == 16)
+ p->sw_code = swap_bytes;
+ else
+ p->sw_code = change_sign8;
+ b16 = (p->precision == 16);
+ break;
+
+ case AUDIO_ENCODING_ULINEAR_BE:
+ if (p->precision == 16) {
+ if (mode == AUMODE_PLAY)
+ p->sw_code = swap_bytes_change_sign16_le;
+ else
+ p->sw_code = change_sign16_swap_bytes_le;
+ }
+ b16 = (p->precision == 16);
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ return (emuxki_voice_set_audioparms(voice, p->channels == 2,
+ b16, p->sample_rate));
+}
+
+int
+emuxki_set_params(void *addr, int setmode, int usemode,
+ struct audio_params *play, struct audio_params *rec)
+{
+ struct emuxki_softc *sc = addr;
+ int mode, error;
+ struct audio_params *p;
+
+ for (mode = AUMODE_RECORD; mode != -1;
+ mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) {
+ if ((usemode & setmode & mode) == 0)
+ continue;
+
+ p = (mode == AUMODE_PLAY) ? play : rec;
+
+ /* No multiple voice support for now */
+ if ((error = emuxki_set_vparms((mode == AUMODE_PLAY) ?
+ sc->pvoice : sc->rvoice, p)))
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+emuxki_halt_output(void *addr)
+{
+ struct emuxki_softc *sc = addr;
+
+ /* No multiple voice support for now */
+ if (sc->pvoice == NULL)
+ return (ENXIO);
+
+ emuxki_voice_halt(sc->pvoice);
+ return (0);
+}
+
+int
+emuxki_halt_input(void *addr)
+{
+ struct emuxki_softc *sc = addr;
+
+#ifdef EMUXKI_DEBUG
+ printf("%s: emuxki_halt_input called\n", sc->sc_dev.dv_xname);
+#endif
+
+ /* No multiple voice support for now */
+ if (sc->rvoice == NULL)
+ return (ENXIO);
+ emuxki_voice_halt(sc->rvoice);
+ return (0);
+}
+
+int
+emuxki_getdev(void *addr, struct audio_device *dev)
+{
+ strncpy(dev->name, "Creative EMU10k1", sizeof(dev->name));
+ strcpy(dev->version, "");
+ strncpy(dev->config, "emuxki", sizeof(dev->config));
+
+ return (0);
+}
+
+int
+emuxki_set_port(void *addr, mixer_ctrl_t *mctl)
+{
+ struct emuxki_softc *sc = addr;
+
+ return sc->codecif->vtbl->mixer_set_port(sc->codecif, mctl);
+}
+
+int
+emuxki_get_port(void *addr, mixer_ctrl_t *mctl)
+{
+ struct emuxki_softc *sc = addr;
+
+ return sc->codecif->vtbl->mixer_get_port(sc->codecif, mctl);
+}
+
+int
+emuxki_query_devinfo(void *addr, mixer_devinfo_t *minfo)
+{
+ struct emuxki_softc *sc = addr;
+
+ return sc->codecif->vtbl->query_devinfo(sc->codecif, minfo);
+}
+
+void *
+emuxki_allocm(void *addr, int direction, size_t size, int type, int flags)
+{
+ struct emuxki_softc *sc = addr;
+
+ if (direction == AUMODE_PLAY)
+ return emuxki_pmem_alloc(sc, size, type, flags);
+ else
+ return emuxki_rmem_alloc(sc, size, type, flags);
+}
+
+void
+emuxki_freem(void *addr, void *ptr, int type)
+{
+ struct emuxki_softc *sc = addr;
+ int i, s;
+ struct emuxki_mem *mem;
+ size_t numblocks;
+ u_int32_t *ptb, silentpage;
+
+ ptb = KERNADDR(sc->ptb);
+ silentpage = DMAADDR(sc->silentpage) << 1;
+ LIST_FOREACH(mem, &sc->mem, next) {
+ if (KERNADDR(mem->dmamem) != ptr)
+ continue;
+
+ s = splaudio();
+ if (mem->ptbidx != EMU_RMEM) {
+ numblocks = DMASIZE(mem->dmamem) / EMU_PTESIZE;
+ if (DMASIZE(mem->dmamem) % EMU_PTESIZE)
+ numblocks++;
+ for (i = 0; i < numblocks; i++)
+ ptb[mem->ptbidx + i] =
+ silentpage | (mem->ptbidx + i);
+ }
+ LIST_REMOVE(mem, next);
+ splx(s);
+
+ emuxki_mem_delete(mem, type);
+ break;
+ }
+}
+
+size_t
+emuxki_round_buffersize(void *addr, int direction, size_t bsize)
+{
+ static const int recbuf_sz[] = {
+ 0, 384, 448, 512, 640, 768, 896, 1024, 1280, 1536, 1792,
+ 2048, 2560, 3072, 3584, 4096, 5120, 6144, 7168, 8192, 10240,
+ 12288, 14366, 16384, 20480, 24576, 28672, 32768, 40960, 49152,
+ 57344, 65536
+ };
+
+ if (direction == AUMODE_PLAY) {
+ if (bsize < EMU_PTESIZE)
+ bsize = EMU_PTESIZE;
+ else if (bsize > (EMU_PTESIZE * EMU_MAXPTE))
+ bsize = EMU_PTESIZE * EMU_MAXPTE;
+ /* Would be better if set to max available */
+ else if (bsize % EMU_PTESIZE)
+ bsize = bsize -
+ (bsize % EMU_PTESIZE) +
+ EMU_PTESIZE;
+ } else {
+ int idx;
+
+ /* find nearest lower recbuf size */
+ for(idx=32; --idx >= 0; ) {
+ if (bsize >= recbuf_sz[idx]) {
+ bsize = recbuf_sz[idx];
+ break;
+ }
+ }
+
+ if (bsize == 0)
+ bsize = 384;
+ }
+
+ return (bsize);
+}
+
+int
+emuxki_mappage(void *addr, void *ptr, int off, int prot)
+{
+ struct emuxki_softc *sc = addr;
+ struct emuxki_mem *mem;
+ u_int32_t *ptb;
+
+ ptb = KERNADDR(sc->ptb);
+ LIST_FOREACH(mem, &sc->mem, next) {
+ if (KERNADDR(mem->dmamem) == ptr) {
+ struct dmamem *dm = mem->dmamem;
+
+ return bus_dmamem_mmap(dm->dmat, dm->segs, dm->nsegs,
+ off, prot, BUS_DMA_WAITOK);
+ }
+ }
+
+ return (-1);
+}
+
+int
+emuxki_get_props(void *addr)
+{
+ return (AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT |
+ AUDIO_PROP_FULLDUPLEX);
+}
+
+int
+emuxki_trigger_output(void *addr, void *start, void *end, int blksize,
+ void (*inth) (void *), void *inthparam,
+ struct audio_params *params)
+{
+ struct emuxki_softc *sc = addr;
+ /* No multiple voice support for now */
+ struct emuxki_voice *voice = sc->pvoice;
+ int error;
+
+ if (voice == NULL)
+ return (ENXIO);
+ if ((error = emuxki_set_vparms(voice, params)))
+ return (error);
+ if ((error = emuxki_voice_set_bufparms(voice, start,
+ (caddr_t)end - (caddr_t)start, blksize)))
+ return (error);
+ emuxki_voice_commit_parms(voice);
+ emuxki_voice_start(voice, inth, inthparam);
+
+ return (0);
+}
+
+int
+emuxki_trigger_input(void *addr, void *start, void *end, int blksize,
+ void (*inth) (void *), void *inthparam,
+ struct audio_params *params)
+{
+ struct emuxki_softc *sc = addr;
+ /* No multiple voice support for now */
+ struct emuxki_voice *voice = sc->rvoice;
+ int error;
+
+ if (voice == NULL)
+ return (ENXIO);
+ if ((error = emuxki_set_vparms(voice, params)))
+ return (error);
+ if ((error = emuxki_voice_set_bufparms(voice, start,
+ (caddr_t)end - (caddr_t)start,
+ blksize)))
+ return (error);
+ emuxki_voice_commit_parms(voice); /* Useless for record ? */
+ emuxki_voice_start(voice, inth, inthparam);
+
+ return (0);
+}
+
+
+/*
+ * AC97 callbacks
+ */
+
+int
+emuxki_ac97_attach(void *arg, struct ac97_codec_if *codecif)
+{
+ struct emuxki_softc *sc = arg;
+
+ sc->codecif = codecif;
+ return (0);
+}
+
+int
+emuxki_ac97_read(void *arg, u_int8_t reg, u_int16_t *val)
+{
+ struct emuxki_softc *sc = arg;
+ int s;
+
+ s = splaudio();
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, EMU_AC97ADDR, reg);
+ *val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, EMU_AC97DATA);
+ splx(s);
+
+ return (0);
+}
+
+int
+emuxki_ac97_write(void *arg, u_int8_t reg, u_int16_t val)
+{
+ struct emuxki_softc *sc = arg;
+ int s;
+
+ s = splaudio();
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, EMU_AC97ADDR, reg);
+ bus_space_write_2(sc->sc_iot, sc->sc_ioh, EMU_AC97DATA, val);
+ splx(s);
+
+ return (0);
+}
+
+void
+emuxki_ac97_reset(void *arg)
+{
+}
diff --git a/sys/dev/pci/emuxkireg.h b/sys/dev/pci/emuxkireg.h
new file mode 100644
index 00000000000..06da79350db
--- /dev/null
+++ b/sys/dev/pci/emuxkireg.h
@@ -0,0 +1,651 @@
+/* $NetBSD: emuxkireg.h,v 1.1 2001/10/17 18:39:41 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Yannick Montulet.
+ *
+ * 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.
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#ifndef _DEV_PCI_EMUXKIREG_H_
+#define _DEV_PCI_EMUXKIREG_H_
+
+/*
+ * Register values for Creative EMU10000. The register values have been
+ * taken from GPLed SBLive! header file published by Creative. The comments
+ * have been stripped to avoid GPL pollution in kernel. The Creative version
+ * including comments is available in Linux 2.4.* kernel as file
+ * drivers/sound/emu10k1/8010.h
+ */
+
+
+#define EMU_MKSUBREG(sz, idx, reg) (((sz) << 24) | ((idx) << 16) | (reg))
+
+#define EMU_PTR 0x00
+#define EMU_PTR_CHNO_MASK 0x0000003f
+#define EMU_PTR_ADDR_MASK 0x07ff0000
+
+#define EMU_DATA 0x04
+
+#define EMU_IPR 0x08
+#define EMU_IPR_RATETRCHANGE 0x01000000
+#define EMU_IPR_FXDSP 0x00800000
+#define EMU_IPR_FORCEINT 0x00400000
+#define EMU_PCIERROR 0x00200000
+#define EMU_IPR_VOLINCR 0x00100000
+#define EMU_IPR_VOLDECR 0x00080000
+#define EMU_IPR_MUTE 0x00040000
+#define EMU_IPR_MICBUFFULL 0x00020000
+#define EMU_IPR_MICBUFHALFFULL 0x00010000
+#define EMU_IPR_ADCBUFFULL 0x00008000
+#define EMU_IPR_ADCBUFHALFFULL 0x00004000
+#define EMU_IPR_EFXBUFFULL 0x00002000
+#define EMU_IPR_EFXBUFHALFFULL 0x00001000
+#define EMU_IPR_GPSPDIFSTCHANGE 0x00000800
+#define EMU_IPR_CDROMSTCHANGE 0x00000400
+#define EMU_IPR_INTERVALTIMER 0x00000200
+#define EMU_IPR_MIDITRANSBUFE 0x00000100
+#define EMU_IPR_MIDIRECVBUFE 0x00000080
+#define EMU_IPR_CHANNELLOOP 0x00000040
+#define EMU_IPR_CHNOMASK 0x0000003f
+
+#define EMU_INTE 0x0c
+
+#define EMU_INTE_VSB_MASK 0xc0000000
+#define EMU_INTE_VSB_220 0x00000000
+#define EMU_INTE_VSB_240 0x40000000
+#define EMU_INTE_VSB_260 0x80000000
+#define EMU_INTE_VSB_280 0xc0000000
+
+#define EMU_INTE_VMPU_MASK 0x30000000
+#define EMU_INTE_VMPU_300 0x00000000
+#define EMU_INTE_VMPU_310 0x10000000
+#define EMU_INTE_VMPU_320 0x20000000
+#define EMU_INTE_VMPU_330 0x30000000
+#define EMU_INTE_MDMAENABLE 0x08000000
+#define EMU_INTE_SDMAENABLE 0x04000000
+#define EMU_INTE_MPICENABLE 0x02000000
+#define EMU_INTE_SPICENABLE 0x01000000
+#define EMU_INTE_VSBENABLE 0x00800000
+#define EMU_INTE_ADLIBENABLE 0x00400000
+#define EMU_INTE_MPUENABLE 0x00200000
+#define EMU_INTE_FORCEINT 0x00100000
+#define EMU_INTE_MRHANDENABLE 0x00080000
+#define EMU_INTE_SAMPLERATER 0x00002000
+#define EMU_INTE_FXDSPENABLE 0x00001000
+#define EMU_INTE_PCIERRENABLE 0x00000800
+#define EMU_INTE_VOLINCRENABLE 0x00000400
+#define EMU_INTE_VOLDECRENABLE 0x00000200
+#define EMU_INTE_MUTEENABLE 0x00000100
+#define EMU_INTE_MICBUFENABLE 0x00000080
+#define EMU_INTE_ADCBUFENABLE 0x00000040
+#define EMU_INTE_EFXBUFENABLE 0x00000020
+#define EMU_INTE_GPSPDIFENABLE 0x00000010
+#define EMU_INTE_CDSPDIFENABLE 0x00000008
+#define EMU_INTE_INTERTIMERENB 0x00000004
+#define EMU_INTE_MIDITXENABLE 0x00000002
+#define EMU_INTE_MIDIRXENABLE 0x00000001
+
+#define EMU_WC 0x10
+
+#define EMU_WC_SAMPLECOUNTER_MASK 0x03FFFFC0
+#define EMU_WC_SAMPLECOUNTER EMU_MKSUBREG(20, 6, EMU_WC)
+#define EMU_WC_CURRENTCHANNEL 0x0000003F
+
+#define EMU_HCFG 0x14
+#define EMU_HCFG_LEGACYFUNC_MASK 0xe0000000
+#define EMU_HCFG_LEGACYFUNC_MPU 0x00000000
+#define EMU_HCFG_LEGACYFUNC_SB 0x40000000
+#define EMU_HCFG_LEGACYFUNC_AD 0x60000000
+#define EMU_HCFG_LEGACYFUNC_MPIC 0x80000000
+#define EMU_HCFG_LEGACYFUNC_MDMA 0xa0000000
+#define EMU_HCFG_LEGACYFUNC_SPCI 0xc0000000
+#define EMU_HCFG_LEGACYFUNC_SDMA 0xe0000000
+#define EMU_HCFG_IOCAPTUREADDR 0x1f000000
+#define EMU_HCFG_LEGACYWRITE 0x00800000
+#define EMU_HCFG_LEGACYWORD 0x00400000
+
+#define EMU_HCFG_LEGACYINT 0x00200000
+#define EMU_HCFG_CODECFMT_MASK 0x00070000
+#define EMU_HCFG_CODECFMT_AC97 0x00000000
+#define EMU_HCFG_CODECFMT_I2S 0x00010000
+#define EMU_HCFG_GPINPUT0 0x00004000
+#define EMU_HCFG_GPINPUT1 0x00002000
+#define EMU_HCFG_GPOUTPUT_MASK 0x00001c00
+#define EMU_HCFG_JOYENABLE 0x00000200
+#define EMU_HCFG_PHASETRACKENABLE 0x00000100
+#define EMU_HCFG_AC3ENABLE_MASK 0x000000e0
+#define EMU_HCFG_AC3ENABLE_ZVIDEO 0x00000080
+#define EMU_HCFG_AC3ENABLE_CDSPDIF 0x00000040
+#define EMU_HCFG_AC3ENABLE_GPSPDIF 0x00000020
+#define EMU_HCFG_AUTOMUTE 0x00000010
+#define EMU_HCFG_LOCKSOUNDCACHE 0x00000008
+#define EMU_HCFG_LOCKTANKCACHE_MASK 0x00000004
+#define EMU_HCFG_LOCKTANKCACHE EMU_MKSUBREG(1, 2, EMU_HCFG)
+#define EMU_HCFG_MUTEBUTTONENABLE 0x00000002
+#define EMU_HCFG_AUDIOENABLE 0x00000001
+
+#define EMU_MUDATA 0x18
+#define EMU_MUCMD 0x19
+#define EMU_MUCMD_RESET 0xff
+#define EMU_MUCMD_ENTERUARTMODE 0x3f
+
+#define EMU_MUSTAT EMU_MUCMD
+#define EMU_MUSTAT_IRDYN 0x80
+#define EMU_MUSTAT_ORDYN 0x40
+
+#define EMU_TIMER 0x1a
+#define EMU_TIMER_RATE_MASK 0x000003ff
+#define EMU_TIMER_RATE EMU_MKSUBREG(10, 0, EMU_TIMER)
+
+#define EMU_AC97DATA 0x1c
+#define EMU_AC97ADDR 0x1e
+#define EMU_AC97ADDR_RDY 0x80
+#define EMU_AC97ADDR_ADDR 0x7f
+
+
+#define EMU_CHAN_CPF 0x00
+
+#define EMU_CHAN_CPF_PITCH_MASK 0xffff0000
+#define EMU_CHAN_CPF_PITCH EMU_MKSUBREG(16, 16, EMU_CHAN_CPF)
+
+#define EMU_CHAN_CPF_STEREO_MASK 0x00008000
+#define EMU_CHAN_CPF_STEREO EMU_MKSUBREG(1, 15, EMU_CHAN_CPF)
+#define EMU_CHAN_CPF_STOP_MASK 0x00004000
+
+#define EMU_CHAN_CPF_FRACADDRESS_MASK 0x00003fff
+
+
+#define EMU_CHAN_PTRX 0x01
+
+#define EMU_CHAN_PTRX_PITCHTARGET_MASK 0xffff0000
+#define EMU_CHAN_PTRX_PITCHTARGET EMU_MKSUBREG(16, 16, EMU_CHAN_PTRX)
+
+#define EMU_CHAN_PTRX_FXSENDAMOUNT_A_MASK 0x0000ff00
+#define EMU_CHAN_PTRX_FXSENDAMOUNT_A EMU_MKSUBREG(8, 8, EMU_CHAN_PTRX)
+
+#define EMU_CHAN_PTRX_FXSENDAMOUNT_B_MASK 0x000000ff
+#define EMU_CHAN_PTRX_FXSENDAMOUNT_B EMU_MKSUBREG(8, 0, EMU_CHAN_PTRX)
+
+#define EMU_CHAN_CVCF 0x02
+#define EMU_CHAN_CVCF_CURRVOL_MASK 0xffff0000
+
+#define EMU_CHAN_CVCF_CURRVOL EMU_MKSUBREG(16, 16, EMU_CHAN_CVCF)
+#define EMU_CHAN_CVCF_CURRFILTER_MASK 0x0000ffff
+
+#define EMU_CHAN_CVCF_CURRFILTER EMU_MKSUBREG(16, 0, EMU_CHAN_CVCF)
+
+#define EMU_CHAN_VTFT 0x03
+#define EMU_CHAN_VTFT_VOLUMETARGET_MASK 0xffff0000
+
+#define EMU_CHAN_VTFT_VOLUMETARGET EMU_MKSUBREG(16, 16, EMU_CHAN_VTFT)
+#define EMU_CHAN_VTFT_FILTERTARGET_MASK 0x0000ffff
+
+#define EMU_CHAN_VTFT_FILTERTARGET EMU_MKSUBREG(16, 0, EMU_CHAN_VTFT)
+
+#define EMU_CHAN_Z1 0x05
+#define EMU_CHAN_Z2 0x04
+
+#define EMU_CHAN_PSST 0x06
+#define EMU_CHAN_PSST_FXSENDAMOUNT_C_MASK 0xff000000
+
+#define EMU_CHAN_PSST_FXSENDAMOUNT_C EMU_MKSUBREG(8, 24, EMU_CHAN_PSST)
+#define EMU_CHAN_PSST_LOOPSTARTADDR_MASK 0x00ffffff
+
+#define EMU_CHAN_PSST_LOOPSTARTADDR EMU_MKSUBREG(24, 0, EMU_CHAN_PSST)
+
+#define EMU_CHAN_DSL 0x07
+#define EMU_CHAN_DSL_FXSENDAMOUNT_D_MASK 0xff000000
+
+#define EMU_CHAN_DSL_FXSENDAMOUNT_D EMU_MKSUBREG(8, 24, EMU_CHAN_DSL)
+#define EMU_CHAN_DSL_LOOPENDADDR_MASK 0x00ffffff
+
+#define EMU_CHAN_DSL_LOOPENDADDR EMU_MKSUBREG(24, 0, EMU_CHAN_DSL)
+
+#define EMU_CHAN_CCCA 0x08
+
+#define EMU_CHAN_CCCA_RESONANCE 0xf0000000
+#define EMU_CHAN_CCCA_INTERPROMMASK 0x0e000000
+#define EMU_CHAN_CCCA_INTERPROM_0 0x00000000
+#define EMU_CHAN_CCCA_INTERPROM_1 0x02000000
+#define EMU_CHAN_CCCA_INTERPROM_2 0x04000000
+#define EMU_CHAN_CCCA_INTERPROM_3 0x06000000
+#define EMU_CHAN_CCCA_INTERPROM_4 0x08000000
+#define EMU_CHAN_CCCA_INTERPROM_5 0x0a000000
+#define EMU_CHAN_CCCA_INTERPROM_6 0x0c000000
+#define EMU_CHAN_CCCA_INTERPROM_7 0x0e000000
+
+#define EMU_CHAN_CCCA_8BITSELECT 0x01000000
+
+#define EMU_CHAN_CCCA_CURRADDR_MASK 0x00ffffff
+#define EMU_CHAN_CCCA_CURRADDR EMU_MKSUBREG(24, 0, EMU_CHAN_CCCA)
+
+#define EMU_CHAN_CCR 0x09
+#define EMU_CHAN_CCR_CACHEINVALIDSIZE_MASK 0xfe000000
+
+#define EMU_CHAN_CCR_CACHEINVALIDSIZE EMU_MKSUBREG(7, 25, EMU_CHAN_CCR)
+#define EMU_CHAN_CCR_CACHELOOPFLAG 0x01000000
+
+#define EMU_CHAN_CCR_INTERLEAVEDSAMPLES 0x00800000
+
+#define EMU_CHAN_CCR_WORDSIZEDSAMPLES 0x00400000
+
+#define EMU_CHAN_CCR_READADDRESS_MASK 0x003f0000
+
+#define EMU_CHAN_CCR_READADDRESS EMU_MKSUBREG(6, 16, EMU_CHAN_CCR)
+#define EMU_CHAN_CCR_LOOPINVALSIZE 0x0000fe00
+#define EMU_CHAN_CCR_LOOPFLAG 0x00000100
+
+#define EMU_CHAN_CCR_CACHELOOPADDRHI 0x000000ff
+
+#define EMU_CHAN_CLP 0x0a
+#define EMU_CHAN_CLP_CACHELOOPADDR 0x0000ffff
+
+#define EMU_CHAN_FXRT 0x0b
+#define EMU_CHAN_FXRT_CHANNELA 0x000f0000
+#define EMU_CHAN_FXRT_CHANNELB 0x00f00000
+#define EMU_CHAN_FXRT_CHANNELC 0x0f000000
+#define EMU_CHAN_FXRT_CHANNELD 0xf0000000
+
+#define EMU_CHAN_MAPA 0x0c
+#define EMU_CHAN_MAPB 0x0d
+
+#define EMU_CHAN_MAP_PTE_MASK 0xffffe000
+#define EMU_CHAN_MAP_PTI_MASK 0x00001fff
+
+
+#define EMU_CHAN_ENVVOL 0x10
+#define EMU_CHAN_ENVVOL_MASK 0x0000ffff
+
+
+#define EMU_CHAN_ATKHLDV 0x11
+#define EMU_CHAN_ATKHLDV_PHASE0 0x00008000
+#define EMU_CHAN_ATKHLDV_HOLDTIME_MASK 0x00007f00
+#define EMU_CHAN_ATKHLDV_ATTACKTIME_MASK 0x0000007f
+
+
+#define EMU_CHAN_DCYSUSV 0x12
+
+#define EMU_CHAN_DCYSUSV_PHASE1_MASK 0x00008000
+
+#define EMU_CHAN_DCYSUSV_SUSTAINLEVEL_MASK 0x00007f00
+#define EMU_CHAN_DCYSUSV_CHANNELENABLE_MASK 0x00000080
+#define EMU_CHAN_DCYSUSV_DECAYTIME_MASK 0x0000007f
+
+
+#define EMU_CHAN_LFOVAL1 0x13
+#define EMU_CHAN_LFOVAL_MASK 0x0000ffff
+
+
+#define EMU_CHAN_ENVVAL 0x14
+#define EMU_CHAN_ENVVAL_MASK 0x0000ffff
+
+
+#define EMU_CHAN_ATKHLDM 0x15
+#define EMU_CHAN_ATKHLDM_PHASE0 0x00008000
+#define EMU_CHAN_ATKHLDM_HOLDTIME 0x00007f00
+#define EMU_CHAN_ATKHLDM_ATTACKTIME 0x0000007f
+
+
+#define EMU_CHAN_DCYSUSM 0x16
+#define EMU_CHAN_DCYSUSM_PHASE1_MASK 0x00008000
+#define EMU_CHAN_DCYSUSM_SUSTAINLEVEL_MASK 0x00007f00
+#define EMU_CHAN_DCYSUSM_DECAYTIME_MASK 0x0000007f
+
+
+#define EMU_CHAN_LFOVAL2 0x17
+#define EMU_CHAN_LFOVAL2_MASK 0x0000ffff
+
+
+#define EMU_CHAN_IP 0x18
+#define EMU_CHAN_IP_MASK 0x0000ffff
+#define EMU_CHAN_IP_UNITY 0x0000e000
+
+#define EMU_CHAN_IFATN 0x19
+#define EMU_CHAN_IFATN_FILTERCUTOFF_MASK 0x0000ff00
+#define EMU_CHAN_IFATN_FILTERCUTOFF EMU_MKSUBREG(8, 8, EMU_CHAN_IFATN)
+
+#define EMU_CHAN_IFATN_ATTENUATION_MASK 0x000000ff
+#define EMU_CHAN_IFATN_ATTENUATION EMU_MKSUBREG(8, 0, EMU_CHAN_IFATN)
+
+
+#define EMU_CHAN_PEFE 0x1a
+#define EMU_CHAN_PEFE_PITCHAMOUNT_MASK 0x0000ff00
+#define EMU_CHAN_PEFE_PITCHAMOUNT EMU_MKSUBREG(8, 8, EMU_CHAN_PEFE)
+#define EMU_CHAN_PEFE_FILTERAMOUNT_MASK 0x000000ff
+#define EMU_CHAN_PEFE_FILTERAMOUNT EMU_MKSUBREG(8, 0, EMU_CHAN_PEFE)
+
+
+#define EMU_CHAN_FMMOD 0x1b
+#define EMU_CHAN_FMMOD_MODVIBRATO 0x0000ff00
+#define EMU_CHAN_FMMOD_MOFILTER 0x000000ff
+
+
+#define EMU_CHAN_TREMFRQ 0x1c
+#define EMU_CHAN_TREMFRQ_DEPTH 0x0000ff00
+
+
+#define EMU_CHAN_FM2FRQ2 0x1d
+#define EMU_CHAN_FM2FRQ2_DEPTH 0x0000ff00
+#define EMU_CHAN_FM2FRQ2_FREQUENCY 0x000000ff
+
+
+#define EMU_CHAN_TEMPENV 0x1e
+#define EMU_CHAN_TEMPENV_MASK 0x0000ffff
+
+#define EMU_CHAN_CD0 0x20
+#define EMU_CHAN_CD1 0x21
+#define EMU_CHAN_CD2 0x22
+#define EMU_CHAN_CD3 0x23
+#define EMU_CHAN_CD4 0x24
+#define EMU_CHAN_CD5 0x25
+#define EMU_CHAN_CD6 0x26
+#define EMU_CHAN_CD7 0x27
+#define EMU_CHAN_CD8 0x28
+#define EMU_CHAN_CD9 0x29
+#define EMU_CHAN_CDA 0x2a
+#define EMU_CHAN_CDB 0x2b
+#define EMU_CHAN_CDC 0x2c
+#define EMU_CHAN_CDD 0x2d
+#define EMU_CHAN_CDE 0x2e
+#define EMU_CHAN_CDF 0x2f
+
+/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
+
+#define EMU_PTB 0x40
+#define EMU_PTB_MASK 0xfffff000
+
+#define EMU_TCB 0x41
+#define EMU_TCB_MASK 0xfffff000
+
+#define EMU_ADCCR 0x42
+#define EMU_ADCCR_RCHANENABLE 0x00000010
+#define EMU_ADCCR_LCHANENABLE 0x00000008
+
+#define EMU_ADCCR_SAMPLERATE_MASK 0x00000007
+#define EMU_ADCCR_SAMPLERATE_48 0x00000000
+#define EMU_ADCCR_SAMPLERATE_44 0x00000001
+#define EMU_ADCCR_SAMPLERATE_32 0x00000002
+#define EMU_ADCCR_SAMPLERATE_24 0x00000003
+#define EMU_ADCCR_SAMPLERATE_22 0x00000004
+#define EMU_ADCCR_SAMPLERATE_16 0x00000005
+#define EMU_ADCCR_SAMPLERATE_11 0x00000006
+#define EMU_ADCCR_SAMPLERATE_8 0x00000007
+
+#define EMU_FXWC 0x43
+#define EMU_TCBS 0x44
+#define EMU_TCBS_MASK 0x00000007
+#define EMU_TCBS_BUFFSIZE_16K 0x00000000
+#define EMU_TCBS_BUFFSIZE_32K 0x00000001
+#define EMU_TCBS_BUFFSIZE_64K 0x00000002
+#define EMU_TCBS_BUFFSIZE_128K 0x00000003
+#define EMU_TCBS_BUFFSIZE_256K 0x00000004
+#define EMU_TCBS_BUFFSIZE_512K 0x00000005
+#define EMU_TCBS_BUFFSIZE_1024K 0x00000006
+#define EMU_TCBS_BUFFSIZE_2048K 0x00000007
+
+
+#define EMU_MICBA 0x45
+#define EMU_ADCBA 0x46
+#define EMU_FXBA 0x47
+
+#define EMU_RECBA_MASK 0xfffff000
+
+#define EMU_MICBS 0x49
+#define EMU_ADCBS 0x4a
+#define EMU_FXBS 0x4b
+#define EMU_RECBS_BUFSIZE_NONE 0x00000000
+#define EMU_RECBS_BUFSIZE_384 0x00000001
+#define EMU_RECBS_BUFSIZE_448 0x00000002
+#define EMU_RECBS_BUFSIZE_512 0x00000003
+#define EMU_RECBS_BUFSIZE_640 0x00000004
+#define EMU_RECBS_BUFSIZE_768 0x00000005
+#define EMU_RECBS_BUFSIZE_896 0x00000006
+#define EMU_RECBS_BUFSIZE_1024 0x00000007
+#define EMU_RECBS_BUFSIZE_1280 0x00000008
+#define EMU_RECBS_BUFSIZE_1536 0x00000009
+#define EMU_RECBS_BUFSIZE_1792 0x0000000a
+#define EMU_RECBS_BUFSIZE_2048 0x0000000b
+#define EMU_RECBS_BUFSIZE_2560 0x0000000c
+#define EMU_RECBS_BUFSIZE_3072 0x0000000d
+#define EMU_RECBS_BUFSIZE_3584 0x0000000e
+#define EMU_RECBS_BUFSIZE_4096 0x0000000f
+#define EMU_RECBS_BUFSIZE_5120 0x00000010
+#define EMU_RECBS_BUFSIZE_6144 0x00000011
+#define EMU_RECBS_BUFSIZE_7168 0x00000012
+#define EMU_RECBS_BUFSIZE_8192 0x00000013
+#define EMU_RECBS_BUFSIZE_10240 0x00000014
+#define EMU_RECBS_BUFSIZE_12288 0x00000015
+#define EMU_RECBS_BUFSIZE_14366 0x00000016
+#define EMU_RECBS_BUFSIZE_16384 0x00000017
+#define EMU_RECBS_BUFSIZE_20480 0x00000018
+#define EMU_RECBS_BUFSIZE_24576 0x00000019
+#define EMU_RECBS_BUFSIZE_28672 0x0000001a
+#define EMU_RECBS_BUFSIZE_32768 0x0000001b
+#define EMU_RECBS_BUFSIZE_40960 0x0000001c
+#define EMU_RECBS_BUFSIZE_49152 0x0000001d
+#define EMU_RECBS_BUFSIZE_57344 0x0000001e
+#define EMU_RECBS_BUFSIZE_65536 0x0000001f
+
+
+#define EMU_CDCS 0x50
+#define EMU_GPSCS 0x51
+
+
+#define EMU_DBG 0x52
+#define EMU_DBG_ZC 0x80000000
+#define EMU_DBG_SATURATION_OCCURED 0x02000000
+#define EMU_DBG_SATURATION_ADDR 0x01ff0000
+#define EMU_DBG_SINGLE_STEP 0x00008000
+#define EMU_DBG_STEP 0x00004000
+#define EMU_DBG_CONDITION_CODE 0x00003e00
+#define EMU_DBG_SINGLE_STEP_ADDR 0x000001ff
+#define EMU_REG53 0x53
+
+
+#define EMU_SPCS0 0x54
+#define EMU_SPCS1 0x55
+#define EMU_SPCS2 0x56
+
+#define EMU_SPCS_CLKACCYMASK 0x30000000
+#define EMU_SPCS_CLKACCY_1000PPM 0x00000000
+#define EMU_SPCS_CLKACCY_50PPM 0x10000000
+#define EMU_SPCS_CLKACCY_VARIABLE 0x20000000
+
+#define EMU_SPCS_SAMPLERATEMASK 0x0f000000
+#define EMU_SPCS_SAMPLERATE_44 0x00000000
+#define EMU_SPCS_SAMPLERATE_48 0x02000000
+#define EMU_SPCS_SAMPLERATE_32 0x03000000
+
+#define EMU_SPCS_CHANNELNUMMASK 0x00f00000
+
+#define EMU_SPCS_CHANNELNUM_UNSPEC 0x00000000
+#define EMU_SPCS_CHANNELNUM_LEFT 0x00100000
+#define EMU_SPCS_CHANNELNUM_RIGHT 0x00200000
+#define EMU_SPCS_SOURCENUMMASK 0x000f0000
+#define EMU_SPCS_SOURCENUM_UNSPEC 0x00000000
+#define EMU_SPCS_GENERATIONSTATUS 0x00008000
+
+#define EMU_SPCS_CATEGORYCODEMASK 0x00007f00
+
+#define EMU_SPCS_MODEMASK 0x000000c0
+#define EMU_SPCS_EMPHASISMASK 0x00000038
+#define EMU_SPCS_EMPHASIS_NONE 0x00000000
+#define EMU_SPCS_EMPHASIS_50_15 0x00000008
+#define EMU_SPCS_COPYRIGHT 0x00000004
+
+#define EMU_SPCS_NOTAUDIODATA 0x00000002
+
+#define EMU_SPCS_PROFESSIONAL 0x00000001
+
+
+#define EMU_CLIEL 0x58
+#define EMU_CLIEH 0x59
+#define EMU_CLIPL 0x5a
+#define EMU_CLIPH 0x5b
+#define EMU_SOLEL 0x5c
+#define EMU_SOLEH 0x5d
+
+#define EMU_SPBYPASS 0x5e
+#define EMU_SPBYPASS_ENABLE 0x00000001
+
+
+#define EMU_CDSRCS 0x60
+#define EMU_GPSRCS 0x61
+#define EMU_ZVSRCS 0x62
+#define EMU_SRCS_SPDIFLOCKED 0x02000000
+#define EMU_SRCS_RATELOCKED 0x01000000
+#define EMU_SRCS_ESTSAMPLERATE 0x0007ffff
+
+
+#define EMU_MICIDX 0x63
+#define EMU_ADCIDX 0x64
+#define EMU_FXIDX 0x65
+#define EMU_RECIDX_MASK 0x0000ffff
+#define EMU_RECIDX(idxreg) (0x10000000|(idxreg))
+/*
+#define EMU_MICIDX_IDX 0x10000063
+#define EMU_ADCIDX_IDX 0x10000064
+#define EMU_FXIDX_IDX 0x10000065
+*/
+
+#define EMU_FXGPREGBASE 0x100
+#define EMU_TANKMEMDATAREGBASE 0x200
+#define EMU_TANKMEMDATAREG_MASK 0x000fffff
+
+#define EMU_TANKMEMADDRREGBASE 0x300
+#define EMU_TANKMEMADDRREG_ADDR_MASK 0x000fffff
+#define EMU_TANKMEMADDRREG_CLEAR 0x00800000
+#define EMU_TANKMEMADDRREG_ALIGN 0x00400000
+#define EMU_TANKMEMADDRREG_WRITE 0x00200000
+#define EMU_TANKMEMADDRREG_READ 0x00100000
+
+
+#define EMU_MICROCODEBASE 0x400
+
+#define EMU_DSP_LOWORD_OPX_MASK 0x000ffc00
+#define EMU_DSP_LOWORD_OPY_MASK 0x000003ff
+#define EMU_DSP_HIWORD_OPCODE_MASK 0x00f00000
+#define EMU_DSP_HIWORD_RESULT_MASK 0x000ffc00
+#define EMU_DSP_HIWORD_OPA_MASK 0x000003ff
+
+
+#define EMU_DSP_OP_MACS 0x0
+#define EMU_DSP_OP_MACS1 0x1
+#define EMU_DSP_OP_MACW 0x2
+#define EMU_DSP_OP_MACW1 0x3
+#define EMU_DSP_OP_MACINTS 0x4
+#define EMU_DSP_OP_MACINTW 0x5
+#define EMU_DSP_OP_ACC3 0x6
+#define EMU_DSP_OP_MACMV 0x7
+#define EMU_DSP_OP_ANDXOR 0x8
+#define EMU_DSP_OP_TSTNEG 0x9
+#define EMU_DSP_OP_LIMIT 0xA
+#define EMU_DSP_OP_LIMIT1 0xB
+#define EMU_DSP_OP_LOG 0xC
+#define EMU_DSP_OP_EXP 0xD
+#define EMU_DSP_OP_INTERP 0xE
+#define EMU_DSP_OP_SKIP 0xF
+
+
+#define EMU_DSP_FX(num) (num)
+
+
+#define EMU_DSP_IOL(base, num) (base + (num << 1))
+#define EMU_DSP_IOR(base, num) (EMU_DSP_IOL(base, num) + 1)
+
+#define EMU_DSP_INL_BASE 0x010
+#define EMU_DSP_INL(num) (EMU_DSP_IOL(EMU_DSP_INL_BASE, num))
+#define EMU_DSP_INR(num) (EMU_DSP_IOR(EMU_DSP_INL_BASE, num))
+#define EMU_DSP_IN_AC97 0
+#define EMU_DSP_IN_CDSPDIF 1
+#define EMU_DSP_IN_ZOOM 2
+#define EMU_DSP_IN_TOSOPT 3
+#define EMU_DSP_IN_LVDLM1 4
+#define EMU_DSP_IN_LVDCOS 5
+#define EMU_DSP_IN_LVDLM2 6
+#define EMU_DSP_IN_UNKOWN 7
+
+#define EMU_DSP_OUTL_BASE 0x020
+#define EMU_DSP_OUTL(num) (EMU_DSP_IOL(EMU_DSP_OUTL_BASE, num))
+#define EMU_DSP_OUTR(num) (EMU_DSP_IOR(EMU_DSP_OUTL_BASE, num))
+#define EMU_DSP_OUT_AC97 0
+#define EMU_DSP_OUT_TOSOPT 1
+#define EMU_DSP_OUT_UNKNOWN 2
+#define EMU_DSP_OUT_HEAD 3
+#define EMU_DSP_OUT_RCHAN 4
+#define EMU_DSP_OUT_ADC 5
+#define EMU_DSP_OUTL_MIC 6
+
+
+#define EMU_DSP_CST_BASE 0x40
+#define EMU_DSP_CST(num) (EMU_DSP_CST_BASE + num)
+/*
+00 = 0x00000000
+01 = 0x00000001
+02 = 0x00000002
+03 = 0x00000003
+04 = 0x00000004
+05 = 0x00000008
+06 = 0x00000010
+07 = 0x00000020
+08 = 0x00000100
+09 = 0x00010000
+0A = 0x00080000
+0B = 0x10000000
+0C = 0x20000000
+0D = 0x40000000
+0E = 0x80000000
+0F = 0x7FFFFFFF
+10 = 0xFFFFFFFF
+11 = 0xFFFFFFFE
+12 = 0xC0000000
+13 = 0x4F1BBCDC
+14 = 0x5A7EF9DB
+15 = 0x00100000
+*/
+
+#define EMU_DSP_HWR_ACC 0x056
+#define EMU_DSP_HWR_CCR 0x057
+#define EMU_DSP_HWR_CCR_S 0x04
+#define EMU_DSP_HWR_CCR_Z 0x03
+#define EMU_DSP_HWR_CCR_M 0x02
+#define EMU_DSP_HWR_CCR_N 0x01
+#define EMU_DSP_HWR_CCR_B 0x00
+#define EMU_DSP_HWR_NOISE0 0x058
+#define EMU_DSP_HWR_NOISE1 0x059
+#define EMU_DSP_HWR_INTR 0x05A
+#define EMU_DSP_HWR_DBAC 0x05B
+
+#define EMU_DSP_GPR(num) (EMU_FXGPREGBASE + num)
+
+#endif /* _DEV_PCI_EMUXKIREG_H_ */
diff --git a/sys/dev/pci/emuxkivar.h b/sys/dev/pci/emuxkivar.h
new file mode 100644
index 00000000000..d592248b819
--- /dev/null
+++ b/sys/dev/pci/emuxkivar.h
@@ -0,0 +1,259 @@
+/* $NetBSD: emuxkivar.h,v 1.1 2001/10/17 18:39:41 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Yannick Montulet.
+ *
+ * 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.
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+#ifndef _DEV_PCI_EMU10K1VAR_H_
+#define _DEV_PCI_EMU10K1VAR_H_
+
+#define EMU_PCI_CBIO 0x10
+
+/*
+ * dma memory managment
+ */
+
+struct dmamem {
+ bus_dma_tag_t dmat;
+ bus_size_t size;
+ bus_size_t align;
+ bus_size_t bound;
+ bus_dma_segment_t *segs;
+ int nsegs;
+ int rsegs;
+ caddr_t kaddr;
+ bus_dmamap_t map;
+};
+
+#define KERNADDR(ptr) ((void *)((ptr)->kaddr))
+#define DMASEGADDR(ptr, segno) ((ptr)->segs[segno].ds_addr)
+#define DMAADDR(ptr) DMASEGADDR(ptr, 0)
+#define DMASIZE(ptr) ((ptr)->size)
+
+/*
+ * Emu10k1 hardware limits
+ */
+
+#define EMU_PTESIZE 4096
+#define EMU_MAXPTE ((EMU_CHAN_PSST_LOOPSTARTADDR_MASK + 1) / \
+ EMU_PTESIZE)
+#define EMU_NUMCHAN 64
+#define EMU_NUMRECSRCS 3
+
+#define EMU_DMA_ALIGN 4096
+#define EMU_DMAMEM_NSEG 1
+
+/*
+ * Emu10k1 memory managment
+ */
+
+struct emuxki_mem {
+ LIST_ENTRY(emuxki_mem) next;
+ struct dmamem *dmamem;
+ u_int16_t ptbidx;
+#define EMU_RMEM 0xFFFF /* recording memory */
+};
+
+/*
+ * Emu10k1 play channel params
+ */
+
+struct emuxki_chanparms_fxsend {
+ struct {
+ u_int8_t level, dest;
+ } a, b, c, d;
+};
+
+struct emuxki_chanparms_pitch {
+ u_int16_t intial; /* 4 bits of octave, 12 bits of fractional
+ * octave */
+ u_int16_t current;/* 0x4000 == unity pitch shift */
+ u_int16_t target; /* 0x4000 == unity pitch shift */
+ u_int8_t envelope_amount; /* Signed 2's complement, +/-
+ * one octave peak extremes */
+};
+
+struct emuxki_chanparms_envelope {
+ u_int16_t current_state; /* 0x8000-n == 666*n usec delay */
+ u_int8_t hold_time; /* 127-n == n*(volume ? 88.2 :
+ * 42)msec */
+ u_int8_t attack_time; /* 0 = infinite, 1 = (volume ? 11 :
+ * 10.9) msec, 0x7f = 5.5msec */
+ u_int8_t sustain_level; /* 127 = full, 0 = off, 0.75dB
+ * increments */
+ u_int8_t decay_time; /* 0 = 43.7msec, 1 = 21.8msec, 0x7f =
+ * 22msec */
+};
+
+struct emuxki_chanparms_volume {
+ u_int16_t current, target;
+ struct emuxki_chanparms_envelope envelope;
+};
+
+struct emuxki_chanparms_filter {
+ u_int16_t initial_cutoff_frequency;
+ /*
+ * 6 most significant bits are semitones, 2 least significant bits
+ * are fractions
+ */
+ u_int16_t current_cutoff_frequency;
+ u_int16_t target_cutoff_frequency;
+ u_int8_t lowpass_resonance_height;
+ u_int8_t interpolation_ROM; /* 1 = full band, 7 = low
+ * pass */
+ u_int8_t envelope_amount; /* Signed 2's complement, +/-
+ * six octaves peak extremes */
+ u_int8_t LFO_modulation_depth; /* Signed 2's complement, +/-
+ * three octave extremes */
+};
+
+struct emuxki_chanparms_loop {
+ u_int32_t start; /* index in the PTB (in samples) */
+ u_int32_t end; /* index in the PTB (in samples) */
+};
+
+struct emuxki_chanparms_modulation {
+ struct emuxki_chanparms_envelope envelope;
+ u_int16_t LFO_state; /* 0x8000-n = 666*n usec delay */
+};
+
+struct emuxki_chanparms_vibrato_LFO {
+ u_int16_t state; /* 0x8000-n == 666*n usec delay */
+ u_int8_t modulation_depth; /* Signed 2's complement, +/-
+ * one octave extremes */
+ u_int8_t vibrato_depth; /* Signed 2's complement, +/- one
+ * octave extremes */
+ u_int8_t frequency; /* 0.039Hz steps, maximum of 9.85 Hz */
+};
+
+struct emuxki_channel {
+ u_int8_t num; /* voice number */
+ struct emuxki_voice *voice;
+ struct emuxki_chanparms_fxsend fxsend;
+ struct emuxki_chanparms_pitch pitch;
+ u_int16_t initial_attenuation; /* 0.375dB steps */
+ struct emuxki_chanparms_volume volume;
+ struct emuxki_chanparms_filter filter;
+ struct emuxki_chanparms_loop loop;
+ struct emuxki_chanparms_modulation modulation;
+ struct emuxki_chanparms_vibrato_LFO vibrato_LFO;
+ u_int8_t tremolo_depth;
+};
+
+/*
+ * Voices, streams
+ */
+
+typedef enum {
+ EMU_RECSRC_MIC = 0,
+ EMU_RECSRC_ADC,
+ EMU_RECSRC_FX,
+ EMU_RECSRC_NOTSET
+} emuxki_recsrc_t;
+
+struct emuxki_voice {
+ struct emuxki_softc *sc; /* our softc */
+
+ u_int8_t use;
+#define EMU_VOICE_USE_PLAY (1 << 0)
+ u_int8_t state;
+#define EMU_VOICE_STATE_STARTED (1 << 0)
+ u_int8_t stereo;
+#define EMU_VOICE_STEREO_NOTSET 0xFF
+ u_int8_t b16;
+ u_int32_t sample_rate;
+ union {
+ struct emuxki_channel *chan[2];
+ emuxki_recsrc_t source;
+ } dataloc;
+ struct emuxki_mem *buffer;
+ u_int16_t blksize;/* in samples */
+ u_int16_t trigblk;/* blk on which to trigger inth */
+ u_int16_t blkmod; /* Modulo value to wrap trigblk */
+ u_int16_t timerate;
+ void (*inth) (void *);
+ void *inthparam;
+ LIST_ENTRY(emuxki_voice) next;
+};
+
+#if 0 /* Not yet */
+/*
+ * I intend this to be able to manage things like AC-3
+ */
+struct emuxki_stream {
+ struct emu10k1 *emu;
+ u_int8_t nmono;
+ u_int8_t nstereo;
+ struct emuxki_voice *mono;
+ struct emuxki_voice *stereo;
+ LIST_ENTRY(emuxki_stream) next;
+};
+#endif /* Not yet */
+
+struct emuxki_softc {
+ struct device sc_dev;
+
+ /* Autoconfig parameters */
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ bus_addr_t sc_iob;
+ bus_size_t sc_ios;
+ pci_chipset_tag_t sc_pc; /* PCI tag */
+ bus_dma_tag_t sc_dmat;
+ void *sc_ih; /* interrupt handler */
+
+ /* EMU10k1 device structures */
+ LIST_HEAD(, emuxki_mem) mem;
+
+ struct dmamem *ptb;
+ struct dmamem *silentpage;
+
+ struct emuxki_channel *channel[EMU_NUMCHAN];
+ struct emuxki_voice *recsrc[EMU_NUMRECSRCS];
+
+ LIST_HEAD(, emuxki_voice) voices;
+ /* LIST_HEAD(, emuxki_stream) streams; */
+
+ u_int8_t timerstate;
+#define EMU_TIMER_STATE_ENABLED 1
+
+ struct ac97_host_if hostif;
+ struct ac97_codec_if *codecif;
+ struct device *sc_audev;
+
+ struct emuxki_voice *pvoice, *rvoice;
+};
+
+#endif /* !_DEV_PCI_EMU10K1VAR_H_ */
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 79ba5fb1281..b74b58ee4da 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.116 2001/10/05 18:57:28 nate Exp $
+# $OpenBSD: files.pci,v 1.117 2001/10/24 03:21:06 mickey Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -93,6 +93,11 @@ device auich: audio, auconv, mulaw, ac97
attach auich at pci
file dev/pci/auich.c auich
+# Creative Labs EMU10k1 (SBLive! series and PCI512)
+device emu: audio, auconv, mulaw, ac97
+attach emu at pci
+file dev/pci/emuxki.c emu
+
# CS4280 CrystalClear Audio
device clcs: audio, auconv, mulaw, ac97
attach clcs at pci