From 12e50ba5198eebf4c4be9d62bc2820700eb9f93f Mon Sep 17 00:00:00 2001 From: kettenis Date: Fri, 2 Sep 2022 17:54:42 +0000 Subject: [PATCH] First attempt at supporting audio on machines with multiple speakers. Probably needs more work as the device tree bindings evolve. Note that speakers are currently disabled in the device tree for all Apple Silicon machines except for the Mac mini. --- sys/arch/arm64/dev/aplaudio.c | 238 +++++++++++++++++++++++----------- sys/arch/arm64/dev/aplmca.c | 45 +++++-- 2 files changed, 198 insertions(+), 85 deletions(-) diff --git a/sys/arch/arm64/dev/aplaudio.c b/sys/arch/arm64/dev/aplaudio.c index 7090b2ce114..2e06448a882 100644 --- a/sys/arch/arm64/dev/aplaudio.c +++ b/sys/arch/arm64/dev/aplaudio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aplaudio.c,v 1.2 2022/08/05 13:25:43 kettenis Exp $ */ +/* $OpenBSD: aplaudio.c,v 1.3 2022/09/02 17:54:42 kettenis Exp $ */ /* * Copyright (c) 2022 Mark Kettenis * Copyright (c) 2020 Patrick Wildt @@ -39,11 +39,12 @@ struct aplaudio_softc { uint32_t sc_mclk_fs; struct dai_device *sc_dai_cpu; - struct dai_device *sc_dai_codec; + struct dai_device *sc_dai_codec[6]; }; void aplaudio_set_format(struct aplaudio_softc *, uint32_t, uint32_t, uint32_t); +void aplaudio_set_tdm_slots(struct aplaudio_softc *); int aplaudio_open(void *, int); void aplaudio_close(void *); @@ -108,8 +109,11 @@ aplaudio_attach(struct device *parent, struct device *self, void *aux) struct fdt_attach_args *faa = aux; uint32_t fmt, pol, clk; uint32_t node, cpu, codec; - uint32_t phandle; + uint32_t *dais; char status[32]; + int count = 0; + int i, ncells; + int len; printf("\n"); @@ -130,13 +134,28 @@ aplaudio_attach(struct device *parent, struct device *self, void *aux) if (codec == 0) continue; - phandle = 0; - OF_getpropintarray(codec, "sound-dai", - &phandle, sizeof(phandle)); + len = OF_getproplen(codec, "sound-dai"); + if (len < 0) + continue; + + dais = malloc(len, M_TEMP, M_WAITOK); + OF_getpropintarray(codec, "sound-dai", dais, len); + + ncells = len / sizeof(uint32_t); + ncells = MIN(ncells, nitems(sc->sc_dai_codec)); + for (i = 0; i < ncells; i++) { + sc->sc_dai_codec[i] = dai_byphandle(dais[i]); + if (sc->sc_dai_codec[i] == NULL) + continue; + count++; + } + + free(dais, M_TEMP, len); - sc->sc_dai_codec = dai_byphandle(phandle); - if (sc->sc_dai_codec == NULL) + if (count == 0) continue; + if (count > 1) + aplaudio_set_tdm_slots(sc); sc->sc_mclk_fs = OF_getpropint(node, "mclk-fs", 0); @@ -157,12 +176,34 @@ void aplaudio_set_format(struct aplaudio_softc *sc, uint32_t fmt, uint32_t pol, uint32_t clk) { + struct dai_device *dai; + int i; + if (sc->sc_dai_cpu->dd_set_format) sc->sc_dai_cpu->dd_set_format(sc->sc_dai_cpu->dd_cookie, fmt, pol, clk); - if (sc->sc_dai_codec->dd_set_format) - sc->sc_dai_codec->dd_set_format(sc->sc_dai_codec->dd_cookie, - fmt, pol, clk); + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + if (dai->dd_set_format) + dai->dd_set_format(dai->dd_cookie, fmt, pol, clk); + } +} + +void +aplaudio_set_tdm_slots(struct aplaudio_softc *sc) +{ + struct dai_device *dai; + int i; + + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + if (dai->dd_set_tdm_slot) + dai->dd_set_tdm_slot(dai->dd_cookie, i % 2); + } } int @@ -172,6 +213,7 @@ aplaudio_open(void *cookie, int flags) struct dai_device *dai; const struct audio_hw_if *hwif; int error; + int i; dai = sc->sc_dai_cpu; hwif = dai->dd_hw_if; @@ -183,13 +225,17 @@ aplaudio_open(void *cookie, int flags) } } - dai = sc->sc_dai_codec; - hwif = dai->dd_hw_if; - if (hwif->open) { - error = hwif->open(dai->dd_cookie, flags); - if (error) { - aplaudio_close(cookie); - return error; + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + hwif = dai->dd_hw_if; + if (hwif->open) { + error = hwif->open(dai->dd_cookie, flags); + if (error) { + aplaudio_close(cookie); + return error; + } } } @@ -202,11 +248,16 @@ aplaudio_close(void *cookie) struct aplaudio_softc *sc = cookie; struct dai_device *dai; const struct audio_hw_if *hwif; + int i; - dai = sc->sc_dai_codec; - hwif = dai->dd_hw_if; - if (hwif->close) - hwif->close(dai->dd_cookie); + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + hwif = dai->dd_hw_if; + if (hwif->close) + hwif->close(dai->dd_cookie); + } dai = sc->sc_dai_cpu; hwif = dai->dd_hw_if; @@ -223,6 +274,7 @@ aplaudio_set_params(void *cookie, int setmode, int usemode, const struct audio_hw_if *hwif; uint32_t rate; int error; + int i; if (sc->sc_mclk_fs) { if (setmode & AUMODE_PLAY) @@ -230,11 +282,15 @@ aplaudio_set_params(void *cookie, int setmode, int usemode, else rate = rec->sample_rate * sc->sc_mclk_fs; - dai = sc->sc_dai_codec; - if (dai->dd_set_sysclk) { - error = dai->dd_set_sysclk(dai->dd_cookie, rate); - if (error) - return error; + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + if (dai->dd_set_sysclk) { + error = dai->dd_set_sysclk(dai->dd_cookie, rate); + if (error) + return error; + } } dai = sc->sc_dai_cpu; @@ -254,15 +310,6 @@ aplaudio_set_params(void *cookie, int setmode, int usemode, return error; } - dai = sc->sc_dai_codec; - hwif = dai->dd_hw_if; - if (hwif->set_params) { - error = hwif->set_params(dai->dd_cookie, - setmode, usemode, play, rec); - if (error) - return error; - } - return 0; } @@ -296,37 +343,60 @@ int aplaudio_set_port(void *cookie, mixer_ctrl_t *cp) { struct aplaudio_softc *sc = cookie; - struct dai_device *dai = sc->sc_dai_codec; - const struct audio_hw_if *hwif = dai->dd_hw_if; + struct dai_device *dai; + const struct audio_hw_if *hwif; + int error = ENXIO; + int i; - if (hwif->set_port) - return hwif->set_port(dai->dd_cookie, cp); + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + hwif = dai->dd_hw_if; + if (hwif->set_port) + error = hwif->set_port(dai->dd_cookie, cp); + } - return ENXIO; + return error; } int aplaudio_get_port(void *cookie, mixer_ctrl_t *cp) { struct aplaudio_softc *sc = cookie; - struct dai_device *dai = sc->sc_dai_codec; - const struct audio_hw_if *hwif = dai->dd_hw_if; + struct dai_device *dai; + const struct audio_hw_if *hwif; + int error = ENXIO; + int i; - if (hwif->get_port) - return hwif->get_port(dai->dd_cookie, cp); + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + hwif = dai->dd_hw_if; + if (hwif->get_port) + error = hwif->get_port(dai->dd_cookie, cp); + } - return ENXIO; + return error; } int aplaudio_query_devinfo(void *cookie, mixer_devinfo_t *dip) { struct aplaudio_softc *sc = cookie; - struct dai_device *dai = sc->sc_dai_codec; - const struct audio_hw_if *hwif = dai->dd_hw_if; + struct dai_device *dai; + const struct audio_hw_if *hwif; + int i; - if (hwif->query_devinfo) - return hwif->query_devinfo(dai->dd_cookie, dip); + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + hwif = dai->dd_hw_if; + if (hwif->query_devinfo) + return hwif->query_devinfo(dai->dd_cookie, dip); + } return ENXIO; } @@ -379,15 +449,20 @@ aplaudio_trigger_output(void *cookie, void *start, void *end, int blksize, struct dai_device *dai; const struct audio_hw_if *hwif; int error; + int i; - dai = sc->sc_dai_codec; - hwif = dai->dd_hw_if; - if (hwif->trigger_output) { - error = hwif->trigger_output(dai->dd_cookie, - start, end, blksize, intr, arg, param); - if (error) { - aplaudio_halt_output(cookie); - return error; + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + hwif = dai->dd_hw_if; + if (hwif->trigger_output) { + error = hwif->trigger_output(dai->dd_cookie, + start, end, blksize, intr, arg, param); + if (error) { + aplaudio_halt_output(cookie); + return error; + } } } @@ -413,15 +488,20 @@ aplaudio_trigger_input(void *cookie, void *start, void *end, int blksize, struct dai_device *dai; const struct audio_hw_if *hwif; int error; + int i; - dai = sc->sc_dai_codec; - hwif = dai->dd_hw_if; - if (hwif->trigger_input) { - error = hwif->trigger_input(dai->dd_cookie, - start, end, blksize, intr, arg, param); - if (error) { - aplaudio_halt_input(cookie); - return error; + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + hwif = dai->dd_hw_if; + if (hwif->trigger_input) { + error = hwif->trigger_input(dai->dd_cookie, + start, end, blksize, intr, arg, param); + if (error) { + aplaudio_halt_input(cookie); + return error; + } } } @@ -445,11 +525,16 @@ aplaudio_halt_output(void *cookie) struct aplaudio_softc *sc = cookie; struct dai_device *dai; const struct audio_hw_if *hwif; + int i; - dai = sc->sc_dai_codec; - hwif = dai->dd_hw_if; - if (hwif->halt_output) - hwif->halt_output(dai->dd_cookie); + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + hwif = dai->dd_hw_if; + if (hwif->halt_output) + hwif->halt_output(dai->dd_cookie); + } dai = sc->sc_dai_cpu; hwif = dai->dd_hw_if; @@ -465,11 +550,16 @@ aplaudio_halt_input(void *cookie) struct aplaudio_softc *sc = cookie; struct dai_device *dai; const struct audio_hw_if *hwif; + int i; - dai = sc->sc_dai_codec; - hwif = dai->dd_hw_if; - if (hwif->halt_input) - hwif->halt_input(dai->dd_cookie); + for (i = 0; i < nitems(sc->sc_dai_codec); i++) { + dai = sc->sc_dai_codec[i]; + if (dai == NULL) + continue; + hwif = dai->dd_hw_if; + if (hwif->halt_input) + hwif->halt_input(dai->dd_cookie); + } dai = sc->sc_dai_cpu; hwif = dai->dd_hw_if; diff --git a/sys/arch/arm64/dev/aplmca.c b/sys/arch/arm64/dev/aplmca.c index 3bb0fdefdb6..07a53103be4 100644 --- a/sys/arch/arm64/dev/aplmca.c +++ b/sys/arch/arm64/dev/aplmca.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aplmca.c,v 1.2 2022/08/06 09:42:13 kettenis Exp $ */ +/* $OpenBSD: aplmca.c,v 1.3 2022/09/02 17:54:42 kettenis Exp $ */ /* * Copyright (c) 2022 Mark Kettenis * @@ -256,6 +256,19 @@ aplmca_dai_init(struct aplmca_softc *sc, int port) return 0; } +void +aplmca_dai_link(struct aplmca_softc *sc, int master, int port) +{ + struct aplmca_dai *ad = &sc->sc_ad[master]; + + HWRITE4(sc, MCA_PORT_CLOCK_SEL(port), + (ad->ad_cluster + 1) << MCA_PORT_CLOCK_SEL_SHIFT); + HWRITE4(sc, MCA_PORT_DATA_SEL(port), + MCA_PORT_DATA_SEL_TXA(ad->ad_cluster)); + HWRITE4(sc, MCA_PORT_ENABLE(port), + MCA_PORT_ENABLE_CLOCKS | MCA_PORT_ENABLE_TX_DATA); +} + uint32_t * aplmca_dai_next_dai(uint32_t *cells) { @@ -276,9 +289,9 @@ aplmca_alloc_cluster(int node) struct aplmca_softc *sc = aplmca_cd.cd_devs[0]; uint32_t *dais; uint32_t *dai; - uint32_t port; + uint32_t ports[2]; int nports = 0; - int len; + int len, i; len = OF_getproplen(node, "sound-dai"); if (len != 2 * sizeof(uint32_t) && len != 4 * sizeof(uint32_t)) @@ -289,11 +302,8 @@ aplmca_alloc_cluster(int node) dai = dais; while (dai && dai < dais + (len / sizeof(uint32_t))) { - if (dai[0] == sc->sc_phandle) { - port = dai[1]; - nports++; - break; - } + if (dai[0] == sc->sc_phandle && nports < nitems(ports)) + ports[nports++] = dai[1]; dai = aplmca_dai_next_dai(dai); } @@ -301,14 +311,27 @@ aplmca_alloc_cluster(int node) if (nports == 0) return NULL; + for (i = 0; i < nports; i++) { + if (ports[i] >= sc->sc_nclusters) + return NULL; + } - if (sc->sc_ad[port].ad_ac != NULL) + if (sc->sc_ad[ports[0]].ad_ac != NULL) return NULL; - if (aplmca_dai_init(sc, port)) + /* Setup the primary cluster. */ + if (aplmca_dai_init(sc, ports[0])) return NULL; - return &sc->sc_ad[port].ad_dai; + /* + * Additional interfaces receive the same output as the + * primary interface by linking the output port to the primary + * cluster. + */ + for (i = 1; i < nports; i++) + aplmca_dai_link(sc, ports[0], ports[i]); + + return &sc->sc_ad[ports[0]].ad_dai; } int -- 2.20.1