diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 4a907aec7f4e..86f75174b02d 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.49 1998/11/04 00:31:05 fvdl Exp $ +# $NetBSD: files.pci,v 1.50 1998/12/10 18:47:18 augustss Exp $ # # Config file and device description for machine-independent PCI code. # Included by ports that need it. Requires that the SCSI files be @@ -133,6 +133,14 @@ device eap: audio, auconv, mulaw attach eap at pci file dev/pci/eap.c eap +# S3 SonicVibes (S3 617) +device sv { }: audio, auconv, mulaw, midibus +attach sv at pci +file dev/pci/sv.c sv + +attach opl at sv with opl_sv +file dev/pci/opl_sv.c opl_sv + # SMC EPIC/100 Fast Ethernet on PCI attach epic at pci with epic_pci file dev/pci/if_epic_pci.c epic_pci diff --git a/sys/dev/pci/opl_sv.c b/sys/dev/pci/opl_sv.c new file mode 100644 index 000000000000..0378bd8e6ba0 --- /dev/null +++ b/sys/dev/pci/opl_sv.c @@ -0,0 +1,100 @@ +/* $NetBSD: opl_sv.c,v 1.1 1998/12/10 18:47:19 augustss Exp $ */ + +/* + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Lennart Augustsson (augustss@netbsd.org). + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +int opl_sv_match __P((struct device *, struct cfdata *, void *)); +void opl_sv_attach __P((struct device *, struct device *, void *)); + +struct cfattach opl_sv_ca = { + sizeof (struct opl_softc), opl_sv_match, opl_sv_attach +}; + +int +opl_sv_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct audio_attach_args *aa = (struct audio_attach_args *)aux; + + if (aa->type != AUDIODEV_TYPE_OPL) + return (0); + return (1); +} + +void +opl_sv_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct sv_softc *ssc = (struct sv_softc *)parent; + struct opl_softc *sc = (struct opl_softc *)self; + + sc->ioh = ssc->sc_oplioh; + sc->iot = ssc->sc_opliot; + sc->offs = 0; + strcpy(sc->syn.name, "SV "); + /*sc->spkrctl = 0; + sc->spkrarg = 0;*/ + + opl_attach(sc); +} diff --git a/sys/dev/pci/sv.c b/sys/dev/pci/sv.c new file mode 100644 index 000000000000..9a6d63c0965d --- /dev/null +++ b/sys/dev/pci/sv.c @@ -0,0 +1,1443 @@ +/* $NetBSD: sv.c,v 1.1 1998/12/10 18:47:19 augustss Exp $ */ +/* $OpenBSD: sv.c,v 1.2 1998/07/13 01:50:15 csapuntz Exp $ */ + +/* + * Copyright (c) 1998 Constantine Paul Sapuntzakis + * All rights reserved + * + * Author: Constantine Paul Sapuntzakis (csapuntz@cvs.openbsd.org) + * + * 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. The author's name or those of the contributors may be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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. + */ + +/* + * S3 SonicVibes driver + * Heavily based on the eap driver by Lennart Augustsson + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef AUDIO_DEBUG +#define DPRINTF(x) if (svdebug) printf x +#define DPRINTFN(n,x) if (svdebug>(n)) printf x +int svdebug = 0; +#else +#define DPRINTF(x) +#define DPRINTFN(n,x) +#endif + +int sv_match __P((struct device *, struct cfdata *, void *)); +void sv_attach __P((struct device *, struct device *, void *)); +int sv_intr __P((void *)); + +struct sv_dma { + bus_dmamap_t map; + caddr_t addr; + bus_dma_segment_t segs[1]; + int nsegs; + size_t size; + struct sv_dma *next; +}; +#define DMAADDR(map) ((map)->segs[0].ds_addr) +#define KERNADDR(map) ((void *)((map)->addr)) + + +struct cfattach sv_ca = { + sizeof(struct sv_softc), sv_match, sv_attach +}; + +struct audio_device sv_device = { + "S3 SonicVibes", + "", + "sv" +}; + +#define ARRAY_SIZE(foo) ((sizeof(foo)) / sizeof(foo[0])) + +int sv_allocmem __P((struct sv_softc *, size_t, size_t, struct sv_dma *)); +int sv_freemem __P((struct sv_softc *, struct sv_dma *)); + +int sv_open __P((void *, int)); +void sv_close __P((void *)); +int sv_query_encoding __P((void *, struct audio_encoding *)); +int sv_set_params __P((void *, int, int, struct audio_params *, struct audio_params *)); +int sv_round_blocksize __P((void *, int)); +int sv_dma_init_output __P((void *, void *, int)); +int sv_dma_init_input __P((void *, void *, int)); +int sv_dma_output __P((void *, void *, int, void (*)(void *), void*)); +int sv_dma_input __P((void *, void *, int, void (*)(void *), void*)); +int sv_halt_in_dma __P((void *)); +int sv_halt_out_dma __P((void *)); +int sv_getdev __P((void *, struct audio_device *)); +int sv_mixer_set_port __P((void *, mixer_ctrl_t *)); +int sv_mixer_get_port __P((void *, mixer_ctrl_t *)); +int sv_query_devinfo __P((void *, mixer_devinfo_t *)); +void *sv_malloc __P((void *, u_long, int, int)); +void sv_free __P((void *, void *, int)); +u_long sv_round __P((void *, u_long)); +int sv_mappage __P((void *, void *, int, int)); +int sv_get_props __P((void *)); + +#ifdef AUDIO_DEBUG +void sv_dumpregs __P((struct sv_softc *sc)); +#endif + +struct audio_hw_if sv_hw_if = { + sv_open, + sv_close, + NULL, + sv_query_encoding, + sv_set_params, + sv_round_blocksize, + NULL, + sv_dma_init_output, + sv_dma_init_input, + sv_dma_output, + sv_dma_input, + sv_halt_out_dma, + sv_halt_in_dma, + NULL, + sv_getdev, + NULL, + sv_mixer_set_port, + sv_mixer_get_port, + sv_query_devinfo, + sv_malloc, + sv_free, + sv_round, + sv_mappage, + sv_get_props, +}; + + +static u_int8_t sv_read __P((struct sv_softc *, u_int8_t)); +static u_int8_t sv_read_indirect __P((struct sv_softc *, u_int8_t)); +static void sv_write __P((struct sv_softc *, u_int8_t, u_int8_t )); +static void sv_write_indirect __P((struct sv_softc *, u_int8_t, u_int8_t )); +static void sv_init_mixer __P((struct sv_softc *)); + +static void sv_defer __P((struct device *self)); + +static void +sv_write (sc, reg, val) + struct sv_softc *sc; + u_int8_t reg, val; + +{ + DPRINTFN(8,("sv_write(0x%x, 0x%x)\n", reg, val)); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, val); +} + +static u_int8_t +sv_read(sc, reg) + struct sv_softc *sc; + u_int8_t reg; + +{ + u_int8_t val; + + val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg); + DPRINTFN(8,("sv_read(0x%x) = 0x%x\n", reg, val)); + return val; +} + +static u_int8_t +sv_read_indirect(sc, reg) + struct sv_softc *sc; + u_int8_t reg; +{ + u_int8_t val; + int s = splaudio(); + + sv_write(sc, SV_CODEC_IADDR, reg & SV_IADDR_MASK); + val = sv_read(sc, SV_CODEC_IDATA); + splx(s); + return (val); +} + +static void +sv_write_indirect(sc, reg, val) + struct sv_softc *sc; + u_int8_t reg, val; +{ + u_int8_t iaddr = reg & SV_IADDR_MASK; + int s = splaudio(); + + if (reg == SV_DMA_DATA_FORMAT) + iaddr |= SV_IADDR_MCE; + + sv_write(sc, SV_CODEC_IADDR, iaddr); + sv_write(sc, SV_CODEC_IDATA, val); + splx(s); +} + +int +sv_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_S3 && + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_S3_SONICVIBES) + return (1); + + return (0); +} + +int pci_alloc_io __P((pci_chipset_tag_t pc, pcitag_t pt, + int pcioffs, + bus_space_tag_t iot, bus_size_t size, + bus_size_t align, bus_size_t bound, int flags, + bus_space_handle_t *ioh)); + +#define PCI_IO_ALLOC_LOW 0xa000 +#define PCI_IO_ALLOC_HIGH 0xb000 +int +pci_alloc_io(pc, pt, pcioffs, iot, size, align, bound, flags, ioh) + pci_chipset_tag_t pc; + pcitag_t pt; + int pcioffs; + bus_space_tag_t iot; + bus_size_t size; + bus_size_t align; + bus_size_t bound; + int flags; + bus_space_handle_t *ioh; +{ + bus_addr_t addr; + int error; + + error = bus_space_alloc(iot, PCI_IO_ALLOC_LOW, PCI_IO_ALLOC_HIGH, + size, align, bound, flags, &addr, ioh); + if (error) + return(error); + + pci_conf_write(pc, pt, pcioffs, addr); + return (0); +} + +/* + * Allocate IO addresses when all other configuration is done. + */ +void +sv_defer(self) + struct device *self; +{ + struct sv_softc *sc = (struct sv_softc *)self; + pci_chipset_tag_t pc = sc->sc_pa.pa_pc; + pcitag_t pt = sc->sc_pa.pa_tag; + pcireg_t dmaio; + + DPRINTF(("sv_defer: %p\n", sc)); + if (pci_alloc_io(pc, pt, SV_DMAA_CONFIG_OFF, + sc->sc_iot, SV_DMAA_SIZE, SV_DMAA_ALIGN, 0, + 0, &sc->sc_dmaa_ioh)) { + printf("sv_attach: cannot allocate DMA A range\n"); + return; + } + dmaio = pci_conf_read(pc, pt, SV_DMAA_CONFIG_OFF); + DPRINTF(("sv_attach: addr a dmaio=0x%lx\n", (u_long)dmaio)); + pci_conf_write(pc, pt, SV_DMAA_CONFIG_OFF, + dmaio | SV_DMA_CHANNEL_ENABLE | SV_DMAA_EXTENDED_ADDR); + + if (pci_alloc_io(pc, pt, SV_DMAC_CONFIG_OFF, + sc->sc_iot, SV_DMAA_SIZE, SV_DMAA_ALIGN, 0, + 0, &sc->sc_dmac_ioh)) { + printf("sv_attach: cannot allocate DMA C range\n"); + return; + } + dmaio = pci_conf_read(pc, pt, SV_DMAC_CONFIG_OFF); + DPRINTF(("sv_attach: addr c dmaio=0x%lx\n", (u_long)dmaio)); + pci_conf_write(pc, pt, SV_DMAC_CONFIG_OFF, + dmaio | SV_DMA_CHANNEL_ENABLE); + + sc->sc_dmaset = 1; +} + +void +sv_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct sv_softc *sc = (struct sv_softc *)self; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pcitag_t pt = pa->pa_tag; + pci_intr_handle_t ih; + pcireg_t csr; + char const *intrstr; + u_int8_t reg; + struct audio_attach_args arg; + + printf ("\n"); + + /* Map I/O registers */ + if (pci_mapreg_map(pa, SV_ENHANCED_PORTBASE_SLOT, + PCI_MAPREG_TYPE_IO, 0, + &sc->sc_iot, &sc->sc_ioh, NULL, NULL)) { + printf("%s: can't map enhanced i/o space\n", + sc->sc_dev.dv_xname); + return; + } + if (pci_mapreg_map(pa, SV_FM_PORTBASE_SLOT, + PCI_MAPREG_TYPE_IO, 0, + &sc->sc_opliot, &sc->sc_oplioh, NULL, NULL)) { + printf("%s: can't map FM i/o space\n", sc->sc_dev.dv_xname); + return; + } + if (pci_mapreg_map(pa, SV_MIDI_PORTBASE_SLOT, + PCI_MAPREG_TYPE_IO, 0, + &sc->sc_midiiot, &sc->sc_midiioh, NULL, NULL)) { + printf("%s: can't map MIDI i/o space\n", sc->sc_dev.dv_xname); + return; + } + DPRINTF(("sv: IO ports: enhanced=0x%x, OPL=0x%x, MIDI=0x%x\n", + (int)sc->sc_ioh, (int)sc->sc_oplioh, (int)sc->sc_midiioh)); + + sc->sc_dmatag = pa->pa_dmat; + + pci_conf_write(pc, pt, SV_DMAA_CONFIG_OFF, 0); + pci_conf_write(pc, pt, SV_DMAC_CONFIG_OFF, 0); + + /* Enable the device. */ + csr = pci_conf_read(pc, pt, PCI_COMMAND_STATUS_REG); + pci_conf_write(pc, pt, PCI_COMMAND_STATUS_REG, + csr | PCI_COMMAND_MASTER_ENABLE); + + sv_write_indirect(sc, SV_ANALOG_POWER_DOWN_CONTROL, 0); + sv_write_indirect(sc, SV_DIGITAL_POWER_DOWN_CONTROL, 0); + + /* initialize codec registers */ + reg = sv_read(sc, SV_CODEC_CONTROL); + reg |= SV_CTL_RESET; + sv_write(sc, SV_CODEC_CONTROL, reg); + delay(50); + + reg = sv_read(sc, SV_CODEC_CONTROL); + reg &= ~SV_CTL_RESET; + reg |= SV_CTL_INTA | SV_CTL_ENHANCED; + + /* This write clears the reset */ + sv_write(sc, SV_CODEC_CONTROL, reg); + delay(50); + + /* This write actually shoves the new values in */ + sv_write(sc, SV_CODEC_CONTROL, reg); + + DPRINTF(("sv_attach: control=0x%x\n", sv_read(sc, SV_CODEC_CONTROL))); + + /* Enable DMA interrupts */ + reg = sv_read(sc, SV_CODEC_INTMASK); + reg &= ~(SV_INTMASK_DMAA | SV_INTMASK_DMAC); + reg |= SV_INTMASK_UD | SV_INTMASK_SINT | SV_INTMASK_MIDI; + sv_write(sc, SV_CODEC_INTMASK, reg); + + sv_read(sc, SV_CODEC_STATUS); + + /* Map and establish the interrupt. */ + if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, + pa->pa_intrline, &ih)) { + printf("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname); + return; + } + intrstr = pci_intr_string(pc, ih); + sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, sv_intr, sc); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt", + sc->sc_dev.dv_xname); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr); + printf("%s: rev %d", sc->sc_dev.dv_xname, + sv_read_indirect(sc, SV_REVISION_LEVEL)); + if (sv_read(sc, SV_CODEC_CONTROL) & SV_CTL_MD1) + printf(", reverb SRAM present"); + if (!(sv_read_indirect(sc, SV_WAVETABLE_SOURCE_SELECT) & SV_WSS_WT0)) + printf(", wavetable ROM present"); + printf("\n"); + + sv_init_mixer(sc); + + audio_attach_mi(&sv_hw_if, sc, &sc->sc_dev); + + arg.type = AUDIODEV_TYPE_OPL; + arg.hwif = 0; + arg.hdl = 0; + (void)config_found(&sc->sc_dev, &arg, audioprint); + + sc->sc_pa = *pa; /* for deferred setup */ + config_defer(self, sv_defer); +} + +#ifdef AUDIO_DEBUG +void +sv_dumpregs(sc) + struct sv_softc *sc; +{ + int idx; + +#if 0 + for (idx = 0; idx < 0x50; idx += 4) + printf ("%02x = %x\n", idx, + pci_conf_read(pa->pa_pc, pa->pa_tag, idx)); +#endif + + for (idx = 0; idx < 6; idx++) + printf ("REG %02x = %02x\n", idx, sv_read(sc, idx)); + + for (idx = 0; idx < 0x32; idx++) + printf ("IREG %02x = %02x\n", idx, sv_read_indirect(sc, idx)); + + for (idx = 0; idx < 0x10; idx++) + printf ("DMA %02x = %02x\n", idx, + bus_space_read_1(sc->sc_iot, sc->sc_dmaa_ioh, idx)); +} +#endif + +int +sv_intr(p) + void *p; +{ + struct sv_softc *sc = p; + u_int8_t intr; + + intr = sv_read(sc, SV_CODEC_STATUS); + DPRINTFN(5,("sv_intr: intr=0x%x\n", intr)); + + if (!(intr & (SV_INTSTATUS_DMAA | SV_INTSTATUS_DMAC))) + return (0); + + if (intr & SV_INTSTATUS_DMAA) { + if (sc->sc_pintr) + sc->sc_pintr(sc->sc_parg); + } + + if (intr & SV_INTSTATUS_DMAC) { + if (sc->sc_rintr) + sc->sc_rintr(sc->sc_rarg); + } + + return (1); +} + +int +sv_allocmem(sc, size, align, p) + struct sv_softc *sc; + size_t size; + size_t align; + struct sv_dma *p; +{ + int error; + + p->size = size; + error = bus_dmamem_alloc(sc->sc_dmatag, p->size, align, 0, + p->segs, ARRAY_SIZE(p->segs), + &p->nsegs, BUS_DMA_NOWAIT); + if (error) + return (error); + + error = bus_dmamem_map(sc->sc_dmatag, p->segs, p->nsegs, p->size, + &p->addr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT); + if (error) + goto free; + + error = bus_dmamap_create(sc->sc_dmatag, p->size, 1, p->size, + 0, BUS_DMA_NOWAIT, &p->map); + if (error) + goto unmap; + + error = bus_dmamap_load(sc->sc_dmatag, p->map, p->addr, p->size, NULL, + BUS_DMA_NOWAIT); + if (error) + goto destroy; + return (0); + +destroy: + bus_dmamap_destroy(sc->sc_dmatag, p->map); +unmap: + bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size); +free: + bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs); + return (error); +} + +int +sv_freemem(sc, p) + struct sv_softc *sc; + struct sv_dma *p; +{ + bus_dmamap_unload(sc->sc_dmatag, p->map); + bus_dmamap_destroy(sc->sc_dmatag, p->map); + bus_dmamem_unmap(sc->sc_dmatag, p->addr, p->size); + bus_dmamem_free(sc->sc_dmatag, p->segs, p->nsegs); + return (0); +} + +int +sv_open(addr, flags) + void *addr; + int flags; +{ + struct sv_softc *sc = addr; + + DPRINTF(("sv_open\n")); + if (!sc->sc_dmaset) + return (ENXIO); + sc->sc_pintr = 0; + sc->sc_rintr = 0; + + return (0); +} + +/* + * Close function is called at splaudio(). + */ +void +sv_close(addr) + void *addr; +{ + struct sv_softc *sc = addr; + + DPRINTF(("sv_close\n")); + sv_halt_in_dma(sc); + sv_halt_out_dma(sc); + + sc->sc_pintr = 0; + sc->sc_rintr = 0; +} + +int +sv_query_encoding(addr, fp) + void *addr; + struct audio_encoding *fp; +{ + switch (fp->index) { + case 0: + strcpy(fp->name, AudioEulinear); + fp->encoding = AUDIO_ENCODING_ULINEAR; + fp->precision = 8; + fp->flags = 0; + return (0); + case 1: + strcpy(fp->name, AudioEmulaw); + fp->encoding = AUDIO_ENCODING_ULAW; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 2: + strcpy(fp->name, AudioEalaw); + fp->encoding = AUDIO_ENCODING_ALAW; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 3: + strcpy(fp->name, AudioEslinear); + fp->encoding = AUDIO_ENCODING_SLINEAR; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 4: + strcpy(fp->name, AudioEslinear_le); + fp->encoding = AUDIO_ENCODING_SLINEAR_LE; + fp->precision = 16; + fp->flags = 0; + return (0); + case 5: + strcpy(fp->name, AudioEulinear_le); + fp->encoding = AUDIO_ENCODING_ULINEAR_LE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 6: + strcpy(fp->name, AudioEslinear_be); + fp->encoding = AUDIO_ENCODING_SLINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + case 7: + strcpy(fp->name, AudioEulinear_be); + fp->encoding = AUDIO_ENCODING_ULINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return (0); + default: + return (EINVAL); + } +} + +int +sv_set_params(addr, setmode, usemode, p, r) + void *addr; + int setmode, usemode; + struct audio_params *p, *r; +{ + struct sv_softc *sc = addr; + void (*pswcode) __P((void *, u_char *buf, int cnt)); + void (*rswcode) __P((void *, u_char *buf, int cnt)); + u_int32_t mode, val; + u_int8_t reg; + + DPRINTF(("sv_set_params\n")); + pswcode = rswcode = 0; + switch (p->encoding) { + case AUDIO_ENCODING_SLINEAR_BE: + if (p->precision == 16) + rswcode = pswcode = swap_bytes; + else + pswcode = rswcode = change_sign8; + break; + case AUDIO_ENCODING_SLINEAR_LE: + if (p->precision != 16) + pswcode = rswcode = change_sign8; + break; + case AUDIO_ENCODING_ULINEAR_BE: + if (p->precision == 16) { + pswcode = swap_bytes_change_sign16; + rswcode = change_sign16_swap_bytes; + } + break; + case AUDIO_ENCODING_ULINEAR_LE: + if (p->precision == 16) + pswcode = rswcode = change_sign16; + break; + case AUDIO_ENCODING_ULAW: + pswcode = mulaw_to_ulinear8; + rswcode = ulinear8_to_mulaw; + break; + case AUDIO_ENCODING_ALAW: + pswcode = alaw_to_ulinear8; + rswcode = ulinear8_to_alaw; + break; + default: + return (EINVAL); + } + + if (p->precision == 16) + mode = SV_DMAA_FORMAT16 | SV_DMAC_FORMAT16; + else + mode = 0; + if (p->channels == 2) + mode |= SV_DMAA_STEREO | SV_DMAC_STEREO; + else if (p->channels != 1) + return (EINVAL); + if (p->sample_rate < 2000 || p->sample_rate > 48000) + return (EINVAL); + + p->sw_code = pswcode; + r->sw_code = rswcode; + + /* Set the encoding */ + reg = sv_read_indirect(sc, SV_DMA_DATA_FORMAT); + reg &= ~(SV_DMAA_FORMAT16 | SV_DMAC_FORMAT16 | SV_DMAA_STEREO | + SV_DMAC_STEREO); + reg |= mode; + sv_write_indirect(sc, SV_DMA_DATA_FORMAT, reg); + + val = p->sample_rate * 65536 / 48000; + + sv_write_indirect(sc, SV_PCM_SAMPLE_RATE_0, (val & 0xff)); + sv_write_indirect(sc, SV_PCM_SAMPLE_RATE_1, (val >> 8)); + +#define F_REF 24576000 + +#define ABS(x) (((x) < 0) ? (-x) : (x)) + + if (setmode & AUMODE_RECORD) { + /* The ADC reference frequency (f_out) is 512 * sample rate */ + + /* f_out is dervied from the 24.576MHZ crystal by three values: + M & N & R. The equation is as follows: + + f_out = (m + 2) * f_ref / ((n + 2) * (2 ^ a)) + + with the constraint that: + + 80 MhZ < (m + 2) / (n + 2) * f_ref <= 150Mhz + and n, m >= 1 + */ + + int goal_f_out = 512 * r->sample_rate; + int a, n, m, best_n = 0, best_m = 0, best_error = 10000000; + int pll_sample; + int error; + + for (a = 0; a < 8; a++) { + if ((goal_f_out * (1 << a)) >= 80000000) + break; + } + + /* a != 8 because sample_rate >= 2000 */ + + for (n = 33; n > 2; n--) { + m = (goal_f_out * n * (1 << a)) / F_REF; + if ((m > 257) || (m < 3)) continue; + + pll_sample = (m * F_REF) / (n * (1 << a)); + pll_sample /= 512; + + /* Threshold might be good here */ + error = pll_sample - r->sample_rate; + error = ABS(error); + + if (error < best_error) { + best_error = error; + best_n = n; + best_m = m; + if (error == 0) break; + } + } + + best_n -= 2; + best_m -= 2; + + sv_write_indirect(sc, SV_ADC_PLL_M, best_m); + sv_write_indirect(sc, SV_ADC_PLL_N, + best_n | (a << SV_PLL_R_SHIFT)); + } + return (0); +} + +int +sv_round_blocksize(addr, blk) + void *addr; + int blk; +{ + return (blk & -32); /* keep good alignment */ +} + +int +sv_dma_init_input(addr, buf, cc) + void *addr; + void *buf; + int cc; +{ + struct sv_softc *sc = addr; + struct sv_dma *p; + int dma_count; + + DPRINTF(("sv_dma_init_input: dma start loop input addr=%p cc=%d\n", + buf, cc)); + for (p = sc->sc_dmas; p && KERNADDR(p) != buf; p = p->next) + ; + if (!p) { + printf("sv_dma_init_input: bad addr %p\n", buf); + return (EINVAL); + } + + dma_count = (cc >> 1) - 1; + + bus_space_write_4(sc->sc_iot, sc->sc_dmac_ioh, SV_DMA_ADDR0, + DMAADDR(p)); + bus_space_write_4(sc->sc_iot, sc->sc_dmac_ioh, SV_DMA_COUNT0, + dma_count); + bus_space_write_1(sc->sc_iot, sc->sc_dmac_ioh, SV_DMA_MODE, + DMA37MD_WRITE | DMA37MD_LOOP); + + return (0); +} + +int +sv_dma_init_output(addr, buf, cc) + void *addr; + void *buf; + int cc; +{ + struct sv_softc *sc = addr; + struct sv_dma *p; + int dma_count; + + DPRINTF(("sv_dma_init_output: start loop output buf=%p cc=%d\n", buf, cc)); + for (p = sc->sc_dmas; p && KERNADDR(p) != buf; p = p->next) + ; + if (!p) { + printf("sv_dma_init_output: bad addr %p\n", buf); + return (EINVAL); + } + + dma_count = cc - 1; + + DPRINTF(("sv_dma_init_output: addr0=0x%08lx count0=0x%08x mode=0x%02x\n", + (u_long)DMAADDR(p), dma_count, DMA37MD_READ | DMA37MD_LOOP)); + bus_space_write_4(sc->sc_iot, sc->sc_dmaa_ioh, SV_DMA_ADDR0, + DMAADDR(p)); + bus_space_write_4(sc->sc_iot, sc->sc_dmaa_ioh, SV_DMA_COUNT0, + dma_count); + bus_space_write_1(sc->sc_iot, sc->sc_dmaa_ioh, SV_DMA_MODE, + DMA37MD_READ | DMA37MD_LOOP); + + return (0); +} + +int +sv_dma_output(addr, p, cc, intr, arg) + void *addr; + void *p; + int cc; + void (*intr) __P((void *)); + void *arg; +{ + struct sv_softc *sc = addr; + u_int8_t mode; + + DPRINTFN(1, ("sv_dma_output: sc=%p buf=%p cc=%d intr=%p(%p)\n", + addr, p, cc, intr, arg)); + + sc->sc_pintr = intr; + sc->sc_parg = arg; + if (!(sc->sc_enable & SV_PLAY_ENABLE)) { + int dma_count = cc - 1; + + DPRINTF(("sv_dma_output: set count=%d\n", dma_count)); + sv_write_indirect(sc, SV_DMAA_COUNT1, dma_count >> 8); + sv_write_indirect(sc, SV_DMAA_COUNT0, (dma_count & 0xFF)); + + mode = sv_read_indirect(sc, SV_PLAY_RECORD_ENABLE); + mode |= SV_PLAY_ENABLE; + sv_write_indirect(sc, SV_PLAY_RECORD_ENABLE, mode); + sc->sc_enable |= SV_PLAY_ENABLE; + } + return (0); +} + +int +sv_dma_input(addr, p, cc, intr, arg) + void *addr; + void *p; + int cc; + void (*intr) __P((void *)); + void *arg; +{ + struct sv_softc *sc = addr; + u_int8_t mode; + + DPRINTFN(1, ("sv_dma_input: sc=%p buf=%p cc=%d intr=%p(%p)\n", + addr, p, cc, intr, arg)); + sc->sc_rintr = intr; + sc->sc_rarg = arg; + if (!(sc->sc_enable & SV_RECORD_ENABLE)) { + int dma_count = (cc >> 1) - 1; + + sv_write_indirect(sc, SV_DMAC_COUNT1, dma_count >> 8); + sv_write_indirect(sc, SV_DMAC_COUNT0, (dma_count & 0xFF)); + + mode = sv_read_indirect(sc, SV_PLAY_RECORD_ENABLE); + mode |= SV_RECORD_ENABLE; + sv_write_indirect(sc, SV_PLAY_RECORD_ENABLE, mode); + sc->sc_enable |= SV_RECORD_ENABLE; + } + return (0); +} + +int +sv_halt_out_dma(addr) + void *addr; +{ + struct sv_softc *sc = addr; + u_int8_t mode; + + DPRINTF(("sv: sv_halt_out_dma\n")); + mode = sv_read_indirect(sc, SV_PLAY_RECORD_ENABLE); + mode &= ~SV_PLAY_ENABLE; + sc->sc_enable &= ~SV_PLAY_ENABLE; + sv_write_indirect(sc, SV_PLAY_RECORD_ENABLE, mode); + + return (0); +} + +int +sv_halt_in_dma(addr) + void *addr; +{ + struct sv_softc *sc = addr; + u_int8_t mode; + + DPRINTF(("sv: sv_halt_in_dma\n")); + mode = sv_read_indirect(sc, SV_PLAY_RECORD_ENABLE); + mode &= ~SV_RECORD_ENABLE; + sc->sc_enable &= ~SV_RECORD_ENABLE; + sv_write_indirect(sc, SV_PLAY_RECORD_ENABLE, mode); + + return (0); +} + +int +sv_getdev(addr, retp) + void *addr; + struct audio_device *retp; +{ + *retp = sv_device; + return (0); +} + + +/* + * Mixer related code is here + * + */ + +#define SV_INPUT_CLASS 0 +#define SV_OUTPUT_CLASS 1 +#define SV_RECORD_CLASS 2 + +#define SV_LAST_CLASS 2 + +static const char *mixer_classes[] = + { AudioCinputs, AudioCoutputs, AudioCrecord }; + +static const struct { + u_int8_t l_port; + u_int8_t r_port; + u_int8_t mask; + u_int8_t class; + const char *audio; +} ports[] = { + { SV_LEFT_AUX1_INPUT_CONTROL, SV_RIGHT_AUX1_INPUT_CONTROL, SV_AUX1_MASK, + SV_INPUT_CLASS, "aux1" }, + { SV_LEFT_CD_INPUT_CONTROL, SV_RIGHT_CD_INPUT_CONTROL, SV_CD_MASK, + SV_INPUT_CLASS, AudioNcd }, + { SV_LEFT_LINE_IN_INPUT_CONTROL, SV_RIGHT_LINE_IN_INPUT_CONTROL, SV_LINE_IN_MASK, + SV_INPUT_CLASS, AudioNline }, + { SV_MIC_INPUT_CONTROL, 0, SV_MIC_MASK, SV_INPUT_CLASS, AudioNmicrophone }, + { SV_LEFT_SYNTH_INPUT_CONTROL, SV_RIGHT_SYNTH_INPUT_CONTROL, + SV_SYNTH_MASK, SV_INPUT_CLASS, AudioNfmsynth }, + { SV_LEFT_AUX2_INPUT_CONTROL, SV_RIGHT_AUX2_INPUT_CONTROL, SV_AUX2_MASK, + SV_INPUT_CLASS, "aux2" }, + { SV_LEFT_PCM_INPUT_CONTROL, SV_RIGHT_PCM_INPUT_CONTROL, SV_PCM_MASK, + SV_INPUT_CLASS, AudioNdac }, + { SV_LEFT_MIXER_OUTPUT_CONTROL, SV_RIGHT_MIXER_OUTPUT_CONTROL, + SV_MIXER_OUT_MASK, SV_OUTPUT_CLASS, AudioNmaster } +}; + + +static const struct { + int idx; + const char *name; +} record_sources[] = { + { SV_REC_CD, AudioNcd }, + { SV_REC_DAC, AudioNdac }, + { SV_REC_AUX2, "aux2" }, + { SV_REC_LINE, AudioNline }, + { SV_REC_AUX1, "aux1" }, + { SV_REC_MIC, AudioNmicrophone }, + { SV_REC_MIXER, AudioNmixerout } +}; + + +#define SV_DEVICES_PER_PORT 2 +#define SV_FIRST_MIXER (SV_LAST_CLASS + 1) +#define SV_LAST_MIXER (SV_DEVICES_PER_PORT * (ARRAY_SIZE(ports)) + SV_LAST_CLASS) +#define SV_RECORD_SOURCE (SV_LAST_MIXER + 1) +#define SV_MIC_BOOST (SV_LAST_MIXER + 2) +#define SV_RECORD_GAIN (SV_LAST_MIXER + 3) +#define SV_SRS_MODE (SV_LAST_MIXER + 4) + +int +sv_query_devinfo(addr, dip) + void *addr; + mixer_devinfo_t *dip; +{ + int i; + + /* It's a class */ + if (dip->index <= SV_LAST_CLASS) { + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = dip->index; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, + mixer_classes[dip->index]); + return (0); + } + + if (dip->index >= SV_FIRST_MIXER && + dip->index <= SV_LAST_MIXER) { + int off = dip->index - SV_FIRST_MIXER; + int mute = (off % SV_DEVICES_PER_PORT); + int idx = off / SV_DEVICES_PER_PORT; + + dip->mixer_class = ports[idx].class; + strcpy(dip->label.name, ports[idx].audio); + + if (!mute) { + dip->type = AUDIO_MIXER_VALUE; + dip->prev = AUDIO_MIXER_LAST; + dip->next = dip->index + 1; + + if (ports[idx].r_port != 0) + dip->un.v.num_channels = 2; + else + dip->un.v.num_channels = 1; + + strcpy(dip->un.v.units.name, AudioNvolume); + } else { + dip->type = AUDIO_MIXER_ENUM; + dip->prev = dip->index - 1; + dip->next = AUDIO_MIXER_LAST; + + strcpy(dip->label.name, AudioNmute); + dip->un.e.num_mem = 2; + strcpy(dip->un.e.member[0].label.name, AudioNoff); + dip->un.e.member[0].ord = 0; + strcpy(dip->un.e.member[1].label.name, AudioNon); + dip->un.e.member[1].ord = 1; + } + + return (0); + } + + switch (dip->index) { + case SV_RECORD_SOURCE: + dip->mixer_class = SV_RECORD_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = SV_RECORD_GAIN; + strcpy(dip->label.name, AudioNsource); + dip->type = AUDIO_MIXER_ENUM; + + dip->un.e.num_mem = ARRAY_SIZE(record_sources); + for (i = 0; i < ARRAY_SIZE(record_sources); i++) { + strcpy(dip->un.e.member[i].label.name, + record_sources[i].name); + dip->un.e.member[i].ord = record_sources[i].idx; + } + return (0); + + case SV_RECORD_GAIN: + dip->mixer_class = SV_RECORD_CLASS; + dip->prev = SV_RECORD_SOURCE; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "gain"); + dip->type = AUDIO_MIXER_VALUE; + dip->un.v.num_channels = 1; + strcpy(dip->un.v.units.name, AudioNvolume); + return (0); + + case SV_MIC_BOOST: + dip->mixer_class = SV_RECORD_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "micboost"); + goto on_off; + + case SV_SRS_MODE: + dip->mixer_class = SV_OUTPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNspatial); + + on_off: + dip->type = AUDIO_MIXER_ENUM; + dip->un.e.num_mem = 2; + strcpy(dip->un.e.member[0].label.name, AudioNoff); + dip->un.e.member[0].ord = 0; + strcpy(dip->un.e.member[1].label.name, AudioNon); + dip->un.e.member[1].ord = 1; + return (0); + } + + return (ENXIO); +} + +int +sv_mixer_set_port(addr, cp) + void *addr; + mixer_ctrl_t *cp; +{ + struct sv_softc *sc = addr; + u_int8_t reg; + int idx; + + if (cp->dev >= SV_FIRST_MIXER && + cp->dev <= SV_LAST_MIXER) { + int off = cp->dev - SV_FIRST_MIXER; + int mute = (off % SV_DEVICES_PER_PORT); + idx = off / SV_DEVICES_PER_PORT; + + if (mute) { + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + + reg = sv_read_indirect(sc, ports[idx].l_port); + if (cp->un.ord) + reg |= SV_MUTE_BIT; + else + reg &= ~SV_MUTE_BIT; + sv_write_indirect(sc, ports[idx].l_port, reg); + + if (ports[idx].r_port) { + reg = sv_read_indirect(sc, ports[idx].r_port); + if (cp->un.ord) + reg |= SV_MUTE_BIT; + else + reg &= ~SV_MUTE_BIT; + sv_write_indirect(sc, ports[idx].r_port, reg); + } + } else { + int lval, rval; + + if (cp->type != AUDIO_MIXER_VALUE) + return (EINVAL); + + if (cp->un.value.num_channels != 1 && + cp->un.value.num_channels != 2) + return (EINVAL); + + if (ports[idx].r_port == 0) { + if (cp->un.value.num_channels != 1) + return (EINVAL); + lval = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]; + rval = 0; /* shut up GCC */ + } else { + if (cp->un.value.num_channels != 2) + return (EINVAL); + + lval = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + rval = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; + } + + + reg = sv_read_indirect(sc, ports[idx].l_port); + reg &= ~(ports[idx].mask); + lval = (AUDIO_MAX_GAIN - lval) * ports[idx].mask / + AUDIO_MAX_GAIN; + reg |= lval; + sv_write_indirect(sc, ports[idx].l_port, reg); + + if (ports[idx].r_port != 0) { + reg = sv_read_indirect(sc, ports[idx].r_port); + reg &= ~(ports[idx].mask); + + rval = (AUDIO_MAX_GAIN - rval) * ports[idx].mask / + AUDIO_MAX_GAIN; + reg |= rval; + + sv_write_indirect(sc, ports[idx].r_port, reg); + } + + sv_read_indirect(sc, ports[idx].l_port); + } + + return (0); + } + + + switch (cp->dev) { + case SV_RECORD_SOURCE: + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + + for (idx = 0; idx < ARRAY_SIZE(record_sources); idx++) { + if (record_sources[idx].idx == cp->un.ord) + goto found; + } + + return (EINVAL); + + found: + reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL); + reg &= ~SV_REC_SOURCE_MASK; + reg |= (((cp->un.ord) << SV_REC_SOURCE_SHIFT) & SV_REC_SOURCE_MASK); + sv_write_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL, reg); + + reg = sv_read_indirect(sc, SV_RIGHT_ADC_INPUT_CONTROL); + reg &= ~SV_REC_SOURCE_MASK; + reg |= (((cp->un.ord) << SV_REC_SOURCE_SHIFT) & SV_REC_SOURCE_MASK); + sv_write_indirect(sc, SV_RIGHT_ADC_INPUT_CONTROL, reg); + return (0); + + case SV_RECORD_GAIN: + { + int val; + + if (cp->type != AUDIO_MIXER_VALUE) + return (EINVAL); + + if (cp->un.value.num_channels != 1) + return (EINVAL); + + val = (cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] * SV_REC_GAIN_MASK) + / AUDIO_MAX_GAIN; + + reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL); + reg &= ~SV_REC_GAIN_MASK; + reg |= val; + sv_write_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL, reg); + + reg = sv_read_indirect(sc, SV_RIGHT_ADC_INPUT_CONTROL); + reg &= ~SV_REC_GAIN_MASK; + reg |= val; + sv_write_indirect(sc, SV_RIGHT_ADC_INPUT_CONTROL, reg); + } + return (0); + + case SV_MIC_BOOST: + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + + reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL); + if (cp->un.ord) { + reg |= SV_MIC_BOOST_BIT; + } else { + reg &= ~SV_MIC_BOOST_BIT; + } + + sv_write_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL, reg); + return (0); + + case SV_SRS_MODE: + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + + reg = sv_read_indirect(sc, SV_SRS_SPACE_CONTROL); + if (cp->un.ord) { + reg &= ~SV_SRS_SPACE_ONOFF; + } else { + reg |= SV_SRS_SPACE_ONOFF; + } + + sv_write_indirect(sc, SV_SRS_SPACE_CONTROL, reg); + return (0); + } + + return (EINVAL); +} + +int +sv_mixer_get_port(addr, cp) + void *addr; + mixer_ctrl_t *cp; +{ + struct sv_softc *sc = addr; + int val; + u_int8_t reg; + + if (cp->dev >= SV_FIRST_MIXER && + cp->dev <= SV_LAST_MIXER) { + int off = cp->dev - SV_FIRST_MIXER; + int mute = (off % 2); + int idx = off / 2; + + if (mute) { + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + + reg = sv_read_indirect(sc, ports[idx].l_port); + cp->un.ord = ((reg & SV_MUTE_BIT) ? 1 : 0); + } else { + if (cp->type != AUDIO_MIXER_VALUE) + return (EINVAL); + + if (cp->un.value.num_channels != 1 && + cp->un.value.num_channels != 2) + return (EINVAL); + + if ((ports[idx].r_port == 0 && + cp->un.value.num_channels != 1) || + (ports[idx].r_port != 0 && + cp->un.value.num_channels != 2)) + return (EINVAL); + + reg = sv_read_indirect(sc, ports[idx].l_port); + reg &= ports[idx].mask; + + val = AUDIO_MAX_GAIN - ((reg * AUDIO_MAX_GAIN) / ports[idx].mask); + + if (ports[idx].r_port != 0) { + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = val; + + reg = sv_read_indirect(sc, ports[idx].r_port); + reg &= ports[idx].mask; + + val = AUDIO_MAX_GAIN - ((reg * AUDIO_MAX_GAIN) / ports[idx].mask); + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = val; + } else + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = val; + } + + return (0); + } + + switch (cp->dev) { + case SV_RECORD_SOURCE: + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + + reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL); + cp->un.ord = ((reg & SV_REC_SOURCE_MASK) >> SV_REC_SOURCE_SHIFT); + + return (0); + + case SV_RECORD_GAIN: + if (cp->type != AUDIO_MIXER_VALUE) + return (EINVAL); + if (cp->un.value.num_channels != 1) + return (EINVAL); + + reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL) & SV_REC_GAIN_MASK; + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = + (((unsigned int)reg) * AUDIO_MAX_GAIN) / SV_REC_GAIN_MASK; + + return (0); + + case SV_MIC_BOOST: + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + reg = sv_read_indirect(sc, SV_LEFT_ADC_INPUT_CONTROL); + cp->un.ord = ((reg & SV_MIC_BOOST_BIT) ? 1 : 0); + return (0); + + + case SV_SRS_MODE: + if (cp->type != AUDIO_MIXER_ENUM) + return (EINVAL); + reg = sv_read_indirect(sc, SV_SRS_SPACE_CONTROL); + cp->un.ord = ((reg & SV_SRS_SPACE_ONOFF) ? 0 : 1); + return (0); + } + + return (EINVAL); +} + + +static void +sv_init_mixer(sc) + struct sv_softc *sc; +{ + mixer_ctrl_t cp; + int i; + + cp.type = AUDIO_MIXER_ENUM; + cp.dev = SV_SRS_MODE; + cp.un.ord = 0; + + sv_mixer_set_port(sc, &cp); + + for (i = 0; i < ARRAY_SIZE(ports); i++) { + if (ports[i].audio == AudioNdac) { + cp.type = AUDIO_MIXER_ENUM; + cp.dev = SV_FIRST_MIXER + i * SV_DEVICES_PER_PORT + 1; + cp.un.ord = 0; + sv_mixer_set_port(sc, &cp); + break; + } + } +} + +void * +sv_malloc(addr, size, pool, flags) + void *addr; + u_long size; + int pool; + int flags; +{ + struct sv_softc *sc = addr; + struct sv_dma *p; + int error; + + p = malloc(sizeof(*p), pool, flags); + if (!p) + return (0); + error = sv_allocmem(sc, size, 16, p); + if (error) { + free(p, pool); + return (0); + } + p->next = sc->sc_dmas; + sc->sc_dmas = p; + return (KERNADDR(p)); +} + +void +sv_free(addr, ptr, pool) + void *addr; + void *ptr; + int pool; +{ + struct sv_softc *sc = addr; + struct sv_dma **p; + + for (p = &sc->sc_dmas; *p; p = &(*p)->next) { + if (KERNADDR(*p) == ptr) { + sv_freemem(sc, *p); + *p = (*p)->next; + free(*p, pool); + return; + } + } +} + +u_long +sv_round(addr, size) + void *addr; + u_long size; +{ + return (size); +} + +int +sv_mappage(addr, mem, off, prot) + void *addr; + void *mem; + int off; + int prot; +{ + struct sv_softc *sc = addr; + struct sv_dma *p; + + for (p = sc->sc_dmas; p && KERNADDR(p) != mem; p = p->next) + ; + if (!p) + return (-1); + return (bus_dmamem_mmap(sc->sc_dmatag, p->segs, p->nsegs, + off, prot, BUS_DMA_WAITOK)); +} + +int +sv_get_props(addr) + void *addr; +{ + return (AUDIO_PROP_MMAP | AUDIO_PROP_FULLDUPLEX); +} diff --git a/sys/dev/pci/svreg.h b/sys/dev/pci/svreg.h new file mode 100644 index 000000000000..40b58ad6a7b7 --- /dev/null +++ b/sys/dev/pci/svreg.h @@ -0,0 +1,235 @@ +/* $NetBSD: svreg.h,v 1.1 1998/12/10 18:47:19 augustss Exp $ */ +/* + * Copyright (c) 1998 Constantine Paul Sapuntzakis + * All rights reserved + * + * Author: Constantine Paul Sapuntzakis (csapuntz@cvs.openbsd.org) + * + * 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. The author's name or those of the contributors may be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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. + */ + +/* + * PCI BIOS Configuration area ports + */ + +enum { + SV_SB_PORTBASE_SLOT = 0x10, + SV_ENHANCED_PORTBASE_SLOT = 0x14, + SV_FM_PORTBASE_SLOT = 0x18, + SV_MIDI_PORTBASE_SLOT = 0x1c, + SV_GAME_PORTBASE_SLOT = 0x20 +}; + +/* + * Enhanced CODEC registers + * These are offset from the base specified in the PCI configuration area + */ +enum { + SV_CODEC_CONTROL = 0, + SV_CODEC_INTMASK = 1, + SV_CODEC_STATUS = 2, + SV_CODEC_IADDR = 4, + SV_CODEC_IDATA = 5 +}; + +/* + * DMA Configuration register + */ + +enum { + SV_DMAA_CONFIG_OFF = 0x40, + SV_DMAC_CONFIG_OFF = 0x48 +}; +#define SV_DMAA_SIZE 0x10 +#define SV_DMAA_ALIGN 0x10 +#define SV_DMAC_SIZE 0x10 +#define SV_DMAC_ALIGN 0x10 + +enum { + SV_DMA_CHANNEL_ENABLE = 0x1, + SV_DMAA_EXTENDED_ADDR = 0x8, + SV_DMA_PORTBASE_MASK = 0xFFFFFFF0 +}; + + +enum { + SV_DMA_ADDR0 = 0, + SV_DMA_ADDR1 = 1, + SV_DMA_ADDR2 = 2, + SV_DMA_ADDR3 = 3, + SV_DMA_COUNT0 = 4, + SV_DMA_COUNT1 = 5, + SV_DMA_COUNT2 = 6, + SV_DMA_CMDSTATUS = 8, + SV_DMA_MODE = 0xB, + SV_DMA_MASTERCLEAR = 0xD, + SV_DMA_MASK = 0xF +}; + + +/* + * DMA Mode (see reg 0xB) + */ + +enum { + SV_DMA_MODE_IOR_MASK = 0x0C, + SV_DMA_MODE_IOW_MASK = 0x0C, + SV_DMA_MODE_IOR = 0x04, + SV_DMA_MODE_IOW = 0x08, + SV_DMA_MODE_AUTOINIT = 0x10 +}; + +enum { + SV_CTL_ENHANCED = 0x01, + SV_CTL_MD1 = 0x04, + SV_CTL_FWS = 0x08, + SV_CTL_INTA = 0x20, + SV_CTL_RESET = 0x80 +}; + +enum { + SV_INTMASK_DMAA = 0x1, + SV_INTMASK_DMAC = 0x4, + SV_INTMASK_SINT = 0x8, + SV_INTMASK_UD = 0x40, + SV_INTMASK_MIDI = 0x80 +}; + +enum { + SV_INTSTATUS_DMAA = 0x1, + SV_INTSTATUS_DMAC = 0x4, + SV_INTSTATUS_SINT = 0x8, + SV_INTSTATUS_UD = 0x40, + SV_INTSTATUS_MIDI = 0x80 +}; + +enum { + SV_IADDR_MASK = 0x3f, + SV_IADDR_MCE = 0x40, + /* TRD = DMA Transfer request disable */ + SV_IADDR_TRD = 0x80 +}; + + +enum { + SV_LEFT_ADC_INPUT_CONTROL = 0x0, + SV_RIGHT_ADC_INPUT_CONTROL = 0x1, + SV_LEFT_AUX1_INPUT_CONTROL = 0x2, + SV_RIGHT_AUX1_INPUT_CONTROL = 0x3, + SV_LEFT_CD_INPUT_CONTROL = 0x4, + SV_RIGHT_CD_INPUT_CONTROL = 0x5, + SV_LEFT_LINE_IN_INPUT_CONTROL = 0x6, + SV_RIGHT_LINE_IN_INPUT_CONTROL = 0x7, + SV_MIC_INPUT_CONTROL = 0x8, + SV_GAME_PORT_CONTROL = 0x9, + SV_LEFT_SYNTH_INPUT_CONTROL = 0x0A, + SV_RIGHT_SYNTH_INPUT_CONTROL = 0x0B, + SV_LEFT_AUX2_INPUT_CONTROL = 0x0C, + SV_RIGHT_AUX2_INPUT_CONTROL = 0x0D, + SV_LEFT_MIXER_OUTPUT_CONTROL = 0x0E, + SV_RIGHT_MIXER_OUTPUT_CONTROL = 0x0F, + SV_LEFT_PCM_INPUT_CONTROL = 0x10, + SV_RIGHT_PCM_INPUT_CONTROL = 0x11, + SV_DMA_DATA_FORMAT = 0x12, + SV_PLAY_RECORD_ENABLE = 0x13, + SV_UP_DOWN_CONTROL = 0x14, + SV_REVISION_LEVEL = 0x15, + SV_MONITOR_CONTROL = 0x16, + SV_DMAA_COUNT1 = 0x18, + SV_DMAA_COUNT0 = 0x19, + SV_DMAC_COUNT1 = 0x1C, + SV_DMAC_COUNT0 = 0x1d, + SV_PCM_SAMPLE_RATE_0 = 0x1e, + SV_PCM_SAMPLE_RATE_1 = 0x1f, + SV_SYNTH_SAMPLE_RATE_0 = 0x20, + SV_SYNTH_SAMPLE_RATE_1 = 0x21, + SV_ADC_CLOCK_SOURCE = 0x22, + SV_ADC_ALT_SAMPLE_RATE = 0x23, + SV_ADC_PLL_M = 0x24, + SV_ADC_PLL_N = 0x25, + SV_SYNTH_PLL_M = 0x26, + SV_SYNTH_PLL_N = 0x27, + SV_MPU401 = 0x2A, + SV_DRIVE_CONTROL = 0x2B, + SV_SRS_SPACE_CONTROL = 0x2c, + SV_SRS_CENTER_CONTROL = 0x2d, + SV_WAVETABLE_SOURCE_SELECT = 0x2e, + SV_ANALOG_POWER_DOWN_CONTROL = 0x30, + SV_DIGITAL_POWER_DOWN_CONTROL = 0x31 +}; + +enum { + SV_MUTE_BIT = 0x80, + SV_AUX1_MASK = 0x1F, + SV_CD_MASK = 0x1F, + SV_LINE_IN_MASK = 0x1F, + SV_MIC_MASK = 0x0F, + SV_SYNTH_MASK = 0x1F, + SV_AUX2_MASK = 0x1F, + SV_MIXER_OUT_MASK = 0x1F, + SV_PCM_MASK = 0x3F +}; + +enum { + SV_DMAA_STEREO = 0x1, + SV_DMAA_FORMAT16 = 0x2, + SV_DMAC_STEREO = 0x10, + SV_DMAC_FORMAT16 = 0x20 +}; + +enum { + SV_PLAY_ENABLE = 0x1, + SV_RECORD_ENABLE = 0x2 +}; + +enum { + SV_PLL_R_SHIFT = 5 +}; + +/* ADC input source (registers 0 & 1) */ +enum { + SV_REC_SOURCE_MASK = 0xE0, + SV_REC_SOURCE_SHIFT = 5, + SV_MIC_BOOST_BIT = 0x10, + SV_REC_GAIN_MASK = 0x0F, + SV_REC_CD = 1, + SV_REC_DAC = 2, + SV_REC_AUX2 = 3, + SV_REC_LINE = 4, + SV_REC_AUX1 = 5, + SV_REC_MIC = 6, + SV_REC_MIXER = 7 +}; + +/* SRS Space control register (reg 0x2C) */ + +enum { + SV_SRS_SPACE_ONOFF = 0x80 +}; + +enum { + SV_WSS_WT0 = 0x01, + SV_WSS_WT1 = 0x02, +}; diff --git a/sys/dev/pci/svvar.h b/sys/dev/pci/svvar.h new file mode 100644 index 000000000000..ae8c8e0218de --- /dev/null +++ b/sys/dev/pci/svvar.h @@ -0,0 +1,64 @@ +/* $NetBSD: svvar.h,v 1.1 1998/12/10 18:47:19 augustss Exp $ */ + +/* + * Copyright (c) 1998 Constantine Paul Sapuntzakis + * All rights reserved + * + * Author: Constantine Paul Sapuntzakis (csapuntz@cvs.openbsd.org) + * + * 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. The author's name or those of the contributors may be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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. + */ + +struct sv_softc { + struct device sc_dev; /* base device */ + void *sc_ih; /* interrupt vectoring */ + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_space_handle_t sc_dmaa_ioh; + bus_space_handle_t sc_dmac_ioh; + bus_dma_tag_t sc_dmatag; /* DMA tag */ + + bus_space_tag_t sc_opliot; + bus_space_handle_t sc_oplioh; + + bus_space_tag_t sc_midiiot; + bus_space_handle_t sc_midiioh; + + struct sv_dma *sc_dmas; + + void (*sc_pintr)(void *); /* dma completion intr handler */ + void *sc_parg; /* arg for sc_intr() */ + + void (*sc_rintr)(void *); /* dma completion intr handler */ + void *sc_rarg; /* arg for sc_intr() */ + char sc_enable; + + u_int sc_record_source; /* recording source mask */ + + struct pci_attach_args sc_pa; + char sc_dmaset; +}; +