ASoC: tlv320aic31xx: Fix master mode clock I2S bus clocks

In the reset state of the codec we do not have complete playback or capture
routes.

The audio playback/capture will not work due to missing clock signals on
the I2S bus if PLL, MDAC/NDAC/DAC MADC/NADC/ADC is powered down.

To make sure that even if all output/input is disconnected the codec is
generating clocks, we need to have valid DAPM route in every case to power
up the must needed parts of the codec.

I have verified that switching DAC (during playback) or ADC (during
capture) will stop the I2S clocks, so the only solution is to connect the
'Off' routes as well to output/input.

The routes will be only added if the codec is clock master. In case the
role changes runtime, the applied routes are removed.

Tested on am43x-epos-evm with aic3111 codec in master mode.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Reviewed-by: Jyri Sarha <jsarha@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Peter Ujfalusi 2018-02-14 14:20:56 +02:00 коммит произвёл Mark Brown
Родитель f2e6f95b4b
Коммит d460b3f861
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 24D68B725D5487D0
1 изменённых файлов: 72 добавлений и 1 удалений

Просмотреть файл

@ -166,6 +166,7 @@ struct aic31xx_priv {
unsigned int sysclk; unsigned int sysclk;
u8 p_div; u8 p_div;
int rate_div_line; int rate_div_line;
bool master_dapm_route_applied;
}; };
struct aic31xx_rate_divs { struct aic31xx_rate_divs {
@ -670,6 +671,29 @@ aic310x_audio_map[] = {
{"SPK", NULL, "SPK ClassD"}, {"SPK", NULL, "SPK ClassD"},
}; };
/*
* Always connected DAPM routes for codec clock master modes.
* If the codec is the master on the I2S bus, we need to power on components
* to have valid DAC_CLK and also the DACs and ADC for playback/capture.
* Otherwise the codec will not generate clocks on the bus.
*/
static const struct snd_soc_dapm_route
common31xx_cm_audio_map[] = {
{"DAC Left Input", "Off", "DAC IN"},
{"DAC Right Input", "Off", "DAC IN"},
{"HPL", NULL, "DAC Left"},
{"HPR", NULL, "DAC Right"},
};
static const struct snd_soc_dapm_route
aic31xx_cm_audio_map[] = {
{"MIC1LP P-Terminal", "Off", "MIC1LP"},
{"MIC1RP P-Terminal", "Off", "MIC1RP"},
{"MIC1LM P-Terminal", "Off", "MIC1LM"},
{"MIC1LM M-Terminal", "Off", "MIC1LM"},
};
static int aic31xx_add_controls(struct snd_soc_component *component) static int aic31xx_add_controls(struct snd_soc_component *component)
{ {
int ret = 0; int ret = 0;
@ -912,6 +936,53 @@ static int aic31xx_dac_mute(struct snd_soc_dai *codec_dai, int mute)
return 0; return 0;
} }
static int aic31xx_clock_master_routes(struct snd_soc_component *component,
unsigned int fmt)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
int ret;
fmt &= SND_SOC_DAIFMT_MASTER_MASK;
if (fmt == SND_SOC_DAIFMT_CBS_CFS &&
aic31xx->master_dapm_route_applied) {
/*
* Remove the DAPM route(s) for codec clock master modes,
* if applied
*/
ret = snd_soc_dapm_del_routes(dapm, common31xx_cm_audio_map,
ARRAY_SIZE(common31xx_cm_audio_map));
if (!ret && !(aic31xx->codec_type & DAC31XX_BIT))
ret = snd_soc_dapm_del_routes(dapm,
aic31xx_cm_audio_map,
ARRAY_SIZE(aic31xx_cm_audio_map));
if (ret)
return ret;
aic31xx->master_dapm_route_applied = false;
} else if (fmt != SND_SOC_DAIFMT_CBS_CFS &&
!aic31xx->master_dapm_route_applied) {
/*
* Add the needed DAPM route(s) for codec clock master modes,
* if it is not done already
*/
ret = snd_soc_dapm_add_routes(dapm, common31xx_cm_audio_map,
ARRAY_SIZE(common31xx_cm_audio_map));
if (!ret && !(aic31xx->codec_type & DAC31XX_BIT))
ret = snd_soc_dapm_add_routes(dapm,
aic31xx_cm_audio_map,
ARRAY_SIZE(aic31xx_cm_audio_map));
if (ret)
return ret;
aic31xx->master_dapm_route_applied = true;
}
return 0;
}
static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai, static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt) unsigned int fmt)
{ {
@ -992,7 +1063,7 @@ static int aic31xx_set_dai_fmt(struct snd_soc_dai *codec_dai,
AIC31XX_BCLKINV_MASK, AIC31XX_BCLKINV_MASK,
iface_reg2); iface_reg2);
return 0; return aic31xx_clock_master_routes(component, fmt);
} }
static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,