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);
|
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_DESCRIPTION("Amlogic AXG TDM formatter driver");
|
||||||
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
|
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
|
||||||
MODULE_LICENSE("GPL v2");
|
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 snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(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;
|
int ret;
|
||||||
|
|
||||||
switch (iface->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
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 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,
|
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,
|
.set_fmt = axg_tdm_iface_set_fmt,
|
||||||
.startup = axg_tdm_iface_startup,
|
.startup = axg_tdm_iface_startup,
|
||||||
.hw_params = axg_tdm_iface_hw_params,
|
.hw_params = axg_tdm_iface_hw_params,
|
||||||
|
.hw_free = axg_tdm_iface_hw_free,
|
||||||
.trigger = axg_tdm_iface_trigger,
|
.trigger = axg_tdm_iface_trigger,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -58,12 +58,17 @@ struct axg_tdm_stream {
|
||||||
unsigned int physical_width;
|
unsigned int physical_width;
|
||||||
u32 *mask;
|
u32 *mask;
|
||||||
bool ready;
|
bool ready;
|
||||||
|
|
||||||
|
/* For continuous clock tracking */
|
||||||
|
bool clk_enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface);
|
struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface);
|
||||||
void axg_tdm_stream_free(struct axg_tdm_stream *ts);
|
void axg_tdm_stream_free(struct axg_tdm_stream *ts);
|
||||||
int axg_tdm_stream_start(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);
|
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)
|
static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue