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