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:
parent
f949ed458a
commit
a5a89037d0
@ -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");
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user