git: 2cfe870acdec - main - arm64: Add Soc audio framework
Emmanuel Vadot
manu at FreeBSD.org
Fri Jun 11 19:06:55 UTC 2021
The branch main has been updated by manu:
URL: https://cgit.FreeBSD.org/src/commit/?id=2cfe870acdecd35b621932f2b0cb702c48ce087a
commit 2cfe870acdecd35b621932f2b0cb702c48ce087a
Author: Oleksandr Tymoshenko <gonzo at FreeBSD.org>
AuthorDate: 2021-05-13 19:09:50 +0000
Commit: Emmanuel Vadot <manu at FreeBSD.org>
CommitDate: 2021-06-11 19:06:04 +0000
arm64: Add Soc audio framework
This framework is initial implementation of the simple-audio-card compatible
audio driver framework. It provides glue for CPU/codec/aux device.
Differential Revision: https://reviews.freebsd.org/D27830
---
sys/conf/files.arm64 | 8 +
sys/dev/sound/fdt/audio_dai.h | 72 +++++
sys/dev/sound/fdt/audio_dai_if.m | 95 ++++++
sys/dev/sound/fdt/audio_soc.c | 546 +++++++++++++++++++++++++++++++++++
sys/dev/sound/fdt/dummy_codec.c | 127 ++++++++
sys/dev/sound/fdt/simple_amplifier.c | 206 +++++++++++++
6 files changed, 1054 insertions(+)
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
index 7b9b2583aaa1..5c819001493d 100644
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -148,6 +148,14 @@ cddl/dev/dtrace/aarch64/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}"
cddl/dev/dtrace/aarch64/dtrace_subr.c optional dtrace compile-with "${DTRACE_C}"
cddl/dev/fbt/aarch64/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}"
+##
+## ASoC support
+##
+dev/sound/fdt/audio_dai_if.m optional sound fdt
+dev/sound/fdt/audio_soc.c optional sound fdt
+dev/sound/fdt/dummy_codec.c optional sound fdt
+dev/sound/fdt/simple_amplifier.c optional sound fdt
+
##
## Device drivers
##
diff --git a/sys/dev/sound/fdt/audio_dai.h b/sys/dev/sound/fdt/audio_dai.h
new file mode 100644
index 000000000000..f82fe9c7b838
--- /dev/null
+++ b/sys/dev/sound/fdt/audio_dai.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2019 Oleksandr Tymoshenko <gonzo at FreeBSD.org>
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __DAI_H__
+#define __DAI_H__
+
+#define AUDIO_DAI_FORMAT_I2S 0
+#define AUDIO_DAI_FORMAT_RJ 1
+#define AUDIO_DAI_FORMAT_LJ 2
+#define AUDIO_DAI_FORMAT_DSPA 3
+#define AUDIO_DAI_FORMAT_DSPB 4
+#define AUDIO_DAI_FORMAT_AC97 5
+#define AUDIO_DAI_FORMAT_PDM 6
+
+/*
+ * Polarity: Normal/Inverted BCLK/Frame
+ */
+#define AUDIO_DAI_POLARITY_NB_NF 0
+#define AUDIO_DAI_POLARITY_NB_IF 1
+#define AUDIO_DAI_POLARITY_IB_NF 2
+#define AUDIO_DAI_POLARITY_IB_IF 3
+#define AUDIO_DAI_POLARITY_INVERTED_FRAME(n) ((n) & 0x01)
+#define AUDIO_DAI_POLARITY_INVERTED_BCLK(n) ((n) & 0x2)
+
+#define AUDIO_DAI_CLOCK_CBM_CFM 0
+#define AUDIO_DAI_CLOCK_CBS_CFM 1
+#define AUDIO_DAI_CLOCK_CBM_CFS 2
+#define AUDIO_DAI_CLOCK_CBS_CFS 3
+
+#define AUDIO_DAI_CLOCK_IN 0
+#define AUDIO_DAI_CLOCK_OUT 1
+
+#define AUDIO_DAI_JACK_HP 0
+#define AUDIO_DAI_JACK_MIC 1
+
+/*
+ * Signal to audio_soc that chn_intr required
+ * for either recording or playback
+ */
+#define AUDIO_DAI_REC_INTR (1 << 1)
+#define AUDIO_DAI_PLAY_INTR (1 << 0)
+
+#define AUDIO_DAI_FORMAT(fmt, pol, clk) (((fmt) << 16) | ((pol) << 8) | (clk))
+#define AUDIO_DAI_FORMAT_FORMAT(format) (((format) >> 16) & 0xff)
+#define AUDIO_DAI_FORMAT_POLARITY(format) (((format) >> 8) & 0xff)
+#define AUDIO_DAI_FORMAT_CLOCK(format) (((format) >> 0) & 0xff)
+
+
+#endif /* __DAI_H__ */
diff --git a/sys/dev/sound/fdt/audio_dai_if.m b/sys/dev/sound/fdt/audio_dai_if.m
new file mode 100644
index 000000000000..dc3ebbba5489
--- /dev/null
+++ b/sys/dev/sound/fdt/audio_dai_if.m
@@ -0,0 +1,95 @@
+#-
+# Copyright (c) 2019 Oleksandr Tymoshenko <gonzo at FreeBSD.org>
+#
+# 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+#
+# $FreeBSD$
+#
+
+CODE {
+ #include <sys/param.h>
+ #include <sys/bus.h>
+ #include <dev/sound/pcm/sound.h>
+}
+
+INTERFACE audio_dai;
+
+# set DAI format for communications between CPU/codec nodes
+METHOD int init {
+ device_t dev;
+ uint32_t format;
+}
+
+# Initialize DAI and set up interrrupt handler
+METHOD int setup_intr {
+ device_t dev;
+ driver_intr_t intr_handler;
+ void *intr_arg;
+}
+
+# Setup mixers for codec node
+METHOD int setup_mixer {
+ device_t dev;
+ device_t ausocdev;
+}
+
+# setup clock speed
+METHOD int set_sysclk {
+ device_t dev;
+ uint32_t rate;
+ int dai_dir;
+}
+
+METHOD int trigger {
+ device_t dev;
+ int go;
+ int pcm_dir;
+}
+
+METHOD struct pcmchan_caps* get_caps {
+ device_t dev;
+}
+
+METHOD uint32_t get_ptr {
+ device_t dev;
+ int pcm_dir;
+}
+
+# Set PCM channel format
+METHOD uint32_t set_chanformat {
+ device_t dev;
+ uint32_t format;
+}
+
+# Set PCM channel sampling rate
+METHOD uint32_t set_chanspeed {
+ device_t dev;
+ uint32_t speed;
+}
+
+# call DAI interrupt handler
+# returns 1 if call to chn_intr required, 0 otherwise
+METHOD int intr {
+ device_t dev;
+ struct snd_dbuf *play_buf;
+ struct snd_dbuf *rec_buf;
+}
diff --git a/sys/dev/sound/fdt/audio_soc.c b/sys/dev/sound/fdt/audio_soc.c
new file mode 100644
index 000000000000..cd5807d955c9
--- /dev/null
+++ b/sys/dev/sound/fdt/audio_soc.c
@@ -0,0 +1,546 @@
+/*-
+ * Copyright (c) 2019 Oleksandr Tymoshenko <gonzo at FreeBSD.org>
+ *
+ * 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>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/endian.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/sound/fdt/audio_dai.h>
+#include <dev/sound/pcm/sound.h>
+#include "audio_dai_if.h"
+
+#define AUDIO_BUFFER_SIZE 48000 * 4
+
+struct audio_soc_aux_node {
+ SLIST_ENTRY(audio_soc_aux_node) link;
+ device_t dev;
+};
+
+struct audio_soc_channel {
+ struct audio_soc_softc *sc; /* parent device's softc */
+ struct pcm_channel *pcm; /* PCM channel */
+ struct snd_dbuf *buf; /* PCM buffer */
+ int dir; /* direction */
+};
+
+struct audio_soc_softc {
+ /*
+ * pcm_register assumes that sc is snddev_info,
+ * so this has to be first structure member for "compatiblity"
+ */
+ struct snddev_info info;
+ device_t dev;
+ char *name;
+ struct intr_config_hook init_hook;
+ device_t cpu_dev;
+ device_t codec_dev;
+ SLIST_HEAD(, audio_soc_aux_node) aux_devs;
+ unsigned int mclk_fs;
+ struct audio_soc_channel play_channel;
+ struct audio_soc_channel rec_channel;
+ /*
+ * The format is from the CPU node, for CODEC node clock roles
+ * need to be reversed.
+ */
+ uint32_t format;
+ uint32_t link_mclk_fs;
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"simple-audio-card", 1},
+ {NULL, 0},
+};
+
+static struct {
+ const char *name;
+ unsigned int fmt;
+} ausoc_dai_formats[] = {
+ { "i2s", AUDIO_DAI_FORMAT_I2S },
+ { "right_j", AUDIO_DAI_FORMAT_RJ },
+ { "left_j", AUDIO_DAI_FORMAT_LJ },
+ { "dsp_a", AUDIO_DAI_FORMAT_DSPA },
+ { "dsp_b", AUDIO_DAI_FORMAT_DSPB },
+ { "ac97", AUDIO_DAI_FORMAT_AC97 },
+ { "pdm", AUDIO_DAI_FORMAT_PDM },
+};
+
+static int audio_soc_probe(device_t dev);
+static int audio_soc_attach(device_t dev);
+static int audio_soc_detach(device_t dev);
+
+/*
+ * Invert master/slave roles for CODEC side of the node
+ */
+static uint32_t
+audio_soc_reverse_clocks(uint32_t format)
+{
+ int fmt, pol, clk;
+
+ fmt = AUDIO_DAI_FORMAT_FORMAT(format);
+ pol = AUDIO_DAI_FORMAT_POLARITY(format);
+ clk = AUDIO_DAI_FORMAT_CLOCK(format);
+
+ switch (clk) {
+ case AUDIO_DAI_CLOCK_CBM_CFM:
+ clk = AUDIO_DAI_CLOCK_CBS_CFS;
+ break;
+ case AUDIO_DAI_CLOCK_CBS_CFM:
+ clk = AUDIO_DAI_CLOCK_CBM_CFS;
+ break;
+ case AUDIO_DAI_CLOCK_CBM_CFS:
+ clk = AUDIO_DAI_CLOCK_CBS_CFM;
+ break;
+ case AUDIO_DAI_CLOCK_CBS_CFS:
+ clk = AUDIO_DAI_CLOCK_CBM_CFM;
+ break;
+ }
+
+ return AUDIO_DAI_FORMAT(fmt, pol, clk);
+}
+
+static uint32_t
+audio_soc_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksz)
+{
+
+ return (blocksz);
+}
+
+static int
+audio_soc_chan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+
+ struct audio_soc_softc *sc;
+ struct audio_soc_channel *ausoc_chan;
+
+ ausoc_chan = data;
+ sc = ausoc_chan->sc;
+
+ return AUDIO_DAI_SET_CHANFORMAT(sc->cpu_dev, format);
+}
+
+static uint32_t
+audio_soc_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+
+ struct audio_soc_softc *sc;
+ struct audio_soc_channel *ausoc_chan;
+ uint32_t rate;
+ struct audio_soc_aux_node *aux_node;
+
+ ausoc_chan = data;
+ sc = ausoc_chan->sc;
+
+ if (sc->link_mclk_fs) {
+ rate = speed * sc->link_mclk_fs;
+ if (AUDIO_DAI_SET_SYSCLK(sc->cpu_dev, rate, AUDIO_DAI_CLOCK_IN))
+ device_printf(sc->dev, "failed to set sysclk for CPU node\n");
+
+ if (AUDIO_DAI_SET_SYSCLK(sc->codec_dev, rate, AUDIO_DAI_CLOCK_OUT))
+ device_printf(sc->dev, "failed to set sysclk for codec node\n");
+
+ SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
+ if (AUDIO_DAI_SET_SYSCLK(aux_node->dev, rate, AUDIO_DAI_CLOCK_OUT))
+ device_printf(sc->dev, "failed to set sysclk for aux node\n");
+ }
+ }
+
+ /*
+ * Let CPU node determine speed
+ */
+ speed = AUDIO_DAI_SET_CHANSPEED(sc->cpu_dev, speed);
+ AUDIO_DAI_SET_CHANSPEED(sc->codec_dev, speed);
+ SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
+ AUDIO_DAI_SET_CHANSPEED(aux_node->dev, speed);
+ }
+
+ return (speed);
+}
+
+static uint32_t
+audio_soc_chan_getptr(kobj_t obj, void *data)
+{
+ struct audio_soc_softc *sc;
+ struct audio_soc_channel *ausoc_chan;
+
+ ausoc_chan = data;
+ sc = ausoc_chan->sc;
+
+ return AUDIO_DAI_GET_PTR(sc->cpu_dev, ausoc_chan->dir);
+}
+
+static void *
+audio_soc_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct audio_soc_softc *sc;
+ struct audio_soc_channel *ausoc_chan;
+ void *buffer;
+
+ ausoc_chan = devinfo;
+ sc = ausoc_chan->sc;
+ buffer = malloc(AUDIO_BUFFER_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
+
+ if (sndbuf_setup(b, buffer, AUDIO_BUFFER_SIZE) != 0) {
+ free(buffer, M_DEVBUF);
+ return NULL;
+ }
+
+ ausoc_chan->dir = dir;
+ ausoc_chan->buf = b;
+ ausoc_chan->pcm = c;
+
+ return (devinfo);
+}
+
+static int
+audio_soc_chan_trigger(kobj_t obj, void *data, int go)
+{
+ struct audio_soc_softc *sc;
+ struct audio_soc_channel *ausoc_chan;
+ struct audio_soc_aux_node *aux_node;
+
+ ausoc_chan = (struct audio_soc_channel *)data;
+ sc = ausoc_chan->sc;
+ AUDIO_DAI_TRIGGER(sc->codec_dev, go, ausoc_chan->dir);
+ SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
+ AUDIO_DAI_TRIGGER(aux_node->dev, go, ausoc_chan->dir);
+ }
+
+ return AUDIO_DAI_TRIGGER(sc->cpu_dev, go, ausoc_chan->dir);
+}
+
+static int
+audio_soc_chan_free(kobj_t obj, void *data)
+{
+
+ struct audio_soc_softc *sc;
+ struct audio_soc_channel *ausoc_chan;
+ void *buffer;
+
+ ausoc_chan = (struct audio_soc_channel *)data;
+ sc = ausoc_chan->sc;
+
+ buffer = sndbuf_getbuf(ausoc_chan->buf);
+ if (buffer)
+ free(buffer, M_DEVBUF);
+
+ return (0);
+}
+
+static struct pcmchan_caps *
+audio_soc_chan_getcaps(kobj_t obj, void *data)
+{
+ struct audio_soc_softc *sc;
+ struct audio_soc_channel *ausoc_chan;
+
+ ausoc_chan = data;
+ sc = ausoc_chan->sc;
+
+ return AUDIO_DAI_GET_CAPS(sc->cpu_dev);
+}
+
+static kobj_method_t audio_soc_chan_methods[] = {
+ KOBJMETHOD(channel_init, audio_soc_chan_init),
+ KOBJMETHOD(channel_free, audio_soc_chan_free),
+ KOBJMETHOD(channel_setformat, audio_soc_chan_setformat),
+ KOBJMETHOD(channel_setspeed, audio_soc_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize,audio_soc_chan_setblocksize),
+ KOBJMETHOD(channel_trigger, audio_soc_chan_trigger),
+ KOBJMETHOD(channel_getptr, audio_soc_chan_getptr),
+ KOBJMETHOD(channel_getcaps, audio_soc_chan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(audio_soc_chan);
+
+static void
+audio_soc_intr(void *arg)
+{
+ struct audio_soc_softc *sc;
+ int channel_intr_required;
+
+ sc = (struct audio_soc_softc *)arg;
+ channel_intr_required = AUDIO_DAI_INTR(sc->cpu_dev, sc->play_channel.buf, sc->rec_channel.buf);
+ if (channel_intr_required & AUDIO_DAI_PLAY_INTR)
+ chn_intr(sc->play_channel.pcm);
+ if (channel_intr_required & AUDIO_DAI_REC_INTR)
+ chn_intr(sc->rec_channel.pcm);
+}
+
+static int
+audio_soc_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "simple-audio-card");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENXIO);
+}
+
+static void
+audio_soc_init(void *arg)
+{
+ struct audio_soc_softc *sc;
+ phandle_t node, child;
+ device_t daidev, auxdev;
+ uint32_t xref;
+ uint32_t *aux_devs;
+ int ncells, i;
+ struct audio_soc_aux_node *aux_node;
+
+ sc = (struct audio_soc_softc *)arg;
+ config_intrhook_disestablish(&sc->init_hook);
+
+ node = ofw_bus_get_node(sc->dev);
+ /* TODO: handle multi-link nodes */
+ child = ofw_bus_find_child(node, "simple-audio-card,cpu");
+ if (child == 0) {
+ device_printf(sc->dev, "cpu node is missing\n");
+ return;
+ }
+ if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
+ device_printf(sc->dev, "missing sound-dai property in cpu node\n");
+ return;
+ }
+ daidev = OF_device_from_xref(xref);
+ if (daidev == NULL) {
+ device_printf(sc->dev, "no driver attached to cpu node\n");
+ return;
+ }
+ sc->cpu_dev = daidev;
+
+ child = ofw_bus_find_child(node, "simple-audio-card,codec");
+ if (child == 0) {
+ device_printf(sc->dev, "codec node is missing\n");
+ return;
+ }
+ if ((OF_getencprop(child, "sound-dai", &xref, sizeof(xref))) <= 0) {
+ device_printf(sc->dev, "missing sound-dai property in codec node\n");
+ return;
+ }
+ daidev = OF_device_from_xref(xref);
+ if (daidev == NULL) {
+ device_printf(sc->dev, "no driver attached to codec node\n");
+ return;
+ }
+ sc->codec_dev = daidev;
+
+ /* Add AUX devices */
+ aux_devs = NULL;
+ ncells = OF_getencprop_alloc_multi(node, "simple-audio-card,aux-devs", sizeof(*aux_devs),
+ (void **)&aux_devs);
+
+ for (i = 0; i < ncells; i++) {
+ auxdev = OF_device_from_xref(aux_devs[i]);
+ if (auxdev == NULL)
+ device_printf(sc->dev, "warning: no driver attached to aux node\n");
+ aux_node = (struct audio_soc_aux_node *)malloc(sizeof(*aux_node), M_DEVBUF, M_NOWAIT);
+ if (aux_node == NULL) {
+ device_printf(sc->dev, "failed to allocate aux node struct\n");
+ return;
+ }
+ aux_node->dev = auxdev;
+ SLIST_INSERT_HEAD(&sc->aux_devs, aux_node, link);
+ }
+
+ if (aux_devs)
+ OF_prop_free(aux_devs);
+
+ if (AUDIO_DAI_INIT(sc->cpu_dev, sc->format)) {
+ device_printf(sc->dev, "failed to initialize cpu node\n");
+ return;
+ }
+
+ /* Reverse clock roles for CODEC */
+ if (AUDIO_DAI_INIT(sc->codec_dev, audio_soc_reverse_clocks(sc->format))) {
+ device_printf(sc->dev, "failed to initialize codec node\n");
+ return;
+ }
+
+ SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
+ if (AUDIO_DAI_INIT(aux_node->dev, audio_soc_reverse_clocks(sc->format))) {
+ device_printf(sc->dev, "failed to initialize aux node\n");
+ return;
+ }
+ }
+
+ if (pcm_register(sc->dev, sc, 1, 1)) {
+ device_printf(sc->dev, "failed to register PCM\n");
+ return;
+ }
+
+ pcm_getbuffersize(sc->dev, AUDIO_BUFFER_SIZE, AUDIO_BUFFER_SIZE,
+ AUDIO_BUFFER_SIZE);
+ sc->play_channel.sc = sc;
+ sc->rec_channel.sc = sc;
+
+ pcm_addchan(sc->dev, PCMDIR_PLAY, &audio_soc_chan_class, &sc->play_channel);
+ pcm_addchan(sc->dev, PCMDIR_REC, &audio_soc_chan_class, &sc->rec_channel);
+
+ pcm_setstatus(sc->dev, "at EXPERIMENT");
+
+ AUDIO_DAI_SETUP_INTR(sc->cpu_dev, audio_soc_intr, sc);
+ AUDIO_DAI_SETUP_MIXER(sc->codec_dev, sc->dev);
+ SLIST_FOREACH(aux_node, &sc->aux_devs, link) {
+ AUDIO_DAI_SETUP_MIXER(aux_node->dev, sc->dev);
+ }
+}
+
+static int
+audio_soc_attach(device_t dev)
+{
+ struct audio_soc_softc *sc;
+ char *name;
+ phandle_t node, cpu_child;
+ uint32_t xref;
+ int i, ret;
+ char tmp[32];
+ unsigned int fmt, pol, clk;
+ bool frame_master, bitclock_master;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ ret = OF_getprop_alloc(node, "name", (void **)&name);
+ if (ret == -1)
+ name = "SoC audio";
+
+ sc->name = strdup(name, M_DEVBUF);
+ device_set_desc(dev, sc->name);
+
+ if (ret != -1)
+ OF_prop_free(name);
+
+ SLIST_INIT(&sc->aux_devs);
+
+ ret = OF_getprop(node, "simple-audio-card,format", tmp, sizeof(tmp));
+ if (ret == 0) {
+ for (i = 0; i < nitems(ausoc_dai_formats); i++) {
+ if (strcmp(tmp, ausoc_dai_formats[i].name) == 0) {
+ fmt = ausoc_dai_formats[i].fmt;
+ break;
+ }
+ }
+ if (i == nitems(ausoc_dai_formats))
+ return (EINVAL);
+ } else
+ fmt = AUDIO_DAI_FORMAT_I2S;
+
+ if (OF_getencprop(node, "simple-audio-card,mclk-fs",
+ &sc->link_mclk_fs, sizeof(sc->link_mclk_fs)) <= 0)
+ sc->link_mclk_fs = 0;
+
+ /* Unless specified otherwise, CPU node is the master */
+ frame_master = bitclock_master = true;
+
+ cpu_child = ofw_bus_find_child(node, "simple-audio-card,cpu");
+
+ if ((OF_getencprop(node, "simple-audio-card,frame-master", &xref, sizeof(xref))) > 0)
+ frame_master = cpu_child == OF_node_from_xref(xref);
+
+ if ((OF_getencprop(node, "simple-audio-card,bitclock-master", &xref, sizeof(xref))) > 0)
+ bitclock_master = cpu_child == OF_node_from_xref(xref);
+
+ if (frame_master) {
+ clk = bitclock_master ?
+ AUDIO_DAI_CLOCK_CBM_CFM : AUDIO_DAI_CLOCK_CBS_CFM;
+ } else {
+ clk = bitclock_master ?
+ AUDIO_DAI_CLOCK_CBM_CFS : AUDIO_DAI_CLOCK_CBS_CFS;
+ }
+
+ bool bitclock_inversion = OF_hasprop(node, "simple-audio-card,bitclock-inversion");
+ bool frame_inversion = OF_hasprop(node, "simple-audio-card,frame-inversion");
+ if (bitclock_inversion) {
+ pol = frame_inversion ?
+ AUDIO_DAI_POLARITY_IB_IF : AUDIO_DAI_POLARITY_IB_NF;
+ } else {
+ pol = frame_inversion ?
+ AUDIO_DAI_POLARITY_NB_IF : AUDIO_DAI_POLARITY_NB_NF;
+ }
+
+ sc->format = AUDIO_DAI_FORMAT(fmt, pol, clk);
+
+ sc->init_hook.ich_func = audio_soc_init;
+ sc->init_hook.ich_arg = sc;
+ if (config_intrhook_establish(&sc->init_hook) != 0)
+ return (ENOMEM);
+
+ return (0);
+}
+
+static int
+audio_soc_detach(device_t dev)
+{
+ struct audio_soc_softc *sc;
+ struct audio_soc_aux_node *aux;
+
+ sc = device_get_softc(dev);
+ if (sc->name)
+ free(sc->name, M_DEVBUF);
+
+ while ((aux = SLIST_FIRST(&sc->aux_devs)) != NULL) {
+ SLIST_REMOVE_HEAD(&sc->aux_devs, link);
+ free(aux, M_DEVBUF);
+ }
+
+ return (0);
+}
+
+static device_method_t audio_soc_methods[] = {
+ /* device_if methods */
+ DEVMETHOD(device_probe, audio_soc_probe),
+ DEVMETHOD(device_attach, audio_soc_attach),
+ DEVMETHOD(device_detach, audio_soc_detach),
+
+ DEVMETHOD_END,
+};
+
+static driver_t audio_soc_driver = {
+ "pcm",
+ audio_soc_methods,
+ sizeof(struct audio_soc_softc),
+};
+
+DRIVER_MODULE(audio_soc, simplebus, audio_soc_driver, pcm_devclass, NULL, NULL);
+MODULE_VERSION(audio_soc, 1);
diff --git a/sys/dev/sound/fdt/dummy_codec.c b/sys/dev/sound/fdt/dummy_codec.c
new file mode 100644
index 000000000000..6be2491a069d
--- /dev/null
+++ b/sys/dev/sound/fdt/dummy_codec.c
@@ -0,0 +1,127 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo at FreeBSD.org>
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "opt_snd.h"
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/fdt/audio_dai.h>
+#include "audio_dai_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ { "dummy-codec", 1},
+ { NULL, 0 }
+};
+
+struct dummy_codec_softc {
+ device_t dev;
+};
+
+static int dummy_codec_probe(device_t dev);
+static int dummy_codec_attach(device_t dev);
+static int dummy_codec_detach(device_t dev);
+
+static int
+dummy_codec_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Dummy Codec");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+dummy_codec_attach(device_t dev)
+{
+ struct dummy_codec_softc *sc;
+ phandle_t node;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ node = ofw_bus_get_node(dev);
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+
+ return (0);
+}
+
+static int
+dummy_codec_detach(device_t dev)
+{
+
+ return (0);
+}
+
+static int
+dummy_codec_dai_init(device_t dev, uint32_t format)
+{
+
+ return (0);
+}
+
+static device_method_t dummy_codec_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, dummy_codec_probe),
+ DEVMETHOD(device_attach, dummy_codec_attach),
+ DEVMETHOD(device_detach, dummy_codec_detach),
+
+ DEVMETHOD(audio_dai_init, dummy_codec_dai_init),
+
+ DEVMETHOD_END
+};
+
+static driver_t dummy_codec_driver = {
+ "dummycodec",
+ dummy_codec_methods,
+ sizeof(struct dummy_codec_softc),
+};
+
+static devclass_t dummy_codec_devclass;
+
+DRIVER_MODULE(dummy_codec, simplebus, dummy_codec_driver, dummy_codec_devclass, 0, 0);
+SIMPLEBUS_PNP_INFO(compat_data);
diff --git a/sys/dev/sound/fdt/simple_amplifier.c b/sys/dev/sound/fdt/simple_amplifier.c
new file mode 100644
index 000000000000..a0531914d9be
--- /dev/null
+++ b/sys/dev/sound/fdt/simple_amplifier.c
@@ -0,0 +1,206 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo at FreeBSD.org>
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/regulator/regulator.h>
+#include <dev/gpio/gpiobusvar.h>
+
+#include "opt_snd.h"
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/fdt/audio_dai.h>
+#include "audio_dai_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ { "simple-audio-amplifier", 1},
+ { NULL, 0}
+};
+
+struct simple_amp_softc {
+ device_t dev;
+ regulator_t supply_vcc;
+ gpio_pin_t gpio_enable;
+ bool gpio_is_valid;
+};
+
+static int simple_amp_probe(device_t dev);
+static int simple_amp_attach(device_t dev);
+static int simple_amp_detach(device_t dev);
+
+static int
+simple_amp_probe(device_t dev)
+{
*** 133 LINES SKIPPED ***
More information about the dev-commits-src-main
mailing list