diff --git a/sys/dev/pci/emuxki.c b/sys/dev/pci/emuxki.c new file mode 100644 index 000000000000..044363b4f128 --- /dev/null +++ b/sys/dev/pci/emuxki.c @@ -0,0 +1,2032 @@ +/* $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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* autconf goo */ +static int emuxki_match __P((struct device *, struct cfdata *, void *)); +static void emuxki_attach __P((struct device *, struct device *, void *)); +static int emuxki_detach __P((struct device *, int)); + +/* dma mem mgmt */ +static struct dmamem *dmamem_alloc __P((bus_dma_tag_t, size_t, bus_size_t, + int, int, int)); +static void dmamem_free __P((struct dmamem *, int)); + +/* Emu10k1 init & shutdown */ +static int emuxki_init __P((struct emuxki_softc *)); +static void emuxki_shutdown __P((struct emuxki_softc *)); + +/* Emu10k1 mem mgmt */ +static void *emuxki_pmem_alloc __P((struct emuxki_softc *, size_t,int,int)); +static 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 */ +static struct emuxki_voice *emuxki_voice_new __P((struct emuxki_softc *, + u_int8_t)); +static void emuxki_voice_delete __P((struct emuxki_voice *)); +static 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 */ +static int emuxki_voice_set_bufparms __P((struct emuxki_voice *, + void *, u_int32_t, u_int16_t)); +static void emuxki_voice_commit_parms __P((struct emuxki_voice *)); +static u_int32_t emuxki_voice_curaddr __P((struct emuxki_voice *)); +static void emuxki_voice_start __P((struct emuxki_voice *, + void (*) (void *), void *)); +static void emuxki_voice_halt __P((struct emuxki_voice *)); + +/* + * Emu10k1 stream mgmt : not done yet + */ +#if 0 +static struct emuxki_stream *emuxki_stream_new __P((struct emu10k1 *)); +static void emuxki_stream_delete __P((struct emuxki_stream *)); +static int emuxki_stream_set_audio_params __P((struct emuxki_stream *, u_int8_t, + u_int8_t, u_int8_t, u_int16_t)); +static void emuxki_stream_start __P((struct emuxki_stream *)); +static void emuxki_stream_halt __P((struct emuxki_stream *)); +#endif + +/* audio interface callbacks */ + +static int emuxki_open __P((void *, int)); +static void emuxki_close __P((void *)); + +static int emuxki_query_encoding __P((void *, struct audio_encoding *)); +static int emuxki_set_params __P((void *, int, int, + struct audio_params *, + struct audio_params *)); + +static size_t emuxki_round_buffersize __P((void *, int, size_t)); + +static int emuxki_trigger_output __P((void *, void *, void *, int, + void (*)(void *), void *, + struct audio_params *)); +static int emuxki_trigger_input __P((void *, void *, void *, int, + void (*) (void *), void *, + struct audio_params *)); +static int emuxki_halt_output __P((void *)); +static int emuxki_halt_input __P((void *)); + +static int emuxki_getdev __P((void *, struct audio_device *)); +static int emuxki_set_port __P((void *, mixer_ctrl_t *)); +static int emuxki_get_port __P((void *, mixer_ctrl_t *)); +static int emuxki_query_devinfo __P((void *, mixer_devinfo_t *)); + +static void *emuxki_allocm __P((void *, int, size_t, int, int)); +static void emuxki_freem __P((void *, void *, int)); + +static paddr_t emuxki_mappage __P((void *, void *, off_t, int)); +static int emuxki_get_props __P((void *)); + +/* Interrupt handler */ +static int emuxki_intr __P((void *)); + +/* Emu10k1 AC97 interface callbacks */ +static int emuxki_ac97_attach __P((void *, struct ac97_codec_if *)); +static int emuxki_ac97_read __P((void *, u_int8_t, u_int16_t *)); +static int emuxki_ac97_write __P((void *, u_int8_t, u_int16_t)); +static void emuxki_ac97_reset __P((void *)); + +/* + * Autoconfig goo. + */ +struct cfattach emuxki_ca = { + sizeof(struct emuxki_softc), + emuxki_match, + emuxki_attach, + emuxki_detach, + NULL /* config activate */ +}; + +static 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, + emuxki_allocm, + emuxki_freem, + emuxki_round_buffersize, + emuxki_mappage, + emuxki_get_props, + emuxki_trigger_output, + emuxki_trigger_input, + NULL, /* dev_ioctl */ +}; + +/* + * Dma memory mgmt + */ + +static void +dmamem_delete(struct dmamem *mem, int type) +{ + free(mem->segs, type); + free(mem, type); +} + +static struct dmamem * +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)) { + 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); + 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); + 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); + dmamem_delete(mem, type); + return (NULL); + } + + return (mem); +} + +static void +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); + dmamem_delete(mem, type); +} + + +/* + * Autoconf device callbacks : attach and detach + */ + +static 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); +} + +static 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); +} + +static 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))); +} + +static int +emuxki_match(struct device *parent, struct cfdata *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); +} + +static 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; + char devinfo[256]; + 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))) { + printf(": can't map iospace\n"); + return; + } + pci_devinfo(pa->pa_id, pa->pa_class, 1, devinfo); + printf(": %s\n", devinfo); + + 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); + if (sc->sc_ih == NULL) { + printf(", couldn't establish interrupt", sc->sc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + return; + } + printf("%s: interrupting at %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); +} + +static 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 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 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 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); + 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 ? */ + +static void +emuxki_write_micro(struct emuxki_softc *sc, u_int32_t pc, u_int32_t data) +{ + emuxki_write(sc, 0, EMU_MICROCODEBASE + pc, data); +} + +static 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 */ + +static 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 ? */ +} + +static 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 = 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 = dmamem_alloc(sc->sc_dmat, EMU_PTESIZE, + EMU_DMA_ALIGN, EMU_DMAMEM_NSEG, M_DEVBUF, M_WAITOK))==NULL){ + 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); +} + +static 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 ? */ + + dmamem_free(sc->silentpage, M_DEVBUF); + dmamem_free(sc->ptb, M_DEVBUF); +} + +/* Emu10k1 Memory managment */ + +static 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 = dmamem_alloc(sc->sc_dmat, size, EMU_DMA_ALIGN, + EMU_DMAMEM_NSEG, type, flags)) == NULL) { + free(mem, type); + return (NULL); + } + return (mem); +} + +static void +emuxki_mem_delete(struct emuxki_mem *mem, int type) +{ + dmamem_free(mem->dmamem, type); + free(mem, type); +} + +static 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); +} + +static 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 ? + */ +static 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 */ +static 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 */ +static void +emuxki_channel_delete(struct emuxki_channel *chan) +{ + chan->voice->sc->channel[chan->num] = NULL; + free(chan, M_DEVBUF); +} + +static 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; +} + +static 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 */ +static 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; +} + +static 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); +} + +static 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); +} + +static 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 */ +static 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 */ +static 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 +static 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 */ +static void +emuxki_voice_recsrc_release(struct emuxki_softc *sc, emuxki_recsrc_t source) +{ + sc->recsrc[source] = NULL; +} + +static 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); +} + +static 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; + } + } +} + +static 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); +} + +static 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); +} + +static 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); +} + +static 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); +} + +static 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 */ +static 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); +} + +static 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]); + } +} + +static 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); +} + +static 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); +} + +static 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); +} + +static 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 + */ +static int +emuxki_intr(void *arg) +{ + struct emuxki_softc *sc = arg; + u_int32_t ipr, curblk; + 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; + } + } + } + + /* Got interrupt */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_IPR, ipr); + } + + return (0); +} + + +/* + * Audio Architecture callbacks + */ + +static 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); +} + +static 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; +} + +static 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); +} + +static 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)); +} + +static 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); +} + +static 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); +} + +static 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); +} + +static 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); +} + +static 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); +} + +static 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); +} + +static int +emuxki_query_devinfo(void *addr, mixer_devinfo_t *minfo) +{ + struct emuxki_softc *sc = addr; + + return sc->codecif->vtbl->query_devinfo(sc->codecif, minfo); +} + +static 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); +} + +static 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; + } +} + +static 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); +} + +static paddr_t +emuxki_mappage(void *addr, void *ptr, off_t 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); +} + +static int +emuxki_get_props(void *addr) +{ + return (AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | + AUDIO_PROP_FULLDUPLEX); +} + +static 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); +} + +static 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 + */ + +static int +emuxki_ac97_attach(void *arg, struct ac97_codec_if *codecif) +{ + struct emuxki_softc *sc = arg; + + sc->codecif = codecif; + return (0); +} + +static 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); +} + +static 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); +} + +static 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 000000000000..06da79350db1 --- /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 000000000000..d592248b819b --- /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 963dae47738a..2ec1e7177e8d 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.141 2001/10/13 15:00:23 ichiro Exp $ +# $NetBSD: files.pci,v 1.142 2001/10/17 18:39:41 jdolecek Exp $ # # Config file and device description for machine-independent PCI code. # Included by ports that need it. Requires that the SCSI files be @@ -316,6 +316,11 @@ file dev/pci/opl_yds.c opl_yds attach mpu at yds with mpu_yds file dev/pci/mpu_yds.c mpu_yds +# Creative Labs EMU10k1 (SBLive! series and PCI512) +device emuxki: audio, auconv, mulaw, ac97 +attach emuxki at pci +file dev/pci/emuxki.c emuxki + # SMC EPIC/100 Fast Ethernet on PCI attach epic at pci with epic_pci file dev/pci/if_epic_pci.c epic_pci