Merge remote-tracking branches 'asoc/topic/ab8500', 'asoc/topic/adau17x1', 'asoc...
authorMark Brown <broonie@kernel.org>
Sun, 13 Mar 2016 08:16:41 +0000 (15:16 +0700)
committerMark Brown <broonie@kernel.org>
Sun, 13 Mar 2016 08:16:41 +0000 (15:16 +0700)
1  2  3  4  5  6 
sound/soc/codecs/ab8500-codec.c
sound/soc/codecs/arizona.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm_adsp.c

@@@@@@@ -1130,7 -1130,7 -1130,7 -1130,7 -1130,7 -1130,7 +1130,7 @@@@@@@ static int sid_status_control_get(struc
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
      
        mutex_lock(&drvdata->ctrl_lock);
 -----  ucontrol->value.integer.value[0] = drvdata->sid_status;
 +++++  ucontrol->value.enumerated.item[0] = drvdata->sid_status;
        mutex_unlock(&drvdata->ctrl_lock);
      
        return 0;
@@@@@@@ -1147,7 -1147,7 -1147,7 -1147,7 -1147,7 -1147,7 +1147,7 @@@@@@@ static int sid_status_control_put(struc
      
        dev_dbg(codec->dev, "%s: Enter\n", __func__);
      
 -----  if (ucontrol->value.integer.value[0] != SID_APPLY_FIR) {
 +++++  if (ucontrol->value.enumerated.item[0] != SID_APPLY_FIR) {
                dev_err(codec->dev,
                        "%s: ERROR: This control supports '%s' only!\n",
                        __func__, enum_sid_state[SID_APPLY_FIR]);
@@@@@@@ -1199,7 -1199,7 -1199,7 -1199,7 -1199,7 -1199,7 +1199,7 @@@@@@@ static int anc_status_control_get(struc
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
      
        mutex_lock(&drvdata->ctrl_lock);
 -----  ucontrol->value.integer.value[0] = drvdata->anc_status;
 +++++  ucontrol->value.enumerated.item[0] = drvdata->anc_status;
        mutex_unlock(&drvdata->ctrl_lock);
      
        return 0;
@@@@@@@ -1220,7 -1220,7 -1220,7 -1220,7 -1220,7 -1220,7 +1220,7 @@@@@@@ static int anc_status_control_put(struc
      
        mutex_lock(&drvdata->ctrl_lock);
      
 -----  req = ucontrol->value.integer.value[0];
 +++++  req = ucontrol->value.enumerated.item[0];
        if (req >= ARRAY_SIZE(enum_anc_state)) {
                status = -EINVAL;
                goto cleanup;
@@@@@@@ -2134,7 -2134,6 -2134,7 -2134,7 -2134,7 -2134,7 +2134,6 @@@@@@@ static int ab8500_codec_set_dai_fmt(str
                        "%s: ERROR: Unsupporter master mask 0x%x\n",
                        __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
                return -EINVAL;
- ----          break;
        }
      
        snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val);
@@@@@@@ -1398,29 -1398,29 -1398,29 -1398,29 -1398,29 -1398,6 +1398,6 @@@@@@@ static const int arizona_48k_bclk_rates
        24576000,
      };
      
----- static const unsigned int arizona_48k_rates[] = {
-----   12000,
-----   24000,
-----   48000,
-----   96000,
-----   192000,
-----   384000,
-----   768000,
-----   4000,
-----   8000,
-----   16000,
-----   32000,
-----   64000,
-----   128000,
-----   256000,
-----   512000,
----- };
----- 
----- static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = {
-----   .count  = ARRAY_SIZE(arizona_48k_rates),
-----   .list   = arizona_48k_rates,
----- };
----- 
      static const int arizona_44k1_bclk_rates[] = {
        -1,
        44100,
        22579200,
      };
      
----- static const unsigned int arizona_44k1_rates[] = {
-----   11025,
-----   22050,
-----   44100,
-----   88200,
-----   176400,
-----   352800,
-----   705600,
----- };
----- 
----- static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = {
-----   .count  = ARRAY_SIZE(arizona_44k1_rates),
-----   .list   = arizona_44k1_rates,
----- };
----- 
----- static int arizona_sr_vals[] = {
+++++ static const unsigned int arizona_sr_vals[] = {
        0,
        12000,
        24000,
        512000,
      };
      
+++++ #define ARIZONA_48K_RATE_MASK     0x0F003E
+++++ #define ARIZONA_44K1_RATE_MASK    0x003E00
+++++ #define ARIZONA_RATE_MASK (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK)
+++++ 
+++++ static const struct snd_pcm_hw_constraint_list arizona_constraint = {
+++++   .count  = ARRAY_SIZE(arizona_sr_vals),
+++++   .list   = arizona_sr_vals,
+++++ };
+++++ 
      static int arizona_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
      {
        struct snd_soc_codec *codec = dai->codec;
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
        struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
-----   const struct snd_pcm_hw_constraint_list *constraint;
        unsigned int base_rate;
      
 +      if (!substream->runtime)
 +              return 0;
 +    
        switch (dai_priv->clk) {
        case ARIZONA_CLK_SYSCLK:
                base_rate = priv->sysclk;
        }
      
        if (base_rate == 0)
-----           return 0;
----- 
-----   if (base_rate % 8000)
-----           constraint = &arizona_44k1_constraint;
+++++           dai_priv->constraint.mask = ARIZONA_RATE_MASK;
+++++   else if (base_rate % 8000)
+++++           dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK;
        else
-----           constraint = &arizona_48k_constraint;
+++++           dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK;
      
        return snd_pcm_hw_constraint_list(substream->runtime, 0,
                                          SNDRV_PCM_HW_PARAM_RATE,
-----                                     constraint);
+++++                                     &dai_priv->constraint);
      }
      
      static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
@@@@@@@ -1659,7 -1656,7 -1659,7 -1659,7 -1659,7 -1628,7 +1628,7 @@@@@@@ static int arizona_hw_params(struct snd
        bool reconfig;
        unsigned int aif_tx_state, aif_rx_state;
      
 -      if (params_rate(params) % 8000)
 +      if (params_rate(params) % 4000)
                rates = &arizona_44k1_bclk_rates[0];
        else
                rates = &arizona_48k_bclk_rates[0];
@@@@@@@ -1911,6 -1908,6 -1911,6 -1911,6 -1911,6 -1880,7 +1880,7 @@@@@@@ int arizona_init_dai(struct arizona_pri
        struct arizona_dai_priv *dai_priv = &priv->dai[id];
      
        dai_priv->clk = ARIZONA_CLK_SYSCLK;
+++++   dai_priv->constraint = arizona_constraint;
      
        return 0;
      }
@@@@@@@ -1929,25 -1926,6 -1929,6 -1929,6 -1929,6 -1899,6 +1899,25 @@@@@@@ static struct 
        { 1000000, 13500000, 0,  1 },
      };
      
 +++++static const unsigned int pseudo_fref_max[ARIZONA_FLL_MAX_FRATIO] = {
 +++++  13500000,
 +++++   6144000,
 +++++   6144000,
 +++++   3072000,
 +++++   3072000,
 +++++   2822400,
 +++++   2822400,
 +++++   1536000,
 +++++   1536000,
 +++++   1536000,
 +++++   1536000,
 +++++   1536000,
 +++++   1536000,
 +++++   1536000,
 +++++   1536000,
 +++++    768000,
 +++++};
 +++++
      static struct {
        unsigned int min;
        unsigned int max;
@@@@@@@ -2061,32 -2039,16 -2042,16 -2042,16 -2042,16 -2012,16 +2031,32 @@@@@@@ static int arizona_calc_fratio(struct a
        /* Adjust FRATIO/refdiv to avoid integer mode if possible */
        refdiv = cfg->refdiv;
      
 +++++  arizona_fll_dbg(fll, "pseudo: initial ratio=%u fref=%u refdiv=%u\n",
 +++++                  init_ratio, Fref, refdiv);
 +++++
        while (div <= ARIZONA_FLL_MAX_REFDIV) {
                for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO;
                     ratio++) {
                        if ((ARIZONA_FLL_VCO_CORNER / 2) /
 -----                      (fll->vco_mult * ratio) < Fref)
 +++++                      (fll->vco_mult * ratio) < Fref) {
 +++++                          arizona_fll_dbg(fll, "pseudo: hit VCO corner\n");
                                break;
 +++++                  }
 +++++
 +++++                  if (Fref > pseudo_fref_max[ratio - 1]) {
 +++++                          arizona_fll_dbg(fll,
 +++++                                  "pseudo: exceeded max fref(%u) for ratio=%u\n",
 +++++                                  pseudo_fref_max[ratio - 1],
 +++++                                  ratio);
 +++++                          break;
 +++++                  }
      
                        if (target % (ratio * Fref)) {
                                cfg->refdiv = refdiv;
                                cfg->fratio = ratio - 1;
 +++++                          arizona_fll_dbg(fll,
 +++++                                  "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
 +++++                                  Fref, refdiv, div, ratio);
                                return ratio;
                        }
                }
                        if (target % (ratio * Fref)) {
                                cfg->refdiv = refdiv;
                                cfg->fratio = ratio - 1;
 +++++                          arizona_fll_dbg(fll,
 +++++                                  "pseudo: found fref=%u refdiv=%d(%d) ratio=%d\n",
 +++++                                  Fref, refdiv, div, ratio);
                                return ratio;
                        }
                }
                Fref /= 2;
                refdiv++;
                init_ratio = arizona_find_fratio(Fref, NULL);
 +++++          arizona_fll_dbg(fll,
 +++++                          "pseudo: change fref=%u refdiv=%d(%d) ratio=%u\n",
 +++++                          Fref, refdiv, div, init_ratio);
        }
      
        arizona_fll_warn(fll, "Falling back to integer mode operation\n");
@@@@@@@ -2179,11 -2135,11 -2138,11 -2138,11 -2138,11 -2108,12 +2149,12 @@@@@@@ static int arizona_calc_fll(struct ariz
                return -EINVAL;
        }
      
-----   arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
+++++   arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
                        cfg->n, cfg->theta, cfg->lambda);
-----   arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
-----                   cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
-----   arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain);
+++++   arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n",
+++++                   cfg->fratio, ratio, cfg->outdiv,
+++++                   cfg->refdiv, 1 << cfg->refdiv);
+++++   arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
      
        return 0;
      
@@@@@@@ -191,6 -191,6 -191,6 -191,6 -191,6 -191,25 +191,25 @@@@@@@ static int wm5110_sysclk_ev(struct snd_
        return 0;
      }
      
+++++ static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w,
+++++                           struct snd_kcontrol *kcontrol, int event)
+++++ {
+++++   struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+++++   struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+++++   unsigned int v;
+++++   int ret;
+++++ 
+++++   ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+++++   if (ret != 0) {
+++++           dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret);
+++++           return ret;
+++++   }
+++++ 
+++++   v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+++++ 
+++++   return wm_adsp2_early_event(w, kcontrol, event, v);
+++++ }
+++++ 
      static const struct reg_sequence wm5110_no_dre_left_enable[] = {
        { 0x3024, 0xE410 },
        { 0x3025, 0x0056 },
@@@@@@@ -360,13 -360,15 -360,13 -360,13 -360,13 -379,13 +379,13 @@@@@@@ static int wm5110_hp_ev(struct snd_soc_
      
      static int wm5110_clear_pga_volume(struct arizona *arizona, int output)
      {
 -      struct reg_sequence clear_pga = {
 -              ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4, 0x80
 -      };
 +      unsigned int reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + output * 4;
        int ret;
      
 -      ret = regmap_multi_reg_write_bypassed(arizona->regmap, &clear_pga, 1);
 +      ret = regmap_write(arizona->regmap, reg, 0x80);
        if (ret)
                dev_err(arizona->dev, "Failed to clear PGA (0x%x): %d\n",
 -                      clear_pga.reg, ret);
 +                      reg, ret);
      
        return ret;
      }
@@@@@@@ -437,17 -439,18 -437,17 -437,17 -437,17 -456,17 +456,17 @@@@@@@ static int wm5110_in_pga_get(struct snd
      {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 -      struct snd_soc_card *card = dapm->card;
        int ret;
      
        /*
         * PGA Volume is also used as part of the enable sequence, so
         * usage of it should be avoided whilst that is running.
         */
 -      mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 +      snd_soc_dapm_mutex_lock(dapm);
      
        ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
      
 -      mutex_unlock(&card->dapm_mutex);
 +      snd_soc_dapm_mutex_unlock(dapm);
      
        return ret;
      }
@@@@@@@ -457,17 -460,18 -457,17 -457,17 -457,17 -476,17 +476,17 @@@@@@@ static int wm5110_in_pga_put(struct snd
      {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
 -      struct snd_soc_card *card = dapm->card;
        int ret;
      
        /*
         * PGA Volume is also used as part of the enable sequence, so
         * usage of it should be avoided whilst that is running.
         */
 -      mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 +      snd_soc_dapm_mutex_lock(dapm);
      
        ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
      
 -      mutex_unlock(&card->dapm_mutex);
 +      snd_soc_dapm_mutex_unlock(dapm);
      
        return ret;
      }
@@@@@@@ -1179,10 -1183,10 -1179,10 -1179,10 -1179,10 -1198,10 +1198,10 @@@@@@@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC
      SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
                 NULL, 0),
      
----- WM_ADSP2("DSP1", 0),
----- WM_ADSP2("DSP2", 1),
----- WM_ADSP2("DSP3", 2),
----- WM_ADSP2("DSP4", 3),
+++++ WM_ADSP2("DSP1", 0, wm5110_adsp_power_ev),
+++++ WM_ADSP2("DSP2", 1, wm5110_adsp_power_ev),
+++++ WM_ADSP2("DSP3", 2, wm5110_adsp_power_ev),
+++++ WM_ADSP2("DSP4", 3, wm5110_adsp_power_ev),
      
      SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
                 ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
@@@@@@@ -1809,6 -1813,6 -1809,6 -1809,6 -1809,6 -1828,9 +1828,9 @@@@@@@ static const struct snd_soc_dapm_route 
        { "Voice Control DSP", NULL, "DSP3" },
        { "Voice Control DSP", NULL, "SYSCLK" },
      
+++++   { "Audio Trace DSP", NULL, "DSP1" },
+++++   { "Audio Trace DSP", NULL, "SYSCLK" },
+++++ 
        { "IN1L PGA", NULL, "IN1L" },
        { "IN1R PGA", NULL, "IN1R" },
      
@@@@@@@ -2002,7 -2006,7 -2002,7 -2002,7 -2002,7 -2024,7 +2024,7 @@@@@@@ static int wm5110_set_fll(struct snd_so
        }
      }
      
----- #define WM5110_RATES SNDRV_PCM_RATE_8000_192000
+++++ #define WM5110_RATES SNDRV_PCM_RATE_KNOT
      
      #define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@@@@@@ -2152,6 -2156,6 -2152,6 -2152,6 -2152,6 -2174,27 +2174,27 @@@@@@@ static struct snd_soc_dai_driver wm5110
                        .formats = WM5110_FORMATS,
                },
        },
+++++   {
+++++           .name = "wm5110-cpu-trace",
+++++           .capture = {
+++++                   .stream_name = "Audio Trace CPU",
+++++                   .channels_min = 1,
+++++                   .channels_max = 6,
+++++                   .rates = WM5110_RATES,
+++++                   .formats = WM5110_FORMATS,
+++++           },
+++++           .compress_new = snd_soc_new_compress,
+++++   },
+++++   {
+++++           .name = "wm5110-dsp-trace",
+++++           .capture = {
+++++                   .stream_name = "Audio Trace DSP",
+++++                   .channels_min = 1,
+++++                   .channels_max = 6,
+++++                   .rates = WM5110_RATES,
+++++                   .formats = WM5110_FORMATS,
+++++           },
+++++   },
      };
      
      static int wm5110_open(struct snd_compr_stream *stream)
      
        if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
                n_adsp = 2;
+++++   } else if (strcmp(rtd->codec_dai->name, "wm5110-dsp-trace") == 0) {
+++++           n_adsp = 0;
        } else {
                dev_err(arizona->dev,
                        "No suitable compressed stream for DAI '%s'\n",
        return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
      }
      
- ---   struct wm5110_priv *florida = data;
- ---   int ret;
 +    static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
 +    {
- ---   ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]);
- ---   if (ret == -ENODEV)
+++++   struct wm5110_priv *priv = data;
+++++   struct arizona *arizona = priv->core.arizona;
+++++   int serviced = 0;
+++++   int i, ret;
 +    
+++++   for (i = 0; i < WM5110_NUM_ADSP; ++i) {
+++++           ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
+++++           if (ret != -ENODEV)
+++++                   serviced++;
+++++   }
+++++ 
+++++   if (!serviced) {
+++++           dev_err(arizona->dev, "Spurious compressed data IRQ\n");
 +              return IRQ_NONE;
+++++   }
 +    
 +      return IRQ_HANDLED;
 +    }
 +    
      static int wm5110_codec_probe(struct snd_soc_codec *codec)
      {
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
 +      struct arizona *arizona = priv->core.arizona;
        int i, ret;
      
        priv->core.arizona->dapm = dapm;
        arizona_init_gpio(codec);
        arizona_init_mono(codec);
      
 +      ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
 +                                "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
 +                                priv);
 +      if (ret != 0) {
 +              dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
 +              return ret;
 +      }
 +    
        for (i = 0; i < WM5110_NUM_ADSP; ++i) {
                ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
                if (ret)
@@@@@@@ -2226,15 -2209,12 -2226,15 -2226,15 -2226,15 -2280,15 +2280,15 @@@@@@@ err_adsp2_codec_probe
        for (--i; i >= 0; --i)
                wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
      
 +      arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
 +    
        return ret;
      }
      
      static int wm5110_codec_remove(struct snd_soc_codec *codec)
      {
        struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
 +      struct arizona *arizona = priv->core.arizona;
        int i;
      
        for (i = 0; i < WM5110_NUM_ADSP; ++i)
      
        priv->core.arizona->dapm = NULL;
      
 +      arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
 +    
        return 0;
      }
      
@@@@@@@ -2295,8 -2273,6 -2295,8 -2295,8 -2295,8 -2349,8 +2349,8 @@@@@@@ static struct snd_compr_ops wm5110_comp
        .set_params = wm_adsp_compr_set_params,
        .get_caps = wm_adsp_compr_get_caps,
        .trigger = wm_adsp_compr_trigger,
 +      .pointer = wm_adsp_compr_pointer,
 +      .copy = wm_adsp_compr_copy,
      };
      
      static struct snd_soc_platform_driver wm5110_compr_platform = {
@@@@@@@ -2366,7 -2342,7 -2366,7 -2366,7 -2366,7 -2420,7 +2420,7 @@@@@@@ static int wm5110_probe(struct platform
        ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
        if (ret < 0) {
                dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
-----           goto error;
+++++           return ret;
        }
      
        ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
                snd_soc_unregister_platform(&pdev->dev);
        }
      
----- error:
        return ret;
      }
      
      static int wm5110_remove(struct platform_device *pdev)
      {
 +++++  snd_soc_unregister_platform(&pdev->dev);
        snd_soc_unregister_codec(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
      
      #include <sound/initval.h>
      #include <sound/tlv.h>
      
----- #include <linux/mfd/arizona/registers.h>
----- 
----- #include "arizona.h"
      #include "wm_adsp.h"
      
      #define adsp_crit(_dsp, fmt, ...) \
@@@@@@@ -279,11 -279,6 -279,11 -279,11 -279,11 -276,11 +276,11 @@@@@@@ struct wm_adsp_compr_buf 
      
        struct wm_adsp_buffer_region *regions;
        u32 host_buf_ptr;
 +    
 +      u32 error;
 +      u32 irq_count;
 +      int read_index;
 +      int avail;
      };
      
      struct wm_adsp_compr {
      
        struct snd_compr_stream *stream;
        struct snd_compressed_buffer size;
 +    
 +      u32 *raw_buf;
 +      unsigned int copied_total;
++++ +
++++ +  unsigned int sample_rate;
      };
      
      #define WM_ADSP_DATA_WORD_SIZE         3
@@@@@@@ -328,7 -320,7 -328,7 -328,7 -330,7 -325,7 +327,7 @@@@@@@ struct wm_adsp_buffer_region_def 
        unsigned int size_offset;
      };
      
----- static struct wm_adsp_buffer_region_def ez2control_regions[] = {
+++++ static const struct wm_adsp_buffer_region_def default_regions[] = {
        {
                .mem_type = WMFW_ADSP2_XM,
                .base_offset = HOST_BUFFER_FIELD(X_buf_base),
@@@@@@@ -350,10 -342,10 -350,10 -350,10 -352,10 -347,10 +349,10 @@@@@@@ struct wm_adsp_fw_caps 
        u32 id;
        struct snd_codec_desc desc;
        int num_regions;
-----   struct wm_adsp_buffer_region_def *region_defs;
+++++   const struct wm_adsp_buffer_region_def *region_defs;
      };
      
----- static const struct wm_adsp_fw_caps ez2control_caps[] = {
+++++ static const struct wm_adsp_fw_caps ctrl_caps[] = {
        {
                .id = SND_AUDIOCODEC_BESPOKE,
                .desc = {
                        .num_sample_rates = 1,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
                },
-----           .num_regions = ARRAY_SIZE(ez2control_regions),
-----           .region_defs = ez2control_regions,
+++++           .num_regions = ARRAY_SIZE(default_regions),
+++++           .region_defs = default_regions,
+++++   },
+++++ };
+++++ 
+++++ static const struct wm_adsp_fw_caps trace_caps[] = {
+++++   {
+++++           .id = SND_AUDIOCODEC_BESPOKE,
+++++           .desc = {
+++++                   .max_ch = 8,
+++++                   .sample_rates = {
+++++                           4000, 8000, 11025, 12000, 16000, 22050,
+++++                           24000, 32000, 44100, 48000, 64000, 88200,
+++++                           96000, 176400, 192000
+++++                   },
+++++                   .num_sample_rates = 15,
+++++                   .formats = SNDRV_PCM_FMTBIT_S16_LE,
+++++           },
+++++           .num_regions = ARRAY_SIZE(default_regions),
+++++           .region_defs = default_regions,
        },
      };
      
@@@@@@@ -382,11 -374,11 -382,11 -382,11 -384,11 -397,16 +399,16 @@@@@@@ static const struct 
        [WM_ADSP_FW_CTRL] =     {
                .file = "ctrl",
                .compr_direction = SND_COMPRESS_CAPTURE,
-----           .num_caps = ARRAY_SIZE(ez2control_caps),
-----           .caps = ez2control_caps,
+++++           .num_caps = ARRAY_SIZE(ctrl_caps),
+++++           .caps = ctrl_caps,
        },
        [WM_ADSP_FW_ASR] =      { .file = "asr" },
-----   [WM_ADSP_FW_TRACE] =    { .file = "trace" },
+++++   [WM_ADSP_FW_TRACE] =    {
+++++           .file = "trace",
+++++           .compr_direction = SND_COMPRESS_CAPTURE,
+++++           .num_caps = ARRAY_SIZE(trace_caps),
+++++           .caps = trace_caps,
+++++   },
        [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
        [WM_ADSP_FW_MISC] =     { .file = "misc" },
      };
@@@@@@@ -586,7 -578,7 -586,7 -586,7 -588,7 -606,7 +608,7 @@@@@@@ static int wm_adsp_fw_get(struct snd_kc
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
      
 -----  ucontrol->value.integer.value[0] = dsp[e->shift_l].fw;
 +++++  ucontrol->value.enumerated.item[0] = dsp[e->shift_l].fw;
      
        return 0;
      }
@@@@@@@ -599,10 -591,10 -599,10 -599,10 -601,10 -619,10 +621,10 @@@@@@@ static int wm_adsp_fw_put(struct snd_kc
        struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
        int ret = 0;
      
 -----  if (ucontrol->value.integer.value[0] == dsp[e->shift_l].fw)
 +++++  if (ucontrol->value.enumerated.item[0] == dsp[e->shift_l].fw)
                return 0;
      
 -----  if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
 +++++  if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
                return -EINVAL;
      
        mutex_lock(&dsp[e->shift_l].pwr_lock);
        if (dsp[e->shift_l].running || dsp[e->shift_l].compr)
                ret = -EBUSY;
        else
 -----          dsp[e->shift_l].fw = ucontrol->value.integer.value[0];
 +++++          dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
      
        mutex_unlock(&dsp[e->shift_l].pwr_lock);
      
@@@@@@@ -719,19 -711,19 -719,19 -719,19 -721,19 -739,19 +741,19 @@@@@@@ static int wm_coeff_write_control(struc
        reg = ctl->alg_region.base + ctl->offset;
        reg = wm_adsp_region_to_reg(mem, reg);
      
---- -  scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
++++ +  scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
        if (!scratch)
                return -ENOMEM;
      
        ret = regmap_raw_write(dsp->regmap, reg, scratch,
---- -                         ctl->len);
++++ +                         len);
        if (ret) {
                adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
---- -                   ctl->len, reg, ret);
++++ +                   len, reg, ret);
                kfree(scratch);
                return ret;
        }
---- -  adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
++++ +  adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg);
      
        kfree(scratch);
      
@@@@@@@ -778,20 -770,20 -778,20 -778,20 -780,20 -798,20 +800,20 @@@@@@@ static int wm_coeff_read_control(struc
        reg = ctl->alg_region.base + ctl->offset;
        reg = wm_adsp_region_to_reg(mem, reg);
      
---- -  scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
++++ +  scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
        if (!scratch)
                return -ENOMEM;
      
---- -  ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
++++ +  ret = regmap_raw_read(dsp->regmap, reg, scratch, len);
        if (ret) {
                adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
---- -                   ctl->len, reg, ret);
++++ +                   len, reg, ret);
                kfree(scratch);
                return ret;
        }
---- -  adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
++++ +  adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);
      
---- -  memcpy(buf, scratch, ctl->len);
++++ +  memcpy(buf, scratch, len);
        kfree(scratch);
      
        return 0;
@@@@@@@ -855,17 -847,17 -855,17 -855,17 -857,18 -875,17 +877,18 @@@@@@@ static int wmfw_add_ctl(struct wm_adsp 
                        kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
                if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
                        kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
++++ +  } else {
++++ +          kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
++++ +          kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
        }
      
---- -  ret = snd_soc_add_card_controls(dsp->card,
---- -                                  kcontrol, 1);
++++ +  ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
        if (ret < 0)
                goto err_kcontrol;
      
        kfree(kcontrol);
      
---- -  ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
---- -                                            ctl->name);
++++ +  ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name);
      
        return 0;
      
@@@@@@@ -885,9 -877,9 -885,9 -885,9 -888,7 -905,9 +908,7 @@@@@@@ static int wm_coeff_init_control_caches
                if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
                        continue;
      
---- -          ret = wm_coeff_read_control(ctl,
---- -                                      ctl->cache,
---- -                                      ctl->len);
++++ +          ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
                if (ret < 0)
                        return ret;
        }
@@@@@@@ -904,9 -896,9 -904,9 -904,9 -905,7 -924,9 +925,7 @@@@@@@ static int wm_coeff_sync_controls(struc
                if (!ctl->enabled)
                        continue;
                if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
---- -                  ret = wm_coeff_write_control(ctl,
---- -                                               ctl->cache,
---- -                                               ctl->len);
++++ +                  ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len);
                        if (ret < 0)
                                return ret;
                }
@@@@@@@ -1502,8 -1494,8 -1502,8 -1502,8 -1501,7 -1522,8 +1521,7 @@@@@@@ static void *wm_adsp_read_algs(struct w
      
        ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
        if (ret != 0) {
---- -          adsp_err(dsp, "Failed to read algorithm list: %d\n",
---- -                  ret);
++++ +          adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
                kfree(alg);
                return ERR_PTR(ret);
        }
@@@@@@@ -2002,8 -1994,8 -2002,8 -2002,8 -2000,7 -2022,8 +2020,7 @@@@@@@ int wm_adsp1_event(struct snd_soc_dapm_
                                goto err_mutex;
                        }
      
---- -                  val = (val & dsp->sysclk_mask)
---- -                          >> dsp->sysclk_shift;
++++ +                  val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift;
      
                        ret = regmap_update_bits(dsp->regmap,
                                                 dsp->base + ADSP1_CONTROL_31,
@@@@@@@ -2096,8 -2088,8 -2096,8 -2096,8 -2093,7 -2116,8 +2113,7 @@@@@@@ static int wm_adsp2_ena(struct wm_adsp 
      
        /* Wait for the RAM to start, should be near instantaneous */
        for (count = 0; count < 10; ++count) {
---- -          ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
---- -                            &val);
++++ +          ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);
                if (ret != 0)
                        return ret;
      
@@@@@@@ -2123,30 -2115,30 -2123,30 -2123,30 -2119,30 -2143,9 +2139,9 @@@@@@@ static void wm_adsp2_boot_work(struct w
                                           struct wm_adsp,
                                           boot_work);
        int ret;
-----   unsigned int val;
      
        mutex_lock(&dsp->pwr_lock);
      
-----   /*
-----    * For simplicity set the DSP clock rate to be the
-----    * SYSCLK rate rather than making it configurable.
-----    */
-----   ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
-----   if (ret != 0) {
-----           adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
-----           goto err_mutex;
-----   }
-----   val = (val & ARIZONA_SYSCLK_FREQ_MASK)
-----           >> ARIZONA_SYSCLK_FREQ_SHIFT;
----- 
-----   ret = regmap_update_bits_async(dsp->regmap,
-----                                  dsp->base + ADSP2_CLOCKING,
-----                                  ADSP2_CLK_SEL_MASK, val);
-----   if (ret != 0) {
-----           adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
-----           goto err_mutex;
-----   }
----- 
        ret = wm_adsp2_ena(dsp);
        if (ret != 0)
                goto err_mutex;
        mutex_unlock(&dsp->pwr_lock);
      }
      
+++++ static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
+++++ {
+++++   int ret;
+++++ 
+++++   ret = regmap_update_bits_async(dsp->regmap,
+++++                                  dsp->base + ADSP2_CLOCKING,
+++++                                  ADSP2_CLK_SEL_MASK,
+++++                                  freq << ADSP2_CLK_SEL_SHIFT);
+++++   if (ret != 0)
+++++           adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+++++ }
+++++ 
      int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
-----              struct snd_kcontrol *kcontrol, int event)
+++++                    struct snd_kcontrol *kcontrol, int event,
+++++                    unsigned int freq)
      {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
      
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
+++++           wm_adsp2_set_dspclk(dsp, freq);
                queue_work(system_unbound_wq, &dsp->boot_work);
                break;
        default:
@@@@@@@ -2386,7 -2378,6 -2386,7 -2386,7 -2382,7 -2399,7 +2395,7 @@@@@@@ int wm_adsp_compr_free(struct snd_compr
      
        dsp->compr = NULL;
      
 +      kfree(compr->raw_buf);
        kfree(compr);
      
        mutex_unlock(&dsp->pwr_lock);
@@@@@@@ -2445,16 -2436,10 -2445,16 -2445,16 -2441,16 -2458,16 +2454,16 @@@@@@@ static int wm_adsp_compr_check_params(s
        return -EINVAL;
      }
      
 +    static inline unsigned int wm_adsp_compr_frag_words(struct wm_adsp_compr *compr)
 +    {
 +      return compr->size.fragment_size / WM_ADSP_DATA_WORD_SIZE;
 +    }
 +    
      int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
                             struct snd_compr_params *params)
      {
        struct wm_adsp_compr *compr = stream->runtime->private_data;
 +      unsigned int size;
        int ret;
      
        ret = wm_adsp_compr_check_params(stream, params);
        adsp_dbg(compr->dsp, "fragment_size=%d fragments=%d\n",
                 compr->size.fragment_size, compr->size.fragments);
      
 +      size = wm_adsp_compr_frag_words(compr) * sizeof(*compr->raw_buf);
 +      compr->raw_buf = kmalloc(size, GFP_DMA | GFP_KERNEL);
 +      if (!compr->raw_buf)
 +              return -ENOMEM;
 +    
++++ +  compr->sample_rate = params->codec.sample_rate;
++++ +
        return 0;
      }
      EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
@@@@@@@ -2642,8 -2622,6 -2642,8 -2642,8 -2640,8 -2655,8 +2653,8 @@@@@@@ static int wm_adsp_buffer_init(struct w
                return -ENOMEM;
      
        buf->dsp = dsp;
 +      buf->read_index = -1;
 +      buf->irq_count = 0xFFFFFFFF;
      
        ret = wm_adsp_buffer_locate(buf);
        if (ret < 0) {
@@@@@@@ -2727,16 -2705,6 -2727,16 -2727,16 -2725,16 -2740,16 +2738,16 @@@@@@@ int wm_adsp_compr_trigger(struct snd_co
                                 ret);
                        break;
                }
 +    
 +              /* Trigger the IRQ at one fragment of data */
 +              ret = wm_adsp_buffer_write(compr->buf,
 +                                         HOST_BUFFER_FIELD(high_water_mark),
 +                                         wm_adsp_compr_frag_words(compr));
 +              if (ret < 0) {
 +                      adsp_err(dsp, "Failed to set high water mark: %d\n",
 +                               ret);
 +                      break;
 +              }
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                break;
      }
      EXPORT_SYMBOL_GPL(wm_adsp_compr_trigger);
      
- ---           adsp_err(dsp, "Spurious buffer IRQ\n");
 +    static inline int wm_adsp_buffer_size(struct wm_adsp_compr_buf *buf)
 +    {
 +      int last_region = wm_adsp_fw[buf->dsp->fw].caps->num_regions - 1;
 +    
 +      return buf->regions[last_region].cumulative_size;
 +    }
 +    
 +    static int wm_adsp_buffer_update_avail(struct wm_adsp_compr_buf *buf)
 +    {
 +      u32 next_read_index, next_write_index;
 +      int write_index, read_index, avail;
 +      int ret;
 +    
 +      /* Only sync read index if we haven't already read a valid index */
 +      if (buf->read_index < 0) {
 +              ret = wm_adsp_buffer_read(buf,
 +                              HOST_BUFFER_FIELD(next_read_index),
 +                              &next_read_index);
 +              if (ret < 0)
 +                      return ret;
 +    
 +              read_index = sign_extend32(next_read_index, 23);
 +    
 +              if (read_index < 0) {
 +                      adsp_dbg(buf->dsp, "Avail check on unstarted stream\n");
 +                      return 0;
 +              }
 +    
 +              buf->read_index = read_index;
 +      }
 +    
 +      ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(next_write_index),
 +                      &next_write_index);
 +      if (ret < 0)
 +              return ret;
 +    
 +      write_index = sign_extend32(next_write_index, 23);
 +    
 +      avail = write_index - buf->read_index;
 +      if (avail < 0)
 +              avail += wm_adsp_buffer_size(buf);
 +    
 +      adsp_dbg(buf->dsp, "readindex=0x%x, writeindex=0x%x, avail=%d\n",
 +               buf->read_index, write_index, avail);
 +    
 +      buf->avail = avail;
 +    
 +      return 0;
 +    }
 +    
 +    int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
 +    {
 +      struct wm_adsp_compr_buf *buf = dsp->buffer;
 +      struct wm_adsp_compr *compr = dsp->compr;
 +      int ret = 0;
 +    
 +      mutex_lock(&dsp->pwr_lock);
 +    
 +      if (!buf) {
- -- -  if (compr->stream)
 +              ret = -ENODEV;
 +              goto out;
 +      }
 +    
 +      adsp_dbg(dsp, "Handling buffer IRQ\n");
 +    
 +      ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(error), &buf->error);
 +      if (ret < 0) {
 +              adsp_err(dsp, "Failed to check buffer error: %d\n", ret);
 +              goto out;
 +      }
 +      if (buf->error != 0) {
 +              adsp_err(dsp, "Buffer error occurred: %d\n", buf->error);
 +              ret = -EIO;
 +              goto out;
 +      }
 +    
 +      ret = wm_adsp_buffer_read(buf, HOST_BUFFER_FIELD(irq_count),
 +                                &buf->irq_count);
 +      if (ret < 0) {
 +              adsp_err(dsp, "Failed to get irq_count: %d\n", ret);
 +              goto out;
 +      }
 +    
 +      ret = wm_adsp_buffer_update_avail(buf);
 +      if (ret < 0) {
 +              adsp_err(dsp, "Error reading avail: %d\n", ret);
 +              goto out;
 +      }
 +    
++++ +  if (compr && compr->stream)
 +              snd_compr_fragment_elapsed(compr->stream);
 +    
 +    out:
 +      mutex_unlock(&dsp->pwr_lock);
 +    
 +      return ret;
 +    }
 +    EXPORT_SYMBOL_GPL(wm_adsp_compr_handle_irq);
 +    
 +    static int wm_adsp_buffer_reenable_irq(struct wm_adsp_compr_buf *buf)
 +    {
 +      if (buf->irq_count & 0x01)
 +              return 0;
 +    
 +      adsp_dbg(buf->dsp, "Enable IRQ(0x%x) for next fragment\n",
 +               buf->irq_count);
 +    
 +      buf->irq_count |= 0x01;
 +    
 +      return wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(irq_ack),
 +                                  buf->irq_count);
 +    }
 +    
 +    int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
 +                        struct snd_compr_tstamp *tstamp)
 +    {
 +      struct wm_adsp_compr *compr = stream->runtime->private_data;
 +      struct wm_adsp_compr_buf *buf = compr->buf;
 +      struct wm_adsp *dsp = compr->dsp;
 +      int ret = 0;
 +    
 +      adsp_dbg(dsp, "Pointer request\n");
 +    
 +      mutex_lock(&dsp->pwr_lock);
 +    
 +      if (!compr->buf) {
 +              ret = -ENXIO;
 +              goto out;
 +      }
 +    
 +      if (compr->buf->error) {
 +              ret = -EIO;
 +              goto out;
 +      }
 +    
 +      if (buf->avail < wm_adsp_compr_frag_words(compr)) {
 +              ret = wm_adsp_buffer_update_avail(buf);
 +              if (ret < 0) {
 +                      adsp_err(dsp, "Error reading avail: %d\n", ret);
 +                      goto out;
 +              }
 +    
 +              /*
 +               * If we really have less than 1 fragment available tell the
 +               * DSP to inform us once a whole fragment is available.
 +               */
 +              if (buf->avail < wm_adsp_compr_frag_words(compr)) {
 +                      ret = wm_adsp_buffer_reenable_irq(buf);
 +                      if (ret < 0) {
 +                              adsp_err(dsp,
 +                                       "Failed to re-enable buffer IRQ: %d\n",
 +                                       ret);
 +                              goto out;
 +                      }
 +              }
 +      }
 +    
 +      tstamp->copied_total = compr->copied_total;
 +      tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
++++ +  tstamp->sampling_rate = compr->sample_rate;
 +    
 +    out:
 +      mutex_unlock(&dsp->pwr_lock);
 +    
 +      return ret;
 +    }
 +    EXPORT_SYMBOL_GPL(wm_adsp_compr_pointer);
 +    
 +    static int wm_adsp_buffer_capture_block(struct wm_adsp_compr *compr, int target)
 +    {
 +      struct wm_adsp_compr_buf *buf = compr->buf;
 +      u8 *pack_in = (u8 *)compr->raw_buf;
 +      u8 *pack_out = (u8 *)compr->raw_buf;
 +      unsigned int adsp_addr;
 +      int mem_type, nwords, max_read;
 +      int i, j, ret;
 +    
 +      /* Calculate read parameters */
 +      for (i = 0; i < wm_adsp_fw[buf->dsp->fw].caps->num_regions; ++i)
 +              if (buf->read_index < buf->regions[i].cumulative_size)
 +                      break;
 +    
 +      if (i == wm_adsp_fw[buf->dsp->fw].caps->num_regions)
 +              return -EINVAL;
 +    
 +      mem_type = buf->regions[i].mem_type;
 +      adsp_addr = buf->regions[i].base_addr +
 +                  (buf->read_index - buf->regions[i].offset);
 +    
 +      max_read = wm_adsp_compr_frag_words(compr);
 +      nwords = buf->regions[i].cumulative_size - buf->read_index;
 +    
 +      if (nwords > target)
 +              nwords = target;
 +      if (nwords > buf->avail)
 +              nwords = buf->avail;
 +      if (nwords > max_read)
 +              nwords = max_read;
 +      if (!nwords)
 +              return 0;
 +    
 +      /* Read data from DSP */
 +      ret = wm_adsp_read_data_block(buf->dsp, mem_type, adsp_addr,
 +                                    nwords, compr->raw_buf);
 +      if (ret < 0)
 +              return ret;
 +    
 +      /* Remove the padding bytes from the data read from the DSP */
 +      for (i = 0; i < nwords; i++) {
 +              for (j = 0; j < WM_ADSP_DATA_WORD_SIZE; j++)
 +                      *pack_out++ = *pack_in++;
 +    
 +              pack_in += sizeof(*(compr->raw_buf)) - WM_ADSP_DATA_WORD_SIZE;
 +      }
 +    
 +      /* update read index to account for words read */
 +      buf->read_index += nwords;
 +      if (buf->read_index == wm_adsp_buffer_size(buf))
 +              buf->read_index = 0;
 +    
 +      ret = wm_adsp_buffer_write(buf, HOST_BUFFER_FIELD(next_read_index),
 +                                 buf->read_index);
 +      if (ret < 0)
 +              return ret;
 +    
 +      /* update avail to account for words read */
 +      buf->avail -= nwords;
 +    
 +      return nwords;
 +    }
 +    
 +    static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
 +                            char __user *buf, size_t count)
 +    {
 +      struct wm_adsp *dsp = compr->dsp;
 +      int ntotal = 0;
 +      int nwords, nbytes;
 +    
 +      adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
 +    
 +      if (!compr->buf)
 +              return -ENXIO;
 +    
 +      if (compr->buf->error)
 +              return -EIO;
 +    
 +      count /= WM_ADSP_DATA_WORD_SIZE;
 +    
 +      do {
 +              nwords = wm_adsp_buffer_capture_block(compr, count);
 +              if (nwords < 0) {
 +                      adsp_err(dsp, "Failed to capture block: %d\n", nwords);
 +                      return nwords;
 +              }
 +    
 +              nbytes = nwords * WM_ADSP_DATA_WORD_SIZE;
 +    
 +              adsp_dbg(dsp, "Read %d bytes\n", nbytes);
 +    
 +              if (copy_to_user(buf + ntotal, compr->raw_buf, nbytes)) {
 +                      adsp_err(dsp, "Failed to copy data to user: %d, %d\n",
 +                               ntotal, nbytes);
 +                      return -EFAULT;
 +              }
 +    
 +              count -= nwords;
 +              ntotal += nbytes;
 +      } while (nwords > 0 && count > 0);
 +    
 +      compr->copied_total += ntotal;
 +    
 +      return ntotal;
 +    }
 +    
 +    int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
 +                     size_t count)
 +    {
 +      struct wm_adsp_compr *compr = stream->runtime->private_data;
 +      struct wm_adsp *dsp = compr->dsp;
 +      int ret;
 +    
 +      mutex_lock(&dsp->pwr_lock);
 +    
 +      if (stream->direction == SND_COMPRESS_CAPTURE)
 +              ret = wm_adsp_compr_read(compr, buf, count);
 +      else
 +              ret = -ENOTSUPP;
 +    
 +      mutex_unlock(&dsp->pwr_lock);
 +    
 +      return ret;
 +    }
 +    EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
 +    
      MODULE_LICENSE("GPL v2");