Module Name: src Committed By: jmcneill Date: Mon Jan 22 21:28:15 UTC 2024
Modified Files: src/sys/arch/evbppc/conf: WII files.wii src/sys/arch/evbppc/include: wii.h src/sys/arch/evbppc/wii: machdep.c mainbus.c Added Files: src/sys/arch/evbppc/wii/dev: bwai.c bwai.h bwdsp.c Log Message: wii: Add drivers for Broadway DSP and Audio interface. 0: [*] audio0 @ bwdsp0: Broadway DSP playback: 16, 2ch, 48000Hz record: unavailable (P-) slinear_be 16/16, 2ch, { 48000 } To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.2 src/sys/arch/evbppc/conf/WII \ src/sys/arch/evbppc/conf/files.wii cvs rdiff -u -r1.2 -r1.3 src/sys/arch/evbppc/include/wii.h cvs rdiff -u -r1.2 -r1.3 src/sys/arch/evbppc/wii/machdep.c cvs rdiff -u -r1.1 -r1.2 src/sys/arch/evbppc/wii/mainbus.c cvs rdiff -u -r0 -r1.1 src/sys/arch/evbppc/wii/dev/bwai.c \ src/sys/arch/evbppc/wii/dev/bwai.h src/sys/arch/evbppc/wii/dev/bwdsp.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/evbppc/conf/WII diff -u src/sys/arch/evbppc/conf/WII:1.1 src/sys/arch/evbppc/conf/WII:1.2 --- src/sys/arch/evbppc/conf/WII:1.1 Sat Jan 20 21:35:59 2024 +++ src/sys/arch/evbppc/conf/WII Mon Jan 22 21:28:15 2024 @@ -1,4 +1,4 @@ -# $NetBSD: WII,v 1.1 2024/01/20 21:35:59 jmcneill Exp $ +# $NetBSD: WII,v 1.2 2024/01/22 21:28:15 jmcneill Exp $ # # Nintendo Wii # @@ -128,6 +128,8 @@ options WSDISPLAY_DEFAULTSCREENS=4 options WSDISPLAY_SCROLLSUPPORT hollywood0 at mainbus0 irq 14 +bwai0 at mainbus0 addr 0x0d006c00 irq 5 # Audio interface +bwdsp0 at mainbus0 addr 0x0c005000 irq 6 # DSP #iosipc0 at hollywood0 addr 0x0d000000 irq 30 # IOS IPC resetbtn0 at hollywood0 irq 17 # Reset button Index: src/sys/arch/evbppc/conf/files.wii diff -u src/sys/arch/evbppc/conf/files.wii:1.1 src/sys/arch/evbppc/conf/files.wii:1.2 --- src/sys/arch/evbppc/conf/files.wii:1.1 Sat Jan 20 21:35:59 2024 +++ src/sys/arch/evbppc/conf/files.wii Mon Jan 22 21:28:15 2024 @@ -1,4 +1,4 @@ -# $NetBSD: files.wii,v 1.1 2024/01/20 21:35:59 jmcneill Exp $ +# $NetBSD: files.wii,v 1.2 2024/01/22 21:28:15 jmcneill Exp $ # # maxpartitions 16 @@ -39,6 +39,14 @@ file arch/evbppc/wii/mainbus.c mainbus attach genfb at mainbus with wiifb file arch/evbppc/wii/dev/wiifb.c wiifb +device bwai +attach bwai at mainbus +file arch/evbppc/wii/dev/bwai.c bwai + +device bwdsp: audiobus +attach bwdsp at mainbus +file arch/evbppc/wii/dev/bwdsp.c bwdsp + define hollywood { [addr=-1], [irq=-1] } device hollywood: hollywood attach hollywood at mainbus Index: src/sys/arch/evbppc/include/wii.h diff -u src/sys/arch/evbppc/include/wii.h:1.2 src/sys/arch/evbppc/include/wii.h:1.3 --- src/sys/arch/evbppc/include/wii.h:1.2 Sun Jan 21 01:41:54 2024 +++ src/sys/arch/evbppc/include/wii.h Mon Jan 22 21:28:15 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: wii.h,v 1.2 2024/01/21 01:41:54 jmcneill Exp $ */ +/* $NetBSD: wii.h,v 1.3 2024/01/22 21:28:15 jmcneill Exp $ */ /*- * Copyright (c) 2024 Jared McNeill <jmcne...@invisible.ca> @@ -42,18 +42,24 @@ #define WII_IOMEM_BASE 0x0c000000 -#define GLOBAL_BASE 0x00000000 +#define GLOBAL_BASE 0x00000000 #define GLOBAL_SIZE 0x00003400 #define BROADWAY_BASE 0x0c000000 #define BROADWAY_SIZE 0x00000004 -#define VI_BASE 0x0c002000 -#define VI_SIZE 0x00000100 +#define VI_BASE 0x0c002000 +#define VI_SIZE 0x00000100 #define PI_BASE 0x0c003000 #define PI_SIZE 0x00000100 +#define DSP_BASE 0x0c005000 +#define DSP_SIZE 0x00000200 + +#define AI_BASE 0x0d006c00 +#define AI_SIZE 0x00000020 + #define HOLLYWOOD_BASE 0x0d000000 #define HOLLYWOOD_PRIV_BASE 0x0d800000 #define HOLLYWOOD_SIZE 0x00008000 @@ -61,8 +67,8 @@ #define XFB_START 0x01698000 #define XFB_SIZE 0x00168000 -#define DSP_START 0x10000000 -#define DSP_SIZE 0x00004000 +#define DSP_MEM_START 0x10000000 +#define DSP_MEM_SIZE 0x00004000 #define IPC_START 0x133e0000 #define IPC_SIZE 0x00020000 @@ -90,12 +96,14 @@ #define PI_INTMR (PI_BASE + 0x04) /* Processor IRQs */ -#define PI_IRQ_HOLLYWOOD 14 +#define PI_IRQ_AI 5 +#define PI_IRQ_DSP 6 +#define PI_IRQ_HOLLYWOOD 14 /* Hollywood registers */ #define HW_PPCIRQFLAGS (HOLLYWOOD_BASE + 0x030) #define HW_PPCIRQMASK (HOLLYWOOD_BASE + 0x034) -#define HW_ARMIRQFLAGS (HOLLYWOOD_PRIV_BASE + 0x038) +#define HW_ARMIRQFLAGS (HOLLYWOOD_PRIV_BASE + 0x038) #define HW_ARMIRQMASK (HOLLYWOOD_PRIV_BASE + 0x03c) #define HW_AHBPROT (HOLLYWOOD_PRIV_BASE + 0x064) #define HW_GPIOB_OUT (HOLLYWOOD_BASE + 0x0c0) Index: src/sys/arch/evbppc/wii/machdep.c diff -u src/sys/arch/evbppc/wii/machdep.c:1.2 src/sys/arch/evbppc/wii/machdep.c:1.3 --- src/sys/arch/evbppc/wii/machdep.c:1.2 Sun Jan 21 01:41:54 2024 +++ src/sys/arch/evbppc/wii/machdep.c Mon Jan 22 21:28:15 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: machdep.c,v 1.2 2024/01/21 01:41:54 jmcneill Exp $ */ +/* $NetBSD: machdep.c,v 1.3 2024/01/22 21:28:15 jmcneill Exp $ */ /* * Copyright (c) 2002, 2024 The NetBSD Foundation, Inc. @@ -63,7 +63,7 @@ #define _POWERPC_BUS_DMA_PRIVATE #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.2 2024/01/21 01:41:54 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.3 2024/01/22 21:28:15 jmcneill Exp $"); #include "opt_compat_netbsd.h" #include "opt_ddb.h" @@ -254,8 +254,8 @@ initppc(u_int startkernel, u_int endkern availmemr[1].size = physmemr[1].size; if (mem2_size != 0) { /* DSP uses 16KB at the start of MEM2 */ - availmemr[1].start += DSP_SIZE; - availmemr[1].size -= DSP_SIZE; + availmemr[1].start += DSP_MEM_SIZE; + availmemr[1].size -= DSP_MEM_SIZE; /* IPC and Starlet use memory at the end of MEM2 */ availmemr[1].size -= IPC_SIZE; availmemr[1].size -= ARM_SIZE; Index: src/sys/arch/evbppc/wii/mainbus.c diff -u src/sys/arch/evbppc/wii/mainbus.c:1.1 src/sys/arch/evbppc/wii/mainbus.c:1.2 --- src/sys/arch/evbppc/wii/mainbus.c:1.1 Sat Jan 20 21:36:00 2024 +++ src/sys/arch/evbppc/wii/mainbus.c Mon Jan 22 21:28:15 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: mainbus.c,v 1.1 2024/01/20 21:36:00 jmcneill Exp $ */ +/* $NetBSD: mainbus.c,v 1.2 2024/01/22 21:28:15 jmcneill Exp $ */ /* * Copyright (c) 2002, 2024 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.1 2024/01/20 21:36:00 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.2 2024/01/22 21:28:15 jmcneill Exp $"); #include <sys/param.h> #include <sys/device.h> @@ -106,6 +106,16 @@ mainbus_attach(device_t parent, device_t maa.maa_addr = MAINBUSCF_ADDR_DEFAULT; maa.maa_irq = PI_IRQ_HOLLYWOOD; config_found(self, &maa, mainbus_print, CFARGS_NONE); + + maa.maa_name = "bwai"; + maa.maa_addr = AI_BASE; + maa.maa_irq = PI_IRQ_AI; + config_found(self, &maa, mainbus_print, CFARGS_NONE); + + maa.maa_name = "bwdsp"; + maa.maa_addr = DSP_BASE; + maa.maa_irq = PI_IRQ_DSP; + config_found(self, &maa, mainbus_print, CFARGS_NONE); } static int cpu_match(device_t, cfdata_t, void *); Added files: Index: src/sys/arch/evbppc/wii/dev/bwai.c diff -u /dev/null src/sys/arch/evbppc/wii/dev/bwai.c:1.1 --- /dev/null Mon Jan 22 21:28:15 2024 +++ src/sys/arch/evbppc/wii/dev/bwai.c Mon Jan 22 21:28:15 2024 @@ -0,0 +1,315 @@ +/* $NetBSD: bwai.c,v 1.1 2024/01/22 21:28:15 jmcneill Exp $ */ + +/*- + * Copyright (c) 2024 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: bwai.c,v 1.1 2024/01/22 21:28:15 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/cpu.h> +#include <sys/device.h> +#include <sys/kmem.h> +#include <sys/mutex.h> +#include <sys/bitops.h> + +#include <dev/audio/audio_dai.h> + +#include <machine/wii.h> + +#include "mainbus.h" +#include "bwai.h" + +#define AI_CONTROL 0x00 +#define AI_CONTROL_RATE __BIT(6) +#define AI_CONTROL_SCRESET __BIT(5) +#define AI_CONTROL_AIINTVLD __BIT(4) +#define AI_CONTROL_AIINT __BIT(3) +#define AI_CONTROL_AIINTMSK __BIT(2) +#define AI_CONTROL_AFR __BIT(1) +#define AI_CONTROL_PSTAT __BIT(0) +#define AI_AIIT 0x0c + +struct bwai_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + int sc_irq; + + struct audio_dai_device sc_dai; + + uint8_t sc_swvol; + void (*sc_intr)(void *); + void *sc_intrarg; + + kmutex_t *sc_intr_lock; +}; + +enum bwai_mixer_ctrl { + BWAI_OUTPUT_CLASS, + BWAI_INPUT_CLASS, + + BWAI_OUTPUT_MASTER_VOLUME, + BWAI_INPUT_DAC_VOLUME, + + BWAI_MIXER_CTRL_LAST +}; + +#define RD4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define WR4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +static int +bwai_intr(void *priv) +{ + struct bwai_softc * const sc = priv; + uint32_t val; + + val = RD4(sc, AI_CONTROL); + if ((val & AI_CONTROL_AIINT) != 0) { + WR4(sc, AI_CONTROL, val | AI_CONTROL_SCRESET); + + mutex_enter(sc->sc_intr_lock); + if (sc->sc_intr) { + sc->sc_intr(sc->sc_intrarg); + } + mutex_exit(sc->sc_intr_lock); + } + + return 1; +} + +audio_dai_tag_t +bwai_dsp_init(kmutex_t *intr_lock) +{ + struct bwai_softc *sc; + device_t dev; + + dev = device_find_by_driver_unit("bwai", 0); + if (dev == NULL) { + return NULL; + } + sc = device_private(dev); + + sc->sc_intr_lock = intr_lock; + + intr_establish(sc->sc_irq, IST_LEVEL, IPL_AUDIO, bwai_intr, sc); + + return &sc->sc_dai; +} + +static void +bwai_swvol_codec(audio_filter_arg_t *arg) +{ + struct bwai_softc * const sc = arg->context; + const aint_t *src; + int16_t *dst; + u_int sample_count; + u_int i; + + src = arg->src; + dst = arg->dst; + sample_count = arg->count * arg->srcfmt->channels; + for (i = 0; i < sample_count; i++) { + aint2_t v = (aint2_t)(*src++); + v = v * sc->sc_swvol / 255; + *dst++ = (aint_t)v; + } +} + +static int +bwai_set_port(void *priv, mixer_ctrl_t *mc) +{ + struct bwai_softc * const sc = priv; + + if (mc->dev != BWAI_OUTPUT_MASTER_VOLUME && + mc->dev != BWAI_INPUT_DAC_VOLUME) { + return ENXIO; + } + + sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT]; + + return 0; +} + +static int +bwai_get_port(void *priv, mixer_ctrl_t *mc) +{ + struct bwai_softc * const sc = priv; + + if (mc->dev != BWAI_OUTPUT_MASTER_VOLUME && + mc->dev != BWAI_INPUT_DAC_VOLUME) { + return ENXIO; + } + + mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->sc_swvol; + mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->sc_swvol; + + return 0; +} + +static int +bwai_query_devinfo(void *priv, mixer_devinfo_t *di) +{ + switch (di->index) { + case BWAI_OUTPUT_CLASS: + di->mixer_class = di->index; + strcpy(di->label.name, AudioCoutputs); + di->type = AUDIO_MIXER_CLASS; + di->next = di->prev = AUDIO_MIXER_LAST; + return 0; + + case BWAI_INPUT_CLASS: + di->mixer_class = di->index; + strcpy(di->label.name, AudioCinputs); + di->type = AUDIO_MIXER_CLASS; + di->next = di->prev = AUDIO_MIXER_LAST; + return 0; + + case BWAI_OUTPUT_MASTER_VOLUME: + di->mixer_class = BWAI_OUTPUT_CLASS; + strcpy(di->label.name, AudioNmaster); + di->un.v.delta = 1; + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + di->un.v.num_channels = 2; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + + case BWAI_INPUT_DAC_VOLUME: + di->mixer_class = BWAI_INPUT_CLASS; + strcpy(di->label.name, AudioNdac); + di->un.v.delta = 1; + di->type = AUDIO_MIXER_VALUE; + di->next = di->prev = AUDIO_MIXER_LAST; + di->un.v.num_channels = 2; + strcpy(di->un.v.units.name, AudioNvolume); + return 0; + } + + return ENXIO; +} + +static int +bwai_set_format(void *priv, int setmode, + const audio_params_t *play, const audio_params_t *rec, + audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) +{ + struct bwai_softc * const sc = priv; + + pfil->codec = bwai_swvol_codec; + pfil->context = sc; + + return 0; +} + +static int +bwai_trigger_output(void *priv, void *start, void *end, int blksize, + void (*intr)(void *), void *intrarg, const audio_params_t *params) +{ + struct bwai_softc * const sc = priv; + uint32_t val; + + sc->sc_intr = intr; + sc->sc_intrarg = intrarg; + + val = RD4(sc, AI_CONTROL); + if ((val & AI_CONTROL_PSTAT) != 0) { + WR4(sc, AI_CONTROL, 0); + } + + WR4(sc, AI_AIIT, blksize / 4); + + val = AI_CONTROL_SCRESET | + AI_CONTROL_AIINT | + AI_CONTROL_AIINTMSK | + AI_CONTROL_AFR; + WR4(sc, AI_CONTROL, val); + + val |= AI_CONTROL_PSTAT; + WR4(sc, AI_CONTROL, val); + + return 0; +} + +static int +bwai_halt_output(void *priv) +{ + struct bwai_softc * const sc = priv; + + WR4(sc, AI_CONTROL, 0); + + sc->sc_intr = NULL; + sc->sc_intrarg = NULL; + + return 0; +} + +static const struct audio_hw_if bwai_hw_if = { + .set_port = bwai_set_port, + .get_port = bwai_get_port, + .query_devinfo = bwai_query_devinfo, + .set_format = bwai_set_format, + .trigger_output = bwai_trigger_output, + .halt_output = bwai_halt_output, +}; + +static int +bwai_match(device_t parent, cfdata_t cf, void *aux) +{ + struct mainbus_attach_args * const maa = aux; + + return strcmp(maa->maa_name, "bwai") == 0; +} + +static void +bwai_attach(device_t parent, device_t self, void *aux) +{ + struct bwai_softc * const sc = device_private(self); + struct mainbus_attach_args * const maa = aux; + + sc->sc_dev = self; + sc->sc_bst = maa->maa_bst; + if (bus_space_map(sc->sc_bst, maa->maa_addr, AI_SIZE, 0, + &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + sc->sc_irq = maa->maa_irq; + + aprint_naive("\n"); + aprint_normal(": Audio Interface\n"); + + sc->sc_dai.dai_hw_if = &bwai_hw_if; + sc->sc_dai.dai_dev = self; + sc->sc_dai.dai_priv = sc; + sc->sc_swvol = 255; +} + +CFATTACH_DECL_NEW(bwai, sizeof(struct bwai_softc), + bwai_match, bwai_attach, NULL, NULL); Index: src/sys/arch/evbppc/wii/dev/bwai.h diff -u /dev/null src/sys/arch/evbppc/wii/dev/bwai.h:1.1 --- /dev/null Mon Jan 22 21:28:15 2024 +++ src/sys/arch/evbppc/wii/dev/bwai.h Mon Jan 22 21:28:15 2024 @@ -0,0 +1,34 @@ +/* $NetBSD: bwai.h,v 1.1 2024/01/22 21:28:15 jmcneill Exp $ */ + +/*- + * Copyright (c) 2024 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _WII_DEV_BWAI_H_ +#define _WII_DEV_BWAI_H_ + +audio_dai_tag_t bwai_dsp_init(kmutex_t *); + +#endif /* _WII_DEV_BWAI_H_ */ Index: src/sys/arch/evbppc/wii/dev/bwdsp.c diff -u /dev/null src/sys/arch/evbppc/wii/dev/bwdsp.c:1.1 --- /dev/null Mon Jan 22 21:28:15 2024 +++ src/sys/arch/evbppc/wii/dev/bwdsp.c Mon Jan 22 21:28:15 2024 @@ -0,0 +1,425 @@ +/* $NetBSD: bwdsp.c,v 1.1 2024/01/22 21:28:15 jmcneill Exp $ */ + +/*- + * Copyright (c) 2024 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: bwdsp.c,v 1.1 2024/01/22 21:28:15 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/cpu.h> +#include <sys/device.h> +#include <sys/kmem.h> + +#include <sys/audioio.h> +#include <dev/audio/audio_if.h> +#include <dev/audio/audio_dai.h> + +#include "mainbus.h" +#include "bwai.h" + +#define BWDSP_MAP_FLAGS BUS_DMA_NOCACHE + +#define DSP_CONTROL_STATUS 0x0a +#define DSP_CONTROL_STATUS_DSPINT __BIT(7) +#define DSP_CONTROL_STATUS_ARINT __BIT(5) +#define DSP_CONTROL_STATUS_AIDINTMASK __BIT(4) +#define DSP_CONTROL_STATUS_AIDINT __BIT(3) +#define DSP_CONTROL_STATUS_HALT __BIT(2) +#define DSP_CONTROL_STATUS_PIINT __BIT(1) +#define DSP_DMA_START_ADDR_H 0x30 +#define DSP_DMA_START_ADDR_L 0x32 +#define DSP_DMA_CONTROL_LENGTH 0x36 +#define DSP_DMA_CONTROL_LENGTH_CTRL __BIT(15) +#define DSP_DMA_CONTROL_LENGTH_NUM_CLS __BITS(14,0) + +#define DSP_DMA_ALIGN 32 +#define DSP_DMA_MAX_BUFSIZE (DSP_DMA_CONTROL_LENGTH_NUM_CLS * 32) + +struct bwdsp_dma { + LIST_ENTRY(bwdsp_dma) dma_list; + bus_dmamap_t dma_map; + void *dma_addr; + size_t dma_size; + bus_dma_segment_t dma_segs[1]; + int dma_nsegs; +}; + +struct bwdsp_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_dma_tag_t sc_dmat; + + LIST_HEAD(, bwdsp_dma) sc_dmalist; + + kmutex_t sc_lock; + kmutex_t sc_intr_lock; + + struct audio_format sc_format; + + audio_dai_tag_t sc_dai; +}; + +#define RD2(sc, reg) \ + bus_space_read_2((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define WR2(sc, reg, val) \ + bus_space_write_2((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +static int +bwdsp_allocdma(struct bwdsp_softc *sc, size_t size, + size_t align, struct bwdsp_dma *dma) +{ + int error; + + dma->dma_size = size; + error = bus_dmamem_alloc(sc->sc_dmat, dma->dma_size, align, 0, + dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK); + if (error) + return error; + + error = bus_dmamem_map(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs, + dma->dma_size, &dma->dma_addr, BUS_DMA_WAITOK | BWDSP_MAP_FLAGS); + if (error) + goto free; + + error = bus_dmamap_create(sc->sc_dmat, dma->dma_size, dma->dma_nsegs, + dma->dma_size, 0, BUS_DMA_WAITOK, &dma->dma_map); + if (error) + goto unmap; + + error = bus_dmamap_load(sc->sc_dmat, dma->dma_map, dma->dma_addr, + dma->dma_size, NULL, BUS_DMA_WAITOK); + if (error) + goto destroy; + + return 0; + +destroy: + bus_dmamap_destroy(sc->sc_dmat, dma->dma_map); +unmap: + bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size); +free: + bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs); + + return error; +} + +static void +bwdsp_freedma(struct bwdsp_softc *sc, struct bwdsp_dma *dma) +{ + bus_dmamap_unload(sc->sc_dmat, dma->dma_map); + bus_dmamap_destroy(sc->sc_dmat, dma->dma_map); + bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size); + bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs); +} + +static int +bwdsp_query_format(void *priv, audio_format_query_t *afp) +{ + struct bwdsp_softc * const sc = priv; + + return audio_query_format(&sc->sc_format, 1, afp); +} + +static int +bwdsp_set_format(void *priv, int setmode, + const audio_params_t *play, const audio_params_t *rec, + audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) +{ + struct bwdsp_softc * const sc = priv; + + return audio_dai_mi_set_format(sc->sc_dai, setmode, play, rec, + pfil, rfil); +} + +static int +bwdsp_set_port(void *priv, mixer_ctrl_t *mc) +{ + struct bwdsp_softc * const sc = priv; + + return audio_dai_set_port(sc->sc_dai, mc); +} + +static int +bwdsp_get_port(void *priv, mixer_ctrl_t *mc) +{ + struct bwdsp_softc * const sc = priv; + + return audio_dai_get_port(sc->sc_dai, mc); +} + +static int +bwdsp_query_devinfo(void *priv, mixer_devinfo_t *di) +{ + struct bwdsp_softc * const sc = priv; + + return audio_dai_query_devinfo(sc->sc_dai, di); +} + +static void * +bwdsp_allocm(void *priv, int dir, size_t size) +{ + struct bwdsp_softc * const sc = priv; + struct bwdsp_dma *dma; + int error; + + dma = kmem_alloc(sizeof(*dma), KM_SLEEP); + + error = bwdsp_allocdma(sc, size, DSP_DMA_ALIGN, dma); + if (error) { + kmem_free(dma, sizeof(*dma)); + device_printf(sc->sc_dev, "couldn't allocate DMA memory (%d)\n", + error); + return NULL; + } + + LIST_INSERT_HEAD(&sc->sc_dmalist, dma, dma_list); + + return dma->dma_addr; +} + +static void +bwdsp_freem(void *priv, void *addr, size_t size) +{ + struct bwdsp_softc * const sc = priv; + struct bwdsp_dma *dma; + + LIST_FOREACH(dma, &sc->sc_dmalist, dma_list) + if (dma->dma_addr == addr) { + bwdsp_freedma(sc, dma); + LIST_REMOVE(dma, dma_list); + kmem_free(dma, sizeof(*dma)); + break; + } +} + +static int +bwdsp_getdev(void *priv, struct audio_device *adev) +{ + snprintf(adev->name, sizeof(adev->name), "Broadway DSP"); + snprintf(adev->version, sizeof(adev->version), ""); + snprintf(adev->config, sizeof(adev->config), "bwdsp"); + + return 0; +} + +static int +bwdsp_get_props(void *priv) +{ + return AUDIO_PROP_PLAYBACK; +} + +static int +bwdsp_round_blocksize(void *priv, int bs, int mode, + const audio_params_t *params) +{ + bs = roundup(bs, DSP_DMA_ALIGN); + if (bs > DSP_DMA_MAX_BUFSIZE) { + bs = DSP_DMA_MAX_BUFSIZE; + } + return bs; +} + +static size_t +bwdsp_round_buffersize(void *priv, int dir, size_t bufsize) +{ + if (bufsize > DSP_DMA_MAX_BUFSIZE) { + bufsize = DSP_DMA_MAX_BUFSIZE; + } + return bufsize; +} + +static void +bwdsp_transfer(struct bwdsp_softc *sc, uint32_t phys_addr, size_t bufsize) +{ + if (bufsize != 0) { + WR2(sc, DSP_DMA_START_ADDR_H, phys_addr >> 16); + WR2(sc, DSP_DMA_START_ADDR_L, phys_addr & 0xffff); + WR2(sc, DSP_DMA_CONTROL_LENGTH, + DSP_DMA_CONTROL_LENGTH_CTRL | (bufsize / 32)); + } else { + WR2(sc, DSP_DMA_CONTROL_LENGTH, 0); + } +} + +static int +bwdsp_trigger_output(void *priv, void *start, void *end, int blksize, + void (*intr)(void *), void *intrarg, const audio_params_t *params) +{ + struct bwdsp_softc * const sc = priv; + struct bwdsp_dma *dma; + bus_addr_t pstart; + bus_size_t psize; + int error; + + pstart = 0; + psize = (uintptr_t)end - (uintptr_t)start; + + LIST_FOREACH(dma, &sc->sc_dmalist, dma_list) + if (dma->dma_addr == start) { + pstart = dma->dma_map->dm_segs[0].ds_addr; + break; + } + if (pstart == 0) { + device_printf(sc->sc_dev, "bad addr %p\n", start); + return EINVAL; + } + + error = audio_dai_trigger(sc->sc_dai, start, end, blksize, + intr, intrarg, params, AUMODE_PLAY); + if (error != 0) { + return error; + } + + /* Start DMA transfer */ + bwdsp_transfer(sc, pstart, psize); + + return 0; +} + +static int +bwdsp_halt_output(void *priv) +{ + struct bwdsp_softc * const sc = priv; + + /* Stop DMA transfer */ + bwdsp_transfer(sc, 0, 0); + + return audio_dai_halt(sc->sc_dai, AUMODE_PLAY); +} + +static void +bwdsp_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread) +{ + struct bwdsp_softc * const sc = priv; + + *intr = &sc->sc_intr_lock; + *thread = &sc->sc_lock; +} + +static const struct audio_hw_if bwdsp_hw_if = { + .query_format = bwdsp_query_format, + .set_format = bwdsp_set_format, + .allocm = bwdsp_allocm, + .freem = bwdsp_freem, + .getdev = bwdsp_getdev, + .set_port = bwdsp_set_port, + .get_port = bwdsp_get_port, + .query_devinfo = bwdsp_query_devinfo, + .get_props = bwdsp_get_props, + .round_blocksize = bwdsp_round_blocksize, + .round_buffersize = bwdsp_round_buffersize, + .trigger_output = bwdsp_trigger_output, + .halt_output = bwdsp_halt_output, + .get_locks = bwdsp_get_locks, +}; + +static int +bwdsp_intr(void *priv) +{ + struct bwdsp_softc * const sc = priv; + uint16_t val; + + val = RD2(sc, DSP_CONTROL_STATUS); + if ((val & DSP_CONTROL_STATUS_AIDINT) != 0) { + /* Acknowledge audio interrupt */ + val &= ~(DSP_CONTROL_STATUS_DSPINT | + DSP_CONTROL_STATUS_ARINT | + DSP_CONTROL_STATUS_PIINT); + WR2(sc, DSP_CONTROL_STATUS, val); + } + + return 1; +} + +static void +bwdsp_late_attach(device_t dev) +{ + struct bwdsp_softc * const sc = device_private(dev); + + sc->sc_dai = bwai_dsp_init(&sc->sc_intr_lock); + if (sc->sc_dai == NULL) { + aprint_error_dev(dev, "can't find bwai device\n"); + return; + } + + audio_attach_mi(&bwdsp_hw_if, sc, dev); +} + +static int +bwdsp_match(device_t parent, cfdata_t cf, void *aux) +{ + struct mainbus_attach_args * const maa = aux; + + return strcmp(maa->maa_name, "bwdsp") == 0; +} + +static void +bwdsp_attach(device_t parent, device_t self, void *aux) +{ + struct bwdsp_softc * const sc = device_private(self); + struct mainbus_attach_args * const maa = aux; + bus_addr_t addr = maa->maa_addr; + bus_size_t size = 0x200; + uint16_t val; + + sc->sc_dev = self; + sc->sc_bst = maa->maa_bst; + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + sc->sc_dmat = maa->maa_dmat; + LIST_INIT(&sc->sc_dmalist); + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); + + aprint_naive("\n"); + aprint_normal(": DSP\n"); + + sc->sc_format.mode = AUMODE_PLAY; + sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_BE; + sc->sc_format.validbits = 16; + sc->sc_format.precision = 16; + sc->sc_format.channels = 2; + sc->sc_format.channel_mask = AUFMT_STEREO; + sc->sc_format.frequency_type = 1; + sc->sc_format.frequency[0] = 48000; + + val = RD2(sc, DSP_CONTROL_STATUS); + val |= DSP_CONTROL_STATUS_AIDINTMASK; + val |= DSP_CONTROL_STATUS_PIINT; + WR2(sc, DSP_CONTROL_STATUS, val); + + intr_establish(maa->maa_irq, IST_LEVEL, IPL_AUDIO, bwdsp_intr, sc); + + config_defer(self, bwdsp_late_attach); +} + +CFATTACH_DECL_NEW(bwdsp, sizeof(struct bwdsp_softc), + bwdsp_match, bwdsp_attach, NULL, NULL);