ALSA: hda - Apply AMP fix in hdmi_setup_audio_infoframe() generically
[cascardo/linux.git] / sound / pci / hda / patch_hdmi.c
index bcbc4ee..fcd207d 100644 (file)
@@ -39,6 +39,7 @@
 #include <sound/tlv.h>
 #include <sound/hdaudio.h>
 #include <sound/hda_i915.h>
+#include <sound/hda_chmap.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_jack.h"
@@ -75,6 +76,8 @@ struct hdmi_spec_per_cvt {
 
 struct hdmi_spec_per_pin {
        hda_nid_t pin_nid;
+       /* pin idx, different device entries on the same pin use the same idx */
+       int pin_nid_idx;
        int num_mux_nids;
        hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
        int mux_idx;
@@ -84,8 +87,8 @@ struct hdmi_spec_per_pin {
        struct hdmi_eld sink_eld;
        struct mutex lock;
        struct delayed_work work;
-       struct snd_kcontrol *eld_ctl;
-       struct snd_jack *acomp_jack; /* jack via audio component */
+       struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/
+       int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */
        int repoll_count;
        bool setup; /* the stream has been set up by prepare callback */
        int channels; /* current number of channels */
@@ -97,19 +100,11 @@ struct hdmi_spec_per_pin {
 #endif
 };
 
-struct cea_channel_speaker_allocation;
-
 /* operations used by generic code that can be overridden by patches */
 struct hdmi_ops {
        int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
                           unsigned char *buf, int *eld_size);
 
-       /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
-       int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
-                                   int asp_slot);
-       int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
-                                   int asp_slot, int channel);
-
        void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
                                    int ca, int active_channels, int conn_type);
 
@@ -119,15 +114,12 @@ struct hdmi_ops {
        int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
                            hda_nid_t pin_nid, u32 stream_tag, int format);
 
-       /* Helpers for producing the channel map TLVs. These can be overridden
-        * for devices that have non-standard mapping requirements. */
-       int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap,
-                                                int channels);
-       void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap,
-                                      unsigned int *chmap, int channels);
+};
 
-       /* check that the user-given chmap is supported */
-       int (*chmap_validate)(int ca, int channels, unsigned char *chmap);
+struct hdmi_pcm {
+       struct hda_pcm *pcm;
+       struct snd_jack *jack;
+       struct snd_kcontrol *eld_ctl;
 };
 
 struct hdmi_spec {
@@ -137,14 +129,22 @@ struct hdmi_spec {
 
        int num_pins;
        struct snd_array pins; /* struct hdmi_spec_per_pin */
-       struct hda_pcm *pcm_rec[16];
-       unsigned int channels_max; /* max over all cvts */
+       struct hdmi_pcm pcm_rec[16];
+       struct mutex pcm_lock;
+       /* pcm_bitmap means which pcms have been assigned to pins*/
+       unsigned long pcm_bitmap;
+       int pcm_used;   /* counter of pcm_rec[] */
+       /* bitmap shows whether the pcm is opened in user space
+        * bit 0 means the first playback PCM (PCM3);
+        * bit 1 means the second playback PCM, and so on.
+        */
+       unsigned long pcm_in_use;
 
        struct hdmi_eld temp_eld;
        struct hdmi_ops ops;
 
        bool dyn_pin_out;
-
+       bool dyn_pcm_assign;
        /*
         * Non-generic VIA/NVIDIA specific
         */
@@ -152,13 +152,18 @@ struct hdmi_spec {
        struct hda_pcm_stream pcm_playback;
 
        /* i915/powerwell (Haswell+/Valleyview+) specific */
+       bool use_acomp_notifier; /* use i915 eld_notify callback for hotplug */
        struct i915_audio_component_audio_ops i915_audio_ops;
-       bool i915_bound; /* was i915 bound in this driver? */
+
+       struct hdac_chmap chmap;
 };
 
 #ifdef CONFIG_SND_HDA_I915
-#define codec_has_acomp(codec) \
-       ((codec)->bus->core.audio_component != NULL)
+static inline bool codec_has_acomp(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       return spec->use_acomp_notifier;
+}
 #else
 #define codec_has_acomp(codec) false
 #endif
@@ -195,173 +200,6 @@ union audio_infoframe {
        u8 bytes[0];
 };
 
-/*
- * CEA speaker placement:
- *
- *        FLH       FCH        FRH
- *  FLW    FL  FLC   FC   FRC   FR   FRW
- *
- *                                  LFE
- *                     TC
- *
- *          RL  RLC   RC   RRC   RR
- *
- * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
- * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
- */
-enum cea_speaker_placement {
-       FL  = (1 <<  0),        /* Front Left           */
-       FC  = (1 <<  1),        /* Front Center         */
-       FR  = (1 <<  2),        /* Front Right          */
-       FLC = (1 <<  3),        /* Front Left Center    */
-       FRC = (1 <<  4),        /* Front Right Center   */
-       RL  = (1 <<  5),        /* Rear Left            */
-       RC  = (1 <<  6),        /* Rear Center          */
-       RR  = (1 <<  7),        /* Rear Right           */
-       RLC = (1 <<  8),        /* Rear Left Center     */
-       RRC = (1 <<  9),        /* Rear Right Center    */
-       LFE = (1 << 10),        /* Low Frequency Effect */
-       FLW = (1 << 11),        /* Front Left Wide      */
-       FRW = (1 << 12),        /* Front Right Wide     */
-       FLH = (1 << 13),        /* Front Left High      */
-       FCH = (1 << 14),        /* Front Center High    */
-       FRH = (1 << 15),        /* Front Right High     */
-       TC  = (1 << 16),        /* Top Center           */
-};
-
-/*
- * ELD SA bits in the CEA Speaker Allocation data block
- */
-static int eld_speaker_allocation_bits[] = {
-       [0] = FL | FR,
-       [1] = LFE,
-       [2] = FC,
-       [3] = RL | RR,
-       [4] = RC,
-       [5] = FLC | FRC,
-       [6] = RLC | RRC,
-       /* the following are not defined in ELD yet */
-       [7] = FLW | FRW,
-       [8] = FLH | FRH,
-       [9] = TC,
-       [10] = FCH,
-};
-
-struct cea_channel_speaker_allocation {
-       int ca_index;
-       int speakers[8];
-
-       /* derived values, just for convenience */
-       int channels;
-       int spk_mask;
-};
-
-/*
- * ALSA sequence is:
- *
- *       surround40   surround41   surround50   surround51   surround71
- * ch0   front left   =            =            =            =
- * ch1   front right  =            =            =            =
- * ch2   rear left    =            =            =            =
- * ch3   rear right   =            =            =            =
- * ch4                LFE          center       center       center
- * ch5                                          LFE          LFE
- * ch6                                                       side left
- * ch7                                                       side right
- *
- * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
- */
-static int hdmi_channel_mapping[0x32][8] = {
-       /* stereo */
-       [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* 2.1 */
-       [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* Dolby Surround */
-       [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* surround40 */
-       [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
-       /* 4ch */
-       [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
-       /* surround41 */
-       [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
-       /* surround50 */
-       [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
-       /* surround51 */
-       [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
-       /* 7.1 */
-       [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
-};
-
-/*
- * This is an ordered list!
- *
- * The preceding ones have better chances to be selected by
- * hdmi_channel_allocation().
- */
-static struct cea_channel_speaker_allocation channel_allocations[] = {
-/*                       channel:   7     6    5    4    3     2    1    0  */
-{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
-                                /* 2.1 */
-{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
-                                /* Dolby Surround */
-{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
-                                /* surround40 */
-{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
-                                /* surround41 */
-{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
-                                /* surround50 */
-{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-                                /* surround51 */
-{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* 6.1 */
-{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* surround71 */
-{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-
-{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
-{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
-};
-
-
 /*
  * HDMI routines
  */
@@ -370,7 +208,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
        ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
 #define get_cvt(spec, idx) \
        ((struct hdmi_spec_per_cvt  *)snd_array_elem(&spec->cvts, idx))
-#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx])
+/* obtain hdmi_pcm object assigned to idx */
+#define get_hdmi_pcm(spec, idx)        (&(spec)->pcm_rec[idx])
+/* obtain hda_pcm object assigned to idx */
+#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm)
 
 static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
 {
@@ -385,20 +226,52 @@ static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
        return -EINVAL;
 }
 
+static int hinfo_to_pcm_index(struct hda_codec *codec,
+                       struct hda_pcm_stream *hinfo)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int pcm_idx;
+
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++)
+               if (get_pcm_rec(spec, pcm_idx)->stream == hinfo)
+                       return pcm_idx;
+
+       codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+       return -EINVAL;
+}
+
 static int hinfo_to_pin_index(struct hda_codec *codec,
                              struct hda_pcm_stream *hinfo)
 {
        struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin;
        int pin_idx;
 
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
-               if (get_pcm_rec(spec, pin_idx)->stream == hinfo)
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               per_pin = get_pin(spec, pin_idx);
+               if (per_pin->pcm &&
+                       per_pin->pcm->pcm->stream == hinfo)
                        return pin_idx;
+       }
 
-       codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+       codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo);
        return -EINVAL;
 }
 
+static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec,
+                                               int pcm_idx)
+{
+       int i;
+       struct hdmi_spec_per_pin *per_pin;
+
+       for (i = 0; i < spec->num_pins; i++) {
+               per_pin = get_pin(spec, i);
+               if (per_pin->pcm_idx == pcm_idx)
+                       return per_pin;
+       }
+       return NULL;
+}
+
 static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid)
 {
        struct hdmi_spec *spec = codec->spec;
@@ -419,17 +292,22 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
-       int pin_idx;
+       int pcm_idx;
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
 
-       pin_idx = kcontrol->private_value;
-       per_pin = get_pin(spec, pin_idx);
+       pcm_idx = kcontrol->private_value;
+       mutex_lock(&spec->pcm_lock);
+       per_pin = pcm_idx_to_pin(spec, pcm_idx);
+       if (!per_pin) {
+               /* no pin is bound to the pcm */
+               uinfo->count = 0;
+               mutex_unlock(&spec->pcm_lock);
+               return 0;
+       }
        eld = &per_pin->sink_eld;
-
-       mutex_lock(&per_pin->lock);
        uinfo->count = eld->eld_valid ? eld->eld_size : 0;
-       mutex_unlock(&per_pin->lock);
+       mutex_unlock(&spec->pcm_lock);
 
        return 0;
 }
@@ -441,16 +319,23 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
-       int pin_idx;
-
-       pin_idx = kcontrol->private_value;
-       per_pin = get_pin(spec, pin_idx);
+       int pcm_idx;
+
+       pcm_idx = kcontrol->private_value;
+       mutex_lock(&spec->pcm_lock);
+       per_pin = pcm_idx_to_pin(spec, pcm_idx);
+       if (!per_pin) {
+               /* no pin is bound to the pcm */
+               memset(ucontrol->value.bytes.data, 0,
+                      ARRAY_SIZE(ucontrol->value.bytes.data));
+               mutex_unlock(&spec->pcm_lock);
+               return 0;
+       }
        eld = &per_pin->sink_eld;
 
-       mutex_lock(&per_pin->lock);
        if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
            eld->eld_size > ELD_MAX_SIZE) {
-               mutex_unlock(&per_pin->lock);
+               mutex_unlock(&spec->pcm_lock);
                snd_BUG();
                return -EINVAL;
        }
@@ -460,7 +345,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
        if (eld->eld_valid)
                memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
                       eld->eld_size);
-       mutex_unlock(&per_pin->lock);
+       mutex_unlock(&spec->pcm_lock);
 
        return 0;
 }
@@ -473,7 +358,7 @@ static struct snd_kcontrol_new eld_bytes_ctl = {
        .get = hdmi_eld_ctl_get,
 };
 
-static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
+static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx,
                        int device)
 {
        struct snd_kcontrol *kctl;
@@ -483,14 +368,17 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
        kctl = snd_ctl_new1(&eld_bytes_ctl, codec);
        if (!kctl)
                return -ENOMEM;
-       kctl->private_value = pin_idx;
+       kctl->private_value = pcm_idx;
        kctl->id.device = device;
 
-       err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl);
+       /* no pin nid is associated with the kctl now
+        * tbd: associate pin nid to eld ctl later
+        */
+       err = snd_hda_ctl_add(codec, 0, kctl);
        if (err < 0)
                return err;
 
-       get_pin(spec, pin_idx)->eld_ctl = kctl;
+       get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl;
        return 0;
 }
 
@@ -547,20 +435,6 @@ static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
                            AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
 }
 
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid)
-{
-       return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
-                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
-}
-
-static void hdmi_set_channel_count(struct hda_codec *codec,
-                                  hda_nid_t cvt_nid, int chs)
-{
-       if (chs != hdmi_get_channel_count(codec, cvt_nid))
-               snd_hda_codec_write(codec, cvt_nid, 0,
-                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-}
-
 /*
  * ELD proc files
  */
@@ -624,339 +498,6 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
 }
 #endif
 
-/*
- * Channel mapping routines
- */
-
-/*
- * Compute derived values in channel_allocations[].
- */
-static void init_channel_allocations(void)
-{
-       int i, j;
-       struct cea_channel_speaker_allocation *p;
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               p = channel_allocations + i;
-               p->channels = 0;
-               p->spk_mask = 0;
-               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
-                       if (p->speakers[j]) {
-                               p->channels++;
-                               p->spk_mask |= p->speakers[j];
-                       }
-       }
-}
-
-static int get_channel_allocation_order(int ca)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if (channel_allocations[i].ca_index == ca)
-                       break;
-       }
-       return i;
-}
-
-/*
- * The transformation takes two steps:
- *
- *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
- *           spk_mask => (channel_allocations[])         => ai->CA
- *
- * TODO: it could select the wrong CA from multiple candidates.
-*/
-static int hdmi_channel_allocation(struct hda_codec *codec,
-                                  struct hdmi_eld *eld, int channels)
-{
-       int i;
-       int ca = 0;
-       int spk_mask = 0;
-       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-
-       /*
-        * CA defaults to 0 for basic stereo audio
-        */
-       if (channels <= 2)
-               return 0;
-
-       /*
-        * expand ELD's speaker allocation mask
-        *
-        * ELD tells the speaker mask in a compact(paired) form,
-        * expand ELD's notions to match the ones used by Audio InfoFrame.
-        */
-       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
-               if (eld->info.spk_alloc & (1 << i))
-                       spk_mask |= eld_speaker_allocation_bits[i];
-       }
-
-       /* search for the first working match in the CA table */
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if (channels == channel_allocations[i].channels &&
-                   (spk_mask & channel_allocations[i].spk_mask) ==
-                               channel_allocations[i].spk_mask) {
-                       ca = channel_allocations[i].ca_index;
-                       break;
-               }
-       }
-
-       if (!ca) {
-               /* if there was no match, select the regular ALSA channel
-                * allocation with the matching number of channels */
-               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-                       if (channels == channel_allocations[i].channels) {
-                               ca = channel_allocations[i].ca_index;
-                               break;
-                       }
-               }
-       }
-
-       snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf));
-       codec_dbg(codec, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
-                   ca, channels, buf);
-
-       return ca;
-}
-
-static void hdmi_debug_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       struct hdmi_spec *spec = codec->spec;
-       int i;
-       int channel;
-
-       for (i = 0; i < 8; i++) {
-               channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i);
-               codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n",
-                                               channel, i);
-       }
-#endif
-}
-
-static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid,
-                                      bool non_pcm,
-                                      int ca)
-{
-       struct hdmi_spec *spec = codec->spec;
-       struct cea_channel_speaker_allocation *ch_alloc;
-       int i;
-       int err;
-       int order;
-       int non_pcm_mapping[8];
-
-       order = get_channel_allocation_order(ca);
-       ch_alloc = &channel_allocations[order];
-
-       if (hdmi_channel_mapping[ca][1] == 0) {
-               int hdmi_slot = 0;
-               /* fill actual channel mappings in ALSA channel (i) order */
-               for (i = 0; i < ch_alloc->channels; i++) {
-                       while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
-                               hdmi_slot++; /* skip zero slots */
-
-                       hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
-               }
-               /* fill the rest of the slots with ALSA channel 0xf */
-               for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
-                       if (!ch_alloc->speakers[7 - hdmi_slot])
-                               hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
-       }
-
-       if (non_pcm) {
-               for (i = 0; i < ch_alloc->channels; i++)
-                       non_pcm_mapping[i] = (i << 4) | i;
-               for (; i < 8; i++)
-                       non_pcm_mapping[i] = (0xf << 4) | i;
-       }
-
-       for (i = 0; i < 8; i++) {
-               int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
-               int hdmi_slot = slotsetup & 0x0f;
-               int channel = (slotsetup & 0xf0) >> 4;
-               err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel);
-               if (err) {
-                       codec_dbg(codec, "HDMI: channel mapping failed\n");
-                       break;
-               }
-       }
-}
-
-struct channel_map_table {
-       unsigned char map;              /* ALSA API channel map position */
-       int spk_mask;                   /* speaker position bit mask */
-};
-
-static struct channel_map_table map_tables[] = {
-       { SNDRV_CHMAP_FL,       FL },
-       { SNDRV_CHMAP_FR,       FR },
-       { SNDRV_CHMAP_RL,       RL },
-       { SNDRV_CHMAP_RR,       RR },
-       { SNDRV_CHMAP_LFE,      LFE },
-       { SNDRV_CHMAP_FC,       FC },
-       { SNDRV_CHMAP_RLC,      RLC },
-       { SNDRV_CHMAP_RRC,      RRC },
-       { SNDRV_CHMAP_RC,       RC },
-       { SNDRV_CHMAP_FLC,      FLC },
-       { SNDRV_CHMAP_FRC,      FRC },
-       { SNDRV_CHMAP_TFL,      FLH },
-       { SNDRV_CHMAP_TFR,      FRH },
-       { SNDRV_CHMAP_FLW,      FLW },
-       { SNDRV_CHMAP_FRW,      FRW },
-       { SNDRV_CHMAP_TC,       TC },
-       { SNDRV_CHMAP_TFC,      FCH },
-       {} /* terminator */
-};
-
-/* from ALSA API channel position to speaker bit mask */
-static int to_spk_mask(unsigned char c)
-{
-       struct channel_map_table *t = map_tables;
-       for (; t->map; t++) {
-               if (t->map == c)
-                       return t->spk_mask;
-       }
-       return 0;
-}
-
-/* from ALSA API channel position to CEA slot */
-static int to_cea_slot(int ordered_ca, unsigned char pos)
-{
-       int mask = to_spk_mask(pos);
-       int i;
-
-       if (mask) {
-               for (i = 0; i < 8; i++) {
-                       if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
-                               return i;
-               }
-       }
-
-       return -1;
-}
-
-/* from speaker bit mask to ALSA API channel position */
-static int spk_to_chmap(int spk)
-{
-       struct channel_map_table *t = map_tables;
-       for (; t->map; t++) {
-               if (t->spk_mask == spk)
-                       return t->map;
-       }
-       return 0;
-}
-
-/* from CEA slot to ALSA API channel position */
-static int from_cea_slot(int ordered_ca, unsigned char slot)
-{
-       int mask = channel_allocations[ordered_ca].speakers[7 - slot];
-
-       return spk_to_chmap(mask);
-}
-
-/* get the CA index corresponding to the given ALSA API channel map */
-static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
-{
-       int i, spks = 0, spk_mask = 0;
-
-       for (i = 0; i < chs; i++) {
-               int mask = to_spk_mask(map[i]);
-               if (mask) {
-                       spk_mask |= mask;
-                       spks++;
-               }
-       }
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if ((chs == channel_allocations[i].channels ||
-                    spks == channel_allocations[i].channels) &&
-                   (spk_mask & channel_allocations[i].spk_mask) ==
-                               channel_allocations[i].spk_mask)
-                       return channel_allocations[i].ca_index;
-       }
-       return -1;
-}
-
-/* set up the channel slots for the given ALSA API channel map */
-static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
-                                            hda_nid_t pin_nid,
-                                            int chs, unsigned char *map,
-                                            int ca)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int ordered_ca = get_channel_allocation_order(ca);
-       int alsa_pos, hdmi_slot;
-       int assignments[8] = {[0 ... 7] = 0xf};
-
-       for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
-
-               hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
-
-               if (hdmi_slot < 0)
-                       continue; /* unassigned channel */
-
-               assignments[hdmi_slot] = alsa_pos;
-       }
-
-       for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
-               int err;
-
-               err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot,
-                                                    assignments[hdmi_slot]);
-               if (err)
-                       return -EINVAL;
-       }
-       return 0;
-}
-
-/* store ALSA API channel map from the current default map */
-static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
-{
-       int i;
-       int ordered_ca = get_channel_allocation_order(ca);
-       for (i = 0; i < 8; i++) {
-               if (i < channel_allocations[ordered_ca].channels)
-                       map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
-               else
-                       map[i] = 0;
-       }
-}
-
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid, bool non_pcm, int ca,
-                                      int channels, unsigned char *map,
-                                      bool chmap_set)
-{
-       if (!non_pcm && chmap_set) {
-               hdmi_manual_setup_channel_mapping(codec, pin_nid,
-                                                 channels, map, ca);
-       } else {
-               hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca);
-               hdmi_setup_fake_chmap(map, ca);
-       }
-
-       hdmi_debug_channel_mapping(codec, pin_nid);
-}
-
-static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                    int asp_slot, int channel)
-{
-       return snd_hda_codec_write(codec, pin_nid, 0,
-                                  AC_VERB_SET_HDMI_CHAN_SLOT,
-                                  (channel << 4) | asp_slot);
-}
-
-static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                    int asp_slot)
-{
-       return (snd_hda_codec_read(codec, pin_nid, 0,
-                                  AC_VERB_GET_HDMI_CHAN_SLOT,
-                                  asp_slot) & 0xf0) >> 4;
-}
-
 /*
  * Audio InfoFrame routines
  */
@@ -1132,41 +673,40 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                                       bool non_pcm)
 {
        struct hdmi_spec *spec = codec->spec;
+       struct hdac_chmap *chmap = &spec->chmap;
        hda_nid_t pin_nid = per_pin->pin_nid;
        int channels = per_pin->channels;
        int active_channels;
        struct hdmi_eld *eld;
-       int ca, ordered_ca;
+       int ca;
 
        if (!channels)
                return;
 
-       if (is_haswell_plus(codec))
+       /* some HW (e.g. HSW+) needs reprogramming the amp at each time */
+       if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
                snd_hda_codec_write(codec, pin_nid, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
                                            AMP_OUT_UNMUTE);
 
        eld = &per_pin->sink_eld;
 
-       if (!non_pcm && per_pin->chmap_set)
-               ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
-       else
-               ca = hdmi_channel_allocation(codec, eld, channels);
-       if (ca < 0)
-               ca = 0;
+       ca = snd_hdac_channel_allocation(&codec->core,
+                       eld->info.spk_alloc, channels,
+                       per_pin->chmap_set, non_pcm, per_pin->chmap);
 
-       ordered_ca = get_channel_allocation_order(ca);
-       active_channels = channel_allocations[ordered_ca].channels;
+       active_channels = snd_hdac_get_active_channels(ca);
 
-       hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels);
+       chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid,
+                                               active_channels);
 
        /*
         * always configure channel mapping, it may have been changed by the
         * user in the meantime
         */
-       hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
-                                  channels, per_pin->chmap,
-                                  per_pin->chmap_set);
+       snd_hdac_setup_channel_mapping(&spec->chmap,
+                               pin_nid, non_pcm, ca, channels,
+                               per_pin->chmap, per_pin->chmap_set);
 
        spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
                                      eld->info.conn_type);
@@ -1338,6 +878,11 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
        return 0;
 }
 
+/* Try to find an available converter
+ * If pin_idx is less then zero, just try to find an available converter.
+ * Otherwise, try to find an available converter and get the cvt mux index
+ * of the pin.
+ */
 static int hdmi_choose_cvt(struct hda_codec *codec,
                        int pin_idx, int *cvt_id, int *mux_id)
 {
@@ -1346,7 +891,11 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
        struct hdmi_spec_per_cvt *per_cvt = NULL;
        int cvt_idx, mux_idx = 0;
 
-       per_pin = get_pin(spec, pin_idx);
+       /* pin_idx < 0 means no pin will be bound to the converter */
+       if (pin_idx < 0)
+               per_pin = NULL;
+       else
+               per_pin = get_pin(spec, pin_idx);
 
        /* Dynamically assign converter to stream */
        for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
@@ -1355,6 +904,8 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
                /* Must not already be assigned */
                if (per_cvt->assigned)
                        continue;
+               if (per_pin == NULL)
+                       break;
                /* Must be in pin's mux's list of converters */
                for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
                        if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
@@ -1367,9 +918,10 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
 
        /* No free converters */
        if (cvt_idx == spec->num_cvts)
-               return -ENODEV;
+               return -EBUSY;
 
-       per_pin->mux_idx = mux_idx;
+       if (per_pin != NULL)
+               per_pin->mux_idx = mux_idx;
 
        if (cvt_id)
                *cvt_id = cvt_idx;
@@ -1395,6 +947,20 @@ static void intel_verify_pin_cvt_connect(struct hda_codec *codec,
                                            mux_idx);
 }
 
+/* get the mux index for the converter of the pins
+ * converter's mux index is the same for all pins on Intel platform
+ */
+static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
+                       hda_nid_t cvt_nid)
+{
+       int i;
+
+       for (i = 0; i < spec->num_cvts; i++)
+               if (spec->cvt_nids[i] == cvt_nid)
+                       return i;
+       return -EINVAL;
+}
+
 /* Intel HDMI workaround to fix audio routing issue:
  * For some Intel display codecs, pins share the same connection list.
  * So a conveter can be selected by multiple pins and playback on any of these
@@ -1446,6 +1012,74 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec,
        }
 }
 
+/* A wrapper of intel_not_share_asigned_cvt() */
+static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
+                       hda_nid_t pin_nid, hda_nid_t cvt_nid)
+{
+       int mux_idx;
+       struct hdmi_spec *spec = codec->spec;
+
+       if (!is_haswell_plus(codec) && !is_valleyview_plus(codec))
+               return;
+
+       /* On Intel platform, the mapping of converter nid to
+        * mux index of the pins are always the same.
+        * The pin nid may be 0, this means all pins will not
+        * share the converter.
+        */
+       mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid);
+       if (mux_idx >= 0)
+               intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
+}
+
+/* called in hdmi_pcm_open when no pin is assigned to the PCM
+ * in dyn_pcm_assign mode.
+ */
+static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
+                        struct hda_codec *codec,
+                        struct snd_pcm_substream *substream)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int cvt_idx, pcm_idx;
+       struct hdmi_spec_per_cvt *per_cvt = NULL;
+       int err;
+
+       pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+       if (pcm_idx < 0)
+               return -EINVAL;
+
+       err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL);
+       if (err)
+               return err;
+
+       per_cvt = get_cvt(spec, cvt_idx);
+       per_cvt->assigned = 1;
+       hinfo->nid = per_cvt->cvt_nid;
+
+       intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
+
+       set_bit(pcm_idx, &spec->pcm_in_use);
+       /* todo: setup spdif ctls assign */
+
+       /* Initially set the converter's capabilities */
+       hinfo->channels_min = per_cvt->channels_min;
+       hinfo->channels_max = per_cvt->channels_max;
+       hinfo->rates = per_cvt->rates;
+       hinfo->formats = per_cvt->formats;
+       hinfo->maxbps = per_cvt->maxbps;
+
+       /* Store the updated parameters */
+       runtime->hw.channels_min = hinfo->channels_min;
+       runtime->hw.channels_max = hinfo->channels_max;
+       runtime->hw.formats = hinfo->formats;
+       runtime->hw.rates = hinfo->rates;
+
+       snd_pcm_hw_constraint_step(substream->runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+       return 0;
+}
+
 /*
  * HDA PCM callbacks
  */
@@ -1455,26 +1089,47 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 {
        struct hdmi_spec *spec = codec->spec;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       int pin_idx, cvt_idx, mux_idx = 0;
+       int pin_idx, cvt_idx, pcm_idx, mux_idx = 0;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
        struct hdmi_spec_per_cvt *per_cvt = NULL;
        int err;
 
        /* Validate hinfo */
-       pin_idx = hinfo_to_pin_index(codec, hinfo);
-       if (snd_BUG_ON(pin_idx < 0))
+       pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+       if (pcm_idx < 0)
                return -EINVAL;
-       per_pin = get_pin(spec, pin_idx);
-       eld = &per_pin->sink_eld;
+
+       mutex_lock(&spec->pcm_lock);
+       pin_idx = hinfo_to_pin_index(codec, hinfo);
+       if (!spec->dyn_pcm_assign) {
+               if (snd_BUG_ON(pin_idx < 0)) {
+                       mutex_unlock(&spec->pcm_lock);
+                       return -EINVAL;
+               }
+       } else {
+               /* no pin is assigned to the PCM
+                * PA need pcm open successfully when probe
+                */
+               if (pin_idx < 0) {
+                       err = hdmi_pcm_open_no_pin(hinfo, codec, substream);
+                       mutex_unlock(&spec->pcm_lock);
+                       return err;
+               }
+       }
 
        err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
-       if (err < 0)
+       if (err < 0) {
+               mutex_unlock(&spec->pcm_lock);
                return err;
+       }
 
        per_cvt = get_cvt(spec, cvt_idx);
        /* Claim converter */
        per_cvt->assigned = 1;
+
+       set_bit(pcm_idx, &spec->pcm_in_use);
+       per_pin = get_pin(spec, pin_idx);
        per_pin->cvt_nid = per_cvt->cvt_nid;
        hinfo->nid = per_cvt->cvt_nid;
 
@@ -1486,7 +1141,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        if (is_haswell_plus(codec) || is_valleyview_plus(codec))
                intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
 
-       snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
+       snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
 
        /* Initially set the converter's capabilities */
        hinfo->channels_min = per_cvt->channels_min;
@@ -1495,6 +1150,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        hinfo->formats = per_cvt->formats;
        hinfo->maxbps = per_cvt->maxbps;
 
+       eld = &per_pin->sink_eld;
        /* Restrict capabilities by ELD if this isn't disabled */
        if (!static_hdmi_pcm && eld->eld_valid) {
                snd_hdmi_eld_update_pcm_info(&eld->info, hinfo);
@@ -1502,43 +1158,164 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
                    !hinfo->rates || !hinfo->formats) {
                        per_cvt->assigned = 0;
                        hinfo->nid = 0;
-                       snd_hda_spdif_ctls_unassign(codec, pin_idx);
+                       snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+                       mutex_unlock(&spec->pcm_lock);
                        return -ENODEV;
                }
        }
 
+       mutex_unlock(&spec->pcm_lock);
        /* Store the updated parameters */
        runtime->hw.channels_min = hinfo->channels_min;
        runtime->hw.channels_max = hinfo->channels_max;
        runtime->hw.formats = hinfo->formats;
        runtime->hw.rates = hinfo->rates;
 
-       snd_pcm_hw_constraint_step(substream->runtime, 0,
-                                  SNDRV_PCM_HW_PARAM_CHANNELS, 2);
-       return 0;
+       snd_pcm_hw_constraint_step(substream->runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+       return 0;
+}
+
+/*
+ * HDA/HDMI auto parsing
+ */
+static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
+       hda_nid_t pin_nid = per_pin->pin_nid;
+
+       if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
+               codec_warn(codec,
+                          "HDMI: pin %d wcaps %#x does not support connection list\n",
+                          pin_nid, get_wcaps(codec, pin_nid));
+               return -EINVAL;
+       }
+
+       per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid,
+                                                       per_pin->mux_nids,
+                                                       HDA_MAX_CONNECTIONS);
+
+       return 0;
+}
+
+static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
+                               struct hdmi_spec_per_pin *per_pin)
+{
+       int i;
+
+       /* try the prefer PCM */
+       if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
+               return per_pin->pin_nid_idx;
+
+       /* have a second try; check the "reserved area" over num_pins */
+       for (i = spec->num_pins; i < spec->pcm_used; i++) {
+               if (!test_bit(i, &spec->pcm_bitmap))
+                       return i;
+       }
+
+       /* the last try; check the empty slots in pins */
+       for (i = 0; i < spec->num_pins; i++) {
+               if (!test_bit(i, &spec->pcm_bitmap))
+                       return i;
+       }
+       return -EBUSY;
+}
+
+static void hdmi_attach_hda_pcm(struct hdmi_spec *spec,
+                               struct hdmi_spec_per_pin *per_pin)
+{
+       int idx;
+
+       /* pcm already be attached to the pin */
+       if (per_pin->pcm)
+               return;
+       idx = hdmi_find_pcm_slot(spec, per_pin);
+       if (idx == -EBUSY)
+               return;
+       per_pin->pcm_idx = idx;
+       per_pin->pcm = get_hdmi_pcm(spec, idx);
+       set_bit(idx, &spec->pcm_bitmap);
+}
+
+static void hdmi_detach_hda_pcm(struct hdmi_spec *spec,
+                               struct hdmi_spec_per_pin *per_pin)
+{
+       int idx;
+
+       /* pcm already be detached from the pin */
+       if (!per_pin->pcm)
+               return;
+       idx = per_pin->pcm_idx;
+       per_pin->pcm_idx = -1;
+       per_pin->pcm = NULL;
+       if (idx >= 0 && idx < spec->pcm_used)
+               clear_bit(idx, &spec->pcm_bitmap);
+}
+
+static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec,
+               struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid)
+{
+       int mux_idx;
+
+       for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+               if (per_pin->mux_nids[mux_idx] == cvt_nid)
+                       break;
+       return mux_idx;
+}
+
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid);
+
+static void hdmi_pcm_setup_pin(struct hdmi_spec *spec,
+                          struct hdmi_spec_per_pin *per_pin)
+{
+       struct hda_codec *codec = per_pin->codec;
+       struct hda_pcm *pcm;
+       struct hda_pcm_stream *hinfo;
+       struct snd_pcm_substream *substream;
+       int mux_idx;
+       bool non_pcm;
+
+       if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+               pcm = get_pcm_rec(spec, per_pin->pcm_idx);
+       else
+               return;
+       if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use))
+               return;
+
+       /* hdmi audio only uses playback and one substream */
+       hinfo = pcm->stream;
+       substream = pcm->pcm->streams[0].substream;
+
+       per_pin->cvt_nid = hinfo->nid;
+
+       mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid);
+       if (mux_idx < per_pin->num_mux_nids)
+               snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+                               AC_VERB_SET_CONNECT_SEL,
+                               mux_idx);
+       snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid);
+
+       non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid);
+       if (substream->runtime)
+               per_pin->channels = substream->runtime->channels;
+       per_pin->setup = true;
+       per_pin->mux_idx = mux_idx;
+
+       hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
 }
 
-/*
- * HDA/HDMI auto parsing
- */
-static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
+static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
+                          struct hdmi_spec_per_pin *per_pin)
 {
-       struct hdmi_spec *spec = codec->spec;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       hda_nid_t pin_nid = per_pin->pin_nid;
-
-       if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
-               codec_warn(codec,
-                          "HDMI: pin %d wcaps %#x does not support connection list\n",
-                          pin_nid, get_wcaps(codec, pin_nid));
-               return -EINVAL;
-       }
+       if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+               snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx);
 
-       per_pin->num_mux_nids = snd_hda_get_connections(codec, pin_nid,
-                                                       per_pin->mux_nids,
-                                                       HDA_MAX_CONNECTIONS);
+       per_pin->chmap_set = false;
+       memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
 
-       return 0;
+       per_pin->setup = false;
+       per_pin->channels = 0;
 }
 
 /* update per_pin ELD from the given new ELD;
@@ -1549,8 +1326,27 @@ static void update_eld(struct hda_codec *codec,
                       struct hdmi_eld *eld)
 {
        struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+       struct hdmi_spec *spec = codec->spec;
        bool old_eld_valid = pin_eld->eld_valid;
        bool eld_changed;
+       int pcm_idx = -1;
+
+       /* for monitor disconnection, save pcm_idx firstly */
+       pcm_idx = per_pin->pcm_idx;
+       if (spec->dyn_pcm_assign) {
+               if (eld->eld_valid) {
+                       hdmi_attach_hda_pcm(spec, per_pin);
+                       hdmi_pcm_setup_pin(spec, per_pin);
+               } else {
+                       hdmi_pcm_reset_pin(spec, per_pin);
+                       hdmi_detach_hda_pcm(spec, per_pin);
+               }
+       }
+       /* if pcm_idx == -1, it means this is in monitor connection event
+        * we can get the correct pcm_idx now.
+        */
+       if (pcm_idx == -1)
+               pcm_idx = per_pin->pcm_idx;
 
        if (eld->eld_valid)
                snd_hdmi_show_eld(codec, &eld->info);
@@ -1562,6 +1358,7 @@ static void update_eld(struct hda_codec *codec,
                           eld->eld_size) != 0)
                        eld_changed = true;
 
+       pin_eld->monitor_present = eld->monitor_present;
        pin_eld->eld_valid = eld->eld_valid;
        pin_eld->eld_size = eld->eld_size;
        if (eld->eld_valid)
@@ -1584,11 +1381,11 @@ static void update_eld(struct hda_codec *codec,
                hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
        }
 
-       if (eld_changed)
+       if (eld_changed && pcm_idx >= 0)
                snd_ctl_notify(codec->card,
                               SNDRV_CTL_EVENT_MASK_VALUE |
                               SNDRV_CTL_EVENT_MASK_INFO,
-                              &per_pin->eld_ctl->id);
+                              &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
 }
 
 /* update ELD and jack state via HD-audio verbs */
@@ -1613,7 +1410,6 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
        bool ret;
        bool do_repoll = false;
 
-       snd_hda_power_up_pm(codec);
        present = snd_hda_pin_sense(codec, pin_nid);
 
        mutex_lock(&per_pin->lock);
@@ -1652,24 +1448,46 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
                jack->block_report = !ret;
 
        mutex_unlock(&per_pin->lock);
-       snd_hda_power_down_pm(codec);
        return ret;
 }
 
+static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec,
+                                struct hdmi_spec_per_pin *per_pin)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct snd_jack *jack = NULL;
+       struct hda_jack_tbl *jack_tbl;
+
+       /* if !dyn_pcm_assign, get jack from hda_jack_tbl
+        * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not
+        * NULL even after snd_hda_jack_tbl_clear() is called to
+        * free snd_jack. This may cause access invalid memory
+        * when calling snd_jack_report
+        */
+       if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign)
+               jack = spec->pcm_rec[per_pin->pcm_idx].jack;
+       else if (!spec->dyn_pcm_assign) {
+               jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
+               if (jack_tbl)
+                       jack = jack_tbl->jack;
+       }
+       return jack;
+}
+
 /* update ELD and jack state via audio component */
 static void sync_eld_via_acomp(struct hda_codec *codec,
                               struct hdmi_spec_per_pin *per_pin)
 {
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_eld *eld = &spec->temp_eld;
+       struct snd_jack *jack = NULL;
        int size;
 
        mutex_lock(&per_pin->lock);
+       eld->monitor_present = false;
        size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid,
                                      &eld->monitor_present, eld->eld_buffer,
                                      ELD_MAX_SIZE);
-       if (size < 0)
-               goto unlock;
        if (size > 0) {
                size = min(size, ELD_MAX_SIZE);
                if (snd_hdmi_parse_eld(codec, &eld->info,
@@ -1685,8 +1503,16 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
                eld->eld_size = 0;
        }
 
+       /* pcm_idx >=0 before update_eld() means it is in monitor
+        * disconnected event. Jack must be fetched before update_eld()
+        */
+       jack = pin_idx_to_jack(codec, per_pin);
        update_eld(codec, per_pin, eld);
-       snd_jack_report(per_pin->acomp_jack,
+       if (jack == NULL)
+               jack = pin_idx_to_jack(codec, per_pin);
+       if (jack == NULL)
+               goto unlock;
+       snd_jack_report(jack,
                        eld->monitor_present ? SND_JACK_AVOUT : 0);
  unlock:
        mutex_unlock(&per_pin->lock);
@@ -1695,13 +1521,26 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
 static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
 {
        struct hda_codec *codec = per_pin->codec;
+       struct hdmi_spec *spec = codec->spec;
+       int ret;
 
+       /* no temporary power up/down needed for component notifier */
+       if (!codec_has_acomp(codec))
+               snd_hda_power_up_pm(codec);
+
+       mutex_lock(&spec->pcm_lock);
        if (codec_has_acomp(codec)) {
                sync_eld_via_acomp(codec, per_pin);
-               return false; /* don't call snd_hda_jack_report_sync() */
+               ret = false; /* don't call snd_hda_jack_report_sync() */
        } else {
-               return hdmi_present_sense_via_verbs(per_pin, repoll);
+               ret = hdmi_present_sense_via_verbs(per_pin, repoll);
        }
+       mutex_unlock(&spec->pcm_lock);
+
+       if (!codec_has_acomp(codec))
+               snd_hda_power_down_pm(codec);
+
+       return ret;
 }
 
 static void hdmi_repoll_eld(struct work_struct *work)
@@ -1745,6 +1584,13 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 
        per_pin->pin_nid = pin_nid;
        per_pin->non_pcm = false;
+       if (spec->dyn_pcm_assign)
+               per_pin->pcm_idx = -1;
+       else {
+               per_pin->pcm = get_hdmi_pcm(spec, pin_idx);
+               per_pin->pcm_idx = pin_idx;
+       }
+       per_pin->pin_nid_idx = pin_idx;
 
        err = hdmi_read_pin_conn(codec, pin_idx);
        if (err < 0)
@@ -1773,8 +1619,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
        per_cvt->channels_min = 2;
        if (chans <= 16) {
                per_cvt->channels_max = chans;
-               if (chans > spec->channels_max)
-                       spec->channels_max = chans;
+               if (chans > spec->chmap.channels_max)
+                       spec->chmap.channels_max = chans;
        }
 
        err = snd_hda_query_supported_pcm(codec, cvt_nid,
@@ -1851,13 +1697,34 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 {
        hda_nid_t cvt_nid = hinfo->nid;
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx = hinfo_to_pin_index(codec, hinfo);
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       hda_nid_t pin_nid = per_pin->pin_nid;
+       int pin_idx;
+       struct hdmi_spec_per_pin *per_pin;
+       hda_nid_t pin_nid;
        struct snd_pcm_runtime *runtime = substream->runtime;
        bool non_pcm;
        int pinctl;
+       int err;
+
+       mutex_lock(&spec->pcm_lock);
+       pin_idx = hinfo_to_pin_index(codec, hinfo);
+       if (spec->dyn_pcm_assign && pin_idx < 0) {
+               /* when dyn_pcm_assign and pcm is not bound to a pin
+                * skip pin setup and return 0 to make audio playback
+                * be ongoing
+                */
+               intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+               snd_hda_codec_setup_stream(codec, cvt_nid,
+                                       stream_tag, 0, format);
+               mutex_unlock(&spec->pcm_lock);
+               return 0;
+       }
 
+       if (snd_BUG_ON(pin_idx < 0)) {
+               mutex_unlock(&spec->pcm_lock);
+               return -EINVAL;
+       }
+       per_pin = get_pin(spec, pin_idx);
+       pin_nid = per_pin->pin_nid;
        if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
                /* Verify pin:cvt selections to avoid silent audio after S3.
                 * After S3, the audio driver restores pin:cvt selections
@@ -1873,7 +1740,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
        /* Todo: add DP1.2 MST audio support later */
-       snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate);
+       if (codec_has_acomp(codec))
+               snd_hdac_sync_audio_rate(&codec->bus->core, pin_nid, runtime->rate);
 
        non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
        mutex_lock(&per_pin->lock);
@@ -1882,7 +1750,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
        mutex_unlock(&per_pin->lock);
-
        if (spec->dyn_pin_out) {
                pinctl = snd_hda_codec_read(codec, pin_nid, 0,
                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
@@ -1891,7 +1758,10 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                    pinctl | PIN_OUT);
        }
 
-       return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+       err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
+                                stream_tag, format);
+       mutex_unlock(&spec->pcm_lock);
+       return err;
 }
 
 static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -1907,12 +1777,15 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                          struct snd_pcm_substream *substream)
 {
        struct hdmi_spec *spec = codec->spec;
-       int cvt_idx, pin_idx;
+       int cvt_idx, pin_idx, pcm_idx;
        struct hdmi_spec_per_cvt *per_cvt;
        struct hdmi_spec_per_pin *per_pin;
        int pinctl;
 
        if (hinfo->nid) {
+               pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+               if (snd_BUG_ON(pcm_idx < 0))
+                       return -EINVAL;
                cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
                if (snd_BUG_ON(cvt_idx < 0))
                        return -EINVAL;
@@ -1922,9 +1795,19 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                per_cvt->assigned = 0;
                hinfo->nid = 0;
 
+               mutex_lock(&spec->pcm_lock);
+               snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+               clear_bit(pcm_idx, &spec->pcm_in_use);
                pin_idx = hinfo_to_pin_index(codec, hinfo);
-               if (snd_BUG_ON(pin_idx < 0))
+               if (spec->dyn_pcm_assign && pin_idx < 0) {
+                       mutex_unlock(&spec->pcm_lock);
+                       return 0;
+               }
+
+               if (snd_BUG_ON(pin_idx < 0)) {
+                       mutex_unlock(&spec->pcm_lock);
                        return -EINVAL;
+               }
                per_pin = get_pin(spec, pin_idx);
 
                if (spec->dyn_pin_out) {
@@ -1935,8 +1818,6 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                                            pinctl & ~PIN_OUT);
                }
 
-               snd_hda_spdif_ctls_unassign(codec, pin_idx);
-
                mutex_lock(&per_pin->lock);
                per_pin->chmap_set = false;
                memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
@@ -1944,6 +1825,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                per_pin->setup = false;
                per_pin->channels = 0;
                mutex_unlock(&per_pin->lock);
+               mutex_unlock(&spec->pcm_lock);
        }
 
        return 0;
@@ -1956,162 +1838,42 @@ static const struct hda_pcm_ops generic_ops = {
        .cleanup = generic_hdmi_playback_pcm_cleanup,
 };
 
-/*
- * ALSA API channel-map control callbacks
- */
-static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_info *uinfo)
+static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+                                       unsigned char *chmap)
 {
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        struct hdmi_spec *spec = codec->spec;
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = spec->channels_max;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = SNDRV_CHMAP_LAST;
-       return 0;
-}
-
-static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
-                                                 int channels)
-{
-       /* If the speaker allocation matches the channel count, it is OK.*/
-       if (cap->channels != channels)
-               return -1;
-
-       /* all channels are remappable freely */
-       return SNDRV_CTL_TLVT_CHMAP_VAR;
-}
-
-static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
-                                       unsigned int *chmap, int channels)
-{
-       int count = 0;
-       int c;
-
-       for (c = 7; c >= 0; c--) {
-               int spk = cap->speakers[c];
-               if (!spk)
-                       continue;
-
-               chmap[count++] = spk_to_chmap(spk);
-       }
-
-       WARN_ON(count != channels);
-}
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
 
-static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
-                             unsigned int size, unsigned int __user *tlv)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
-       struct hdmi_spec *spec = codec->spec;
-       unsigned int __user *dst;
-       int chs, count = 0;
+       /* chmap is already set to 0 in caller */
+       if (!per_pin)
+               return;
 
-       if (size < 8)
-               return -ENOMEM;
-       if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
-               return -EFAULT;
-       size -= 8;
-       dst = tlv + 2;
-       for (chs = 2; chs <= spec->channels_max; chs++) {
-               int i;
-               struct cea_channel_speaker_allocation *cap;
-               cap = channel_allocations;
-               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
-                       int chs_bytes = chs * 4;
-                       int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs);
-                       unsigned int tlv_chmap[8];
-
-                       if (type < 0)
-                               continue;
-                       if (size < 8)
-                               return -ENOMEM;
-                       if (put_user(type, dst) ||
-                           put_user(chs_bytes, dst + 1))
-                               return -EFAULT;
-                       dst += 2;
-                       size -= 8;
-                       count += 8;
-                       if (size < chs_bytes)
-                               return -ENOMEM;
-                       size -= chs_bytes;
-                       count += chs_bytes;
-                       spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs);
-                       if (copy_to_user(dst, tlv_chmap, chs_bytes))
-                               return -EFAULT;
-                       dst += chs;
-               }
-       }
-       if (put_user(count, tlv + 1))
-               return -EFAULT;
-       return 0;
+       memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap));
 }
 
-static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
+static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+                               unsigned char *chmap, int prepared)
 {
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx = kcontrol->private_value;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       int i;
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
 
-       for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
-               ucontrol->value.integer.value[i] = per_pin->chmap[i];
-       return 0;
-}
-
-static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
-       struct hdmi_spec *spec = codec->spec;
-       int pin_idx = kcontrol->private_value;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       unsigned int ctl_idx;
-       struct snd_pcm_substream *substream;
-       unsigned char chmap[8];
-       int i, err, ca, prepared = 0;
-
-       ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       substream = snd_pcm_chmap_substream(info, ctl_idx);
-       if (!substream || !substream->runtime)
-               return 0; /* just for avoiding error from alsactl restore */
-       switch (substream->runtime->status->state) {
-       case SNDRV_PCM_STATE_OPEN:
-       case SNDRV_PCM_STATE_SETUP:
-               break;
-       case SNDRV_PCM_STATE_PREPARED:
-               prepared = 1;
-               break;
-       default:
-               return -EBUSY;
-       }
-       memset(chmap, 0, sizeof(chmap));
-       for (i = 0; i < ARRAY_SIZE(chmap); i++)
-               chmap[i] = ucontrol->value.integer.value[i];
-       if (!memcmp(chmap, per_pin->chmap, sizeof(chmap)))
-               return 0;
-       ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
-       if (ca < 0)
-               return -EINVAL;
-       if (spec->ops.chmap_validate) {
-               err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
-               if (err)
-                       return err;
-       }
        mutex_lock(&per_pin->lock);
        per_pin->chmap_set = true;
-       memcpy(per_pin->chmap, chmap, sizeof(chmap));
+       memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap));
        if (prepared)
                hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
        mutex_unlock(&per_pin->lock);
+}
 
-       return 0;
+static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
+{
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+       struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+       return per_pin ? true:false;
 }
 
 static int generic_hdmi_build_pcms(struct hda_codec *codec)
@@ -2126,7 +1888,9 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
                info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
                if (!info)
                        return -ENOMEM;
-               spec->pcm_rec[pin_idx] = info;
+
+               spec->pcm_rec[pin_idx].pcm = info;
+               spec->pcm_used++;
                info->pcm_type = HDA_PCM_TYPE_HDMI;
                info->own_chmap = true;
 
@@ -2139,15 +1903,16 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
-static void free_acomp_jack_priv(struct snd_jack *jack)
+static void free_hdmi_jack_priv(struct snd_jack *jack)
 {
-       struct hdmi_spec_per_pin *per_pin = jack->private_data;
+       struct hdmi_pcm *pcm = jack->private_data;
 
-       per_pin->acomp_jack = NULL;
+       pcm->jack = NULL;
 }
 
-static int add_acomp_jack_kctl(struct hda_codec *codec,
-                              struct hdmi_spec_per_pin *per_pin,
+static int add_hdmi_jack_kctl(struct hda_codec *codec,
+                              struct hdmi_spec *spec,
+                              int pcm_idx,
                               const char *name)
 {
        struct snd_jack *jack;
@@ -2157,88 +1922,107 @@ static int add_acomp_jack_kctl(struct hda_codec *codec,
                           true, false);
        if (err < 0)
                return err;
-       per_pin->acomp_jack = jack;
-       jack->private_data = per_pin;
-       jack->private_free = free_acomp_jack_priv;
+
+       spec->pcm_rec[pcm_idx].jack = jack;
+       jack->private_data = &spec->pcm_rec[pcm_idx];
+       jack->private_free = free_hdmi_jack_priv;
        return 0;
 }
 
-static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
+static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
 {
        char hdmi_str[32] = "HDMI/DP";
        struct hdmi_spec *spec = codec->spec;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       int pcmdev = get_pcm_rec(spec, pin_idx)->device;
+       struct hdmi_spec_per_pin *per_pin;
+       struct hda_jack_tbl *jack;
+       int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
        bool phantom_jack;
+       int ret;
 
        if (pcmdev > 0)
                sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
-       if (codec_has_acomp(codec))
-               return add_acomp_jack_kctl(codec, per_pin, hdmi_str);
+
+       if (spec->dyn_pcm_assign)
+               return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str);
+
+       /* for !dyn_pcm_assign, we still use hda_jack for compatibility */
+       /* if !dyn_pcm_assign, it must be non-MST mode.
+        * This means pcms and pins are statically mapped.
+        * And pcm_idx is pin_idx.
+        */
+       per_pin = get_pin(spec, pcm_idx);
        phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid);
        if (phantom_jack)
                strncat(hdmi_str, " Phantom",
                        sizeof(hdmi_str) - strlen(hdmi_str) - 1);
-
-       return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
-                                    phantom_jack);
+       ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
+                                   phantom_jack);
+       if (ret < 0)
+               return ret;
+       jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
+       if (jack == NULL)
+               return 0;
+       /* assign jack->jack to pcm_rec[].jack to
+        * align with dyn_pcm_assign mode
+        */
+       spec->pcm_rec[pcm_idx].jack = jack->jack;
+       return 0;
 }
 
 static int generic_hdmi_build_controls(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
        int err;
-       int pin_idx;
+       int pin_idx, pcm_idx;
 
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
-               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
 
-               err = generic_hdmi_build_jack(codec, pin_idx);
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+               err = generic_hdmi_build_jack(codec, pcm_idx);
                if (err < 0)
                        return err;
 
-               err = snd_hda_create_dig_out_ctls(codec,
+               /* create the spdif for each pcm
+                * pin will be bound when monitor is connected
+                */
+               if (spec->dyn_pcm_assign)
+                       err = snd_hda_create_dig_out_ctls(codec,
+                                         0, spec->cvt_nids[0],
+                                         HDA_PCM_TYPE_HDMI);
+               else {
+                       struct hdmi_spec_per_pin *per_pin =
+                               get_pin(spec, pcm_idx);
+                       err = snd_hda_create_dig_out_ctls(codec,
                                                  per_pin->pin_nid,
                                                  per_pin->mux_nids[0],
                                                  HDA_PCM_TYPE_HDMI);
+               }
                if (err < 0)
                        return err;
-               snd_hda_spdif_ctls_unassign(codec, pin_idx);
+               snd_hda_spdif_ctls_unassign(codec, pcm_idx);
 
                /* add control for ELD Bytes */
-               err = hdmi_create_eld_ctl(codec, pin_idx,
-                                         get_pcm_rec(spec, pin_idx)->device);
-
+               err = hdmi_create_eld_ctl(codec, pcm_idx,
+                                       get_pcm_rec(spec, pcm_idx)->device);
                if (err < 0)
                        return err;
+       }
+
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
 
                hdmi_present_sense(per_pin, 0);
        }
 
        /* add channel maps */
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
                struct hda_pcm *pcm;
-               struct snd_pcm_chmap *chmap;
-               struct snd_kcontrol *kctl;
-               int i;
 
-               pcm = spec->pcm_rec[pin_idx];
+               pcm = get_pcm_rec(spec, pcm_idx);
                if (!pcm || !pcm->pcm)
                        break;
-               err = snd_pcm_add_chmap_ctls(pcm->pcm,
-                                            SNDRV_PCM_STREAM_PLAYBACK,
-                                            NULL, 0, pin_idx, &chmap);
+               err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap);
                if (err < 0)
                        return err;
-               /* override handlers */
-               chmap->private_data = codec;
-               kctl = chmap->kctl;
-               for (i = 0; i < kctl->count; i++)
-                       kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
-               kctl->info = hdmi_chmap_ctl_info;
-               kctl->get = hdmi_chmap_ctl_get;
-               kctl->put = hdmi_chmap_ctl_put;
-               kctl->tlv.c = hdmi_chmap_ctl_tlv;
        }
 
        return 0;
@@ -2290,27 +2074,43 @@ static void hdmi_array_free(struct hdmi_spec *spec)
        snd_array_free(&spec->cvts);
 }
 
+static void generic_spec_free(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+
+       if (spec) {
+               hdmi_array_free(spec);
+               kfree(spec);
+               codec->spec = NULL;
+       }
+       codec->dp_mst = false;
+}
+
 static void generic_hdmi_free(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx;
+       int pin_idx, pcm_idx;
 
        if (codec_has_acomp(codec))
                snd_hdac_i915_register_notifier(NULL);
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
                struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-
                cancel_delayed_work_sync(&per_pin->work);
                eld_proc_free(per_pin);
-               if (per_pin->acomp_jack)
-                       snd_device_free(codec->card, per_pin->acomp_jack);
        }
 
-       if (spec->i915_bound)
-               snd_hdac_i915_exit(&codec->bus->core);
-       hdmi_array_free(spec);
-       kfree(spec);
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+               if (spec->pcm_rec[pcm_idx].jack == NULL)
+                       continue;
+               if (spec->dyn_pcm_assign)
+                       snd_device_free(codec->card,
+                                       spec->pcm_rec[pcm_idx].jack);
+               else
+                       spec->pcm_rec[pcm_idx].jack = NULL;
+       }
+
+       generic_spec_free(codec);
 }
 
 #ifdef CONFIG_PM
@@ -2343,15 +2143,58 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
 
 static const struct hdmi_ops generic_standard_hdmi_ops = {
        .pin_get_eld                            = snd_hdmi_get_eld,
-       .pin_get_slot_channel                   = hdmi_pin_get_slot_channel,
-       .pin_set_slot_channel                   = hdmi_pin_set_slot_channel,
        .pin_setup_infoframe                    = hdmi_pin_setup_infoframe,
        .pin_hbr_setup                          = hdmi_pin_hbr_setup,
        .setup_stream                           = hdmi_setup_stream,
-       .chmap_cea_alloc_validate_get_type      = hdmi_chmap_cea_alloc_validate_get_type,
-       .cea_alloc_to_tlv_chmap                 = hdmi_cea_alloc_to_tlv_chmap,
 };
 
+/* allocate codec->spec and assign/initialize generic parser ops */
+static int alloc_generic_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+
+       spec->ops = generic_standard_hdmi_ops;
+       mutex_init(&spec->pcm_lock);
+       snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+
+       spec->chmap.ops.get_chmap = hdmi_get_chmap;
+       spec->chmap.ops.set_chmap = hdmi_set_chmap;
+       spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+
+       codec->spec = spec;
+       hdmi_array_init(spec, 4);
+
+       codec->patch_ops = generic_hdmi_patch_ops;
+
+       return 0;
+}
+
+/* generic HDMI parser */
+static int patch_generic_hdmi(struct hda_codec *codec)
+{
+       int err;
+
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
+               return err;
+       }
+
+       generic_hdmi_init_per_pins(codec);
+       return 0;
+}
+
+/*
+ * Intel codec parsers and helpers
+ */
 
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
                                             hda_nid_t nid)
@@ -2432,6 +2275,10 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
        struct hda_codec *codec = audio_ptr;
        int pin_nid = port + 0x04;
 
+       /* we assume only from port-B to port-D */
+       if (port < 1 || port > 3)
+               return;
+
        /* skip notification during system suspend (but not in runtime PM);
         * the state will be updated at resume
         */
@@ -2444,72 +2291,96 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
        check_presence_and_report(codec, pin_nid);
 }
 
-static int patch_generic_hdmi(struct hda_codec *codec)
+/* register i915 component pin_eld_notify callback */
+static void register_i915_notifier(struct hda_codec *codec)
 {
-       struct hdmi_spec *spec;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
+       struct hdmi_spec *spec = codec->spec;
 
-       spec->ops = generic_standard_hdmi_ops;
-       codec->spec = spec;
-       hdmi_array_init(spec, 4);
+       spec->use_acomp_notifier = true;
+       spec->i915_audio_ops.audio_ptr = codec;
+       /* intel_audio_codec_enable() or intel_audio_codec_disable()
+        * will call pin_eld_notify with using audio_ptr pointer
+        * We need make sure audio_ptr is really setup
+        */
+       wmb();
+       spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
+       snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
+}
 
-       /* Try to bind with i915 for any Intel codecs (if not done yet) */
-       if (!codec_has_acomp(codec) &&
-           (codec->core.vendor_id >> 16) == 0x8086)
-               if (!snd_hdac_i915_init(&codec->bus->core))
-                       spec->i915_bound = true;
+/* Intel Haswell and onwards; audio component with eld notifier */
+static int patch_i915_hsw_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err;
 
-       if (is_haswell_plus(codec)) {
-               intel_haswell_enable_all_pins(codec, true);
-               intel_haswell_fixup_enable_dp12(codec);
+       /* HSW+ requires i915 binding */
+       if (!codec->bus->core.audio_component) {
+               codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+               return -ENODEV;
        }
 
-       /* For Valleyview/Cherryview, only the display codec is in the display
-        * power well and can use link_power ops to request/release the power.
-        * For Haswell/Broadwell, the controller is also in the power well and
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+       spec = codec->spec;
+
+       intel_haswell_enable_all_pins(codec, true);
+       intel_haswell_fixup_enable_dp12(codec);
+
+       /* For Haswell/Broadwell, the controller is also in the power well and
         * can cover the codec power request, and so need not set this flag.
-        * For previous platforms, there is no such power well feature.
         */
-       if (is_valleyview_plus(codec) || is_skylake(codec) ||
-                       is_broxton(codec))
+       if (!is_haswell(codec) && !is_broadwell(codec))
                codec->core.link_power_control = 1;
 
-       if (hdmi_parse_codec(codec) < 0) {
-               if (spec->i915_bound)
-                       snd_hdac_i915_exit(&codec->bus->core);
-               codec->spec = NULL;
-               kfree(spec);
-               return -EINVAL;
+       codec->patch_ops.set_power_state = haswell_set_power_state;
+       codec->dp_mst = true;
+       codec->depop_delay = 0;
+       codec->auto_runtime_pm = 1;
+
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
+               return err;
        }
-       codec->patch_ops = generic_hdmi_patch_ops;
-       if (is_haswell_plus(codec)) {
-               codec->patch_ops.set_power_state = haswell_set_power_state;
-               codec->dp_mst = true;
+
+       generic_hdmi_init_per_pins(codec);
+       register_i915_notifier(codec);
+       return 0;
+}
+
+/* Intel Baytrail and Braswell; without get_eld notifier */
+static int patch_i915_byt_hdmi(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int err;
+
+       /* requires i915 binding */
+       if (!codec->bus->core.audio_component) {
+               codec_info(codec, "No i915 binding for Intel HDMI/DP codec\n");
+               return -ENODEV;
        }
 
-       /* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */
-       if (is_haswell_plus(codec) || is_valleyview_plus(codec))
-               codec->auto_runtime_pm = 1;
+       err = alloc_generic_hdmi(codec);
+       if (err < 0)
+               return err;
+       spec = codec->spec;
 
-       generic_hdmi_init_per_pins(codec);
+       /* For Valleyview/Cherryview, only the display codec is in the display
+        * power well and can use link_power ops to request/release the power.
+        */
+       codec->core.link_power_control = 1;
 
-       init_channel_allocations();
+       codec->depop_delay = 0;
+       codec->auto_runtime_pm = 1;
 
-       if (codec_has_acomp(codec)) {
-               codec->depop_delay = 0;
-               spec->i915_audio_ops.audio_ptr = codec;
-               /* intel_audio_codec_enable() or intel_audio_codec_disable()
-                * will call pin_eld_notify with using audio_ptr pointer
-                * We need make sure audio_ptr is really setup
-                */
-               wmb();
-               spec->i915_audio_ops.pin_eld_notify = intel_pin_eld_notify;
-               snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
+       err = hdmi_parse_codec(codec);
+       if (err < 0) {
+               generic_spec_free(codec);
+               return err;
        }
 
+       generic_hdmi_init_per_pins(codec);
        return 0;
 }
 
@@ -2532,7 +2403,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
        info = snd_hda_codec_pcm_new(codec, "HDMI 0");
        if (!info)
                return -ENOMEM;
-       spec->pcm_rec[0] = info;
+       spec->pcm_rec[0].pcm = info;
        info->pcm_type = HDA_PCM_TYPE_HDMI;
        pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
        *pstr = spec->pcm_playback;
@@ -3043,16 +2914,22 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
  * - 0x10de0015
  * - 0x10de0040
  */
-static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
-                                                   int channels)
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap, int channels)
 {
        if (cap->ca_index == 0x00 && channels == 2)
                return SNDRV_CTL_TLVT_CHMAP_FIXED;
 
-       return hdmi_chmap_cea_alloc_validate_get_type(cap, channels);
+       /* If the speaker allocation matches the channel count, it is OK. */
+       if (cap->channels != channels)
+               return -1;
+
+       /* all channels are remappable freely */
+       return SNDRV_CTL_TLVT_CHMAP_VAR;
 }
 
-static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map)
+static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
+               int ca, int chs, unsigned char *map)
 {
        if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
                return -EINVAL;
@@ -3072,9 +2949,9 @@ static int patch_nvhdmi(struct hda_codec *codec)
        spec = codec->spec;
        spec->dyn_pin_out = true;
 
-       spec->ops.chmap_cea_alloc_validate_get_type =
+       spec->chmap.ops.chmap_cea_alloc_validate_get_type =
                nvhdmi_chmap_cea_alloc_validate_get_type;
-       spec->ops.chmap_validate = nvhdmi_chmap_validate;
+       spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
 
        return 0;
 }
@@ -3322,16 +3199,17 @@ static int atihdmi_paired_swap_fc_lfe(int pos)
        return pos;
 }
 
-static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
+static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap,
+                       int ca, int chs, unsigned char *map)
 {
-       struct cea_channel_speaker_allocation *cap;
+       struct hdac_cea_channel_speaker_allocation *cap;
        int i, j;
 
        /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
 
-       cap = &channel_allocations[get_channel_allocation_order(ca)];
+       cap = snd_hdac_get_ch_alloc_from_ca(ca);
        for (i = 0; i < chs; ++i) {
-               int mask = to_spk_mask(map[i]);
+               int mask = snd_hdac_chmap_to_spk_mask(map[i]);
                bool ok = false;
                bool companion_ok = false;
 
@@ -3347,7 +3225,7 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
                                if (i % 2 == 0 && i + 1 < chs) {
                                        /* even channel, check the odd companion */
                                        int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1);
-                                       int comp_mask_req = to_spk_mask(map[i+1]);
+                                       int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]);
                                        int comp_mask_act = cap->speakers[comp_chan_idx];
 
                                        if (comp_mask_req == comp_mask_act)
@@ -3369,9 +3247,10 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
        return 0;
 }
 
-static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                       int hdmi_slot, int stream_channel)
+static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
+               hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
 {
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        int verb;
        int ati_channel_setup = 0;
 
@@ -3404,9 +3283,10 @@ static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
        return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
 }
 
-static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                       int asp_slot)
+static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac,
+                               hda_nid_t pin_nid, int asp_slot)
 {
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        bool was_odd = false;
        int ati_asp_slot = asp_slot;
        int verb;
@@ -3433,8 +3313,10 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
        return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
 }
 
-static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
-                                                           int channels)
+static int atihdmi_paired_chmap_cea_alloc_validate_get_type(
+               struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               int channels)
 {
        int c;
 
@@ -3461,8 +3343,9 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_s
        return SNDRV_CTL_TLVT_CHMAP_PAIRED;
 }
 
-static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
-                                                 unsigned int *chmap, int channels)
+static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               unsigned int *chmap, int channels)
 {
        /* produce paired maps for pre-rev3 ATI/AMD codecs */
        int count = 0;
@@ -3479,7 +3362,7 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_all
                        continue;
                }
 
-               chmap[count++] = spk_to_chmap(spk);
+               chmap[count++] = snd_hdac_spk_to_chmap(spk);
        }
 
        WARN_ON(count != channels);
@@ -3573,18 +3456,21 @@ static int patch_atihdmi(struct hda_codec *codec)
        spec = codec->spec;
 
        spec->ops.pin_get_eld = atihdmi_pin_get_eld;
-       spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
-       spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
        spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
        spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
        spec->ops.setup_stream = atihdmi_setup_stream;
 
        if (!has_amd_full_remap_support(codec)) {
                /* override to ATI/AMD-specific versions with pairwise mapping */
-               spec->ops.chmap_cea_alloc_validate_get_type =
+               spec->chmap.ops.chmap_cea_alloc_validate_get_type =
                        atihdmi_paired_chmap_cea_alloc_validate_get_type;
-               spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap;
-               spec->ops.chmap_validate = atihdmi_paired_chmap_validate;
+               spec->chmap.ops.cea_alloc_to_tlv_chmap =
+                               atihdmi_paired_cea_alloc_to_tlv_chmap;
+               spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate;
+               spec->chmap.ops.pin_get_slot_channel =
+                               atihdmi_pin_get_slot_channel;
+               spec->chmap.ops.pin_set_slot_channel =
+                               atihdmi_pin_set_slot_channel;
        }
 
        /* ATI/AMD converters do not advertise all of their capabilities */
@@ -3596,7 +3482,7 @@ static int patch_atihdmi(struct hda_codec *codec)
                per_cvt->maxbps = max(per_cvt->maxbps, 24u);
        }
 
-       spec->channels_max = max(spec->channels_max, 8u);
+       spec->chmap.channels_max = max(spec->chmap.channels_max, 8u);
 
        return 0;
 }
@@ -3659,6 +3545,7 @@ HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP",     patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI",      patch_nvhdmi_2ch),
 HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP",   patch_via_hdmi),
@@ -3672,14 +3559,14 @@ HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI",   patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI",   patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI",        patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI",  patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI",    patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI",    patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI",  patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI",    patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI",    patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI",   patch_i915_hsw_hdmi),
 HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_generic_hdmi),
-HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_i915_byt_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_i915_byt_hdmi),
 HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI",  patch_generic_hdmi),
 /* special ID for generic HDMI */
 HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),