From: Luca Ceresoli luca.ceresoli@bootlin.com
The Rockchip RK3308 8-channel I2S adapter has a mainline driver that can work fine with an audio-graph-card or simple-audio-card. Those card drivers call the .set_sysclk op once, and this is usually enough for applications using an external codec.
But in reality the I2S adapter has two clock inputs (TX and RX), and the preferred way to use the RK3308 internal codec is enabling both clocks, potentially with different rates. The existing simple-card code does not implement this possibility.
To allow setting both clocks, add a new minimal driver that builds on top of audio-graph-card and changes the dai_link->init callback with a modified version of asoc_simple_init_dai(). This ultimately calls the set_sysclk() callback as many times as the number of clocks defined in device tree.
With this implementation, the same rate is set to all the sysclks. Setting different rates can be added later.
Signed-off-by: Luca Ceresoli luca.ceresoli@bootlin.com --- MAINTAINERS | 1 + sound/soc/rockchip/Kconfig | 14 ++++ sound/soc/rockchip/Makefile | 1 + sound/soc/rockchip/rockchip_rk3308_card.c | 97 +++++++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 sound/soc/rockchip/rockchip_rk3308_card.c
diff --git a/MAINTAINERS b/MAINTAINERS index fb2a0a6e3c1f..96ccda9625f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17600,6 +17600,7 @@ ROCKCHIP RK3308 SOUND CARD DRIVER M: Luca Ceresoli luca.ceresoli@bootlin.com S: Maintained F: Documentation/devicetree/bindings/sound/rockchip,rk3308-audio-graph-card.yaml +F: sound/soc/rockchip/rockchip_rk3308_card.c
ROCKCHIP VIDEO DECODER DRIVER M: Ezequiel Garcia ezequiel@vanguardiasur.com.ar diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 42f76bc0fb02..b00dc04f8fd0 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -45,6 +45,20 @@ config SND_SOC_ROCKCHIP_SPDIF Say Y or M if you want to add support for SPDIF driver for Rockchip SPDIF transceiver device.
+config SND_SOC_ROCKCHIP_RK3308_INTERNAL_CODEC + tristate "ASoC sound card based on the internal RK3308 codec" + depends on HAVE_CLK && SND_SOC_ROCKCHIP + depends on SND_AUDIO_GRAPH_CARD + select SND_SOC_ROCKCHIP_I2S_TDM + select SND_SOC_GENERIC_DMAENGINE_PCM + help + ASoC sound card driver for the RK3308 internal audio codec. + + The Rockchip RK3308 SoC has a built-in audio codec that is + connected internally to one out of a selection of the internal + I2S controllers. This driver implements an audio card using these + components. + config SND_SOC_ROCKCHIP_MAX98090 tristate "ASoC support for Rockchip boards using a MAX98090 codec" depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 30c57c0d7660..680decae0c02 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -15,6 +15,7 @@ snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o snd-soc-rk3288-hdmi-analog-objs := rk3288_hdmi_analog.o snd-soc-rk3399-gru-sound-objs := rk3399_gru_sound.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_RK3308_INTERNAL_CODEC) += rockchip_rk3308_card.o obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o obj-$(CONFIG_SND_SOC_RK3288_HDMI_ANALOG) += snd-soc-rk3288-hdmi-analog.o diff --git a/sound/soc/rockchip/rockchip_rk3308_card.c b/sound/soc/rockchip/rockchip_rk3308_card.c new file mode 100644 index 000000000000..3cfc751993fe --- /dev/null +++ b/sound/soc/rockchip/rockchip_rk3308_card.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Audio card using the RK3308 internal I2S +// +// Allows driving the I2S peripheral with both the RX and TX clocks. This +// is useful to use the RK3308 internal audio codec. +// +// Copyright (c) 2022, Vivax-Metrotech Ltd +// +// Based on sound/soc/generic/audio-graph-card.c + +#include <linux/module.h> +#include <linux/of_clk.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <sound/graph_card.h> + +static int rk3308_audio_asoc_simple_init_dai(struct snd_soc_dai *dai, + struct asoc_simple_dai *simple_dai) +{ + const int nclks = 2; /* The sysclks are clk_id 0 and 1 for the RK3308 driver */ + int err; + int i; + + if (!simple_dai || !simple_dai->sysclk) + return 0; + + /* Can be extended to get two different sysclk values via device tree */ + for (i = 0; i < nclks; i++) { + err = snd_soc_dai_set_sysclk(dai, i, simple_dai->sysclk, + simple_dai->clk_direction); + if (err && err != -ENOTSUPP) + return dev_err_probe(dai->dev, err, "simple-card: set_sysclk error\n"); + } + + return 0; +} + +static int rk3308_audio_asoc_simple_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *props = simple_priv_to_props(priv, rtd->num); + struct asoc_simple_dai *dai; + int i, ret; + + for_each_prop_dai_codec(props, i, dai) { + ret = rk3308_audio_asoc_simple_init_dai(asoc_rtd_to_codec(rtd, i), dai); + if (ret < 0) + return ret; + } + for_each_prop_dai_cpu(props, i, dai) { + ret = rk3308_audio_asoc_simple_init_dai(asoc_rtd_to_cpu(rtd, i), dai); + if (ret < 0) + return ret; + } + + return 0; +} + +static int rk3308_audio_graph_probe(struct platform_device *pdev) +{ + struct asoc_simple_priv *priv; + struct device *dev = &pdev->dev; + struct snd_soc_card *card; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + card = simple_priv_to_card(priv); + card->driver_name = "rk3308-audio-graph-card"; + card->probe = asoc_graph_card_probe; + priv->init = rk3308_audio_asoc_simple_dai_init; + + return audio_graph_parse_of(priv, dev); +} + +static const struct of_device_id graph_of_rk3308_card_match[] = { + { .compatible = "rockchip,rk3308-audio-graph-card" }, + {}, +}; +MODULE_DEVICE_TABLE(of, graph_of_rk3308_card_match); + +static struct platform_driver rk3308_audio_graph_card = { + .driver = { + .name = "rk3308-audio-graph-card", + .pm = &snd_soc_pm_ops, + .of_match_table = graph_of_rk3308_card_match, + }, + .probe = rk3308_audio_graph_probe, + .remove = asoc_simple_remove, +}; +module_platform_driver(rk3308_audio_graph_card); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ASoC Audio Graph Card for Rockchip RK3308"); +MODULE_AUTHOR("Luca Ceresoli luca.ceresoli@bootlin.com");