MLK-11429-20: ASoC: cs4xx8: fix the setting of Functional mode
authorShengjiu Wang <b02247@freescale.com>
Wed, 2 Jul 2014 09:24:45 +0000 (17:24 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:48:12 +0000 (14:48 -0500)
cherry-pick below patch from imx_3.14.y
ENGR00320849-1 ASoC: cs4xx8: fix the setting of Functional mode

Failed case:
arecord -Dhw:0,1 -f S16_LE -r 96000 -c 2 -traw | aplay -Dhw:0,0 -f
S16_LE -r 96000 -c 2 -traw.
There is no sound when use this case.The reason is that the setting
of Functional mode is not correct.

Signed-off-by: Shengjiu Wang <b02247@freescale.com>
(cherry picked from commit be1ef3bb8da5c263345ebdad4a7c218a095d0f48)
(cherry picked from commit c2dfac6c09bc1fcb044022bcdaea450707feda4c)

sound/soc/codecs/cs42xx8.c

index b4d8737..790efca 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Cirrus Logic CS42448/CS42888 Audio CODEC Digital Audio Interface (DAI) driver
  *
- * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
  *
  * Author: Nicolin Chen <Guangyu.Chen@freescale.com>
  *
@@ -45,6 +45,7 @@ struct cs42xx8_priv {
        bool slave_mode;
        unsigned long sysclk;
        u32 tx_channels;
+       int rate[2];
 };
 
 /* -127.5dB to 0dB with step of 0.5dB */
@@ -174,21 +175,18 @@ static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
 };
 
 struct cs42xx8_ratios {
-       unsigned int ratio;
-       unsigned char speed;
-       unsigned char mclk;
+       unsigned int mfreq;
+       unsigned int min_mclk;
+       unsigned int max_mclk;
+       unsigned int ratio[3];
 };
 
 static const struct cs42xx8_ratios cs42xx8_ratios[] = {
-       { 64, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_256(4) },
-       { 96, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_384(4) },
-       { 128, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_512(4) },
-       { 192, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_768(4) },
-       { 256, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_256(1) },
-       { 384, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_384(1) },
-       { 512, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_512(1) },
-       { 768, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_768(1) },
-       { 1024, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_1024(1) }
+       { 0, 1029000, 12800000, {256, 128, 64} },
+       { 2, 1536000, 19200000, {384, 192, 96} },
+       { 4, 2048000, 25600000, {512, 256, 128} },
+       { 6, 3072000, 38400000, {768, 384, 192} },
+       { 8, 4096000, 51200000, {1024, 512, 256} },
 };
 
 static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai,
@@ -255,15 +253,70 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_codec *codec = dai->codec;
        struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
        bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-       u32 ratio = cs42xx8->sysclk / params_rate(params);
+       u32 rate = params_rate(params);
+       u32 ratio_tx, ratio_rx;
+       u32 rate_tx, rate_rx;
+       u32 fm_tx, fm_rx;
        u32 i, fm, val, mask;
 
        if (tx)
                cs42xx8->tx_channels = params_channels(params);
 
-       for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
-               if (cs42xx8_ratios[i].ratio == ratio)
-                       break;
+       rate_tx = tx ? rate : cs42xx8->rate[0];
+       rate_rx = tx ? cs42xx8->rate[1] : rate;
+
+       ratio_tx = rate_tx > 0 ? cs42xx8->sysclk / rate_tx : 0;
+       ratio_rx = rate_rx > 0 ? cs42xx8->sysclk / rate_rx : 0;
+
+       if (cs42xx8->slave_mode) {
+               fm_rx = CS42XX8_FM_AUTO;
+               fm_tx = CS42XX8_FM_AUTO;
+       } else {
+               if (rate_tx >= 0 && rate_tx < 50000)
+                       fm_tx = CS42XX8_FM_SINGLE;
+               else if (rate_tx > 50000 && rate_tx < 100000)
+                       fm_tx = CS42XX8_FM_DOUBLE;
+               else if (rate_tx > 100000 && rate_tx < 200000)
+                       fm_tx = CS42XX8_FM_QUAD;
+               else {
+                       dev_err(codec->dev, "unsupported sample rate or rate combine\n");
+                       return -EINVAL;
+               }
+
+               if (rate_rx >= 0 && rate_rx < 50000)
+                       fm_rx = CS42XX8_FM_SINGLE;
+               else if (rate_rx > 50000 && rate_rx < 100000)
+                       fm_rx = CS42XX8_FM_DOUBLE;
+               else if (rate_rx > 100000 && rate_rx < 200000)
+                       fm_rx = CS42XX8_FM_QUAD;
+               else {
+                       dev_err(codec->dev, "unsupported sample rate or rate combine\n");
+                       return -EINVAL;
+               }
+       }
+
+       fm = tx ? fm_tx : fm_rx;
+
+       if (fm == CS42XX8_FM_AUTO) {
+               for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
+                       if ((ratio_tx > 0 ? (cs42xx8_ratios[i].ratio[0] == ratio_tx ||
+                               cs42xx8_ratios[i].ratio[1] == ratio_tx ||
+                               cs42xx8_ratios[i].ratio[2] == ratio_tx) : true) &&
+                           (ratio_rx > 0 ? (cs42xx8_ratios[i].ratio[0] == ratio_rx ||
+                               cs42xx8_ratios[i].ratio[1] == ratio_rx ||
+                               cs42xx8_ratios[i].ratio[2] == ratio_rx) : true) &&
+                           cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk &&
+                           cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk)
+                               break;
+               }
+       } else {
+               for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
+                       if ((ratio_tx > 0 ? (cs42xx8_ratios[i].ratio[fm_tx] == ratio_tx) : true) &&
+                               (ratio_rx > 0 ? (cs42xx8_ratios[i].ratio[fm_rx] == ratio_rx) : true) &&
+                               cs42xx8->sysclk >= cs42xx8_ratios[i].min_mclk &&
+                               cs42xx8->sysclk <= cs42xx8_ratios[i].max_mclk)
+                               break;
+               }
        }
 
        if (i == ARRAY_SIZE(cs42xx8_ratios)) {
@@ -271,10 +324,10 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       mask = CS42XX8_FUNCMOD_MFREQ_MASK;
-       val = cs42xx8_ratios[i].mclk;
+       cs42xx8->rate[substream->stream] = rate;
 
-       fm = cs42xx8->slave_mode ? CS42XX8_FM_AUTO : cs42xx8_ratios[i].speed;
+       mask = CS42XX8_FUNCMOD_MFREQ_MASK;
+       val = cs42xx8_ratios[i].mfreq;
 
        regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
                           CS42XX8_FUNCMOD_xC_FM_MASK(tx) | mask,
@@ -283,6 +336,22 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
+static int cs42xx8_hw_free(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+       bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+       cs42xx8->rate[substream->stream] = 0;
+
+       regmap_update_bits(cs42xx8->regmap, CS42XX8_FUNCMOD,
+                          CS42XX8_FUNCMOD_xC_FM_MASK(tx),
+                          CS42XX8_FUNCMOD_xC_FM(tx, CS42XX8_FM_AUTO));
+       return 0;
+}
+
 static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
 {
        struct snd_soc_codec *codec = dai->codec;
@@ -300,6 +369,7 @@ static const struct snd_soc_dai_ops cs42xx8_dai_ops = {
        .set_fmt        = cs42xx8_set_dai_fmt,
        .set_sysclk     = cs42xx8_set_dai_sysclk,
        .hw_params      = cs42xx8_hw_params,
+       .hw_free        = cs42xx8_hw_free,
        .digital_mute   = cs42xx8_digital_mute,
 };