struct hdac_hdmi_pin *pin;
        struct hdac_hdmi_cvt *cvt;
        struct snd_jack *jack;
+       int stream_tag;
+       int channels;
+       int format;
 };
 
 struct hdac_hdmi_dai_pin_map {
        struct hdac_chmap chmap;
 };
 
-static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev,
-                       struct hdac_hdmi_dai_pin_map *dai_map);
+static struct hdac_hdmi_pcm *
+hdac_hdmi_get_pcm_from_cvt(struct hdac_hdmi_priv *hdmi,
+                          struct hdac_hdmi_cvt *cvt)
+{
+       struct hdac_hdmi_pcm *pcm = NULL;
+
+       list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+               if (pcm->cvt == cvt)
+                       break;
+       }
 
-static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
-                       struct hdac_hdmi_dai_pin_map *dai_map);
+       return pcm;
+}
 
 static struct hdac_hdmi_pcm *get_hdmi_pcm_from_id(struct hdac_hdmi_priv *hdmi,
                                                int pcm_idx)
 
 }
 
-static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
-                               hda_nid_t cvt_nid, hda_nid_t pin_nid,
-                               u32 stream_tag, int format)
-{
-       unsigned int val;
-
-       dev_dbg(&hdac->hdac.dev, "cvt nid %d pnid %d stream %d format 0x%x\n",
-                       cvt_nid, pin_nid, stream_tag, format);
-
-       val = (stream_tag << 4);
-
-       snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
-                               AC_VERB_SET_CHANNEL_STREAMID, val);
-       snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
-                               AC_VERB_SET_STREAM_FORMAT, format);
-
-       return 0;
-}
-
 static void
 hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
                                int packet_index, int byte_index)
        return 0;
 }
 
-static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
-               struct hdac_hdmi_dai_pin_map *dai_map, unsigned int pwr_state)
+static int hdac_hdmi_set_tdm_slot(struct snd_soc_dai *dai,
+               unsigned int tx_mask, unsigned int rx_mask,
+               int slots, int slot_width)
 {
-       /* Power up pin widget */
-       if (!snd_hdac_check_power_state(&edev->hdac, dai_map->pin->nid,
-                                               pwr_state))
-               snd_hdac_codec_write(&edev->hdac, dai_map->pin->nid, 0,
-                       AC_VERB_SET_POWER_STATE, pwr_state);
-
-       /* Power up converter */
-       if (!snd_hdac_check_power_state(&edev->hdac, dai_map->cvt->nid,
-                                               pwr_state))
-               snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-                       AC_VERB_SET_POWER_STATE, pwr_state);
-}
-
-static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
-{
-       struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
-       struct hdac_hdmi_priv *hdmi = hdac->private_data;
+       struct hdac_ext_device *edev = snd_soc_dai_get_drvdata(dai);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_dai_pin_map *dai_map;
-       struct hdac_hdmi_pin *pin;
-       struct hdac_ext_dma_params *dd;
-       int ret;
+       struct hdac_hdmi_pcm *pcm;
+
+       dev_dbg(&edev->hdac.dev, "%s: strm_tag: %d\n", __func__, tx_mask);
 
        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);
+       pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
 
-       hdac_hdmi_enable_cvt(hdac, dai_map);
-       ret = hdac_hdmi_enable_pin(hdac, dai_map);
-       if (ret < 0)
-               return ret;
-       mutex_lock(&pin->lock);
-       pin->channels = substream->runtime->channels;
+       if (pcm)
+               pcm->stream_tag = (tx_mask << 4);
 
-       ret = hdac_hdmi_setup_audio_infoframe(hdac, dai_map->cvt->nid,
-                                               dai_map->pin->nid);
-       mutex_unlock(&pin->lock);
-       if (ret < 0)
-               return ret;
-
-       return hdac_hdmi_setup_stream(hdac, dai_map->cvt->nid,
-                       dai_map->pin->nid, dd->stream_tag, dd->format);
+       return 0;
 }
 
 static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
        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;
+       struct hdac_hdmi_pcm *pcm;
+       int format;
 
        dai_map = &hdmi->dai_map[dai->id];
        pin = dai_map->pin;
                return -ENODEV;
        }
 
-       dd = snd_soc_dai_get_dma_data(dai, substream);
-       if (!dd) {
-               dd = kzalloc(sizeof(*dd), GFP_KERNEL);
-               if (!dd)
-                       return -ENOMEM;
-       }
-
-       dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
+       format = snd_hdac_calc_stream_format(params_rate(hparams),
                        params_channels(hparams), params_format(hparams),
                        24, 0);
 
-       snd_soc_dai_set_dma_data(dai, substream, (void *)dd);
-
-       return 0;
-}
-
-static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
-               struct snd_soc_dai *dai)
-{
-       struct hdac_ext_dma_params *dd;
-
-       dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
-
-       if (dd) {
-               snd_soc_dai_set_dma_data(dai, substream, NULL);
-               kfree(dd);
-       }
-
-       return 0;
-}
-
-static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev,
-               struct hdac_hdmi_dai_pin_map *dai_map)
-{
-       /* Enable transmission */
-       snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-                       AC_VERB_SET_DIGI_CONVERT_1, 1);
-
-       /* Category Code (CC) to zero */
-       snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-                       AC_VERB_SET_DIGI_CONVERT_2, 0);
-}
-
-static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
-               struct hdac_hdmi_dai_pin_map *dai_map)
-{
-       int mux_idx;
-       struct hdac_hdmi_pin *pin = dai_map->pin;
-
-       for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) {
-               if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) {
-                       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
-                                       AC_VERB_SET_CONNECT_SEL, mux_idx);
-                       break;
-               }
-       }
-
-       if (mux_idx == pin->num_mux_nids)
+       pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
+       if (!pcm)
                return -EIO;
 
-       /* Enable out path for this pin widget */
-       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-
-       hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
-
-       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
-                       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+       pcm->format = format;
+       pcm->channels = params_channels(hparams);
 
        return 0;
 }
                                pin->eld.eld_buffer);
 }
 
-static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
-               struct snd_soc_dai *dai)
-{
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_RESUME:
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               return hdac_hdmi_playback_prepare(substream, dai);
-
-       default:
-               return 0;
-       }
-
-       return 0;
-}
-
 static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
        dai_map = &hdmi->dai_map[dai->id];
 
        if (dai_map->pin) {
-               snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
-                               AC_VERB_SET_CHANNEL_STREAMID, 0);
-               snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
-                               AC_VERB_SET_STREAM_FORMAT, 0);
-
-               hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
-
-               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));
 }
 
 static int hdac_hdmi_fill_widget_info(struct device *dev,
-                               struct snd_soc_dapm_widget *w,
-                               enum snd_soc_dapm_type id, void *priv,
-                               const char *wname, const char *stream,
-                               struct snd_kcontrol_new *wc, int numkc)
+               struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id,
+               void *priv, const char *wname, const char *stream,
+               struct snd_kcontrol_new *wc, int numkc,
+               int (*event)(struct snd_soc_dapm_widget *,
+               struct snd_kcontrol *, int), unsigned short event_flags)
 {
        w->id = id;
        w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
        w->kcontrol_news = wc;
        w->num_kcontrols = numkc;
        w->priv = priv;
+       w->event = event;
+       w->event_flags = event_flags;
 
        return 0;
 }
        return NULL;
 }
 
+static void hdac_hdmi_set_power_state(struct hdac_ext_device *edev,
+                            hda_nid_t nid, unsigned int pwr_state)
+{
+       if (get_wcaps(&edev->hdac, nid) & AC_WCAP_POWER) {
+               if (!snd_hdac_check_power_state(&edev->hdac, nid, pwr_state))
+                       snd_hdac_codec_write(&edev->hdac, nid, 0,
+                               AC_VERB_SET_POWER_STATE, pwr_state);
+       }
+}
+
+static void hdac_hdmi_set_amp(struct hdac_ext_device *edev,
+                                  hda_nid_t nid, int val)
+{
+       if (get_wcaps(&edev->hdac, nid) & AC_WCAP_OUT_AMP)
+               snd_hdac_codec_write(&edev->hdac, nid, 0,
+                                       AC_VERB_SET_AMP_GAIN_MUTE, val);
+}
+
+
+static int hdac_hdmi_pin_output_widget_event(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kc, int event)
+{
+       struct hdac_hdmi_pin *pin = w->priv;
+       struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+       struct hdac_hdmi_pcm *pcm;
+
+       dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n",
+                       __func__, w->name, event);
+
+       pcm = hdac_hdmi_get_pcm(edev, pin);
+       if (!pcm)
+               return -EIO;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               hdac_hdmi_set_power_state(edev, pin->nid, AC_PWRST_D0);
+
+               /* Enable out path for this pin widget */
+               snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+               hdac_hdmi_set_amp(edev, pin->nid, AMP_OUT_UNMUTE);
+
+               return hdac_hdmi_setup_audio_infoframe(edev, pcm->cvt->nid,
+                                                               pin->nid);
+
+       case SND_SOC_DAPM_POST_PMD:
+               hdac_hdmi_set_amp(edev, pin->nid, AMP_OUT_MUTE);
+
+               /* Disable out path for this pin widget */
+               snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+
+               hdac_hdmi_set_power_state(edev, pin->nid, AC_PWRST_D3);
+               break;
+
+       }
+
+       return 0;
+}
+
+static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kc, int event)
+{
+       struct hdac_hdmi_cvt *cvt = w->priv;
+       struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm;
+
+       dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n",
+                       __func__, w->name, event);
+
+       pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, cvt);
+       if (!pcm)
+               return -EIO;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D0);
+
+               /* Enable transmission */
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                       AC_VERB_SET_DIGI_CONVERT_1, 1);
+
+               /* Category Code (CC) to zero */
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                       AC_VERB_SET_DIGI_CONVERT_2, 0);
+
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                               AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag);
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                               AC_VERB_SET_STREAM_FORMAT, pcm->format);
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                               AC_VERB_SET_CHANNEL_STREAMID, 0);
+               snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
+                               AC_VERB_SET_STREAM_FORMAT, 0);
+
+               hdac_hdmi_set_power_state(edev, cvt->nid, AC_PWRST_D3);
+               break;
+
+       }
+
+       return 0;
+}
+
+static int hdac_hdmi_pin_mux_widget_event(struct snd_soc_dapm_widget *w,
+                                       struct snd_kcontrol *kc, int event)
+{
+       struct hdac_hdmi_pin *pin = w->priv;
+       struct hdac_ext_device *edev = to_hda_ext_device(w->dapm->dev);
+       int mux_idx;
+
+       dev_dbg(&edev->hdac.dev, "%s: widget: %s event: %x\n",
+                       __func__, w->name, event);
+
+       if (!kc)
+               kc  = w->kcontrols[0];
+
+       mux_idx = dapm_kcontrol_get_value(kc);
+       if (mux_idx > 0) {
+               snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
+                       AC_VERB_SET_CONNECT_SEL, (mux_idx - 1));
+       }
+
+       return 0;
+}
+
 /*
  * Based on user selection, map the PINs with the PCMs.
  */
                return -ENOMEM;
 
        return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
-                       snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1);
+                       snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1,
+                       hdac_hdmi_pin_mux_widget_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_REG);
 }
 
 /* Add cvt <- input <- mux route map */
        list_for_each_entry(cvt, &hdmi->cvt_list, head) {
                sprintf(widget_name, "Converter %d", cvt->nid);
                ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
-                       snd_soc_dapm_aif_in, &cvt->nid,
-                       widget_name, dai_drv[i].playback.stream_name, NULL, 0);
+                       snd_soc_dapm_aif_in, cvt,
+                       widget_name, dai_drv[i].playback.stream_name, NULL, 0,
+                       hdac_hdmi_cvt_output_widget_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
                if (ret < 0)
                        return ret;
                i++;
        list_for_each_entry(pin, &hdmi->pin_list, head) {
                sprintf(widget_name, "hif%d Output", pin->nid);
                ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
-                               snd_soc_dapm_output, &pin->nid,
-                               widget_name, NULL, NULL, 0);
+                               snd_soc_dapm_output, pin,
+                               widget_name, NULL, NULL, 0,
+                       hdac_hdmi_pin_output_widget_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
                if (ret < 0)
                        return ret;
                i++;
        .startup = hdac_hdmi_pcm_open,
        .shutdown = hdac_hdmi_pcm_close,
        .hw_params = hdac_hdmi_set_hw_params,
-       .prepare = hdac_hdmi_playback_prepare,
-       .trigger = hdac_hdmi_trigger,
-       .hw_free = hdac_hdmi_playback_cleanup,
+       .set_tdm_slot = hdac_hdmi_set_tdm_slot,
 };
 
 /*