aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorGravatar Jerome Brunet <jbrunet@baylibre.com> 2024-04-26 17:29:41 +0200
committerGravatar Mark Brown <broonie@kernel.org> 2024-04-29 23:48:50 +0900
commita5a89037d080e0870d7517c61f8b2123d58ab33b (patch)
tree9cdcc233aa9a99f30f1de159aa2456416540c8b6 /sound
parentASoC: meson: axg-tdm-interface: manage formatters in trigger (diff)
downloadlinux-a5a89037d080e0870d7517c61f8b2123d58ab33b.tar.gz
linux-a5a89037d080e0870d7517c61f8b2123d58ab33b.tar.bz2
linux-a5a89037d080e0870d7517c61f8b2123d58ab33b.zip
ASoC: meson: axg-tdm: add continuous clock support
Some devices may need the clocks running, even while paused. Add support for this use case. Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> Link: https://lore.kernel.org/r/20240426152946.3078805-5-jbrunet@baylibre.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/meson/axg-tdm-formatter.c40
-rw-r--r--sound/soc/meson/axg-tdm-interface.c16
-rw-r--r--sound/soc/meson/axg-tdm.h5
3 files changed, 60 insertions, 1 deletions
diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c
index 63333a2b0a9c..a6579efd3775 100644
--- a/sound/soc/meson/axg-tdm-formatter.c
+++ b/sound/soc/meson/axg-tdm-formatter.c
@@ -392,6 +392,46 @@ void axg_tdm_stream_free(struct axg_tdm_stream *ts)
}
EXPORT_SYMBOL_GPL(axg_tdm_stream_free);
+int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts,
+ unsigned int fmt)
+{
+ int ret = 0;
+
+ if (fmt & SND_SOC_DAIFMT_CONT) {
+ /* Clock are already enabled - skipping */
+ if (ts->clk_enabled)
+ return 0;
+
+ ret = clk_prepare_enable(ts->iface->mclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(ts->iface->sclk);
+ if (ret)
+ goto err_sclk;
+
+ ret = clk_prepare_enable(ts->iface->lrclk);
+ if (ret)
+ goto err_lrclk;
+
+ ts->clk_enabled = true;
+ return 0;
+ }
+
+ /* Clocks are already disabled - skipping */
+ if (!ts->clk_enabled)
+ return 0;
+
+ clk_disable_unprepare(ts->iface->lrclk);
+err_lrclk:
+ clk_disable_unprepare(ts->iface->sclk);
+err_sclk:
+ clk_disable_unprepare(ts->iface->mclk);
+ ts->clk_enabled = false;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(axg_tdm_stream_set_cont_clocks);
+
MODULE_DESCRIPTION("Amlogic AXG TDM formatter driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c
index 8bf3735dedaa..62057c71f742 100644
--- a/sound/soc/meson/axg-tdm-interface.c
+++ b/sound/soc/meson/axg-tdm-interface.c
@@ -309,6 +309,7 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
int ret;
switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -346,7 +347,19 @@ static int axg_tdm_iface_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- return 0;
+ ret = axg_tdm_stream_set_cont_clocks(ts, iface->fmt);
+ if (ret)
+ dev_err(dai->dev, "failed to apply continuous clock setting\n");
+
+ return ret;
+}
+
+static int axg_tdm_iface_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct axg_tdm_stream *ts = snd_soc_dai_get_dma_data(dai, substream);
+
+ return axg_tdm_stream_set_cont_clocks(ts, 0);
}
static int axg_tdm_iface_trigger(struct snd_pcm_substream *substream,
@@ -417,6 +430,7 @@ static const struct snd_soc_dai_ops axg_tdm_iface_ops = {
.set_fmt = axg_tdm_iface_set_fmt,
.startup = axg_tdm_iface_startup,
.hw_params = axg_tdm_iface_hw_params,
+ .hw_free = axg_tdm_iface_hw_free,
.trigger = axg_tdm_iface_trigger,
};
diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h
index 42f7470b9a7f..daaca10fec9e 100644
--- a/sound/soc/meson/axg-tdm.h
+++ b/sound/soc/meson/axg-tdm.h
@@ -58,12 +58,17 @@ struct axg_tdm_stream {
unsigned int physical_width;
u32 *mask;
bool ready;
+
+ /* For continuous clock tracking */
+ bool clk_enabled;
};
struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface);
void axg_tdm_stream_free(struct axg_tdm_stream *ts);
int axg_tdm_stream_start(struct axg_tdm_stream *ts);
void axg_tdm_stream_stop(struct axg_tdm_stream *ts);
+int axg_tdm_stream_set_cont_clocks(struct axg_tdm_stream *ts,
+ unsigned int fmt);
static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts)
{