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>
This commit is contained in:
Jerome Brunet 2024-04-26 17:29:41 +02:00 committed by Mark Brown
parent f949ed458a
commit a5a89037d0
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 60 additions and 1 deletions

View File

@ -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");

View File

@ -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,
};

View File

@ -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)
{