Merge branch 'for-5.5' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorMark Brown <broonie@kernel.org>
Tue, 10 Dec 2019 13:27:14 +0000 (13:27 +0000)
committerMark Brown <broonie@kernel.org>
Tue, 10 Dec 2019 13:27:14 +0000 (13:27 +0000)
1  2 
include/sound/soc.h
sound/soc/codecs/max98090.c
sound/soc/codecs/max98090.h
sound/soc/codecs/rt5682.c
sound/soc/codecs/wm8904.c
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/soc-core.c
sound/soc/soc-pcm.c
sound/soc/soc-topology.c
sound/soc/sof/intel/byt.c
sound/soc/sof/topology.c

diff --combined include/sound/soc.h
@@@ -464,8 -464,10 +464,8 @@@ static inline int snd_soc_new_compress(
  
  void snd_soc_disconnect_sync(struct device *dev);
  
 -struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
 -              const char *dai_link, int stream);
  struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
 -              const char *dai_link);
 +                              struct snd_soc_dai_link *dai_link);
  
  bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd);
  void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream);
@@@ -850,6 -852,7 +850,6 @@@ struct snd_soc_dai_link 
        /* Do not create a PCM for this DAI link (Backend link) */
        unsigned int ignore:1;
  
 -      struct list_head list; /* DAI link list of the soc card */
  #ifdef CONFIG_SND_SOC_TOPOLOGY
        struct snd_soc_dobj dobj; /* For topology */
  #endif
@@@ -1034,6 -1037,7 +1034,6 @@@ struct snd_soc_card 
        /* CPU <--> Codec DAI links  */
        struct snd_soc_dai_link *dai_link;  /* predefined links only */
        int num_links;  /* predefined links only */
 -      struct list_head dai_link_list; /* all links */
  
        struct list_head rtd_list;
        int num_rtd;
             ((i) < (card)->num_aux_devs) && ((aux) = &(card)->aux_dev[i]); \
             (i)++)
  
 -#define for_each_card_links(card, link)                               \
 -      list_for_each_entry(link, &(card)->dai_link_list, list)
 -#define for_each_card_links_safe(card, link, _link)                   \
 -      list_for_each_entry_safe(link, _link, &(card)->dai_link_list, list)
 -
  #define for_each_card_rtds(card, rtd)                 \
        list_for_each_entry(rtd, &(card)->rtd_list, list)
  #define for_each_card_rtds_safe(card, rtd, _rtd)      \
@@@ -1141,6 -1150,7 +1141,7 @@@ struct snd_soc_pcm_runtime 
        unsigned int num_codecs;
  
        struct delayed_work delayed_work;
+       void (*close_delayed_work_func)(struct snd_soc_pcm_runtime *rtd);
  #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_dpcm_root;
  #endif
@@@ -1323,10 -1333,13 +1324,10 @@@ int snd_soc_of_get_dai_link_codecs(stru
                                   struct snd_soc_dai_link *dai_link);
  void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link);
  
 -int snd_soc_add_dai_link(struct snd_soc_card *card,
 -                              struct snd_soc_dai_link *dai_link);
 -void snd_soc_remove_dai_link(struct snd_soc_card *card,
 -                           struct snd_soc_dai_link *dai_link);
 -struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
 -                                             int id, const char *name,
 -                                             const char *stream_name);
 +int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
 +                          struct snd_soc_dai_link *dai_link);
 +void snd_soc_remove_pcm_runtime(struct snd_soc_card *card,
 +                              struct snd_soc_pcm_runtime *rtd);
  
  struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
                                         struct snd_soc_dai_driver *dai_drv,
   * Copyright 2011-2012 Maxim Integrated Products
   */
  
 +#include <linux/acpi.h>
 +#include <linux/clk.h>
  #include <linux/delay.h>
  #include <linux/i2c.h>
  #include <linux/module.h>
 +#include <linux/mutex.h>
  #include <linux/of.h>
  #include <linux/pm.h>
  #include <linux/pm_runtime.h>
  #include <linux/regmap.h>
  #include <linux/slab.h>
 -#include <linux/acpi.h>
 -#include <linux/clk.h>
  #include <sound/jack.h>
 +#include <sound/max98090.h>
  #include <sound/pcm.h>
  #include <sound/pcm_params.h>
  #include <sound/soc.h>
  #include <sound/tlv.h>
 -#include <sound/max98090.h>
  #include "max98090.h"
  
 +static void max98090_shdn_save_locked(struct max98090_priv *max98090)
 +{
 +      int shdn = 0;
 +
 +      /* saved_shdn, saved_count, SHDN are protected by card->dapm_mutex */
 +      regmap_read(max98090->regmap, M98090_REG_DEVICE_SHUTDOWN, &shdn);
 +      max98090->saved_shdn |= shdn;
 +      ++max98090->saved_count;
 +
 +      if (shdn)
 +              regmap_write(max98090->regmap, M98090_REG_DEVICE_SHUTDOWN, 0x0);
 +}
 +
 +static void max98090_shdn_restore_locked(struct max98090_priv *max98090)
 +{
 +      /* saved_shdn, saved_count, SHDN are protected by card->dapm_mutex */
 +      if (--max98090->saved_count == 0) {
 +              if (max98090->saved_shdn) {
 +                      regmap_write(max98090->regmap,
 +                                   M98090_REG_DEVICE_SHUTDOWN,
 +                                   M98090_SHDNN_MASK);
 +                      max98090->saved_shdn = 0;
 +              }
 +      }
 +}
 +
 +static void max98090_shdn_save(struct max98090_priv *max98090)
 +{
 +      mutex_lock(&max98090->component->card->dapm_mutex);
 +      max98090_shdn_save_locked(max98090);
 +}
 +
 +static void max98090_shdn_restore(struct max98090_priv *max98090)
 +{
 +      max98090_shdn_restore_locked(max98090);
 +      mutex_unlock(&max98090->component->card->dapm_mutex);
 +}
 +
 +static int max98090_put_volsw(struct snd_kcontrol *kcontrol,
 +      struct snd_ctl_elem_value *ucontrol)
 +{
 +      struct snd_soc_component *component =
 +              snd_soc_kcontrol_component(kcontrol);
 +      struct max98090_priv *max98090 =
 +              snd_soc_component_get_drvdata(component);
 +      int ret;
 +
 +      max98090_shdn_save(max98090);
 +      ret = snd_soc_put_volsw(kcontrol, ucontrol);
 +      max98090_shdn_restore(max98090);
 +
 +      return ret;
 +}
 +
 +static int max98090_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 +      struct snd_ctl_elem_value *ucontrol)
 +{
 +      struct snd_soc_component *component =
 +              snd_soc_kcontrol_component(kcontrol);
 +      struct max98090_priv *max98090 =
 +              snd_soc_component_get_drvdata(component);
 +      int ret;
 +
 +      max98090_shdn_save(max98090);
 +      ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
 +      max98090_shdn_restore(max98090);
 +
 +      return ret;
 +}
 +
 +static int max98090_put_enum_double(struct snd_kcontrol *kcontrol,
 +      struct snd_ctl_elem_value *ucontrol)
 +{
 +      struct snd_soc_component *component =
 +              snd_soc_kcontrol_component(kcontrol);
 +      struct max98090_priv *max98090 =
 +              snd_soc_component_get_drvdata(component);
 +      int ret;
 +
 +      max98090_shdn_save(max98090);
 +      ret = snd_soc_put_enum_double(kcontrol, ucontrol);
 +      max98090_shdn_restore(max98090);
 +
 +      return ret;
 +}
 +
 +static int max98090_bytes_put(struct snd_kcontrol *kcontrol,
 +      struct snd_ctl_elem_value *ucontrol)
 +{
 +      struct snd_soc_component *component =
 +              snd_soc_kcontrol_component(kcontrol);
 +      struct max98090_priv *max98090 =
 +              snd_soc_component_get_drvdata(component);
 +      int ret;
 +
 +      max98090_shdn_save(max98090);
 +      ret = snd_soc_bytes_put(kcontrol, ucontrol);
 +      max98090_shdn_restore(max98090);
 +
 +      return ret;
 +}
 +
 +static int max98090_dapm_event(struct snd_soc_dapm_widget *w,
 +      struct snd_kcontrol *kcontrol, int event)
 +{
 +      struct snd_soc_component *component =
 +              snd_soc_dapm_to_component(w->dapm);
 +      struct max98090_priv *max98090 =
 +              snd_soc_component_get_drvdata(component);
 +
 +      switch (event) {
 +      case SND_SOC_DAPM_PRE_PMU:
 +      case SND_SOC_DAPM_PRE_PMD:
 +              max98090_shdn_save_locked(max98090);
 +              break;
 +      case SND_SOC_DAPM_POST_PMU:
 +      case SND_SOC_DAPM_POST_PMD:
 +              max98090_shdn_restore_locked(max98090);
 +              break;
 +      }
 +
 +      return 0;
 +}
 +
  /* Allows for sparsely populated register maps */
  static const struct reg_default max98090_reg[] = {
        { 0x00, 0x00 }, /* 00 Software Reset */
@@@ -631,13 -506,10 +631,13 @@@ static SOC_ENUM_SINGLE_DECL(max98090_ad
                            max98090_pwr_perf_text);
  
  static const struct snd_kcontrol_new max98090_snd_controls[] = {
 -      SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum),
 +      SOC_ENUM_EXT("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
  
 -      SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG,
 -              M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0),
 +      SOC_SINGLE_EXT("DMIC MIC Comp Filter Config",
 +              M98090_REG_DIGITAL_MIC_CONFIG,
 +              M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
  
        SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
                M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT,
                M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1,
                max98090_av_tlv),
  
 -      SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum),
 -      SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL,
 -              M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0),
 -      SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum),
 -
 -      SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION,
 -              M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0),
 -      SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION,
 -              M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0),
 -      SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION,
 -              M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0),
 -      SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION,
 -              M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1),
 -      SOC_ENUM("Filter Mode", max98090_mode_enum),
 -      SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG,
 -              M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0),
 -      SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG,
 -              M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0),
 +      SOC_ENUM_EXT("ADC Oversampling Rate", max98090_osr128_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
 +      SOC_SINGLE_EXT("ADC Quantizer Dither", M98090_REG_ADC_CONTROL,
 +              M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
 +      SOC_ENUM_EXT("ADC High Performance Mode", max98090_adchp_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
 +
 +      SOC_SINGLE_EXT("DAC Mono Mode", M98090_REG_IO_CONFIGURATION,
 +              M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
 +      SOC_SINGLE_EXT("SDIN Mode", M98090_REG_IO_CONFIGURATION,
 +              M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
 +      SOC_SINGLE_EXT("SDOUT Mode", M98090_REG_IO_CONFIGURATION,
 +              M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
 +      SOC_SINGLE_EXT("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION,
 +              M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1,
 +              snd_soc_get_volsw, max98090_put_volsw),
 +      SOC_ENUM_EXT("Filter Mode", max98090_mode_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
 +      SOC_SINGLE_EXT("Record Path DC Blocking", M98090_REG_FILTER_CONFIG,
 +              M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
 +      SOC_SINGLE_EXT("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG,
 +              M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
        SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL,
                M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv),
        SOC_SINGLE_EXT_TLV("Digital Sidetone Volume",
        SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
                M98090_DV_SHIFT, M98090_DV_NUM - 1, 1,
                max98090_dv_tlv),
 -      SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105),
 -      SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
 -              M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0),
 -      SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
 -              M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0),
 -      SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
 -              M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0),
 +      SND_SOC_BYTES_E("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105,
 +              snd_soc_bytes_get, max98090_bytes_put),
 +      SOC_SINGLE_EXT("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
 +              M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
 +      SOC_SINGLE_EXT("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
 +              M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
 +      SOC_SINGLE_EXT("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
 +              M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
        SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ,
                M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1,
                1),
                M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1,
                max98090_dv_tlv),
  
 -      SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING,
 -              M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0),
 -      SOC_ENUM("ALC Attack Time", max98090_drcatk_enum),
 -      SOC_ENUM("ALC Release Time", max98090_drcrls_enum),
 +      SOC_SINGLE_EXT("ALC Enable", M98090_REG_DRC_TIMING,
 +              M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
 +      SOC_ENUM_EXT("ALC Attack Time", max98090_drcatk_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
 +      SOC_ENUM_EXT("ALC Release Time", max98090_drcrls_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
        SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN,
                M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0,
                max98090_alcmakeup_tlv),
 -      SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum),
 -      SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum),
 -      SOC_SINGLE_TLV("ALC Compression Threshold Volume",
 +      SOC_ENUM_EXT("ALC Compression Ratio", max98090_alccmp_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
 +      SOC_ENUM_EXT("ALC Expansion Ratio", max98090_drcexp_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
 +      SOC_SINGLE_EXT_TLV("ALC Compression Threshold Volume",
                M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT,
 -              M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv),
 -      SOC_SINGLE_TLV("ALC Expansion Threshold Volume",
 +              M98090_DRCTHC_NUM - 1, 1,
 +              snd_soc_get_volsw, max98090_put_volsw, max98090_alccomp_tlv),
 +      SOC_SINGLE_EXT_TLV("ALC Expansion Threshold Volume",
                M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT,
 -              M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv),
 +              M98090_DRCTHE_NUM - 1, 1,
 +              snd_soc_get_volsw, max98090_put_volsw, max98090_drcexp_tlv),
  
 -      SOC_ENUM("DAC HP Playback Performance Mode",
 -              max98090_dac_perfmode_enum),
 -      SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum),
 +      SOC_ENUM_EXT("DAC HP Playback Performance Mode",
 +              max98090_dac_perfmode_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
 +      SOC_ENUM_EXT("DAC High Performance Mode", max98090_dachp_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
  
        SOC_SINGLE_TLV("Headphone Left Mixer Volume",
                M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT,
        SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL,
                M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1),
  
 -      SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15),
 -      SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
 -              M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0),
 +      SND_SOC_BYTES_E("Biquad Coefficients",
 +              M98090_REG_RECORD_BIQUAD_BASE, 15,
 +              snd_soc_bytes_get, max98090_bytes_put),
 +      SOC_SINGLE_EXT("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
 +              M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
  };
  
  static const struct snd_kcontrol_new max98091_snd_controls[] = {
                M98090_DMIC34_ZEROPAD_SHIFT,
                M98090_DMIC34_ZEROPAD_NUM - 1, 0),
  
 -      SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum),
 -      SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG,
 +      SOC_ENUM_EXT("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum,
 +              snd_soc_get_enum_double, max98090_put_enum_double),
 +      SOC_SINGLE_EXT("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG,
                M98090_FLT_DMIC34HPF_SHIFT,
 -              M98090_FLT_DMIC34HPF_NUM - 1, 0),
 +              M98090_FLT_DMIC34HPF_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
  
        SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME,
                M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0,
  
        SND_SOC_BYTES("DMIC34 Biquad Coefficients",
                M98090_REG_DMIC34_BIQUAD_BASE, 15),
 -      SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
 -              M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0),
 +      SOC_SINGLE_EXT("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
 +              M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0,
 +              snd_soc_get_volsw, max98090_put_volsw),
  
        SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume",
                M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT,
@@@ -928,6 -771,19 +928,6 @@@ static int max98090_micinput_event(stru
        return 0;
  }
  
 -static int max98090_shdn_event(struct snd_soc_dapm_widget *w,
 -                               struct snd_kcontrol *kcontrol, int event)
 -{
 -      struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
 -      struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
 -
 -      if (event & SND_SOC_DAPM_POST_PMU)
 -              max98090->shdn_pending = true;
 -
 -      return 0;
 -
 -}
 -
  static const char *mic1_mux_text[] = { "IN12", "IN56" };
  
  static SOC_ENUM_SINGLE_DECL(mic1_mux_enum,
@@@ -1028,14 -884,10 +1028,14 @@@ static SOC_ENUM_SINGLE_DECL(ltenr_mux_e
                            lten_mux_text);
  
  static const struct snd_kcontrol_new max98090_ltenl_mux =
 -      SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum);
 +      SOC_DAPM_ENUM_EXT("LTENL Mux", ltenl_mux_enum,
 +                        snd_soc_dapm_get_enum_double,
 +                        max98090_dapm_put_enum_double);
  
  static const struct snd_kcontrol_new max98090_ltenr_mux =
 -      SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum);
 +      SOC_DAPM_ENUM_EXT("LTENR Mux", ltenr_mux_enum,
 +                        snd_soc_dapm_get_enum_double,
 +                        max98090_dapm_put_enum_double);
  
  static const char *lben_mux_text[] = { "Normal", "Loopback" };
  
@@@ -1050,14 -902,10 +1050,14 @@@ static SOC_ENUM_SINGLE_DECL(lbenr_mux_e
                            lben_mux_text);
  
  static const struct snd_kcontrol_new max98090_lbenl_mux =
 -      SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum);
 +      SOC_DAPM_ENUM_EXT("LBENL Mux", lbenl_mux_enum,
 +                        snd_soc_dapm_get_enum_double,
 +                        max98090_dapm_put_enum_double);
  
  static const struct snd_kcontrol_new max98090_lbenr_mux =
 -      SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum);
 +      SOC_DAPM_ENUM_EXT("LBENR Mux", lbenr_mux_enum,
 +                        snd_soc_dapm_get_enum_double,
 +                        max98090_dapm_put_enum_double);
  
  static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" };
  
@@@ -1224,25 -1072,21 +1224,25 @@@ static const struct snd_soc_dapm_widge
        SND_SOC_DAPM_INPUT("IN56"),
  
        SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE,
 -              M98090_MBEN_SHIFT, 0, NULL, 0),
 +              M98090_MBEN_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
        SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN,
                M98090_SHDNN_SHIFT, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION,
 -              M98090_SDIEN_SHIFT, 0, NULL, 0),
 +              M98090_SDIEN_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
        SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION,
 -              M98090_SDOEN_SHIFT, 0, NULL, 0),
 +              M98090_SDOEN_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
        SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
 -               M98090_DIGMICL_SHIFT, 0, max98090_shdn_event,
 -                      SND_SOC_DAPM_POST_PMU),
 +              M98090_DIGMICL_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
        SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
 -               M98090_DIGMICR_SHIFT, 0, max98090_shdn_event,
 -                       SND_SOC_DAPM_POST_PMU),
 +              M98090_DIGMICR_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
        SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG,
 -              M98090_AHPF_SHIFT, 0, NULL, 0),
 +              M98090_AHPF_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
  
  /*
   * Note: Sysclk and misc power supplies are taken care of by SHDN
                &max98090_lineb_mixer_controls[0],
                ARRAY_SIZE(max98090_lineb_mixer_controls)),
  
 -      SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE,
 -              M98090_LINEAEN_SHIFT, 0, NULL, 0),
 -      SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE,
 -              M98090_LINEBEN_SHIFT, 0, NULL, 0),
 +      SND_SOC_DAPM_PGA_E("LINEA Input", M98090_REG_INPUT_ENABLE,
 +              M98090_LINEAEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 +      SND_SOC_DAPM_PGA_E("LINEB Input", M98090_REG_INPUT_ENABLE,
 +              M98090_LINEBEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
  
        SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
                &max98090_left_adc_mixer_controls[0],
                ARRAY_SIZE(max98090_right_adc_mixer_controls)),
  
        SND_SOC_DAPM_ADC_E("ADCL", NULL, M98090_REG_INPUT_ENABLE,
 -              M98090_ADLEN_SHIFT, 0, max98090_shdn_event,
 -              SND_SOC_DAPM_POST_PMU),
 +              M98090_ADLEN_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
        SND_SOC_DAPM_ADC_E("ADCR", NULL, M98090_REG_INPUT_ENABLE,
 -              M98090_ADREN_SHIFT, 0, max98090_shdn_event,
 -              SND_SOC_DAPM_POST_PMU),
 +              M98090_ADREN_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
  
        SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0,
                SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0),
  
 -      SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE,
 -              M98090_DALEN_SHIFT, 0),
 -      SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE,
 -              M98090_DAREN_SHIFT, 0),
 +      SND_SOC_DAPM_DAC_E("DACL", NULL, M98090_REG_OUTPUT_ENABLE,
 +              M98090_DALEN_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 +      SND_SOC_DAPM_DAC_E("DACR", NULL, M98090_REG_OUTPUT_ENABLE,
 +              M98090_DAREN_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
  
        SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
                &max98090_left_hp_mixer_controls[0],
        SND_SOC_DAPM_MUX("MIXHPRSEL Mux", SND_SOC_NOPM, 0, 0,
                &max98090_mixhprsel_mux),
  
 -      SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE,
 -              M98090_HPLEN_SHIFT, 0, NULL, 0),
 -      SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE,
 -              M98090_HPREN_SHIFT, 0, NULL, 0),
 -
 -      SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE,
 -              M98090_SPLEN_SHIFT, 0, NULL, 0),
 -      SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE,
 -              M98090_SPREN_SHIFT, 0, NULL, 0),
 -
 -      SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE,
 -              M98090_RCVLEN_SHIFT, 0, NULL, 0),
 -      SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE,
 -              M98090_RCVREN_SHIFT, 0, NULL, 0),
 +      SND_SOC_DAPM_PGA_E("HP Left Out", M98090_REG_OUTPUT_ENABLE,
 +              M98090_HPLEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 +      SND_SOC_DAPM_PGA_E("HP Right Out", M98090_REG_OUTPUT_ENABLE,
 +              M98090_HPREN_SHIFT, 0, NULL, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 +
 +      SND_SOC_DAPM_PGA_E("SPK Left Out", M98090_REG_OUTPUT_ENABLE,
 +              M98090_SPLEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 +      SND_SOC_DAPM_PGA_E("SPK Right Out", M98090_REG_OUTPUT_ENABLE,
 +              M98090_SPREN_SHIFT, 0, NULL, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 +
 +      SND_SOC_DAPM_PGA_E("RCV Left Out", M98090_REG_OUTPUT_ENABLE,
 +              M98090_RCVLEN_SHIFT, 0, NULL, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
 +      SND_SOC_DAPM_PGA_E("RCV Right Out", M98090_REG_OUTPUT_ENABLE,
 +              M98090_RCVREN_SHIFT, 0, NULL, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
  
        SND_SOC_DAPM_OUTPUT("HPL"),
        SND_SOC_DAPM_OUTPUT("HPR"),
@@@ -1394,11 -1228,9 +1394,11 @@@ static const struct snd_soc_dapm_widge
        SND_SOC_DAPM_INPUT("DMIC4"),
  
        SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
 -               M98090_DIGMIC3_SHIFT, 0, NULL, 0),
 +              M98090_DIGMIC3_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
        SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
 -               M98090_DIGMIC4_SHIFT, 0, NULL, 0),
 +              M98090_DIGMIC4_SHIFT, 0, max98090_dapm_event,
 +              SND_SOC_DAPM_PRE_POST_PMU | SND_SOC_DAPM_PRE_POST_PMD),
  };
  
  static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
@@@ -1669,11 -1501,6 +1669,11 @@@ static void max98090_configure_bclk(str
                return;
        }
  
 +      /*
 +       * Master mode: no need to save and restore SHDN for the following
 +       * sensitive registers.
 +       */
 +
        /* Check for supported PCLK to LRCLK ratios */
        for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) {
                if ((pclk_rates[i] == max98090->sysclk) &&
@@@ -1760,14 -1587,12 +1760,14 @@@ static int max98090_dai_set_fmt(struct 
                switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
                case SND_SOC_DAIFMT_CBS_CFS:
                        /* Set to slave mode PLL - MAS mode off */
 +                      max98090_shdn_save(max98090);
                        snd_soc_component_write(component,
                                M98090_REG_CLOCK_RATIO_NI_MSB, 0x00);
                        snd_soc_component_write(component,
                                M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
                        snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
                                M98090_USE_M1_MASK, 0);
 +                      max98090_shdn_restore(max98090);
                        max98090->master = false;
                        break;
                case SND_SOC_DAIFMT_CBM_CFM:
                        dev_err(component->dev, "DAI clock mode unsupported");
                        return -EINVAL;
                }
 +              max98090_shdn_save(max98090);
                snd_soc_component_write(component, M98090_REG_MASTER_MODE, regval);
 +              max98090_shdn_restore(max98090);
  
                regval = 0;
                switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
                if (max98090->tdm_slots > 1)
                        regval ^= M98090_BCI_MASK;
  
 +              max98090_shdn_save(max98090);
                snd_soc_component_write(component,
                        M98090_REG_INTERFACE_FORMAT, regval);
 +              max98090_shdn_restore(max98090);
        }
  
        return 0;
@@@ -1855,7 -1676,6 +1855,7 @@@ static int max98090_set_tdm_slot(struc
        struct snd_soc_component *component = codec_dai->component;
        struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
        struct max98090_cdata *cdata;
 +
        cdata = &max98090->dai[0];
  
        if (slots < 0 || slots > 4)
        max98090->tdm_width = slot_width;
  
        if (max98090->tdm_slots > 1) {
 +              max98090_shdn_save(max98090);
                /* SLOTL SLOTR SLOTDLY */
                snd_soc_component_write(component, M98090_REG_TDM_FORMAT,
                        0 << M98090_TDM_SLOTL_SHIFT |
                snd_soc_component_update_bits(component, M98090_REG_TDM_CONTROL,
                        M98090_TDM_MASK,
                        M98090_TDM_MASK);
 +              max98090_shdn_restore(max98090);
        }
  
        /*
@@@ -2076,7 -1894,6 +2076,7 @@@ static int max98090_configure_dmic(stru
        dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq;
        dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i];
  
 +      max98090_shdn_save(max98090);
        regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE,
                           M98090_MICCLK_MASK,
                           micclk_index << M98090_MICCLK_SHIFT);
                           M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK,
                           dmic_comp << M98090_DMIC_COMP_SHIFT |
                           dmic_freq << M98090_DMIC_FREQ_SHIFT);
 +      max98090_shdn_restore(max98090);
  
        return 0;
  }
@@@ -2122,10 -1938,8 +2122,10 @@@ static int max98090_dai_hw_params(struc
  
        switch (params_width(params)) {
        case 16:
 +              max98090_shdn_save(max98090);
                snd_soc_component_update_bits(component, M98090_REG_INTERFACE_FORMAT,
                        M98090_WS_MASK, 0);
 +              max98090_shdn_restore(max98090);
                break;
        default:
                return -EINVAL;
  
        cdata->rate = max98090->lrclk;
  
 +      max98090_shdn_save(max98090);
        /* Update filter mode */
        if (max98090->lrclk < 24000)
                snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
        else
                snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
                        M98090_DHF_MASK, M98090_DHF_MASK);
 +      max98090_shdn_restore(max98090);
  
        max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
                                max98090->lrclk);
@@@ -2183,7 -1995,6 +2183,7 @@@ static int max98090_dai_set_sysclk(stru
         *               0x02 (when master clk is 20MHz to 40MHz)..
         *               0x03 (when master clk is 40MHz to 60MHz)..
         */
 +      max98090_shdn_save(max98090);
        if ((freq >= 10000000) && (freq <= 20000000)) {
                snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
                        M98090_PSCLK_DIV1);
                max98090->pclk = freq >> 2;
        } else {
                dev_err(component->dev, "Invalid master clock frequency\n");
 +              max98090_shdn_restore(max98090);
                return -EINVAL;
        }
 +      max98090_shdn_restore(max98090);
  
        max98090->sysclk = freq;
  
@@@ -2294,28 -2103,40 +2294,42 @@@ static void max98090_pll_det_disable_wo
                            M98090_IULK_MASK, 0);
  }
  
- static void max98090_pll_work(struct work_struct *work)
+ static void max98090_pll_work(struct max98090_priv *max98090)
  {
-       struct max98090_priv *max98090 =
-               container_of(work, struct max98090_priv, pll_work);
        struct snd_soc_component *component = max98090->component;
+       unsigned int pll;
+       int i;
  
        if (!snd_soc_component_is_active(component))
                return;
  
        dev_info_ratelimited(component->dev, "PLL unlocked\n");
  
+       /*
+        * As the datasheet suggested, the maximum PLL lock time should be
+        * 7 msec.  The workaround resets the codec softly by toggling SHDN
+        * off and on if PLL failed to lock for 10 msec.  Notably, there is
+        * no suggested hold time for SHDN off.
+        */
        /* Toggle shutdown OFF then ON */
 +      mutex_lock(&component->card->dapm_mutex);
        snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
                            M98090_SHDNN_MASK, 0);
-       msleep(10);
        snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
                            M98090_SHDNN_MASK, M98090_SHDNN_MASK);
 +      mutex_unlock(&component->card->dapm_mutex);
  
-       /* Give PLL time to lock */
-       msleep(10);
+       for (i = 0; i < 10; ++i) {
+               /* Give PLL time to lock */
+               usleep_range(1000, 1200);
+               /* Check lock status */
+               pll = snd_soc_component_read32(
+                               component, M98090_REG_DEVICE_STATUS);
+               if (!(pll & M98090_ULK_MASK))
+                       break;
+       }
  }
  
  static void max98090_jack_work(struct work_struct *work)
@@@ -2452,7 -2273,7 +2466,7 @@@ static irqreturn_t max98090_interrupt(i
  
        if (active & M98090_ULK_MASK) {
                dev_dbg(component->dev, "M98090_ULK_MASK\n");
-               schedule_work(&max98090->pll_work);
+               max98090_pll_work(max98090);
        }
  
        if (active & M98090_JDET_MASK) {
@@@ -2615,7 -2436,6 +2629,6 @@@ static int max98090_probe(struct snd_so
                          max98090_pll_det_enable_work);
        INIT_WORK(&max98090->pll_det_disable_work,
                  max98090_pll_det_disable_work);
-       INIT_WORK(&max98090->pll_work, max98090_pll_work);
  
        /* Enable jack detection */
        snd_soc_component_write(component, M98090_REG_JACK_DETECT,
         */
        snd_soc_component_read32(component, M98090_REG_DEVICE_STATUS);
  
 -      /* High Performance is default */
 +      /*
 +       * SHDN should be 0 at the point, no need to save/restore for the
 +       * following registers.
 +       *
 +       * High Performance is default
 +       */
        snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
                M98090_DACHP_MASK,
                1 << M98090_DACHP_SHIFT);
                M98090_ADCHP_MASK,
                1 << M98090_ADCHP_SHIFT);
  
 -      /* Turn on VCM bandgap reference */
 +      /*
 +       * SHDN should be 0 at the point, no need to save/restore for the
 +       * following registers.
 +       *
 +       * Turn on VCM bandgap reference
 +       */
        snd_soc_component_write(component, M98090_REG_BIAS_CONTROL,
                M98090_VCM_MODE_MASK);
  
@@@ -2678,13 -2488,28 +2691,12 @@@ static void max98090_remove(struct snd_
        cancel_delayed_work_sync(&max98090->jack_work);
        cancel_delayed_work_sync(&max98090->pll_det_enable_work);
        cancel_work_sync(&max98090->pll_det_disable_work);
-       cancel_work_sync(&max98090->pll_work);
        max98090->component = NULL;
  }
  
 -static void max98090_seq_notifier(struct snd_soc_component *component,
 -      enum snd_soc_dapm_type event, int subseq)
 -{
 -      struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
 -
 -      if (max98090->shdn_pending) {
 -              snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
 -                              M98090_SHDNN_MASK, 0);
 -              msleep(40);
 -              snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
 -                              M98090_SHDNN_MASK, M98090_SHDNN_MASK);
 -              max98090->shdn_pending = false;
 -      }
 -}
 -
  static const struct snd_soc_component_driver soc_component_dev_max98090 = {
        .probe                  = max98090_probe,
        .remove                 = max98090_remove,
 -      .seq_notifier           = max98090_seq_notifier,
        .set_bias_level         = max98090_set_bias_level,
        .idle_bias_on           = 1,
        .use_pmdown_time        = 1,
@@@ -1530,7 -1530,6 +1530,6 @@@ struct max98090_priv 
        struct delayed_work jack_work;
        struct delayed_work pll_det_enable_work;
        struct work_struct pll_det_disable_work;
-       struct work_struct pll_work;
        struct snd_soc_jack *jack;
        unsigned int dai_fmt;
        int tdm_slots;
        unsigned int pa2en;
        unsigned int sidetone;
        bool master;
 -      bool shdn_pending;
 +      int saved_count;
 +      int saved_shdn;
  };
  
  int max98090_mic_detect(struct snd_soc_component *component,
@@@ -73,6 -73,7 +73,7 @@@ struct rt5682_priv 
  static const struct reg_sequence patch_list[] = {
        {RT5682_HP_IMP_SENS_CTRL_19, 0x1000},
        {RT5682_DAC_ADC_DIG_VOL1, 0xa020},
+       {RT5682_I2C_CTRL, 0x000f},
  };
  
  static const struct reg_default rt5682_reg[] = {
@@@ -1003,7 -1004,6 +1004,7 @@@ static int rt5682_set_jack_detect(struc
                                   RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
                regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
                                   RT5682_POW_JDH | RT5682_POW_JDL, 0);
 +              cancel_delayed_work_sync(&rt5682->jack_detect_work);
                return 0;
        }
  
@@@ -1464,6 -1464,28 +1465,6 @@@ static const struct snd_kcontrol_new hp
        SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1,
                                        RT5682_R_MUTE_SFT, 1, 1);
  
 -static int rt5682_charge_pump_event(struct snd_soc_dapm_widget *w,
 -      struct snd_kcontrol *kcontrol, int event)
 -{
 -      struct snd_soc_component *component =
 -              snd_soc_dapm_to_component(w->dapm);
 -
 -      switch (event) {
 -      case SND_SOC_DAPM_PRE_PMU:
 -              snd_soc_component_update_bits(component,
 -                      RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_HV);
 -              break;
 -      case SND_SOC_DAPM_POST_PMD:
 -              snd_soc_component_update_bits(component,
 -                      RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_LV);
 -              break;
 -      default:
 -              return 0;
 -      }
 -
 -      return 0;
 -}
 -
  static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
  {
@@@ -1747,7 -1769,8 +1748,7 @@@ static const struct snd_soc_dapm_widge
        SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1,
                RT5682_PWR_HA_R_BIT, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1,
 -              RT5682_PUMP_EN_SFT, 0, rt5682_charge_pump_event,
 -              SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 +              RT5682_PUMP_EN_SFT, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1,
                RT5682_CAPLESS_EN_SFT, 0, NULL, 0),
  
@@@ -2474,6 -2497,7 +2475,7 @@@ static void rt5682_calibrate(struct rt5
        mutex_lock(&rt5682->calibrate_mutex);
  
        rt5682_reset(rt5682->regmap);
+       regmap_write(rt5682->regmap, RT5682_I2C_CTRL, 0x000f);
        regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af);
        usleep_range(15000, 20000);
        regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af);
@@@ -2647,8 -2671,6 +2649,8 @@@ static int rt5682_i2c_probe(struct i2c_
                        RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA);
        regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1,
                        RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ);
 +      regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1,
 +                      RT5682_PM_HP_MASK, RT5682_PM_HP_HV);
  
        INIT_DELAYED_WORK(&rt5682->jack_detect_work,
                                rt5682_jack_detect_handler);
@@@ -1806,6 -1806,12 +1806,12 @@@ static int wm8904_set_sysclk(struct snd
  
        switch (clk_id) {
        case WM8904_CLK_AUTO:
+               /* We don't have any rate constraints, so just ignore the
+                * request to disable constraining.
+                */
+               if (!freq)
+                       return 0;
                mclk_freq = clk_get_rate(priv->mclk);
                /* enable FLL if a different sysclk is desired */
                if (mclk_freq != freq) {
@@@ -1933,7 -1939,6 +1939,7 @@@ static int wm8904_set_bias_level(struc
                snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0,
                                    WM8904_BIAS_ENA, 0);
  
 +              snd_soc_component_write(component, WM8904_SW_RESET_AND_ID, 0);
                regcache_cache_only(wm8904->regmap, true);
                regcache_mark_dirty(wm8904->regmap);
  
@@@ -405,12 -405,10 +405,12 @@@ static const struct dmi_system_id byt_r
                        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
                },
 -              .driver_data = (void *)(BYT_RT5640_IN1_MAP |
 -                                               BYT_RT5640_MCLK_EN |
 -                                               BYT_RT5640_SSP0_AIF1),
 -
 +              .driver_data = (void *)(BYT_RT5640_DMIC1_MAP |
 +                                      BYT_RT5640_JD_SRC_JD2_IN4N |
 +                                      BYT_RT5640_OVCD_TH_2000UA |
 +                                      BYT_RT5640_OVCD_SF_0P75 |
 +                                      BYT_RT5640_SSP0_AIF1 |
 +                                      BYT_RT5640_MCLK_EN),
        },
        {
                .matches = {
                                        BYT_RT5640_MCLK_EN),
        },
        {
+               /* Teclast X89 */
                .matches = {
                        DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
                        DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
                },
                .driver_data = (void *)(BYT_RT5640_IN3_MAP |
-                                       BYT_RT5640_MCLK_EN |
-                                       BYT_RT5640_SSP0_AIF1),
+                                       BYT_RT5640_JD_SRC_JD1_IN4P |
+                                       BYT_RT5640_OVCD_TH_2000UA |
+                                       BYT_RT5640_OVCD_SF_1P0 |
+                                       BYT_RT5640_SSP0_AIF1 |
+                                       BYT_RT5640_MCLK_EN),
        },
        {       /* Toshiba Satellite Click Mini L9W-B */
                .matches = {
@@@ -1082,10 -1084,7 +1086,10 @@@ static struct snd_soc_dai_link byt_rt56
  static char byt_rt5640_codec_name[SND_ACPI_I2C_ID_LEN];
  static char byt_rt5640_codec_aif_name[12]; /*  = "rt5640-aif[1|2]" */
  static char byt_rt5640_cpu_dai_name[10]; /*  = "ssp[0|2]-port" */
 +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES)
  static char byt_rt5640_long_name[40]; /* = "bytcr-rt5640-*-spk-*-mic" */
 +#endif
 +static char byt_rt5640_components[32]; /* = "cfg-spk:* cfg-mic:*" */
  
  static int byt_rt5640_suspend(struct snd_soc_card *card)
  {
@@@ -1308,19 -1307,12 +1312,19 @@@ static int snd_byt_rt5640_mc_probe(stru
                }
        }
  
 +      snprintf(byt_rt5640_components, sizeof(byt_rt5640_components),
 +               "cfg-spk:%s cfg-mic:%s",
 +               (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) ? "1" : "2",
 +               map_name[BYT_RT5640_MAP(byt_rt5640_quirk)]);
 +      byt_rt5640_card.components = byt_rt5640_components;
 +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES)
        snprintf(byt_rt5640_long_name, sizeof(byt_rt5640_long_name),
                 "bytcr-rt5640-%s-spk-%s-mic",
                 (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) ?
                        "mono" : "stereo",
                 map_name[BYT_RT5640_MAP(byt_rt5640_quirk)]);
        byt_rt5640_card.long_name = byt_rt5640_long_name;
 +#endif
  
        /* override plaform name, if required */
        platform_name = mach->mach_params.platform;
diff --combined sound/soc/soc-core.c
@@@ -389,22 -389,22 +389,22 @@@ struct snd_soc_component *snd_soc_looku
  }
  EXPORT_SYMBOL_GPL(snd_soc_lookup_component);
  
 -struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
 -              const char *dai_link, int stream)
 +static const struct snd_soc_ops null_snd_soc_ops;
 +
 +struct snd_soc_pcm_runtime
 +*snd_soc_get_pcm_runtime(struct snd_soc_card *card,
 +                       struct snd_soc_dai_link *dai_link)
  {
        struct snd_soc_pcm_runtime *rtd;
  
        for_each_card_rtds(card, rtd) {
 -              if (rtd->dai_link->no_pcm &&
 -                      !strcmp(rtd->dai_link->name, dai_link))
 -                      return rtd->pcm->streams[stream].substream;
 +              if (rtd->dai_link == dai_link)
 +                      return rtd;
        }
 -      dev_dbg(card->dev, "ASoC: failed to find dai link %s\n", dai_link);
 +      dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link->name);
        return NULL;
  }
 -EXPORT_SYMBOL_GPL(snd_soc_get_dai_substream);
 -
 -static const struct snd_soc_ops null_snd_soc_ops;
 +EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
  
  static void soc_release_rtd_dev(struct device *dev)
  {
@@@ -419,7 -419,8 +419,8 @@@ static void soc_free_pcm_runtime(struc
  
        list_del(&rtd->list);
  
-       flush_delayed_work(&rtd->delayed_work);
+       if (delayed_work_pending(&rtd->delayed_work))
+               flush_delayed_work(&rtd->delayed_work);
        snd_soc_pcm_component_free(rtd);
  
        /*
        device_unregister(rtd->dev);
  }
  
+ static void close_delayed_work(struct work_struct *work) {
+       struct snd_soc_pcm_runtime *rtd =
+                       container_of(work, struct snd_soc_pcm_runtime,
+                                    delayed_work.work);
+       if (rtd->close_delayed_work_func)
+               rtd->close_delayed_work_func(rtd);
+ }
  static struct snd_soc_pcm_runtime *soc_new_pcm_runtime(
        struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
  {
  
        rtd->dev = dev;
        dev_set_drvdata(dev, rtd);
+       INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
  
        /*
         * for rtd->codec_dais
@@@ -506,6 -517,20 +517,6 @@@ free_rtd
        return NULL;
  }
  
 -struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
 -              const char *dai_link)
 -{
 -      struct snd_soc_pcm_runtime *rtd;
 -
 -      for_each_card_rtds(card, rtd) {
 -              if (!strcmp(rtd->dai_link->name, dai_link))
 -                      return rtd;
 -      }
 -      dev_dbg(card->dev, "ASoC: failed to find rtd %s\n", dai_link);
 -      return NULL;
 -}
 -EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
 -
  static void snd_soc_flush_all_delayed_work(struct snd_soc_card *card)
  {
        struct snd_soc_pcm_runtime *rtd;
@@@ -901,6 -926,47 +912,6 @@@ struct snd_soc_dai *snd_soc_find_dai
  }
  EXPORT_SYMBOL_GPL(snd_soc_find_dai);
  
 -/**
 - * snd_soc_find_dai_link - Find a DAI link
 - *
 - * @card: soc card
 - * @id: DAI link ID to match
 - * @name: DAI link name to match, optional
 - * @stream_name: DAI link stream name to match, optional
 - *
 - * This function will search all existing DAI links of the soc card to
 - * find the link of the same ID. Since DAI links may not have their
 - * unique ID, so name and stream name should also match if being
 - * specified.
 - *
 - * Return: pointer of DAI link, or NULL if not found.
 - */
 -struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
 -                                             int id, const char *name,
 -                                             const char *stream_name)
 -{
 -      struct snd_soc_dai_link *link;
 -
 -      lockdep_assert_held(&client_mutex);
 -
 -      for_each_card_links(card, link) {
 -              if (link->id != id)
 -                      continue;
 -
 -              if (name && (!link->name || strcmp(name, link->name)))
 -                      continue;
 -
 -              if (stream_name && (!link->stream_name
 -                      || strcmp(stream_name, link->stream_name)))
 -                      continue;
 -
 -              return link;
 -      }
 -
 -      return NULL;
 -}
 -EXPORT_SYMBOL_GPL(snd_soc_find_dai_link);
 -
  static int soc_dai_link_sanity_check(struct snd_soc_card *card,
                                     struct snd_soc_dai_link *link)
  {
  }
  
  /**
 - * snd_soc_remove_dai_link - Remove a DAI link from the list
 - * @card: The ASoC card that owns the link
 - * @dai_link: The DAI link to remove
 + * snd_soc_remove_pcm_runtime - Remove a pcm_runtime from card
 + * @card: The ASoC card to which the pcm_runtime has
 + * @rtd: The pcm_runtime to remove
   *
 - * This function removes a DAI link from the ASoC card's link list.
 - *
 - * For DAI links previously added by topology, topology should
 - * remove them by using the dobj embedded in the link.
 + * This function removes a pcm_runtime from the ASoC card.
   */
 -void snd_soc_remove_dai_link(struct snd_soc_card *card,
 -                           struct snd_soc_dai_link *dai_link)
 +void snd_soc_remove_pcm_runtime(struct snd_soc_card *card,
 +                              struct snd_soc_pcm_runtime *rtd)
  {
 -      struct snd_soc_pcm_runtime *rtd;
 -
        lockdep_assert_held(&client_mutex);
  
        /*
         * Notify the machine driver for extra destruction
         */
        if (card->remove_dai_link)
 -              card->remove_dai_link(card, dai_link);
 +              card->remove_dai_link(card, rtd->dai_link);
  
 -      list_del(&dai_link->list);
 -
 -      rtd = snd_soc_get_pcm_runtime(card, dai_link->name);
 -      if (rtd)
 -              soc_free_pcm_runtime(rtd);
 +      soc_free_pcm_runtime(rtd);
  }
 -EXPORT_SYMBOL_GPL(snd_soc_remove_dai_link);
 +EXPORT_SYMBOL_GPL(snd_soc_remove_pcm_runtime);
  
  /**
 - * snd_soc_add_dai_link - Add a DAI link dynamically
 - * @card: The ASoC card to which the DAI link is added
 - * @dai_link: The new DAI link to add
 + * snd_soc_add_pcm_runtime - Add a pcm_runtime dynamically via dai_link
 + * @card: The ASoC card to which the pcm_runtime is added
 + * @dai_link: The DAI link to find pcm_runtime
   *
 - * This function adds a DAI link to the ASoC card's link list.
 + * This function adds a pcm_runtime ASoC card by using dai_link.
   *
 - * Note: Topology can use this API to add DAI links when probing the
 + * Note: Topology can use this API to add pcm_runtime when probing the
   * topology component. And machine drivers can still define static
   * DAI links in dai_link array.
   */
 -int snd_soc_add_dai_link(struct snd_soc_card *card,
 -                       struct snd_soc_dai_link *dai_link)
 +int snd_soc_add_pcm_runtime(struct snd_soc_card *card,
 +                          struct snd_soc_dai_link *dai_link)
  {
        struct snd_soc_pcm_runtime *rtd;
        struct snd_soc_dai_link_component *codec, *platform;
                }
        }
  
 -      /* see for_each_card_links */
 -      list_add_tail(&dai_link->list, &card->dai_link_list);
 -
        return 0;
  
  _err_defer:
 -      soc_free_pcm_runtime(rtd);
 +      snd_soc_remove_pcm_runtime(card, rtd);
        return -EPROBE_DEFER;
  }
 -EXPORT_SYMBOL_GPL(snd_soc_add_dai_link);
 +EXPORT_SYMBOL_GPL(snd_soc_add_pcm_runtime);
 +
 +static int soc_dai_pcm_new(struct snd_soc_dai **dais, int num_dais,
 +                         struct snd_soc_pcm_runtime *rtd)
 +{
 +      int i, ret = 0;
 +
 +      for (i = 0; i < num_dais; ++i) {
 +              struct snd_soc_dai_driver *drv = dais[i]->driver;
 +
 +              if (drv->pcm_new)
 +                      ret = drv->pcm_new(rtd, dais[i]);
 +              if (ret < 0) {
 +                      dev_err(dais[i]->dev,
 +                              "ASoC: Failed to bind %s with pcm device\n",
 +                              dais[i]->name);
 +                      return ret;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static int soc_init_pcm_runtime(struct snd_soc_card *card,
 +                              struct snd_soc_pcm_runtime *rtd)
 +{
 +      struct snd_soc_dai_link *dai_link = rtd->dai_link;
 +      struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 +      struct snd_soc_rtdcom_list *rtdcom;
 +      struct snd_soc_component *component;
 +      int ret, num;
 +
 +      /* set default power off timeout */
 +      rtd->pmdown_time = pmdown_time;
 +
 +      /* do machine specific initialization */
 +      if (dai_link->init) {
 +              ret = dai_link->init(rtd);
 +              if (ret < 0) {
 +                      dev_err(card->dev, "ASoC: failed to init %s: %d\n",
 +                              dai_link->name, ret);
 +                      return ret;
 +              }
 +      }
 +
 +      if (dai_link->dai_fmt) {
 +              ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
 +              if (ret)
 +                      return ret;
 +      }
 +
 +      /* add DPCM sysfs entries */
 +      soc_dpcm_debugfs_add(rtd);
 +
 +      num = rtd->num;
 +
 +      /*
 +       * most drivers will register their PCMs using DAI link ordering but
 +       * topology based drivers can use the DAI link id field to set PCM
 +       * device number and then use rtd + a base offset of the BEs.
 +       */
 +      for_each_rtd_components(rtd, rtdcom, component) {
 +              if (!component->driver->use_dai_pcm_id)
 +                      continue;
 +
 +              if (rtd->dai_link->no_pcm)
 +                      num += component->driver->be_pcm_base;
 +              else
 +                      num = rtd->dai_link->id;
 +      }
 +
 +      /* create compress_device if possible */
 +      ret = snd_soc_dai_compress_new(cpu_dai, rtd, num);
 +      if (ret != -ENOTSUPP) {
 +              if (ret < 0)
 +                      dev_err(card->dev, "ASoC: can't create compress %s\n",
 +                              dai_link->stream_name);
 +              return ret;
 +      }
 +
 +      /* create the pcm */
 +      ret = soc_new_pcm(rtd, num);
 +      if (ret < 0) {
 +              dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
 +                      dai_link->stream_name, ret);
 +              return ret;
 +      }
 +      ret = soc_dai_pcm_new(&cpu_dai, 1, rtd);
 +      if (ret < 0)
 +              return ret;
 +      ret = soc_dai_pcm_new(rtd->codec_dais,
 +                            rtd->num_codecs, rtd);
 +      return ret;
 +}
  
  static void soc_set_of_name_prefix(struct snd_soc_component *component)
  {
  static void soc_set_name_prefix(struct snd_soc_card *card,
                                struct snd_soc_component *component)
  {
 +      struct device_node *of_node = soc_component_to_node(component);
        int i;
  
 -      for (i = 0; i < card->num_configs && card->codec_conf; i++) {
 +      for (i = 0; i < card->num_configs; i++) {
                struct snd_soc_codec_conf *map = &card->codec_conf[i];
 -              struct device_node *of_node = soc_component_to_node(component);
  
                if (map->of_node && of_node != map->of_node)
                        continue;
@@@ -1472,6 -1457,111 +1483,6 @@@ static int soc_probe_link_components(st
        return 0;
  }
  
 -void snd_soc_disconnect_sync(struct device *dev)
 -{
 -      struct snd_soc_component *component =
 -                      snd_soc_lookup_component(dev, NULL);
 -
 -      if (!component || !component->card)
 -              return;
 -
 -      snd_card_disconnect_sync(component->card->snd_card);
 -}
 -EXPORT_SYMBOL_GPL(snd_soc_disconnect_sync);
 -
 -static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais,
 -                              struct snd_soc_pcm_runtime *rtd)
 -{
 -      int i, ret = 0;
 -
 -      for (i = 0; i < num_dais; ++i) {
 -              struct snd_soc_dai_driver *drv = dais[i]->driver;
 -
 -              if (drv->pcm_new)
 -                      ret = drv->pcm_new(rtd, dais[i]);
 -              if (ret < 0) {
 -                      dev_err(dais[i]->dev,
 -                              "ASoC: Failed to bind %s with pcm device\n",
 -                              dais[i]->name);
 -                      return ret;
 -              }
 -      }
 -
 -      return 0;
 -}
 -
 -static int soc_link_init(struct snd_soc_card *card,
 -                       struct snd_soc_pcm_runtime *rtd)
 -{
 -      struct snd_soc_dai_link *dai_link = rtd->dai_link;
 -      struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 -      struct snd_soc_rtdcom_list *rtdcom;
 -      struct snd_soc_component *component;
 -      int ret, num;
 -
 -      /* set default power off timeout */
 -      rtd->pmdown_time = pmdown_time;
 -
 -      /* do machine specific initialization */
 -      if (dai_link->init) {
 -              ret = dai_link->init(rtd);
 -              if (ret < 0) {
 -                      dev_err(card->dev, "ASoC: failed to init %s: %d\n",
 -                              dai_link->name, ret);
 -                      return ret;
 -              }
 -      }
 -
 -      if (dai_link->dai_fmt) {
 -              ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
 -              if (ret)
 -                      return ret;
 -      }
 -
 -      /* add DPCM sysfs entries */
 -      soc_dpcm_debugfs_add(rtd);
 -
 -      num = rtd->num;
 -
 -      /*
 -       * most drivers will register their PCMs using DAI link ordering but
 -       * topology based drivers can use the DAI link id field to set PCM
 -       * device number and then use rtd + a base offset of the BEs.
 -       */
 -      for_each_rtd_components(rtd, rtdcom, component) {
 -              if (!component->driver->use_dai_pcm_id)
 -                      continue;
 -
 -              if (rtd->dai_link->no_pcm)
 -                      num += component->driver->be_pcm_base;
 -              else
 -                      num = rtd->dai_link->id;
 -      }
 -
 -      /* create compress_device if possible */
 -      ret = snd_soc_dai_compress_new(cpu_dai, rtd, num);
 -      if (ret != -ENOTSUPP) {
 -              if (ret < 0)
 -                      dev_err(card->dev, "ASoC: can't create compress %s\n",
 -                                       dai_link->stream_name);
 -              return ret;
 -      }
 -
 -      /* create the pcm */
 -      ret = soc_new_pcm(rtd, num);
 -      if (ret < 0) {
 -              dev_err(card->dev, "ASoC: can't create pcm %s :%d\n",
 -                      dai_link->stream_name, ret);
 -              return ret;
 -      }
 -      ret = soc_link_dai_pcm_new(&cpu_dai, 1, rtd);
 -      if (ret < 0)
 -              return ret;
 -      ret = soc_link_dai_pcm_new(rtd->codec_dais,
 -                                 rtd->num_codecs, rtd);
 -      return ret;
 -}
 -
  static void soc_unbind_aux_dev(struct snd_soc_card *card)
  {
        struct snd_soc_component *component, *_component;
@@@ -1852,7 -1942,7 +1863,7 @@@ static void __soc_setup_card_name(char 
  static void soc_cleanup_card_resources(struct snd_soc_card *card,
                                       int card_probed)
  {
 -      struct snd_soc_dai_link *link, *_link;
 +      struct snd_soc_pcm_runtime *rtd, *n;
  
        if (card->snd_card)
                snd_card_disconnect_sync(card->snd_card);
        soc_remove_link_dais(card);
        soc_remove_link_components(card);
  
 -      for_each_card_links_safe(card, link, _link)
 -              snd_soc_remove_dai_link(card, link);
 +      for_each_card_rtds_safe(card, rtd, n)
 +              snd_soc_remove_pcm_runtime(card, rtd);
  
        /* remove auxiliary devices */
        soc_remove_aux_devices(card);
@@@ -1922,7 -2012,7 +1933,7 @@@ static int snd_soc_bind_card(struct snd
        /* add predefined DAI links to the list */
        card->num_rtd = 0;
        for_each_card_prelinks(card, i, dai_link) {
 -              ret = snd_soc_add_dai_link(card, dai_link);
 +              ret = snd_soc_add_pcm_runtime(card, dai_link);
                if (ret < 0)
                        goto probe_end;
        }
                goto probe_end;
        }
  
 -      for_each_card_rtds(card, rtd)
 -              soc_link_init(card, rtd);
 +      for_each_card_rtds(card, rtd) {
 +              ret = soc_init_pcm_runtime(card, rtd);
 +              if (ret < 0)
 +                      goto probe_end;
 +      }
  
        snd_soc_dapm_link_dai_widgets(card);
        snd_soc_dapm_connect_dai_link_widgets(card);
@@@ -2317,6 -2404,7 +2328,6 @@@ int snd_soc_register_card(struct snd_so
        INIT_LIST_HEAD(&card->aux_comp_list);
        INIT_LIST_HEAD(&card->component_dev_list);
        INIT_LIST_HEAD(&card->list);
 -      INIT_LIST_HEAD(&card->dai_link_list);
        INIT_LIST_HEAD(&card->rtd_list);
        INIT_LIST_HEAD(&card->dapm_dirty);
        INIT_LIST_HEAD(&card->dobj_list);
@@@ -2421,8 -2509,6 +2432,8 @@@ EXPORT_SYMBOL_GPL(snd_soc_unregister_da
   *
   * @component: The component the DAIs are registered for
   * @dai_drv: DAI driver to use for the DAI
 + * @legacy_dai_naming: if %true, use legacy single-name format;
 + *    if %false, use multiple-name format;
   *
   * Topology can use this API to register DAIs when probing a component.
   * These DAIs's widgets will be freed in the card cleanup and the DAIs
diff --combined sound/soc/soc-pcm.c
@@@ -637,10 -637,8 +637,8 @@@ out
   * This is to ensure there are no pops or clicks in between any music tracks
   * due to DAPM power cycling.
   */
- static void close_delayed_work(struct work_struct *work)
+ static void close_delayed_work(struct snd_soc_pcm_runtime *rtd)
  {
-       struct snd_soc_pcm_runtime *rtd =
-                       container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
        struct snd_soc_dai *codec_dai = rtd->codec_dais[0];
  
        mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass);
        mutex_unlock(&rtd->card->pcm_mutex);
  }
  
- static void codec2codec_close_delayed_work(struct work_struct *work)
+ static void codec2codec_close_delayed_work(struct snd_soc_pcm_runtime *rtd)
  {
        /*
         * Currently nothing to do for c2c links
@@@ -2974,10 -2972,9 +2972,9 @@@ int soc_new_pcm(struct snd_soc_pcm_runt
  
        /* DAPM dai link stream work */
        if (rtd->dai_link->params)
-               INIT_DELAYED_WORK(&rtd->delayed_work,
-                                 codec2codec_close_delayed_work);
+               rtd->close_delayed_work_func = codec2codec_close_delayed_work;
        else
-               INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+               rtd->close_delayed_work_func = close_delayed_work;
  
        pcm->nonatomic = rtd->dai_link->nonatomic;
        rtd->pcm = pcm;
                rtd->ops.hw_free        = dpcm_fe_dai_hw_free;
                rtd->ops.close          = dpcm_fe_dai_close;
                rtd->ops.pointer        = soc_pcm_pointer;
 -              rtd->ops.ioctl          = snd_soc_pcm_component_ioctl;
        } else {
                rtd->ops.open           = soc_pcm_open;
                rtd->ops.hw_params      = soc_pcm_hw_params;
                rtd->ops.hw_free        = soc_pcm_hw_free;
                rtd->ops.close          = soc_pcm_close;
                rtd->ops.pointer        = soc_pcm_pointer;
 -              rtd->ops.ioctl          = snd_soc_pcm_component_ioctl;
        }
  
        for_each_rtd_components(rtd, rtdcom, component) {
                const struct snd_soc_component_driver *drv = component->driver;
  
 +              if (drv->ioctl)
 +                      rtd->ops.ioctl          = snd_soc_pcm_component_ioctl;
 +              if (drv->sync_stop)
 +                      rtd->ops.sync_stop      = snd_soc_pcm_component_sync_stop;
                if (drv->copy_user)
                        rtd->ops.copy_user      = snd_soc_pcm_component_copy_user;
                if (drv->page)
diff --combined sound/soc/soc-topology.c
@@@ -553,9 -553,7 +553,9 @@@ static void remove_link(struct snd_soc_
        kfree(link->cpus->dai_name);
  
        list_del(&dobj->list);
 -      snd_soc_remove_dai_link(comp->card, link);
 +
 +      snd_soc_remove_pcm_runtime(comp->card,
 +                      snd_soc_get_pcm_runtime(comp->card, link));
        kfree(link);
  }
  
@@@ -1935,11 -1933,13 +1935,13 @@@ static int soc_tplg_fe_link_create(stru
        ret = soc_tplg_dai_link_load(tplg, link, NULL);
        if (ret < 0) {
                dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n");
-               kfree(link->name);
-               kfree(link->stream_name);
-               kfree(link->cpus->dai_name);
-               kfree(link);
-               return ret;
+               goto err;
+       }
 -      ret = snd_soc_add_dai_link(tplg->comp->card, link);
++      ret = snd_soc_add_pcm_runtime(tplg->comp->card, link);
+       if (ret < 0) {
+               dev_err(tplg->comp->dev, "ASoC: adding FE link failed\n");
+               goto err;
        }
  
        link->dobj.index = tplg->index;
        link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
        list_add(&link->dobj.list, &tplg->comp->dobj_list);
  
-       snd_soc_add_pcm_runtime(tplg->comp->card, link);
        return 0;
+ err:
+       kfree(link->name);
+       kfree(link->stream_name);
+       kfree(link->cpus->dai_name);
+       kfree(link);
+       return ret;
  }
  
  /* create a FE DAI and DAI link from the PCM object */
@@@ -2041,6 -2046,7 +2048,7 @@@ static int soc_tplg_pcm_elems_load(stru
        int size;
        int i;
        bool abi_match;
+       int ret;
  
        count = le32_to_cpu(hdr->count);
  
                }
  
                /* create the FE DAIs and DAI links */
-               soc_tplg_pcm_create(tplg, _pcm);
+               ret = soc_tplg_pcm_create(tplg, _pcm);
+               if (ret < 0) {
+                       if (!abi_match)
+                               kfree(_pcm);
+                       return ret;
+               }
  
                /* offset by version-specific struct size and
                 * real priv data size
@@@ -2209,47 -2220,6 +2222,47 @@@ static int link_new_ver(struct soc_tpl
        return 0;
  }
  
 +/**
 + * snd_soc_find_dai_link - Find a DAI link
 + *
 + * @card: soc card
 + * @id: DAI link ID to match
 + * @name: DAI link name to match, optional
 + * @stream_name: DAI link stream name to match, optional
 + *
 + * This function will search all existing DAI links of the soc card to
 + * find the link of the same ID. Since DAI links may not have their
 + * unique ID, so name and stream name should also match if being
 + * specified.
 + *
 + * Return: pointer of DAI link, or NULL if not found.
 + */
 +static struct snd_soc_dai_link *snd_soc_find_dai_link(struct snd_soc_card *card,
 +                                                    int id, const char *name,
 +                                                    const char *stream_name)
 +{
 +      struct snd_soc_pcm_runtime *rtd;
 +      struct snd_soc_dai_link *link;
 +
 +      for_each_card_rtds(card, rtd) {
 +              link = rtd->dai_link;
 +
 +              if (link->id != id)
 +                      continue;
 +
 +              if (name && (!link->name || strcmp(name, link->name)))
 +                      continue;
 +
 +              if (stream_name && (!link->stream_name
 +                                  || strcmp(stream_name, link->stream_name)))
 +                      continue;
 +
 +              return link;
 +      }
 +
 +      return NULL;
 +}
 +
  /* Find and configure an existing physical DAI link */
  static int soc_tplg_link_config(struct soc_tplg *tplg,
        struct snd_soc_tplg_link_config *cfg)
@@@ -17,8 -17,6 +17,8 @@@
  #include <sound/sof/xtensa.h>
  #include "../ops.h"
  #include "shim.h"
 +#include "../sof-audio.h"
 +#include "../../intel/common/soc-intel-quirks.h"
  
  /* DSP memories */
  #define IRAM_OFFSET           0x0C0000
@@@ -26,7 -24,8 +26,8 @@@
  #define DRAM_OFFSET           0x100000
  #define DRAM_SIZE             (160 * 1024)
  #define SHIM_OFFSET           0x140000
- #define SHIM_SIZE             0x100
+ #define SHIM_SIZE_BYT         0x100
+ #define SHIM_SIZE_CHT         0x118
  #define MBOX_OFFSET           0x144000
  #define MBOX_SIZE             0x1000
  #define EXCEPT_OFFSET         0x800
@@@ -77,7 -76,7 +78,7 @@@ static const struct snd_sof_debugfs_ma
         SOF_DEBUGFS_ACCESS_D0_ONLY},
        {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
         SOF_DEBUGFS_ACCESS_D0_ONLY},
-       {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE,
+       {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_BYT,
         SOF_DEBUGFS_ACCESS_ALWAYS},
  };
  
@@@ -104,7 -103,7 +105,7 @@@ static const struct snd_sof_debugfs_ma
         SOF_DEBUGFS_ACCESS_D0_ONLY},
        {"dram", BYT_DSP_BAR, DRAM_OFFSET, DRAM_SIZE,
         SOF_DEBUGFS_ACCESS_D0_ONLY},
-       {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE,
+       {"shim", BYT_DSP_BAR, SHIM_OFFSET, SHIM_SIZE_CHT,
         SOF_DEBUGFS_ACCESS_ALWAYS},
  };
  
@@@ -147,33 -146,33 +148,33 @@@ static void byt_dump(struct snd_sof_de
        struct sof_ipc_dsp_oops_xtensa xoops;
        struct sof_ipc_panic_info panic_info;
        u32 stack[BYT_STACK_DUMP_SIZE];
-       u32 status, panic, imrd, imrx;
+       u64 status, panic, imrd, imrx;
  
        /* now try generic SOF status messages */
-       status = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCD);
-       panic = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IPCX);
+       status = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCD);
+       panic = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IPCX);
        byt_get_registers(sdev, &xoops, &panic_info, stack,
                          BYT_STACK_DUMP_SIZE);
        snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack,
                           BYT_STACK_DUMP_SIZE);
  
        /* provide some context for firmware debug */
-       imrx = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IMRX);
-       imrd = snd_sof_dsp_read(sdev, BYT_DSP_BAR, SHIM_IMRD);
+       imrx = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRX);
+       imrd = snd_sof_dsp_read64(sdev, BYT_DSP_BAR, SHIM_IMRD);
        dev_err(sdev->dev,
-               "error: ipc host -> DSP: pending %s complete %s raw 0x%8.8x\n",
+               "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",
                (panic & SHIM_IPCX_BUSY) ? "yes" : "no",
                (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
        dev_err(sdev->dev,
-               "error: mask host: pending %s complete %s raw 0x%8.8x\n",
+               "error: mask host: pending %s complete %s raw 0x%llx\n",
                (imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
                (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
        dev_err(sdev->dev,
-               "error: ipc DSP -> host: pending %s complete %s raw 0x%8.8x\n",
+               "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",
                (status & SHIM_IPCD_BUSY) ? "yes" : "no",
                (status & SHIM_IPCD_DONE) ? "yes" : "no", status);
        dev_err(sdev->dev,
-               "error: mask DSP: pending %s complete %s raw 0x%8.8x\n",
+               "error: mask DSP: pending %s complete %s raw 0x%llx\n",
                (imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
                (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
  
@@@ -384,76 -383,6 +385,76 @@@ static int byt_reset(struct snd_sof_de
        return 0;
  }
  
 +static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
 +                                 const char *sof_tplg_filename,
 +                                 const char *ssp_str)
 +{
 +      const char *tplg_filename = NULL;
 +      char *filename;
 +      char *split_ext;
 +
 +      filename = devm_kstrdup(sdev->dev, sof_tplg_filename, GFP_KERNEL);
 +      if (!filename)
 +              return NULL;
 +
 +      /* this assumes a .tplg extension */
 +      split_ext = strsep(&filename, ".");
 +      if (split_ext) {
 +              tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
 +                                             "%s-%s.tplg",
 +                                             split_ext, ssp_str);
 +              if (!tplg_filename)
 +                      return NULL;
 +      }
 +      return tplg_filename;
 +}
 +
 +static void byt_machine_select(struct snd_sof_dev *sdev)
 +{
 +      struct snd_sof_pdata *sof_pdata = sdev->pdata;
 +      const struct sof_dev_desc *desc = sof_pdata->desc;
 +      struct snd_soc_acpi_mach *mach;
 +      struct platform_device *pdev;
 +      const char *tplg_filename;
 +
 +      mach = snd_soc_acpi_find_machine(desc->machines);
 +      if (!mach) {
 +              dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
 +              return;
 +      }
 +
 +      pdev = to_platform_device(sdev->dev);
 +      if (soc_intel_is_byt_cr(pdev)) {
 +              dev_dbg(sdev->dev,
 +                      "BYT-CR detected, SSP0 used instead of SSP2\n");
 +
 +              tplg_filename = fixup_tplg_name(sdev,
 +                                              mach->sof_tplg_filename,
 +                                              "ssp0");
 +      } else {
 +              tplg_filename = mach->sof_tplg_filename;
 +      }
 +
 +      if (!tplg_filename) {
 +              dev_dbg(sdev->dev,
 +                      "error: no topology filename\n");
 +              return;
 +      }
 +
 +      sof_pdata->tplg_filename = tplg_filename;
 +      mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
 +      sof_pdata->machine = mach;
 +}
 +
 +static void byt_set_mach_params(const struct snd_soc_acpi_mach *mach,
 +                              struct device *dev)
 +{
 +      struct snd_soc_acpi_mach_params *mach_params;
 +
 +      mach_params = (struct snd_soc_acpi_mach_params *)&mach->mach_params;
 +      mach_params->platform = dev_name(dev);
 +}
 +
  /* Baytrail DAIs */
  static struct snd_soc_dai_driver byt_dai[] = {
  {
@@@ -586,12 -515,6 +587,12 @@@ const struct snd_sof_dsp_ops sof_tng_op
        .ipc_msg_data   = intel_ipc_msg_data,
        .ipc_pcm_params = intel_ipc_pcm_params,
  
 +      /* machine driver */
 +      .machine_select = byt_machine_select,
 +      .machine_register = sof_machine_register,
 +      .machine_unregister = sof_machine_unregister,
 +      .set_mach_params = byt_set_mach_params,
 +
        /* debug */
        .debug_map      = byt_debugfs,
        .debug_map_count        = ARRAY_SIZE(byt_debugfs),
@@@ -760,12 -683,6 +761,12 @@@ const struct snd_sof_dsp_ops sof_byt_op
        .ipc_msg_data   = intel_ipc_msg_data,
        .ipc_pcm_params = intel_ipc_pcm_params,
  
 +      /* machine driver */
 +      .machine_select = byt_machine_select,
 +      .machine_register = sof_machine_register,
 +      .machine_unregister = sof_machine_unregister,
 +      .set_mach_params = byt_set_mach_params,
 +
        /* debug */
        .debug_map      = byt_debugfs,
        .debug_map_count        = ARRAY_SIZE(byt_debugfs),
@@@ -832,12 -749,6 +833,12 @@@ const struct snd_sof_dsp_ops sof_cht_op
        .ipc_msg_data   = intel_ipc_msg_data,
        .ipc_pcm_params = intel_ipc_pcm_params,
  
 +      /* machine driver */
 +      .machine_select = byt_machine_select,
 +      .machine_register = sof_machine_register,
 +      .machine_unregister = sof_machine_unregister,
 +      .set_mach_params = byt_set_mach_params,
 +
        /* debug */
        .debug_map      = cht_debugfs,
        .debug_map_count        = ARRAY_SIZE(cht_debugfs),
diff --combined sound/soc/sof/topology.c
@@@ -13,7 -13,6 +13,7 @@@
  #include <sound/pcm_params.h>
  #include <uapi/sound/sof/tokens.h>
  #include "sof-priv.h"
 +#include "sof-audio.h"
  #include "ops.h"
  
  #define COMP_ID_UNASSIGNED            0xffffffff
@@@ -54,8 -53,7 +54,8 @@@ struct sof_widget_data 
  static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir)
  {
        struct sof_ipc_pcm_params_reply ipc_params_reply;
 -      struct snd_sof_dev *sdev = swidget->sdev;
 +      struct snd_soc_component *scomp = swidget->scomp;
 +      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc_pcm_params pcm;
        struct snd_pcm_hw_params *params;
        struct snd_sof_pcm *spcm;
@@@ -64,9 -62,9 +64,9 @@@
        memset(&pcm, 0, sizeof(pcm));
  
        /* get runtime PCM params using widget's stream name */
 -      spcm = snd_sof_find_spcm_name(sdev, swidget->widget->sname);
 +      spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
        if (!spcm) {
 -              dev_err(sdev->dev, "error: cannot find PCM for %s\n",
 +              dev_err(scomp->dev, "error: cannot find PCM for %s\n",
                        swidget->widget->name);
                return -EINVAL;
        }
        ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
                                 &ipc_params_reply, sizeof(ipc_params_reply));
        if (ret < 0)
 -              dev_err(sdev->dev, "error: pcm params failed for %s\n",
 +              dev_err(scomp->dev, "error: pcm params failed for %s\n",
                        swidget->widget->name);
  
        return ret;
   /* send stream trigger ipc */
  static int ipc_trigger(struct snd_sof_widget *swidget, int cmd)
  {
 -      struct snd_sof_dev *sdev = swidget->sdev;
 +      struct snd_soc_component *scomp = swidget->scomp;
 +      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct sof_ipc_stream stream;
        struct sof_ipc_reply reply;
        int ret = 0;
        ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream,
                                 sizeof(stream), &reply, sizeof(reply));
        if (ret < 0)
 -              dev_err(sdev->dev, "error: failed to trigger %s\n",
 +              dev_err(scomp->dev, "error: failed to trigger %s\n",
                        swidget->widget->name);
  
        return ret;
@@@ -138,23 -135,23 +138,23 @@@ static int sof_keyword_dapm_event(struc
                                  struct snd_kcontrol *k, int event)
  {
        struct snd_sof_widget *swidget = w->dobj.private;
 +      struct snd_soc_component *scomp;
        int stream = SNDRV_PCM_STREAM_CAPTURE;
 -      struct snd_sof_dev *sdev;
        struct snd_sof_pcm *spcm;
        int ret = 0;
  
        if (!swidget)
                return 0;
  
 -      sdev = swidget->sdev;
 +      scomp = swidget->scomp;
  
 -      dev_dbg(sdev->dev, "received event %d for widget %s\n",
 +      dev_dbg(scomp->dev, "received event %d for widget %s\n",
                event, w->name);
  
        /* get runtime PCM params using widget's stream name */
 -      spcm = snd_sof_find_spcm_name(sdev, swidget->widget->sname);
 +      spcm = snd_sof_find_spcm_name(scomp, swidget->widget->sname);
        if (!spcm) {
 -              dev_err(sdev->dev, "error: cannot find PCM for %s\n",
 +              dev_err(scomp->dev, "error: cannot find PCM for %s\n",
                        swidget->widget->name);
                return -EINVAL;
        }
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
                if (spcm->stream[stream].suspend_ignored) {
 -                      dev_dbg(sdev->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n");
 +                      dev_dbg(scomp->dev, "PRE_PMU event ignored, KWD pipeline is already RUNNING\n");
                        return 0;
                }
  
                /* set pcm params */
                ret = ipc_pcm_params(swidget, stream);
                if (ret < 0) {
 -                      dev_err(sdev->dev,
 +                      dev_err(scomp->dev,
                                "error: failed to set pcm params for widget %s\n",
                                swidget->widget->name);
                        break;
                /* start trigger */
                ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_START);
                if (ret < 0)
 -                      dev_err(sdev->dev,
 +                      dev_err(scomp->dev,
                                "error: failed to trigger widget %s\n",
                                swidget->widget->name);
                break;
        case SND_SOC_DAPM_POST_PMD:
                if (spcm->stream[stream].suspend_ignored) {
 -                      dev_dbg(sdev->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n");
 +                      dev_dbg(scomp->dev, "POST_PMD even ignored, KWD pipeline will remain RUNNING\n");
                        return 0;
                }
  
                /* stop trigger */
                ret = ipc_trigger(swidget, SOF_IPC_STREAM_TRIG_STOP);
                if (ret < 0)
 -                      dev_err(sdev->dev,
 +                      dev_err(scomp->dev,
                                "error: failed to trigger widget %s\n",
                                swidget->widget->name);
  
                /* pcm free */
                ret = ipc_trigger(swidget, SOF_IPC_STREAM_PCM_FREE);
                if (ret < 0)
 -                      dev_err(sdev->dev,
 +                      dev_err(scomp->dev,
                                "error: failed to trigger widget %s\n",
                                swidget->widget->name);
                break;
@@@ -573,20 -570,6 +573,20 @@@ static const struct sof_topology_token 
                offsetof(struct sof_ipc_comp_src, sink_rate), 0},
  };
  
 +/* ASRC */
 +static const struct sof_topology_token asrc_tokens[] = {
 +      {SOF_TKN_ASRC_RATE_IN, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
 +              offsetof(struct sof_ipc_comp_asrc, source_rate), 0},
 +      {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
 +              offsetof(struct sof_ipc_comp_asrc, sink_rate), 0},
 +      {SOF_TKN_ASRC_ASYNCHRONOUS_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
 +              get_token_u32,
 +              offsetof(struct sof_ipc_comp_asrc, asynchronous_mode), 0},
 +      {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD,
 +              get_token_u32,
 +              offsetof(struct sof_ipc_comp_asrc, operation_mode), 0},
 +};
 +
  /* Tone */
  static const struct sof_topology_token tone_tokens[] = {
  };
@@@ -855,7 -838,7 +855,7 @@@ static void sof_parse_word_tokens(struc
  
                                /* check if array index is valid */
                                if (!index || *index == 0) {
 -                                      dev_err(sdev->dev,
 +                                      dev_err(scomp->dev,
                                                "error: invalid array offset\n");
                                        continue;
                                } else {
@@@ -883,6 -866,7 +883,6 @@@ static int sof_parse_tokens(struct snd_
                            struct snd_soc_tplg_vendor_array *array,
                            int priv_size)
  {
 -      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        int asize;
  
        while (priv_size > 0) {
  
                /* validate asize */
                if (asize < 0) { /* FIXME: A zero-size array makes no sense */
 -                      dev_err(sdev->dev, "error: invalid array size 0x%x\n",
 +                      dev_err(scomp->dev, "error: invalid array size 0x%x\n",
                                asize);
                        return -EINVAL;
                }
                /* make sure there is enough data before parsing */
                priv_size -= asize;
                if (priv_size < 0) {
 -                      dev_err(sdev->dev, "error: invalid array size 0x%x\n",
 +                      dev_err(scomp->dev, "error: invalid array size 0x%x\n",
                                asize);
                        return -EINVAL;
                }
                                              array);
                        break;
                default:
 -                      dev_err(sdev->dev, "error: unknown token type %d\n",
 +                      dev_err(scomp->dev, "error: unknown token type %d\n",
                                array->type);
                        return -EINVAL;
                }
  static void sof_dbg_comp_config(struct snd_soc_component *scomp,
                                struct sof_ipc_comp_config *config)
  {
 -      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
 -
 -      dev_dbg(sdev->dev, " config: periods snk %d src %d fmt %d\n",
 +      dev_dbg(scomp->dev, " config: periods snk %d src %d fmt %d\n",
                config->periods_sink, config->periods_source,
                config->frame_fmt);
  }
@@@ -988,7 -974,7 +988,7 @@@ static int sof_control_load_volume(stru
  
        /* extract tlv data */
        if (get_tlv_data(kc->tlv.p, tlv) < 0) {
 -              dev_err(sdev->dev, "error: invalid TLV data\n");
 +              dev_err(scomp->dev, "error: invalid TLV data\n");
                ret = -EINVAL;
                goto out_free;
        }
        /* set up volume table */
        ret = set_up_volume_table(scontrol, tlv, le32_to_cpu(mc->max) + 1);
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: setting up volume table\n");
 +              dev_err(scomp->dev, "error: setting up volume table\n");
                goto out_free;
        }
  
@@@ -1013,12 -999,12 +1013,12 @@@ skip
                               ARRAY_SIZE(led_tokens), mc->priv.array,
                               le32_to_cpu(mc->priv.size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse led tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse led tokens failed %d\n",
                        le32_to_cpu(mc->priv.size));
                goto out_free_table;
        }
  
 -      dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n",
 +      dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
                scontrol->comp_id, scontrol->num_channels);
  
        return ret;
@@@ -1057,7 -1043,7 +1057,7 @@@ static int sof_control_load_enum(struc
  
        scontrol->cmd = SOF_CTRL_CMD_ENUM;
  
 -      dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n",
 +      dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n",
                scontrol->comp_id, scontrol->num_channels, scontrol->comp_id);
  
        return 0;
@@@ -1081,7 -1067,7 +1081,7 @@@ static int sof_control_load_bytes(struc
                le32_to_cpu(control->priv.size);
  
        if (scontrol->size > max_size) {
 -              dev_err(sdev->dev, "err: bytes data size %d exceeds max %d.\n",
 +              dev_err(scomp->dev, "err: bytes data size %d exceeds max %d.\n",
                        scontrol->size, max_size);
                ret = -EINVAL;
                goto out;
        scontrol->comp_id = sdev->next_comp_id;
        scontrol->cmd = SOF_CTRL_CMD_BINARY;
  
 -      dev_dbg(sdev->dev, "tplg: load kcontrol index %d chans %d\n",
 +      dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n",
                scontrol->comp_id, scontrol->num_channels);
  
        if (le32_to_cpu(control->priv.size) > 0) {
                       le32_to_cpu(control->priv.size));
  
                if (cdata->data->magic != SOF_ABI_MAGIC) {
 -                      dev_err(sdev->dev, "error: Wrong ABI magic 0x%08x.\n",
 +                      dev_err(scomp->dev, "error: Wrong ABI magic 0x%08x.\n",
                                cdata->data->magic);
                        ret = -EINVAL;
                        goto out_free;
                }
                if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION,
                                                 cdata->data->abi)) {
 -                      dev_err(sdev->dev,
 +                      dev_err(scomp->dev,
                                "error: Incompatible ABI version 0x%08x.\n",
                                cdata->data->abi);
                        ret = -EINVAL;
                }
                if (cdata->data->size + sizeof(const struct sof_abi_hdr) !=
                    le32_to_cpu(control->priv.size)) {
 -                      dev_err(sdev->dev,
 +                      dev_err(scomp->dev,
                                "error: Conflict in bytes vs. priv size.\n");
                        ret = -EINVAL;
                        goto out_free;
@@@ -1148,14 -1134,14 +1148,14 @@@ static int sof_control_load(struct snd_
        struct snd_sof_control *scontrol;
        int ret = -EINVAL;
  
 -      dev_dbg(sdev->dev, "tplg: load control type %d name : %s\n",
 +      dev_dbg(scomp->dev, "tplg: load control type %d name : %s\n",
                hdr->type, hdr->name);
  
        scontrol = kzalloc(sizeof(*scontrol), GFP_KERNEL);
        if (!scontrol)
                return -ENOMEM;
  
 -      scontrol->sdev = sdev;
 +      scontrol->scomp = scomp;
  
        switch (le32_to_cpu(hdr->ops.info)) {
        case SND_SOC_TPLG_CTL_VOLSW:
        case SND_SOC_TPLG_DAPM_CTL_ENUM_VALUE:
        case SND_SOC_TPLG_DAPM_CTL_PIN:
        default:
 -              dev_warn(sdev->dev, "control type not supported %d:%d:%d\n",
 +              dev_warn(scomp->dev, "control type not supported %d:%d:%d\n",
                         hdr->ops.get, hdr->ops.put, hdr->ops.info);
                kfree(scontrol);
                return 0;
@@@ -1207,7 -1193,7 +1207,7 @@@ static int sof_control_unload(struct sn
        struct sof_ipc_free fcomp;
        struct snd_sof_control *scontrol = dobj->private;
  
 -      dev_dbg(sdev->dev, "tplg: unload control name : %s\n", scomp->name);
 +      dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scomp->name);
  
        fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE;
        fcomp.hdr.size = sizeof(fcomp);
@@@ -1231,11 -1217,12 +1231,11 @@@ static int sof_connect_dai_widget(struc
                                  struct snd_soc_tplg_dapm_widget *tw,
                                  struct snd_sof_dai *dai)
  {
 -      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct snd_soc_card *card = scomp->card;
        struct snd_soc_pcm_runtime *rtd;
  
        list_for_each_entry(rtd, &card->rtd_list, list) {
 -              dev_vdbg(sdev->dev, "tplg: check widget: %s stream: %s dai stream: %s\n",
 +              dev_vdbg(scomp->dev, "tplg: check widget: %s stream: %s dai stream: %s\n",
                         w->name,  w->sname, rtd->dai_link->stream_name);
  
                if (!w->sname || !rtd->dai_link->stream_name)
                case snd_soc_dapm_dai_out:
                        rtd->cpu_dai->capture_widget = w;
                        dai->name = rtd->dai_link->name;
 -                      dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n",
 +                      dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
                                w->name, rtd->dai_link->name);
                        break;
                case snd_soc_dapm_dai_in:
                        rtd->cpu_dai->playback_widget = w;
                        dai->name = rtd->dai_link->name;
 -                      dev_dbg(sdev->dev, "tplg: connected widget %s -> DAI link %s\n",
 +                      dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
                                w->name, rtd->dai_link->name);
                        break;
                default:
  
        /* check we have a connection */
        if (!dai->name) {
 -              dev_err(sdev->dev, "error: can't connect DAI %s stream %s\n",
 +              dev_err(scomp->dev, "error: can't connect DAI %s stream %s\n",
                        w->name, w->sname);
                return -EINVAL;
        }
@@@ -1297,7 -1284,7 +1297,7 @@@ static int sof_widget_load_dai(struct s
                               ARRAY_SIZE(dai_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse dai tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse dai tokens failed %d\n",
                        le32_to_cpu(private->size));
                return ret;
        }
                               ARRAY_SIZE(comp_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse dai.cfg tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse dai.cfg tokens failed %d\n",
                        private->size);
                return ret;
        }
  
 -      dev_dbg(sdev->dev, "dai %s: type %d index %d\n",
 +      dev_dbg(scomp->dev, "dai %s: type %d index %d\n",
                swidget->widget->name, comp_dai.type, comp_dai.dai_index);
        sof_dbg_comp_config(scomp, &comp_dai.config);
  
                                 &comp_dai, sizeof(comp_dai), r, sizeof(*r));
  
        if (ret == 0 && dai) {
 -              dai->sdev = sdev;
 +              dai->scomp = scomp;
                memcpy(&dai->comp_dai, &comp_dai, sizeof(comp_dai));
        }
  
@@@ -1355,13 -1342,13 +1355,13 @@@ static int sof_widget_load_buffer(struc
                               ARRAY_SIZE(buffer_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse buffer tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse buffer tokens failed %d\n",
                        private->size);
                kfree(buffer);
                return ret;
        }
  
 -      dev_dbg(sdev->dev, "buffer %s: size %d caps 0x%x\n",
 +      dev_dbg(scomp->dev, "buffer %s: size %d caps 0x%x\n",
                swidget->widget->name, buffer->size, buffer->caps);
  
        swidget->private = buffer;
        ret = sof_ipc_tx_message(sdev->ipc, buffer->comp.hdr.cmd, buffer,
                                 sizeof(*buffer), r, sizeof(*r));
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: buffer %s load failed\n",
 +              dev_err(scomp->dev, "error: buffer %s load failed\n",
                        swidget->widget->name);
                kfree(buffer);
        }
  }
  
  /* bind PCM ID to host component ID */
 -static int spcm_bind(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
 +static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm,
                     int dir)
  {
        struct snd_sof_widget *host_widget;
  
 -      host_widget = snd_sof_find_swidget_sname(sdev,
 +      host_widget = snd_sof_find_swidget_sname(scomp,
                                                 spcm->pcm.caps[dir].name,
                                                 dir);
        if (!host_widget) {
 -              dev_err(sdev->dev, "can't find host comp to bind pcm\n");
 +              dev_err(scomp->dev, "can't find host comp to bind pcm\n");
                return -EINVAL;
        }
  
@@@ -1428,7 -1415,7 +1428,7 @@@ static int sof_widget_load_pcm(struct s
                               ARRAY_SIZE(pcm_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse host tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse host tokens failed %d\n",
                        private->size);
                goto err;
        }
                               ARRAY_SIZE(comp_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse host.cfg tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse host.cfg tokens failed %d\n",
                        le32_to_cpu(private->size));
                goto err;
        }
  
 -      dev_dbg(sdev->dev, "loaded host %s\n", swidget->widget->name);
 +      dev_dbg(scomp->dev, "loaded host %s\n", swidget->widget->name);
        sof_dbg_comp_config(scomp, &host->config);
  
        swidget->private = host;
  /*
   * Pipeline Topology
   */
 -int sof_load_pipeline_ipc(struct snd_sof_dev *sdev,
 +int sof_load_pipeline_ipc(struct device *dev,
                          struct sof_ipc_pipe_new *pipeline,
                          struct sof_ipc_comp_reply *r)
  {
 +      struct snd_sof_dev *sdev = dev_get_drvdata(dev);
        struct sof_ipc_pm_core_config pm_core_config;
        int ret;
  
        ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline,
                                 sizeof(*pipeline), r, sizeof(*r));
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: load pipeline ipc failure\n");
 +              dev_err(dev, "error: load pipeline ipc failure\n");
                return ret;
        }
  
        /* power up the core that this pipeline is scheduled on */
        ret = snd_sof_dsp_core_power_up(sdev, 1 << pipeline->core);
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: powering up pipeline schedule core %d\n",
 +              dev_err(dev, "error: powering up pipeline schedule core %d\n",
                        pipeline->core);
                return ret;
        }
                                 &pm_core_config, sizeof(pm_core_config),
                                 &pm_core_config, sizeof(pm_core_config));
        if (ret < 0)
 -              dev_err(sdev->dev, "error: core enable ipc failure\n");
 +              dev_err(dev, "error: core enable ipc failure\n");
  
        return ret;
  }
@@@ -1511,6 -1497,7 +1511,6 @@@ static int sof_widget_load_pipeline(str
                                    struct snd_soc_tplg_dapm_widget *tw,
                                    struct sof_ipc_comp_reply *r)
  {
 -      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct snd_soc_tplg_private *private = &tw->priv;
        struct sof_ipc_pipe_new *pipeline;
        struct snd_sof_widget *comp_swidget;
        pipeline->comp_id = swidget->comp_id;
  
        /* component at start of pipeline is our stream id */
 -      comp_swidget = snd_sof_find_swidget(sdev, tw->sname);
 +      comp_swidget = snd_sof_find_swidget(scomp, tw->sname);
        if (!comp_swidget) {
 -              dev_err(sdev->dev, "error: widget %s refers to non existent widget %s\n",
 +              dev_err(scomp->dev, "error: widget %s refers to non existent widget %s\n",
                        tw->name, tw->sname);
                ret = -EINVAL;
                goto err;
  
        pipeline->sched_id = comp_swidget->comp_id;
  
 -      dev_dbg(sdev->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n",
 +      dev_dbg(scomp->dev, "tplg: pipeline id %d comp %d scheduling comp id %d\n",
                pipeline->pipeline_id, pipeline->comp_id, pipeline->sched_id);
  
        ret = sof_parse_tokens(scomp, pipeline, sched_tokens,
                               ARRAY_SIZE(sched_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse pipeline tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse pipeline tokens failed %d\n",
                        private->size);
                goto err;
        }
  
 -      dev_dbg(sdev->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d\n",
 +      dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d\n",
                swidget->widget->name, pipeline->period, pipeline->priority,
                pipeline->period_mips, pipeline->core, pipeline->frames_per_sched);
  
        swidget->private = pipeline;
  
        /* send ipc's to create pipeline comp and power up schedule core */
 -      ret = sof_load_pipeline_ipc(sdev, pipeline, r);
 +      ret = sof_load_pipeline_ipc(scomp->dev, pipeline, r);
        if (ret >= 0)
                return ret;
  err:
@@@ -1594,7 -1581,7 +1594,7 @@@ static int sof_widget_load_mixer(struc
                               ARRAY_SIZE(comp_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse mixer.cfg tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse mixer.cfg tokens failed %d\n",
                        private->size);
                kfree(mixer);
                return ret;
@@@ -1641,7 -1628,7 +1641,7 @@@ static int sof_widget_load_mux(struct s
                               ARRAY_SIZE(comp_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse mux.cfg tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse mux.cfg tokens failed %d\n",
                        private->size);
                kfree(mux);
                return ret;
@@@ -1681,7 -1668,7 +1681,7 @@@ static int sof_widget_load_pga(struct s
                return -ENOMEM;
  
        if (!le32_to_cpu(tw->num_kcontrols)) {
 -              dev_err(sdev->dev, "error: invalid kcontrol count %d for volume\n",
 +              dev_err(scomp->dev, "error: invalid kcontrol count %d for volume\n",
                        tw->num_kcontrols);
                ret = -EINVAL;
                goto err;
                               ARRAY_SIZE(volume_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse volume tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse volume tokens failed %d\n",
                        private->size);
                goto err;
        }
                               ARRAY_SIZE(comp_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse volume.cfg tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse volume.cfg tokens failed %d\n",
                        le32_to_cpu(private->size));
                goto err;
        }
@@@ -1767,7 -1754,7 +1767,7 @@@ static int sof_widget_load_src(struct s
                               ARRAY_SIZE(src_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse src tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse src tokens failed %d\n",
                        private->size);
                goto err;
        }
                               ARRAY_SIZE(comp_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse src.cfg tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse src.cfg tokens failed %d\n",
                        le32_to_cpu(private->size));
                goto err;
        }
  
 -      dev_dbg(sdev->dev, "src %s: source rate %d sink rate %d\n",
 +      dev_dbg(scomp->dev, "src %s: source rate %d sink rate %d\n",
                swidget->widget->name, src->source_rate, src->sink_rate);
        sof_dbg_comp_config(scomp, &src->config);
  
  }
  
  /*
 + * ASRC Topology
 + */
 +
 +static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index,
 +                              struct snd_sof_widget *swidget,
 +                              struct snd_soc_tplg_dapm_widget *tw,
 +                              struct sof_ipc_comp_reply *r)
 +{
 +      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
 +      struct snd_soc_tplg_private *private = &tw->priv;
 +      struct sof_ipc_comp_asrc *asrc;
 +      int ret;
 +
 +      asrc = kzalloc(sizeof(*asrc), GFP_KERNEL);
 +      if (!asrc)
 +              return -ENOMEM;
 +
 +      /* configure ASRC IPC message */
 +      asrc->comp.hdr.size = sizeof(*asrc);
 +      asrc->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW;
 +      asrc->comp.id = swidget->comp_id;
 +      asrc->comp.type = SOF_COMP_ASRC;
 +      asrc->comp.pipeline_id = index;
 +      asrc->config.hdr.size = sizeof(asrc->config);
 +
 +      ret = sof_parse_tokens(scomp, asrc, asrc_tokens,
 +                             ARRAY_SIZE(asrc_tokens), private->array,
 +                             le32_to_cpu(private->size));
 +      if (ret != 0) {
 +              dev_err(scomp->dev, "error: parse asrc tokens failed %d\n",
 +                      private->size);
 +              goto err;
 +      }
 +
 +      ret = sof_parse_tokens(scomp, &asrc->config, comp_tokens,
 +                             ARRAY_SIZE(comp_tokens), private->array,
 +                             le32_to_cpu(private->size));
 +      if (ret != 0) {
 +              dev_err(scomp->dev, "error: parse asrc.cfg tokens failed %d\n",
 +                      le32_to_cpu(private->size));
 +              goto err;
 +      }
 +
 +      dev_dbg(scomp->dev, "asrc %s: source rate %d sink rate %d "
 +              "asynch %d operation %d\n",
 +              swidget->widget->name, asrc->source_rate, asrc->sink_rate,
 +              asrc->asynchronous_mode, asrc->operation_mode);
 +      sof_dbg_comp_config(scomp, &asrc->config);
 +
 +      swidget->private = asrc;
 +
 +      ret = sof_ipc_tx_message(sdev->ipc, asrc->comp.hdr.cmd, asrc,
 +                               sizeof(*asrc), r, sizeof(*r));
 +      if (ret >= 0)
 +              return ret;
 +err:
 +      kfree(asrc);
 +      return ret;
 +}
 +
 +/*
   * Signal Generator Topology
   */
  
@@@ -1887,7 -1813,7 +1887,7 @@@ static int sof_widget_load_siggen(struc
                               ARRAY_SIZE(tone_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse tone tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse tone tokens failed %d\n",
                        le32_to_cpu(private->size));
                goto err;
        }
                               ARRAY_SIZE(comp_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse tone.cfg tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse tone.cfg tokens failed %d\n",
                        le32_to_cpu(private->size));
                goto err;
        }
  
 -      dev_dbg(sdev->dev, "tone %s: frequency %d amplitude %d\n",
 +      dev_dbg(scomp->dev, "tone %s: frequency %d amplitude %d\n",
                swidget->widget->name, tone->frequency, tone->amplitude);
        sof_dbg_comp_config(scomp, &tone->config);
  
@@@ -1916,7 -1842,7 +1916,7 @@@ err
        return ret;
  }
  
 -static int sof_get_control_data(struct snd_sof_dev *sdev,
 +static int sof_get_control_data(struct snd_soc_component *scomp,
                                struct snd_soc_dapm_widget *widget,
                                struct sof_widget_data *wdata,
                                size_t *size)
                        wdata[i].control = se->dobj.private;
                        break;
                default:
 -                      dev_err(sdev->dev, "error: unknown kcontrol type %d in widget %s\n",
 +                      dev_err(scomp->dev, "error: unknown kcontrol type %d in widget %s\n",
                                widget->dobj.widget.kcontrol_type,
                                widget->name);
                        return -EINVAL;
                }
  
                if (!wdata[i].control) {
 -                      dev_err(sdev->dev, "error: no scontrol for widget %s\n",
 +                      dev_err(scomp->dev, "error: no scontrol for widget %s\n",
                                widget->name);
                        return -EINVAL;
                }
@@@ -2006,7 -1932,7 +2006,7 @@@ static int sof_process_load(struct snd_
        int i;
  
        if (type == SOF_COMP_NONE) {
 -              dev_err(sdev->dev, "error: invalid process comp type %d\n",
 +              dev_err(scomp->dev, "error: invalid process comp type %d\n",
                        type);
                return -EINVAL;
        }
                        return -ENOMEM;
  
                /* get possible component controls and get size of all pdata */
 -              ret = sof_get_control_data(sdev, widget, wdata,
 +              ret = sof_get_control_data(scomp, widget, wdata,
                                           &ipc_data_size);
  
                if (ret < 0)
                               ARRAY_SIZE(comp_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse process.cfg tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse process.cfg tokens failed %d\n",
                        le32_to_cpu(private->size));
                goto err;
        }
                                 ipc_size, r, sizeof(*r));
  
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: create process failed\n");
 +              dev_err(scomp->dev, "error: create process failed\n");
                goto err;
        }
  
        /* send control data with large message supported method */
        for (i = 0; i < widget->num_kcontrols; i++) {
                wdata[i].control->readback_offset = 0;
 -              ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, wdata[i].control,
 +              ret = snd_sof_ipc_set_get_comp_data(wdata[i].control,
                                                    wdata[i].ipc_cmd,
                                                    wdata[i].ctrl_type,
                                                    wdata[i].control->cmd,
                                                    true);
                if (ret != 0) {
 -                      dev_err(sdev->dev, "error: send control failed\n");
 +                      dev_err(scomp->dev, "error: send control failed\n");
                        break;
                }
        }
@@@ -2124,13 -2050,14 +2124,13 @@@ static int sof_widget_load_process(stru
                                   struct snd_soc_tplg_dapm_widget *tw,
                                   struct sof_ipc_comp_reply *r)
  {
 -      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct snd_soc_tplg_private *private = &tw->priv;
        struct sof_ipc_comp_process config;
        int ret;
  
        /* check we have some tokens - we need at least process type */
        if (le32_to_cpu(private->size) == 0) {
 -              dev_err(sdev->dev, "error: process tokens not found\n");
 +              dev_err(scomp->dev, "error: process tokens not found\n");
                return -EINVAL;
        }
  
                               ARRAY_SIZE(process_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse process tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse process tokens failed %d\n",
                        le32_to_cpu(private->size));
                return ret;
        }
        ret = sof_process_load(scomp, index, swidget, tw, r,
                               find_process_comp_type(config.type));
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: process loading failed\n");
 +              dev_err(scomp->dev, "error: process loading failed\n");
                return ret;
        }
  
        return 0;
  }
  
 -static int sof_widget_bind_event(struct snd_sof_dev *sdev,
 +static int sof_widget_bind_event(struct snd_soc_component *scomp,
                                 struct snd_sof_widget *swidget,
                                 u16 event_type)
  {
                break;
        }
  
 -      dev_err(sdev->dev,
 +      dev_err(scomp->dev,
                "error: invalid event type %d for widget %s\n",
                event_type, swidget->widget->name);
        return -EINVAL;
@@@ -2205,7 -2132,7 +2205,7 @@@ static int sof_widget_ready(struct snd_
        if (!swidget)
                return -ENOMEM;
  
 -      swidget->sdev = sdev;
 +      swidget->scomp = scomp;
        swidget->widget = w;
        swidget->comp_id = sdev->next_comp_id++;
        swidget->complete = 0;
        swidget->private = NULL;
        memset(&reply, 0, sizeof(reply));
  
 -      dev_dbg(sdev->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n",
 +      dev_dbg(scomp->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n",
                swidget->comp_id, index, swidget->id, tw->name,
                strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0
                        ? tw->sname : "none");
        case snd_soc_dapm_src:
                ret = sof_widget_load_src(scomp, index, swidget, tw, &reply);
                break;
 +      case snd_soc_dapm_asrc:
 +              ret = sof_widget_load_asrc(scomp, index, swidget, tw, &reply);
 +              break;
        case snd_soc_dapm_siggen:
                ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply);
                break;
        case snd_soc_dapm_dai_link:
        case snd_soc_dapm_kcontrol:
        default:
 -              dev_warn(sdev->dev, "warning: widget type %d name %s not handled\n",
 +              dev_warn(scomp->dev, "warning: widget type %d name %s not handled\n",
                         swidget->id, tw->name);
                break;
        }
  
        /* check IPC reply */
        if (ret < 0 || reply.rhdr.error < 0) {
 -              dev_err(sdev->dev,
 +              dev_err(scomp->dev,
                        "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n",
                        tw->shift, swidget->id, tw->name,
                        strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0
  
        /* bind widget to external event */
        if (tw->event_type) {
 -              ret = sof_widget_bind_event(sdev, swidget,
 +              ret = sof_widget_bind_event(scomp, swidget,
                                            le16_to_cpu(tw->event_type));
                if (ret) {
 -                      dev_err(sdev->dev, "error: widget event binding failed\n");
 +                      dev_err(scomp->dev, "error: widget event binding failed\n");
                        kfree(swidget->private);
                        kfree(swidget);
                        return ret;
@@@ -2377,7 -2301,7 +2377,7 @@@ static int sof_widget_unload(struct snd
                pipeline = swidget->private;
                ret = snd_sof_dsp_core_power_down(sdev, 1 << pipeline->core);
                if (ret < 0)
 -                      dev_err(sdev->dev, "error: powering down pipeline schedule core %d\n",
 +                      dev_err(scomp->dev, "error: powering down pipeline schedule core %d\n",
                                pipeline->core);
  
                /* update enabled cores mask */
                        scontrol = sbe->dobj.private;
                        break;
                default:
 -                      dev_warn(sdev->dev, "unsupported kcontrol_type\n");
 +                      dev_warn(scomp->dev, "unsupported kcontrol_type\n");
                        goto out;
                }
                kfree(scontrol->control_data);
@@@ -2448,12 -2372,12 +2448,12 @@@ static int sof_dai_load(struct snd_soc_
        if (!spcm)
                return -ENOMEM;
  
 -      spcm->sdev = sdev;
 +      spcm->scomp = scomp;
        spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = COMP_ID_UNASSIGNED;
        spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = COMP_ID_UNASSIGNED;
  
        spcm->pcm = *pcm;
 -      dev_dbg(sdev->dev, "tplg: load pcm %s\n", pcm->dai_name);
 +      dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name);
  
        dai_drv->dobj.private = spcm;
        list_add(&spcm->list, &sdev->pcm_list);
                               ARRAY_SIZE(stream_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret) {
 -              dev_err(sdev->dev, "error: parse stream tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse stream tokens failed %d\n",
                        le32_to_cpu(private->size));
                return ret;
        }
        if (!spcm->pcm.playback)
                goto capture;
  
 -      dev_vdbg(sdev->dev, "tplg: pcm %s stream tokens: playback d0i3:%d\n",
 +      dev_vdbg(scomp->dev, "tplg: pcm %s stream tokens: playback d0i3:%d\n",
                 spcm->pcm.pcm_name, spcm->stream[0].d0i3_compatible);
  
        caps = &spcm->pcm.caps[stream];
        ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
                                  PAGE_SIZE, &spcm->stream[stream].page_table);
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: can't alloc page table for %s %d\n",
 +              dev_err(scomp->dev, "error: can't alloc page table for %s %d\n",
                        caps->name, ret);
  
                return ret;
        }
  
        /* bind pcm to host comp */
 -      ret = spcm_bind(sdev, spcm, stream);
 +      ret = spcm_bind(scomp, spcm, stream);
        if (ret) {
 -              dev_err(sdev->dev,
 +              dev_err(scomp->dev,
                        "error: can't bind pcm to host\n");
                goto free_playback_tables;
        }
@@@ -2501,7 -2425,7 +2501,7 @@@ capture
        if (!spcm->pcm.capture)
                return ret;
  
 -      dev_vdbg(sdev->dev, "tplg: pcm %s stream tokens: capture d0i3:%d\n",
 +      dev_vdbg(scomp->dev, "tplg: pcm %s stream tokens: capture d0i3:%d\n",
                 spcm->pcm.pcm_name, spcm->stream[1].d0i3_compatible);
  
        caps = &spcm->pcm.caps[stream];
        ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
                                  PAGE_SIZE, &spcm->stream[stream].page_table);
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: can't alloc page table for %s %d\n",
 +              dev_err(scomp->dev, "error: can't alloc page table for %s %d\n",
                        caps->name, ret);
                goto free_playback_tables;
        }
  
        /* bind pcm to host comp */
 -      ret = spcm_bind(sdev, spcm, stream);
 +      ret = spcm_bind(scomp, spcm, stream);
        if (ret) {
 -              dev_err(sdev->dev,
 +              dev_err(scomp->dev,
                        "error: can't bind pcm to host\n");
                snd_dma_free_pages(&spcm->stream[stream].page_table);
                goto free_playback_tables;
@@@ -2644,7 -2568,7 +2644,7 @@@ static int sof_link_ssp_load(struct snd
                               ARRAY_SIZE(ssp_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse ssp tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse ssp tokens failed %d\n",
                        le32_to_cpu(private->size));
                return ret;
        }
        config->ssp.rx_slots = le32_to_cpu(hw_config->rx_slots);
        config->ssp.tx_slots = le32_to_cpu(hw_config->tx_slots);
  
 -      dev_dbg(sdev->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d\n",
 +      dev_dbg(scomp->dev, "tplg: config SSP%d fmt 0x%x mclk %d bclk %d fclk %d width (%d)%d slots %d mclk id %d quirks %d\n",
                config->dai_index, config->format,
                config->ssp.mclk_rate, config->ssp.bclk_rate,
                config->ssp.fsync_rate, config->ssp.sample_valid_bits,
  
        /* validate SSP fsync rate and channel count */
        if (config->ssp.fsync_rate < 8000 || config->ssp.fsync_rate > 192000) {
 -              dev_err(sdev->dev, "error: invalid fsync rate for SSP%d\n",
 +              dev_err(scomp->dev, "error: invalid fsync rate for SSP%d\n",
                        config->dai_index);
                return -EINVAL;
        }
  
        if (config->ssp.tdm_slots < 1 || config->ssp.tdm_slots > 8) {
 -              dev_err(sdev->dev, "error: invalid channel count for SSP%d\n",
 +              dev_err(scomp->dev, "error: invalid channel count for SSP%d\n",
                        config->dai_index);
                return -EINVAL;
        }
                                 sizeof(reply));
  
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: failed to set DAI config for SSP%d\n",
 +              dev_err(scomp->dev, "error: failed to set DAI config for SSP%d\n",
                        config->dai_index);
                return ret;
        }
        /* set config for all DAI's with name matching the link name */
        ret = sof_set_dai_config(sdev, size, link, config);
        if (ret < 0)
 -              dev_err(sdev->dev, "error: failed to save DAI config for SSP%d\n",
 +              dev_err(scomp->dev, "error: failed to save DAI config for SSP%d\n",
                        config->dai_index);
  
        return ret;
@@@ -2731,7 -2655,7 +2731,7 @@@ static int sof_link_esai_load(struct sn
                               ARRAY_SIZE(esai_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse esai tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse esai tokens failed %d\n",
                        le32_to_cpu(private->size));
                return ret;
        }
        config->esai.rx_slots = le32_to_cpu(hw_config->rx_slots);
        config->esai.tx_slots = le32_to_cpu(hw_config->tx_slots);
  
 -      dev_info(sdev->dev,
 +      dev_info(scomp->dev,
                 "tplg: config ESAI%d fmt 0x%x mclk %d width %d slots %d mclk id %d\n",
                config->dai_index, config->format,
                config->esai.mclk_rate, config->esai.tdm_slot_width,
                config->esai.tdm_slots, config->esai.mclk_id);
  
        if (config->esai.tdm_slots < 1 || config->esai.tdm_slots > 8) {
 -              dev_err(sdev->dev, "error: invalid channel count for ESAI%d\n",
 +              dev_err(scomp->dev, "error: invalid channel count for ESAI%d\n",
                        config->dai_index);
                return -EINVAL;
        }
                                 config->hdr.cmd, config, size, &reply,
                                 sizeof(reply));
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: failed to set DAI config for ESAI%d\n",
 +              dev_err(scomp->dev, "error: failed to set DAI config for ESAI%d\n",
                        config->dai_index);
                return ret;
        }
        /* set config for all DAI's with name matching the link name */
        ret = sof_set_dai_config(sdev, size, link, config);
        if (ret < 0)
 -              dev_err(sdev->dev, "error: failed to save DAI config for ESAI%d\n",
 +              dev_err(scomp->dev, "error: failed to save DAI config for ESAI%d\n",
                        config->dai_index);
  
        return ret;
@@@ -2803,7 -2727,7 +2803,7 @@@ static int sof_link_dmic_load(struct sn
                               ARRAY_SIZE(dmic_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse dmic tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse dmic tokens failed %d\n",
                        le32_to_cpu(private->size));
                return ret;
        }
                               ARRAY_SIZE(dmic_pdm_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse dmic pdm tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse dmic pdm tokens failed %d\n",
                        le32_to_cpu(private->size));
                goto err;
        }
        ipc_config->hdr.size = size;
  
        /* debug messages */
 -      dev_dbg(sdev->dev, "tplg: config DMIC%d driver version %d\n",
 +      dev_dbg(scomp->dev, "tplg: config DMIC%d driver version %d\n",
                ipc_config->dai_index, ipc_config->dmic.driver_ipc_version);
 -      dev_dbg(sdev->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n",
 +      dev_dbg(scomp->dev, "pdmclk_min %d pdm_clkmax %d duty_min %hd\n",
                ipc_config->dmic.pdmclk_min, ipc_config->dmic.pdmclk_max,
                ipc_config->dmic.duty_min);
 -      dev_dbg(sdev->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n",
 +      dev_dbg(scomp->dev, "duty_max %hd fifo_fs %d num_pdms active %d\n",
                ipc_config->dmic.duty_max, ipc_config->dmic.fifo_fs,
                ipc_config->dmic.num_pdm_active);
 -      dev_dbg(sdev->dev, "fifo word length %hd\n",
 +      dev_dbg(scomp->dev, "fifo word length %hd\n",
                ipc_config->dmic.fifo_bits);
  
        for (j = 0; j < ipc_config->dmic.num_pdm_active; j++) {
 -              dev_dbg(sdev->dev, "pdm %hd mic a %hd mic b %hd\n",
 +              dev_dbg(scomp->dev, "pdm %hd mic a %hd mic b %hd\n",
                        ipc_config->dmic.pdm[j].id,
                        ipc_config->dmic.pdm[j].enable_mic_a,
                        ipc_config->dmic.pdm[j].enable_mic_b);
 -              dev_dbg(sdev->dev, "pdm %hd polarity a %hd polarity b %hd\n",
 +              dev_dbg(scomp->dev, "pdm %hd polarity a %hd polarity b %hd\n",
                        ipc_config->dmic.pdm[j].id,
                        ipc_config->dmic.pdm[j].polarity_mic_a,
                        ipc_config->dmic.pdm[j].polarity_mic_b);
 -              dev_dbg(sdev->dev, "pdm %hd clk_edge %hd skew %hd\n",
 +              dev_dbg(scomp->dev, "pdm %hd clk_edge %hd skew %hd\n",
                        ipc_config->dmic.pdm[j].id,
                        ipc_config->dmic.pdm[j].clk_edge,
                        ipc_config->dmic.pdm[j].skew);
                                 sizeof(reply));
  
        if (ret < 0) {
 -              dev_err(sdev->dev,
 +              dev_err(scomp->dev,
                        "error: failed to set DAI config for DMIC%d\n",
                        config->dai_index);
                goto err;
        /* set config for all DAI's with name matching the link name */
        ret = sof_set_dai_config(sdev, size, link, ipc_config);
        if (ret < 0)
 -              dev_err(sdev->dev, "error: failed to save DAI config for DMIC%d\n",
 +              dev_err(scomp->dev, "error: failed to save DAI config for DMIC%d\n",
                        config->dai_index);
  
  err:
@@@ -2984,21 -2908,21 +2984,21 @@@ static int sof_link_hda_load(struct snd
                               ARRAY_SIZE(hda_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse hda tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse hda tokens failed %d\n",
                        le32_to_cpu(private->size));
                return ret;
        }
  
        dai = snd_soc_find_dai(link->cpus);
        if (!dai) {
 -              dev_err(sdev->dev, "error: failed to find dai %s in %s",
 +              dev_err(scomp->dev, "error: failed to find dai %s in %s",
                        link->cpus->dai_name, __func__);
                return -EINVAL;
        }
  
        ret = sof_link_hda_process(sdev, link, config);
        if (ret < 0)
 -              dev_err(sdev->dev, "error: failed to process hda dai link %s",
 +              dev_err(scomp->dev, "error: failed to process hda dai link %s",
                        link->name);
  
        return ret;
@@@ -3024,7 -2948,7 +3024,7 @@@ static int sof_link_alh_load(struct snd
                                 sizeof(reply));
  
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: failed to set DAI config for ALH %d\n",
 +              dev_err(scomp->dev, "error: failed to set DAI config for ALH %d\n",
                        config->dai_index);
                return ret;
        }
        /* set config for all DAI's with name matching the link name */
        ret = sof_set_dai_config(sdev, size, link, config);
        if (ret < 0)
 -              dev_err(sdev->dev, "error: failed to save DAI config for ALH %d\n",
 +              dev_err(scomp->dev, "error: failed to save DAI config for ALH %d\n",
                        config->dai_index);
  
        return ret;
@@@ -3043,6 -2967,7 +3043,6 @@@ static int sof_link_load(struct snd_soc
                         struct snd_soc_dai_link *link,
                         struct snd_soc_tplg_link_config *cfg)
  {
 -      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct snd_soc_tplg_private *private = &cfg->priv;
        struct sof_ipc_dai_config config;
        struct snd_soc_tplg_hw_config *hw_config;
        int i = 0;
  
        if (!link->platforms) {
 -              dev_err(sdev->dev, "error: no platforms\n");
 +              dev_err(scomp->dev, "error: no platforms\n");
                return -EINVAL;
        }
 -      link->platforms->name = dev_name(sdev->dev);
 +      link->platforms->name = dev_name(scomp->dev);
  
        /*
         * Set nonatomic property for FE dai links as their trigger action
  
        /* check we have some tokens - we need at least DAI type */
        if (le32_to_cpu(private->size) == 0) {
 -              dev_err(sdev->dev, "error: expected tokens for DAI, none found\n");
 +              dev_err(scomp->dev, "error: expected tokens for DAI, none found\n");
                return -EINVAL;
        }
  
                               ARRAY_SIZE(dai_link_tokens), private->array,
                               le32_to_cpu(private->size));
        if (ret != 0) {
 -              dev_err(sdev->dev, "error: parse link tokens failed %d\n",
 +              dev_err(scomp->dev, "error: parse link tokens failed %d\n",
                        le32_to_cpu(private->size));
                return ret;
        }
        num_hw_configs = le32_to_cpu(cfg->num_hw_configs);
        if (!num_hw_configs) {
                if (config.type != SOF_DAI_INTEL_HDA) {
 -                      dev_err(sdev->dev, "error: unexpected DAI config count %d!\n",
 +                      dev_err(scomp->dev, "error: unexpected DAI config count %d!\n",
                                le32_to_cpu(cfg->num_hw_configs));
                        return -EINVAL;
                }
        } else {
 -              dev_dbg(sdev->dev, "tplg: %d hw_configs found, default id: %d!\n",
 +              dev_dbg(scomp->dev, "tplg: %d hw_configs found, default id: %d!\n",
                        cfg->num_hw_configs, le32_to_cpu(cfg->default_hw_config_id));
  
                for (i = 0; i < num_hw_configs; i++) {
                }
  
                if (i == num_hw_configs) {
 -                      dev_err(sdev->dev, "error: default hw_config id: %d not found!\n",
 +                      dev_err(scomp->dev, "error: default hw_config id: %d not found!\n",
                                le32_to_cpu(cfg->default_hw_config_id));
                        return -EINVAL;
                }
                                         &config);
                break;
        default:
 -              dev_err(sdev->dev, "error: invalid DAI type %d\n", config.type);
 +              dev_err(scomp->dev, "error: invalid DAI type %d\n",
 +                      config.type);
                ret = -EINVAL;
                break;
        }
@@@ -3199,7 -3123,7 +3199,7 @@@ static int sof_link_unload(struct snd_s
                        goto found;
        }
  
 -      dev_err(sdev->dev, "error: failed to find dai %s in %s",
 +      dev_err(scomp->dev, "error: failed to find dai %s in %s",
                link->name, __func__);
        return -EINVAL;
  found:
        case SOF_DAI_INTEL_SSP:
        case SOF_DAI_INTEL_DMIC:
        case SOF_DAI_INTEL_ALH:
-               /* no resource needs to be released for SSP, DMIC and ALH */
+       case SOF_DAI_IMX_SAI:
+       case SOF_DAI_IMX_ESAI:
+               /* no resource needs to be released for all cases above */
                break;
        case SOF_DAI_INTEL_HDA:
                ret = sof_link_hda_unload(sdev, link);
                break;
        default:
 -              dev_err(sdev->dev, "error: invalid DAI type %d\n",
 +              dev_err(scomp->dev, "error: invalid DAI type %d\n",
                        sof_dai->dai_config->type);
                ret = -EINVAL;
                break;
@@@ -3240,7 -3166,7 +3242,7 @@@ static int sof_route_load(struct snd_so
        if (!sroute)
                return -ENOMEM;
  
 -      sroute->sdev = sdev;
 +      sroute->scomp = scomp;
  
        connect = kzalloc(sizeof(*connect), GFP_KERNEL);
        if (!connect) {
        connect->hdr.size = sizeof(*connect);
        connect->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT;
  
 -      dev_dbg(sdev->dev, "sink %s control %s source %s\n",
 +      dev_dbg(scomp->dev, "sink %s control %s source %s\n",
                route->sink, route->control ? route->control : "none",
                route->source);
  
        /* source component */
 -      source_swidget = snd_sof_find_swidget(sdev, (char *)route->source);
 +      source_swidget = snd_sof_find_swidget(scomp, (char *)route->source);
        if (!source_swidget) {
 -              dev_err(sdev->dev, "error: source %s not found\n",
 +              dev_err(scomp->dev, "error: source %s not found\n",
                        route->source);
                ret = -EINVAL;
                goto err;
        connect->source_id = source_swidget->comp_id;
  
        /* sink component */
 -      sink_swidget = snd_sof_find_swidget(sdev, (char *)route->sink);
 +      sink_swidget = snd_sof_find_swidget(scomp, (char *)route->sink);
        if (!sink_swidget) {
 -              dev_err(sdev->dev, "error: sink %s not found\n",
 +              dev_err(scomp->dev, "error: sink %s not found\n",
                        route->sink);
                ret = -EINVAL;
                goto err;
         */
        if (source_swidget->id != snd_soc_dapm_buffer &&
            sink_swidget->id != snd_soc_dapm_buffer) {
 -              dev_dbg(sdev->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n",
 +              dev_dbg(scomp->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n",
                        route->source, route->sink);
                ret = 0;
                goto err;
  
                /* check IPC return value */
                if (ret < 0) {
 -                      dev_err(sdev->dev, "error: failed to add route sink %s control %s source %s\n",
 +                      dev_err(scomp->dev, "error: failed to add route sink %s control %s source %s\n",
                                route->sink,
                                route->control ? route->control : "none",
                                route->source);
  
                /* check IPC reply */
                if (reply.error < 0) {
 -                      dev_err(sdev->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n",
 +                      dev_err(scomp->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n",
                                route->sink,
                                route->control ? route->control : "none",
                                route->source, reply.error);
@@@ -3351,9 -3277,8 +3353,9 @@@ err
  /* Function to set the initial value of SOF kcontrols.
   * The value will be stored in scontrol->control_data
   */
 -static int snd_sof_cache_kcontrol_val(struct snd_sof_dev *sdev)
 +static int snd_sof_cache_kcontrol_val(struct snd_soc_component *scomp)
  {
 +      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        struct snd_sof_control *scontrol = NULL;
        int ipc_cmd, ctrl_type;
        int ret = 0;
                        ctrl_type = SOF_CTRL_TYPE_DATA_GET;
                        break;
                default:
 -                      dev_err(sdev->dev,
 +                      dev_err(scomp->dev,
                                "error: Invalid scontrol->cmd: %d\n",
                                scontrol->cmd);
                        return -EINVAL;
                }
 -              ret = snd_sof_ipc_set_get_comp_data(sdev->ipc, scontrol,
 +              ret = snd_sof_ipc_set_get_comp_data(scontrol,
                                                    ipc_cmd, ctrl_type,
                                                    scontrol->cmd,
                                                    false);
                if (ret < 0) {
 -                      dev_warn(sdev->dev,
 -                              "error: kcontrol value get for widget: %d\n",
 -                              scontrol->comp_id);
 +                      dev_warn(scomp->dev,
 +                               "error: kcontrol value get for widget: %d\n",
 +                               scontrol->comp_id);
                }
        }
  
        return ret;
  }
  
 -int snd_sof_complete_pipeline(struct snd_sof_dev *sdev,
 +int snd_sof_complete_pipeline(struct device *dev,
                              struct snd_sof_widget *swidget)
  {
 +      struct snd_sof_dev *sdev = dev_get_drvdata(dev);
        struct sof_ipc_pipe_ready ready;
        struct sof_ipc_reply reply;
        int ret;
  
 -      dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n",
 +      dev_dbg(dev, "tplg: complete pipeline %s id %d\n",
                swidget->widget->name, swidget->comp_id);
  
        memset(&ready, 0, sizeof(ready));
@@@ -3430,7 -3354,7 +3432,7 @@@ static void sof_complete(struct snd_soc
                switch (swidget->id) {
                case snd_soc_dapm_scheduler:
                        swidget->complete =
 -                              snd_sof_complete_pipeline(sdev, swidget);
 +                              snd_sof_complete_pipeline(scomp->dev, swidget);
                        break;
                default:
                        break;
         * cache initial values of SOF kcontrols by reading DSP value over
         * IPC. It may be overwritten by alsa-mixer after booting up
         */
 -      snd_sof_cache_kcontrol_val(sdev);
 +      snd_sof_cache_kcontrol_val(scomp);
  }
  
  /* manifest - optional to inform component of manifest */
  static int sof_manifest(struct snd_soc_component *scomp, int index,
                        struct snd_soc_tplg_manifest *man)
  {
 -      struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
        u32 size;
        u32 abi_version;
  
  
        /* backward compatible with tplg without ABI info */
        if (!size) {
 -              dev_dbg(sdev->dev, "No topology ABI info\n");
 +              dev_dbg(scomp->dev, "No topology ABI info\n");
                return 0;
        }
  
        if (size != SOF_TPLG_ABI_SIZE) {
 -              dev_err(sdev->dev, "error: invalid topology ABI size\n");
 +              dev_err(scomp->dev, "error: invalid topology ABI size\n");
                return -EINVAL;
        }
  
 -      dev_info(sdev->dev,
 +      dev_info(scomp->dev,
                 "Topology: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",
                 man->priv.data[0], man->priv.data[1],
                 man->priv.data[2], SOF_ABI_MAJOR, SOF_ABI_MINOR,
                                  man->priv.data[2]);
  
        if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, abi_version)) {
 -              dev_err(sdev->dev, "error: incompatible topology ABI version\n");
 +              dev_err(scomp->dev, "error: incompatible topology ABI version\n");
                return -EINVAL;
        }
  
        if (abi_version > SOF_ABI_VERSION) {
                if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
 -                      dev_warn(sdev->dev, "warn: topology ABI is more recent than kernel\n");
 +                      dev_warn(scomp->dev, "warn: topology ABI is more recent than kernel\n");
                } else {
 -                      dev_err(sdev->dev, "error: topology ABI is more recent than kernel\n");
 +                      dev_err(scomp->dev, "error: topology ABI is more recent than kernel\n");
                        return -EINVAL;
                }
        }
@@@ -3540,25 -3465,34 +3542,25 @@@ static struct snd_soc_tplg_ops sof_tplg
        .bytes_ext_ops_count    = ARRAY_SIZE(sof_bytes_ext_ops),
  };
  
 -int snd_sof_init_topology(struct snd_sof_dev *sdev,
 -                        struct snd_soc_tplg_ops *ops)
 -{
 -      /* TODO: support linked list of topologies */
 -      sdev->tplg_ops = ops;
 -      return 0;
 -}
 -EXPORT_SYMBOL(snd_sof_init_topology);
 -
 -int snd_sof_load_topology(struct snd_sof_dev *sdev, const char *file)
 +int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
  {
        const struct firmware *fw;
        int ret;
  
 -      dev_dbg(sdev->dev, "loading topology:%s\n", file);
 +      dev_dbg(scomp->dev, "loading topology:%s\n", file);
  
 -      ret = request_firmware(&fw, file, sdev->dev);
 +      ret = request_firmware(&fw, file, scomp->dev);
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: tplg request firmware %s failed err: %d\n",
 +              dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n",
                        file, ret);
                return ret;
        }
  
 -      ret = snd_soc_tplg_component_load(sdev->component,
 +      ret = snd_soc_tplg_component_load(scomp,
                                          &sof_tplg_ops, fw,
                                          SND_SOC_TPLG_INDEX_ALL);
        if (ret < 0) {
 -              dev_err(sdev->dev, "error: tplg component load failed %d\n",
 +              dev_err(scomp->dev, "error: tplg component load failed %d\n",
                        ret);
                ret = -EINVAL;
        }