Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177289159
D27830.1794097742.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
29 KB
Referenced Files
None
Subscribers
None
D27830.1794097742.diff
View Options
diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64
--- a/sys/conf/files.arm64
+++ b/sys/conf/files.arm64
@@ -148,6 +148,14 @@
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
--- /dev/null
+++ b/sys/dev/sound/fdt/audio_dai.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2019 Oleksandr Tymoshenko <gonzo@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
--- /dev/null
+++ b/sys/dev/sound/fdt/audio_dai_if.m
@@ -0,0 +1,95 @@
+#-
+# Copyright (c) 2019 Oleksandr Tymoshenko <gonzo@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
--- /dev/null
+++ b/sys/dev/sound/fdt/audio_soc.c
@@ -0,0 +1,546 @@
+/*-
+ * Copyright (c) 2019 Oleksandr Tymoshenko <gonzo@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
--- /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@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
--- /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@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)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "Simple Amplifier");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+simple_amp_attach(device_t dev)
+{
+ struct simple_amp_softc *sc;
+ phandle_t node;
+ int error;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ node = ofw_bus_get_node(dev);
+
+ error = gpio_pin_get_by_ofw_property(dev, node,
+ "enable-gpios", &sc->gpio_enable);
+ if (error != 0)
+ sc->gpio_is_valid = false;
+ else
+ sc->gpio_is_valid = true;
+
+ error = regulator_get_by_ofw_property(dev, 0, "VCC-supply",
+ &sc->supply_vcc);
+ if (error != 0)
+ device_printf(dev, "no VCC supply");
+
+ OF_device_register_xref(OF_xref_from_node(node), dev);
+
+ return (0);
+}
+
+static int
+simple_amp_detach(device_t dev)
+{
+
+ return (0);
+}
+
+static int
+simple_amp_dai_init(device_t dev, uint32_t format)
+{
+
+ return (0);
+}
+
+static int
+simple_amp_dai_trigger(device_t dev, int go, int pcm_dir)
+{
+ struct simple_amp_softc *sc;
+ int error;
+
+ if ((pcm_dir != PCMDIR_PLAY) && (pcm_dir != PCMDIR_REC))
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ error = 0;
+ switch (go) {
+ case PCMTRIG_START:
+ if (sc->supply_vcc != NULL) {
+ error = regulator_enable(sc->supply_vcc);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not enable 'VCC' regulator\n");
+ break;
+ }
+ }
+
+ if (sc->gpio_is_valid) {
+ error = gpio_pin_set_active(sc->gpio_enable, 1);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not set 'gpio-enable' gpio\n");
+ break;
+ }
+ }
+
+ break;
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ if (sc->gpio_is_valid) {
+ error = gpio_pin_set_active(sc->gpio_enable, 0);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not clear 'gpio-enable' gpio\n");
+ break;
+ }
+ }
+
+ if (sc->supply_vcc != NULL) {
+ error = regulator_disable(sc->supply_vcc);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not disable 'VCC' regulator\n");
+ break;
+ }
+ }
+
+ break;
+ }
+
+ return (error);
+}
+
+static device_method_t simple_amp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, simple_amp_probe),
+ DEVMETHOD(device_attach, simple_amp_attach),
+ DEVMETHOD(device_detach, simple_amp_detach),
+
+ DEVMETHOD(audio_dai_init, simple_amp_dai_init),
+ DEVMETHOD(audio_dai_trigger, simple_amp_dai_trigger),
+
+ DEVMETHOD_END
+};
+
+static driver_t simple_amp_driver = {
+ "simpleamp",
+ simple_amp_methods,
+ sizeof(struct simple_amp_softc),
+};
+
+static devclass_t simple_amp_devclass;
+
+DRIVER_MODULE(simple_amp, simplebus, simple_amp_driver, simple_amp_devclass, 0, 0);
+SIMPLEBUS_PNP_INFO(compat_data);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Nov 8, 12:29 AM (7 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41557994
Default Alt Text
D27830.1794097742.diff (29 KB)
Attached To
Mode
D27830: Add SoC audio framework
Attached
Detach File
Event Timeline