ASoC: tlv320aic3x: fix PLL D configuration
authorDmitry Lavnikevich <d.lavnikevich@sam-solutions.com>
Fri, 3 Oct 2014 13:18:56 +0000 (16:18 +0300)
committerMark Brown <broonie@kernel.org>
Fri, 3 Oct 2014 15:06:11 +0000 (16:06 +0100)
Current caching implementation during regcache_sync() call bypasses
all register writes of values that are already known as default
(regmap reg_defaults). Same time in TLV320AIC3x codecs register 5
(AIC3X_PLL_PROGC_REG) write should be immediately followed by register
6 write (AIC3X_PLL_PROGD_REG) even if it was not changed. Otherwise
both registers will not be written.

This brings to issue that appears particulary in case of 44.1kHz
playback with 19.2MHz master clock. In this case AIC3X_PLL_PROGC_REG
is 0x6e while AIC3X_PLL_PROGD_REG is 0x0 (same as register
default). Thus AIC3X_PLL_PROGC_REG also remains not written and we get
wrong playback speed.

In this patch snd_soc_read() is used to get cached pll values and
snd_soc_write() (unlike regcache_sync() this function doesn't bypasses
hardware default values) to write them to registers.

Signed-off-by: Dmitry Lavnikevich <d.lavnikevich@sam-solutions.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Cc: stable@vger.kernel.org
sound/soc/codecs/tlv320aic3x.c

index 64f179ee98345f841319599c255188e14fc37767..5e8626ae612bd8fcf1d0055df7a08bb21d4ae1ee 100644 (file)
@@ -1121,6 +1121,7 @@ static int aic3x_regulator_event(struct notifier_block *nb,
 static int aic3x_set_power(struct snd_soc_codec *codec, int power)
 {
        struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+       unsigned int pll_c, pll_d;
        int ret;
 
        if (power) {
@@ -1138,6 +1139,18 @@ static int aic3x_set_power(struct snd_soc_codec *codec, int power)
                /* Sync reg_cache with the hardware */
                regcache_cache_only(aic3x->regmap, false);
                regcache_sync(aic3x->regmap);
+
+               /* Rewrite paired PLL D registers in case cached sync skipped
+                * writing one of them and thus caused other one also not
+                * being written
+                */
+               pll_c = snd_soc_read(codec, AIC3X_PLL_PROGC_REG);
+               pll_d = snd_soc_read(codec, AIC3X_PLL_PROGD_REG);
+               if (pll_c == aic3x_reg[AIC3X_PLL_PROGC_REG].def ||
+                       pll_d == aic3x_reg[AIC3X_PLL_PROGD_REG].def) {
+                       snd_soc_write(codec, AIC3X_PLL_PROGC_REG, pll_c);
+                       snd_soc_write(codec, AIC3X_PLL_PROGD_REG, pll_d);
+               }
        } else {
                /*
                 * Do soft reset to this codec instance in order to clear