* [PATCH RFC 1/6] ASoC: meson: [HACK] let AIU export its clocks through clk-regmap
2025-02-10 15:01 [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB Valerio Setti
@ 2025-02-10 15:01 ` Valerio Setti
2025-02-10 15:01 ` [PATCH RFC 2/6] ASoC: meson: audin: add audio input dt-bindings Valerio Setti
` (5 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Valerio Setti @ 2025-02-10 15:01 UTC (permalink / raw)
To: jbrunet, neil.armstrong, khilman, martin.blumenstingl,
linux-amlogic, linux-sound
Cc: linux-kernel, Valerio Setti
Make AIU export its clocks gates and dividers through the clk-regmap
interface. The advantage is that other periperals, such as AUDIN (audio
input) which requires some clocks owned by AIU, can easily
use them.
Since clk-regmap interface is not public yet, this commit is
marked as HACK, but it is necessary to build and test the following
commits that introduce support for AUDIN driver.
Being an HACK commit, I took the liberty to condense all the changes
(driver, dt-bindings, DT) in a single commit. Once clk-regmap interace
will be public, I can rework this by splitting it into a proper
commit sequence.
Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 14 ++-
include/dt-bindings/sound/meson-aiu.h | 5 +
sound/soc/meson/Makefile | 1 +
sound/soc/meson/aiu-clocks.c | 123 ++++++++++++++++++++
sound/soc/meson/aiu-encoder-i2s.c | 121 +++++++++++--------
sound/soc/meson/aiu.c | 22 ++++
sound/soc/meson/aiu.h | 10 ++
7 files changed, 245 insertions(+), 51 deletions(-)
create mode 100644 sound/soc/meson/aiu-clocks.c
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index ed00e67e6923..e2026b7aa03f 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -10,6 +10,7 @@
#include <dt-bindings/clock/gxbb-clkc.h>
#include <dt-bindings/clock/gxbb-aoclkc.h>
#include <dt-bindings/reset/gxbb-aoclkc.h>
+#include <dt-bindings/sound/meson-aiu.h>
/ {
compatible = "amlogic,meson-gxbb";
@@ -62,6 +63,7 @@ usb1: usb@c9100000 {
};
&aiu {
+ #clock-cells = <1>;
compatible = "amlogic,aiu-gxbb", "amlogic,aiu";
clocks = <&clkc CLKID_AIU_GLUE>,
<&clkc CLKID_I2S_OUT>,
@@ -71,7 +73,11 @@ &aiu {
<&clkc CLKID_IEC958>,
<&clkc CLKID_IEC958_GATE>,
<&clkc CLKID_CTS_MCLK_I958>,
- <&clkc CLKID_CTS_I958>;
+ <&clkc CLKID_CTS_I958>,
+ <&aiu AIU_AOCLK_DIV_GATE>,
+ <&aiu AIU_AOCLK_BASIC_DIV>,
+ <&aiu AIU_AOCLK_MORE_DIV>,
+ <&aiu AIU_LRCLK_DIV>;
clock-names = "pclk",
"i2s_pclk",
"i2s_aoclk",
@@ -80,7 +86,11 @@ &aiu {
"spdif_pclk",
"spdif_aoclk",
"spdif_mclk",
- "spdif_mclk_sel";
+ "spdif_mclk_sel",
+ "i2s_aoclk_div_gate",
+ "i2s_aoclk_basic_div",
+ "i2s_aoclk_more_div",
+ "i2s_lrclk_div";
resets = <&reset RESET_AIU>;
};
diff --git a/include/dt-bindings/sound/meson-aiu.h b/include/dt-bindings/sound/meson-aiu.h
index 1051b8af298b..8e8834273fe6 100644
--- a/include/dt-bindings/sound/meson-aiu.h
+++ b/include/dt-bindings/sound/meson-aiu.h
@@ -15,4 +15,9 @@
#define CTRL_PCM 1
#define CTRL_OUT 2
+#define AIU_AOCLK_DIV_GATE 0
+#define AIU_AOCLK_BASIC_DIV 1
+#define AIU_AOCLK_MORE_DIV 2
+#define AIU_LRCLK_DIV 3
+
#endif /* __DT_MESON_AIU_H */
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
index 24078e4396b0..af75f386feda 100644
--- a/sound/soc/meson/Makefile
+++ b/sound/soc/meson/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
snd-soc-meson-aiu-y := aiu.o
+snd-soc-meson-aiu-y += aiu-clocks.o
snd-soc-meson-aiu-y += aiu-acodec-ctrl.o
snd-soc-meson-aiu-y += aiu-codec-ctrl.o
snd-soc-meson-aiu-y += aiu-encoder-i2s.o
diff --git a/sound/soc/meson/aiu-clocks.c b/sound/soc/meson/aiu-clocks.c
new file mode 100644
index 000000000000..ec4b4846b0de
--- /dev/null
+++ b/sound/soc/meson/aiu-clocks.c
@@ -0,0 +1,123 @@
+#include <linux/clk.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/clk-provider.h>
+#include "../../../drivers/clk/meson/clk-regmap.h"
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include <dt-bindings/sound/meson-aiu.h>
+#include "aiu.h"
+
+static struct clk_regmap i2s_aoclk_div_gate = {
+ .data = &(struct clk_regmap_gate_data){
+ .offset = AIU_CLK_CTRL,
+ .bit_idx = 0,
+ .flags = 0,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "i2s_aoclk_div_gate",
+ .ops = &clk_regmap_gate_ops,
+ .parent_names = (const char *[]) {
+ "cts_amclk",
+ },
+ .num_parents = 1,
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap i2s_aoclk_basic_divider = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = AIU_CLK_CTRL,
+ .shift = 2,
+ .width = 2,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "i2s_aoclk_basic_divider",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]) {
+ "i2s_aoclk_div_gate",
+ },
+ .num_parents = 1,
+ .flags = CLK_DIVIDER_POWER_OF_TWO,
+ },
+};
+
+static struct clk_regmap i2s_aoclk_more_divider = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = AIU_CLK_CTRL_MORE,
+ .shift = 0,
+ .width = 6,
+ .flags = 0,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "i2s_aoclk_more_divider",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]) {
+ "i2s_aoclk_basic_divider",
+ },
+ .num_parents = 1,
+ .flags = 0,
+ },
+};
+
+static struct clk_regmap i2s_lrclk_divider = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = AIU_CODEC_DAC_LRCLK_CTRL,
+ .shift = 0,
+ .width = 12,
+ .flags = 0,
+ },
+ .hw.init = &(struct clk_init_data){
+ .name = "i2s_lrlk_divider",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]) {
+ "i2s_aoclk_more_divider",
+ },
+ .num_parents = 1,
+ .flags = 0,
+ },
+};
+
+struct clk_regmap *const aiu_clk_regmaps[] = {
+ &i2s_aoclk_div_gate,
+ &i2s_aoclk_basic_divider,
+ &i2s_aoclk_more_divider,
+ &i2s_lrclk_divider,
+};
+
+static struct clk_hw *aiu_clk_hw_get(struct of_phandle_args *clkspec, void *clk_hw_data)
+{
+ struct clk_regmap **const aiu_clk_regmaps_ptr = clk_hw_data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= ARRAY_SIZE(aiu_clk_regmaps)) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &(aiu_clk_regmaps_ptr[idx]->hw);
+}
+
+int aiu_register_clocks(struct device *dev, struct regmap *map)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(aiu_clk_regmaps); i++) {
+ aiu_clk_regmaps[i]->map = map;
+ ret = devm_clk_hw_register(dev, &(aiu_clk_regmaps[i]->hw));
+ if (ret) {
+ dev_err(dev, "Failed to register AIU clock %d\n", i);
+ return ret;
+ }
+ }
+
+ ret = devm_of_clk_add_hw_provider(dev, aiu_clk_hw_get, (void *)&aiu_clk_regmaps);
+ if (ret) {
+ dev_err(dev, "devm_of_clk_add_hw_provider failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c
index a0dd914c8ed1..d469ff429177 100644
--- a/sound/soc/meson/aiu-encoder-i2s.c
+++ b/sound/soc/meson/aiu-encoder-i2s.c
@@ -18,22 +18,10 @@
#define AIU_RST_SOFT_I2S_FAST BIT(0)
#define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2)
-#define AIU_CLK_CTRL_I2S_DIV_EN BIT(0)
-#define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2)
#define AIU_CLK_CTRL_AOCLK_INVERT BIT(6)
#define AIU_CLK_CTRL_LRCLK_INVERT BIT(7)
#define AIU_CLK_CTRL_LRCLK_SKEW GENMASK(9, 8)
#define AIU_CLK_CTRL_MORE_HDMI_AMCLK BIT(6)
-#define AIU_CLK_CTRL_MORE_I2S_DIV GENMASK(5, 0)
-#define AIU_CODEC_DAC_LRCLK_CTRL_DIV GENMASK(11, 0)
-
-static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component,
- bool enable)
-{
- snd_soc_component_update_bits(component, AIU_CLK_CTRL,
- AIU_CLK_CTRL_I2S_DIV_EN,
- enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0);
-}
static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
@@ -80,8 +68,13 @@ static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component,
static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component,
struct snd_pcm_hw_params *params,
- unsigned int bs)
+ unsigned long mclk_rate,
+ unsigned long aoclk_rate)
{
+ struct aiu *aiu = snd_soc_component_get_drvdata(component);
+ unsigned long bs = mclk_rate / aoclk_rate;
+ int ret;
+
switch (bs) {
case 1:
case 2:
@@ -91,27 +84,38 @@ static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component,
break;
default:
- dev_err(component->dev, "Unsupported i2s divider: %u\n", bs);
+ dev_err(component->dev, "Unsupported i2s divider: %lu\n", bs);
return -EINVAL;
}
- snd_soc_component_update_bits(component, AIU_CLK_CTRL,
- AIU_CLK_CTRL_I2S_DIV,
- FIELD_PREP(AIU_CLK_CTRL_I2S_DIV,
- __ffs(bs)));
+ /* Use AOCLK_BASIC divider, i.e. set AOCLK_MORE to the same rate as
+ * its parent so that it acts as a passthrough.
+ */
+ ret = clk_set_rate(aiu->i2s_extra.clks[AOCLK_BASIC_DIV].clk,
+ aoclk_rate);
+ if (ret) {
+ dev_err(component->dev, "failed to set AOCLK_BASIC_DIV\n");
+ return ret;
+ }
- snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
- AIU_CLK_CTRL_MORE_I2S_DIV,
- FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV,
- 0));
+ ret = clk_set_rate(aiu->i2s_extra.clks[AOCLK_MORE_DIV].clk, aoclk_rate);
+ if (ret) {
+ dev_err(component->dev, "failed to set AOCLK_MORE_DIV\n");
+ return ret;
+ }
return 0;
}
static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component,
struct snd_pcm_hw_params *params,
- unsigned int bs)
+ unsigned long mclk_rate,
+ unsigned long aoclk_rate)
{
+ struct aiu *aiu = snd_soc_component_get_drvdata(component);
+ unsigned long bs = mclk_rate / aoclk_rate;
+ int ret;
+
/*
* NOTE: this HW is odd.
* In most configuration, the i2s divider is 'mclk / blck'.
@@ -126,31 +130,41 @@ static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component,
return -EINVAL;
}
bs += bs / 2;
+ aoclk_rate = mclk_rate / bs;
}
- /* Use CLK_MORE for mclk to bclk divider */
- snd_soc_component_update_bits(component, AIU_CLK_CTRL,
- AIU_CLK_CTRL_I2S_DIV,
- FIELD_PREP(AIU_CLK_CTRL_I2S_DIV, 0));
+ /* Use AOCLK_MORE divider, i.e. set AOCLK_BASIC to the same rate as
+ * its parent so that it acts as a passthough.
+ */
+ ret = clk_set_rate(aiu->i2s_extra.clks[AOCLK_BASIC_DIV].clk, mclk_rate);
+ if (ret) {
+ dev_err(component->dev, "failed to set AOCLK_BASIC_DIV\n");
+ return ret;
+ }
- snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
- AIU_CLK_CTRL_MORE_I2S_DIV,
- FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV,
- bs - 1));
+ ret = clk_set_rate(aiu->i2s_extra.clks[AOCLK_MORE_DIV].clk, aoclk_rate);
+ if (ret) {
+ dev_err(component->dev, "failed to set AOCLK_MORE_DIV\n");
+ return ret;
+ }
return 0;
}
static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
- struct snd_pcm_hw_params *params)
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
struct aiu *aiu = snd_soc_component_get_drvdata(component);
unsigned int srate = params_rate(params);
- unsigned int fs, bs;
+ unsigned int fs;
+ unsigned long mclk_rate, aoclk_rate;
int ret;
+ mclk_rate = clk_get_rate(aiu->i2s.clks[MCLK].clk);
+
/* Get the oversampling factor */
- fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate);
+ fs = DIV_ROUND_CLOSEST(mclk_rate, srate);
if (fs % 64)
return -EINVAL;
@@ -160,22 +174,26 @@ static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
AIU_I2S_DAC_CFG_MSB_FIRST,
AIU_I2S_DAC_CFG_MSB_FIRST);
- /* Set bclk to lrlck ratio */
- snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL,
- AIU_CODEC_DAC_LRCLK_CTRL_DIV,
- FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV,
- 64 - 1));
-
- bs = fs / 64;
+ /* aoclk rate is 64 times the sample rate */
+ aoclk_rate = srate * 64;
if (aiu->platform->has_clk_ctrl_more_i2s_div)
- ret = aiu_encoder_i2s_set_more_div(component, params, bs);
+ ret = aiu_encoder_i2s_set_more_div(component, params,
+ mclk_rate, aoclk_rate);
else
- ret = aiu_encoder_i2s_set_legacy_div(component, params, bs);
+ ret = aiu_encoder_i2s_set_legacy_div(component, params,
+ mclk_rate, aoclk_rate);
if (ret)
return ret;
+ /* lrclk rate is equal to the sample rate */
+ ret = clk_set_rate(aiu->i2s_extra.clks[LRCLK_DIV].clk, srate);
+ if (ret) {
+ dev_err(dai->dev, "failed to set LRCLK_DIV\n");
+ return ret;
+ }
+
/* Make sure amclk is used for HDMI i2s as well */
snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
AIU_CLK_CTRL_MORE_HDMI_AMCLK,
@@ -189,10 +207,11 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
+ struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
int ret;
/* Disable the clock while changing the settings */
- aiu_encoder_i2s_divider_enable(component, false);
+ // clk_disable_unprepare(aiu->i2s_extra.clks[AOCLK_DIV_GATE].clk);
ret = aiu_encoder_i2s_setup_desc(component, params);
if (ret) {
@@ -200,13 +219,17 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- ret = aiu_encoder_i2s_set_clocks(component, params);
+ ret = aiu_encoder_i2s_set_clocks(component, params, dai);
if (ret) {
dev_err(dai->dev, "setting i2s clocks failed\n");
return ret;
}
- aiu_encoder_i2s_divider_enable(component, true);
+ ret = clk_prepare_enable(aiu->i2s_extra.clks[AOCLK_DIV_GATE].clk);
+ if (ret) {
+ dev_err(dai->dev, "failed to enable AOCLK_DIV_GATE\n");
+ return ret;
+ }
return 0;
}
@@ -214,10 +237,10 @@ static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream,
static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_component *component = dai->component;
-
- aiu_encoder_i2s_divider_enable(component, false);
+ struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
+ clk_disable_unprepare(aiu->i2s_extra.clks[AOCLK_DIV_GATE].clk);
+
return 0;
}
diff --git a/sound/soc/meson/aiu.c b/sound/soc/meson/aiu.c
index f2890111c1d2..ef3365348aa1 100644
--- a/sound/soc/meson/aiu.c
+++ b/sound/soc/meson/aiu.c
@@ -9,6 +9,8 @@
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/reset.h>
+#include <linux/clk-provider.h>
+#include "../../../drivers/clk/meson/clk-regmap.h"
#include <sound/soc.h>
#include <sound/soc-dai.h>
@@ -203,6 +205,13 @@ static const char * const aiu_i2s_ids[] = {
[MIXER] = "i2s_mixer",
};
+static const char * const aiu_i2s_extra_ids[] = {
+ [AOCLK_DIV_GATE] = "i2s_aoclk_div_gate",
+ [AOCLK_BASIC_DIV] = "i2s_aoclk_basic_div",
+ [AOCLK_MORE_DIV] = "i2s_aoclk_more_div",
+ [LRCLK_DIV] = "i2s_lrclk_div",
+};
+
static const char * const aiu_spdif_ids[] = {
[PCLK] = "spdif_pclk",
[AOCLK] = "spdif_aoclk",
@@ -229,6 +238,13 @@ static int aiu_clk_get(struct device *dev)
if (ret)
return dev_err_probe(dev, ret, "Can't get the i2s clocks\n");
+ ret = aiu_clk_bulk_get(dev, aiu_i2s_extra_ids,
+ ARRAY_SIZE(aiu_i2s_extra_ids),
+ &aiu->i2s_extra);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Can't get the i2s extra clocks\n");
+
ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids),
&aiu->spdif);
if (ret)
@@ -278,6 +294,12 @@ static int aiu_probe(struct platform_device *pdev)
if (aiu->spdif.irq < 0)
return aiu->spdif.irq;
+ ret = aiu_register_clocks(dev, map);
+ if (ret) {
+ dev_err(dev, "Failed to register AIU clocks\n");
+ return ret;
+ }
+
ret = aiu_clk_get(dev);
if (ret)
return ret;
diff --git a/sound/soc/meson/aiu.h b/sound/soc/meson/aiu.h
index 0f94c8bf6081..847466c02408 100644
--- a/sound/soc/meson/aiu.h
+++ b/sound/soc/meson/aiu.h
@@ -21,6 +21,13 @@ enum aiu_clk_ids {
MIXER
};
+enum aiu_i2s_extra_clk_ids {
+ AOCLK_DIV_GATE = 0,
+ AOCLK_BASIC_DIV,
+ AOCLK_MORE_DIV,
+ LRCLK_DIV,
+};
+
struct aiu_interface {
struct clk_bulk_data *clks;
unsigned int clk_num;
@@ -35,6 +42,7 @@ struct aiu_platform_data {
struct aiu {
struct clk *spdif_mclk;
struct aiu_interface i2s;
+ struct aiu_interface i2s_extra;
struct aiu_interface spdif;
const struct aiu_platform_data *platform;
};
@@ -54,6 +62,8 @@ int aiu_acodec_ctrl_register_component(struct device *dev);
int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai);
int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai);
+int aiu_register_clocks(struct device *dev, struct regmap *map);
+
extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops;
extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops;
extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops;
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH RFC 2/6] ASoC: meson: audin: add audio input dt-bindings
2025-02-10 15:01 [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB Valerio Setti
2025-02-10 15:01 ` [PATCH RFC 1/6] ASoC: meson: [HACK] let AIU export its clocks through clk-regmap Valerio Setti
@ 2025-02-10 15:01 ` Valerio Setti
2025-02-11 7:33 ` Krzysztof Kozlowski
2025-02-10 15:01 ` [PATCH RFC 3/6] ASoC: meson: add AUDIN driver Valerio Setti
` (4 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Valerio Setti @ 2025-02-10 15:01 UTC (permalink / raw)
To: jbrunet, neil.armstrong, khilman, martin.blumenstingl,
linux-amlogic, linux-sound
Cc: linux-kernel, Valerio Setti
Add the dt-bindings and documentation of the AUDIN audio controller.
This component provides most audio input found on the Amlogic GXBB SoC
family.
Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
.../bindings/sound/amlogic,audin.yaml | 104 ++++++++++++++++++
include/dt-bindings/sound/meson-audin.h | 10 ++
2 files changed, 114 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/amlogic,audin.yaml
create mode 100644 include/dt-bindings/sound/meson-audin.h
diff --git a/Documentation/devicetree/bindings/sound/amlogic,audin.yaml b/Documentation/devicetree/bindings/sound/amlogic,audin.yaml
new file mode 100644
index 000000000000..7fb231a8d5a7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/amlogic,audin.yaml
@@ -0,0 +1,104 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/amlogic,audin.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic AUDIN audio output controller
+
+maintainers:
+ - Valerio Setti <vsetti@baylibre.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ $nodename:
+ pattern: "^audio-input-controller@.*"
+
+ "#sound-dai-cells":
+ const: 1
+
+ compatible:
+ items:
+ - enum:
+ - amlogic,audin-gxbb
+ - const: amlogic,audin
+
+ clocks:
+ items:
+ - description: AIU peripheral clock
+ - description: I2S peripheral clock
+ - description: I2S output clock
+ - description: I2S master clock
+ - description: I2S mixer clock
+ - description: AUDIN peripheral clock
+ - description: I2S bit clock gate
+ - description: I2S bit clock basic divider
+ - description: I2S bit clock more divider
+ - description: I2S L/R clock divider
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: i2s_pclk
+ - const: i2s_aoclk
+ - const: i2s_mclk
+ - const: i2s_mixer
+ - const: i2s_input_clk
+ - const: i2s_aoclk_div_gate
+ - const: i2s_aoclk_basic_div
+ - const: i2s_aoclk_more_div
+ - const: i2s_lrclk_div
+
+ reg:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ sound-name-prefix: true
+
+required:
+ - "#sound-dai-cells"
+ - compatible
+ - clocks
+ - clock-names
+ - reg
+ - resets
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/gxbb-clkc.h>
+ #include <dt-bindings/sound/meson-gxbb.h>
+ #include <dt-bindings/reset/amlogic,meson-gxbb-reset.h>
+
+ audin: audio-input-controller@a000 {
+ compatible = "amlogic,audin-gxbb", "amlogic,audin";
+ #sound-dai-cells = <1>;
+ sound-name-prefix = "AUDIN";
+ reg = <0x0 0xa000 0x0 0x308>;
+ clocks = <&clkc CLKID_AIU_GLUE>,
+ <&clkc CLKID_I2S_OUT>,
+ <&clkc CLKID_AOCLK_GATE>,
+ <&clkc CLKID_CTS_AMCLK>,
+ <&clkc CLKID_MIXER_IFACE>,
+ <&clkc CLKID_I2S_SPDIF>,
+ <&aiu AIU_AOCLK_DIV_GATE>,
+ <&aiu AIU_AOCLK_BASIC_DIV>,
+ <&aiu AIU_AOCLK_MORE_DIV>,
+ <&aiu AIU_LRCLK_DIV>;
+ clock-names = "pclk",
+ "i2s_pclk",
+ "i2s_aoclk",
+ "i2s_mclk",
+ "i2s_mixer",
+ "i2s_input_clk",
+ "i2s_aoclk_div_gate",
+ "i2s_aoclk_basic_div",
+ "i2s_aoclk_more_div",
+ "i2s_lrclk_div";
+ resets = <&reset RESET_AIU>;
+ };
diff --git a/include/dt-bindings/sound/meson-audin.h b/include/dt-bindings/sound/meson-audin.h
new file mode 100644
index 000000000000..30c2fe218ab4
--- /dev/null
+++ b/include/dt-bindings/sound/meson-audin.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DT_MESON_AUDIN_H
+#define __DT_MESON_AUDIN_H
+
+#define CPU_AUDIN_TODDR_0 0
+#define CPU_AUDIN_TODDR_1 1
+#define CPU_AUDIN_TODDR_2 2
+#define CPU_I2S_DECODER 3
+
+#endif /* __DT_MESON_AUDIN_H */
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH RFC 2/6] ASoC: meson: audin: add audio input dt-bindings
2025-02-10 15:01 ` [PATCH RFC 2/6] ASoC: meson: audin: add audio input dt-bindings Valerio Setti
@ 2025-02-11 7:33 ` Krzysztof Kozlowski
0 siblings, 0 replies; 10+ messages in thread
From: Krzysztof Kozlowski @ 2025-02-11 7:33 UTC (permalink / raw)
To: Valerio Setti, jbrunet, neil.armstrong, khilman,
martin.blumenstingl, linux-amlogic, linux-sound
Cc: linux-kernel
On 10/02/2025 16:01, Valerio Setti wrote:
> Add the dt-bindings and documentation of the AUDIN audio controller.
> This component provides most audio input found on the Amlogic GXBB SoC
> family.
>
> Signed-off-by: Valerio Setti <vsetti@baylibre.com>
Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters
<form letter>
Please use scripts/get_maintainers.pl to get a list of necessary people
and lists to CC. It might happen, that command when run on an older
kernel, gives you outdated entries. Therefore please be sure you base
your patches on recent Linux kernel.
Tools like b4 or scripts/get_maintainer.pl provide you proper list of
people, so fix your workflow. Tools might also fail if you work on some
ancient tree (don't, instead use mainline) or work on fork of kernel
(don't, instead use mainline). Just use b4 and everything should be
fine, although remember about `b4 prep --auto-to-cc` if you added new
patches to the patchset.
You missed at least devicetree list (maybe more), so this won't be
tested by automated tooling. Performing review on untested code might be
a waste of time.
Please kindly resend and include all necessary To/Cc entries.
</form letter>
(unless this RFC means you do not expect review)
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC 3/6] ASoC: meson: add AUDIN driver
2025-02-10 15:01 [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB Valerio Setti
2025-02-10 15:01 ` [PATCH RFC 1/6] ASoC: meson: [HACK] let AIU export its clocks through clk-regmap Valerio Setti
2025-02-10 15:01 ` [PATCH RFC 2/6] ASoC: meson: audin: add audio input dt-bindings Valerio Setti
@ 2025-02-10 15:01 ` Valerio Setti
2025-02-11 9:21 ` Jerome Brunet
2025-02-10 15:01 ` [PATCH RFC 4/6] ASoC: meson: add support for AUDIN in gx-card Valerio Setti
` (3 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Valerio Setti @ 2025-02-10 15:01 UTC (permalink / raw)
To: jbrunet, neil.armstrong, khilman, martin.blumenstingl,
linux-amlogic, linux-sound
Cc: linux-kernel, Valerio Setti
Add support for the AUDIN driver which provides audio input capabilities
to the Amlogic GXBB platform. As of now it is composed by:
- I2S decoder which receives I2S audio samples from some external codec;
- toddr module which transfers collected audio samples to an internal
FIFO first and then to RAM.
I2S is the only supported audio source as of now, but others are possible
and can be added in the future.
Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
sound/soc/meson/Kconfig | 6 +
sound/soc/meson/Makefile | 4 +
sound/soc/meson/aiu-encoder-i2s.c | 2 +-
sound/soc/meson/audin-decoder-i2s.c | 247 +++++++++++++++++
sound/soc/meson/audin-toddr.c | 403 ++++++++++++++++++++++++++++
sound/soc/meson/audin.c | 321 ++++++++++++++++++++++
sound/soc/meson/audin.h | 119 ++++++++
7 files changed, 1101 insertions(+), 1 deletion(-)
create mode 100644 sound/soc/meson/audin-decoder-i2s.c
create mode 100644 sound/soc/meson/audin-toddr.c
create mode 100644 sound/soc/meson/audin.c
create mode 100644 sound/soc/meson/audin.h
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index 6458d5dc4902..3a86c6be08aa 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -12,6 +12,12 @@ config SND_MESON_AIU
Select Y or M to add support for the Audio output subsystem found
in the Amlogic Meson8, Meson8b and GX SoC families
+config SND_MESON_AUDIN
+ tristate "Amlogic AUDIN"
+ help
+ Select Y or M to add support for the Audio input subsystem found
+ in the Amlogic GXBB SoC family.
+
config SND_MESON_AXG_FIFO
tristate
select REGMAP_MMIO
diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
index af75f386feda..e4869af72811 100644
--- a/sound/soc/meson/Makefile
+++ b/sound/soc/meson/Makefile
@@ -9,6 +9,9 @@ snd-soc-meson-aiu-y += aiu-encoder-spdif.o
snd-soc-meson-aiu-y += aiu-fifo.o
snd-soc-meson-aiu-y += aiu-fifo-i2s.o
snd-soc-meson-aiu-y += aiu-fifo-spdif.o
+snd-soc-meson-audin-y := audin.o
+snd-soc-meson-audin-y += audin-toddr.o
+snd-soc-meson-audin-y += audin-decoder-i2s.o
snd-soc-meson-axg-fifo-y := axg-fifo.o
snd-soc-meson-axg-frddr-y := axg-frddr.o
snd-soc-meson-axg-toddr-y := axg-toddr.o
@@ -28,6 +31,7 @@ snd-soc-meson-g12a-tohdmitx-y := g12a-tohdmitx.o
snd-soc-meson-t9015-y := t9015.o
obj-$(CONFIG_SND_MESON_AIU) += snd-soc-meson-aiu.o
+obj-$(CONFIG_SND_MESON_AUDIN) += snd-soc-meson-audin.o
obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o
obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o
obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o
diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c
index d469ff429177..b4d5311eb3c5 100644
--- a/sound/soc/meson/aiu-encoder-i2s.c
+++ b/sound/soc/meson/aiu-encoder-i2s.c
@@ -240,7 +240,7 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream,
struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
clk_disable_unprepare(aiu->i2s_extra.clks[AOCLK_DIV_GATE].clk);
-
+
return 0;
}
diff --git a/sound/soc/meson/audin-decoder-i2s.c b/sound/soc/meson/audin-decoder-i2s.c
new file mode 100644
index 000000000000..a6c01399af0c
--- /dev/null
+++ b/sound/soc/meson/audin-decoder-i2s.c
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2025 BayLibre, SAS.
+// Author: Valerio Setti <vsetti@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "audin.h"
+
+static int audin_decoder_i2s_setup_desc(struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ int val;
+
+ /* I2S decoder always outputs 24bits to the FIFO according to the
+ * manual. The only thing we can do is mask some bits as follows:
+ * - 0: 16 bit
+ * - 1: 18 bits (not exposed as supported format)
+ * - 2: 20 bits (not exposed as supported format)
+ * - 3: 24 bits
+ *
+ * We force 24 bit output here and filter unnecessary ones at the FIFO
+ * stage.
+ * Note: data is left-justified, so in case of 16 bits samples, this
+ * means that the LSB is to be discarded at FIFO level and the
+ * relevant part is in bits [23:8].
+ */
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ val = 3;
+ break;
+ default:
+ dev_err(dai->dev, "Error: wrong sample width %d",
+ params_physical_width(params));
+ return -EINVAL;
+ }
+ val = FIELD_PREP(AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK, val);
+ snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
+ AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK, val);
+
+ /* The manual claims that this platform supports up to 4 streams
+ * (8 channels), but only 1 stream (2 channels) is supported ATM.
+ */
+ val = FIELD_PREP(AUDIN_I2SIN_CTRL_I2SIN_CHAN_EN_MASK, 1);
+ snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
+ AUDIN_I2SIN_CTRL_I2SIN_CHAN_EN_MASK, val);
+
+ return 0;
+}
+
+static int audin_decoder_i2s_set_clocks(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct audin *audin = snd_soc_component_get_drvdata(component);
+ unsigned int sample_rate = params_rate(params);
+ unsigned long mclk;
+ int ret;
+
+ mclk = clk_get_rate(audin->bulk_clks[MCLK].clk);
+
+ /* Set mclk to bclk ratio.
+ * We're going to use the new/finer clock divider (BCLK_MORE_DIV) for
+ * this, so let's keep the legacy one (BCLK_DIV) as passthrough.
+ */
+ ret = clk_set_rate(audin->aoclk_basic_div, mclk);
+ if (ret) {
+ dev_err(dai->dev, "Failed to set aoclk_basic_div %d\n", ret);
+ return ret;
+ }
+
+ /* We're going for a fixed bclk to lrclk ratio of 64. */
+ ret = clk_set_rate(audin->aoclk_more_div, sample_rate * 64UL);
+ if (ret) {
+ dev_err(dai->dev, "Failed to set aoclk_more_div %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_set_rate(audin->lrclk_div, sample_rate);
+ if (ret) {
+ dev_err(dai->dev, "Failed to set lrclk_div %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int audin_decoder_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct audin *audin = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = audin_decoder_i2s_setup_desc(params, dai);
+ if (ret) {
+ dev_err(dai->dev, "setting i2s desc failed\n");
+ return ret;
+ }
+
+ ret = audin_decoder_i2s_set_clocks(component, params, dai);
+ if (ret) {
+ dev_err(dai->dev, "setting i2s clocks failed\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(audin->aoclk_div_gate);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int audin_decoder_i2s_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct audin *audin = snd_soc_component_get_drvdata(dai->component);
+
+ clk_disable_unprepare(audin->aoclk_div_gate);
+
+ return 0;
+}
+
+static int audin_decoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int val = 0;
+
+ /* Only CPU Master / Codec Slave supported ATM */
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP)
+ return -EINVAL;
+
+ /* Use clocks from AIU and not from the pads since we only want to
+ * support master mode.
+ */
+ val = AUDIN_I2SIN_CTRL_I2SIN_CLK_SEL |
+ AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SEL |
+ AUDIN_I2SIN_CTRL_I2SIN_DIR;
+ snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL, val, val);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ val = AUDIN_I2SIN_CTRL_I2SIN_POS_SYNC;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ val = 0;
+ break;
+ default:
+ dev_err(dai->dev, "Error: unsupported format %x", fmt);
+ return -EINVAL;
+ }
+ snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
+ AUDIN_I2SIN_CTRL_I2SIN_POS_SYNC, val);
+
+ /* MSB data starts 1 clock cycle after LRCLK transition, as per I2S
+ * specs.
+ */
+ val = FIELD_PREP(AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SKEW_MASK, 1);
+ snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
+ AUDIN_I2SIN_CTRL_I2SIN_LRCLK_INV |
+ AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SKEW_MASK,
+ val);
+
+ return 0;
+}
+
+static int audin_decoder_i2s_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
+ AUDIN_I2SIN_CTRL_I2SIN_EN,
+ AUDIN_I2SIN_CTRL_I2SIN_EN);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
+ AUDIN_I2SIN_CTRL_I2SIN_EN, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int audin_decoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct audin *audin = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ if (WARN_ON(clk_id != 0))
+ return -EINVAL;
+
+ if (dir == SND_SOC_CLOCK_IN)
+ return 0;
+
+ ret = clk_set_rate(audin->bulk_clks[MCLK].clk, freq);
+ if (ret)
+ dev_err(dai->dev, "Failed to set sysclk %d", ret);
+
+ return ret;
+}
+
+static int audin_decoder_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct audin *audin = snd_soc_component_get_drvdata(dai->component);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(audin->bulk_clks_num, audin->bulk_clks);
+ if (ret)
+ dev_err(dai->dev, "Failed to enable bulk clocks %d\n", ret);
+
+ return ret;
+}
+
+static void audin_decoder_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct audin *audin = snd_soc_component_get_drvdata(dai->component);
+
+ clk_bulk_disable_unprepare(audin->bulk_clks_num, audin->bulk_clks);
+}
+
+const struct snd_soc_dai_ops audin_decoder_i2s_dai_ops = {
+ .hw_params = audin_decoder_i2s_hw_params,
+ .hw_free = audin_decoder_i2s_hw_free,
+ .set_fmt = audin_decoder_i2s_set_fmt,
+ .set_sysclk = audin_decoder_i2s_set_sysclk,
+ .startup = audin_decoder_i2s_startup,
+ .shutdown = audin_decoder_i2s_shutdown,
+ .trigger = audin_decoder_i2s_trigger,
+};
diff --git a/sound/soc/meson/audin-toddr.c b/sound/soc/meson/audin-toddr.c
new file mode 100644
index 000000000000..14db8b9587e8
--- /dev/null
+++ b/sound/soc/meson/audin-toddr.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2025 BayLibre, SAS.
+// Author: Valerio Setti <vsetti@baylibre.com>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <sound/pcm_params.h>
+#include <linux/dma-mapping.h>
+#include <linux/hrtimer.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <dt-bindings/sound/meson-audin.h>
+
+#include "audin.h"
+
+struct fifo_regs {
+ unsigned int start;
+ unsigned int end;
+ unsigned int ptr;
+ unsigned int intr;
+ unsigned int rdptr;
+ unsigned int ctrl;
+ unsigned int ctrl1;
+ unsigned int wrap;
+};
+
+struct fifo_regs_bit_masks {
+ unsigned int overflow_en;
+ unsigned int addr_trigger_en;
+ unsigned int overflow_set;
+ unsigned int addr_trigger_set;
+};
+
+#define AUDIN_FIFO_COUNT 3
+
+struct fifo_regs audin_fifo_regs[AUDIN_FIFO_COUNT] = {
+ [0] = {
+ .start = AUDIN_FIFO0_START,
+ .end = AUDIN_FIFO0_END,
+ .ptr = AUDIN_FIFO0_PTR,
+ .intr = AUDIN_FIFO0_INTR,
+ .rdptr = AUDIN_FIFO0_RDPTR,
+ .ctrl = AUDIN_FIFO0_CTRL,
+ .ctrl1 = AUDIN_FIFO0_CTRL1,
+ .wrap = AUDIN_FIFO0_WRAP,
+ },
+ [1] = {
+ .start = AUDIN_FIFO1_START,
+ .end = AUDIN_FIFO1_END,
+ .ptr = AUDIN_FIFO1_PTR,
+ .intr = AUDIN_FIFO1_INTR,
+ .rdptr = AUDIN_FIFO1_RDPTR,
+ .ctrl = AUDIN_FIFO1_CTRL,
+ .ctrl1 = AUDIN_FIFO1_CTRL1,
+ .wrap = AUDIN_FIFO1_WRAP,
+ },
+ [2] = {
+ .start = AUDIN_FIFO2_START,
+ .end = AUDIN_FIFO2_END,
+ .ptr = AUDIN_FIFO2_PTR,
+ .intr = AUDIN_FIFO2_INTR,
+ .rdptr = AUDIN_FIFO2_RDPTR,
+ .ctrl = AUDIN_FIFO2_CTRL,
+ .ctrl1 = AUDIN_FIFO2_CTRL1,
+ .wrap = AUDIN_FIFO2_WRAP,
+ }
+};
+
+struct fifo_regs_bit_masks audin_fifo_regs_bit_masks[AUDIN_FIFO_COUNT] = {
+ [0] = {
+ .overflow_en = AUDIN_INT_CTRL_FIFO0_OVERFLOW,
+ .addr_trigger_en = AUDIN_INT_CTRL_FIFO0_ADDR_TRIG,
+ .overflow_set = AUDIN_FIFO_INT_FIFO0_OVERFLOW,
+ .addr_trigger_set = AUDIN_FIFO_INT_FIFO0_ADDR_TRIG,
+ },
+ [1] = {
+ .overflow_en = AUDIN_INT_CTRL_FIFO1_OVERFLOW,
+ .addr_trigger_en = AUDIN_INT_CTRL_FIFO1_ADDR_TRIG,
+ .overflow_set = AUDIN_FIFO_INT_FIFO1_OVERFLOW,
+ .addr_trigger_set = AUDIN_FIFO_INT_FIFO1_ADDR_TRIG,
+ },
+ [2] = {
+ .overflow_en = AUDIN_INT_CTRL_FIFO2_OVERFLOW,
+ .addr_trigger_en = AUDIN_INT_CTRL_FIFO2_ADDR_TRIG,
+ .overflow_set = AUDIN_FIFO_INT_FIFO2_OVERFLOW,
+ .addr_trigger_set = AUDIN_FIFO_INT_FIFO2_ADDR_TRIG,
+ },
+
+};
+
+/* This is the size of the FIFO (i.e. 64*64 bytes). */
+#define AUDIN_FIFO_I2S_BLOCK 4096
+
+static struct snd_pcm_hardware toddr_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE),
+ .formats = AUDIN_FORMATS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_min = 2*AUDIN_FIFO_I2S_BLOCK,
+ .period_bytes_max = AUDIN_FIFO_I2S_BLOCK * USHRT_MAX,
+ .periods_min = 2,
+ .periods_max = UINT_MAX,
+
+ /* No real justification for this */
+ .buffer_bytes_max = 1 * 1024 * 1024,
+};
+
+struct audin_fifo {
+ const struct fifo_regs *reg;
+ const struct fifo_regs_bit_masks *reg_bit_masks;
+ struct snd_pcm_hardware *pcm_hw;
+ struct clk *pclk;
+
+ /* The AUDIN peripheral has an IRQ to signal when data is received, but
+ * it cannot grant a periodic behavior. The reason is that the register
+ * which holds the address which triggers the IRQ must be updated
+ * continuously. Therefore we use a periodic timer.
+ */
+ struct hrtimer polling_timer;
+ int poll_time_ns;
+ struct snd_pcm_substream *substream;
+};
+
+static int audin_toddr_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ snd_soc_component_update_bits(component, fifo->reg->ctrl,
+ AUDIN_FIFO_CTRL_EN,
+ AUDIN_FIFO_CTRL_EN);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_soc_component_update_bits(component, fifo->reg->ctrl,
+ AUDIN_FIFO_CTRL_EN, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int audin_toddr_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ dma_addr_t dma_end = runtime->dma_addr + runtime->dma_bytes - 8;
+ unsigned int val;
+
+ /* Setup memory boundaries */
+ snd_soc_component_write(component, fifo->reg->start, runtime->dma_addr);
+ snd_soc_component_write(component, fifo->reg->ptr, runtime->dma_addr);
+ snd_soc_component_write(component, fifo->reg->end, dma_end);
+
+ /* Load new addresses */
+ val = AUDIN_FIFO_CTRL_LOAD | AUDIN_FIFO_CTRL_UG;
+ snd_soc_component_update_bits(component, fifo->reg->ctrl, val, val);
+
+ /* Reset */
+ snd_soc_component_update_bits(dai->component, fifo->reg->ctrl,
+ AUDIN_FIFO_CTRL_RST,
+ AUDIN_FIFO_CTRL_RST);
+
+ return 0;
+}
+
+static int audin_toddr_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
+ unsigned int val;
+
+ switch (params_width(params)) {
+ case 16:
+ /* FIFO is filled line by line and each of them is 8 bytes. The
+ * problem is that each line is filled starting from the end,
+ * so we need to properly reorder them before moving to the
+ * RAM. This is the value required to properly re-order 16 bits
+ * samples.
+ */
+ val = FIELD_PREP(AUDIN_FIFO_CTRL_ENDIAN_MASK, 6);
+ snd_soc_component_update_bits(component, fifo->reg->ctrl,
+ AUDIN_FIFO_CTRL_ENDIAN_MASK, val);
+
+ /* The I2S input decoder passed 24 bits of left-justified data
+ * but samples were 16 bits. Therefore we drop the LSB.
+ */
+ val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_POS_01_MASK, 1);
+ snd_soc_component_update_bits(component, fifo->reg->ctrl1,
+ AUDIN_FIFO_CTRL1_DIN_POS_01_MASK,
+ val);
+
+ /* Set sample size to 2 bytes (16 bit) */
+ val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK, 1);
+ snd_soc_component_update_bits(component, fifo->reg->ctrl1,
+ AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK,
+ val);
+ break;
+ case 24:
+ /* The same as above but in this case we need to reorder 32 bits
+ * samples (because 24 bits samples are stored as 32 bits).
+ */
+ val = FIELD_PREP(AUDIN_FIFO_CTRL_ENDIAN_MASK, 4);
+ snd_soc_component_update_bits(component, fifo->reg->ctrl,
+ AUDIN_FIFO_CTRL_ENDIAN_MASK,
+ val);
+
+ val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_POS_01_MASK, 0);
+ snd_soc_component_update_bits(component, fifo->reg->ctrl1,
+ AUDIN_FIFO_CTRL1_DIN_POS_01_MASK,
+ val);
+
+ /* Set sample size to 3 bytes (24 bit) */
+ val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK, 2);
+ snd_soc_component_update_bits(component, fifo->reg->ctrl1,
+ AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK,
+ val);
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported physical width %u\n",
+ params_physical_width(params));
+ return -EINVAL;
+ }
+
+ /* This is a bit counterintuitive. Even though the platform has a single pin
+ * for I2S input which would mean that we can only support 2 channels,
+ * doing so would cause samples to be stored in a weird way into the FIFO:
+ * all the samples from the 1st channel on the 1st half of the FIFO, then
+ * samples from the 2nd channel in the other half. Of course extra work
+ * would be required to properly interleave them before returning to the
+ * userspace.
+ * Setting a single channel mode instead solves the problem: samples from
+ * 1st and 2nd channel are stored interleaved and sequentially in the FIFO.
+ */
+ val = FIELD_PREP(AUDIN_FIFO_CTRL_CHAN_MASK, 1);
+ snd_soc_component_update_bits(component, fifo->reg->ctrl,
+ AUDIN_FIFO_CTRL_CHAN_MASK, val);
+
+ /* Setup the period for the polling timer. */
+ fifo->poll_time_ns = 1000000000 / params_rate(params) *
+ params_period_size(params);
+
+ return 0;
+}
+
+static enum hrtimer_restart timer_cb(struct hrtimer *timer)
+{
+ struct audin_fifo *fifo = container_of(timer, struct audin_fifo,
+ polling_timer);
+ snd_pcm_period_elapsed(fifo->substream);
+ hrtimer_forward_now(timer, fifo->poll_time_ns);
+ return HRTIMER_RESTART;
+}
+
+static int audin_toddr_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
+ int ret;
+
+ snd_soc_set_runtime_hwparams(substream, fifo->pcm_hw);
+
+ /* Check runtime parameters */
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ AUDIN_FIFO_I2S_BLOCK);
+ if (ret) {
+ dev_err(dai->dev, "Failed to set runtime constraint %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ AUDIN_FIFO_I2S_BLOCK);
+ if (ret) {
+ dev_err(dai->dev, "Failed to set runtime constraint %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(fifo->pclk);
+ if (ret) {
+ dev_err(dai->dev, "Failed to enable PCLK %d\n", ret);
+ return ret;
+ }
+
+ /* Start the reporting timer */
+ fifo->substream = substream;
+ hrtimer_start(&fifo->polling_timer, fifo->poll_time_ns,
+ HRTIMER_MODE_REL);
+
+ return ret;
+}
+
+static void audin_toddr_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
+
+ hrtimer_cancel(&fifo->polling_timer);
+ clk_disable_unprepare(fifo->pclk);
+}
+
+snd_pcm_uframes_t audin_toddr_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int start, ptr;
+
+ start = snd_soc_component_read(component, fifo->reg->start);
+ ptr = snd_soc_component_read(component, fifo->reg->ptr);
+
+ return bytes_to_frames(runtime, ptr - start);
+}
+
+static int audin_toddr_dai_probe(struct snd_soc_dai *dai)
+{
+ struct audin *audin = snd_soc_component_get_drvdata(dai->component);
+ struct audin_fifo *fifo;
+
+ fifo = kzalloc(sizeof(*fifo), GFP_KERNEL);
+ if (!fifo)
+ return -ENOMEM;
+
+ if (dai->id >= AUDIN_FIFO_COUNT) {
+ dev_err(dai->dev, "Invalid DAI ID %d\n", dai->id);
+ kfree(fifo);
+ return -EINVAL;
+ }
+
+ fifo->reg = &audin_fifo_regs[dai->id];
+ fifo->reg_bit_masks = &audin_fifo_regs_bit_masks[dai->id];
+ fifo->pcm_hw = &toddr_pcm_hw;
+ fifo->pclk = audin->bulk_clks[PCLK].clk;
+ hrtimer_init(&fifo->polling_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ fifo->polling_timer.function = timer_cb;
+
+ snd_soc_dai_dma_data_set_capture(dai, fifo);
+
+ return 0;
+}
+
+static int audin_toddr_dai_remove(struct snd_soc_dai *dai)
+{
+ kfree(snd_soc_dai_dma_data_get_capture(dai));
+
+ return 0;
+}
+
+static int audin_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
+ size_t size = fifo->pcm_hw->buffer_bytes_max;
+ int ret;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(dai->dev, "Failed to set DMA mask %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ card->dev, size, size);
+ if (ret) {
+ dev_err(dai->dev, "Failed to set PCM managed buffer %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+const struct snd_soc_dai_ops audin_toddr_dai_ops = {
+ .trigger = audin_toddr_trigger,
+ .prepare = audin_toddr_prepare,
+ .hw_params = audin_toddr_hw_params,
+ .startup = audin_toddr_startup,
+ .shutdown = audin_toddr_shutdown,
+ .pcm_new = audin_toddr_pcm_new,
+ .probe = audin_toddr_dai_probe,
+ .remove = audin_toddr_dai_remove,
+};
diff --git a/sound/soc/meson/audin.c b/sound/soc/meson/audin.c
new file mode 100644
index 000000000000..254d646260f1
--- /dev/null
+++ b/sound/soc/meson/audin.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2025 BayLibre, SAS.
+// Author: Valerio Setti <vsetti@baylibre.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <dt-bindings/sound/meson-audin.h>
+
+#include "audin.h"
+
+static const char * const audin_fifo_input_sel_texts[] = {
+ "SPDIF", "I2S", "PCM", "HDMI", "Demodulator"
+};
+
+static SOC_ENUM_SINGLE_DECL(audin_fifo0_input_sel_enum, AUDIN_FIFO0_CTRL,
+ AUDIN_FIFO_CTRL_DIN_SEL_OFF,
+ audin_fifo_input_sel_texts);
+
+static const struct snd_kcontrol_new audin_fifo0_input_sel_mux =
+ SOC_DAPM_ENUM("FIFO0 SRC SEL", audin_fifo0_input_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(audin_fifo1_input_sel_enum, AUDIN_FIFO1_CTRL,
+ AUDIN_FIFO_CTRL_DIN_SEL_OFF,
+ audin_fifo_input_sel_texts);
+
+static const struct snd_kcontrol_new audin_fifo1_input_sel_mux =
+ SOC_DAPM_ENUM("FIFO1 SRC SEL", audin_fifo1_input_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(audin_fifo2_input_sel_enum, AUDIN_FIFO2_CTRL,
+ AUDIN_FIFO_CTRL_DIN_SEL_OFF,
+ audin_fifo_input_sel_texts);
+
+static const struct snd_kcontrol_new audin_fifo2_input_sel_mux =
+ SOC_DAPM_ENUM("FIFO2 SRC SEL", audin_fifo2_input_sel_enum);
+
+static const struct snd_soc_dapm_widget audin_cpu_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("FIFO0 SRC SEL", SND_SOC_NOPM, 0, 0,
+ &audin_fifo0_input_sel_mux),
+ SND_SOC_DAPM_MUX("FIFO1 SRC SEL", SND_SOC_NOPM, 0, 0,
+ &audin_fifo1_input_sel_mux),
+ SND_SOC_DAPM_MUX("FIFO2 SRC SEL", SND_SOC_NOPM, 0, 0,
+ &audin_fifo2_input_sel_mux),
+};
+
+static const struct snd_soc_dapm_route audin_cpu_dapm_routes[] = {
+ { "FIFO0 SRC SEL", "I2S", "I2S Decoder Capture" },
+ { "FIFO1 SRC SEL", "I2S", "I2S Decoder Capture" },
+ { "FIFO2 SRC SEL", "I2S", "I2S Decoder Capture" },
+ { "TODDR 0 Capture", NULL, "FIFO0 SRC SEL" },
+ { "TODDR 1 Capture", NULL, "FIFO1 SRC SEL" },
+ { "TODDR 2 Capture", NULL, "FIFO2 SRC SEL" },
+};
+
+static int audin_cpu_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ struct snd_soc_dai *dai;
+ int id;
+
+ if (args->args_count != 1) {
+ dev_err(component->dev, "Wrong number of arguments %d\n",
+ args->args_count);
+ return -EINVAL;
+ }
+
+ id = args->args[0];
+
+ if (id < 0 || id >= component->num_dai) {
+ dev_err(component->dev, "Invalid ID %d\n", id);
+ return -EINVAL;
+ }
+
+ for_each_component_dais(component, dai) {
+ if (id == 0)
+ break;
+ id--;
+ }
+
+ *dai_name = dai->driver->name;
+
+ return 0;
+}
+
+static int audin_cpu_component_probe(struct snd_soc_component *component)
+{
+ struct audin *audin = snd_soc_component_get_drvdata(component);
+
+ /* Required for the FIFO Source control operation */
+ return clk_prepare_enable(audin->bulk_clks[INPUT].clk);
+}
+
+static void audin_cpu_component_remove(struct snd_soc_component *component)
+{
+ struct audin *audin = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(audin->bulk_clks[INPUT].clk);
+}
+
+static const struct snd_soc_component_driver audin_cpu_component = {
+ .name = "AUDIN CPU",
+ .dapm_widgets = audin_cpu_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(audin_cpu_dapm_widgets),
+ .dapm_routes = audin_cpu_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(audin_cpu_dapm_routes),
+ .of_xlate_dai_name = audin_cpu_of_xlate_dai_name,
+ .pointer = audin_toddr_pointer,
+ .probe = audin_cpu_component_probe,
+ .remove = audin_cpu_component_remove,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_prefix = "audin-cpu",
+#endif
+};
+
+static struct snd_soc_dai_driver audin_cpu_dai_drv[] = {
+ [CPU_AUDIN_TODDR_0] = {
+ .name = "TODDR 0",
+ .capture = {
+ .stream_name = "TODDR 0 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .formats = AUDIN_FORMATS,
+ },
+ .ops = &audin_toddr_dai_ops,
+ },
+ [CPU_AUDIN_TODDR_1] = {
+ .name = "TODDR 1",
+ .capture = {
+ .stream_name = "TODDR 1 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .formats = AUDIN_FORMATS,
+ },
+ .ops = &audin_toddr_dai_ops,
+ },
+ [CPU_AUDIN_TODDR_2] = {
+ .name = "TODDR 2",
+ .capture = {
+ .stream_name = "TODDR 2 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .formats = AUDIN_FORMATS,
+ },
+ .ops = &audin_toddr_dai_ops,
+ },
+ [CPU_I2S_DECODER] = {
+ .name = "I2S Decoder",
+ .capture = {
+ .stream_name = "I2S Decoder Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = AUDIN_FORMATS,
+ },
+ .ops = &audin_decoder_i2s_dai_ops,
+ },
+};
+
+static const struct regmap_config audin_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x308,
+};
+
+static const char * const clk_bulk_ids[] = {
+ "i2s_pclk",
+ "i2s_aoclk",
+ "i2s_mclk",
+ "i2s_mixer",
+ "i2s_input_clk",
+};
+
+static int audin_clk_single_get(struct device *dev, const unsigned char *id,
+ bool enable, struct clk **clk)
+{
+ *clk = devm_clk_get(dev, id);
+ if (IS_ERR(*clk)) {
+ dev_err(dev, "Failed to get %s clock %ld\n", id, PTR_ERR(*clk));
+ return PTR_ERR(*clk);
+ }
+
+ if (enable)
+ return clk_prepare_enable(*clk);
+
+ return 0;
+}
+
+static int audin_clk_get(struct device *dev)
+{
+ struct audin *audin = dev_get_drvdata(dev);
+ struct clk *pclk;
+ int i, ret;
+
+ ret = audin_clk_single_get(dev, "pclk", true, &pclk);
+ if (ret)
+ return ret;
+
+ audin->bulk_clks_num = ARRAY_SIZE(clk_bulk_ids);
+ audin->bulk_clks = devm_kcalloc(dev, audin->bulk_clks_num,
+ sizeof(struct clk_bulk_data),
+ GFP_KERNEL);
+ if (!audin->bulk_clks)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(clk_bulk_ids); i++)
+ audin->bulk_clks[i].id = clk_bulk_ids[i];
+
+ ret = devm_clk_bulk_get(dev, ARRAY_SIZE(clk_bulk_ids),
+ audin->bulk_clks);
+ if (ret) {
+ dev_err(dev, "Failed to get bulk clocks %d\n", ret);
+ return ret;
+ }
+
+ ret = audin_clk_single_get(dev, "i2s_aoclk_div_gate", false,
+ &audin->aoclk_div_gate);
+ if (ret)
+ return ret;
+
+ ret = audin_clk_single_get(dev, "i2s_aoclk_basic_div", false,
+ &audin->aoclk_basic_div);
+ if (ret)
+ return ret;
+
+ ret = audin_clk_single_get(dev, "i2s_aoclk_more_div", false,
+ &audin->aoclk_more_div);
+ if (ret)
+ return ret;
+
+ ret = audin_clk_single_get(dev, "i2s_lrclk_div", false,
+ &audin->lrclk_div);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int audin_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ void __iomem *regs;
+ struct regmap *map;
+ struct audin *audin;
+ int ret;
+
+ audin = devm_kzalloc(dev, sizeof(*audin), GFP_KERNEL);
+ if (!audin)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, audin);
+
+ ret = device_reset(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to reset device\n");
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ map = devm_regmap_init_mmio(dev, regs, &audin_regmap_cfg);
+ if (IS_ERR(map)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(map));
+ return PTR_ERR(map);
+ }
+
+ ret = audin_clk_get(dev);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_register_component(dev, &audin_cpu_component,
+ audin_cpu_dai_drv,
+ ARRAY_SIZE(audin_cpu_dai_drv));
+ if (ret) {
+ dev_err(dev, "Failed to register cpu component\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void audin_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+}
+
+static const struct of_device_id audin_of_match[] = {
+ { .compatible = "amlogic,audin-gxbb", .data = NULL },
+ {}
+};
+MODULE_DEVICE_TABLE(of, audin_of_match);
+
+static struct platform_driver audin_pdrv = {
+ .probe = audin_probe,
+ .remove = audin_remove,
+ .driver = {
+ .name = "meson-audin",
+ .of_match_table = audin_of_match,
+ },
+};
+module_platform_driver(audin_pdrv);
+
+MODULE_DESCRIPTION("Meson AUDIN Driver");
+MODULE_AUTHOR("Valerio Setti <vsetti@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/audin.h b/sound/soc/meson/audin.h
new file mode 100644
index 000000000000..eb1dbbaeabcc
--- /dev/null
+++ b/sound/soc/meson/audin.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+/*
+ * Copyright (c) 2025 BayLibre, SAS.
+ * Author: Valerio Setti <vsetti@baylibre.com>
+ */
+
+#ifndef _MESON_AUDIN_H
+#define _MESON_AUDIN_H
+
+struct clk;
+struct clk_bulk_data;
+struct device;
+struct of_phandle_args;
+struct snd_soc_dai;
+struct snd_soc_dai_ops;
+
+enum audin_bulk_clks_id {
+ PCLK = 0,
+ AOCLK,
+ MCLK,
+ MIXER,
+ INPUT,
+};
+
+struct audin {
+ struct clk_bulk_data *bulk_clks;
+ unsigned int bulk_clks_num;
+ struct clk *aoclk_div_gate;
+ struct clk *aoclk_basic_div;
+ struct clk *aoclk_more_div;
+ struct clk *lrclk_div;
+};
+
+/* I2SIN_CTRL register and bits */
+#define AUDIN_I2SIN_CTRL 0x040 /* Reg index = 0x10 */
+ #define AUDIN_I2SIN_CTRL_I2SIN_DIR BIT(0)
+ #define AUDIN_I2SIN_CTRL_I2SIN_CLK_SEL BIT(1)
+ #define AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SEL BIT(2)
+ #define AUDIN_I2SIN_CTRL_I2SIN_POS_SYNC BIT(3)
+ #define AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SKEW_MASK GENMASK(6, 4)
+ #define AUDIN_I2SIN_CTRL_I2SIN_LRCLK_INV BIT(7)
+ #define AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK GENMASK(9, 8)
+ #define AUDIN_I2SIN_CTRL_I2SIN_CHAN_EN_MASK GENMASK(13, 10)
+ #define AUDIN_I2SIN_CTRL_I2SIN_EN BIT(15)
+
+/* FIFO0 registers */
+#define AUDIN_FIFO0_START 0x080 /* Reg index = 0x20 */
+#define AUDIN_FIFO0_END 0x084 /* Reg index = 0x21 */
+#define AUDIN_FIFO0_PTR 0x088 /* Reg index = 0x22 */
+#define AUDIN_FIFO0_INTR 0x08C /* Reg index = 0x23 */
+#define AUDIN_FIFO0_RDPTR 0x090 /* Reg index = 0x24 */
+#define AUDIN_FIFO0_WRAP 0x0C4 /* Reg index = 0x31 */
+
+/* FIFO1 registers */
+#define AUDIN_FIFO1_START 0x0CC /* Reg index = 0x33 */
+#define AUDIN_FIFO1_END 0x0D0 /* Reg index = 0x34 */
+#define AUDIN_FIFO1_PTR 0x0D4 /* Reg index = 0x35 */
+#define AUDIN_FIFO1_INTR 0x0D8 /* Reg index = 0x36 */
+#define AUDIN_FIFO1_RDPTR 0x0DC /* Reg index = 0x37 */
+#define AUDIN_FIFO1_WRAP 0x110 /* Reg index = 0x44 */
+
+/* FIFO2 registers */
+#define AUDIN_FIFO2_START 0x114 /* Reg index = 0x45 */
+#define AUDIN_FIFO2_END 0x118 /* Reg index = 0x46 */
+#define AUDIN_FIFO2_PTR 0x11C /* Reg index = 0x47 */
+#define AUDIN_FIFO2_INTR 0x120 /* Reg index = 0x48 */
+#define AUDIN_FIFO2_RDPTR 0x124 /* Reg index = 0x49 */
+#define AUDIN_FIFO2_WRAP 0x140 /* Reg index = 0x50 */
+
+/* FIFOx CTRL registers and bits */
+#define AUDIN_FIFO0_CTRL 0x094 /* Reg index = 0x25 */
+#define AUDIN_FIFO1_CTRL 0x0E0 /* Reg index = 0x38 */
+#define AUDIN_FIFO2_CTRL 0x128 /* Reg index = 0x4a */
+ #define AUDIN_FIFO_CTRL_EN BIT(0)
+ #define AUDIN_FIFO_CTRL_RST BIT(1)
+ #define AUDIN_FIFO_CTRL_LOAD BIT(2)
+ #define AUDIN_FIFO_CTRL_DIN_SEL_OFF 3
+ #define AUDIN_FIFO_CTRL_DIN_SEL_MASK GENMASK(5, 3)
+ #define AUDIN_FIFO_CTRL_ENDIAN_MASK GENMASK(10, 8)
+ #define AUDIN_FIFO_CTRL_CHAN_MASK GENMASK(14, 11)
+ #define AUDIN_FIFO_CTRL_UG BIT(15)
+
+/* FIFOx_CTRL1 registers and bits */
+#define AUDIN_FIFO0_CTRL1 0x098 /* Reg index = 0x26 */
+#define AUDIN_FIFO1_CTRL1 0x0E4 /* Reg index = 0x39 */
+#define AUDIN_FIFO2_CTRL1 0x12C /* Reg index = 0x4b */
+ #define AUDIN_FIFO_CTRL1_DIN_POS_2 BIT(7)
+ #define AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK GENMASK(3, 2)
+ #define AUDIN_FIFO_CTRL1_DIN_POS_01_MASK GENMASK(1, 0)
+
+/* INT_CTRL register and bits */
+#define AUDIN_INT_CTRL 0x144 /* Reg index = 0x51 */
+ #define AUDIN_INT_CTRL_FIFO0_OVERFLOW BIT(0)
+ #define AUDIN_INT_CTRL_FIFO0_ADDR_TRIG BIT(1)
+ #define AUDIN_INT_CTRL_FIFO1_OVERFLOW BIT(2)
+ #define AUDIN_INT_CTRL_FIFO1_ADDR_TRIG BIT(3)
+ #define AUDIN_INT_CTRL_FIFO2_OVERFLOW BIT(11)
+ #define AUDIN_INT_CTRL_FIFO2_ADDR_TRIG BIT(12)
+
+/* FIFO_INT register and bits */
+#define AUDIN_FIFO_INT 0x148 /* Reg index = 0x52 */
+ #define AUDIN_FIFO_INT_FIFO0_OVERFLOW BIT(0)
+ #define AUDIN_FIFO_INT_FIFO0_ADDR_TRIG BIT(1)
+ #define AUDIN_FIFO_INT_FIFO1_OVERFLOW BIT(2)
+ #define AUDIN_FIFO_INT_FIFO1_ADDR_TRIG BIT(3)
+ #define AUDIN_FIFO_INT_FIFO2_OVERFLOW BIT(11)
+ #define AUDIN_FIFO_INT_FIFO2_ADDR_TRIG BIT(12)
+
+#define AUDIN_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+extern const struct snd_soc_dai_ops audin_toddr_dai_ops;
+extern const struct snd_soc_dai_ops audin_decoder_i2s_dai_ops;
+extern struct device_attribute dev_attr_dump_regs;
+
+snd_pcm_uframes_t audin_toddr_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
+
+#endif /* _MESON_AUDIN_H */
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH RFC 3/6] ASoC: meson: add AUDIN driver
2025-02-10 15:01 ` [PATCH RFC 3/6] ASoC: meson: add AUDIN driver Valerio Setti
@ 2025-02-11 9:21 ` Jerome Brunet
0 siblings, 0 replies; 10+ messages in thread
From: Jerome Brunet @ 2025-02-11 9:21 UTC (permalink / raw)
To: Valerio Setti
Cc: neil.armstrong, khilman, martin.blumenstingl, linux-amlogic,
linux-sound, linux-kernel
On Mon 10 Feb 2025 at 16:01, Valerio Setti <vsetti@baylibre.com> wrote:
> Add support for the AUDIN driver which provides audio input capabilities
> to the Amlogic GXBB platform. As of now it is composed by:
> - I2S decoder which receives I2S audio samples from some external codec;
> - toddr module which transfers collected audio samples to an internal
> FIFO first and then to RAM.
>
> I2S is the only supported audio source as of now, but others are possible
> and can be added in the future.
>
> Signed-off-by: Valerio Setti <vsetti@baylibre.com>
> ---
> sound/soc/meson/Kconfig | 6 +
> sound/soc/meson/Makefile | 4 +
> sound/soc/meson/aiu-encoder-i2s.c | 2 +-
> sound/soc/meson/audin-decoder-i2s.c | 247 +++++++++++++++++
> sound/soc/meson/audin-toddr.c | 403 ++++++++++++++++++++++++++++
> sound/soc/meson/audin.c | 321 ++++++++++++++++++++++
> sound/soc/meson/audin.h | 119 ++++++++
I'd suggest to split this into more changes if you can
> 7 files changed, 1101 insertions(+), 1 deletion(-)
> create mode 100644 sound/soc/meson/audin-decoder-i2s.c
> create mode 100644 sound/soc/meson/audin-toddr.c
> create mode 100644 sound/soc/meson/audin.c
> create mode 100644 sound/soc/meson/audin.h
>
> diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
> index 6458d5dc4902..3a86c6be08aa 100644
> --- a/sound/soc/meson/Kconfig
> +++ b/sound/soc/meson/Kconfig
> @@ -12,6 +12,12 @@ config SND_MESON_AIU
> Select Y or M to add support for the Audio output subsystem found
> in the Amlogic Meson8, Meson8b and GX SoC families
>
> +config SND_MESON_AUDIN
> + tristate "Amlogic AUDIN"
> + help
> + Select Y or M to add support for the Audio input subsystem found
> + in the Amlogic GXBB SoC family.
> +
> config SND_MESON_AXG_FIFO
> tristate
> select REGMAP_MMIO
> diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile
> index af75f386feda..e4869af72811 100644
> --- a/sound/soc/meson/Makefile
> +++ b/sound/soc/meson/Makefile
> @@ -9,6 +9,9 @@ snd-soc-meson-aiu-y += aiu-encoder-spdif.o
> snd-soc-meson-aiu-y += aiu-fifo.o
> snd-soc-meson-aiu-y += aiu-fifo-i2s.o
> snd-soc-meson-aiu-y += aiu-fifo-spdif.o
> +snd-soc-meson-audin-y := audin.o
> +snd-soc-meson-audin-y += audin-toddr.o
> +snd-soc-meson-audin-y += audin-decoder-i2s.o
> snd-soc-meson-axg-fifo-y := axg-fifo.o
> snd-soc-meson-axg-frddr-y := axg-frddr.o
> snd-soc-meson-axg-toddr-y := axg-toddr.o
> @@ -28,6 +31,7 @@ snd-soc-meson-g12a-tohdmitx-y := g12a-tohdmitx.o
> snd-soc-meson-t9015-y := t9015.o
>
> obj-$(CONFIG_SND_MESON_AIU) += snd-soc-meson-aiu.o
> +obj-$(CONFIG_SND_MESON_AUDIN) += snd-soc-meson-audin.o
> obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o
> obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o
> obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o
> diff --git a/sound/soc/meson/aiu-encoder-i2s.c b/sound/soc/meson/aiu-encoder-i2s.c
> index d469ff429177..b4d5311eb3c5 100644
> --- a/sound/soc/meson/aiu-encoder-i2s.c
> +++ b/sound/soc/meson/aiu-encoder-i2s.c
> @@ -240,7 +240,7 @@ static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream,
> struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
>
> clk_disable_unprepare(aiu->i2s_extra.clks[AOCLK_DIV_GATE].clk);
> -
> +
?
> return 0;
> }
>
> diff --git a/sound/soc/meson/audin-decoder-i2s.c b/sound/soc/meson/audin-decoder-i2s.c
> new file mode 100644
> index 000000000000..a6c01399af0c
> --- /dev/null
> +++ b/sound/soc/meson/audin-decoder-i2s.c
> @@ -0,0 +1,247 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2025 BayLibre, SAS.
> +// Author: Valerio Setti <vsetti@baylibre.com>
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dai.h>
> +
> +#include "audin.h"
> +
> +static int audin_decoder_i2s_setup_desc(struct snd_pcm_hw_params *params,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_component *component = dai->component;
> + int val;
> +
> + /* I2S decoder always outputs 24bits to the FIFO according to the
Looks like you are using the network comment style. It's not the one we
use in ASoC
> + * manual. The only thing we can do is mask some bits as follows:
> + * - 0: 16 bit
> + * - 1: 18 bits (not exposed as supported format)
> + * - 2: 20 bits (not exposed as supported format)
> + * - 3: 24 bits
> + *
> + * We force 24 bit output here and filter unnecessary ones at the FIFO
> + * stage.
That's something I have seen on the axg series and it was not the best.
I'd prefer if you properly setup this element, if possible.
> + * Note: data is left-justified, so in case of 16 bits samples, this
> + * means that the LSB is to be discarded at FIFO level and the
> + * relevant part is in bits [23:8].
> + */
> + switch (params_width(params)) {
> + case 16:
> + case 24:
> + val = 3;
> + break;
> + default:
> + dev_err(dai->dev, "Error: wrong sample width %d",
> + params_physical_width(params));
> + return -EINVAL;
> + }
> + val = FIELD_PREP(AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK, val);
> + snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
> + AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK, val);
> +
> + /* The manual claims that this platform supports up to 4 streams
> + * (8 channels), but only 1 stream (2 channels) is supported ATM.
> + */
> + val = FIELD_PREP(AUDIN_I2SIN_CTRL_I2SIN_CHAN_EN_MASK, 1);
> + snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
> + AUDIN_I2SIN_CTRL_I2SIN_CHAN_EN_MASK, val);
> +
> + return 0;
> +}
> +
> +static int audin_decoder_i2s_set_clocks(struct snd_soc_component *component,
> + struct snd_pcm_hw_params *params,
> + struct snd_soc_dai *dai)
> +{
> + struct audin *audin = snd_soc_component_get_drvdata(component);
> + unsigned int sample_rate = params_rate(params);
> + unsigned long mclk;
> + int ret;
> +
> + mclk = clk_get_rate(audin->bulk_clks[MCLK].clk);
> +
> + /* Set mclk to bclk ratio.
> + * We're going to use the new/finer clock divider (BCLK_MORE_DIV) for
> + * this, so let's keep the legacy one (BCLK_DIV) as passthrough.
> + */
> + ret = clk_set_rate(audin->aoclk_basic_div, mclk);
> + if (ret) {
> + dev_err(dai->dev, "Failed to set aoclk_basic_div %d\n", ret);
> + return ret;
> + }
The clocks are shared with the playback part, so you'll have to take
care of that
I suspect there will be a need to use clk_set_rate_exclusive() around
here, otherwise you risk wrecking an ongoing playback. You'll have to
update the playback part too to use it.
> +
> + /* We're going for a fixed bclk to lrclk ratio of 64. */
> + ret = clk_set_rate(audin->aoclk_more_div, sample_rate * 64UL);
Why ? you support only 16 and 24 bits samples that is odd.
I'd recommend using the regular TDM parameters for this, such as the slot-width.
> + if (ret) {
> + dev_err(dai->dev, "Failed to set aoclk_more_div %d\n", ret);
> + return ret;
> + }
> +
> + ret = clk_set_rate(audin->lrclk_div, sample_rate);
> + if (ret) {
> + dev_err(dai->dev, "Failed to set lrclk_div %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
To properly handle the sharing of the clocks, I'm starting to think
something similar to the AXG is going to be needed.
On the AXG, rather than exporting the clocks, 1 compoment is
responsible for the interface (owning the pad and setting the clocks)
and others are responsible for the actual work, the formatters.
On this platform, the i2s AIU would provide both the interface and the
formatter but the organisation would be the same. That would take of
possible clock conflict.
Making such change will be tricky DT wise, but I think it might be
possible w/o breaking the ABI
> +
> +static int audin_decoder_i2s_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_component *component = dai->component;
> + struct audin *audin = snd_soc_component_get_drvdata(component);
> + int ret;
> +
> + ret = audin_decoder_i2s_setup_desc(params, dai);
> + if (ret) {
> + dev_err(dai->dev, "setting i2s desc failed\n");
> + return ret;
> + }
> +
> + ret = audin_decoder_i2s_set_clocks(component, params, dai);
> + if (ret) {
> + dev_err(dai->dev, "setting i2s clocks failed\n");
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(audin->aoclk_div_gate);
> + if (ret)
> + return ret;
Since this driver should 'care' which clock can gate, you should
actually enable them all, not only this one.
If you had to enable only one, it would be the lrclk one since it is the
leaf of this clock tree.
> +
> + return 0;
> +}
> +
> +static int audin_decoder_i2s_hw_free(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct audin *audin = snd_soc_component_get_drvdata(dai->component);
> +
> + clk_disable_unprepare(audin->aoclk_div_gate);
> +
> + return 0;
> +}
> +
> +static int audin_decoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
> +{
> + struct snd_soc_component *component = dai->component;
> + unsigned int val = 0;
> +
> + /* Only CPU Master / Codec Slave supported ATM */
> + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP)
> + return -EINVAL;
> +
> + /* Use clocks from AIU and not from the pads since we only want to
> + * support master mode.
> + */
> + val = AUDIN_I2SIN_CTRL_I2SIN_CLK_SEL |
> + AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SEL |
> + AUDIN_I2SIN_CTRL_I2SIN_DIR;
> + snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL, val, val);
> +
> + switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
> + case SND_SOC_DAIFMT_IB_NF:
> + val = AUDIN_I2SIN_CTRL_I2SIN_POS_SYNC;
> + break;
> + case SND_SOC_DAIFMT_NB_NF:
> + val = 0;
> + break;
> + default:
> + dev_err(dai->dev, "Error: unsupported format %x", fmt);
> + return -EINVAL;
> + }
> + snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
> + AUDIN_I2SIN_CTRL_I2SIN_POS_SYNC, val);
> +
> + /* MSB data starts 1 clock cycle after LRCLK transition, as per I2S
> + * specs.
> + */
What about TDM ?
> + val = FIELD_PREP(AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SKEW_MASK, 1);
> + snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
> + AUDIN_I2SIN_CTRL_I2SIN_LRCLK_INV |
> + AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SKEW_MASK,
> + val);
> +
> + return 0;
> +}
> +
> +static int audin_decoder_i2s_trigger(struct snd_pcm_substream *substream,
> + int cmd, struct snd_soc_dai *dai)
> +{
> + struct snd_soc_component *component = dai->component;
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
> + AUDIN_I2SIN_CTRL_I2SIN_EN,
> + AUDIN_I2SIN_CTRL_I2SIN_EN);
> + break;
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + case SNDRV_PCM_TRIGGER_STOP:
> + snd_soc_component_update_bits(component, AUDIN_I2SIN_CTRL,
> + AUDIN_I2SIN_CTRL_I2SIN_EN, 0);
> + break;
> + default:
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> +static int audin_decoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
> + unsigned int freq, int dir)
> +{
> + struct audin *audin = snd_soc_component_get_drvdata(dai->component);
> + int ret;
> +
> + if (WARN_ON(clk_id != 0))
> + return -EINVAL;
> +
> + if (dir == SND_SOC_CLOCK_IN)
> + return 0;
> +
> + ret = clk_set_rate(audin->bulk_clks[MCLK].clk, freq);
> + if (ret)
> + dev_err(dai->dev, "Failed to set sysclk %d", ret);
> +
> + return ret;
> +}
> +
> +static int audin_decoder_i2s_startup(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct audin *audin = snd_soc_component_get_drvdata(dai->component);
> + int ret;
> +
> + ret = clk_bulk_prepare_enable(audin->bulk_clks_num, audin->bulk_clks);
> + if (ret)
> + dev_err(dai->dev, "Failed to enable bulk clocks %d\n", ret);
> +
> + return ret;
> +}
> +
> +static void audin_decoder_i2s_shutdown(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct audin *audin = snd_soc_component_get_drvdata(dai->component);
> +
> + clk_bulk_disable_unprepare(audin->bulk_clks_num, audin->bulk_clks);
> +}
> +
> +const struct snd_soc_dai_ops audin_decoder_i2s_dai_ops = {
> + .hw_params = audin_decoder_i2s_hw_params,
> + .hw_free = audin_decoder_i2s_hw_free,
> + .set_fmt = audin_decoder_i2s_set_fmt,
> + .set_sysclk = audin_decoder_i2s_set_sysclk,
> + .startup = audin_decoder_i2s_startup,
> + .shutdown = audin_decoder_i2s_shutdown,
> + .trigger = audin_decoder_i2s_trigger,
> +};
> diff --git a/sound/soc/meson/audin-toddr.c b/sound/soc/meson/audin-toddr.c
> new file mode 100644
> index 000000000000..14db8b9587e8
> --- /dev/null
> +++ b/sound/soc/meson/audin-toddr.c
> @@ -0,0 +1,403 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2025 BayLibre, SAS.
> +// Author: Valerio Setti <vsetti@baylibre.com>
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <sound/pcm_params.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/hrtimer.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dai.h>
> +#include <dt-bindings/sound/meson-audin.h>
> +
> +#include "audin.h"
> +
> +struct fifo_regs {
> + unsigned int start;
> + unsigned int end;
> + unsigned int ptr;
> + unsigned int intr;
> + unsigned int rdptr;
> + unsigned int ctrl;
> + unsigned int ctrl1;
> + unsigned int wrap;
> +};
> +
> +struct fifo_regs_bit_masks {
> + unsigned int overflow_en;
> + unsigned int addr_trigger_en;
> + unsigned int overflow_set;
> + unsigned int addr_trigger_set;
> +};
> +
> +#define AUDIN_FIFO_COUNT 3
> +
> +struct fifo_regs audin_fifo_regs[AUDIN_FIFO_COUNT] = {
> + [0] = {
> + .start = AUDIN_FIFO0_START,
> + .end = AUDIN_FIFO0_END,
> + .ptr = AUDIN_FIFO0_PTR,
> + .intr = AUDIN_FIFO0_INTR,
> + .rdptr = AUDIN_FIFO0_RDPTR,
> + .ctrl = AUDIN_FIFO0_CTRL,
> + .ctrl1 = AUDIN_FIFO0_CTRL1,
> + .wrap = AUDIN_FIFO0_WRAP,
> + },
> + [1] = {
> + .start = AUDIN_FIFO1_START,
> + .end = AUDIN_FIFO1_END,
> + .ptr = AUDIN_FIFO1_PTR,
> + .intr = AUDIN_FIFO1_INTR,
> + .rdptr = AUDIN_FIFO1_RDPTR,
> + .ctrl = AUDIN_FIFO1_CTRL,
> + .ctrl1 = AUDIN_FIFO1_CTRL1,
> + .wrap = AUDIN_FIFO1_WRAP,
> + },
> + [2] = {
> + .start = AUDIN_FIFO2_START,
> + .end = AUDIN_FIFO2_END,
> + .ptr = AUDIN_FIFO2_PTR,
> + .intr = AUDIN_FIFO2_INTR,
> + .rdptr = AUDIN_FIFO2_RDPTR,
> + .ctrl = AUDIN_FIFO2_CTRL,
> + .ctrl1 = AUDIN_FIFO2_CTRL1,
> + .wrap = AUDIN_FIFO2_WRAP,
> + }
> +};
> +
> +struct fifo_regs_bit_masks audin_fifo_regs_bit_masks[AUDIN_FIFO_COUNT] = {
> + [0] = {
> + .overflow_en = AUDIN_INT_CTRL_FIFO0_OVERFLOW,
> + .addr_trigger_en = AUDIN_INT_CTRL_FIFO0_ADDR_TRIG,
> + .overflow_set = AUDIN_FIFO_INT_FIFO0_OVERFLOW,
> + .addr_trigger_set = AUDIN_FIFO_INT_FIFO0_ADDR_TRIG,
> + },
> + [1] = {
> + .overflow_en = AUDIN_INT_CTRL_FIFO1_OVERFLOW,
> + .addr_trigger_en = AUDIN_INT_CTRL_FIFO1_ADDR_TRIG,
> + .overflow_set = AUDIN_FIFO_INT_FIFO1_OVERFLOW,
> + .addr_trigger_set = AUDIN_FIFO_INT_FIFO1_ADDR_TRIG,
> + },
> + [2] = {
> + .overflow_en = AUDIN_INT_CTRL_FIFO2_OVERFLOW,
> + .addr_trigger_en = AUDIN_INT_CTRL_FIFO2_ADDR_TRIG,
> + .overflow_set = AUDIN_FIFO_INT_FIFO2_OVERFLOW,
> + .addr_trigger_set = AUDIN_FIFO_INT_FIFO2_ADDR_TRIG,
> + },
This looks like you are putting multiple instance of the same thing into
a single file. It would be better to have dideciated FIFO driver and
instanciate 3 times through DT instead.
> +
> +};
> +
> +/* This is the size of the FIFO (i.e. 64*64 bytes). */
> +#define AUDIN_FIFO_I2S_BLOCK 4096
> +
> +static struct snd_pcm_hardware toddr_pcm_hw = {
> + .info = (SNDRV_PCM_INFO_INTERLEAVED |
> + SNDRV_PCM_INFO_MMAP |
> + SNDRV_PCM_INFO_MMAP_VALID |
> + SNDRV_PCM_INFO_PAUSE),
> + .formats = AUDIN_FORMATS,
> + .rate_min = 5512,
> + .rate_max = 192000,
> + .channels_min = 2,
> + .channels_max = 2,
> + .period_bytes_min = 2*AUDIN_FIFO_I2S_BLOCK,
> + .period_bytes_max = AUDIN_FIFO_I2S_BLOCK * USHRT_MAX,
> + .periods_min = 2,
> + .periods_max = UINT_MAX,
> +
> + /* No real justification for this */
> + .buffer_bytes_max = 1 * 1024 * 1024,
> +};
> +
> +struct audin_fifo {
> + const struct fifo_regs *reg;
> + const struct fifo_regs_bit_masks *reg_bit_masks;
> + struct snd_pcm_hardware *pcm_hw;
> + struct clk *pclk;
> +
> + /* The AUDIN peripheral has an IRQ to signal when data is received, but
> + * it cannot grant a periodic behavior. The reason is that the register
> + * which holds the address which triggers the IRQ must be updated
> + * continuously. Therefore we use a periodic timer.
> + */
> + struct hrtimer polling_timer;
> + int poll_time_ns;
> + struct snd_pcm_substream *substream;
> +};
> +
> +static int audin_toddr_trigger(struct snd_pcm_substream *substream, int cmd,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_component *component = dai->component;
> + struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_RESUME:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + snd_soc_component_update_bits(component, fifo->reg->ctrl,
> + AUDIN_FIFO_CTRL_EN,
> + AUDIN_FIFO_CTRL_EN);
> + break;
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + case SNDRV_PCM_TRIGGER_STOP:
> + snd_soc_component_update_bits(component, fifo->reg->ctrl,
> + AUDIN_FIFO_CTRL_EN, 0);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int audin_toddr_prepare(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_component *component = dai->component;
> + struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + dma_addr_t dma_end = runtime->dma_addr + runtime->dma_bytes - 8;
> + unsigned int val;
> +
> + /* Setup memory boundaries */
> + snd_soc_component_write(component, fifo->reg->start, runtime->dma_addr);
> + snd_soc_component_write(component, fifo->reg->ptr, runtime->dma_addr);
> + snd_soc_component_write(component, fifo->reg->end, dma_end);
> +
> + /* Load new addresses */
> + val = AUDIN_FIFO_CTRL_LOAD | AUDIN_FIFO_CTRL_UG;
> + snd_soc_component_update_bits(component, fifo->reg->ctrl, val, val);
> +
> + /* Reset */
> + snd_soc_component_update_bits(dai->component, fifo->reg->ctrl,
> + AUDIN_FIFO_CTRL_RST,
> + AUDIN_FIFO_CTRL_RST);
> +
> + return 0;
> +}
> +
> +static int audin_toddr_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *params,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_component *component = dai->component;
> + struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
> + unsigned int val;
> +
> + switch (params_width(params)) {
> + case 16:
> + /* FIFO is filled line by line and each of them is 8 bytes. The
> + * problem is that each line is filled starting from the end,
> + * so we need to properly reorder them before moving to the
> + * RAM. This is the value required to properly re-order 16 bits
> + * samples.
> + */
> + val = FIELD_PREP(AUDIN_FIFO_CTRL_ENDIAN_MASK, 6);
> + snd_soc_component_update_bits(component, fifo->reg->ctrl,
> + AUDIN_FIFO_CTRL_ENDIAN_MASK, val);
> +
> + /* The I2S input decoder passed 24 bits of left-justified data
> + * but samples were 16 bits. Therefore we drop the LSB.
> + */
Ok I see
> + val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_POS_01_MASK, 1);
> + snd_soc_component_update_bits(component, fifo->reg->ctrl1,
> + AUDIN_FIFO_CTRL1_DIN_POS_01_MASK,
> + val);
> +
> + /* Set sample size to 2 bytes (16 bit) */
> + val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK, 1);
> + snd_soc_component_update_bits(component, fifo->reg->ctrl1,
> + AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK,
> + val);
> + break;
> + case 24:
> + /* The same as above but in this case we need to reorder 32 bits
> + * samples (because 24 bits samples are stored as 32 bits).
> + */
Huu ... now I'm lost. I thought you were always passing 24 bits from the
decoder ?
> + val = FIELD_PREP(AUDIN_FIFO_CTRL_ENDIAN_MASK, 4);
> + snd_soc_component_update_bits(component, fifo->reg->ctrl,
> + AUDIN_FIFO_CTRL_ENDIAN_MASK,
> + val);
> +
> + val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_POS_01_MASK, 0);
> + snd_soc_component_update_bits(component, fifo->reg->ctrl1,
> + AUDIN_FIFO_CTRL1_DIN_POS_01_MASK,
> + val);
> +
> + /* Set sample size to 3 bytes (24 bit) */
> + val = FIELD_PREP(AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK, 2);
> + snd_soc_component_update_bits(component, fifo->reg->ctrl1,
> + AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK,
> + val);
> + break;
> + default:
> + dev_err(dai->dev, "Unsupported physical width %u\n",
> + params_physical_width(params));
> + return -EINVAL;
> + }
> +
> + /* This is a bit counterintuitive. Even though the platform has a single pin
> + * for I2S input which would mean that we can only support 2 channels,
> + * doing so would cause samples to be stored in a weird way into the FIFO:
> + * all the samples from the 1st channel on the 1st half of the FIFO, then
> + * samples from the 2nd channel in the other half. Of course extra work
> + * would be required to properly interleave them before returning to the
> + * userspace.
> + * Setting a single channel mode instead solves the problem: samples from
> + * 1st and 2nd channel are stored interleaved and sequentially in the FIFO.
> + */
> + val = FIELD_PREP(AUDIN_FIFO_CTRL_CHAN_MASK, 1);
> + snd_soc_component_update_bits(component, fifo->reg->ctrl,
> + AUDIN_FIFO_CTRL_CHAN_MASK, val);
> +
> + /* Setup the period for the polling timer. */
> + fifo->poll_time_ns = 1000000000 / params_rate(params) *
> + params_period_size(params);
> +
> + return 0;
> +}
> +
> +static enum hrtimer_restart timer_cb(struct hrtimer *timer)
> +{
> + struct audin_fifo *fifo = container_of(timer, struct audin_fifo,
> + polling_timer);
> + snd_pcm_period_elapsed(fifo->substream);
> + hrtimer_forward_now(timer, fifo->poll_time_ns);
> + return HRTIMER_RESTART;
> +}
> +
> +static int audin_toddr_startup(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
> + int ret;
> +
> + snd_soc_set_runtime_hwparams(substream, fifo->pcm_hw);
> +
> + /* Check runtime parameters */
> + ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
> + SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
> + AUDIN_FIFO_I2S_BLOCK);
> + if (ret) {
> + dev_err(dai->dev, "Failed to set runtime constraint %d\n", ret);
> + return ret;
> + }
> +
> + ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
> + SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
> + AUDIN_FIFO_I2S_BLOCK);
> + if (ret) {
> + dev_err(dai->dev, "Failed to set runtime constraint %d\n", ret);
> + return ret;
> + }
> +
> + ret = clk_prepare_enable(fifo->pclk);
> + if (ret) {
> + dev_err(dai->dev, "Failed to enable PCLK %d\n", ret);
> + return ret;
> + }
> +
> + /* Start the reporting timer */
> + fifo->substream = substream;
> + hrtimer_start(&fifo->polling_timer, fifo->poll_time_ns,
> + HRTIMER_MODE_REL);
> +
> + return ret;
> +}
> +
> +static void audin_toddr_shutdown(struct snd_pcm_substream *substream,
> + struct snd_soc_dai *dai)
> +{
> + struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
> +
> + hrtimer_cancel(&fifo->polling_timer);
> + clk_disable_unprepare(fifo->pclk);
> +}
> +
> +snd_pcm_uframes_t audin_toddr_pointer(struct snd_soc_component *component,
> + struct snd_pcm_substream *substream)
> +{
> + struct snd_soc_pcm_runtime *rtd = substream->private_data;
> + struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
> + struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
> + struct snd_pcm_runtime *runtime = substream->runtime;
> + unsigned int start, ptr;
> +
> + start = snd_soc_component_read(component, fifo->reg->start);
> + ptr = snd_soc_component_read(component, fifo->reg->ptr);
> +
> + return bytes_to_frames(runtime, ptr - start);
> +}
> +
> +static int audin_toddr_dai_probe(struct snd_soc_dai *dai)
> +{
> + struct audin *audin = snd_soc_component_get_drvdata(dai->component);
> + struct audin_fifo *fifo;
> +
> + fifo = kzalloc(sizeof(*fifo), GFP_KERNEL);
> + if (!fifo)
> + return -ENOMEM;
> +
> + if (dai->id >= AUDIN_FIFO_COUNT) {
> + dev_err(dai->dev, "Invalid DAI ID %d\n", dai->id);
> + kfree(fifo);
> + return -EINVAL;
> + }
> +
> + fifo->reg = &audin_fifo_regs[dai->id];
> + fifo->reg_bit_masks = &audin_fifo_regs_bit_masks[dai->id];
> + fifo->pcm_hw = &toddr_pcm_hw;
> + fifo->pclk = audin->bulk_clks[PCLK].clk;
> + hrtimer_init(&fifo->polling_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> + fifo->polling_timer.function = timer_cb;
> +
> + snd_soc_dai_dma_data_set_capture(dai, fifo);
> +
> + return 0;
> +}
> +
> +static int audin_toddr_dai_remove(struct snd_soc_dai *dai)
> +{
> + kfree(snd_soc_dai_dma_data_get_capture(dai));
> +
> + return 0;
> +}
> +
> +static int audin_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_card *card = rtd->card->snd_card;
> + struct audin_fifo *fifo = snd_soc_dai_dma_data_get_capture(dai);
> + size_t size = fifo->pcm_hw->buffer_bytes_max;
> + int ret;
> +
> + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
> + if (ret) {
> + dev_err(dai->dev, "Failed to set DMA mask %d\n", ret);
> + return ret;
> + }
> +
> + ret = snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
> + card->dev, size, size);
> + if (ret) {
> + dev_err(dai->dev, "Failed to set PCM managed buffer %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +const struct snd_soc_dai_ops audin_toddr_dai_ops = {
> + .trigger = audin_toddr_trigger,
> + .prepare = audin_toddr_prepare,
> + .hw_params = audin_toddr_hw_params,
> + .startup = audin_toddr_startup,
> + .shutdown = audin_toddr_shutdown,
> + .pcm_new = audin_toddr_pcm_new,
> + .probe = audin_toddr_dai_probe,
> + .remove = audin_toddr_dai_remove,
> +};
> diff --git a/sound/soc/meson/audin.c b/sound/soc/meson/audin.c
> new file mode 100644
> index 000000000000..254d646260f1
> --- /dev/null
> +++ b/sound/soc/meson/audin.c
> @@ -0,0 +1,321 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (c) 2025 BayLibre, SAS.
> +// Author: Valerio Setti <vsetti@baylibre.com>
> +
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/of_platform.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dai.h>
> +#include <dt-bindings/sound/meson-audin.h>
> +
> +#include "audin.h"
> +
> +static const char * const audin_fifo_input_sel_texts[] = {
> + "SPDIF", "I2S", "PCM", "HDMI", "Demodulator"
> +};
> +
> +static SOC_ENUM_SINGLE_DECL(audin_fifo0_input_sel_enum, AUDIN_FIFO0_CTRL,
> + AUDIN_FIFO_CTRL_DIN_SEL_OFF,
> + audin_fifo_input_sel_texts);
> +
> +static const struct snd_kcontrol_new audin_fifo0_input_sel_mux =
> + SOC_DAPM_ENUM("FIFO0 SRC SEL", audin_fifo0_input_sel_enum);
> +
> +static SOC_ENUM_SINGLE_DECL(audin_fifo1_input_sel_enum, AUDIN_FIFO1_CTRL,
> + AUDIN_FIFO_CTRL_DIN_SEL_OFF,
> + audin_fifo_input_sel_texts);
> +
> +static const struct snd_kcontrol_new audin_fifo1_input_sel_mux =
> + SOC_DAPM_ENUM("FIFO1 SRC SEL", audin_fifo1_input_sel_enum);
> +
> +static SOC_ENUM_SINGLE_DECL(audin_fifo2_input_sel_enum, AUDIN_FIFO2_CTRL,
> + AUDIN_FIFO_CTRL_DIN_SEL_OFF,
> + audin_fifo_input_sel_texts);
> +
> +static const struct snd_kcontrol_new audin_fifo2_input_sel_mux =
> + SOC_DAPM_ENUM("FIFO2 SRC SEL", audin_fifo2_input_sel_enum);
> +
> +static const struct snd_soc_dapm_widget audin_cpu_dapm_widgets[] = {
> + SND_SOC_DAPM_MUX("FIFO0 SRC SEL", SND_SOC_NOPM, 0, 0,
> + &audin_fifo0_input_sel_mux),
> + SND_SOC_DAPM_MUX("FIFO1 SRC SEL", SND_SOC_NOPM, 0, 0,
> + &audin_fifo1_input_sel_mux),
> + SND_SOC_DAPM_MUX("FIFO2 SRC SEL", SND_SOC_NOPM, 0, 0,
> + &audin_fifo2_input_sel_mux),
> +};
> +
> +static const struct snd_soc_dapm_route audin_cpu_dapm_routes[] = {
> + { "FIFO0 SRC SEL", "I2S", "I2S Decoder Capture" },
> + { "FIFO1 SRC SEL", "I2S", "I2S Decoder Capture" },
> + { "FIFO2 SRC SEL", "I2S", "I2S Decoder Capture" },
> + { "TODDR 0 Capture", NULL, "FIFO0 SRC SEL" },
> + { "TODDR 1 Capture", NULL, "FIFO1 SRC SEL" },
> + { "TODDR 2 Capture", NULL, "FIFO2 SRC SEL" },
> +};
> +
> +static int audin_cpu_of_xlate_dai_name(struct snd_soc_component *component,
> + const struct of_phandle_args *args,
> + const char **dai_name)
> +{
> + struct snd_soc_dai *dai;
> + int id;
> +
> + if (args->args_count != 1) {
> + dev_err(component->dev, "Wrong number of arguments %d\n",
> + args->args_count);
> + return -EINVAL;
> + }
> +
> + id = args->args[0];
> +
> + if (id < 0 || id >= component->num_dai) {
> + dev_err(component->dev, "Invalid ID %d\n", id);
> + return -EINVAL;
> + }
> +
> + for_each_component_dais(component, dai) {
> + if (id == 0)
> + break;
> + id--;
> + }
> +
> + *dai_name = dai->driver->name;
> +
> + return 0;
> +}
> +
> +static int audin_cpu_component_probe(struct snd_soc_component *component)
> +{
> + struct audin *audin = snd_soc_component_get_drvdata(component);
> +
> + /* Required for the FIFO Source control operation */
> + return clk_prepare_enable(audin->bulk_clks[INPUT].clk);
> +}
> +
> +static void audin_cpu_component_remove(struct snd_soc_component *component)
> +{
> + struct audin *audin = snd_soc_component_get_drvdata(component);
> +
> + clk_disable_unprepare(audin->bulk_clks[INPUT].clk);
> +}
> +
> +static const struct snd_soc_component_driver audin_cpu_component = {
> + .name = "AUDIN CPU",
> + .dapm_widgets = audin_cpu_dapm_widgets,
> + .num_dapm_widgets = ARRAY_SIZE(audin_cpu_dapm_widgets),
> + .dapm_routes = audin_cpu_dapm_routes,
> + .num_dapm_routes = ARRAY_SIZE(audin_cpu_dapm_routes),
> + .of_xlate_dai_name = audin_cpu_of_xlate_dai_name,
> + .pointer = audin_toddr_pointer,
> + .probe = audin_cpu_component_probe,
> + .remove = audin_cpu_component_remove,
> +#ifdef CONFIG_DEBUG_FS
> + .debugfs_prefix = "audin-cpu",
> +#endif
> +};
> +
> +static struct snd_soc_dai_driver audin_cpu_dai_drv[] = {
> + [CPU_AUDIN_TODDR_0] = {
> + .name = "TODDR 0",
> + .capture = {
> + .stream_name = "TODDR 0 Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_CONTINUOUS,
> + .rate_min = 5512,
> + .rate_max = 192000,
> + .formats = AUDIN_FORMATS,
> + },
> + .ops = &audin_toddr_dai_ops,
> + },
> + [CPU_AUDIN_TODDR_1] = {
> + .name = "TODDR 1",
> + .capture = {
> + .stream_name = "TODDR 1 Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_CONTINUOUS,
> + .rate_min = 5512,
> + .rate_max = 192000,
> + .formats = AUDIN_FORMATS,
> + },
> + .ops = &audin_toddr_dai_ops,
> + },
> + [CPU_AUDIN_TODDR_2] = {
> + .name = "TODDR 2",
> + .capture = {
> + .stream_name = "TODDR 2 Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_CONTINUOUS,
> + .rate_min = 5512,
> + .rate_max = 192000,
> + .formats = AUDIN_FORMATS,
> + },
> + .ops = &audin_toddr_dai_ops,
> + },
> + [CPU_I2S_DECODER] = {
> + .name = "I2S Decoder",
> + .capture = {
> + .stream_name = "I2S Decoder Capture",
> + .channels_min = 2,
> + .channels_max = 2,
> + .rates = SNDRV_PCM_RATE_8000_192000,
> + .formats = AUDIN_FORMATS,
> + },
> + .ops = &audin_decoder_i2s_dai_ops,
> + },
> +};
> +
> +static const struct regmap_config audin_regmap_cfg = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = 0x308,
> +};
> +
> +static const char * const clk_bulk_ids[] = {
> + "i2s_pclk",
> + "i2s_aoclk",
> + "i2s_mclk",
> + "i2s_mixer",
> + "i2s_input_clk",
> +};
> +
> +static int audin_clk_single_get(struct device *dev, const unsigned char *id,
> + bool enable, struct clk **clk)
> +{
> + *clk = devm_clk_get(dev, id);
> + if (IS_ERR(*clk)) {
> + dev_err(dev, "Failed to get %s clock %ld\n", id, PTR_ERR(*clk));
> + return PTR_ERR(*clk);
> + }
> +
> + if (enable)
> + return clk_prepare_enable(*clk);
> +
> + return 0;
> +}
> +
> +static int audin_clk_get(struct device *dev)
> +{
> + struct audin *audin = dev_get_drvdata(dev);
> + struct clk *pclk;
> + int i, ret;
> +
> + ret = audin_clk_single_get(dev, "pclk", true, &pclk);
> + if (ret)
> + return ret;
> +
> + audin->bulk_clks_num = ARRAY_SIZE(clk_bulk_ids);
> + audin->bulk_clks = devm_kcalloc(dev, audin->bulk_clks_num,
> + sizeof(struct clk_bulk_data),
> + GFP_KERNEL);
> + if (!audin->bulk_clks)
> + return -ENOMEM;
> +
> + for (i = 0; i < ARRAY_SIZE(clk_bulk_ids); i++)
> + audin->bulk_clks[i].id = clk_bulk_ids[i];
> +
> + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(clk_bulk_ids),
> + audin->bulk_clks);
> + if (ret) {
> + dev_err(dev, "Failed to get bulk clocks %d\n", ret);
> + return ret;
> + }
> +
> + ret = audin_clk_single_get(dev, "i2s_aoclk_div_gate", false,
> + &audin->aoclk_div_gate);
> + if (ret)
> + return ret;
> +
> + ret = audin_clk_single_get(dev, "i2s_aoclk_basic_div", false,
> + &audin->aoclk_basic_div);
> + if (ret)
> + return ret;
> +
> + ret = audin_clk_single_get(dev, "i2s_aoclk_more_div", false,
> + &audin->aoclk_more_div);
> + if (ret)
> + return ret;
> +
> + ret = audin_clk_single_get(dev, "i2s_lrclk_div", false,
> + &audin->lrclk_div);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int audin_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + void __iomem *regs;
> + struct regmap *map;
> + struct audin *audin;
> + int ret;
> +
> + audin = devm_kzalloc(dev, sizeof(*audin), GFP_KERNEL);
> + if (!audin)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, audin);
> +
> + ret = device_reset(dev);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to reset device\n");
> +
> + regs = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(regs))
> + return PTR_ERR(regs);
> +
> + map = devm_regmap_init_mmio(dev, regs, &audin_regmap_cfg);
> + if (IS_ERR(map)) {
> + dev_err(dev, "failed to init regmap: %ld\n",
> + PTR_ERR(map));
> + return PTR_ERR(map);
> + }
> +
> + ret = audin_clk_get(dev);
> + if (ret)
> + return ret;
> +
> + ret = snd_soc_register_component(dev, &audin_cpu_component,
> + audin_cpu_dai_drv,
> + ARRAY_SIZE(audin_cpu_dai_drv));
> + if (ret) {
> + dev_err(dev, "Failed to register cpu component\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static void audin_remove(struct platform_device *pdev)
> +{
> + snd_soc_unregister_component(&pdev->dev);
> +}
> +
> +static const struct of_device_id audin_of_match[] = {
> + { .compatible = "amlogic,audin-gxbb", .data = NULL },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, audin_of_match);
> +
> +static struct platform_driver audin_pdrv = {
> + .probe = audin_probe,
> + .remove = audin_remove,
> + .driver = {
> + .name = "meson-audin",
> + .of_match_table = audin_of_match,
> + },
> +};
> +module_platform_driver(audin_pdrv);
> +
> +MODULE_DESCRIPTION("Meson AUDIN Driver");
> +MODULE_AUTHOR("Valerio Setti <vsetti@baylibre.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/sound/soc/meson/audin.h b/sound/soc/meson/audin.h
> new file mode 100644
> index 000000000000..eb1dbbaeabcc
> --- /dev/null
> +++ b/sound/soc/meson/audin.h
> @@ -0,0 +1,119 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> +/*
> + * Copyright (c) 2025 BayLibre, SAS.
> + * Author: Valerio Setti <vsetti@baylibre.com>
> + */
> +
> +#ifndef _MESON_AUDIN_H
> +#define _MESON_AUDIN_H
> +
> +struct clk;
> +struct clk_bulk_data;
> +struct device;
> +struct of_phandle_args;
> +struct snd_soc_dai;
> +struct snd_soc_dai_ops;
> +
> +enum audin_bulk_clks_id {
> + PCLK = 0,
> + AOCLK,
> + MCLK,
> + MIXER,
> + INPUT,
> +};
> +
> +struct audin {
> + struct clk_bulk_data *bulk_clks;
> + unsigned int bulk_clks_num;
> + struct clk *aoclk_div_gate;
> + struct clk *aoclk_basic_div;
> + struct clk *aoclk_more_div;
> + struct clk *lrclk_div;
> +};
> +
> +/* I2SIN_CTRL register and bits */
> +#define AUDIN_I2SIN_CTRL 0x040 /* Reg index = 0x10 */
> + #define AUDIN_I2SIN_CTRL_I2SIN_DIR BIT(0)
> + #define AUDIN_I2SIN_CTRL_I2SIN_CLK_SEL BIT(1)
> + #define AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SEL BIT(2)
> + #define AUDIN_I2SIN_CTRL_I2SIN_POS_SYNC BIT(3)
> + #define AUDIN_I2SIN_CTRL_I2SIN_LRCLK_SKEW_MASK GENMASK(6, 4)
> + #define AUDIN_I2SIN_CTRL_I2SIN_LRCLK_INV BIT(7)
> + #define AUDIN_I2SIN_CTRL_I2SIN_SIZE_MASK GENMASK(9, 8)
> + #define AUDIN_I2SIN_CTRL_I2SIN_CHAN_EN_MASK GENMASK(13, 10)
> + #define AUDIN_I2SIN_CTRL_I2SIN_EN BIT(15)
> +
> +/* FIFO0 registers */
> +#define AUDIN_FIFO0_START 0x080 /* Reg index = 0x20 */
> +#define AUDIN_FIFO0_END 0x084 /* Reg index = 0x21 */
> +#define AUDIN_FIFO0_PTR 0x088 /* Reg index = 0x22 */
> +#define AUDIN_FIFO0_INTR 0x08C /* Reg index = 0x23 */
> +#define AUDIN_FIFO0_RDPTR 0x090 /* Reg index = 0x24 */
> +#define AUDIN_FIFO0_WRAP 0x0C4 /* Reg index = 0x31 */
Do you really need these definition in multiple drivers ?
If not, they do not need to be in header
> +
> +/* FIFO1 registers */
> +#define AUDIN_FIFO1_START 0x0CC /* Reg index = 0x33 */
No need to repeat the reg index thing ... just add one comment about the
4 multiplication if you must
> +#define AUDIN_FIFO1_END 0x0D0 /* Reg index = 0x34 */
> +#define AUDIN_FIFO1_PTR 0x0D4 /* Reg index = 0x35 */
> +#define AUDIN_FIFO1_INTR 0x0D8 /* Reg index = 0x36 */
> +#define AUDIN_FIFO1_RDPTR 0x0DC /* Reg index = 0x37 */
> +#define AUDIN_FIFO1_WRAP 0x110 /* Reg index = 0x44 */
> +
> +/* FIFO2 registers */
> +#define AUDIN_FIFO2_START 0x114 /* Reg index = 0x45 */
> +#define AUDIN_FIFO2_END 0x118 /* Reg index = 0x46 */
> +#define AUDIN_FIFO2_PTR 0x11C /* Reg index = 0x47 */
> +#define AUDIN_FIFO2_INTR 0x120 /* Reg index = 0x48 */
> +#define AUDIN_FIFO2_RDPTR 0x124 /* Reg index = 0x49 */
> +#define AUDIN_FIFO2_WRAP 0x140 /* Reg index = 0x50 */
> +
> +/* FIFOx CTRL registers and bits */
> +#define AUDIN_FIFO0_CTRL 0x094 /* Reg index = 0x25 */
> +#define AUDIN_FIFO1_CTRL 0x0E0 /* Reg index = 0x38 */
> +#define AUDIN_FIFO2_CTRL 0x128 /* Reg index = 0x4a */
> + #define AUDIN_FIFO_CTRL_EN BIT(0)
> + #define AUDIN_FIFO_CTRL_RST BIT(1)
> + #define AUDIN_FIFO_CTRL_LOAD BIT(2)
> + #define AUDIN_FIFO_CTRL_DIN_SEL_OFF 3
> + #define AUDIN_FIFO_CTRL_DIN_SEL_MASK GENMASK(5, 3)
> + #define AUDIN_FIFO_CTRL_ENDIAN_MASK GENMASK(10, 8)
> + #define AUDIN_FIFO_CTRL_CHAN_MASK GENMASK(14, 11)
> + #define AUDIN_FIFO_CTRL_UG BIT(15)
> +
> +/* FIFOx_CTRL1 registers and bits */
> +#define AUDIN_FIFO0_CTRL1 0x098 /* Reg index = 0x26 */
> +#define AUDIN_FIFO1_CTRL1 0x0E4 /* Reg index = 0x39 */
> +#define AUDIN_FIFO2_CTRL1 0x12C /* Reg index = 0x4b */
> + #define AUDIN_FIFO_CTRL1_DIN_POS_2 BIT(7)
> + #define AUDIN_FIFO_CTRL1_DIN_BYTE_NUM_MASK GENMASK(3, 2)
> + #define AUDIN_FIFO_CTRL1_DIN_POS_01_MASK GENMASK(1, 0)
> +
> +/* INT_CTRL register and bits */
> +#define AUDIN_INT_CTRL 0x144 /* Reg index = 0x51 */
> + #define AUDIN_INT_CTRL_FIFO0_OVERFLOW BIT(0)
> + #define AUDIN_INT_CTRL_FIFO0_ADDR_TRIG BIT(1)
> + #define AUDIN_INT_CTRL_FIFO1_OVERFLOW BIT(2)
> + #define AUDIN_INT_CTRL_FIFO1_ADDR_TRIG BIT(3)
> + #define AUDIN_INT_CTRL_FIFO2_OVERFLOW BIT(11)
> + #define AUDIN_INT_CTRL_FIFO2_ADDR_TRIG BIT(12)
> +
> +/* FIFO_INT register and bits */
> +#define AUDIN_FIFO_INT 0x148 /* Reg index = 0x52 */
> + #define AUDIN_FIFO_INT_FIFO0_OVERFLOW BIT(0)
> + #define AUDIN_FIFO_INT_FIFO0_ADDR_TRIG BIT(1)
> + #define AUDIN_FIFO_INT_FIFO1_OVERFLOW BIT(2)
> + #define AUDIN_FIFO_INT_FIFO1_ADDR_TRIG BIT(3)
> + #define AUDIN_FIFO_INT_FIFO2_OVERFLOW BIT(11)
> + #define AUDIN_FIFO_INT_FIFO2_ADDR_TRIG BIT(12)
> +
> +#define AUDIN_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
> + SNDRV_PCM_FMTBIT_S24_LE)
> +
> +extern const struct snd_soc_dai_ops audin_toddr_dai_ops;
> +extern const struct snd_soc_dai_ops audin_decoder_i2s_dai_ops;
> +extern struct device_attribute dev_attr_dump_regs;
> +
> +snd_pcm_uframes_t audin_toddr_pointer(struct snd_soc_component *component,
> + struct snd_pcm_substream *substream);
> +
> +#endif /* _MESON_AUDIN_H */
--
Jerome
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH RFC 4/6] ASoC: meson: add support for AUDIN in gx-card
2025-02-10 15:01 [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB Valerio Setti
` (2 preceding siblings ...)
2025-02-10 15:01 ` [PATCH RFC 3/6] ASoC: meson: add AUDIN driver Valerio Setti
@ 2025-02-10 15:01 ` Valerio Setti
2025-02-10 15:01 ` [PATCH RFC 5/6] arm64: dts: meson-gx: add audin support Valerio Setti
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Valerio Setti @ 2025-02-10 15:01 UTC (permalink / raw)
To: jbrunet, neil.armstrong, khilman, martin.blumenstingl,
linux-amlogic, linux-sound
Cc: linux-kernel, Valerio Setti
Extend the gx-card module in order to support the audio input capabilities
provided by AUDIN driver.
Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
sound/soc/meson/gx-card.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c
index b408cc2bbc91..c59bb18b0951 100644
--- a/sound/soc/meson/gx-card.c
+++ b/sound/soc/meson/gx-card.c
@@ -67,7 +67,8 @@ static int gx_card_parse_i2s(struct snd_soc_card *card,
static int gx_card_cpu_identify(struct snd_soc_dai_link_component *c,
char *match)
{
- if (of_device_is_compatible(c->of_node, DT_PREFIX "aiu")) {
+ if ((of_device_is_compatible(c->of_node, DT_PREFIX "aiu")) ||
+ (of_device_is_compatible(c->of_node, DT_PREFIX "audin"))) {
if (strstr(c->dai_name, match))
return 1;
}
@@ -96,6 +97,8 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
if (gx_card_cpu_identify(dai_link->cpus, "FIFO"))
return meson_card_set_fe_link(card, dai_link, np, true);
+ else if (gx_card_cpu_identify(dai_link->cpus, "TODDR"))
+ return meson_card_set_fe_link(card, dai_link, np, false);
ret = meson_card_set_be_link(card, dai_link, np);
if (ret)
@@ -107,8 +110,11 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
dai_link->num_c2c_params = 1;
} else {
dai_link->no_pcm = 1;
- /* Check if the cpu is the i2s encoder and parse i2s data */
- if (gx_card_cpu_identify(dai_link->cpus, "I2S Encoder"))
+ /* Check if the cpu is the i2s encoder|decoder and parse
+ * i2s data
+ */
+ if (gx_card_cpu_identify(dai_link->cpus, "I2S Encoder") ||
+ gx_card_cpu_identify(dai_link->cpus, "I2S Decoder"))
ret = gx_card_parse_i2s(card, np, index);
}
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH RFC 5/6] arm64: dts: meson-gx: add audin support
2025-02-10 15:01 [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB Valerio Setti
` (3 preceding siblings ...)
2025-02-10 15:01 ` [PATCH RFC 4/6] ASoC: meson: add support for AUDIN in gx-card Valerio Setti
@ 2025-02-10 15:01 ` Valerio Setti
2025-02-10 15:01 ` [PATCH RFC 6/6] arm64: dts: meson-gx: enable audin on odroidc2 platform Valerio Setti
2025-02-11 0:10 ` [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB Rob Herring (Arm)
6 siblings, 0 replies; 10+ messages in thread
From: Valerio Setti @ 2025-02-10 15:01 UTC (permalink / raw)
To: jbrunet, neil.armstrong, khilman, martin.blumenstingl,
linux-amlogic, linux-sound
Cc: linux-kernel, Valerio Setti
Add the AUDIN audio device to the Amlogic GX SoC family DT.
Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 8 +++++++
arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 25 +++++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
index 2673f0dbafe7..594d7de606ec 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
@@ -304,6 +304,14 @@ aiu: audio-controller@5400 {
status = "disabled";
};
+ audin: audio-input-controller@a000 {
+ compatible = "amlogic,audin";
+ #sound-dai-cells = <1>;
+ sound-name-prefix = "AUDIN";
+ reg = <0x0 0xa000 0x0 0x308>;
+ status = "disabled";
+ };
+
uart_A: serial@84c0 {
compatible = "amlogic,meson-gx-uart";
reg = <0x0 0x84c0 0x0 0x18>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
index e2026b7aa03f..2db6916684fc 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi
@@ -94,6 +94,31 @@ &aiu {
resets = <&reset RESET_AIU>;
};
+&audin {
+ compatible = "amlogic,audin-gxbb", "amlogic,audin";
+ clocks = <&clkc CLKID_AIU_GLUE>,
+ <&clkc CLKID_I2S_OUT>,
+ <&clkc CLKID_AOCLK_GATE>,
+ <&clkc CLKID_CTS_AMCLK>,
+ <&clkc CLKID_MIXER_IFACE>,
+ <&clkc CLKID_I2S_SPDIF>,
+ <&aiu AIU_AOCLK_DIV_GATE>,
+ <&aiu AIU_AOCLK_BASIC_DIV>,
+ <&aiu AIU_AOCLK_MORE_DIV>,
+ <&aiu AIU_LRCLK_DIV>;
+ clock-names = "pclk",
+ "i2s_pclk",
+ "i2s_aoclk",
+ "i2s_mclk",
+ "i2s_mixer",
+ "i2s_input_clk",
+ "i2s_aoclk_div_gate",
+ "i2s_aoclk_basic_div",
+ "i2s_aoclk_more_div",
+ "i2s_lrclk_div";
+ resets = <&reset RESET_AIU>;
+};
+
&aobus {
pinctrl_aobus: pinctrl@14 {
compatible = "amlogic,meson-gxbb-aobus-pinctrl";
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH RFC 6/6] arm64: dts: meson-gx: enable audin on odroidc2 platform
2025-02-10 15:01 [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB Valerio Setti
` (4 preceding siblings ...)
2025-02-10 15:01 ` [PATCH RFC 5/6] arm64: dts: meson-gx: add audin support Valerio Setti
@ 2025-02-10 15:01 ` Valerio Setti
2025-02-11 0:10 ` [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB Rob Herring (Arm)
6 siblings, 0 replies; 10+ messages in thread
From: Valerio Setti @ 2025-02-10 15:01 UTC (permalink / raw)
To: jbrunet, neil.armstrong, khilman, martin.blumenstingl,
linux-amlogic, linux-sound
Cc: linux-kernel, Valerio Setti
This commit adds the TODDR #0 module to the Odroid-C2 platform.
Note: this not enough to be able to start recording data. An addiditional
dai-link node should be added with a reference to the external codec
being used. For example here's what should be added when an NXP SGTL5000
is used:
dai-link-4 {
sound-dai = <&audin CPU_I2S_DECODER>;
dai-format = "i2s";
mclk-fs = <256>;
codec-0 {
sound-dai = <&sgtl5000>;
};
};
Signed-off-by: Valerio Setti <vsetti@baylibre.com>
---
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index 959bd8d77a82..a83373739019 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -10,6 +10,7 @@
#include "meson-gxbb.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/sound/meson-aiu.h>
+#include <dt-bindings/sound/meson-audin.h>
/ {
compatible = "hardkernel,odroid-c2", "amlogic,meson-gxbb";
@@ -210,6 +211,10 @@ codec-0 {
sound-dai = <&hdmi_tx>;
};
};
+
+ dai-link-3 {
+ sound-dai = <&audin CPU_AUDIN_TODDR_0>;
+ };
};
};
@@ -217,6 +222,10 @@ &aiu {
status = "okay";
};
+&audin {
+ status = "okay";
+};
+
&cec_AO {
status = "okay";
pinctrl-0 = <&ao_cec_pins>;
--
2.39.5
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB
2025-02-10 15:01 [PATCH RFC 0/6] Add support for AUDIN driver in Amlogic GXBB Valerio Setti
` (5 preceding siblings ...)
2025-02-10 15:01 ` [PATCH RFC 6/6] arm64: dts: meson-gx: enable audin on odroidc2 platform Valerio Setti
@ 2025-02-11 0:10 ` Rob Herring (Arm)
6 siblings, 0 replies; 10+ messages in thread
From: Rob Herring (Arm) @ 2025-02-11 0:10 UTC (permalink / raw)
To: Valerio Setti
Cc: martin.blumenstingl, neil.armstrong, jbrunet, linux-sound,
khilman, linux-kernel, linux-amlogic
On Mon, 10 Feb 2025 16:01:23 +0100, Valerio Setti wrote:
> This series adds support for audio input (AUDIN) on the Amlogic GXBB platform.
>
> It starts with a HACK commit which is necessary to make AIU (audio output)
> export its clocks. These clocks are necessary also for AUDIN, but they are
> already owned and controlled by AIU. Therefore in order to avoid direct
> dependencies between AIU and AUDIN drivers, we let AIU export its clocks.
> This export is done through the clk-regmap interface which is not public yet,
> so that's why the commit is marked as HACK.
>
> The series then continues with adding dt-bindings, driver and DTS support
> for AUDIN. As of now only I2S input is supported, because it's the only one
> I could physically test in my setup, but other input sources (ex: SPDIF) are
> also allowed according to the SOC's manual and can be added in the future.
>
> This series was tested on an OdroidC2 board (Amlogic S905 SOC) with an
> NXP SGTL5000 codec connected to its I2S input port. It was tested on tag
> v6.14-rc1.
>
> Valerio Setti (6):
> ASoC: meson: [HACK] let AIU export its clocks through clk-regmap
> ASoC: meson: audin: add audio input dt-bindings
> ASoC: meson: add AUDIN driver
> ASoC: meson: add support for AUDIN in gx-card
> arm64: dts: meson-gx: add audin support
> arm64: dts: meson-gx: enable audin on odroidc2 platform
>
> .../bindings/sound/amlogic,audin.yaml | 104 +++++
> arch/arm64/boot/dts/amlogic/meson-gx.dtsi | 8 +
> .../boot/dts/amlogic/meson-gxbb-odroidc2.dts | 9 +
> arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi | 39 +-
> include/dt-bindings/sound/meson-aiu.h | 5 +
> include/dt-bindings/sound/meson-audin.h | 10 +
> sound/soc/meson/Kconfig | 6 +
> sound/soc/meson/Makefile | 5 +
> sound/soc/meson/aiu-clocks.c | 123 ++++++
> sound/soc/meson/aiu-encoder-i2s.c | 119 +++---
> sound/soc/meson/aiu.c | 22 +
> sound/soc/meson/aiu.h | 10 +
> sound/soc/meson/audin-decoder-i2s.c | 247 +++++++++++
> sound/soc/meson/audin-toddr.c | 403 ++++++++++++++++++
> sound/soc/meson/audin.c | 321 ++++++++++++++
> sound/soc/meson/audin.h | 119 ++++++
> sound/soc/meson/gx-card.c | 12 +-
> 17 files changed, 1509 insertions(+), 53 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/sound/amlogic,audin.yaml
> create mode 100644 include/dt-bindings/sound/meson-audin.h
> create mode 100644 sound/soc/meson/aiu-clocks.c
> create mode 100644 sound/soc/meson/audin-decoder-i2s.c
> create mode 100644 sound/soc/meson/audin-toddr.c
> create mode 100644 sound/soc/meson/audin.c
> create mode 100644 sound/soc/meson/audin.h
>
> --
> 2.39.5
>
>
>
My bot found new DTB warnings on the .dts files added or changed in this
series.
Some warnings may be from an existing SoC .dtsi. Or perhaps the warnings
are fixed by another series. Ultimately, it is up to the platform
maintainer whether these warnings are acceptable or not. No need to reply
unless the platform maintainer has comments.
If you already ran DT checks and didn't see these error(s), then
make sure dt-schema is up to date:
pip3 install dtschema --upgrade
New warnings running 'make CHECK_DTBS=y for arch/arm64/boot/dts/amlogic/' for 20250210150129.40248-1-vsetti@baylibre.com:
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-sml5442tw.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-khadas-vim.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-s912-libretech-pc.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905w-p281.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-q200.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-hwacom-amazetv.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxlx-s905l-p271.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-phicomm-n1.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-mecool-kiii-pro.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-vega-s96.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-wetek-core2.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc-v2.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-q201.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p230.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-play2.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-mecool-kii-pro.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-gt1-ultimate.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-p231.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-libretech-pc.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-vero4k.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905w-tx3-mini.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxm-minix-neo-u9h.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s805x-libretech-ac.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905d-vero4k-plus.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-p201.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-p201.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-p201.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-p201.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-p201.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxm-rbox-pro.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-p200.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-p200.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-p200.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-p200.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-p200.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-libretech-cc.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905x-nexbox-a95x.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s905w-jethome-jethub-j80.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-hub.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-hub.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-hub.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-hub.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-wetek-hub.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-kii-pro.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-kii-pro.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-kii-pro.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-kii-pro.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-kii-pro.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxl-s805x-p241.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dtb: audio-controller@5400: clocks: [[3, 38], [3, 40], [3, 80], [3, 107], [3, 44], [3, 39], [3, 81], [3, 110], [3, 113], [17, 0], [17, 1], [17, 2], [17, 3]] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dtb: audio-controller@5400: clock-names: ['pclk', 'i2s_pclk', 'i2s_aoclk', 'i2s_mclk', 'i2s_mixer', 'spdif_pclk', 'spdif_aoclk', 'spdif_mclk', 'spdif_mclk_sel', 'i2s_aoclk_div_gate', 'i2s_aoclk_basic_div', 'i2s_aoclk_more_div', 'i2s_lrclk_div'] is too long
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dtb: audio-controller@5400: '#clock-cells' does not match any of the regexes: 'pinctrl-[0-9]+'
from schema $id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml#
arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
arch/arm64/boot/dts/amlogic/meson-gxbb-nanopi-k2.dtb: /soc/bus@c1100000/audio-input-controller@a000: failed to match any schema with compatible: ['amlogic,audin-gxbb', 'amlogic,audin']
^ permalink raw reply [flat|nested] 10+ messages in thread