[PATCH v7 7/7] ASoC: meson: g12a-tohdmitx: add S4 support
From: christian . koever-draxl
Date: Sun Oct 25 2026 - 15:30:31 EST
From: Jiebing Chen <jiebing.chen@xxxxxxxxxxx>
S4 replaces the single ENABLE bit of the G12A HDMI TX glue with
separate data and clock enables for each of the I2S and SPDIF paths.
Model the clock enables as supply widgets and give S4 its own component
driver, selected through match data.
Signed-off-by: Jiebing Chen <jiebing.chen@xxxxxxxxxxx>
[ckoever@xxxxxxxxxx: rebased, use the SPDIF data bit for the SPDIF
output enable, style fixes]
Signed-off-by: Christian Stefan Kövér-Draxl <ckoever@xxxxxxxxxx>
---
sound/soc/meson/g12a-tohdmitx.c | 80 ++++++++++++++++++++++++++++++++-
1 file changed, 78 insertions(+), 2 deletions(-)
diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c
index 967109ca2b57..eafade3d7474 100644
--- a/sound/soc/meson/g12a-tohdmitx.c
+++ b/sound/soc/meson/g12a-tohdmitx.c
@@ -31,6 +31,15 @@
#define CTRL0_SPDIF_SEL (0x1 << CTRL0_SPDIF_SEL_SHIFT)
#define CTRL0_SPDIF_CLK_SEL BIT(0)
+#define CTRL0_I2S_DATA_SHIFT 29
+#define CTRL0_I2S_CLK_SHIFT 28
+#define CTRL0_SPDIF_DATA_SHIFT 31
+#define CTRL0_SPDIF_CLK_SHIFT 30
+
+struct g12a_tohdmitx_match_data {
+ const struct snd_soc_component_driver *component_drv;
+};
+
static const char * const g12a_tohdmitx_i2s_mux_texts[] = {
"I2S A", "I2S B", "I2S C",
};
@@ -130,6 +139,13 @@ static const struct snd_kcontrol_new g12a_tohdmitx_out_enable =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOHDMITX_CTRL0,
CTRL0_ENABLE_SHIFT, 1, 0);
+static const struct snd_kcontrol_new s4_i2s_out_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOHDMITX_CTRL0,
+ CTRL0_I2S_DATA_SHIFT, 1, 0);
+static const struct snd_kcontrol_new s4_spdif_out_enable =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", TOHDMITX_CTRL0,
+ CTRL0_SPDIF_DATA_SHIFT, 1, 0);
+
static const struct snd_soc_dapm_widget g12a_tohdmitx_widgets[] = {
SND_SOC_DAPM_MUX("I2S SRC", SND_SOC_NOPM, 0, 0,
&g12a_tohdmitx_i2s_mux),
@@ -141,6 +157,21 @@ static const struct snd_soc_dapm_widget g12a_tohdmitx_widgets[] = {
&g12a_tohdmitx_out_enable),
};
+static const struct snd_soc_dapm_widget s4_tohdmitx_widgets[] = {
+ SND_SOC_DAPM_MUX("I2S SRC", SND_SOC_NOPM, 0, 0,
+ &g12a_tohdmitx_i2s_mux),
+ SND_SOC_DAPM_SWITCH("I2S OUT EN", SND_SOC_NOPM, 0, 0,
+ &s4_i2s_out_enable),
+ SND_SOC_DAPM_MUX("SPDIF SRC", SND_SOC_NOPM, 0, 0,
+ &g12a_tohdmitx_spdif_mux),
+ SND_SOC_DAPM_SWITCH("SPDIF OUT EN", SND_SOC_NOPM, 0, 0,
+ &s4_spdif_out_enable),
+ SND_SOC_DAPM_SUPPLY("I2S CLK Gate", TOHDMITX_CTRL0,
+ CTRL0_I2S_CLK_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SPDIF CLK Gate", TOHDMITX_CTRL0,
+ CTRL0_SPDIF_CLK_SHIFT, 0, NULL, 0),
+};
+
static const struct snd_soc_dai_ops g12a_tohdmitx_input_ops = {
.probe = meson_codec_glue_input_dai_probe,
.remove = meson_codec_glue_input_dai_remove,
@@ -221,6 +252,20 @@ static const struct snd_soc_dapm_route g12a_tohdmitx_routes[] = {
{ "SPDIF OUT Capture", NULL, "SPDIF OUT EN" },
};
+static const struct snd_soc_dapm_route s4_tohdmitx_routes[] = {
+ { "I2S SRC", "I2S A", "I2S IN A Playback" },
+ { "I2S SRC", "I2S B", "I2S IN B Playback" },
+ { "I2S SRC", "I2S C", "I2S IN C Playback" },
+ { "I2S OUT EN", "Switch", "I2S SRC" },
+ { "I2S OUT EN", NULL, "I2S CLK Gate" },
+ { "I2S OUT Capture", NULL, "I2S OUT EN" },
+ { "SPDIF SRC", "SPDIF A", "SPDIF IN A Playback" },
+ { "SPDIF SRC", "SPDIF B", "SPDIF IN B Playback" },
+ { "SPDIF OUT EN", "Switch", "SPDIF SRC" },
+ { "SPDIF OUT EN", NULL, "SPDIF CLK Gate" },
+ { "SPDIF OUT Capture", NULL, "SPDIF OUT EN" },
+};
+
static const struct snd_soc_component_driver g12a_tohdmitx_component_drv = {
.probe = g12a_tohdmi_component_probe,
.dapm_widgets = g12a_tohdmitx_widgets,
@@ -230,25 +275,56 @@ static const struct snd_soc_component_driver g12a_tohdmitx_component_drv = {
.endianness = 1,
};
+static const struct snd_soc_component_driver s4_tohdmitx_component_drv = {
+ .probe = g12a_tohdmi_component_probe,
+ .dapm_widgets = s4_tohdmitx_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(s4_tohdmitx_widgets),
+ .dapm_routes = s4_tohdmitx_routes,
+ .num_dapm_routes = ARRAY_SIZE(s4_tohdmitx_routes),
+ .endianness = 1,
+};
+
static const struct regmap_config g12a_tohdmitx_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
+static const struct g12a_tohdmitx_match_data g12a_hdmitx_match_data = {
+ .component_drv = &g12a_tohdmitx_component_drv,
+};
+
+static const struct g12a_tohdmitx_match_data s4_hdmitx_match_data = {
+ .component_drv = &s4_tohdmitx_component_drv,
+};
+
static const struct of_device_id g12a_tohdmitx_of_match[] = {
- { .compatible = "amlogic,g12a-tohdmitx", },
+ {
+ .compatible = "amlogic,g12a-tohdmitx",
+ .data = &g12a_hdmitx_match_data,
+ },
+ {
+ .compatible = "amlogic,s4-tohdmitx",
+ .data = &s4_hdmitx_match_data,
+ },
{}
};
MODULE_DEVICE_TABLE(of, g12a_tohdmitx_of_match);
static int g12a_tohdmitx_probe(struct platform_device *pdev)
{
+ const struct g12a_tohdmitx_match_data *data;
struct device *dev = &pdev->dev;
void __iomem *regs;
struct regmap *map;
int ret;
+ data = device_get_match_data(dev);
+ if (!data) {
+ dev_err(dev, "failed to match device\n");
+ return -ENODEV;
+ }
+
ret = device_reset(dev);
if (ret)
return dev_err_probe(dev, ret, "failed to reset device\n");
@@ -265,7 +341,7 @@ static int g12a_tohdmitx_probe(struct platform_device *pdev)
}
return devm_snd_soc_register_component(dev,
- &g12a_tohdmitx_component_drv, g12a_tohdmitx_dai_drv,
+ data->component_drv, g12a_tohdmitx_dai_drv,
ARRAY_SIZE(g12a_tohdmitx_dai_drv));
}
--
2.56.0