bwai.c revision 1.1
11.1Sjmcneill/* $NetBSD: bwai.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 2024 Jared McNeill <jmcneill@invisible.ca> 51.1Sjmcneill * All rights reserved. 61.1Sjmcneill * 71.1Sjmcneill * Redistribution and use in source and binary forms, with or without 81.1Sjmcneill * modification, are permitted provided that the following conditions 91.1Sjmcneill * are met: 101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright 111.1Sjmcneill * notice, this list of conditions and the following disclaimer. 121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 131.1Sjmcneill * notice, this list of conditions and the following disclaimer in the 141.1Sjmcneill * documentation and/or other materials provided with the distribution. 151.1Sjmcneill * 161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 171.1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 181.1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 191.1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 201.1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 211.1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 221.1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 231.1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 241.1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261.1Sjmcneill * SUCH DAMAGE. 271.1Sjmcneill */ 281.1Sjmcneill 291.1Sjmcneill#include <sys/cdefs.h> 301.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: bwai.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $"); 311.1Sjmcneill 321.1Sjmcneill#include <sys/param.h> 331.1Sjmcneill#include <sys/bus.h> 341.1Sjmcneill#include <sys/cpu.h> 351.1Sjmcneill#include <sys/device.h> 361.1Sjmcneill#include <sys/kmem.h> 371.1Sjmcneill#include <sys/mutex.h> 381.1Sjmcneill#include <sys/bitops.h> 391.1Sjmcneill 401.1Sjmcneill#include <dev/audio/audio_dai.h> 411.1Sjmcneill 421.1Sjmcneill#include <machine/wii.h> 431.1Sjmcneill 441.1Sjmcneill#include "mainbus.h" 451.1Sjmcneill#include "avenc.h" 461.1Sjmcneill#include "bwai.h" 471.1Sjmcneill 481.1Sjmcneill#define AI_CONTROL 0x00 491.1Sjmcneill#define AI_CONTROL_RATE __BIT(6) 501.1Sjmcneill#define AI_CONTROL_SCRESET __BIT(5) 511.1Sjmcneill#define AI_CONTROL_AIINTVLD __BIT(4) 521.1Sjmcneill#define AI_CONTROL_AIINT __BIT(3) 531.1Sjmcneill#define AI_CONTROL_AIINTMSK __BIT(2) 541.1Sjmcneill#define AI_CONTROL_AFR __BIT(1) 551.1Sjmcneill#define AI_CONTROL_PSTAT __BIT(0) 561.1Sjmcneill#define AI_AIIT 0x0c 571.1Sjmcneill 581.1Sjmcneillstruct bwai_softc { 591.1Sjmcneill device_t sc_dev; 601.1Sjmcneill bus_space_tag_t sc_bst; 611.1Sjmcneill bus_space_handle_t sc_bsh; 621.1Sjmcneill int sc_irq; 631.1Sjmcneill 641.1Sjmcneill struct audio_dai_device sc_dai; 651.1Sjmcneill 661.1Sjmcneill void (*sc_intr)(void *); 671.1Sjmcneill void *sc_intrarg; 681.1Sjmcneill uint32_t sc_intrnext; 691.1Sjmcneill uint32_t sc_intrstep; 701.1Sjmcneill 711.1Sjmcneill kmutex_t *sc_intr_lock; 721.1Sjmcneill}; 731.1Sjmcneill 741.1Sjmcneillenum bwai_mixer_ctrl { 751.1Sjmcneill BWAI_OUTPUT_CLASS, 761.1Sjmcneill BWAI_INPUT_CLASS, 771.1Sjmcneill 781.1Sjmcneill BWAI_OUTPUT_MASTER_VOLUME, 791.1Sjmcneill BWAI_INPUT_DAC_VOLUME, 801.1Sjmcneill 811.1Sjmcneill BWAI_MIXER_CTRL_LAST 821.1Sjmcneill}; 831.1Sjmcneill 841.1Sjmcneill#define RD4(sc, reg) \ 851.1Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 861.1Sjmcneill#define WR4(sc, reg, val) \ 871.1Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 881.1Sjmcneill 891.1Sjmcneillstatic int 901.1Sjmcneillbwai_intr(void *priv) 911.1Sjmcneill{ 921.1Sjmcneill struct bwai_softc * const sc = priv; 931.1Sjmcneill uint32_t val; 941.1Sjmcneill 951.1Sjmcneill val = RD4(sc, AI_CONTROL); 961.1Sjmcneill if ((val & AI_CONTROL_AIINT) != 0) { 971.1Sjmcneill sc->sc_intrnext += sc->sc_intrstep; 981.1Sjmcneill WR4(sc, AI_AIIT, sc->sc_intrnext); 991.1Sjmcneill WR4(sc, AI_CONTROL, val); 1001.1Sjmcneill 1011.1Sjmcneill mutex_enter(sc->sc_intr_lock); 1021.1Sjmcneill if (sc->sc_intr) { 1031.1Sjmcneill sc->sc_intr(sc->sc_intrarg); 1041.1Sjmcneill } 1051.1Sjmcneill mutex_exit(sc->sc_intr_lock); 1061.1Sjmcneill } 1071.1Sjmcneill 1081.1Sjmcneill return 1; 1091.1Sjmcneill} 1101.1Sjmcneill 1111.1Sjmcneillaudio_dai_tag_t 1121.1Sjmcneillbwai_dsp_init(kmutex_t *intr_lock) 1131.1Sjmcneill{ 1141.1Sjmcneill struct bwai_softc *sc; 1151.1Sjmcneill device_t dev; 1161.1Sjmcneill 1171.1Sjmcneill dev = device_find_by_driver_unit("bwai", 0); 1181.1Sjmcneill if (dev == NULL) { 1191.1Sjmcneill return NULL; 1201.1Sjmcneill } 1211.1Sjmcneill sc = device_private(dev); 1221.1Sjmcneill 1231.1Sjmcneill sc->sc_intr_lock = intr_lock; 1241.1Sjmcneill 1251.1Sjmcneill intr_establish_xname(sc->sc_irq, IST_LEVEL, IPL_AUDIO, bwai_intr, sc, 1261.1Sjmcneill device_xname(dev)); 1271.1Sjmcneill 1281.1Sjmcneill return &sc->sc_dai; 1291.1Sjmcneill} 1301.1Sjmcneill 1311.1Sjmcneillstatic int 1321.1Sjmcneillbwai_set_port(void *priv, mixer_ctrl_t *mc) 1331.1Sjmcneill{ 1341.1Sjmcneill if (mc->dev != BWAI_OUTPUT_MASTER_VOLUME && 1351.1Sjmcneill mc->dev != BWAI_INPUT_DAC_VOLUME) { 1361.1Sjmcneill return ENXIO; 1371.1Sjmcneill } 1381.1Sjmcneill 1391.1Sjmcneill avenc_set_volume(mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT], 1401.1Sjmcneill mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]); 1411.1Sjmcneill 1421.1Sjmcneill return 0; 1431.1Sjmcneill} 1441.1Sjmcneill 1451.1Sjmcneillstatic int 1461.1Sjmcneillbwai_get_port(void *priv, mixer_ctrl_t *mc) 1471.1Sjmcneill{ 1481.1Sjmcneill if (mc->dev != BWAI_OUTPUT_MASTER_VOLUME && 1491.1Sjmcneill mc->dev != BWAI_INPUT_DAC_VOLUME) { 1501.1Sjmcneill return ENXIO; 1511.1Sjmcneill } 1521.1Sjmcneill 1531.1Sjmcneill avenc_get_volume(&mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT], 1541.1Sjmcneill &mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]); 1551.1Sjmcneill 1561.1Sjmcneill return 0; 1571.1Sjmcneill} 1581.1Sjmcneill 1591.1Sjmcneillstatic int 1601.1Sjmcneillbwai_query_devinfo(void *priv, mixer_devinfo_t *di) 1611.1Sjmcneill{ 1621.1Sjmcneill switch (di->index) { 1631.1Sjmcneill case BWAI_OUTPUT_CLASS: 1641.1Sjmcneill di->mixer_class = di->index; 1651.1Sjmcneill strcpy(di->label.name, AudioCoutputs); 1661.1Sjmcneill di->type = AUDIO_MIXER_CLASS; 1671.1Sjmcneill di->next = di->prev = AUDIO_MIXER_LAST; 1681.1Sjmcneill return 0; 1691.1Sjmcneill 1701.1Sjmcneill case BWAI_INPUT_CLASS: 1711.1Sjmcneill di->mixer_class = di->index; 1721.1Sjmcneill strcpy(di->label.name, AudioCinputs); 1731.1Sjmcneill di->type = AUDIO_MIXER_CLASS; 1741.1Sjmcneill di->next = di->prev = AUDIO_MIXER_LAST; 1751.1Sjmcneill return 0; 1761.1Sjmcneill 1771.1Sjmcneill case BWAI_OUTPUT_MASTER_VOLUME: 1781.1Sjmcneill di->mixer_class = BWAI_OUTPUT_CLASS; 1791.1Sjmcneill strcpy(di->label.name, AudioNmaster); 1801.1Sjmcneill di->un.v.delta = 1; 1811.1Sjmcneill di->type = AUDIO_MIXER_VALUE; 1821.1Sjmcneill di->next = di->prev = AUDIO_MIXER_LAST; 1831.1Sjmcneill di->un.v.num_channels = 2; 1841.1Sjmcneill strcpy(di->un.v.units.name, AudioNvolume); 1851.1Sjmcneill return 0; 1861.1Sjmcneill 1871.1Sjmcneill case BWAI_INPUT_DAC_VOLUME: 1881.1Sjmcneill di->mixer_class = BWAI_INPUT_CLASS; 1891.1Sjmcneill strcpy(di->label.name, AudioNdac); 1901.1Sjmcneill di->un.v.delta = 1; 1911.1Sjmcneill di->type = AUDIO_MIXER_VALUE; 1921.1Sjmcneill di->next = di->prev = AUDIO_MIXER_LAST; 1931.1Sjmcneill di->un.v.num_channels = 2; 1941.1Sjmcneill strcpy(di->un.v.units.name, AudioNvolume); 1951.1Sjmcneill return 0; 1961.1Sjmcneill } 1971.1Sjmcneill 1981.1Sjmcneill return ENXIO; 1991.1Sjmcneill} 2001.1Sjmcneill 2011.1Sjmcneillstatic int 2021.1Sjmcneillbwai_set_format(void *priv, int setmode, 2031.1Sjmcneill const audio_params_t *play, const audio_params_t *rec, 2041.1Sjmcneill audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 2051.1Sjmcneill{ 2061.1Sjmcneill return 0; 2071.1Sjmcneill} 2081.1Sjmcneill 2091.1Sjmcneillstatic int 2101.1Sjmcneillbwai_trigger_output(void *priv, void *start, void *end, int blksize, 2111.1Sjmcneill void (*intr)(void *), void *intrarg, const audio_params_t *params) 2121.1Sjmcneill{ 2131.1Sjmcneill struct bwai_softc * const sc = priv; 2141.1Sjmcneill uint32_t val; 2151.1Sjmcneill 2161.1Sjmcneill sc->sc_intr = intr; 2171.1Sjmcneill sc->sc_intrarg = intrarg; 2181.1Sjmcneill 2191.1Sjmcneill val = RD4(sc, AI_CONTROL); 2201.1Sjmcneill if ((val & AI_CONTROL_PSTAT) != 0) { 2211.1Sjmcneill WR4(sc, AI_CONTROL, 0); 2221.1Sjmcneill } 2231.1Sjmcneill 2241.1Sjmcneill sc->sc_intrstep = blksize / 4; 2251.1Sjmcneill sc->sc_intrnext = sc->sc_intrstep; 2261.1Sjmcneill WR4(sc, AI_AIIT, sc->sc_intrnext); 2271.1Sjmcneill 2281.1Sjmcneill val = AI_CONTROL_SCRESET | 2291.1Sjmcneill AI_CONTROL_AIINT | 2301.1Sjmcneill AI_CONTROL_AIINTMSK | 2311.1Sjmcneill AI_CONTROL_AFR; 2321.1Sjmcneill WR4(sc, AI_CONTROL, val); 2331.1Sjmcneill 2341.1Sjmcneill val |= AI_CONTROL_PSTAT; 2351.1Sjmcneill WR4(sc, AI_CONTROL, val); 2361.1Sjmcneill 2371.1Sjmcneill return 0; 2381.1Sjmcneill} 2391.1Sjmcneill 2401.1Sjmcneillstatic int 2411.1Sjmcneillbwai_halt_output(void *priv) 2421.1Sjmcneill{ 2431.1Sjmcneill struct bwai_softc * const sc = priv; 2441.1Sjmcneill 2451.1Sjmcneill WR4(sc, AI_CONTROL, 0); 2461.1Sjmcneill 2471.1Sjmcneill sc->sc_intr = NULL; 2481.1Sjmcneill sc->sc_intrarg = NULL; 2491.1Sjmcneill 2501.1Sjmcneill return 0; 2511.1Sjmcneill} 2521.1Sjmcneill 2531.1Sjmcneillstatic const struct audio_hw_if bwai_hw_if = { 2541.1Sjmcneill .set_port = bwai_set_port, 2551.1Sjmcneill .get_port = bwai_get_port, 2561.1Sjmcneill .query_devinfo = bwai_query_devinfo, 2571.1Sjmcneill .set_format = bwai_set_format, 2581.1Sjmcneill .trigger_output = bwai_trigger_output, 2591.1Sjmcneill .halt_output = bwai_halt_output, 2601.1Sjmcneill}; 2611.1Sjmcneill 2621.1Sjmcneillstatic int 2631.1Sjmcneillbwai_match(device_t parent, cfdata_t cf, void *aux) 2641.1Sjmcneill{ 2651.1Sjmcneill struct mainbus_attach_args * const maa = aux; 2661.1Sjmcneill 2671.1Sjmcneill return strcmp(maa->maa_name, "bwai") == 0; 2681.1Sjmcneill} 2691.1Sjmcneill 2701.1Sjmcneillstatic void 2711.1Sjmcneillbwai_attach(device_t parent, device_t self, void *aux) 2721.1Sjmcneill{ 2731.1Sjmcneill struct bwai_softc * const sc = device_private(self); 2741.1Sjmcneill struct mainbus_attach_args * const maa = aux; 2751.1Sjmcneill 2761.1Sjmcneill sc->sc_dev = self; 2771.1Sjmcneill sc->sc_bst = maa->maa_bst; 2781.1Sjmcneill if (bus_space_map(sc->sc_bst, maa->maa_addr, AI_SIZE, 0, 2791.1Sjmcneill &sc->sc_bsh) != 0) { 2801.1Sjmcneill aprint_error(": couldn't map registers\n"); 2811.1Sjmcneill return; 2821.1Sjmcneill } 2831.1Sjmcneill sc->sc_irq = maa->maa_irq; 2841.1Sjmcneill 2851.1Sjmcneill aprint_naive("\n"); 2861.1Sjmcneill aprint_normal(": Audio Interface\n"); 2871.1Sjmcneill 2881.1Sjmcneill sc->sc_dai.dai_hw_if = &bwai_hw_if; 2891.1Sjmcneill sc->sc_dai.dai_dev = self; 2901.1Sjmcneill sc->sc_dai.dai_priv = sc; 2911.1Sjmcneill} 2921.1Sjmcneill 2931.1SjmcneillCFATTACH_DECL_NEW(bwai, sizeof(struct bwai_softc), 2941.1Sjmcneill bwai_match, bwai_attach, NULL, NULL); 295