Merge branch 'asoc-4.18' into asoc-4.19 wmadsp dep
authorMark Brown <broonie@kernel.org>
Wed, 8 Aug 2018 16:32:10 +0000 (17:32 +0100)
committerMark Brown <broonie@kernel.org>
Wed, 8 Aug 2018 16:32:10 +0000 (17:32 +0100)
1  2 
sound/soc/codecs/hdmi-codec.c
sound/soc/codecs/wm_adsp.c
sound/soc/qcom/qdsp6/q6afe-dai.c
sound/soc/qcom/qdsp6/q6asm-dai.c
sound/soc/qcom/qdsp6/q6routing.c
sound/soc/soc-pcm.c
sound/soc/soc-utils.c

index 38e4a8515709743c75befa77addca1b491c7979b,3e5b12de71bb26a286827f14acdb63c52d7e6871..d00734d31e0429bf3df8c12b0bcc118ccefb3ec2
@@@ -291,10 -291,6 +291,6 @@@ static const struct snd_soc_dapm_widge
        SND_SOC_DAPM_OUTPUT("TX"),
  };
  
- static const struct snd_soc_dapm_route hdmi_routes[] = {
-       { "TX", NULL, "Playback" },
- };
  enum {
        DAI_ID_I2S = 0,
        DAI_ID_SPDIF,
@@@ -689,9 -685,23 +685,23 @@@ static int hdmi_codec_pcm_new(struct sn
        return snd_ctl_add(rtd->card->snd_card, kctl);
  }
  
+ static int hdmi_dai_probe(struct snd_soc_dai *dai)
+ {
+       struct snd_soc_dapm_context *dapm;
+       struct snd_soc_dapm_route route = {
+               .sink = "TX",
+               .source = dai->driver->playback.stream_name,
+       };
+       dapm = snd_soc_component_get_dapm(dai->component);
+       return snd_soc_dapm_add_routes(dapm, &route, 1);
+ }
  static const struct snd_soc_dai_driver hdmi_i2s_dai = {
        .name = "i2s-hifi",
        .id = DAI_ID_I2S,
+       .probe = hdmi_dai_probe,
        .playback = {
                .stream_name = "I2S Playback",
                .channels_min = 2,
  static const struct snd_soc_dai_driver hdmi_spdif_dai = {
        .name = "spdif-hifi",
        .id = DAI_ID_SPDIF,
+       .probe = hdmi_dai_probe,
        .playback = {
                .stream_name = "SPDIF Playback",
                .channels_min = 2,
@@@ -733,8 -744,6 +744,6 @@@ static int hdmi_of_xlate_dai_id(struct 
  static const struct snd_soc_component_driver hdmi_driver = {
        .dapm_widgets           = hdmi_widgets,
        .num_dapm_widgets       = ARRAY_SIZE(hdmi_widgets),
-       .dapm_routes            = hdmi_routes,
-       .num_dapm_routes        = ARRAY_SIZE(hdmi_routes),
        .of_xlate_dai_id        = hdmi_of_xlate_dai_id,
        .idle_bias_on           = 1,
        .use_pmdown_time        = 1,
@@@ -771,7 -780,7 +780,7 @@@ static int hdmi_codec_probe(struct plat
        hcp->hcd = *hcd;
        mutex_init(&hcp->current_stream_lock);
  
 -      hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv),
 +      hcp->daidrv = devm_kcalloc(dev, dai_count, sizeof(*hcp->daidrv),
                                   GFP_KERNEL);
        if (!hcp->daidrv)
                return -ENOMEM;
index fbd0515c49d79011762de1d2dd836d6d73d02d12,08dc82770273272895fb2e32b9a92a9b7117797e..1c12c78dbcce841962d57cfa0ea6bc46970cc413
@@@ -418,7 -418,7 +418,7 @@@ static const struct wm_adsp_fw_caps ctr
        {
                .id = SND_AUDIOCODEC_BESPOKE,
                .desc = {
 -                      .max_ch = 1,
 +                      .max_ch = 8,
                        .sample_rates = { 16000 },
                        .num_sample_rates = 1,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
@@@ -627,21 -627,22 +627,21 @@@ static void wm_adsp2_init_debugfs(struc
        if (!root)
                goto err;
  
 -      if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted))
 +      if (!debugfs_create_bool("booted", 0444, root, &dsp->booted))
                goto err;
  
 -      if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running))
 +      if (!debugfs_create_bool("running", 0444, root, &dsp->running))
                goto err;
  
 -      if (!debugfs_create_x32("fw_id", S_IRUGO, root, &dsp->fw_id))
 +      if (!debugfs_create_x32("fw_id", 0444, root, &dsp->fw_id))
                goto err;
  
 -      if (!debugfs_create_x32("fw_version", S_IRUGO, root,
 -                              &dsp->fw_id_version))
 +      if (!debugfs_create_x32("fw_version", 0444, root, &dsp->fw_id_version))
                goto err;
  
        for (i = 0; i < ARRAY_SIZE(wm_adsp_debugfs_fops); ++i) {
                if (!debugfs_create_file(wm_adsp_debugfs_fops[i].name,
 -                                       S_IRUGO, root, dsp,
 +                                       0444, root, dsp,
                                         &wm_adsp_debugfs_fops[i].fops))
                        goto err;
        }
@@@ -684,8 -685,8 +684,8 @@@ static inline void wm_adsp_debugfs_clea
  }
  #endif
  
 -static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
 -                        struct snd_ctl_elem_value *ucontrol)
 +int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
 +                 struct snd_ctl_elem_value *ucontrol)
  {
        struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
  
        return 0;
  }
 +EXPORT_SYMBOL_GPL(wm_adsp_fw_get);
  
 -static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
 -                        struct snd_ctl_elem_value *ucontrol)
 +int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
 +                 struct snd_ctl_elem_value *ucontrol)
  {
        struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
  
        return ret;
  }
 +EXPORT_SYMBOL_GPL(wm_adsp_fw_put);
  
 -static const struct soc_enum wm_adsp_fw_enum[] = {
 +const struct soc_enum wm_adsp_fw_enum[] = {
        SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
        SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
        SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
        SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
        SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
  };
 -
 -const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
 -      SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
 -                   wm_adsp_fw_get, wm_adsp_fw_put),
 -      SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
 -                   wm_adsp_fw_get, wm_adsp_fw_put),
 -      SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
 -                   wm_adsp_fw_get, wm_adsp_fw_put),
 -      SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
 -                   wm_adsp_fw_get, wm_adsp_fw_put),
 -      SOC_ENUM_EXT("DSP5 Firmware", wm_adsp_fw_enum[4],
 -                   wm_adsp_fw_get, wm_adsp_fw_put),
 -      SOC_ENUM_EXT("DSP6 Firmware", wm_adsp_fw_enum[5],
 -                   wm_adsp_fw_get, wm_adsp_fw_put),
 -      SOC_ENUM_EXT("DSP7 Firmware", wm_adsp_fw_enum[6],
 -                   wm_adsp_fw_get, wm_adsp_fw_put),
 -};
 -EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
 +EXPORT_SYMBOL_GPL(wm_adsp_fw_enum);
  
  static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
                                                        int type)
@@@ -1328,9 -1344,6 +1328,9 @@@ static int wm_adsp_create_control(struc
                        int avail = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - ret - 2;
                        int skip = 0;
  
 +                      if (dsp->component->name_prefix)
 +                              avail -= strlen(dsp->component->name_prefix) + 1;
 +
                        if (subname_len > avail)
                                skip = subname_len - avail;
  
@@@ -1592,15 -1605,6 +1592,15 @@@ static int wm_adsp_parse_coeff(struct w
                        if (ret)
                                return -EINVAL;
                        break;
 +              case WMFW_CTL_TYPE_HOST_BUFFER:
 +                      ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
 +                                              WMFW_CTL_FLAG_SYS |
 +                                              WMFW_CTL_FLAG_VOLATILE |
 +                                              WMFW_CTL_FLAG_READABLE,
 +                                              0);
 +                      if (ret)
 +                              return -EINVAL;
 +                      break;
                default:
                        adsp_err(dsp, "Unknown control type: %d\n",
                                 coeff_blk.ctl_type);
@@@ -1868,11 -1872,9 +1868,11 @@@ static void wm_adsp_ctl_fixup_base(stru
  }
  
  static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
 +                             const struct wm_adsp_region *mem,
                               unsigned int pos, unsigned int len)
  {
        void *alg;
 +      unsigned int reg;
        int ret;
        __be32 val;
  
        }
  
        /* Read the terminator first to validate the length */
 -      ret = regmap_raw_read(dsp->regmap, pos + len, &val, sizeof(val));
 +      reg = wm_adsp_region_to_reg(mem, pos + len);
 +
 +      ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
        if (ret != 0) {
                adsp_err(dsp, "Failed to read algorithm list end: %d\n",
                        ret);
  
        if (be32_to_cpu(val) != 0xbedead)
                adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbedead\n",
 -                        pos + len, be32_to_cpu(val));
 +                        reg, be32_to_cpu(val));
  
 -      alg = kzalloc(len * 2, GFP_KERNEL | GFP_DMA);
 +      /* Convert length from DSP words to bytes */
 +      len *= sizeof(u32);
 +
 +      alg = kzalloc(len, GFP_KERNEL | GFP_DMA);
        if (!alg)
                return ERR_PTR(-ENOMEM);
  
 -      ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
 +      reg = wm_adsp_region_to_reg(mem, pos);
 +
 +      ret = regmap_raw_read(dsp->regmap, reg, alg, len);
        if (ret != 0) {
                adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
                kfree(alg);
@@@ -2008,11 -2003,10 +2008,11 @@@ static int wm_adsp1_setup_algs(struct w
        if (IS_ERR(alg_region))
                return PTR_ERR(alg_region);
  
 -      pos = sizeof(adsp1_id) / 2;
 -      len = (sizeof(*adsp1_alg) * n_algs) / 2;
 +      /* Calculate offset and length in DSP words */
 +      pos = sizeof(adsp1_id) / sizeof(u32);
 +      len = (sizeof(*adsp1_alg) * n_algs) / sizeof(u32);
  
 -      adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
 +      adsp1_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
        if (IS_ERR(adsp1_alg))
                return PTR_ERR(adsp1_alg);
  
@@@ -2120,11 -2114,10 +2120,11 @@@ static int wm_adsp2_setup_algs(struct w
        if (IS_ERR(alg_region))
                return PTR_ERR(alg_region);
  
 -      pos = sizeof(adsp2_id) / 2;
 -      len = (sizeof(*adsp2_alg) * n_algs) / 2;
 +      /* Calculate offset and length in DSP words */
 +      pos = sizeof(adsp2_id) / sizeof(u32);
 +      len = (sizeof(*adsp2_alg) * n_algs) / sizeof(u32);
  
 -      adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem->base + pos, len);
 +      adsp2_alg = wm_adsp_read_algs(dsp, n_algs, mem, pos, len);
        if (IS_ERR(adsp2_alg))
                return PTR_ERR(adsp2_alg);
  
@@@ -2650,7 -2643,10 +2650,10 @@@ int wm_adsp2_preloader_get(struct snd_k
                           struct snd_ctl_elem_value *ucontrol)
  {
        struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-       struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
+       struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct wm_adsp *dsp = &dsps[mc->shift - 1];
  
        ucontrol->value.integer.value[0] = dsp->preloaded;
  
@@@ -2662,10 -2658,11 +2665,11 @@@ int wm_adsp2_preloader_put(struct snd_k
                           struct snd_ctl_elem_value *ucontrol)
  {
        struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
-       struct wm_adsp *dsp = snd_soc_component_get_drvdata(component);
+       struct wm_adsp *dsps = snd_soc_component_get_drvdata(component);
        struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
+       struct wm_adsp *dsp = &dsps[mc->shift - 1];
        char preload[32];
  
        snprintf(preload, ARRAY_SIZE(preload), "DSP%u Preload", mc->shift);
  
        snd_soc_dapm_sync(dapm);
  
+       flush_work(&dsp->boot_work);
        return 0;
  }
  EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
@@@ -2869,7 -2868,9 +2875,7 @@@ int wm_adsp2_component_probe(struct wm_
  
        dsp->component = component;
  
 -      return snd_soc_add_component_controls(component,
 -                                        &wm_adsp_fw_controls[dsp->num - 1],
 -                                        1);
 +      return 0;
  }
  EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
  
@@@ -3192,7 -3193,7 +3198,7 @@@ static inline int wm_adsp_buffer_write(
                                       buf->host_buf_ptr + field_offset, data);
  }
  
 -static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
 +static int wm_adsp_legacy_host_buf_addr(struct wm_adsp_compr_buf *buf)
  {
        struct wm_adsp_alg_region *alg_region;
        struct wm_adsp *dsp = buf->dsp;
        return 0;
  }
  
 +static struct wm_coeff_ctl *
 +wm_adsp_find_host_buffer_ctrl(struct wm_adsp_compr_buf *buf)
 +{
 +      struct wm_adsp *dsp = buf->dsp;
 +      struct wm_coeff_ctl *ctl;
 +
 +      list_for_each_entry(ctl, &dsp->ctl_list, list) {
 +              if (ctl->type != WMFW_CTL_TYPE_HOST_BUFFER)
 +                      continue;
 +
 +              if (!ctl->enabled)
 +                      continue;
 +
 +              return ctl;
 +      }
 +
 +      return NULL;
 +}
 +
 +static int wm_adsp_buffer_locate(struct wm_adsp_compr_buf *buf)
 +{
 +      struct wm_adsp *dsp = buf->dsp;
 +      struct wm_coeff_ctl *ctl;
 +      unsigned int reg;
 +      u32 val;
 +      int i, ret;
 +
 +      ctl = wm_adsp_find_host_buffer_ctrl(buf);
 +      if (!ctl)
 +              return wm_adsp_legacy_host_buf_addr(buf);
 +
 +      ret = wm_coeff_base_reg(ctl, &reg);
 +      if (ret)
 +              return ret;
 +
 +      for (i = 0; i < 5; ++i) {
 +              ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
 +              if (ret < 0)
 +                      return ret;
 +
 +              if (val)
 +                      break;
 +
 +              usleep_range(1000, 2000);
 +      }
 +
 +      if (!val)
 +              return -EIO;
 +
 +      buf->host_buf_ptr = be32_to_cpu(val);
 +      adsp_dbg(dsp, "host_buf_ptr=%x\n", buf->host_buf_ptr);
 +
 +      return 0;
 +}
 +
  static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf)
  {
        const struct wm_adsp_fw_caps *caps = wm_adsp_fw[buf->dsp->fw].caps;
index 139d24be3fb99bedf9d062476045ad8c6be5e817,9ba95956ada8b6b2dc183d2562fb96dc8ca99e16..60ff4a2d35774eebdaced12f36a275ce2738f5f2
@@@ -4,6 -4,7 +4,6 @@@
  
  #include <linux/err.h>
  #include <linux/init.h>
 -#include <linux/component.h>
  #include <linux/module.h>
  #include <linux/device.h>
  #include <linux/platform_device.h>
@@@ -80,6 -81,7 +80,6 @@@ static int q6slim_hw_params(struct snd_
        struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
        struct q6afe_slim_cfg *slim = &dai_data->port_config[dai->id].slim;
  
 -      slim->num_channels = params_channels(params);
        slim->sample_rate = params_rate(params);
  
        switch (params_format(params)) {
@@@ -313,6 -315,9 +313,9 @@@ static void q6afe_dai_shutdown(struct s
        struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
        int rc;
  
+       if (!dai_data->is_port_started[dai->id])
+               return;
        rc = q6afe_port_stop(dai_data->port[dai->id]);
        if (rc < 0)
                dev_err(dai->dev, "fail to close AFE port (%d)\n", rc);
@@@ -380,31 -385,23 +383,31 @@@ static int q6slim_set_channel_map(struc
        struct q6afe_port_config *pcfg = &dai_data->port_config[dai->id];
        int i;
  
 -      if (!rx_slot) {
 -              pr_err("%s: rx slot not found\n", __func__);
 -              return -EINVAL;
 -      }
 +      if (dai->id & 0x1) {
 +              /* TX */
 +              if (!tx_slot) {
 +                      pr_err("%s: tx slot not found\n", __func__);
 +                      return -EINVAL;
 +              }
  
 -      for (i = 0; i < rx_num; i++) {
 -              pcfg->slim.ch_mapping[i] =   rx_slot[i];
 -              pr_debug("%s: find number of channels[%d] ch[%d]\n",
 -                     __func__, i, rx_slot[i]);
 -      }
 +              for (i = 0; i < tx_num; i++)
 +                      pcfg->slim.ch_mapping[i] = tx_slot[i];
  
 -      pcfg->slim.num_channels = rx_num;
 +              pcfg->slim.num_channels = tx_num;
  
 -      pr_debug("%s: SLIMBUS_%d_RX cnt[%d] ch[%d %d]\n", __func__,
 -              (dai->id - SLIMBUS_0_RX) / 2, rx_num,
 -              pcfg->slim.ch_mapping[0],
 -              pcfg->slim.ch_mapping[1]);
 +
 +      } else {
 +              if (!rx_slot) {
 +                      pr_err("%s: rx slot not found\n", __func__);
 +                      return -EINVAL;
 +              }
 +
 +              for (i = 0; i < rx_num; i++)
 +                      pcfg->slim.ch_mapping[i] =   rx_slot[i];
 +
 +              pcfg->slim.num_channels = rx_num;
 +
 +      }
  
        return 0;
  }
@@@ -449,14 -446,6 +452,14 @@@ static const struct snd_soc_dapm_route 
        {"Slimbus5 Playback", NULL, "SLIMBUS_5_RX"},
        {"Slimbus6 Playback", NULL, "SLIMBUS_6_RX"},
  
 +      {"SLIMBUS_0_TX", NULL, "Slimbus Capture"},
 +      {"SLIMBUS_1_TX", NULL, "Slimbus1 Capture"},
 +      {"SLIMBUS_2_TX", NULL, "Slimbus2 Capture"},
 +      {"SLIMBUS_3_TX", NULL, "Slimbus3 Capture"},
 +      {"SLIMBUS_4_TX", NULL, "Slimbus4 Capture"},
 +      {"SLIMBUS_5_TX", NULL, "Slimbus5 Capture"},
 +      {"SLIMBUS_6_TX", NULL, "Slimbus6 Capture"},
 +
        {"Primary MI2S Playback", NULL, "PRI_MI2S_RX"},
        {"Secondary MI2S Playback", NULL, "SEC_MI2S_RX"},
        {"Tertiary MI2S Playback", NULL, "TERT_MI2S_RX"},
@@@ -650,24 -639,6 +653,24 @@@ static struct snd_soc_dai_driver q6afe_
                        .rate_min = 8000,
                        .rate_max = 192000,
                },
 +      }, {
 +              .name = "SLIMBUS_0_TX",
 +              .ops = &q6slim_ops,
 +              .id = SLIMBUS_0_TX,
 +              .probe = msm_dai_q6_dai_probe,
 +              .remove = msm_dai_q6_dai_remove,
 +              .capture = {
 +                      .stream_name = "Slimbus Capture",
 +                      .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
 +                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
 +                               SNDRV_PCM_RATE_192000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE |
 +                                 SNDRV_PCM_FMTBIT_S24_LE,
 +                      .channels_min = 1,
 +                      .channels_max = 8,
 +                      .rate_min = 8000,
 +                      .rate_max = 192000,
 +              },
        }, {
                .playback = {
                        .stream_name = "Slimbus1 Playback",
                .id = SLIMBUS_1_RX,
                .probe = msm_dai_q6_dai_probe,
                .remove = msm_dai_q6_dai_remove,
 +      }, {
 +              .name = "SLIMBUS_1_TX",
 +              .ops = &q6slim_ops,
 +              .id = SLIMBUS_1_TX,
 +              .probe = msm_dai_q6_dai_probe,
 +              .remove = msm_dai_q6_dai_remove,
 +              .capture = {
 +                      .stream_name = "Slimbus1 Capture",
 +                      .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
 +                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
 +                               SNDRV_PCM_RATE_192000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE |
 +                                 SNDRV_PCM_FMTBIT_S24_LE,
 +                      .channels_min = 1,
 +                      .channels_max = 8,
 +                      .rate_min = 8000,
 +                      .rate_max = 192000,
 +              },
        }, {
                .playback = {
                        .stream_name = "Slimbus2 Playback",
                .id = SLIMBUS_2_RX,
                .probe = msm_dai_q6_dai_probe,
                .remove = msm_dai_q6_dai_remove,
 +
 +      }, {
 +              .name = "SLIMBUS_2_TX",
 +              .ops = &q6slim_ops,
 +              .id = SLIMBUS_2_TX,
 +              .probe = msm_dai_q6_dai_probe,
 +              .remove = msm_dai_q6_dai_remove,
 +              .capture = {
 +                      .stream_name = "Slimbus2 Capture",
 +                      .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
 +                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
 +                               SNDRV_PCM_RATE_192000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE |
 +                                 SNDRV_PCM_FMTBIT_S24_LE,
 +                      .channels_min = 1,
 +                      .channels_max = 8,
 +                      .rate_min = 8000,
 +                      .rate_max = 192000,
 +              },
        }, {
                .playback = {
                        .stream_name = "Slimbus3 Playback",
                .id = SLIMBUS_3_RX,
                .probe = msm_dai_q6_dai_probe,
                .remove = msm_dai_q6_dai_remove,
 +
 +      }, {
 +              .name = "SLIMBUS_3_TX",
 +              .ops = &q6slim_ops,
 +              .id = SLIMBUS_3_TX,
 +              .probe = msm_dai_q6_dai_probe,
 +              .remove = msm_dai_q6_dai_remove,
 +              .capture = {
 +                      .stream_name = "Slimbus3 Capture",
 +                      .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
 +                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
 +                               SNDRV_PCM_RATE_192000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE |
 +                                 SNDRV_PCM_FMTBIT_S24_LE,
 +                      .channels_min = 1,
 +                      .channels_max = 8,
 +                      .rate_min = 8000,
 +                      .rate_max = 192000,
 +              },
        }, {
                .playback = {
                        .stream_name = "Slimbus4 Playback",
                .id = SLIMBUS_4_RX,
                .probe = msm_dai_q6_dai_probe,
                .remove = msm_dai_q6_dai_remove,
 +
 +      }, {
 +              .name = "SLIMBUS_4_TX",
 +              .ops = &q6slim_ops,
 +              .id = SLIMBUS_4_TX,
 +              .probe = msm_dai_q6_dai_probe,
 +              .remove = msm_dai_q6_dai_remove,
 +              .capture = {
 +                      .stream_name = "Slimbus4 Capture",
 +                      .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
 +                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
 +                               SNDRV_PCM_RATE_192000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE |
 +                                 SNDRV_PCM_FMTBIT_S24_LE,
 +                      .channels_min = 1,
 +                      .channels_max = 8,
 +                      .rate_min = 8000,
 +                      .rate_max = 192000,
 +              },
        }, {
                .playback = {
                        .stream_name = "Slimbus5 Playback",
                .id = SLIMBUS_5_RX,
                .probe = msm_dai_q6_dai_probe,
                .remove = msm_dai_q6_dai_remove,
 +
 +      }, {
 +              .name = "SLIMBUS_5_TX",
 +              .ops = &q6slim_ops,
 +              .id = SLIMBUS_5_TX,
 +              .probe = msm_dai_q6_dai_probe,
 +              .remove = msm_dai_q6_dai_remove,
 +              .capture = {
 +                      .stream_name = "Slimbus5 Capture",
 +                      .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
 +                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
 +                               SNDRV_PCM_RATE_192000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE |
 +                                 SNDRV_PCM_FMTBIT_S24_LE,
 +                      .channels_min = 1,
 +                      .channels_max = 8,
 +                      .rate_min = 8000,
 +                      .rate_max = 192000,
 +              },
        }, {
                .playback = {
                        .stream_name = "Slimbus6 Playback",
                .id = SLIMBUS_6_RX,
                .probe = msm_dai_q6_dai_probe,
                .remove = msm_dai_q6_dai_remove,
 +
 +      }, {
 +              .name = "SLIMBUS_6_TX",
 +              .ops = &q6slim_ops,
 +              .id = SLIMBUS_6_TX,
 +              .probe = msm_dai_q6_dai_probe,
 +              .remove = msm_dai_q6_dai_remove,
 +              .capture = {
 +                      .stream_name = "Slimbus6 Capture",
 +                      .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
 +                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
 +                               SNDRV_PCM_RATE_192000,
 +                      .formats = SNDRV_PCM_FMTBIT_S16_LE |
 +                                 SNDRV_PCM_FMTBIT_S24_LE,
 +                      .channels_min = 1,
 +                      .channels_max = 8,
 +                      .rate_min = 8000,
 +                      .rate_max = 192000,
 +              },
        }, {
                .playback = {
                        .stream_name = "Primary MI2S Playback",
@@@ -1117,13 -975,6 +1120,13 @@@ static const struct snd_soc_dapm_widge
        SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback", 0, 0, 0, 0),
        SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0),
        SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0, 0),
 +      SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
 +      SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0),
 +      SND_SOC_DAPM_AIF_IN("SLIMBUS_2_TX", "Slimbus2 Capture", 0, 0, 0, 0),
 +      SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0),
 +      SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture", 0, 0, 0, 0),
 +      SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0),
 +      SND_SOC_DAPM_AIF_IN("SLIMBUS_6_TX", "Slimbus6 Capture", 0, 0, 0, 0),
        SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback",
                                                0, 0, 0, 0),
        SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture",
@@@ -1332,7 -1183,7 +1335,7 @@@ static void of_q6afe_parse_dai_data(str
                int id, i, num_lines;
  
                ret = of_property_read_u32(node, "reg", &id);
-               if (ret || id > AFE_PORT_MAX) {
+               if (ret || id < 0 || id >= AFE_PORT_MAX) {
                        dev_err(dev, "valid dai id not found:%d\n", ret);
                        continue;
                }
        }
  }
  
 -static int q6afe_dai_bind(struct device *dev, struct device *master, void *data)
 +static int q6afe_dai_dev_probe(struct platform_device *pdev)
  {
        struct q6afe_dai_data *dai_data;
 +      struct device *dev = &pdev->dev;
  
 -      dai_data = kzalloc(sizeof(*dai_data), GFP_KERNEL);
 +      dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL);
        if (!dai_data)
                return -ENOMEM;
  
  
        of_q6afe_parse_dai_data(dev, dai_data);
  
 -      return snd_soc_register_component(dev, &q6afe_dai_component,
 +      return devm_snd_soc_register_component(dev, &q6afe_dai_component,
                                          q6afe_dais, ARRAY_SIZE(q6afe_dais));
  }
  
 -static void q6afe_dai_unbind(struct device *dev, struct device *master,
 -                           void *data)
 -{
 -      struct q6afe_dai_data *dai_data = dev_get_drvdata(dev);
 -
 -      snd_soc_unregister_component(dev);
 -      kfree(dai_data);
 -}
 -
 -static const struct component_ops q6afe_dai_comp_ops = {
 -      .bind   = q6afe_dai_bind,
 -      .unbind = q6afe_dai_unbind,
 +static const struct of_device_id q6afe_dai_device_id[] = {
 +      { .compatible = "qcom,q6afe-dais" },
 +      {},
  };
 -
 -static int q6afe_dai_dev_probe(struct platform_device *pdev)
 -{
 -      return component_add(&pdev->dev, &q6afe_dai_comp_ops);
 -}
 -
 -static int q6afe_dai_dev_remove(struct platform_device *pdev)
 -{
 -      component_del(&pdev->dev, &q6afe_dai_comp_ops);
 -      return 0;
 -}
 +MODULE_DEVICE_TABLE(of, q6afe_dai_device_id);
  
  static struct platform_driver q6afe_dai_platform_driver = {
        .driver = {
                .name = "q6afe-dai",
 +              .of_match_table = of_match_ptr(q6afe_dai_device_id),
        },
        .probe = q6afe_dai_dev_probe,
 -      .remove = q6afe_dai_dev_remove,
  };
  module_platform_driver(q6afe_dai_platform_driver);
  
index acf96c6549fc4f2e661eaadcdf8e6282e1dd6a8f,360936703b3d5357884416304e3b9a7ef1c61a7e..9db9a2944ef26d23bb1bd95b33a1af6ac2a1517c
@@@ -7,6 -7,7 +7,6 @@@
  #include <linux/module.h>
  #include <linux/platform_device.h>
  #include <linux/slab.h>
 -#include <linux/component.h>
  #include <sound/soc.h>
  #include <sound/soc.h>
  #include <sound/soc-dapm.h>
@@@ -389,7 -390,9 +389,9 @@@ static int q6asm_dai_close(struct snd_p
        struct q6asm_dai_rtd *prtd = runtime->private_data;
  
        if (prtd->audio_client) {
-               q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+               if (prtd->state)
+                       q6asm_cmd(prtd->audio_client, CMD_CLOSE);
                q6asm_unmap_memory_regions(substream->stream,
                                           prtd->audio_client);
                q6asm_audio_client_free(prtd->audio_client);
@@@ -560,15 -563,14 +562,15 @@@ static struct snd_soc_dai_driver q6asm_
        Q6ASM_FEDAI_DRIVER(8),
  };
  
 -static int q6asm_dai_bind(struct device *dev, struct device *master, void *data)
 +static int q6asm_dai_probe(struct platform_device *pdev)
  {
 +      struct device *dev = &pdev->dev;
        struct device_node *node = dev->of_node;
        struct of_phandle_args args;
        struct q6asm_dai_data *pdata;
        int rc;
  
 -      pdata = kzalloc(sizeof(struct q6asm_dai_data), GFP_KERNEL);
 +      pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
                return -ENOMEM;
  
  
        dev_set_drvdata(dev, pdata);
  
 -      return snd_soc_register_component(dev, &q6asm_fe_dai_component,
 +      return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component,
                                        q6asm_fe_dais,
                                        ARRAY_SIZE(q6asm_fe_dais));
  }
 -static void q6asm_dai_unbind(struct device *dev, struct device *master,
 -                           void *data)
 -{
 -      struct q6asm_dai_data *pdata = dev_get_drvdata(dev);
 -
 -      snd_soc_unregister_component(dev);
 -
 -      kfree(pdata);
  
 -}
 -
 -static const struct component_ops q6asm_dai_comp_ops = {
 -      .bind   = q6asm_dai_bind,
 -      .unbind = q6asm_dai_unbind,
 +static const struct of_device_id q6asm_dai_device_id[] = {
 +      { .compatible = "qcom,q6asm-dais" },
 +      {},
  };
 -
 -static int q6asm_dai_probe(struct platform_device *pdev)
 -{
 -      return component_add(&pdev->dev, &q6asm_dai_comp_ops);
 -}
 -
 -static int q6asm_dai_dev_remove(struct platform_device *pdev)
 -{
 -      component_del(&pdev->dev, &q6asm_dai_comp_ops);
 -      return 0;
 -}
 +MODULE_DEVICE_TABLE(of, q6asm_dai_device_id);
  
  static struct platform_driver q6asm_dai_platform_driver = {
        .driver = {
                .name = "q6asm-dai",
 +              .of_match_table = of_match_ptr(q6asm_dai_device_id),
        },
        .probe = q6asm_dai_probe,
 -      .remove = q6asm_dai_dev_remove,
  };
  module_platform_driver(q6asm_dai_platform_driver);
  
index 1d33b00e5b44e5d1e30019e7364be02066d492b2,7a19d627840681e4e5c7c2154ad5f13963e73589..dc94c5c53788b044b183f2406df7a5d9234cc5fc
@@@ -8,6 -8,7 +8,6 @@@
  #include <linux/platform_device.h>
  #include <linux/of_platform.h>
  #include <linux/bitops.h>
 -#include <linux/component.h>
  #include <linux/mutex.h>
  #include <linux/of_device.h>
  #include <linux/slab.h>
        { mix_name, "SEC_MI2S_TX", "SEC_MI2S_TX" },     \
        { mix_name, "QUAT_MI2S_TX", "QUAT_MI2S_TX" },   \
        { mix_name, "TERT_MI2S_TX", "TERT_MI2S_TX" },           \
 +      { mix_name, "SLIMBUS_0_TX", "SLIMBUS_0_TX" },           \
 +      { mix_name, "SLIMBUS_1_TX", "SLIMBUS_1_TX" },           \
 +      { mix_name, "SLIMBUS_2_TX", "SLIMBUS_2_TX" },           \
 +      { mix_name, "SLIMBUS_3_TX", "SLIMBUS_3_TX" },           \
 +      { mix_name, "SLIMBUS_4_TX", "SLIMBUS_4_TX" },           \
 +      { mix_name, "SLIMBUS_5_TX", "SLIMBUS_5_TX" },           \
 +      { mix_name, "SLIMBUS_6_TX", "SLIMBUS_6_TX" },           \
        { mix_name, "PRIMARY_TDM_TX_0", "PRIMARY_TDM_TX_0"},    \
        { mix_name, "PRIMARY_TDM_TX_1", "PRIMARY_TDM_TX_1"},    \
        { mix_name, "PRIMARY_TDM_TX_2", "PRIMARY_TDM_TX_2"},    \
        SOC_SINGLE_EXT("QUAT_MI2S_TX", QUATERNARY_MI2S_TX,              \
                id, 1, 0, msm_routing_get_audio_mixer,                  \
                msm_routing_put_audio_mixer),                           \
 +      SOC_SINGLE_EXT("SLIMBUS_0_TX", SLIMBUS_0_TX,                    \
 +              id, 1, 0, msm_routing_get_audio_mixer,                  \
 +              msm_routing_put_audio_mixer),                           \
 +      SOC_SINGLE_EXT("SLIMBUS_1_TX", SLIMBUS_1_TX,                    \
 +              id, 1, 0, msm_routing_get_audio_mixer,                  \
 +              msm_routing_put_audio_mixer),                           \
 +      SOC_SINGLE_EXT("SLIMBUS_2_TX", SLIMBUS_2_TX,                    \
 +              id, 1, 0, msm_routing_get_audio_mixer,                  \
 +              msm_routing_put_audio_mixer),                           \
 +      SOC_SINGLE_EXT("SLIMBUS_3_TX", SLIMBUS_3_TX,                    \
 +              id, 1, 0, msm_routing_get_audio_mixer,                  \
 +              msm_routing_put_audio_mixer),                           \
 +      SOC_SINGLE_EXT("SLIMBUS_4_TX", SLIMBUS_4_TX,                    \
 +              id, 1, 0, msm_routing_get_audio_mixer,                  \
 +              msm_routing_put_audio_mixer),                           \
 +      SOC_SINGLE_EXT("SLIMBUS_5_TX", SLIMBUS_5_TX,                    \
 +              id, 1, 0, msm_routing_get_audio_mixer,                  \
 +              msm_routing_put_audio_mixer),                           \
 +      SOC_SINGLE_EXT("SLIMBUS_6_TX", SLIMBUS_6_TX,                    \
 +              id, 1, 0, msm_routing_get_audio_mixer,                  \
 +              msm_routing_put_audio_mixer),                           \
        SOC_SINGLE_EXT("PRIMARY_TDM_TX_0", PRIMARY_TDM_TX_0,            \
                id, 1, 0, msm_routing_get_audio_mixer,                  \
                msm_routing_put_audio_mixer),                           \
@@@ -337,7 -310,7 +337,7 @@@ int q6routing_stream_open(int fedai_id
                              session->channels, topology, perf_mode,
                              session->bits_per_sample, 0, 0);
  
 -      if (!copp) {
 +      if (IS_ERR_OR_NULL(copp)) {
                mutex_unlock(&routing_data->lock);
                return -EINVAL;
        }
@@@ -926,7 -899,7 +926,7 @@@ static int routing_hw_params(struct snd
        else
                path_type = ADM_PATH_LIVE_REC;
  
-       if (be_id > AFE_MAX_PORTS)
+       if (be_id >= AFE_MAX_PORTS)
                return -EINVAL;
  
        session = &data->port_data[be_id];
@@@ -976,10 -949,9 +976,10 @@@ static const struct snd_soc_component_d
        .num_dapm_routes = ARRAY_SIZE(intercon),
  };
  
 -static int q6routing_dai_bind(struct device *dev, struct device *master,
 -                            void *data)
 +static int q6pcm_routing_probe(struct platform_device *pdev)
  {
 +      struct device *dev = &pdev->dev;
 +
        routing_data = kzalloc(sizeof(*routing_data), GFP_KERNEL);
        if (!routing_data)
                return -ENOMEM;
        mutex_init(&routing_data->lock);
        dev_set_drvdata(dev, routing_data);
  
 -      return snd_soc_register_component(dev, &msm_soc_routing_component,
 +      return devm_snd_soc_register_component(dev, &msm_soc_routing_component,
                                          NULL, 0);
  }
  
 -static void q6routing_dai_unbind(struct device *dev, struct device *master,
 -                               void *d)
 +static int q6pcm_routing_remove(struct platform_device *pdev)
  {
 -      struct msm_routing_data *data = dev_get_drvdata(dev);
 -
 -      snd_soc_unregister_component(dev);
 -
 -      kfree(data);
 -
 +      kfree(routing_data);
        routing_data = NULL;
 -}
 -
 -static const struct component_ops q6routing_dai_comp_ops = {
 -      .bind   = q6routing_dai_bind,
 -      .unbind = q6routing_dai_unbind,
 -};
  
 -static int q6pcm_routing_probe(struct platform_device *pdev)
 -{
 -      return component_add(&pdev->dev, &q6routing_dai_comp_ops);
 -}
 -
 -static int q6pcm_routing_remove(struct platform_device *pdev)
 -{
 -      component_del(&pdev->dev, &q6routing_dai_comp_ops);
        return 0;
  }
  
 +static const struct of_device_id q6pcm_routing_device_id[] = {
 +      { .compatible = "qcom,q6adm-routing" },
 +      {},
 +};
 +MODULE_DEVICE_TABLE(of, q6pcm_routing_device_id);
 +
  static struct platform_driver q6pcm_routing_platform_driver = {
        .driver = {
                .name = "q6routing",
 +              .of_match_table = of_match_ptr(q6pcm_routing_device_id),
        },
        .probe = q6pcm_routing_probe,
        .remove = q6pcm_routing_remove,
diff --combined sound/soc/soc-pcm.c
index 9833e53754cb2120caf63f147ba661532000fb19,5feae9666822204344809c2c93e2d4796231b229..e8b98bfd4cf13bcb56d118a4c87a6e08ce5139e2
@@@ -1,14 -1,20 +1,14 @@@
 -/*
 - * soc-pcm.c  --  ALSA SoC PCM
 - *
 - * Copyright 2005 Wolfson Microelectronics PLC.
 - * Copyright 2005 Openedhand Ltd.
 - * Copyright (C) 2010 Slimlogic Ltd.
 - * Copyright (C) 2010 Texas Instruments Inc.
 - *
 - * Authors: Liam Girdwood <lrg@ti.com>
 - *          Mark Brown <broonie@opensource.wolfsonmicro.com>
 - *
 - *  This program is free software; you can redistribute  it and/or modify it
 - *  under  the terms of  the GNU General  Public License as published by the
 - *  Free Software Foundation;  either version 2 of the  License, or (at your
 - *  option) any later version.
 - *
 - */
 +// SPDX-License-Identifier: GPL-2.0+
 +//
 +// soc-pcm.c  --  ALSA SoC PCM
 +//
 +// Copyright 2005 Wolfson Microelectronics PLC.
 +// Copyright 2005 Openedhand Ltd.
 +// Copyright (C) 2010 Slimlogic Ltd.
 +// Copyright (C) 2010 Texas Instruments Inc.
 +//
 +// Authors: Liam Girdwood <lrg@ti.com>
 +//          Mark Brown <broonie@opensource.wolfsonmicro.com>
  
  #include <linux/kernel.h>
  #include <linux/init.h>
@@@ -442,29 -448,6 +442,29 @@@ static void soc_pcm_init_runtime_hw(str
        hw->rate_max = min_not_zero(hw->rate_max, rate_max);
  }
  
 +static int soc_pcm_components_close(struct snd_pcm_substream *substream,
 +                                  struct snd_soc_component *last)
 +{
 +      struct snd_soc_pcm_runtime *rtd = substream->private_data;
 +      struct snd_soc_rtdcom_list *rtdcom;
 +      struct snd_soc_component *component;
 +
 +      for_each_rtdcom(rtd, rtdcom) {
 +              component = rtdcom->component;
 +
 +              if (component == last)
 +                      break;
 +
 +              if (!component->driver->ops ||
 +                  !component->driver->ops->close)
 +                      continue;
 +
 +              component->driver->ops->close(substream);
 +      }
 +
 +      return 0;
 +}
 +
  /*
   * Called by ALSA when a PCM substream is opened, the runtime->hw record is
   * then initialized and any private data can be allocated. This also calls
@@@ -479,7 -462,7 +479,7 @@@ static int soc_pcm_open(struct snd_pcm_
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai;
        const char *codec_dai_name = "multicodec";
 -      int i, ret = 0, __ret;
 +      int i, ret = 0;
  
        pinctrl_pm_select_default_state(cpu_dai->dev);
        for (i = 0; i < rtd->num_codecs; i++)
                }
        }
  
 -      ret = 0;
        for_each_rtdcom(rtd, rtdcom) {
                component = rtdcom->component;
  
                    !component->driver->ops->open)
                        continue;
  
 -              __ret = component->driver->ops->open(substream);
 -              if (__ret < 0) {
 +              ret = component->driver->ops->open(substream);
 +              if (ret < 0) {
                        dev_err(component->dev,
                                "ASoC: can't open component %s: %d\n",
 -                              component->name, __ret);
 -                      ret = __ret;
 +                              component->name, ret);
 +                      goto component_err;
                }
        }
 -      if (ret < 0)
 -              goto component_err;
 +      component = NULL;
  
        for (i = 0; i < rtd->num_codecs; i++) {
                codec_dai = rtd->codec_dais[i];
@@@ -627,7 -612,15 +627,7 @@@ codec_dai_err
        }
  
  component_err:
 -      for_each_rtdcom(rtd, rtdcom) {
 -              component = rtdcom->component;
 -
 -              if (!component->driver->ops ||
 -                  !component->driver->ops->close)
 -                      continue;
 -
 -              component->driver->ops->close(substream);
 -      }
 +      soc_pcm_components_close(substream, component);
  
        if (cpu_dai->driver->ops->shutdown)
                cpu_dai->driver->ops->shutdown(substream, cpu_dai);
@@@ -721,7 -714,15 +721,7 @@@ static int soc_pcm_close(struct snd_pcm
        if (rtd->dai_link->ops->shutdown)
                rtd->dai_link->ops->shutdown(substream);
  
 -      for_each_rtdcom(rtd, rtdcom) {
 -              component = rtdcom->component;
 -
 -              if (!component->driver->ops ||
 -                  !component->driver->ops->close)
 -                      continue;
 -
 -              component->driver->ops->close(substream);
 -      }
 +      soc_pcm_components_close(substream, NULL);
  
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                if (snd_soc_runtime_ignore_pmdown_time(rtd)) {
@@@ -859,20 -860,8 +859,20 @@@ int soc_dai_hw_params(struct snd_pcm_su
                      struct snd_pcm_hw_params *params,
                      struct snd_soc_dai *dai)
  {
 +      struct snd_soc_pcm_runtime *rtd = substream->private_data;
        int ret;
  
 +      /* perform any topology hw_params fixups before DAI  */
 +      if (rtd->dai_link->be_hw_params_fixup) {
 +              ret = rtd->dai_link->be_hw_params_fixup(rtd, params);
 +              if (ret < 0) {
 +                      dev_err(rtd->dev,
 +                              "ASoC: hw_params topology fixup failed %d\n",
 +                              ret);
 +                      return ret;
 +              }
 +      }
 +
        if (dai->driver->ops->hw_params) {
                ret = dai->driver->ops->hw_params(substream, params, dai);
                if (ret < 0) {
        return 0;
  }
  
 +static int soc_pcm_components_hw_free(struct snd_pcm_substream *substream,
 +                                    struct snd_soc_component *last)
 +{
 +      struct snd_soc_pcm_runtime *rtd = substream->private_data;
 +      struct snd_soc_rtdcom_list *rtdcom;
 +      struct snd_soc_component *component;
 +
 +      for_each_rtdcom(rtd, rtdcom) {
 +              component = rtdcom->component;
 +
 +              if (component == last)
 +                      break;
 +
 +              if (!component->driver->ops ||
 +                  !component->driver->ops->hw_free)
 +                      continue;
 +
 +              component->driver->ops->hw_free(substream);
 +      }
 +
 +      return 0;
 +}
 +
  /*
   * Called by ALSA when the hardware params are set by application. This
   * function can also be called multiple times and can allocate buffers
@@@ -920,7 -886,7 +920,7 @@@ static int soc_pcm_hw_params(struct snd
        struct snd_soc_component *component;
        struct snd_soc_rtdcom_list *rtdcom;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 -      int i, ret = 0, __ret;
 +      int i, ret = 0;
  
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
        if (rtd->dai_link->ops->hw_params) {
        if (ret < 0)
                goto interface_err;
  
 -      ret = 0;
        for_each_rtdcom(rtd, rtdcom) {
                component = rtdcom->component;
  
                    !component->driver->ops->hw_params)
                        continue;
  
 -              __ret = component->driver->ops->hw_params(substream, params);
 -              if (__ret < 0) {
 +              ret = component->driver->ops->hw_params(substream, params);
 +              if (ret < 0) {
                        dev_err(component->dev,
                                "ASoC: %s hw params failed: %d\n",
 -                              component->name, __ret);
 -                      ret = __ret;
 +                              component->name, ret);
 +                      goto component_err;
                }
        }
 -      if (ret < 0)
 -              goto component_err;
 +      component = NULL;
  
        /* store the parameters for each DAIs */
        cpu_dai->rate = params_rate(params);
@@@ -1009,7 -977,15 +1009,7 @@@ out
        return ret;
  
  component_err:
 -      for_each_rtdcom(rtd, rtdcom) {
 -              component = rtdcom->component;
 -
 -              if (!component->driver->ops ||
 -                  !component->driver->ops->hw_free)
 -                      continue;
 -
 -              component->driver->ops->hw_free(substream);
 -      }
 +      soc_pcm_components_hw_free(substream, component);
  
        if (cpu_dai->driver->ops->hw_free)
                cpu_dai->driver->ops->hw_free(substream, cpu_dai);
@@@ -1038,6 -1014,8 +1038,6 @@@ codec_err
  static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
  {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 -      struct snd_soc_component *component;
 -      struct snd_soc_rtdcom_list *rtdcom;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai;
        bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
                rtd->dai_link->ops->hw_free(substream);
  
        /* free any component resources */
 -      for_each_rtdcom(rtd, rtdcom) {
 -              component = rtdcom->component;
 -
 -              if (!component->driver->ops ||
 -                  !component->driver->ops->hw_free)
 -                      continue;
 -
 -              component->driver->ops->hw_free(substream);
 -      }
 +      soc_pcm_components_hw_free(substream, NULL);
  
        /* now free hw params for the DAIs  */
        for (i = 0; i < rtd->num_codecs; i++) {
@@@ -1179,9 -1165,6 +1179,9 @@@ static snd_pcm_uframes_t soc_pcm_pointe
        snd_pcm_sframes_t codec_delay = 0;
        int i;
  
 +      /* clearing the previous total delay */
 +      runtime->delay = 0;
 +
        for_each_rtdcom(rtd, rtdcom) {
                component = rtdcom->component;
  
                offset = component->driver->ops->pointer(substream);
                break;
        }
 +      /* base delay if assigned in pointer callback */
 +      delay = runtime->delay;
  
        if (cpu_dai->driver->ops->delay)
                delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
@@@ -1677,28 -1658,29 +1677,28 @@@ unwind
  }
  
  static void dpcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
 -                               struct snd_soc_pcm_stream *stream,
 -                               u64 formats)
 +                               struct snd_soc_pcm_stream *stream)
  {
        runtime->hw.rate_min = stream->rate_min;
        runtime->hw.rate_max = stream->rate_max;
        runtime->hw.channels_min = stream->channels_min;
        runtime->hw.channels_max = stream->channels_max;
        if (runtime->hw.formats)
 -              runtime->hw.formats &= formats & stream->formats;
 +              runtime->hw.formats &= stream->formats;
        else
 -              runtime->hw.formats = formats & stream->formats;
 +              runtime->hw.formats = stream->formats;
        runtime->hw.rates = stream->rates;
  }
  
 -static u64 dpcm_runtime_base_format(struct snd_pcm_substream *substream)
 +static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream,
 +                                    u64 *formats)
  {
        struct snd_soc_pcm_runtime *fe = substream->private_data;
        struct snd_soc_dpcm *dpcm;
 -      u64 formats = ULLONG_MAX;
        int stream = substream->stream;
  
        if (!fe->dai_link->dpcm_merged_format)
 -              return formats;
 +              return;
  
        /*
         * It returns merged BE codec format
                int i;
  
                for (i = 0; i < be->num_codecs; i++) {
+                       /*
+                        * Skip CODECs which don't support the current stream
+                        * type. See soc_pcm_init_runtime_hw() for more details
+                        */
+                       if (!snd_soc_dai_stream_valid(be->codec_dais[i],
+                                                     stream))
+                               continue;
                        codec_dai_drv = be->codec_dais[i]->driver;
                        if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                                codec_stream = &codec_dai_drv->playback;
                        else
                                codec_stream = &codec_dai_drv->capture;
  
 -                      formats &= codec_stream->formats;
 +                      *formats &= codec_stream->formats;
 +              }
 +      }
 +}
 +
 +static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream,
 +                                  unsigned int *channels_min,
 +                                  unsigned int *channels_max)
 +{
 +      struct snd_soc_pcm_runtime *fe = substream->private_data;
 +      struct snd_soc_dpcm *dpcm;
 +      int stream = substream->stream;
 +
 +      if (!fe->dai_link->dpcm_merged_chan)
 +              return;
 +
 +      /*
 +       * It returns merged BE codec channel;
 +       * if FE want to use it (= dpcm_merged_chan)
 +       */
 +
 +      list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
 +              struct snd_soc_pcm_runtime *be = dpcm->be;
 +              struct snd_soc_dai_driver *cpu_dai_drv =  be->cpu_dai->driver;
 +              struct snd_soc_dai_driver *codec_dai_drv;
 +              struct snd_soc_pcm_stream *codec_stream;
 +              struct snd_soc_pcm_stream *cpu_stream;
 +
 +              if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 +                      cpu_stream = &cpu_dai_drv->playback;
 +              else
 +                      cpu_stream = &cpu_dai_drv->capture;
 +
 +              *channels_min = max(*channels_min, cpu_stream->channels_min);
 +              *channels_max = min(*channels_max, cpu_stream->channels_max);
 +
 +              /*
 +               * chan min/max cannot be enforced if there are multiple CODEC
 +               * DAIs connected to a single CPU DAI, use CPU DAI's directly
 +               */
 +              if (be->num_codecs == 1) {
 +                      codec_dai_drv = be->codec_dais[0]->driver;
 +
 +                      if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 +                              codec_stream = &codec_dai_drv->playback;
 +                      else
 +                              codec_stream = &codec_dai_drv->capture;
 +
 +                      *channels_min = max(*channels_min,
 +                                          codec_stream->channels_min);
 +                      *channels_max = min(*channels_max,
 +                                          codec_stream->channels_max);
                }
        }
 +}
 +
 +static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream,
 +                                  unsigned int *rates,
 +                                  unsigned int *rate_min,
 +                                  unsigned int *rate_max)
 +{
 +      struct snd_soc_pcm_runtime *fe = substream->private_data;
 +      struct snd_soc_dpcm *dpcm;
 +      int stream = substream->stream;
  
 -      return formats;
 +      if (!fe->dai_link->dpcm_merged_rate)
 +              return;
 +
 +      /*
 +       * It returns merged BE codec channel;
 +       * if FE want to use it (= dpcm_merged_chan)
 +       */
 +
 +      list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
 +              struct snd_soc_pcm_runtime *be = dpcm->be;
 +              struct snd_soc_dai_driver *cpu_dai_drv =  be->cpu_dai->driver;
 +              struct snd_soc_dai_driver *codec_dai_drv;
 +              struct snd_soc_pcm_stream *codec_stream;
 +              struct snd_soc_pcm_stream *cpu_stream;
 +              int i;
 +
 +              if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 +                      cpu_stream = &cpu_dai_drv->playback;
 +              else
 +                      cpu_stream = &cpu_dai_drv->capture;
 +
 +              *rate_min = max(*rate_min, cpu_stream->rate_min);
 +              *rate_max = min_not_zero(*rate_max, cpu_stream->rate_max);
 +              *rates = snd_pcm_rate_mask_intersect(*rates, cpu_stream->rates);
 +
 +              for (i = 0; i < be->num_codecs; i++) {
 +                      /*
 +                       * Skip CODECs which don't support the current stream
 +                       * type. See soc_pcm_init_runtime_hw() for more details
 +                       */
 +                      if (!snd_soc_dai_stream_valid(be->codec_dais[i],
 +                                                    stream))
 +                              continue;
 +
 +                      codec_dai_drv = be->codec_dais[i]->driver;
 +                      if (stream == SNDRV_PCM_STREAM_PLAYBACK)
 +                              codec_stream = &codec_dai_drv->playback;
 +                      else
 +                              codec_stream = &codec_dai_drv->capture;
 +
 +                      *rate_min = max(*rate_min, codec_stream->rate_min);
 +                      *rate_max = min_not_zero(*rate_max,
 +                                               codec_stream->rate_max);
 +                      *rates = snd_pcm_rate_mask_intersect(*rates,
 +                                              codec_stream->rates);
 +              }
 +      }
  }
  
  static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
 -      u64 format = dpcm_runtime_base_format(substream);
  
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 -              dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback, format);
 +              dpcm_init_runtime_hw(runtime, &cpu_dai_drv->playback);
        else
 -              dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture, format);
 +              dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
 +
 +      dpcm_runtime_merge_format(substream, &runtime->hw.formats);
 +      dpcm_runtime_merge_chan(substream, &runtime->hw.channels_min,
 +                              &runtime->hw.channels_max);
 +      dpcm_runtime_merge_rate(substream, &runtime->hw.rates,
 +                              &runtime->hw.rate_min, &runtime->hw.rate_max);
  }
  
  static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
@@@ -2673,113 -2551,106 +2681,113 @@@ static int dpcm_run_old_update(struct s
        return ret;
  }
  
 -/* Called by DAPM mixer/mux changes to update audio routing between PCMs and
 - * any DAI links.
 - */
 -int soc_dpcm_runtime_update(struct snd_soc_card *card)
 +static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
  {
 -      struct snd_soc_pcm_runtime *fe;
 -      int old, new, paths;
 +      struct snd_soc_dapm_widget_list *list;
 +      int count, paths;
  
 -      mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
 -      list_for_each_entry(fe, &card->rtd_list, list) {
 -              struct snd_soc_dapm_widget_list *list;
 +      if (!fe->dai_link->dynamic)
 +              return 0;
  
 -              /* make sure link is FE */
 -              if (!fe->dai_link->dynamic)
 -                      continue;
 +      /* only check active links */
 +      if (!fe->cpu_dai->active)
 +              return 0;
  
 -              /* only check active links */
 -              if (!fe->cpu_dai->active)
 -                      continue;
 +      /* DAPM sync will call this to update DSP paths */
 +      dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n",
 +              new ? "new" : "old", fe->dai_link->name);
  
 -              /* DAPM sync will call this to update DSP paths */
 -              dev_dbg(fe->dev, "ASoC: DPCM runtime update for FE %s\n",
 -                      fe->dai_link->name);
 +      /* skip if FE doesn't have playback capability */
 +      if (!fe->cpu_dai->driver->playback.channels_min ||
 +          !fe->codec_dai->driver->playback.channels_min)
 +              goto capture;
  
 -              /* skip if FE doesn't have playback capability */
 -              if (!fe->cpu_dai->driver->playback.channels_min
 -                  || !fe->codec_dai->driver->playback.channels_min)
 -                      goto capture;
 -
 -              /* skip if FE isn't currently playing */
 -              if (!fe->cpu_dai->playback_active
 -                  || !fe->codec_dai->playback_active)
 -                      goto capture;
 -
 -              paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
 -              if (paths < 0) {
 -                      dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
 -                                      fe->dai_link->name,  "playback");
 -                      mutex_unlock(&card->mutex);
 -                      return paths;
 -              }
 +      /* skip if FE isn't currently playing */
 +      if (!fe->cpu_dai->playback_active || !fe->codec_dai->playback_active)
 +              goto capture;
  
 -              /* update any new playback paths */
 -              new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 1);
 -              if (new) {
 -                      dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
 -                      dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
 -                      dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
 -              }
 +      paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
 +      if (paths < 0) {
 +              dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
 +                       fe->dai_link->name,  "playback");
 +              return paths;
 +      }
  
 -              /* update any old playback paths */
 -              old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, 0);
 -              if (old) {
 +      /* update any playback paths */
 +      count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_PLAYBACK, &list, new);
 +      if (count) {
 +              if (new)
 +                      dpcm_run_new_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
 +              else
                        dpcm_run_old_update(fe, SNDRV_PCM_STREAM_PLAYBACK);
 -                      dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
 -                      dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
 -              }
  
 -              dpcm_path_put(&list);
 +              dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_PLAYBACK);
 +              dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
 +      }
 +
 +      dpcm_path_put(&list);
 +
  capture:
 -              /* skip if FE doesn't have capture capability */
 -              if (!fe->cpu_dai->driver->capture.channels_min
 -                  || !fe->codec_dai->driver->capture.channels_min)
 -                      continue;
 +      /* skip if FE doesn't have capture capability */
 +      if (!fe->cpu_dai->driver->capture.channels_min ||
 +          !fe->codec_dai->driver->capture.channels_min)
 +              return 0;
  
 -              /* skip if FE isn't currently capturing */
 -              if (!fe->cpu_dai->capture_active
 -                  || !fe->codec_dai->capture_active)
 -                      continue;
 +      /* skip if FE isn't currently capturing */
 +      if (!fe->cpu_dai->capture_active || !fe->codec_dai->capture_active)
 +              return 0;
  
 -              paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
 -              if (paths < 0) {
 -                      dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
 -                                      fe->dai_link->name,  "capture");
 -                      mutex_unlock(&card->mutex);
 -                      return paths;
 -              }
 +      paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
 +      if (paths < 0) {
 +              dev_warn(fe->dev, "ASoC: %s no valid %s path\n",
 +                       fe->dai_link->name,  "capture");
 +              return paths;
 +      }
  
 -              /* update any new capture paths */
 -              new = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 1);
 -              if (new) {
 +      /* update any old capture paths */
 +      count = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, new);
 +      if (count) {
 +              if (new)
                        dpcm_run_new_update(fe, SNDRV_PCM_STREAM_CAPTURE);
 -                      dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
 -                      dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
 -              }
 -
 -              /* update any old capture paths */
 -              old = dpcm_process_paths(fe, SNDRV_PCM_STREAM_CAPTURE, &list, 0);
 -              if (old) {
 +              else
                        dpcm_run_old_update(fe, SNDRV_PCM_STREAM_CAPTURE);
 -                      dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
 -                      dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
 -              }
  
 -              dpcm_path_put(&list);
 +              dpcm_clear_pending_state(fe, SNDRV_PCM_STREAM_CAPTURE);
 +              dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_CAPTURE);
        }
  
 -      mutex_unlock(&card->mutex);
 +      dpcm_path_put(&list);
 +
        return 0;
  }
 +
 +/* Called by DAPM mixer/mux changes to update audio routing between PCMs and
 + * any DAI links.
 + */
 +int soc_dpcm_runtime_update(struct snd_soc_card *card)
 +{
 +      struct snd_soc_pcm_runtime *fe;
 +      int ret = 0;
 +
 +      mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
 +      /* shutdown all old paths first */
 +      list_for_each_entry(fe, &card->rtd_list, list) {
 +              ret = soc_dpcm_fe_runtime_update(fe, 0);
 +              if (ret)
 +                      goto out;
 +      }
 +
 +      /* bring new paths up */
 +      list_for_each_entry(fe, &card->rtd_list, list) {
 +              ret = soc_dpcm_fe_runtime_update(fe, 1);
 +              if (ret)
 +                      goto out;
 +      }
 +
 +out:
 +      mutex_unlock(&card->mutex);
 +      return ret;
 +}
  int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
  {
        struct snd_soc_dpcm *dpcm;
diff --combined sound/soc/soc-utils.c
index ea024236c6433b9bcfc86449fb41126fbf0a17e6,a863bb3f66c21ad8af277cbd6613ea6521a1e702..e0c93496c0cda699cef17f07386ae479b758fc03
@@@ -1,11 -1,17 +1,11 @@@
 -/*
 - * soc-util.c  --  ALSA SoC Audio Layer utility functions
 - *
 - * Copyright 2009 Wolfson Microelectronics PLC.
 - *
 - * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 - *         Liam Girdwood <lrg@slimlogic.co.uk>
 - *         
 - *
 - *  This program is free software; you can redistribute  it and/or modify it
 - *  under  the terms of  the GNU General  Public License as published by the
 - *  Free Software Foundation;  either version 2 of the  License, or (at your
 - *  option) any later version.
 - */
 +// SPDX-License-Identifier: GPL-2.0+
 +//
 +// soc-util.c  --  ALSA SoC Audio Layer utility functions
 +//
 +// Copyright 2009 Wolfson Microelectronics PLC.
 +//
 +// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 +//         Liam Girdwood <lrg@slimlogic.co.uk>
  
  #include <linux/platform_device.h>
  #include <linux/export.h>
@@@ -375,6 -381,6 +375,6 @@@ int __init snd_soc_util_init(void
  
  void __exit snd_soc_util_exit(void)
  {
-       platform_device_unregister(soc_dummy_dev);
        platform_driver_unregister(&soc_dummy_driver);
+       platform_device_unregister(soc_dummy_dev);
  }