asoc: tegra-alt: Fix offload and capture usecase using SFC
Deepa Madiregama [Mon, 13 Apr 2015 05:54:01 +0000 (10:54 +0530)]
Add soft reset support for changing the SFC params
dynamically after every stream.

Make changes in machine driver to pick the clk as
per the codec-x-rate set. Configure I2S as per the
codec-x-rate set.

SFC input rate was taken from hw_params which results
in input rate same as stream sample rate. Also the
SFC output rate is set to stream sample rate via mixer
control. This results in same input and output rates
and SFC is put into bypass mode when used in capture
stream path and no rate conversion happens. Fix this
by adding "SFCx input rate" control to allow setting
input rate different than stream rate.

Bug 200097141
Bug 200056741

Change-Id: I294c484050cfa636c8173c5837feb4461ada2ddb
Signed-off-by: Deepa Madiregama <dmadiregama@nvidia.com>
Signed-off-by: Viraj Karandikar <vkarandikar@nvidia.com>
Reviewed-on: http://git-master/r/730685
(cherry picked from commit a2ee28c6ba423751b8bbc0743a645241391823b7)
Reviewed-on: http://git-master/r/751858
Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com>

sound/soc/tegra-alt/tegra210_sfc_alt.c
sound/soc/tegra-alt/tegra210_sfc_alt.h
sound/soc/tegra-alt/tegra_t210ref_mobile_es755_alt.c

index cd6415d..accb1ac 100644 (file)
@@ -30,6 +30,7 @@
 #include <sound/soc.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/of_device.h>
+#include <linux/delay.h>
 
 #include "tegra210_xbar_alt.h"
 #include "tegra210_sfc_alt.h"
@@ -298,6 +299,25 @@ static int tegra210_sfc_set_audio_cif(struct tegra210_sfc *sfc,
        return 0;
 }
 
+static int tegra210_sfc_soft_reset(struct tegra210_sfc *sfc)
+{
+       u32 val;
+       int cnt = 10;
+       int ret = 0;
+
+       regmap_update_bits(sfc->regmap,
+                       TEGRA210_SFC_SOFT_RESET,
+                       TEGRA210_SFC_SOFT_RESET_EN,
+                       1);
+       do {
+               udelay(100);
+               regmap_read(sfc->regmap, TEGRA210_SFC_SOFT_RESET, &val);
+       } while ((val & TEGRA210_SFC_SOFT_RESET_EN) && cnt--);
+       if (!cnt)
+               ret = -ETIMEDOUT;
+       return ret;
+}
+
 static int tegra210_sfc_in_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_pcm_hw_params *params,
                                 struct snd_soc_dai *dai)
@@ -306,7 +326,17 @@ static int tegra210_sfc_in_hw_params(struct snd_pcm_substream *substream,
        struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai);
        int ret;
 
-       sfc->srate_in = params_rate(params);
+       regmap_update_bits(sfc->regmap,
+                       TEGRA210_SFC_COEF_RAM,
+                       TEGRA210_SFC_COEF_RAM_COEF_RAM_EN,
+                       0);
+
+       ret = tegra210_sfc_soft_reset(sfc);
+       if (ret) {
+               dev_err(dev, "SOFT_RESET error: %d\n", ret);
+               return ret;
+       }
+
        ret = tegra210_sfc_set_audio_cif(sfc, params,
                                TEGRA210_SFC_AXBAR_RX_CIF_CTRL);
        if (ret) {
@@ -354,7 +384,10 @@ static int tegra210_sfc_get_srate(struct snd_kcontrol *kcontrol,
        struct tegra210_sfc *sfc = snd_soc_codec_get_drvdata(codec);
 
        /* get the sfc output rate */
-       ucontrol->value.integer.value[0] = sfc->srate_out + 1;
+       if (strstr(kcontrol->id.name, "input"))
+               ucontrol->value.integer.value[0] = sfc->srate_in + 1;
+       else if (strstr(kcontrol->id.name, "output"))
+               ucontrol->value.integer.value[0] = sfc->srate_out + 1;
 
        return 0;
 }
@@ -364,9 +397,16 @@ static int tegra210_sfc_put_srate(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct tegra210_sfc *sfc = snd_soc_codec_get_drvdata(codec);
+       int srate = ucontrol->value.integer.value[0] - 1;
+
+       if ((srate < TEGRA210_SFC_FS8 - 1) || (srate > TEGRA210_SFC_FS192))
+               return -EINVAL;
 
-       /* update the sfc output rate */
-       sfc->srate_out = ucontrol->value.integer.value[0] - 1;
+       /* Update the SFC input/output rate */
+       if (strstr(kcontrol->id.name, "input"))
+               sfc->srate_in = srate;
+       else if (strstr(kcontrol->id.name, "output"))
+               sfc->srate_out = srate;
 
        return 0;
 }
@@ -456,6 +496,8 @@ static const struct soc_enum tegra210_sfc_srate_enum =
                tegra210_sfc_srate_text);
 
 static const struct snd_kcontrol_new tegra210_sfc_controls[] = {
+       SOC_ENUM_EXT("input rate", tegra210_sfc_srate_enum,
+               tegra210_sfc_get_srate, tegra210_sfc_put_srate),
        SOC_ENUM_EXT("output rate", tegra210_sfc_srate_enum,
                tegra210_sfc_get_srate, tegra210_sfc_put_srate),
 };
index aab7d29..b1924cf 100644 (file)
@@ -87,6 +87,8 @@
 /* Fields in TEGRA210_SFC_COEF_RAM */
 #define TEGRA210_SFC_COEF_RAM_COEF_RAM_EN      BIT(0)
 
+#define TEGRA210_SFC_SOFT_RESET_EN              BIT(0)
+
 /* SRC coefficients */
 #define TEGRA210_SFC_COEF_RAM_DEPTH            64
 
index e3f004a..4ba76b8 100644 (file)
@@ -76,6 +76,22 @@ struct tegra_t210ref {
        int rate_via_kcontrol;
 };
 
+static const int tegra_t210ref_srate_values[] = {
+       0,
+       8000,
+       16000,
+       44100,
+       48000,
+       11025,
+       22050,
+       24000,
+       32000,
+       88200,
+       96000,
+       176000,
+       192000,
+};
+
 static struct snd_soc_jack tegra_t210ref_hp_jack;
 
 static struct snd_soc_jack_gpio tegra_t210ref_hp_jack_gpio = {
@@ -158,9 +174,12 @@ static int tegra_t210ref_dai_init(struct snd_soc_pcm_runtime *rtd,
        struct tegra_t210ref *machine = snd_soc_card_get_drvdata(card);
        struct snd_soc_pcm_stream *dai_params;
        unsigned int idx, mclk, clk_out_rate;
-       int err;
+       int err, codec_rate, clk_rate;
 
-       switch (rate) {
+       codec_rate = tegra_t210ref_srate_values[machine->rate_via_kcontrol];
+       clk_rate = (machine->rate_via_kcontrol) ? codec_rate : rate;
+
+       switch (clk_rate) {
        case 11025:
        case 22050:
        case 44100:
@@ -184,7 +203,7 @@ static int tegra_t210ref_dai_init(struct snd_soc_pcm_runtime *rtd,
 
        pr_info("Setting pll_a = %d Hz clk_out = %d Hz\n", mclk, clk_out_rate);
        err = tegra_alt_asoc_utils_set_rate(&machine->audio_clock,
-                               rate, mclk, clk_out_rate);
+                               clk_rate, mclk, clk_out_rate);
        if (err < 0) {
                dev_err(card->dev, "Can't configure clocks\n");
                return err;
@@ -197,6 +216,18 @@ static int tegra_t210ref_dai_init(struct snd_soc_pcm_runtime *rtd,
                return err;
        }
 
+       /* update dai link hw_params for non pcm links */
+       for (idx = 0; idx < card->num_rtd; idx++) {
+               if (card->rtd[idx].dai_link->params) {
+                       dai_params =
+                         (struct snd_soc_pcm_stream *)
+                         card->rtd[idx].dai_link->params;
+                       dai_params->rate_min = rate;
+                       dai_params->channels_min = channels;
+                       dai_params->formats = formats;
+               }
+       }
+
        if (!of_device_is_compatible(np,
          "nvidia,tegra-audio-t210ref-mobile-foster")) {
                idx = tegra_machine_get_codec_dai_link_idx("earSmart-playback");
@@ -210,8 +241,7 @@ static int tegra_t210ref_dai_init(struct snd_soc_pcm_runtime *rtd,
                        if (!machine->rate_via_kcontrol)
                                dai_params->rate_min = rate;
                        else
-                               dai_params->rate_min =
-                                       machine->rate_via_kcontrol;
+                               dai_params->rate_min = codec_rate;
 
                        dai_params->channels_min = channels;
                        dai_params->formats = formats;
@@ -250,16 +280,16 @@ static int tegra_t210ref_dai_init(struct snd_soc_pcm_runtime *rtd,
                }
        }
 
-       /* update dai link hw_params for non pcm links */
-       for (idx = 0; idx < card->num_rtd; idx++) {
-               if (card->rtd[idx].dai_link->params) {
-                       dai_params =
-                         (struct snd_soc_pcm_stream *)
-                         card->rtd[idx].dai_link->params;
+       idx = tegra_machine_get_codec_dai_link_idx("spdif-dit-2");
+       if (idx != -EINVAL) {
+               dai_params =
+               (struct snd_soc_pcm_stream *)card->rtd[idx].dai_link->params;
+
+               /* update link_param to update hw_param for DAPM */
+               if (!machine->rate_via_kcontrol)
                        dai_params->rate_min = rate;
-                       dai_params->channels_min = channels;
-                       dai_params->formats = formats;
-               }
+               else
+                       dai_params->rate_min = codec_rate;
        }
 
        return 0;
@@ -309,8 +339,7 @@ static int tegra_t210ref_compr_set_params(struct snd_compr_stream *cstream)
                return -EINVAL;
        }
 
-       /* TODO: Add SRC in offload path */
-       srate = 48000;
+       srate = snd_pcm_rate_bit_to_rate(codec_params.sample_rate);
 
        err = tegra_t210ref_dai_init(rtd, srate, codec_params.ch_out,
                        SNDRV_PCM_FMTBIT_S16_LE);
@@ -454,21 +483,6 @@ static int tegra_t210ref_event_int_spk(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-static const int tegra_t210ref_srate_values[] = {
-       0,
-       8000,
-       16000,
-       44100,
-       48000,
-       11025,
-       22050,
-       24000,
-       32000,
-       88200,
-       96000,
-       176000,
-       192000,
-};
 
 static const char * const tegra_t210ref_srate_text[] = {
        "None",
@@ -769,6 +783,7 @@ static int tegra_t210ref_driver_probe(struct platform_device *pdev)
        machine->pdata = pdata;
        machine->pcard = card;
        machine->clock_enabled = 1;
+       machine->rate_via_kcontrol = 0;
 
        ret = tegra_alt_asoc_utils_init(&machine->audio_clock,
                                        &pdev->dev,