ASoC: wcd9304: Turn off mclk during clock transition

The clock is not usable when either the clock source changes between
external MCLK and internal RCO or the bandgap mode changes to fast mode.
In such cases, turn off master clock and turn on again later to avoid
codec from going into bad state

CRs-fixed: 442464
Change-Id: Id8d07bbbd4bf6c7be16016800d19ab332b960d1a
Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org>
This commit is contained in:
Joonwoo Park 2013-01-15 11:40:28 -08:00 committed by Iliyan Malchev
parent 08436ad97a
commit a76e7e31e0

View file

@ -2541,6 +2541,12 @@ static void sitar_codec_enable_bandgap(struct snd_soc_codec *codec,
(choice == SITAR_BANDGAP_AUDIO_MODE)) {
sitar_codec_enable_audio_mode_bandgap(codec);
} else if (choice == SITAR_BANDGAP_MBHC_MODE) {
/*
* bandgap mode becomes fast,
* mclk should be off or clk buff source shouldn't be VBG
* Let's turn off mclk always
*/
WARN_ON(snd_soc_read(codec, SITAR_A_CLK_BUFF_EN2) & (1 << 2));
snd_soc_update_bits(codec, SITAR_A_BIAS_CURR_CTL_2, 0x0C, 0x08);
snd_soc_update_bits(codec, SITAR_A_BIAS_CENTRAL_BG_CTL, 0x2,
0x2);
@ -2576,23 +2582,23 @@ static int sitar_codec_enable_config_mode(struct snd_soc_codec *codec,
{
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
pr_debug("%s: enable = %d\n", __func__, enable);
if (enable) {
snd_soc_update_bits(codec, SITAR_A_RC_OSC_FREQ, 0x10, 0);
/* bandgap mode to fast */
snd_soc_write(codec, SITAR_A_BIAS_OSC_BG_CTL, 0x17);
usleep_range(5, 5);
snd_soc_update_bits(codec, SITAR_A_RC_OSC_FREQ, 0x80,
0x80);
snd_soc_update_bits(codec, SITAR_A_RC_OSC_TEST, 0x80,
0x80);
snd_soc_update_bits(codec, SITAR_A_RC_OSC_FREQ, 0x80, 0x80);
snd_soc_update_bits(codec, SITAR_A_RC_OSC_TEST, 0x80, 0x80);
usleep_range(10, 10);
snd_soc_update_bits(codec, SITAR_A_RC_OSC_TEST, 0x80, 0);
usleep_range(20, 20);
usleep_range(10000, 10000);
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN1, 0x08, 0x08);
} else {
snd_soc_update_bits(codec, SITAR_A_BIAS_OSC_BG_CTL, 0x1,
0);
snd_soc_update_bits(codec, SITAR_A_BIAS_OSC_BG_CTL, 0x1, 0);
snd_soc_update_bits(codec, SITAR_A_RC_OSC_FREQ, 0x80, 0);
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN1, 0x08, 0x00);
/* clk source to ext clk and clk buff ref to VBG */
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN1, 0x0C, 0x04);
}
sitar->config_mode_active = enable ? true : false;
@ -2606,23 +2612,28 @@ static int sitar_codec_enable_clock_block(struct snd_soc_codec *codec,
pr_debug("%s\n", __func__);
/* transit to RCO requires mclk off */
WARN_ON(snd_soc_read(codec, SITAR_A_CLK_BUFF_EN2) & (1 << 2));
if (config_mode) {
/* enable RCO and switch to it */
sitar_codec_enable_config_mode(codec, 1);
snd_soc_write(codec, SITAR_A_CLK_BUFF_EN2, 0x00);
snd_soc_write(codec, SITAR_A_CLK_BUFF_EN2, 0x02);
snd_soc_write(codec, SITAR_A_CLK_BUFF_EN1, 0x0D);
usleep_range(1000, 1000);
} else
} else {
/* switch to MCLK */
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN1, 0x08, 0x00);
if (!config_mode && sitar->mbhc_polling_active) {
snd_soc_write(codec, SITAR_A_CLK_BUFF_EN2, 0x02);
sitar_codec_enable_config_mode(codec, 0);
if (sitar->mbhc_polling_active) {
snd_soc_write(codec, SITAR_A_CLK_BUFF_EN2, 0x02);
sitar_codec_enable_config_mode(codec, 0);
}
}
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN1, 0x05, 0x05);
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN1, 0x01, 0x01);
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN2, 0x02, 0x00);
/* on MCLK */
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN2, 0x04, 0x04);
usleep_range(50, 50);
sitar->clock_active = true;
@ -2633,9 +2644,10 @@ static void sitar_codec_disable_clock_block(struct snd_soc_codec *codec)
struct sitar_priv *sitar = snd_soc_codec_get_drvdata(codec);
pr_debug("%s\n", __func__);
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN2, 0x04, 0x00);
ndelay(160);
usleep_range(50, 50);
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN2, 0x02, 0x02);
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN1, 0x05, 0x00);
usleep_range(50, 50);
sitar->clock_active = false;
}
@ -2754,16 +2766,18 @@ int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm)
if (mclk_enable) {
sitar->mclk_enabled = true;
if (sitar->mbhc_polling_active && (sitar->mclk_enabled)) {
if (sitar->mbhc_polling_active) {
sitar_codec_pause_hs_polling(codec);
sitar_codec_disable_clock_block(codec);
sitar_codec_enable_bandgap(codec,
SITAR_BANDGAP_AUDIO_MODE);
SITAR_BANDGAP_AUDIO_MODE);
sitar_codec_enable_clock_block(codec, 0);
sitar_codec_calibrate_hs_polling(codec);
sitar_codec_start_hs_polling(codec);
} else {
sitar_codec_disable_clock_block(codec);
sitar_codec_enable_bandgap(codec,
SITAR_BANDGAP_AUDIO_MODE);
SITAR_BANDGAP_AUDIO_MODE);
sitar_codec_enable_clock_block(codec, 0);
}
} else {
@ -2777,21 +2791,20 @@ int sitar_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm)
sitar->mclk_enabled = false;
if (sitar->mbhc_polling_active) {
if (!sitar->mclk_enabled) {
sitar_codec_pause_hs_polling(codec);
sitar_codec_enable_bandgap(codec,
SITAR_BANDGAP_MBHC_MODE);
sitar_enable_rx_bias(codec, 1);
sitar_codec_enable_clock_block(codec, 1);
sitar_codec_calibrate_hs_polling(codec);
sitar_codec_start_hs_polling(codec);
}
sitar_codec_pause_hs_polling(codec);
sitar_codec_disable_clock_block(codec);
sitar_codec_enable_bandgap(codec,
SITAR_BANDGAP_MBHC_MODE);
sitar_enable_rx_bias(codec, 1);
sitar_codec_enable_clock_block(codec, 1);
sitar_codec_calibrate_hs_polling(codec);
sitar_codec_start_hs_polling(codec);
snd_soc_update_bits(codec, SITAR_A_CLK_BUFF_EN1,
0x05, 0x01);
} else {
sitar_codec_disable_clock_block(codec);
sitar_codec_enable_bandgap(codec,
SITAR_BANDGAP_OFF);
SITAR_BANDGAP_OFF);
}
}
if (dapm)
@ -3437,6 +3450,7 @@ static short sitar_codec_setup_hs_polling(struct snd_soc_codec *codec)
}
if (!sitar->mclk_enabled) {
sitar_codec_disable_clock_block(codec);
sitar_codec_enable_bandgap(codec, SITAR_BANDGAP_MBHC_MODE);
sitar_enable_rx_bias(codec, 1);
sitar_codec_enable_clock_block(codec, 1);