/* $NetBSD: emuxki.c,v 1.54 2008/09/06 03:00:32 gmcgarry 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. * * 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 disappear 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 * - Multiple voices play (problem with /dev/audio architecture) * - Multiple sources recording (Pb with audio(4)) * - Independent modification of each channel's parameters (via mixer ?) * - DSP FX patches (to make fx like chipmunk) */ #include __KERNEL_RCSID(0, "$NetBSD: emuxki.c,v 1.54 2008/09/06 03:00:32 gmcgarry Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* autoconf goo */ static int emuxki_match(struct device *, struct cfdata *, void *); static void emuxki_attach(struct device *, struct device *, void *); static int emuxki_detach(struct device *, int); /* DMA mem mgmt */ static struct dmamem *dmamem_alloc(bus_dma_tag_t, size_t, bus_size_t, int, struct malloc_type *, int); static void dmamem_free(struct dmamem *, struct malloc_type *); /* Emu10k1 init & shutdown */ static int emuxki_init(struct emuxki_softc *); static void emuxki_shutdown(struct emuxki_softc *); /* Emu10k1 mem mgmt */ static void *emuxki_pmem_alloc(struct emuxki_softc *, size_t, struct malloc_type *,int); static void *emuxki_rmem_alloc(struct emuxki_softc *, size_t, struct malloc_type *,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(struct emuxki_softc *, uint8_t); static void emuxki_voice_delete(struct emuxki_voice *); static int emuxki_voice_set_audioparms(struct emuxki_voice *, uint8_t, uint8_t, uint32_t); /* emuxki_voice_set_fxparms will come later, it'll need channel distinction */ static int emuxki_voice_set_bufparms(struct emuxki_voice *, void *, uint32_t, uint16_t); static void emuxki_voice_commit_parms(struct emuxki_voice *); static int emuxki_voice_adc_rate(struct emuxki_voice *); static uint32_t emuxki_voice_curaddr(struct emuxki_voice *); static void emuxki_voice_start(struct emuxki_voice *, void (*) (void *), void *); static void emuxki_voice_halt(struct emuxki_voice *); /* * Emu10k1 stream mgmt : not done yet */ #if 0 static struct emuxki_stream *emuxki_stream_new(struct emu10k1 *); static void emuxki_stream_delete(struct emuxki_stream *); static int emuxki_stream_set_audio_params(struct emuxki_stream *, uint8_t, uint8_t, uint8_t, uint16_t); static void emuxki_stream_start(struct emuxki_stream *); static void emuxki_stream_halt(struct emuxki_stream *); #endif /* audio interface callbacks */ static int emuxki_open(void *, int); static void emuxki_close(void *); static int emuxki_query_encoding(void *, struct audio_encoding *); static int emuxki_set_params(void *, int, int, audio_params_t *, audio_params_t *, stream_filter_list_t *, stream_filter_list_t *); static int emuxki_round_blocksize(void *, int, int, const audio_params_t *); static size_t emuxki_round_buffersize(void *, int, size_t); static int emuxki_trigger_output(void *, void *, void *, int, void (*)(void *), void *, const audio_params_t *); static int emuxki_trigger_input(void *, void *, void *, int, void (*) (void *), void *, const audio_params_t *); static int emuxki_halt_output(void *); static int emuxki_halt_input(void *); static int emuxki_getdev(void *, struct audio_device *); static int emuxki_set_port(void *, mixer_ctrl_t *); static int emuxki_get_port(void *, mixer_ctrl_t *); static int emuxki_query_devinfo(void *, mixer_devinfo_t *); static void *emuxki_allocm(void *, int, size_t, struct malloc_type *, int); static void emuxki_freem(void *, void *, struct malloc_type *); static paddr_t emuxki_mappage(void *, void *, off_t, int); static int emuxki_get_props(void *); /* Interrupt handler */ static int emuxki_intr(void *); /* Emu10k1 AC97 interface callbacks */ static int emuxki_ac97_attach(void *, struct ac97_codec_if *); static int emuxki_ac97_read(void *, uint8_t, uint16_t *); static int emuxki_ac97_write(void *, uint8_t, uint16_t); static int emuxki_ac97_reset(void *); static enum ac97_host_flags emuxki_ac97_flags(void *); /* * Autoconfig goo. */ CFATTACH_DECL(emuxki, sizeof(struct emuxki_softc), emuxki_match, emuxki_attach, emuxki_detach, NULL); static const struct audio_hw_if emuxki_hw_if = { emuxki_open, emuxki_close, NULL, /* drain */ emuxki_query_encoding, emuxki_set_params, emuxki_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 */ NULL, /* powerstate */ }; #if 0 static const int emuxki_recsrc_intrmasks[EMU_NUMRECSRCS] = { EMU_INTE_MICBUFENABLE, EMU_INTE_ADCBUFENABLE, EMU_INTE_EFXBUFENABLE }; #endif static const uint32_t emuxki_recsrc_bufaddrreg[EMU_NUMRECSRCS] = { EMU_MICBA, EMU_ADCBA, EMU_FXBA }; static const uint32_t emuxki_recsrc_szreg[EMU_NUMRECSRCS] = { EMU_MICBS, EMU_ADCBS, EMU_FXBS }; static const int emuxki_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 }; #define EMUXKI_NFORMATS 4 static const struct audio_format emuxki_formats[EMUXKI_NFORMATS] = { {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16, 2, AUFMT_STEREO, 0, {4000, 48000}}, {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16, 1, AUFMT_MONAURAL, 0, {4000, 48000}}, {NULL, AUMODE_PLAY, AUDIO_ENCODING_ULINEAR_LE, 8, 8, 2, AUFMT_STEREO, 0, {4000, 48000}}, {NULL, AUMODE_PLAY, AUDIO_ENCODING_ULINEAR_LE, 8, 8, 1, AUFMT_MONAURAL, 0, {4000, 48000}}, }; /* * DMA memory mgmt */ static void dmamem_delete(struct dmamem *mem, struct malloc_type *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, struct malloc_type *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, struct malloc_type *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; if (sc->sc_type & EMUXKI_AUDIGY2) { bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_HCFG, EMU_HCFG_AUDIOENABLE | EMU_HCFG_AC3ENABLE_CDSPDIF | EMU_HCFG_AC3ENABLE_GPSPDIF | EMU_HCFG_AUTOMUTE); } else if (sc->sc_type & EMUXKI_AUDIGY) { bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_HCFG, EMU_HCFG_AUDIOENABLE | EMU_HCFG_AUTOMUTE); } else { bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_HCFG, EMU_HCFG_AUDIOENABLE | EMU_HCFG_JOYENABLE | 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); if (sc->sc_type & EMUXKI_AUDIGY2) { bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_A_IOCFG, EMU_A_IOCFG_GPOUT0 | bus_space_read_4(sc->sc_iot, sc->sc_ioh, EMU_A_IOCFG)); } /* 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 = emuxki_ac97_flags; return ac97_attach(&sc->hostif, &sc->sc_dev); } static int emuxki_match(struct device *parent, struct cfdata *match, void *aux) { struct pci_attach_args *pa; pa = aux; if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_CREATIVELABS) return 0; switch (PCI_PRODUCT(pa->pa_id)) { case PCI_PRODUCT_CREATIVELABS_SBLIVE: case PCI_PRODUCT_CREATIVELABS_SBLIVE2: case PCI_PRODUCT_CREATIVELABS_AUDIGY: return 1; default: return 0; } } static void emuxki_attach(struct device *parent, struct device *self, void *aux) { struct emuxki_softc *sc; struct pci_attach_args *pa; char devinfo[256]; pci_intr_handle_t ih; const char *intrstr; sc = (struct emuxki_softc *) self; pa = aux; aprint_naive(": Audio controller\n"); 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))) { aprint_error(": can't map iospace\n"); return; } pci_devinfo(pa->pa_id, pa->pa_class, 1, devinfo, sizeof(devinfo)); aprint_normal(": %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)) { aprint_error_dev(&sc->sc_dev, "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) { aprint_error_dev(&sc->sc_dev, "couldn't establish interrupt"); if (intrstr != NULL) aprint_normal(" at %s", intrstr); aprint_normal("\n"); bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); return; } aprint_normal_dev(&sc->sc_dev, "interrupting at %s\n", intrstr); /* XXX it's unknown whether APS is made from Audigy as well */ if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_CREATIVELABS_AUDIGY) { sc->sc_type = EMUXKI_AUDIGY; if (PCI_REVISION(pa->pa_class) == 0x04) { sc->sc_type |= EMUXKI_AUDIGY2; strlcpy(sc->sc_audv.name, "Audigy2", sizeof sc->sc_audv.name); } else { strlcpy(sc->sc_audv.name, "Audigy", sizeof sc->sc_audv.name); } } else if (pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG) == EMU_SUBSYS_APS) { sc->sc_type = EMUXKI_APS; strlcpy(sc->sc_audv.name, "E-mu APS", sizeof sc->sc_audv.name); } else { sc->sc_type = EMUXKI_SBLIVE; strlcpy(sc->sc_audv.name, "SB Live!", sizeof sc->sc_audv.name); } snprintf(sc->sc_audv.version, sizeof sc->sc_audv.version, "0x%02x", PCI_REVISION(pa->pa_class)); strlcpy(sc->sc_audv.config, "emuxki", sizeof sc->sc_audv.config); if (emuxki_scinit(sc) || emuxki_ac97_init(sc) || (sc->sc_audev = audio_attach_mi(&emuxki_hw_if, sc, self)) == NULL) { emuxki_pci_shutdown(sc); return; } #if 0 sc->rsourcectl.dev = sc->codecif->vtbl->get_portnum_by_name(sc->codec_if, AudioCrecord, AudioNsource, NULL); sc->rsourcectl.cp = AUDIO_MIXER_ENUM; #endif } static int emuxki_detach(struct device *self, int flags) { struct emuxki_softc *sc; sc = (struct emuxki_softc *)self; if (sc->sc_audev != NULL) /* Test in case audio didn't attach */ 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 uint32_t emuxki_rate_to_pitch(uint32_t rate) { static const uint32_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 uint8_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 (((uint32_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 uint32_t emuxki_read(struct emuxki_softc *sc, uint16_t chano, uint32_t reg) { uint32_t ptr, mask; uint8_t size, offset; int s; mask = 0xffffffff; offset = 0; ptr = ((((u_int32_t) reg) << 16) & (sc->sc_type & EMUXKI_AUDIGY ? EMU_A_PTR_ADDR_MASK : 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, uint16_t chano, uint32_t reg, uint32_t data) { uint32_t ptr, mask; uint8_t size, offset; int s; ptr = ((((u_int32_t) reg) << 16) & (sc->sc_type & EMUXKI_AUDIGY ? EMU_A_PTR_ADDR_MASK : 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, uint32_t pc, uint32_t data) { emuxki_write(sc, 0, (sc->sc_type & EMUXKI_AUDIGY ? EMU_A_MICROCODEBASE : EMU_MICROCODEBASE) + pc, data); } static void emuxki_dsp_addop(struct emuxki_softc *sc, uint16_t *pc, uint8_t op, uint16_t r, uint16_t a, uint16_t x, uint16_t y) { if (sc->sc_type & EMUXKI_AUDIGY) { emuxki_write_micro(sc, *pc << 1, ((x << 12) & EMU_A_DSP_LOWORD_OPX_MASK) | (y & EMU_A_DSP_LOWORD_OPY_MASK)); emuxki_write_micro(sc, (*pc << 1) + 1, ((op << 24) & EMU_A_DSP_HIWORD_OPCODE_MASK) | ((r << 12) & EMU_A_DSP_HIWORD_RESULT_MASK) | (a & EMU_A_DSP_HIWORD_OPA_MASK)); } else { 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) { uint16_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; if (sc->sc_type & EMUXKI_AUDIGY) { /* AC97 Out (l/r) = AC97 In (l/r) + FX[0/1] * 4 */ emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_MACINTS, EMU_A_DSP_OUTL(EMU_A_DSP_OUT_A_FRONT), EMU_A_DSP_CST(0), EMU_DSP_FX(0), EMU_A_DSP_CST(4)); emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_MACINTS, EMU_A_DSP_OUTR(EMU_A_DSP_OUT_A_FRONT), EMU_A_DSP_CST(0), EMU_DSP_FX(1), EMU_A_DSP_CST(4)); /* Rear channel OUT (l/r) = FX[2/3] * 4 */ #if 0 emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_MACINTS, EMU_A_DSP_OUTL(EMU_A_DSP_OUT_A_REAR), EMU_A_DSP_OUTL(EMU_A_DSP_OUT_A_FRONT), EMU_DSP_FX(0), EMU_A_DSP_CST(4)); emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_MACINTS, EMU_A_DSP_OUTR(EMU_A_DSP_OUT_A_REAR), EMU_A_DSP_OUTR(EMU_A_DSP_OUT_A_FRONT), EMU_DSP_FX(1), EMU_A_DSP_CST(4)); #endif /* ADC recording (l/r) = AC97 In (l/r) */ emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_ACC3, EMU_A_DSP_OUTL(EMU_A_DSP_OUT_ADC), EMU_A_DSP_INL(EMU_DSP_IN_AC97), EMU_A_DSP_CST(0), EMU_A_DSP_CST(0)); emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_ACC3, EMU_A_DSP_OUTR(EMU_A_DSP_OUT_ADC), EMU_A_DSP_INR(EMU_DSP_IN_AC97), EMU_A_DSP_CST(0), EMU_A_DSP_CST(0)); /* zero out the rest of the microcode */ while (pc < 512) emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_ACC3, EMU_A_DSP_CST(0), EMU_A_DSP_CST(0), EMU_A_DSP_CST(0), EMU_A_DSP_CST(0)); emuxki_write(sc, 0, EMU_A_DBG, 0); /* Is it really necessary ? */ } else { /* 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_A_FRONT), 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_A_FRONT), 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_AD_REAR), EMU_DSP_OUTL(EMU_DSP_OUT_A_FRONT), EMU_DSP_FX(0), EMU_DSP_CST(4)); emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_MACINTS, EMU_DSP_OUTR(EMU_DSP_OUT_AD_REAR), EMU_DSP_OUTR(EMU_DSP_OUT_A_FRONT), EMU_DSP_FX(1), EMU_DSP_CST(4)); #endif /* ADC recording (l/r) = AC97 In (l/r) */ emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_ACC3, EMU_DSP_OUTL(EMU_DSP_OUT_ADC), EMU_DSP_INL(EMU_DSP_IN_AC97), EMU_DSP_CST(0), EMU_DSP_CST(0)); emuxki_dsp_addop(sc, &pc, EMU_DSP_OP_ACC3, EMU_DSP_OUTR(EMU_DSP_OUT_ADC), EMU_DSP_INR(EMU_DSP_IN_AC97), EMU_DSP_CST(0), EMU_DSP_CST(0)); /* 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) { uint16_t i; uint32_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); if(sc->sc_type & EMUXKI_AUDIGY) { emuxki_write(sc, 0, EMU_SPBYPASS, EMU_SPBYPASS_24_BITS); emuxki_write(sc, 0, EMU_AC97SLOT, EMU_AC97SLOT_CENTER | EMU_AC97SLOT_LFE); } /* 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); if(sc->sc_type & EMUXKI_AUDIGY2) { emuxki_write(sc, 0, EMU_A2_SPDIF_SAMPLERATE, EMU_A2_SPDIF_UNKNOWN); bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_A2_PTR, EMU_A2_SRCSEL); bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_A2_DATA, EMU_A2_SRCSEL_ENABLE_SPDIF | EMU_A2_SRCSEL_ENABLE_SRCMULTI); bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_A2_PTR, EMU_A2_SRCMULTI); bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_A2_DATA, EMU_A2_SRCMULTI_ENABLE_INPUT); } /* 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] = htole32(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) { uint32_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); if(sc->sc_type & EMUXKI_AUDIGY) { emuxki_write(sc, 0, EMU_A_FXWC1, 0); emuxki_write(sc, 0, EMU_A_FXWC2, 0); } else { 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 management */ static struct emuxki_mem * emuxki_mem_new(struct emuxki_softc *sc, int ptbidx, size_t size, struct malloc_type *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, struct malloc_type *type) { dmamem_free(mem->dmamem, type); free(mem, type); } static void * emuxki_pmem_alloc(struct emuxki_softc *sc, size_t size, struct malloc_type *type, int flags) { int i, j, s; size_t numblocks; struct emuxki_mem *mem; uint32_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 ((le32toh(ptb[i]) & EMU_CHAN_MAP_PTE_MASK) == silentpage) { /* We look for a free PTE */ s = splaudio(); for (j = 0; j < numblocks; j++) if ((le32toh(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] = htole32((((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, struct malloc_type *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 management 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 = /* for audigy */ chan->fxsend.e.level = chan->fxsend.f.level = chan->fxsend.g.level = chan->fxsend.h.level = chan->voice->sc->sc_type & EMUXKI_AUDIGY ? 0xc0 : 0xff; /* not max */ chan->fxsend.a.dest = 0x0; chan->fxsend.b.dest = 0x1; chan->fxsend.c.dest = 0x2; chan->fxsend.d.dest = 0x3; /* for audigy */ chan->fxsend.e.dest = 0x4; chan->fxsend.f.dest = 0x5; chan->fxsend.g.dest = 0x6; chan->fxsend.h.dest = 0x7; chan->pitch.initial = 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; /* for audigy */ chan->fxsend.e.level = fxsend->e.level; chan->fxsend.f.level = fxsend->f.level; chan->fxsend.g.level = fxsend->g.level; chan->fxsend.h.level = fxsend->h.level; chan->fxsend.e.dest = fxsend->e.dest; chan->fxsend.f.dest = fxsend->f.dest; chan->fxsend.g.dest = fxsend->g.dest; chan->fxsend.h.dest = fxsend->h.dest; } static void emuxki_channel_set_srate(struct emuxki_channel *chan, uint32_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.initial = (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, uint32_t start, uint32_t end) { chan->loop.start = start & EMU_CHAN_PSST_LOOPSTARTADDR_MASK; chan->loop.end = end & EMU_CHAN_DSL_LOOPENDADDR_MASK; } static void emuxki_channel_commit_fx(struct emuxki_channel *chan) { struct emuxki_softc *sc; u_int8_t chano; sc = chan->voice->sc; chano = chan->num; if(sc->sc_type & EMUXKI_AUDIGY) { emuxki_write(sc, chano, EMU_A_CHAN_FXRT1, (chan->fxsend.d.dest << 24) | (chan->fxsend.c.dest << 16) | (chan->fxsend.b.dest << 8) | (chan->fxsend.a.dest)); emuxki_write(sc, chano, EMU_A_CHAN_FXRT2, (chan->fxsend.h.dest << 24) | (chan->fxsend.g.dest << 16) | (chan->fxsend.f.dest << 8) | (chan->fxsend.e.dest)); emuxki_write(sc, chano, EMU_A_CHAN_SENDAMOUNTS, (chan->fxsend.e.level << 24) | (chan->fxsend.f.level << 16) | (chan->fxsend.g.level << 8) | (chan->fxsend.h.level)); } else { 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); } static void emuxki_channel_commit_parms(struct emuxki_channel *chan) { struct emuxki_voice *voice; struct emuxki_softc *sc; uint32_t start, mapval; uint8_t chano; int s; voice = chan->voice; sc = voice->sc; chano = chan->num; 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_channel_commit_fx(chan); 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; struct emuxki_softc *sc; u_int8_t cache_sample, cache_invalid_size, chano; u_int32_t sample; int s; voice = chan->voice; sc = voice->sc; chano = chan->num; 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.initial); splx(s); } static void emuxki_channel_stop(struct emuxki_channel *chan) { struct emuxki_softc *sc; int s; u_int8_t chano; sc = chan->voice->sc; chano = chan->num; 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 management * emuxki_voice_dataloc : use(play or rec) independent 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; int s; uint8_t i, stereo; channel = voice->sc->channel; stereo = voice->stereo; for (i = 0; i < EMU_NUMCHAN - stereo; 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 */ static int emuxki_recsrc_reserve(struct emuxki_voice *voice, emuxki_recsrc_t source) { if (source >= EMU_NUMRECSRCS) { #ifdef EMUXKI_DEBUG printf("Tried to reserve invalid source: %d\n", source); #endif return EINVAL; } if (voice->sc->recsrc[source] == voice) return 0; /* XXX */ if (voice->sc->recsrc[source] != NULL) return EBUSY; voice->sc->recsrc[source] = voice; return 0; } /* 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 { if ((error = emuxki_recsrc_reserve(voice, voice->dataloc.source))) return error; } 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, uint8_t use) { struct emuxki_voice *voice; int s; s = splaudio(); voice = sc->lvoice; sc->lvoice = NULL; splx(s); if (!voice) { if (!(voice = malloc(sizeof(*voice), M_DEVBUF, M_WAITOK))) return NULL; } else if (voice->use != use) emuxki_voice_dataloc_destroy(voice); else goto skip_initialize; voice->sc = sc; 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[1] = 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; voice->use = use; skip_initialize: s = splaudio(); LIST_INSERT_HEAD((&sc->voices), voice, next); splx(s); return voice; } static void emuxki_voice_delete(struct emuxki_voice *voice) { struct emuxki_softc *sc; struct emuxki_voice *lvoice; int s; sc = voice->sc; if (voice->state & EMU_VOICE_STATE_STARTED) emuxki_voice_halt(voice); s = splaudio(); LIST_REMOVE(voice, next); lvoice = sc->lvoice; sc->lvoice = voice; splx(s); if (lvoice) { emuxki_voice_dataloc_destroy(lvoice); free(lvoice, M_DEVBUF); } } static int emuxki_voice_set_stereo(struct emuxki_voice *voice, uint8_t stereo) { int error; emuxki_recsrc_t source; struct emuxki_chanparms_fxsend fxsend; source = 0; /* XXX: gcc */ if (! (voice->use & EMU_VOICE_USE_PLAY)) source = voice->dataloc.source; emuxki_voice_dataloc_destroy(voice); if (! (voice->use & EMU_VOICE_USE_PLAY)) voice->dataloc.source = source; 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; /* for audigy */ fxsend.e.dest = 0x4; fxsend.f.dest = 0x5; fxsend.g.dest = 0x6; fxsend.h.dest = 0x7; if (voice->stereo) { fxsend.a.level = fxsend.c.level = 0xc0; fxsend.b.level = fxsend.d.level = 0x00; fxsend.e.level = fxsend.g.level = 0xc0; fxsend.f.level = fxsend.h.level = 0x00; emuxki_channel_set_fxsend(voice->dataloc.chan[0], &fxsend); fxsend.a.level = fxsend.c.level = 0x00; fxsend.b.level = fxsend.d.level = 0xc0; fxsend.e.level = fxsend.g.level = 0x00; fxsend.f.level = fxsend.h.level = 0xc0; 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, uint32_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 { if ((srate < 8000) || (srate > 48000)) return EINVAL; voice->sample_rate = srate; if (emuxki_voice_adc_rate(voice) < 0) { voice->sample_rate = 0; return EINVAL; } } return 0; } static int emuxki_voice_set_audioparms(struct emuxki_voice *voice, uint8_t stereo, uint8_t b16, uint32_t srate) { int error; 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 error = 0; if (voice->stereo != stereo) { if ((error = emuxki_voice_set_stereo(voice, stereo))) return error; } voice->b16 = b16; if (voice->sample_rate != srate) error = emuxki_voice_set_srate(voice, srate); return error; } /* voice audio parms (see just before) must be set prior to this */ static int emuxki_voice_set_bufparms(struct emuxki_voice *voice, void *ptr, uint32_t bufsize, uint16_t blksize) { struct emuxki_mem *mem; struct emuxki_channel **chan; uint32_t start, end; uint8_t sample_size; int idx; int error; 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->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) { voice->blksize = blksize / sample_size; 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 = (uint32_t) 48000 * voice->blksize / voice->sample_rate; if (voice->timerate < 5) error = EINVAL; } else { voice->blksize = blksize; for(idx = sizeof(emuxki_recbuf_sz) / sizeof(emuxki_recbuf_sz[0]); --idx >= 0;) if (emuxki_recbuf_sz[idx] == bufsize) break; if (idx < 0) { #ifdef EMUXKI_DEBUG printf("Invalid bufsize: %d\n", bufsize); #endif return EINVAL; } emuxki_write(voice->sc, 0, emuxki_recsrc_szreg[voice->dataloc.source], idx); emuxki_write(voice->sc, 0, emuxki_recsrc_bufaddrreg[voice->dataloc.source], DMAADDR(mem->dmamem)); /* Use timer to emulate DMA completion interrupt */ voice->timerate = (u_int32_t) 48000 * blksize / (voice->sample_rate * sample_size); if (voice->timerate < 5) { #ifdef EMUXKI_DEBUG printf("Invalid timerate: %d, blksize %d\n", voice->timerate, blksize); #endif error = EINVAL; } } 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 uint32_t emuxki_voice_curaddr(struct emuxki_voice *voice) { int idxreg; int rv; /* XXX different semantics in these cases */ if (voice->use & EMU_VOICE_USE_PLAY) { /* returns number of samples (an l/r pair counts 1) */ rv = emuxki_read(voice->sc, voice->dataloc.chan[0]->num, EMU_CHAN_CCCA_CURRADDR) - voice->dataloc.chan[0]->loop.start; } else { idxreg = 0; /* returns number of bytes */ switch (voice->dataloc.source) { case EMU_RECSRC_MIC: idxreg = (voice->sc->sc_type & EMUXKI_AUDIGY) ? EMU_A_MICIDX : EMU_MICIDX; break; case EMU_RECSRC_ADC: idxreg = (voice->sc->sc_type & EMUXKI_AUDIGY) ? EMU_A_ADCIDX : EMU_ADCIDX; break; case EMU_RECSRC_FX: idxreg = EMU_FXIDX; break; default: #ifdef EMUXKI_DEBUG printf("emu: bad recording source!\n"); #endif break; } rv = emuxki_read(voice->sc, 0, EMU_RECIDX(idxreg) & EMU_RECIDX_MASK); } return rv; } static void emuxki_resched_timer(struct emuxki_softc *sc) { struct emuxki_voice *voice; uint16_t timerate; uint8_t active; int s; timerate = 1024; active = 0; s = splaudio(); LIST_FOREACH(voice, &sc->voices, next) { if ((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_2(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 int emuxki_voice_adc_rate(struct emuxki_voice *voice) { switch(voice->sample_rate) { case 48000: return EMU_ADCCR_SAMPLERATE_48; break; case 44100: return EMU_ADCCR_SAMPLERATE_44; break; case 32000: return EMU_ADCCR_SAMPLERATE_32; break; case 24000: return EMU_ADCCR_SAMPLERATE_24; break; case 22050: return EMU_ADCCR_SAMPLERATE_22; break; case 16000: return EMU_ADCCR_SAMPLERATE_16; break; case 12000: if(voice->sc->sc_type & EMUXKI_AUDIGY) return EMU_A_ADCCR_SAMPLERATE_12; else { #ifdef EMUXKI_DEBUG printf("recording sample_rate not supported : %u\n", voice->sample_rate); #endif return -1; } break; case 11000: if(voice->sc->sc_type & EMUXKI_AUDIGY) return EMU_A_ADCCR_SAMPLERATE_11; else return EMU_ADCCR_SAMPLERATE_11; break; case 8000: if(voice->sc->sc_type & EMUXKI_AUDIGY) return EMU_A_ADCCR_SAMPLERATE_8; else return EMU_ADCCR_SAMPLERATE_8; break; default: #ifdef EMUXKI_DEBUG printf("recording sample_rate not supported : %u\n", voice->sample_rate); #endif return -1; } return -1; /* shouldn't get here */ } static void emuxki_voice_start(struct emuxki_voice *voice, void (*inth) (void *), void *inthparam) { uint32_t val; 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]); } else { voice->trigblk = 1; switch (voice->dataloc.source) { case EMU_RECSRC_ADC: /* XXX need to program DSP to output L+R * XXX in monaural case? */ if (voice->sc->sc_type & EMUXKI_AUDIGY) { val = EMU_A_ADCCR_LCHANENABLE; if (voice->stereo) val |= EMU_A_ADCCR_RCHANENABLE; } else { val = EMU_ADCCR_LCHANENABLE; if (voice->stereo) val |= EMU_ADCCR_RCHANENABLE; } val |= emuxki_voice_adc_rate(voice); emuxki_write(voice->sc, 0, EMU_ADCCR, 0); emuxki_write(voice->sc, 0, EMU_ADCCR, val); break; case EMU_RECSRC_MIC: case EMU_RECSRC_FX: printf("unimplemented\n"); break; case EMU_RECSRC_NOTSET: default: printf("Bad dataloc.source %d\n", voice->dataloc.source); break; } #if 0 switch (voice->dataloc.source) { case EMU_RECSRC_ADC: case EMU_RECSRC_FX: case EMU_RECSRC_MIC: /* DMA completion interrupt is useless; use timer */ int s; s = splaudio(); val = emu_rd(sc, INTE, 4); val |= emuxki_recsrc_intrmasks[voice->dataloc.source]; emu_wr(sc, INTE, val, 4); splx(s); break; default: break; } #endif } voice->state |= EMU_VOICE_STATE_STARTED; 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]); } else { switch (voice->dataloc.source) { case EMU_RECSRC_ADC: emuxki_write(voice->sc, 0, EMU_ADCCR, 0); break; case EMU_RECSRC_FX: case EMU_RECSRC_MIC: printf("unimplemented\n"); break; default: case EMU_RECSRC_NOTSET: printf("Bad dataloc.source %d\n", voice->dataloc.source); break; } switch (voice->dataloc.source) { case EMU_RECSRC_ADC: case EMU_RECSRC_FX: case EMU_RECSRC_MIC: /* This should reset buffer pointer */ emuxki_write(voice->sc, 0, emuxki_recsrc_szreg[voice->dataloc.source], EMU_RECBS_BUFSIZE_NONE); #if 0 int s; s = splaudio(); val = emu_rd(sc, INTE, 4); val &= ~emuxki_recsrc_intrmasks[voice->dataloc.source]; emu_wr(sc, INTE, val, 4); splx(s); #endif break; default: break; } } voice->state &= ~EMU_VOICE_STATE_STARTED; emuxki_resched_timer(voice->sc); } /* * The interrupt handler */ static int emuxki_intr(void *arg) { struct emuxki_softc *sc; struct emuxki_voice *voice; uint32_t ipr, curblk; int claim; sc = arg; claim = 0; 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->state & EMU_VOICE_STATE_STARTED) == 0) continue; curblk = emuxki_voice_curaddr(voice) / voice->blksize; #if 0 if (curblk == voice->trigblk) { voice->inth(voice->inthparam); voice->trigblk++; voice->trigblk %= voice->blkmod; } #else while ((curblk >= voice->trigblk && curblk < (voice->trigblk + voice->blkmod / 2)) || ((int)voice->trigblk - (int)curblk) > (voice->blkmod / 2 + 1)) { voice->inth(voice->inthparam); voice->trigblk++; voice->trigblk %= voice->blkmod; } #endif } } /* Got interrupt */ bus_space_write_4(sc->sc_iot, sc->sc_ioh, EMU_IPR, ipr); claim = 1; } return claim; } /* * Audio Architecture callbacks */ static int emuxki_open(void *addr, int flags) { struct emuxki_softc *sc; sc = addr; #ifdef EMUXKI_DEBUG printf("%s: emuxki_open called\n", device_xname(&sc->sc_dev)); #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 addressed very soon */ if (flags & AUOPEN_READ) { sc->rvoice = emuxki_voice_new(sc, 0 /* EMU_VOICE_USE_RECORD */); if (sc->rvoice == NULL) return EBUSY; /* XXX Hardcode RECSRC_ADC for now */ sc->rvoice->dataloc.source = EMU_RECSRC_ADC; } if (flags & AUOPEN_WRITE) { sc->pvoice = emuxki_voice_new(sc, EMU_VOICE_USE_PLAY); if (sc->pvoice == NULL) { if (sc->rvoice) { emuxki_voice_delete(sc->rvoice); sc->rvoice = NULL; } return EBUSY; } } return 0; } static void emuxki_close(void *addr) { struct emuxki_softc *sc; sc = addr; #ifdef EMUXKI_DEBUG printf("%s: emu10K1_close called\n", device_xname(&sc->sc_dev)); #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; sc = addr; printf("%s: emuxki_query_encoding called\n", device_xname(&sc->sc_dev)); #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, const audio_params_t *p, stream_filter_list_t *fil) { int mode, i; mode = (voice->use & EMU_VOICE_USE_PLAY) ? AUMODE_PLAY : AUMODE_RECORD; i = auconv_set_converter(emuxki_formats, EMUXKI_NFORMATS, mode, p, FALSE, fil); if (i < 0) return EINVAL; if (fil->req_size > 0) p = &fil->filters[0].param; return emuxki_voice_set_audioparms (voice, p->channels == 2, p->precision == 16, p->sample_rate); } static int emuxki_set_params(void *addr, int setmode, int usemode, audio_params_t *play, audio_params_t *rec, stream_filter_list_t *pfil, stream_filter_list_t *rfil) { struct emuxki_softc *sc; struct audio_params *p; struct emuxki_voice *v; stream_filter_list_t *fil; int mode, error; sc = addr; for (mode = AUMODE_RECORD; mode != -1; mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { if ((usemode & setmode & mode) == 0) continue; if (mode == AUMODE_PLAY) { p = play; fil = pfil; v = sc->pvoice; } else { p = rec; fil = rfil; v = sc->rvoice; } if (v == NULL) { continue; } /* No multiple voice support for now */ if ((error = emuxki_set_vparms(v, p, fil))) return error; } return 0; } static int emuxki_halt_output(void *addr) { struct emuxki_softc *sc; 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; sc = addr; #ifdef EMUXKI_DEBUG printf("%s: emuxki_halt_input called\n", device_xname(&sc->sc_dev)); #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) { struct emuxki_softc *sc; sc = addr; *dev = sc->sc_audv; return 0; } static int emuxki_set_port(void *addr, mixer_ctrl_t *mctl) { struct emuxki_softc *sc; 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; 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; sc = addr; return sc->codecif->vtbl->query_devinfo(sc->codecif, minfo); } static void * emuxki_allocm(void *addr, int direction, size_t size, struct malloc_type *type, int flags) { if (direction == AUMODE_PLAY) return emuxki_pmem_alloc(addr, size, type, flags); else return emuxki_rmem_alloc(addr, size, type, flags); } static void emuxki_freem(void *addr, void *ptr, struct malloc_type *type) { struct emuxki_softc *sc; struct emuxki_mem *mem; uint32_t *ptb, silentpage; size_t numblocks; int i, s; sc = addr; 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] = htole32(silentpage | (mem->ptbidx + i)); } LIST_REMOVE(mem, next); splx(s); emuxki_mem_delete(mem, type); break; } } /* blocksize should be a divisor of allowable buffersize */ /* XXX probably this could be done better */ static int emuxki_round_blocksize(void *addr, int blksize, int mode, const audio_params_t* param) { #if 0 struct emuxki_softc *sc; struct audio_softc *au; #endif int bufsize; #if 0 sc = addr; if (sc == NULL) return blksize; au = (void *)sc->sc_audev; if (au == NULL) return blksize; bufsize = emuxki_round_buffersize(sc, AUMODE_RECORD, au->sc_rr.bufsize); #else bufsize = 65536; #endif while (bufsize > blksize) bufsize /= 2; return bufsize; } static size_t emuxki_round_buffersize(void *addr, int direction, size_t bsize) { 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 = sizeof(emuxki_recbuf_sz) / sizeof(emuxki_recbuf_sz[0]); --idx >= 0; ) { if (bsize >= emuxki_recbuf_sz[idx]) { bsize = emuxki_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; struct emuxki_mem *mem; sc = addr; 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, const audio_params_t *params) { struct emuxki_softc *sc; /* No multiple voice support for now */ struct emuxki_voice *voice; int error; sc = addr; voice = sc->pvoice; if (voice == NULL) return ENXIO; if ((error = emuxki_voice_set_audioparms(voice, params->channels == 2, params->precision == 16, params->sample_rate))) return error; if ((error = emuxki_voice_set_bufparms(voice, start, (char *)end - (char *)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, const audio_params_t *params) { struct emuxki_softc *sc; /* No multiple voice support for now */ struct emuxki_voice *voice; int error; sc = addr; voice = sc->rvoice; if (voice == NULL) return ENXIO; if ((error = emuxki_voice_set_audioparms(voice, params->channels == 2, params->precision == 16, params->sample_rate))) return error; if ((error = emuxki_voice_set_bufparms(voice, start, (char *)end - (char *)start, blksize))) return error; 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; sc = arg; sc->codecif = codecif; return 0; } static int emuxki_ac97_read(void *arg, uint8_t reg, uint16_t *val) { struct emuxki_softc *sc; int s; sc = arg; 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, uint8_t reg, uint16_t val) { struct emuxki_softc *sc; int s; sc = arg; 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 int emuxki_ac97_reset(void *arg) { return 0; } enum ac97_host_flags emuxki_ac97_flags(void *arg) { return AC97_HOST_SWAPPED_CHANNELS; }