1 /* $NetBSD: bwai.c,v 1.4 2025/02/15 00:35:18 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2024 Jared McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: bwai.c,v 1.4 2025/02/15 00:35:18 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/cpu.h> 35 #include <sys/device.h> 36 #include <sys/kmem.h> 37 #include <sys/mutex.h> 38 #include <sys/bitops.h> 39 40 #include <dev/audio/audio_dai.h> 41 42 #include <machine/wii.h> 43 44 #include "mainbus.h" 45 #include "avenc.h" 46 #include "bwai.h" 47 48 #define AI_CONTROL 0x00 49 #define AI_CONTROL_RATE __BIT(6) 50 #define AI_CONTROL_SCRESET __BIT(5) 51 #define AI_CONTROL_AIINTVLD __BIT(4) 52 #define AI_CONTROL_AIINT __BIT(3) 53 #define AI_CONTROL_AIINTMSK __BIT(2) 54 #define AI_CONTROL_AFR __BIT(1) 55 #define AI_CONTROL_PSTAT __BIT(0) 56 #define AI_AIIT 0x0c 57 58 struct bwai_softc { 59 device_t sc_dev; 60 bus_space_tag_t sc_bst; 61 bus_space_handle_t sc_bsh; 62 int sc_irq; 63 64 struct audio_dai_device sc_dai; 65 66 void (*sc_intr)(void *); 67 void *sc_intrarg; 68 uint32_t sc_intrnext; 69 uint32_t sc_intrstep; 70 71 kmutex_t *sc_intr_lock; 72 }; 73 74 enum bwai_mixer_ctrl { 75 BWAI_OUTPUT_CLASS, 76 BWAI_INPUT_CLASS, 77 78 BWAI_OUTPUT_MASTER_VOLUME, 79 BWAI_INPUT_DAC_VOLUME, 80 81 BWAI_MIXER_CTRL_LAST 82 }; 83 84 #define RD4(sc, reg) \ 85 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 86 #define WR4(sc, reg, val) \ 87 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 88 89 static int 90 bwai_intr(void *priv) 91 { 92 struct bwai_softc * const sc = priv; 93 uint32_t val; 94 95 val = RD4(sc, AI_CONTROL); 96 if ((val & AI_CONTROL_AIINT) != 0) { 97 sc->sc_intrnext += sc->sc_intrstep; 98 WR4(sc, AI_AIIT, sc->sc_intrnext); 99 WR4(sc, AI_CONTROL, val); 100 101 mutex_enter(sc->sc_intr_lock); 102 if (sc->sc_intr) { 103 sc->sc_intr(sc->sc_intrarg); 104 } 105 mutex_exit(sc->sc_intr_lock); 106 } 107 108 return 1; 109 } 110 111 audio_dai_tag_t 112 bwai_dsp_init(kmutex_t *intr_lock) 113 { 114 struct bwai_softc *sc; 115 device_t dev; 116 117 dev = device_find_by_driver_unit("bwai", 0); 118 if (dev == NULL) { 119 return NULL; 120 } 121 sc = device_private(dev); 122 123 sc->sc_intr_lock = intr_lock; 124 125 intr_establish_xname(sc->sc_irq, IST_LEVEL, IPL_AUDIO, bwai_intr, sc, 126 device_xname(dev)); 127 128 return &sc->sc_dai; 129 } 130 131 static int 132 bwai_set_port(void *priv, mixer_ctrl_t *mc) 133 { 134 if (mc->dev != BWAI_OUTPUT_MASTER_VOLUME && 135 mc->dev != BWAI_INPUT_DAC_VOLUME) { 136 return ENXIO; 137 } 138 139 avenc_set_volume(mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT], 140 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]); 141 142 return 0; 143 } 144 145 static int 146 bwai_get_port(void *priv, mixer_ctrl_t *mc) 147 { 148 if (mc->dev != BWAI_OUTPUT_MASTER_VOLUME && 149 mc->dev != BWAI_INPUT_DAC_VOLUME) { 150 return ENXIO; 151 } 152 153 avenc_get_volume(&mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT], 154 &mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]); 155 156 return 0; 157 } 158 159 static int 160 bwai_query_devinfo(void *priv, mixer_devinfo_t *di) 161 { 162 switch (di->index) { 163 case BWAI_OUTPUT_CLASS: 164 di->mixer_class = di->index; 165 strcpy(di->label.name, AudioCoutputs); 166 di->type = AUDIO_MIXER_CLASS; 167 di->next = di->prev = AUDIO_MIXER_LAST; 168 return 0; 169 170 case BWAI_INPUT_CLASS: 171 di->mixer_class = di->index; 172 strcpy(di->label.name, AudioCinputs); 173 di->type = AUDIO_MIXER_CLASS; 174 di->next = di->prev = AUDIO_MIXER_LAST; 175 return 0; 176 177 case BWAI_OUTPUT_MASTER_VOLUME: 178 di->mixer_class = BWAI_OUTPUT_CLASS; 179 strcpy(di->label.name, AudioNmaster); 180 di->un.v.delta = 1; 181 di->type = AUDIO_MIXER_VALUE; 182 di->next = di->prev = AUDIO_MIXER_LAST; 183 di->un.v.num_channels = 2; 184 strcpy(di->un.v.units.name, AudioNvolume); 185 return 0; 186 187 case BWAI_INPUT_DAC_VOLUME: 188 di->mixer_class = BWAI_INPUT_CLASS; 189 strcpy(di->label.name, AudioNdac); 190 di->un.v.delta = 1; 191 di->type = AUDIO_MIXER_VALUE; 192 di->next = di->prev = AUDIO_MIXER_LAST; 193 di->un.v.num_channels = 2; 194 strcpy(di->un.v.units.name, AudioNvolume); 195 return 0; 196 } 197 198 return ENXIO; 199 } 200 201 static int 202 bwai_set_format(void *priv, int setmode, 203 const audio_params_t *play, const audio_params_t *rec, 204 audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 205 { 206 return 0; 207 } 208 209 static int 210 bwai_trigger_output(void *priv, void *start, void *end, int blksize, 211 void (*intr)(void *), void *intrarg, const audio_params_t *params) 212 { 213 struct bwai_softc * const sc = priv; 214 uint32_t val; 215 216 sc->sc_intr = intr; 217 sc->sc_intrarg = intrarg; 218 219 val = RD4(sc, AI_CONTROL); 220 if ((val & AI_CONTROL_PSTAT) != 0) { 221 WR4(sc, AI_CONTROL, 0); 222 } 223 224 sc->sc_intrstep = blksize / 4; 225 sc->sc_intrnext = sc->sc_intrstep; 226 WR4(sc, AI_AIIT, sc->sc_intrnext); 227 228 val = AI_CONTROL_SCRESET | 229 AI_CONTROL_AIINT | 230 AI_CONTROL_AIINTMSK | 231 AI_CONTROL_AFR; 232 WR4(sc, AI_CONTROL, val); 233 234 val |= AI_CONTROL_PSTAT; 235 WR4(sc, AI_CONTROL, val); 236 237 return 0; 238 } 239 240 static int 241 bwai_halt_output(void *priv) 242 { 243 struct bwai_softc * const sc = priv; 244 245 WR4(sc, AI_CONTROL, 0); 246 247 sc->sc_intr = NULL; 248 sc->sc_intrarg = NULL; 249 250 return 0; 251 } 252 253 static const struct audio_hw_if bwai_hw_if = { 254 .set_port = bwai_set_port, 255 .get_port = bwai_get_port, 256 .query_devinfo = bwai_query_devinfo, 257 .set_format = bwai_set_format, 258 .trigger_output = bwai_trigger_output, 259 .halt_output = bwai_halt_output, 260 }; 261 262 static int 263 bwai_match(device_t parent, cfdata_t cf, void *aux) 264 { 265 struct mainbus_attach_args * const maa = aux; 266 267 return strcmp(maa->maa_name, "bwai") == 0; 268 } 269 270 static void 271 bwai_attach(device_t parent, device_t self, void *aux) 272 { 273 struct bwai_softc * const sc = device_private(self); 274 struct mainbus_attach_args * const maa = aux; 275 276 sc->sc_dev = self; 277 sc->sc_bst = maa->maa_bst; 278 if (bus_space_map(sc->sc_bst, maa->maa_addr, AI_SIZE, 0, 279 &sc->sc_bsh) != 0) { 280 aprint_error(": couldn't map registers\n"); 281 return; 282 } 283 sc->sc_irq = maa->maa_irq; 284 285 aprint_naive("\n"); 286 aprint_normal(": Audio Interface\n"); 287 288 sc->sc_dai.dai_hw_if = &bwai_hw_if; 289 sc->sc_dai.dai_dev = self; 290 sc->sc_dai.dai_priv = sc; 291 } 292 293 CFATTACH_DECL_NEW(bwai, sizeof(struct bwai_softc), 294 bwai_match, bwai_attach, NULL, NULL); 295