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