Merge remote-tracking branch 'asoc/topic/hdmi' into asoc-next
authorMark Brown <broonie@kernel.org>
Fri, 13 May 2016 13:27:16 +0000 (14:27 +0100)
committerMark Brown <broonie@kernel.org>
Fri, 13 May 2016 13:27:16 +0000 (14:27 +0100)
1  2 
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/hdac_hdmi.c
sound/soc/intel/boards/skl_nau88l25_max98357a.c
sound/soc/intel/boards/skl_nau88l25_ssm4567.c
sound/soc/intel/boards/skl_rt286.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-topology.c

diff --combined sound/soc/codecs/Kconfig
index 26ae0b5d3e16824e1575b3cb40187f6e944484b3,06d0e0593ec38f8a2e9e647dbeee2427097b5cc9..b3afae990e39633067b4050f0bee756a0aee128d
@@@ -88,13 -88,13 +88,14 @@@ config SND_SOC_ALL_CODEC
        select SND_SOC_MC13783 if MFD_MC13XXX
        select SND_SOC_ML26124 if I2C
        select SND_SOC_NAU8825 if I2C
+       select SND_SOC_HDMI_CODEC
        select SND_SOC_PCM1681 if I2C
        select SND_SOC_PCM179X_I2C if I2C
        select SND_SOC_PCM179X_SPI if SPI_MASTER
        select SND_SOC_PCM3008
        select SND_SOC_PCM3168A_I2C if I2C
        select SND_SOC_PCM3168A_SPI if SPI_MASTER
 +      select SND_SOC_PCM5102A
        select SND_SOC_PCM512x_I2C if I2C
        select SND_SOC_PCM512x_SPI if SPI_MASTER
        select SND_SOC_RT286 if I2C
@@@ -478,6 -478,11 +479,11 @@@ config SND_SOC_BT_SC
  config SND_SOC_DMIC
        tristate
  
+ config SND_SOC_HDMI_CODEC
+        tristate
+        select SND_PCM_ELD
+        select SND_PCM_IEC958
  config SND_SOC_ES8328
        tristate "Everest Semi ES8328 CODEC"
  
@@@ -576,9 -581,6 +582,9 @@@ config SND_SOC_PCM3168A_SP
        select SND_SOC_PCM3168A
        select REGMAP_SPI
  
 +config SND_SOC_PCM5102A
 +      tristate
 +
  config SND_SOC_PCM512x
        tristate
  
@@@ -633,7 -635,6 +639,7 @@@ config SND_SOC_RT551
  
  config SND_SOC_RT5616
        tristate "Realtek RT5616 CODEC"
 +      depends on I2C
  
  config SND_SOC_RT5631
        tristate "Realtek ALC5631/RT5631 CODEC"
index 4532a743b5f88d32b9ebabf37bf5efdfc8fa054f,d7185dda58b8f4d5103b6563fe2d3940b4a6a007..b7b99416537fd9016360e8b78676d0ea401d0ae6
@@@ -81,6 -81,7 +81,7 @@@ snd-soc-max9850-objs := max9850.
  snd-soc-mc13783-objs := mc13783.o
  snd-soc-ml26124-objs := ml26124.o
  snd-soc-nau8825-objs := nau8825.o
+ snd-soc-hdmi-codec-objs := hdmi-codec.o
  snd-soc-pcm1681-objs := pcm1681.o
  snd-soc-pcm179x-codec-objs := pcm179x.o
  snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
@@@ -89,7 -90,6 +90,7 @@@ snd-soc-pcm3008-objs := pcm3008.
  snd-soc-pcm3168a-objs := pcm3168a.o
  snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
  snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
 +snd-soc-pcm5102a-objs := pcm5102a.o
  snd-soc-pcm512x-objs := pcm512x.o
  snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
  snd-soc-pcm512x-spi-objs := pcm512x-spi.o
@@@ -291,6 -291,7 +292,7 @@@ obj-$(CONFIG_SND_SOC_MAX9850)      += snd-so
  obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
  obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
  obj-$(CONFIG_SND_SOC_NAU8825)   += snd-soc-nau8825.o
+ obj-$(CONFIG_SND_SOC_HDMI_CODEC)      += snd-soc-hdmi-codec.o
  obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
  obj-$(CONFIG_SND_SOC_PCM179X) += snd-soc-pcm179x-codec.o
  obj-$(CONFIG_SND_SOC_PCM179X_I2C)     += snd-soc-pcm179x-i2c.o
@@@ -299,7 -300,6 +301,7 @@@ obj-$(CONFIG_SND_SOC_PCM3008)      += snd-so
  obj-$(CONFIG_SND_SOC_PCM3168A)        += snd-soc-pcm3168a.o
  obj-$(CONFIG_SND_SOC_PCM3168A_I2C)    += snd-soc-pcm3168a-i2c.o
  obj-$(CONFIG_SND_SOC_PCM3168A_SPI)    += snd-soc-pcm3168a-spi.o
 +obj-$(CONFIG_SND_SOC_PCM5102A)        += snd-soc-pcm5102a.o
  obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
  obj-$(CONFIG_SND_SOC_PCM512x_I2C)     += snd-soc-pcm512x-i2c.o
  obj-$(CONFIG_SND_SOC_PCM512x_SPI)     += snd-soc-pcm512x-spi.o
index 13002f33384e4f4486b112d14663eb996393a30c,f1170e06023045488aa167af60216832a7087de7..37332976ccbb8b3fb803c37fa7cfc1fc42f3ea9a
@@@ -29,6 -29,7 +29,7 @@@
  #include <sound/hdaudio_ext.h>
  #include <sound/hda_i915.h>
  #include <sound/pcm_drm_eld.h>
+ #include <sound/hda_chmap.h>
  #include "../../hda/local.h"
  #include "hdac_hdmi.h"
  
@@@ -60,11 -61,17 +61,17 @@@ struct hdac_hdmi_cvt 
        struct hdac_hdmi_cvt_params params;
  };
  
+ /* Currently only spk_alloc, more to be added */
+ struct hdac_hdmi_parsed_eld {
+       u8 spk_alloc;
+ };
  struct hdac_hdmi_eld {
        bool    monitor_present;
        bool    eld_valid;
        int     eld_size;
        char    eld_buffer[ELD_MAX_SIZE];
+       struct  hdac_hdmi_parsed_eld info;
  };
  
  struct hdac_hdmi_pin {
        struct hdac_ext_device *edev;
        int repoll_count;
        struct delayed_work work;
+       struct mutex lock;
+       bool chmap_set;
+       unsigned char chmap[8]; /* ALSA API channel-map */
+       int channels; /* current number of channels */
  };
  
  struct hdac_hdmi_pcm {
@@@ -100,8 -111,22 +111,22 @@@ struct hdac_hdmi_priv 
        int num_pin;
        int num_cvt;
        struct mutex pin_mutex;
+       struct hdac_chmap chmap;
  };
  
+ static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
+                                               int pcm_idx)
+ {
+       struct hdac_hdmi_pcm *pcm;
+       list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+               if (pcm->pcm_id == pcm_idx)
+                       return pcm;
+       }
+       return NULL;
+ }
  static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
  {
        struct hdac_device *hdac = dev_to_hdac_dev(dev);
@@@ -278,26 -303,31 +303,31 @@@ static int hdac_hdmi_setup_audio_infofr
        int i;
        const u8 *eld_buf;
        u8 conn_type;
-       int channels = 2;
+       int channels, ca;
  
        list_for_each_entry(pin, &hdmi->pin_list, head) {
                if (pin->nid == pin_nid)
                        break;
        }
  
+       ca = snd_hdac_channel_allocation(&hdac->hdac, pin->eld.info.spk_alloc,
+                       pin->channels, pin->chmap_set, true, pin->chmap);
+       channels = snd_hdac_get_active_channels(ca);
+       hdmi->chmap.ops.set_channel_count(&hdac->hdac, cvt_nid, channels);
+       snd_hdac_setup_channel_mapping(&hdmi->chmap, pin->nid, false, ca,
+                               pin->channels, pin->chmap, pin->chmap_set);
        eld_buf = pin->eld.eld_buffer;
        conn_type = drm_eld_get_conn_type(eld_buf);
  
-       /* setup channel count */
-       snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
-                           AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
        switch (conn_type) {
        case DRM_ELD_CONN_TYPE_HDMI:
                hdmi_audio_infoframe_init(&frame);
  
-               /* Default stereo for now */
                frame.channels = channels;
+               frame.channel_allocation = ca;
  
                ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
                if (ret < 0)
                dp_ai.len       = 0x1b;
                dp_ai.ver       = 0x11 << 2;
                dp_ai.CC02_CT47 = channels - 1;
-               dp_ai.CA        = 0;
+               dp_ai.CA        = ca;
  
                dip = (u8 *)&dp_ai;
                break;
@@@ -370,17 -400,23 +400,23 @@@ static int hdac_hdmi_playback_prepare(s
        struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
        struct hdac_hdmi_priv *hdmi = hdac->private_data;
        struct hdac_hdmi_dai_pin_map *dai_map;
+       struct hdac_hdmi_pin *pin;
        struct hdac_ext_dma_params *dd;
        int ret;
  
        dai_map = &hdmi->dai_map[dai->id];
+       pin = dai_map->pin;
  
        dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
        dev_dbg(&hdac->hdac.dev, "stream tag from cpu dai %d format in cvt 0x%x\n",
                        dd->stream_tag, dd->format);
  
+       mutex_lock(&pin->lock);
+       pin->channels = substream->runtime->channels;
        ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
                                                dai_map->pin->nid);
+       mutex_unlock(&pin->lock);
        if (ret < 0)
                return ret;
  
@@@ -640,6 -676,12 +676,12 @@@ static void hdac_hdmi_pcm_close(struct 
                snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
                        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
  
+               mutex_lock(&dai_map->pin->lock);
+               dai_map->pin->chmap_set = false;
+               memset(dai_map->pin->chmap, 0, sizeof(dai_map->pin->chmap));
+               dai_map->pin->channels = 0;
+               mutex_unlock(&dai_map->pin->lock);
                dai_map->pin = NULL;
        }
  }
  static int
  hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
  {
+       unsigned int chans;
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
        int err;
  
-       /* Only stereo supported as of now */
-       cvt->params.channels_min = cvt->params.channels_max = 2;
+       chans = get_wcaps(hdac, cvt->nid);
+       chans = get_wcaps_channels(chans);
+       cvt->params.channels_min = 2;
+       cvt->params.channels_max = chans;
+       if (chans > hdmi->chmap.channels_max)
+               hdmi->chmap.channels_max = chans;
  
        err = snd_hdac_query_supported_pcm(hdac, cvt->nid,
                        &cvt->params.rates,
@@@ -1008,6 -1059,12 +1059,12 @@@ static int hdac_hdmi_add_cvt(struct hda
        return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
  }
  
+ static void hdac_hdmi_parse_eld(struct hdac_ext_device *edev,
+                       struct hdac_hdmi_pin *pin)
+ {
+       pin->eld.info.spk_alloc = pin->eld.eld_buffer[DRM_ELD_SPEAKER];
+ }
  static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
  {
        struct hdac_ext_device *edev = pin->edev;
  
                                snd_jack_report(pcm->jack, SND_JACK_AVOUT);
                        }
+                       hdac_hdmi_parse_eld(edev, pin);
  
                        print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
                                        pin->eld.eld_buffer, pin->eld.eld_size);
@@@ -1123,6 -1181,7 +1181,7 @@@ static int hdac_hdmi_add_pin(struct hda
        hdmi->num_pin++;
  
        pin->edev = edev;
+       mutex_init(&pin->lock);
        INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
  
        return 0;
@@@ -1342,6 -1401,19 +1401,19 @@@ static struct i915_audio_component_audi
        .pin_eld_notify = hdac_hdmi_eld_notify_cb,
  };
  
+ static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
+                                               int device)
+ {
+       struct snd_soc_pcm_runtime *rtd;
+       list_for_each_entry(rtd, &card->rtd_list, list) {
+               if (rtd->pcm && (rtd->pcm->device == device))
+                       return rtd->pcm;
+       }
+       return NULL;
+ }
  int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
  {
        char jack_name[NAME_SIZE];
                snd_soc_component_get_dapm(&codec->component);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pcm *pcm;
+       struct snd_pcm *snd_pcm;
+       int err;
  
        /*
         * this is a new PCM device, create new pcm and
        pcm->pcm_id = device;
        pcm->cvt = hdmi->dai_map[dai->id].cvt;
  
+       snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device);
+       if (snd_pcm) {
+               err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap);
+               if (err < 0) {
+                       dev_err(&edev->hdac.dev,
+                               "chmap control add failed with err: %d for pcm: %d\n",
+                               err, device);
+                       kfree(pcm);
+                       return err;
+               }
+       }
        list_add_tail(&pcm->head, &hdmi->pcm_list);
  
        sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
@@@ -1378,18 -1464,10 +1464,18 @@@ static int hdmi_codec_probe(struct snd_
        struct snd_soc_dapm_context *dapm =
                snd_soc_component_get_dapm(&codec->component);
        struct hdac_hdmi_pin *pin;
 +      struct hdac_ext_link *hlink = NULL;
        int ret;
  
        edev->scodec = codec;
  
 +      /*
 +       * hold the ref while we probe, also no need to drop the ref on
 +       * exit, we call pm_runtime_suspend() so that will do for us
 +       */
 +      hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
 +      snd_hdac_ext_bus_link_get(edev->ebus, hlink);
 +
        ret = create_fill_widget_route_map(dapm);
        if (ret < 0)
                return ret;
@@@ -1428,39 -1506,32 +1514,39 @@@ static int hdmi_codec_remove(struct snd
  }
  
  #ifdef CONFIG_PM
 -static int hdmi_codec_resume(struct snd_soc_codec *codec)
 +static int hdmi_codec_prepare(struct device *dev)
  {
 -      struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
 +      struct hdac_ext_device *edev = to_hda_ext_device(dev);
 +      struct hdac_device *hdac = &edev->hdac;
 +
 +      pm_runtime_get_sync(&edev->hdac.dev);
 +
 +      /*
 +       * Power down afg.
 +       * codec_read is preferred over codec_write to set the power state.
 +       * This way verb is send to set the power state and response
 +       * is received. So setting power state is ensured without using loop
 +       * to read the state.
 +       */
 +      snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE,
 +                                                      AC_PWRST_D3);
 +
 +      return 0;
 +}
 +
 +static void hdmi_codec_complete(struct device *dev)
 +{
 +      struct hdac_ext_device *edev = to_hda_ext_device(dev);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pin *pin;
        struct hdac_device *hdac = &edev->hdac;
 -      struct hdac_bus *bus = hdac->bus;
 -      int err;
 -      unsigned long timeout;
 -
 -      hdac_hdmi_skl_enable_all_pins(&edev->hdac);
 -      hdac_hdmi_skl_enable_dp12(&edev->hdac);
  
        /* Power up afg */
 -      if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) {
 -
 -              snd_hdac_codec_write(hdac, hdac->afg, 0,
 -                      AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
 +      snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE,
 +                                                      AC_PWRST_D0);
  
 -              /* Wait till power state is set to D0 */
 -              timeout = jiffies + msecs_to_jiffies(1000);
 -              while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)
 -                              && time_before(jiffies, timeout)) {
 -                      msleep(50);
 -              }
 -      }
 +      hdac_hdmi_skl_enable_all_pins(&edev->hdac);
 +      hdac_hdmi_skl_enable_dp12(&edev->hdac);
  
        /*
         * As the ELD notify callback request is not entertained while the
        list_for_each_entry(pin, &hdmi->pin_list, head)
                hdac_hdmi_present_sense(pin, 1);
  
 -      /*
 -       * Codec power is turned ON during controller resume.
 -       * Turn it OFF here
 -       */
 -      err = snd_hdac_display_power(bus, false);
 -      if (err < 0) {
 -              dev_err(bus->dev,
 -                      "Cannot turn OFF display power on i915, err: %d\n",
 -                      err);
 -              return err;
 -      }
 -
 -      return 0;
 +      pm_runtime_put_sync(&edev->hdac.dev);
  }
  #else
 -#define hdmi_codec_resume NULL
 +#define hdmi_codec_prepare NULL
 +#define hdmi_codec_complete NULL
  #endif
  
  static struct snd_soc_codec_driver hdmi_hda_codec = {
        .probe          = hdmi_codec_probe,
        .remove         = hdmi_codec_remove,
 -      .resume         = hdmi_codec_resume,
        .idle_bias_off  = true,
  };
  
+ static void hdac_hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+                                       unsigned char *chmap)
+ {
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+       struct hdac_hdmi_pin *pin = pcm->pin;
+       /* chmap is already set to 0 in caller */
+       if (!pin)
+               return;
+       memcpy(chmap, pin->chmap, ARRAY_SIZE(pin->chmap));
+ }
+ static void hdac_hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+                               unsigned char *chmap, int prepared)
+ {
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+       struct hdac_hdmi_pin *pin = pcm->pin;
+       mutex_lock(&pin->lock);
+       pin->chmap_set = true;
+       memcpy(pin->chmap, chmap, ARRAY_SIZE(pin->chmap));
+       if (prepared)
+               hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid, pin->nid);
+       mutex_unlock(&pin->lock);
+ }
+ static bool is_hdac_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
+ {
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+       struct hdac_hdmi_pin *pin = pcm->pin;
+       return pin ? true:false;
+ }
+ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx)
+ {
+       struct hdac_ext_device *edev = to_ehdac_device(hdac);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = get_hdmi_pcm_from_id(hdmi, pcm_idx);
+       struct hdac_hdmi_pin *pin = pcm->pin;
+       if (!pin || !pin->eld.eld_valid)
+               return 0;
+       return pin->eld.info.spk_alloc;
+ }
  static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
  {
        struct hdac_device *codec = &edev->hdac;
        struct hdac_hdmi_priv *hdmi_priv;
        struct snd_soc_dai_driver *hdmi_dais = NULL;
 +      struct hdac_ext_link *hlink = NULL;
        int num_dais = 0;
        int ret = 0;
  
 +      /* hold the ref while we probe */
 +      hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
 +      snd_hdac_ext_bus_link_get(edev->ebus, hlink);
 +
        hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
        if (hdmi_priv == NULL)
                return -ENOMEM;
  
        edev->private_data = hdmi_priv;
+       snd_hdac_register_chmap_ops(codec, &hdmi_priv->chmap);
+       hdmi_priv->chmap.ops.get_chmap = hdac_hdmi_get_chmap;
+       hdmi_priv->chmap.ops.set_chmap = hdac_hdmi_set_chmap;
+       hdmi_priv->chmap.ops.is_pcm_attached = is_hdac_hdmi_pcm_attached;
+       hdmi_priv->chmap.ops.get_spk_alloc = hdac_hdmi_get_spk_alloc;
  
        dev_set_drvdata(&codec->dev, edev);
  
        }
  
        /* ASoC specific initialization */
 -      return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
 -                      hdmi_dais, num_dais);
 +      ret = snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
 +                                      hdmi_dais, num_dais);
 +
 +      snd_hdac_ext_bus_link_put(edev->ebus, hlink);
 +
 +      return ret;
  }
  
  static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
@@@ -1574,8 -1707,6 +1719,8 @@@ static int hdac_hdmi_runtime_suspend(st
        struct hdac_device *hdac = &edev->hdac;
        struct hdac_bus *bus = hdac->bus;
        unsigned long timeout;
 +      struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
 +      struct hdac_ext_link *hlink = NULL;
        int err;
  
        dev_dbg(dev, "Enter: %s\n", __func__);
        if (!bus)
                return 0;
  
 -      /* Power down afg */
 -      if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) {
 -              snd_hdac_codec_write(hdac, hdac->afg, 0,
 -                      AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 -
 -              /* Wait till power state is set to D3 */
 -              timeout = jiffies + msecs_to_jiffies(1000);
 -              while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)
 -                              && time_before(jiffies, timeout)) {
 -
 -                      msleep(50);
 -              }
 -      }
 -
 +      /*
 +       * Power down afg.
 +       * codec_read is preferred over codec_write to set the power state.
 +       * This way verb is send to set the power state and response
 +       * is received. So setting power state is ensured without using loop
 +       * to read the state.
 +       */
 +      snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE,
 +                                                      AC_PWRST_D3);
        err = snd_hdac_display_power(bus, false);
        if (err < 0) {
                dev_err(bus->dev, "Cannot turn on display power on i915\n");
                return err;
        }
  
 +      hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
 +      snd_hdac_ext_bus_link_put(ebus, hlink);
 +
        return 0;
  }
  
@@@ -1610,8 -1743,6 +1755,8 @@@ static int hdac_hdmi_runtime_resume(str
        struct hdac_ext_device *edev = to_hda_ext_device(dev);
        struct hdac_device *hdac = &edev->hdac;
        struct hdac_bus *bus = hdac->bus;
 +      struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
 +      struct hdac_ext_link *hlink = NULL;
        int err;
  
        dev_dbg(dev, "Enter: %s\n", __func__);
        if (!bus)
                return 0;
  
 +      hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
 +      snd_hdac_ext_bus_link_get(ebus, hlink);
 +
        err = snd_hdac_display_power(bus, true);
        if (err < 0) {
                dev_err(bus->dev, "Cannot turn on display power on i915\n");
        hdac_hdmi_skl_enable_dp12(&edev->hdac);
  
        /* Power up afg */
 -      if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
 -              snd_hdac_codec_write(hdac, hdac->afg, 0,
 -                      AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
 +      snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE,
 +                                                      AC_PWRST_D0);
  
        return 0;
  }
  
  static const struct dev_pm_ops hdac_hdmi_pm = {
        SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
 +      .prepare = hdmi_codec_prepare,
 +      .complete = hdmi_codec_complete,
  };
  
  static const struct hda_device_id hdmi_list[] = {
index 7cc725153422714a7728e69fb190d558564ee79a,8ccc97c6255f5ff2ba8513a73daf541ec9927fd4..d2808652b9741f2fced8381e356563b63d54bd48
  static struct snd_soc_jack skylake_headset;
  static struct snd_soc_card skylake_audio_card;
  
+ struct skl_hdmi_pcm {
+       struct list_head head;
+       struct snd_soc_dai *codec_dai;
+       int device;
+ };
+ struct skl_nau8825_private {
+       struct list_head hdmi_pcm_list;
+ };
  enum {
        SKL_DPCM_AUDIO_PB = 0,
        SKL_DPCM_AUDIO_CP,
@@@ -192,23 -202,56 +202,56 @@@ static int skylake_nau8825_codec_init(s
  
  static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
  {
+       struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+       pcm->device = SKL_DPCM_AUDIO_HDMI1_PB;
+       pcm->codec_dai = dai;
  
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+       return 0;
  }
  
  static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
  {
+       struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
  
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
+       pcm->device = SKL_DPCM_AUDIO_HDMI2_PB;
+       pcm->codec_dai = dai;
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+       return 0;
  }
  
  static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
  {
+       struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
  
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+       pcm->device = SKL_DPCM_AUDIO_HDMI3_PB;
+       pcm->codec_dai = dai;
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+       return 0;
  }
  
  static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
@@@ -391,6 -434,7 +434,6 @@@ static struct snd_soc_dai_link skylake_
                .platform_name = "0000:00:1f.3",
                .init = NULL,
                .dpcm_capture = 1,
 -              .ignore_suspend = 1,
                .nonatomic = 1,
                .dynamic = 1,
                .ops = &skylaye_refcap_ops,
        {
                /* SSP0 - Codec */
                .name = "SSP0-Codec",
 -              .be_id = 0,
 +              .id = 0,
                .cpu_dai_name = "SSP0 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
        {
                /* SSP1 - Codec */
                .name = "SSP1-Codec",
 -              .be_id = 1,
 +              .id = 1,
                .cpu_dai_name = "SSP1 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
        },
        {
                .name = "dmic01",
 -              .be_id = 2,
 +              .id = 2,
                .cpu_dai_name = "DMIC01 Pin",
                .codec_name = "dmic-codec",
                .codec_dai_name = "dmic-hifi",
        },
        {
                .name = "iDisp1",
 -              .be_id = 3,
 +              .id = 3,
                .cpu_dai_name = "iDisp1 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi1",
        },
        {
                .name = "iDisp2",
 -              .be_id = 4,
 +              .id = 4,
                .cpu_dai_name = "iDisp2 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi2",
        },
        {
                .name = "iDisp3",
 -              .be_id = 5,
 +              .id = 5,
                .cpu_dai_name = "iDisp3 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi3",
        },
  };
  
+ static int skylake_card_late_probe(struct snd_soc_card *card)
+ {
+       struct skl_nau8825_private *ctx = snd_soc_card_get_drvdata(card);
+       struct skl_hdmi_pcm *pcm;
+       int err;
+       list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+ }
  /* skylake audio machine driver for SPT + NAU88L25 */
  static struct snd_soc_card skylake_audio_card = {
        .name = "sklnau8825max",
        .dapm_routes = skylake_map,
        .num_dapm_routes = ARRAY_SIZE(skylake_map),
        .fully_routed = true,
+       .late_probe = skylake_card_late_probe,
  };
  
  static int skylake_audio_probe(struct platform_device *pdev)
  {
+       struct skl_nau8825_private *ctx;
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
        skylake_audio_card.dev = &pdev->dev;
+       snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
  
        return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
  }
index 73cbddb640c6601216b9064db70ad1d877ebe101,bde85bf989b8dfc93168073a609261187643c193..e19aa99c4f7246914fafd1819e561f3b60c7b250
  static struct snd_soc_jack skylake_headset;
  static struct snd_soc_card skylake_audio_card;
  
+ struct skl_hdmi_pcm {
+       struct list_head head;
+       struct snd_soc_dai *codec_dai;
+       int device;
+ };
+ struct skl_nau88125_private {
+       struct list_head hdmi_pcm_list;
+ };
  enum {
        SKL_DPCM_AUDIO_PB = 0,
        SKL_DPCM_AUDIO_CP,
@@@ -222,24 -231,57 +231,57 @@@ static int skylake_nau8825_codec_init(s
  
  static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
  {
+       struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+       pcm->device = SKL_DPCM_AUDIO_HDMI1_PB;
+       pcm->codec_dai = dai;
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
  
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
+       return 0;
  }
  
  static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
  {
+       struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+       pcm->device = SKL_DPCM_AUDIO_HDMI2_PB;
+       pcm->codec_dai = dai;
  
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+       return 0;
  }
  
  
  static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
  {
+       struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
  
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
+       pcm->device = SKL_DPCM_AUDIO_HDMI3_PB;
+       pcm->codec_dai = dai;
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+       return 0;
  }
  
  static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
@@@ -440,6 -482,7 +482,6 @@@ static struct snd_soc_dai_link skylake_
                .platform_name = "0000:00:1f.3",
                .init = NULL,
                .dpcm_capture = 1,
 -              .ignore_suspend = 1,
                .nonatomic = 1,
                .dynamic = 1,
                .ops = &skylaye_refcap_ops,
        {
                /* SSP0 - Codec */
                .name = "SSP0-Codec",
 -              .be_id = 0,
 +              .id = 0,
                .cpu_dai_name = "SSP0 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
        {
                /* SSP1 - Codec */
                .name = "SSP1-Codec",
 -              .be_id = 1,
 +              .id = 1,
                .cpu_dai_name = "SSP1 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
        },
        {
                .name = "dmic01",
 -              .be_id = 2,
 +              .id = 2,
                .cpu_dai_name = "DMIC01 Pin",
                .codec_name = "dmic-codec",
                .codec_dai_name = "dmic-hifi",
        },
        {
                .name = "iDisp1",
 -              .be_id = 3,
 +              .id = 3,
                .cpu_dai_name = "iDisp1 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi1",
        },
        {
                .name = "iDisp2",
 -              .be_id = 4,
 +              .id = 4,
                .cpu_dai_name = "iDisp2 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi2",
        },
        {
                .name = "iDisp3",
 -              .be_id = 5,
 +              .id = 5,
                .cpu_dai_name = "iDisp3 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi3",
        },
  };
  
+ static int skylake_card_late_probe(struct snd_soc_card *card)
+ {
+       struct skl_nau88125_private *ctx = snd_soc_card_get_drvdata(card);
+       struct skl_hdmi_pcm *pcm;
+       int err;
+       list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+ }
  /* skylake audio machine driver for SPT + NAU88L25 */
  static struct snd_soc_card skylake_audio_card = {
        .name = "sklnau8825adi",
        .codec_conf = ssm4567_codec_conf,
        .num_configs = ARRAY_SIZE(ssm4567_codec_conf),
        .fully_routed = true,
+       .late_probe = skylake_card_late_probe,
  };
  
  static int skylake_audio_probe(struct platform_device *pdev)
  {
+       struct skl_nau88125_private *ctx;
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
        skylake_audio_card.dev = &pdev->dev;
+       snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
  
        return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
  }
index d4cdb949f733e6a229155d6ff699e65032c8d8aa,06de802898fb9dc91e641713b89eb196eda14501..426b48233fdb1eed38824dc4b949a1dac8ec3907
  
  static struct snd_soc_jack skylake_headset;
  
+ struct skl_hdmi_pcm {
+       struct list_head head;
+       struct snd_soc_dai *codec_dai;
+       int device;
+ };
+ struct skl_rt286_private {
+       struct list_head hdmi_pcm_list;
+ };
  enum {
        SKL_DPCM_AUDIO_PB = 0,
        SKL_DPCM_AUDIO_CP,
@@@ -142,9 -152,20 +152,20 @@@ static int skylake_rt286_codec_init(str
  
  static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd)
  {
+       struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai = rtd->codec_dai;
+       struct skl_hdmi_pcm *pcm;
+       pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
  
-       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id);
+       pcm->device = SKL_DPCM_AUDIO_HDMI1_PB + dai->id;
+       pcm->codec_dai = dai;
+       list_add_tail(&pcm->head, &ctx->hdmi_pcm_list);
+       return 0;
  }
  
  static unsigned int rates[] = {
@@@ -317,6 -338,7 +338,6 @@@ static struct snd_soc_dai_link skylake_
                .platform_name = "0000:00:1f.3",
                .init = NULL,
                .dpcm_capture = 1,
 -              .ignore_suspend = 1,
                .nonatomic = 1,
                .dynamic = 1,
        },
        {
                /* SSP0 - Codec */
                .name = "SSP0-Codec",
 -              .be_id = 0,
 +              .id = 0,
                .cpu_dai_name = "SSP0 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
        },
        {
                .name = "dmic01",
 -              .be_id = 1,
 +              .id = 1,
                .cpu_dai_name = "DMIC01 Pin",
                .codec_name = "dmic-codec",
                .codec_dai_name = "dmic-hifi",
        },
        {
                .name = "iDisp1",
 -              .be_id = 2,
 +              .id = 2,
                .cpu_dai_name = "iDisp1 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi1",
        },
        {
                .name = "iDisp2",
 -              .be_id = 3,
 +              .id = 3,
                .cpu_dai_name = "iDisp2 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi2",
        },
        {
                .name = "iDisp3",
 -              .be_id = 4,
 +              .id = 4,
                .cpu_dai_name = "iDisp3 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi3",
        },
  };
  
+ static int skylake_card_late_probe(struct snd_soc_card *card)
+ {
+       struct skl_rt286_private *ctx = snd_soc_card_get_drvdata(card);
+       struct skl_hdmi_pcm *pcm;
+       int err;
+       list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
+               err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+ }
  /* skylake audio machine driver for SPT + RT286S */
  static struct snd_soc_card skylake_rt286 = {
        .name = "skylake-rt286",
        .dapm_routes = skylake_rt286_map,
        .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
        .fully_routed = true,
+       .late_probe = skylake_card_late_probe,
  };
  
  static int skylake_audio_probe(struct platform_device *pdev)
  {
+       struct skl_rt286_private *ctx;
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&ctx->hdmi_pcm_list);
        skylake_rt286.dev = &pdev->dev;
+       snd_soc_card_set_drvdata(&skylake_rt286, ctx);
  
        return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
  }
index 15480234b20b9f148a9135ccad8b3b970c33cf96,8de921272f718a1d74f489b92d532ee716b8804a..7c81b31748ffcba2ab7c5d8a8961eb83d4134f55
@@@ -51,7 -51,7 +51,7 @@@ static struct snd_pcm_hardware azx_pcm_
        .rate_min =             8000,
        .rate_max =             48000,
        .channels_min =         1,
-       .channels_max =         HDA_QUAD,
+       .channels_max =         8,
        .buffer_bytes_max =     AZX_MAX_BUF_SIZE,
        .period_bytes_min =     128,
        .period_bytes_max =     AZX_MAX_BUF_SIZE / 2,
@@@ -213,7 -213,7 +213,7 @@@ static int skl_be_prepare(struct snd_pc
        struct skl_sst *ctx = skl->skl_sst;
        struct skl_module_cfg *mconfig;
  
 -      if ((dai->playback_active > 1) || (dai->capture_active > 1))
 +      if (dai->playback_widget->power || dai->capture_widget->power)
                return 0;
  
        mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
@@@ -402,33 -402,23 +402,33 @@@ static int skl_pcm_trigger(struct snd_p
        struct skl_module_cfg *mconfig;
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
        struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
 +      struct snd_soc_dapm_widget *w;
        int ret;
  
        mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
        if (!mconfig)
                return -EIO;
  
 +      if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 +              w = dai->playback_widget;
 +      else
 +              w = dai->capture_widget;
 +
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_RESUME:
 -              skl_pcm_prepare(substream, dai);
 -              /*
 -               * enable DMA Resume enable bit for the stream, set the dpib
 -               * & lpib position to resune before starting the DMA
 -               */
 -              snd_hdac_ext_stream_drsm_enable(ebus, true,
 -                                      hdac_stream(stream)->index);
 -              snd_hdac_ext_stream_set_dpibr(ebus, stream, stream->dpib);
 -              snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
 +              if (!w->ignore_suspend) {
 +                      skl_pcm_prepare(substream, dai);
 +                      /*
 +                       * enable DMA Resume enable bit for the stream, set the
 +                       * dpib & lpib position to resume before starting the
 +                       * DMA
 +                       */
 +                      snd_hdac_ext_stream_drsm_enable(ebus, true,
 +                                              hdac_stream(stream)->index);
 +                      snd_hdac_ext_stream_set_dpibr(ebus, stream,
 +                                                      stream->dpib);
 +                      snd_hdac_ext_stream_set_lpib(stream, stream->lpib);
 +              }
  
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                        return ret;
  
                ret = skl_decoupled_trigger(substream, cmd);
 -              if (cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
 +              if ((cmd == SNDRV_PCM_TRIGGER_SUSPEND) && !w->ignore_suspend) {
                        /* save the dpib and lpib positions */
                        stream->dpib = readl(ebus->bus.remap_addr +
                                        AZX_REG_VS_SDXDPIB_XBASE +
@@@ -533,6 -523,7 +533,6 @@@ static int skl_link_pcm_prepare(struct 
        if (!link)
                return -EINVAL;
  
 -      snd_hdac_ext_bus_link_power_up(link);
        snd_hdac_ext_link_stream_reset(link_dev);
  
        snd_hdac_ext_link_stream_setup(link_dev, format_val);
@@@ -691,7 -682,7 +691,7 @@@ static struct snd_soc_dai_driver skl_pl
        .playback = {
                .stream_name = "HDMI1 Playback",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
                        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
                        SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
        .playback = {
                .stream_name = "HDMI2 Playback",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
                        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
                        SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
        .playback = {
                .stream_name = "HDMI3 Playback",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
                        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
                        SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
                .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
  },
 +{
 +      .name = "SSP2 Pin",
 +      .ops = &skl_be_ssp_dai_ops,
 +      .playback = {
 +              .stream_name = "ssp2 Tx",
 +              .channels_min = HDA_STEREO,
 +              .channels_max = HDA_STEREO,
 +              .rates = SNDRV_PCM_RATE_48000,
 +              .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +      },
 +      .capture = {
 +              .stream_name = "ssp2 Rx",
 +              .channels_min = HDA_STEREO,
 +              .channels_max = HDA_STEREO,
 +              .rates = SNDRV_PCM_RATE_48000,
 +              .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +      },
 +},
 +{
 +      .name = "SSP3 Pin",
 +      .ops = &skl_be_ssp_dai_ops,
 +      .playback = {
 +              .stream_name = "ssp3 Tx",
 +              .channels_min = HDA_STEREO,
 +              .channels_max = HDA_STEREO,
 +              .rates = SNDRV_PCM_RATE_48000,
 +              .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +      },
 +      .capture = {
 +              .stream_name = "ssp3 Rx",
 +              .channels_min = HDA_STEREO,
 +              .channels_max = HDA_STEREO,
 +              .rates = SNDRV_PCM_RATE_48000,
 +              .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +      },
 +},
 +{
 +      .name = "SSP4 Pin",
 +      .ops = &skl_be_ssp_dai_ops,
 +      .playback = {
 +              .stream_name = "ssp4 Tx",
 +              .channels_min = HDA_STEREO,
 +              .channels_max = HDA_STEREO,
 +              .rates = SNDRV_PCM_RATE_48000,
 +              .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +      },
 +      .capture = {
 +              .stream_name = "ssp4 Rx",
 +              .channels_min = HDA_STEREO,
 +              .channels_max = HDA_STEREO,
 +              .rates = SNDRV_PCM_RATE_48000,
 +              .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +      },
 +},
 +{
 +      .name = "SSP5 Pin",
 +      .ops = &skl_be_ssp_dai_ops,
 +      .playback = {
 +              .stream_name = "ssp5 Tx",
 +              .channels_min = HDA_STEREO,
 +              .channels_max = HDA_STEREO,
 +              .rates = SNDRV_PCM_RATE_48000,
 +              .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +      },
 +      .capture = {
 +              .stream_name = "ssp5 Rx",
 +              .channels_min = HDA_STEREO,
 +              .channels_max = HDA_STEREO,
 +              .rates = SNDRV_PCM_RATE_48000,
 +              .formats = SNDRV_PCM_FMTBIT_S16_LE,
 +      },
 +},
  {
        .name = "iDisp1 Pin",
        .ops = &skl_link_dai_ops,
        .playback = {
                .stream_name = "iDisp1 Tx",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
                        SNDRV_PCM_FMTBIT_S24_LE,
        .playback = {
                .stream_name = "iDisp2 Tx",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
                        SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
        .playback = {
                .stream_name = "iDisp3 Tx",
                .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
+               .channels_max = 8,
                .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
                        SNDRV_PCM_RATE_48000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
index a1f4478fabcb307dab5f40d7f776c5e0603a7e82,8fceb7a041470262da76d34f528d397f6856d249..3e036b0349b9771db5c16f0ce0b781fb4b8dca77
@@@ -154,13 -154,32 +154,32 @@@ static void skl_dump_mconfig(struct skl
        dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt[0].ch_cfg);
  }
  
+ static void skl_tplg_update_chmap(struct skl_module_fmt *fmt, int chs)
+ {
+       int slot_map = 0xFFFFFFFF;
+       int start_slot = 0;
+       int i;
+       for (i = 0; i < chs; i++) {
+               /*
+                * For 2 channels with starting slot as 0, slot map will
+                * look like 0xFFFFFF10.
+                */
+               slot_map &= (~(0xF << (4 * i)) | (start_slot << (4 * i)));
+               start_slot++;
+       }
+       fmt->ch_map = slot_map;
+ }
  static void skl_tplg_update_params(struct skl_module_fmt *fmt,
                        struct skl_pipe_params *params, int fixup)
  {
        if (fixup & SKL_RATE_FIXUP_MASK)
                fmt->s_freq = params->s_freq;
-       if (fixup & SKL_CH_FIXUP_MASK)
+       if (fixup & SKL_CH_FIXUP_MASK) {
                fmt->channels = params->ch;
+               skl_tplg_update_chmap(fmt, fmt->channels);
+       }
        if (fixup & SKL_FMT_FIXUP_MASK) {
                fmt->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
  
@@@ -239,7 -258,6 +258,7 @@@ static void skl_tplg_update_buffer_size
  {
        int multiplier = 1;
        struct skl_module_fmt *in_fmt, *out_fmt;
 +      int in_rate, out_rate;
  
  
        /* Since fixups is applied to pin 0 only, ibs, obs needs
  
        if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
                multiplier = 5;
 -      mcfg->ibs = (in_fmt->s_freq / 1000) *
 -                              (mcfg->in_fmt->channels) *
 -                              (mcfg->in_fmt->bit_depth >> 3) *
 -                              multiplier;
 -
 -      mcfg->obs = (mcfg->out_fmt->s_freq / 1000) *
 -                              (mcfg->out_fmt->channels) *
 -                              (mcfg->out_fmt->bit_depth >> 3) *
 -                              multiplier;
 +
 +      if (in_fmt->s_freq % 1000)
 +              in_rate = (in_fmt->s_freq / 1000) + 1;
 +      else
 +              in_rate = (in_fmt->s_freq / 1000);
 +
 +      mcfg->ibs = in_rate * (mcfg->in_fmt->channels) *
 +                      (mcfg->in_fmt->bit_depth >> 3) *
 +                      multiplier;
 +
 +      if (mcfg->out_fmt->s_freq % 1000)
 +              out_rate = (mcfg->out_fmt->s_freq / 1000) + 1;
 +      else
 +              out_rate = (mcfg->out_fmt->s_freq / 1000);
 +
 +      mcfg->obs = out_rate * (mcfg->out_fmt->channels) *
 +                      (mcfg->out_fmt->bit_depth >> 3) *
 +                      multiplier;
  }
  
  static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
@@@ -495,15 -504,11 +514,15 @@@ skl_tplg_init_pipe_modules(struct skl *
                if (!skl_is_pipe_mcps_avail(skl, mconfig))
                        return -ENOMEM;
  
 +              skl_tplg_alloc_pipe_mcps(skl, mconfig);
 +
                if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) {
                        ret = ctx->dsp->fw_ops.load_mod(ctx->dsp,
                                mconfig->id.module_id, mconfig->guid);
                        if (ret < 0)
                                return ret;
 +
 +                      mconfig->m_state = SKL_MODULE_LOADED;
                }
  
                /* update blob if blob is null for be with default value */
                ret = skl_tplg_set_module_params(w, ctx);
                if (ret < 0)
                        return ret;
 -              skl_tplg_alloc_pipe_mcps(skl, mconfig);
        }
  
        return 0;
@@@ -537,8 -543,7 +556,8 @@@ static int skl_tplg_unload_pipe_modules
        list_for_each_entry(w_module, &pipe->w_list, node) {
                mconfig  = w_module->w->priv;
  
 -              if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod)
 +              if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod &&
 +                      mconfig->m_state > SKL_MODULE_UNINIT)
                        return ctx->dsp->fw_ops.unload_mod(ctx->dsp,
                                                mconfig->id.module_id);
        }
@@@ -572,9 -577,6 +591,9 @@@ static int skl_tplg_mixer_dapm_pre_pmu_
        if (!skl_is_pipe_mem_avail(skl, mconfig))
                return -ENOMEM;
  
 +      skl_tplg_alloc_pipe_mem(skl, mconfig);
 +      skl_tplg_alloc_pipe_mcps(skl, mconfig);
 +
        /*
         * Create a list of modules for pipe.
         * This list contains modules from source to sink
                src_module = dst_module;
        }
  
 -      skl_tplg_alloc_pipe_mem(skl, mconfig);
 -      skl_tplg_alloc_pipe_mcps(skl, mconfig);
 -
        return 0;
  }
  
@@@ -1564,8 -1569,6 +1583,8 @@@ static int skl_tplg_widget_load(struct 
                return -ENOMEM;
  
        w->priv = mconfig;
 +      memcpy(&mconfig->guid, &dfw_config->uuid, 16);
 +
        mconfig->id.module_id = dfw_config->module_id;
        mconfig->id.instance_id = dfw_config->instance_id;
        mconfig->mcps = dfw_config->max_mcps;
        mconfig->time_slot = dfw_config->time_slot;
        mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
  
 -      if (dfw_config->is_loadable)
 -              memcpy(mconfig->guid, dfw_config->uuid,
 -                                      ARRAY_SIZE(dfw_config->uuid));
 -
        mconfig->m_in_pin = devm_kzalloc(bus->dev, (mconfig->max_in_queue) *
                                                sizeof(*mconfig->m_in_pin),
                                                GFP_KERNEL);