diff mbox

[RFC,v2,3/3] ALSA - hda: add DP MST support

Message ID 1458549476-77040-4-git-send-email-libin.yang@linux.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

libin.yang@linux.intel.com March 21, 2016, 8:37 a.m. UTC
From: Libin Yang <libin.yang@linux.intel.com>

This patch adds the DP MST support in hdmi audio driver.
---
 sound/pci/hda/hda_codec.c  |   3 +
 sound/pci/hda/patch_hdmi.c | 179 ++++++++++++++++++++++++++++++++-------------
 2 files changed, 133 insertions(+), 49 deletions(-)

Comments

Takashi Iwai March 22, 2016, 11:28 a.m. UTC | #1
On Mon, 21 Mar 2016 09:37:56 +0100,
libin.yang@linux.intel.com wrote:
> 
> From: Libin Yang <libin.yang@linux.intel.com>
> 
> This patch adds the DP MST support in hdmi audio driver.
> ---
>  sound/pci/hda/hda_codec.c  |   3 +
>  sound/pci/hda/patch_hdmi.c | 179 ++++++++++++++++++++++++++++++++-------------
>  2 files changed, 133 insertions(+), 49 deletions(-)
> 
> diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
> index db94a66..d4be769 100644
> --- a/sound/pci/hda/hda_codec.c
> +++ b/sound/pci/hda/hda_codec.c
> @@ -492,6 +492,9 @@ static int read_pin_defaults(struct hda_codec *codec)
>  		pin->nid = nid;
>  		pin->cfg = snd_hda_codec_read(codec, nid, 0,
>  					      AC_VERB_GET_CONFIG_DEFAULT, 0);
> +		/* all device entries are the same widget control so far
> +		 * fixme: if any codec is different, need fix here
> +		 */
>  		pin->ctrl = snd_hda_codec_read(codec, nid, 0,
>  					       AC_VERB_GET_PIN_WIDGET_CONTROL,
>  					       0);
> diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
> index 5987a31..67e191c 100644
> --- a/sound/pci/hda/patch_hdmi.c
> +++ b/sound/pci/hda/patch_hdmi.c
> @@ -144,7 +144,20 @@ struct hdmi_spec {
>  	struct snd_array cvts; /* struct hdmi_spec_per_cvt */
>  	hda_nid_t cvt_nids[4]; /* only for haswell fix */
>  
> +	/* num_pins is the number of virtual pins
> +	 * for example, there are 3 pins, and each pin
> +	 * has 4 device entries, then the num_pins is 12
> +	 */
>  	int num_pins;
> +	/* num_nids is the number of real pins
> +	 * In the above example, num_nids is 3
> +	 */
> +	int num_nids;
> +	/* dev_num is the number of device entries
> +	 * on each pin.
> +	 * In the above example, dev_num is 4
> +	 */
> +	int dev_num;
>  	struct snd_array pins; /* struct hdmi_spec_per_pin */
>  	struct hdmi_pcm pcm_rec[16];
>  	struct mutex pcm_lock;
> @@ -1248,6 +1261,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
>  
>  static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
>  
> +/* todo:
> + * To fully support DP MST, check_presence_and_report need param dev_id
> + * to tell which device entry occurs monitor connection event
> + */
>  static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid)
>  {
>  	struct hdmi_spec *spec = codec->spec;
> @@ -1271,6 +1288,11 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
>  	struct hda_jack_tbl *jack;
>  	int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
>  
> +	/* assume DP MST uses dyn_pcm_assign and acomp and
> +	 * never comes here
> +	 * if DP MST supports unsol event, below code need
> +	 * consider dev_entry
> +	 */
>  	jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
>  	if (!jack)
>  		return;
> @@ -1499,28 +1521,42 @@ static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
>   * by any other pins.
>   */
>  static void intel_not_share_assigned_cvt(struct hda_codec *codec,
> -			hda_nid_t pin_nid, int mux_idx)
> +					 hda_nid_t pin_nid,
> +					 int dev_id, int mux_idx)
>  {
>  	struct hdmi_spec *spec = codec->spec;
>  	hda_nid_t nid;
>  	int cvt_idx, curr;
>  	struct hdmi_spec_per_cvt *per_cvt;
> +	struct hdmi_spec_per_pin *per_pin;
> +	int pin_idx;
>  
>  	/* configure all pins, including "no physical connection" ones */
> -	for_each_hda_codec_node(nid, codec) {
> -		unsigned int wid_caps = get_wcaps(codec, nid);
> -		unsigned int wid_type = get_wcaps_type(wid_caps);
> +	for (pin_idx = 0; pin_idx <= spec->num_pins; pin_idx++) {

Is the condition correct?  i.e. pin_idx = spec->num_pins is valid?


> +		int dev_id_saved;
>  
> -		if (wid_type != AC_WID_PIN)
> +		per_pin = get_pin(spec, pin_idx);
> +		/* pin not connected to monitor
> +		 * no need to operate on it
> +		 */
> +		if (!per_pin->pcm)
>  			continue;
>  
> -		if (nid == pin_nid)
> +		if ((per_pin->pin_nid == pin_nid) &&
> +			(per_pin->dev_id == dev_id))
>  			continue;
>  
> +		nid = per_pin->pin_nid;
> +
> +		/* save the dev id for echo pin, and restore it when return */
> +		dev_id_saved = snd_hda_get_dev_select(codec, nid);
> +		snd_hda_set_dev_select(codec, nid, per_pin->dev_id);
>  		curr = snd_hda_codec_read(codec, nid, 0,
>  					  AC_VERB_GET_CONNECT_SEL, 0);
> -		if (curr != mux_idx)
> +		if (curr != mux_idx) {
> +			snd_hda_set_dev_select(codec, nid, dev_id_saved);
>  			continue;
> +		}
>  
>  		/* choose an unassigned converter. The conveters in the
>  		 * connection list are in the same order as in the codec.
> @@ -1537,12 +1573,13 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec,
>  				break;
>  			}
>  		}
> +		snd_hda_set_dev_select(codec, nid, dev_id_saved);
>  	}
>  }
>  
>  /* 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)
> +			hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
>  {
>  	int mux_idx;
>  	struct hdmi_spec *spec = codec->spec;
> @@ -1557,7 +1594,7 @@ static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
>  	 */
>  	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);
> +		intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx);
>  }
>  
>  /* called in hdmi_pcm_open when no pin is assigned to the PCM
> @@ -1585,7 +1622,7 @@ static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
>  	per_cvt->assigned = 1;
>  	hinfo->nid = per_cvt->cvt_nid;
>  
> -	intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
> +	intel_not_share_assigned_cvt_nid(codec, 0, 0, per_cvt->cvt_nid);
>  
>  	set_bit(pcm_idx, &spec->pcm_in_use);
>  	/* todo: setup spdif ctls assign */
> @@ -1661,13 +1698,15 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
>  	per_pin->cvt_nid = per_cvt->cvt_nid;
>  	hinfo->nid = per_cvt->cvt_nid;
>  
> +	snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id);
>  	snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
>  			    AC_VERB_SET_CONNECT_SEL,
>  			    mux_idx);
>  
>  	/* configure unused pins to choose other converters */
>  	if (is_haswell_plus(codec) || is_valleyview_plus(codec))
> -		intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
> +		intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
> +					     per_pin->dev_id, mux_idx);
>  
>  	snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
>  
> @@ -1737,13 +1776,13 @@ static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
>  		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++) {
> +	for (i = spec->num_nids; 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++) {
> +	for (i = 0; i < spec->num_nids; i++) {
>  		if (!test_bit(i, &spec->pcm_bitmap))
>  			return i;
>  	}
> @@ -1818,10 +1857,13 @@ static void hdmi_pcm_setup_pin(struct hdmi_spec *spec,
>  	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)
> +	if (mux_idx < per_pin->num_mux_nids) {
> +		snd_hda_set_dev_select(codec, per_pin->pin_nid,
> +				   per_pin->dev_id);
>  		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);
> @@ -1902,7 +1944,7 @@ static void update_eld(struct hda_codec *codec,
>  		if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
>  			intel_verify_pin_cvt_connect(codec, per_pin);
>  			intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
> -						     per_pin->mux_idx);
> +				per_pin->dev_id, per_pin->mux_idx);
>  		}
>  
>  		hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
> @@ -1996,6 +2038,7 @@ static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec,
>  	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 not support mst
>  		jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
>  		if (jack_tbl)
>  			jack = jack_tbl->jack;
> @@ -2013,6 +2056,9 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
>  	int size;
>  
>  	mutex_lock(&per_pin->lock);
> +	/* todo:
> +	 * to fully support DP MST, need add param dev_id
> +	 */
>  	size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid,
>  				      per_pin->dev_id, &eld->monitor_present,
>  				      eld->eld_buffer, ELD_MAX_SIZE);
> @@ -2079,7 +2125,7 @@ static void hdmi_repoll_eld(struct work_struct *work)
>  }
>  
>  static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
> -					     hda_nid_t nid);
> +					     hda_nid_t nid, int dev_id);
>  
>  static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
>  {
> @@ -2088,38 +2134,59 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
>  	int pin_idx;
>  	struct hdmi_spec_per_pin *per_pin;
>  	int err;
> +	int dev_num, i;
>  
>  	caps = snd_hda_query_pin_caps(codec, pin_nid);
>  	if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
>  		return 0;
>  
> +	/* For DP MST audio, Configuration Default is the same for
> +	 * all device entries on the same pin
> +	 */
>  	config = snd_hda_codec_get_pincfg(codec, pin_nid);
>  	if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
>  		return 0;
>  
> -	if (is_haswell_plus(codec))
> -		intel_haswell_fixup_connect_list(codec, pin_nid);
> -
> -	pin_idx = spec->num_pins;
> -	per_pin = snd_array_new(&spec->pins);
> -	if (!per_pin)
> -		return -ENOMEM;
> -
> -	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;
> +	if (is_haswell_plus(codec)) {
> +		dev_num = 3;
> +		spec->dev_num = 3;
> +	} else {
> +		dev_num = snd_hda_get_num_devices(codec, pin_nid) + 1;

Does snd_hda_get_num_devices() return 0?  What if it's no mst-capable
codec, and what if a MST-capable codec having a single device?


> +		/* spec->dev_num is the maxinum number of device entries
> +		 * among all the pins
> +		 */
> +		spec->dev_num = (spec->dev_num > dev_num) ?
> +			spec->dev_num : dev_num;
>  	}

The presence of is_haswell_*() or such macro in every place makes
really hard to maintain the code.  In my new patchset (I'll post soon
later), I tried to reduce these usages, and split the functions to
Intel-specific ones.  Let's try not to contaminate too much again.


Takashi
Yang, Libin March 24, 2016, 8:57 a.m. UTC | #2
Hi Takashi,

> -----Original Message-----
> From: Takashi Iwai [mailto:tiwai@suse.de]
> Sent: Tuesday, March 22, 2016 7:28 PM
> To: libin.yang@linux.intel.com
> Cc: alsa-devel@alsa-project.org; Lin, Mengdong; Yang, Libin
> Subject: Re: [alsa-devel] [RFC PATCH v2 3/3] ALSA - hda: add DP MST
> support
> 
> On Mon, 21 Mar 2016 09:37:56 +0100,
> libin.yang@linux.intel.com wrote:
> >
> > From: Libin Yang <libin.yang@linux.intel.com>
> >
> > This patch adds the DP MST support in hdmi audio driver.
> > ---
> >  sound/pci/hda/hda_codec.c  |   3 +
> >  sound/pci/hda/patch_hdmi.c | 179
> ++++++++++++++++++++++++++++++++-------------
> >  2 files changed, 133 insertions(+), 49 deletions(-)
> >
> > diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
> > index db94a66..d4be769 100644
> > --- a/sound/pci/hda/hda_codec.c
> > +++ b/sound/pci/hda/hda_codec.c
> > @@ -492,6 +492,9 @@ static int read_pin_defaults(struct hda_codec
> *codec)
> >  		pin->nid = nid;
> >  		pin->cfg = snd_hda_codec_read(codec, nid, 0,
> >
> AC_VERB_GET_CONFIG_DEFAULT, 0);
> > +		/* all device entries are the same widget control so far
> > +		 * fixme: if any codec is different, need fix here
> > +		 */
> >  		pin->ctrl = snd_hda_codec_read(codec, nid, 0,
> >
> AC_VERB_GET_PIN_WIDGET_CONTROL,
> >  					       0);
> > diff --git a/sound/pci/hda/patch_hdmi.c
> b/sound/pci/hda/patch_hdmi.c
> > index 5987a31..67e191c 100644
> > --- a/sound/pci/hda/patch_hdmi.c
> > +++ b/sound/pci/hda/patch_hdmi.c
> > @@ -144,7 +144,20 @@ struct hdmi_spec {
> >  	struct snd_array cvts; /* struct hdmi_spec_per_cvt */
> >  	hda_nid_t cvt_nids[4]; /* only for haswell fix */
> >
> > +	/* num_pins is the number of virtual pins
> > +	 * for example, there are 3 pins, and each pin
> > +	 * has 4 device entries, then the num_pins is 12
> > +	 */
> >  	int num_pins;
> > +	/* num_nids is the number of real pins
> > +	 * In the above example, num_nids is 3
> > +	 */
> > +	int num_nids;
> > +	/* dev_num is the number of device entries
> > +	 * on each pin.
> > +	 * In the above example, dev_num is 4
> > +	 */
> > +	int dev_num;
> >  	struct snd_array pins; /* struct hdmi_spec_per_pin */
> >  	struct hdmi_pcm pcm_rec[16];
> >  	struct mutex pcm_lock;
> > @@ -1248,6 +1261,10 @@ static void
> hdmi_setup_audio_infoframe(struct hda_codec *codec,
> >
> >  static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin,
> int repoll);
> >
> > +/* todo:
> > + * To fully support DP MST, check_presence_and_report need param
> dev_id
> > + * to tell which device entry occurs monitor connection event
> > + */
> >  static void check_presence_and_report(struct hda_codec *codec,
> hda_nid_t nid)
> >  {
> >  	struct hdmi_spec *spec = codec->spec;
> > @@ -1271,6 +1288,11 @@ static void hdmi_intrinsic_event(struct
> hda_codec *codec, unsigned int res)
> >  	struct hda_jack_tbl *jack;
> >  	int dev_entry = (res & AC_UNSOL_RES_DE) >>
> AC_UNSOL_RES_DE_SHIFT;
> >
> > +	/* assume DP MST uses dyn_pcm_assign and acomp and
> > +	 * never comes here
> > +	 * if DP MST supports unsol event, below code need
> > +	 * consider dev_entry
> > +	 */
> >  	jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
> >  	if (!jack)
> >  		return;
> > @@ -1499,28 +1521,42 @@ static int intel_cvt_id_to_mux_idx(struct
> hdmi_spec *spec,
> >   * by any other pins.
> >   */
> >  static void intel_not_share_assigned_cvt(struct hda_codec *codec,
> > -			hda_nid_t pin_nid, int mux_idx)
> > +					 hda_nid_t pin_nid,
> > +					 int dev_id, int mux_idx)
> >  {
> >  	struct hdmi_spec *spec = codec->spec;
> >  	hda_nid_t nid;
> >  	int cvt_idx, curr;
> >  	struct hdmi_spec_per_cvt *per_cvt;
> > +	struct hdmi_spec_per_pin *per_pin;
> > +	int pin_idx;
> >
> >  	/* configure all pins, including "no physical connection" ones */
> > -	for_each_hda_codec_node(nid, codec) {
> > -		unsigned int wid_caps = get_wcaps(codec, nid);
> > -		unsigned int wid_type = get_wcaps_type(wid_caps);
> > +	for (pin_idx = 0; pin_idx <= spec->num_pins; pin_idx++) {
> 
> Is the condition correct?  i.e. pin_idx = spec->num_pins is valid?

You are right. My code is wrong.

> 
> 
> > +		int dev_id_saved;
> >
> > -		if (wid_type != AC_WID_PIN)
> > +		per_pin = get_pin(spec, pin_idx);
> > +		/* pin not connected to monitor
> > +		 * no need to operate on it
> > +		 */
> > +		if (!per_pin->pcm)
> >  			continue;
> >
> > -		if (nid == pin_nid)
> > +		if ((per_pin->pin_nid == pin_nid) &&
> > +			(per_pin->dev_id == dev_id))
> >  			continue;
> >
> > +		nid = per_pin->pin_nid;
> > +
> > +		/* save the dev id for echo pin, and restore it when
> return */
> > +		dev_id_saved = snd_hda_get_dev_select(codec, nid);
> > +		snd_hda_set_dev_select(codec, nid, per_pin->dev_id);
> >  		curr = snd_hda_codec_read(codec, nid, 0,
> >  					  AC_VERB_GET_CONNECT_SEL,
> 0);
> > -		if (curr != mux_idx)
> > +		if (curr != mux_idx) {
> > +			snd_hda_set_dev_select(codec, nid,
> dev_id_saved);
> >  			continue;
> > +		}
> >
> >  		/* choose an unassigned converter. The conveters in the
> >  		 * connection list are in the same order as in the codec.
> > @@ -1537,12 +1573,13 @@ static void
> intel_not_share_assigned_cvt(struct hda_codec *codec,
> >  				break;
> >  			}
> >  		}
> > +		snd_hda_set_dev_select(codec, nid, dev_id_saved);
> >  	}
> >  }
> >
> >  /* 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)
> > +			hda_nid_t pin_nid, int dev_id, hda_nid_t
> cvt_nid)
> >  {
> >  	int mux_idx;
> >  	struct hdmi_spec *spec = codec->spec;
> > @@ -1557,7 +1594,7 @@ static void
> intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
> >  	 */
> >  	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);
> > +		intel_not_share_assigned_cvt(codec, pin_nid, dev_id,
> mux_idx);
> >  }
> >
> >  /* called in hdmi_pcm_open when no pin is assigned to the PCM
> > @@ -1585,7 +1622,7 @@ static int hdmi_pcm_open_no_pin(struct
> hda_pcm_stream *hinfo,
> >  	per_cvt->assigned = 1;
> >  	hinfo->nid = per_cvt->cvt_nid;
> >
> > -	intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
> > +	intel_not_share_assigned_cvt_nid(codec, 0, 0, per_cvt->cvt_nid);
> >
> >  	set_bit(pcm_idx, &spec->pcm_in_use);
> >  	/* todo: setup spdif ctls assign */
> > @@ -1661,13 +1698,15 @@ static int hdmi_pcm_open(struct
> hda_pcm_stream *hinfo,
> >  	per_pin->cvt_nid = per_cvt->cvt_nid;
> >  	hinfo->nid = per_cvt->cvt_nid;
> >
> > +	snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin-
> >dev_id);
> >  	snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
> >  			    AC_VERB_SET_CONNECT_SEL,
> >  			    mux_idx);
> >
> >  	/* configure unused pins to choose other converters */
> >  	if (is_haswell_plus(codec) || is_valleyview_plus(codec))
> > -		intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
> mux_idx);
> > +		intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
> > +					     per_pin->dev_id, mux_idx);
> >
> >  	snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
> >
> > @@ -1737,13 +1776,13 @@ static int hdmi_find_pcm_slot(struct
> hdmi_spec *spec,
> >  		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++) {
> > +	for (i = spec->num_nids; 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++) {
> > +	for (i = 0; i < spec->num_nids; i++) {
> >  		if (!test_bit(i, &spec->pcm_bitmap))
> >  			return i;
> >  	}
> > @@ -1818,10 +1857,13 @@ static void hdmi_pcm_setup_pin(struct
> hdmi_spec *spec,
> >  	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)
> > +	if (mux_idx < per_pin->num_mux_nids) {
> > +		snd_hda_set_dev_select(codec, per_pin->pin_nid,
> > +				   per_pin->dev_id);
> >  		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);
> > @@ -1902,7 +1944,7 @@ static void update_eld(struct hda_codec
> *codec,
> >  		if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
> >  			intel_verify_pin_cvt_connect(codec, per_pin);
> >  			intel_not_share_assigned_cvt(codec, per_pin-
> >pin_nid,
> > -						     per_pin->mux_idx);
> > +				per_pin->dev_id, per_pin->mux_idx);
> >  		}
> >
> >  		hdmi_setup_audio_infoframe(codec, per_pin, per_pin-
> >non_pcm);
> > @@ -1996,6 +2038,7 @@ static struct snd_jack
> *pin_idx_to_jack(struct hda_codec *codec,
> >  	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 not support mst
> >  		jack_tbl = snd_hda_jack_tbl_get(codec, per_pin-
> >pin_nid);
> >  		if (jack_tbl)
> >  			jack = jack_tbl->jack;
> > @@ -2013,6 +2056,9 @@ static void sync_eld_via_acomp(struct
> hda_codec *codec,
> >  	int size;
> >
> >  	mutex_lock(&per_pin->lock);
> > +	/* todo:
> > +	 * to fully support DP MST, need add param dev_id
> > +	 */
> >  	size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin-
> >pin_nid,
> >  				      per_pin->dev_id, &eld-
> >monitor_present,
> >  				      eld->eld_buffer, ELD_MAX_SIZE);
> > @@ -2079,7 +2125,7 @@ static void hdmi_repoll_eld(struct
> work_struct *work)
> >  }
> >
> >  static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
> > -					     hda_nid_t nid);
> > +					     hda_nid_t nid, int dev_id);
> >
> >  static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
> >  {
> > @@ -2088,38 +2134,59 @@ static int hdmi_add_pin(struct hda_codec
> *codec, hda_nid_t pin_nid)
> >  	int pin_idx;
> >  	struct hdmi_spec_per_pin *per_pin;
> >  	int err;
> > +	int dev_num, i;
> >
> >  	caps = snd_hda_query_pin_caps(codec, pin_nid);
> >  	if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
> >  		return 0;
> >
> > +	/* For DP MST audio, Configuration Default is the same for
> > +	 * all device entries on the same pin
> > +	 */
> >  	config = snd_hda_codec_get_pincfg(codec, pin_nid);
> >  	if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
> >  		return 0;
> >
> > -	if (is_haswell_plus(codec))
> > -		intel_haswell_fixup_connect_list(codec, pin_nid);
> > -
> > -	pin_idx = spec->num_pins;
> > -	per_pin = snd_array_new(&spec->pins);
> > -	if (!per_pin)
> > -		return -ENOMEM;
> > -
> > -	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;
> > +	if (is_haswell_plus(codec)) {
> > +		dev_num = 3;
> > +		spec->dev_num = 3;
> > +	} else {
> > +		dev_num = snd_hda_get_num_devices(codec, pin_nid)
> + 1;
> 
> Does snd_hda_get_num_devices() return 0?  What if it's no mst-capable
> codec, and what if a MST-capable codec having a single device?

If it is no mst-capable, it will return 0. It means only one device is support.
Based on my testing, if I connect MST hub on the port, and connect more
than 1 monitor on the hub, the value will be 2, which means it supports
3 device entries. Otherwise, it will return 0. I'm not sure this is the same
on all other platforms. So I only use the workaround on HSW+.

> 
> 
> > +		/* spec->dev_num is the maxinum number of device
> entries
> > +		 * among all the pins
> > +		 */
> > +		spec->dev_num = (spec->dev_num > dev_num) ?
> > +			spec->dev_num : dev_num;
> >  	}
> 
> The presence of is_haswell_*() or such macro in every place makes
> really hard to maintain the code.  In my new patchset (I'll post soon
> later), I tried to reduce these usages, and split the functions to
> Intel-specific ones.  Let's try not to contaminate too much again.

I see. I will modify the code based on your change.

Regards,
Libin

> 
> 
> Takashi
diff mbox

Patch

diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index db94a66..d4be769 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -492,6 +492,9 @@  static int read_pin_defaults(struct hda_codec *codec)
 		pin->nid = nid;
 		pin->cfg = snd_hda_codec_read(codec, nid, 0,
 					      AC_VERB_GET_CONFIG_DEFAULT, 0);
+		/* all device entries are the same widget control so far
+		 * fixme: if any codec is different, need fix here
+		 */
 		pin->ctrl = snd_hda_codec_read(codec, nid, 0,
 					       AC_VERB_GET_PIN_WIDGET_CONTROL,
 					       0);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 5987a31..67e191c 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -144,7 +144,20 @@  struct hdmi_spec {
 	struct snd_array cvts; /* struct hdmi_spec_per_cvt */
 	hda_nid_t cvt_nids[4]; /* only for haswell fix */
 
+	/* num_pins is the number of virtual pins
+	 * for example, there are 3 pins, and each pin
+	 * has 4 device entries, then the num_pins is 12
+	 */
 	int num_pins;
+	/* num_nids is the number of real pins
+	 * In the above example, num_nids is 3
+	 */
+	int num_nids;
+	/* dev_num is the number of device entries
+	 * on each pin.
+	 * In the above example, dev_num is 4
+	 */
+	int dev_num;
 	struct snd_array pins; /* struct hdmi_spec_per_pin */
 	struct hdmi_pcm pcm_rec[16];
 	struct mutex pcm_lock;
@@ -1248,6 +1261,10 @@  static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
 
 static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll);
 
+/* todo:
+ * To fully support DP MST, check_presence_and_report need param dev_id
+ * to tell which device entry occurs monitor connection event
+ */
 static void check_presence_and_report(struct hda_codec *codec, hda_nid_t nid)
 {
 	struct hdmi_spec *spec = codec->spec;
@@ -1271,6 +1288,11 @@  static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
 	struct hda_jack_tbl *jack;
 	int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
 
+	/* assume DP MST uses dyn_pcm_assign and acomp and
+	 * never comes here
+	 * if DP MST supports unsol event, below code need
+	 * consider dev_entry
+	 */
 	jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
 	if (!jack)
 		return;
@@ -1499,28 +1521,42 @@  static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
  * by any other pins.
  */
 static void intel_not_share_assigned_cvt(struct hda_codec *codec,
-			hda_nid_t pin_nid, int mux_idx)
+					 hda_nid_t pin_nid,
+					 int dev_id, int mux_idx)
 {
 	struct hdmi_spec *spec = codec->spec;
 	hda_nid_t nid;
 	int cvt_idx, curr;
 	struct hdmi_spec_per_cvt *per_cvt;
+	struct hdmi_spec_per_pin *per_pin;
+	int pin_idx;
 
 	/* configure all pins, including "no physical connection" ones */
-	for_each_hda_codec_node(nid, codec) {
-		unsigned int wid_caps = get_wcaps(codec, nid);
-		unsigned int wid_type = get_wcaps_type(wid_caps);
+	for (pin_idx = 0; pin_idx <= spec->num_pins; pin_idx++) {
+		int dev_id_saved;
 
-		if (wid_type != AC_WID_PIN)
+		per_pin = get_pin(spec, pin_idx);
+		/* pin not connected to monitor
+		 * no need to operate on it
+		 */
+		if (!per_pin->pcm)
 			continue;
 
-		if (nid == pin_nid)
+		if ((per_pin->pin_nid == pin_nid) &&
+			(per_pin->dev_id == dev_id))
 			continue;
 
+		nid = per_pin->pin_nid;
+
+		/* save the dev id for echo pin, and restore it when return */
+		dev_id_saved = snd_hda_get_dev_select(codec, nid);
+		snd_hda_set_dev_select(codec, nid, per_pin->dev_id);
 		curr = snd_hda_codec_read(codec, nid, 0,
 					  AC_VERB_GET_CONNECT_SEL, 0);
-		if (curr != mux_idx)
+		if (curr != mux_idx) {
+			snd_hda_set_dev_select(codec, nid, dev_id_saved);
 			continue;
+		}
 
 		/* choose an unassigned converter. The conveters in the
 		 * connection list are in the same order as in the codec.
@@ -1537,12 +1573,13 @@  static void intel_not_share_assigned_cvt(struct hda_codec *codec,
 				break;
 			}
 		}
+		snd_hda_set_dev_select(codec, nid, dev_id_saved);
 	}
 }
 
 /* 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)
+			hda_nid_t pin_nid, int dev_id, hda_nid_t cvt_nid)
 {
 	int mux_idx;
 	struct hdmi_spec *spec = codec->spec;
@@ -1557,7 +1594,7 @@  static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
 	 */
 	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);
+		intel_not_share_assigned_cvt(codec, pin_nid, dev_id, mux_idx);
 }
 
 /* called in hdmi_pcm_open when no pin is assigned to the PCM
@@ -1585,7 +1622,7 @@  static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
 	per_cvt->assigned = 1;
 	hinfo->nid = per_cvt->cvt_nid;
 
-	intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
+	intel_not_share_assigned_cvt_nid(codec, 0, 0, per_cvt->cvt_nid);
 
 	set_bit(pcm_idx, &spec->pcm_in_use);
 	/* todo: setup spdif ctls assign */
@@ -1661,13 +1698,15 @@  static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 	per_pin->cvt_nid = per_cvt->cvt_nid;
 	hinfo->nid = per_cvt->cvt_nid;
 
+	snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id);
 	snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
 			    AC_VERB_SET_CONNECT_SEL,
 			    mux_idx);
 
 	/* configure unused pins to choose other converters */
 	if (is_haswell_plus(codec) || is_valleyview_plus(codec))
-		intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
+		intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
+					     per_pin->dev_id, mux_idx);
 
 	snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
 
@@ -1737,13 +1776,13 @@  static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
 		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++) {
+	for (i = spec->num_nids; 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++) {
+	for (i = 0; i < spec->num_nids; i++) {
 		if (!test_bit(i, &spec->pcm_bitmap))
 			return i;
 	}
@@ -1818,10 +1857,13 @@  static void hdmi_pcm_setup_pin(struct hdmi_spec *spec,
 	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)
+	if (mux_idx < per_pin->num_mux_nids) {
+		snd_hda_set_dev_select(codec, per_pin->pin_nid,
+				   per_pin->dev_id);
 		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);
@@ -1902,7 +1944,7 @@  static void update_eld(struct hda_codec *codec,
 		if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
 			intel_verify_pin_cvt_connect(codec, per_pin);
 			intel_not_share_assigned_cvt(codec, per_pin->pin_nid,
-						     per_pin->mux_idx);
+				per_pin->dev_id, per_pin->mux_idx);
 		}
 
 		hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
@@ -1996,6 +2038,7 @@  static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec,
 	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 not support mst
 		jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
 		if (jack_tbl)
 			jack = jack_tbl->jack;
@@ -2013,6 +2056,9 @@  static void sync_eld_via_acomp(struct hda_codec *codec,
 	int size;
 
 	mutex_lock(&per_pin->lock);
+	/* todo:
+	 * to fully support DP MST, need add param dev_id
+	 */
 	size = snd_hdac_acomp_get_eld(&codec->bus->core, per_pin->pin_nid,
 				      per_pin->dev_id, &eld->monitor_present,
 				      eld->eld_buffer, ELD_MAX_SIZE);
@@ -2079,7 +2125,7 @@  static void hdmi_repoll_eld(struct work_struct *work)
 }
 
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
-					     hda_nid_t nid);
+					     hda_nid_t nid, int dev_id);
 
 static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 {
@@ -2088,38 +2134,59 @@  static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 	int pin_idx;
 	struct hdmi_spec_per_pin *per_pin;
 	int err;
+	int dev_num, i;
 
 	caps = snd_hda_query_pin_caps(codec, pin_nid);
 	if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
 		return 0;
 
+	/* For DP MST audio, Configuration Default is the same for
+	 * all device entries on the same pin
+	 */
 	config = snd_hda_codec_get_pincfg(codec, pin_nid);
 	if (get_defcfg_connect(config) == AC_JACK_PORT_NONE)
 		return 0;
 
-	if (is_haswell_plus(codec))
-		intel_haswell_fixup_connect_list(codec, pin_nid);
-
-	pin_idx = spec->num_pins;
-	per_pin = snd_array_new(&spec->pins);
-	if (!per_pin)
-		return -ENOMEM;
-
-	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;
+	if (is_haswell_plus(codec)) {
+		dev_num = 3;
+		spec->dev_num = 3;
+	} else {
+		dev_num = snd_hda_get_num_devices(codec, pin_nid) + 1;
+		/* spec->dev_num is the maxinum number of device entries
+		 * among all the pins
+		 */
+		spec->dev_num = (spec->dev_num > dev_num) ?
+			spec->dev_num : dev_num;
 	}
-	per_pin->pin_nid_idx = pin_idx;
 
-	err = hdmi_read_pin_conn(codec, pin_idx);
-	if (err < 0)
-		return err;
+	for (i = 0; i < dev_num; i++) {
+		pin_idx = spec->num_pins;
+		per_pin = snd_array_new(&spec->pins);
+
+		if (!per_pin)
+			return -ENOMEM;
 
-	spec->num_pins++;
+		if (spec->dyn_pcm_assign) {
+			per_pin->pcm = NULL;
+			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 = pin_nid;
+		per_pin->pin_nid_idx = spec->num_nids;
+		per_pin->dev_id = i;
+		per_pin->non_pcm = false;
+		snd_hda_set_dev_select(codec, pin_nid, i);
+		if (is_haswell_plus(codec))
+			intel_haswell_fixup_connect_list(codec, pin_nid, i);
+		//need check here
+		err = hdmi_read_pin_conn(codec, pin_idx);
+		if (err < 0)
+			return err;
+		spec->num_pins++;
+	}
+	spec->num_nids++;
 
 	return 0;
 }
@@ -2235,7 +2302,7 @@  static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 		 * skip pin setup and return 0 to make audio playback
 		 * be ongoing
 		 */
-		intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+		intel_not_share_assigned_cvt_nid(codec, 0, 0, cvt_nid);
 		snd_hda_codec_setup_stream(codec, cvt_nid,
 					stream_tag, 0, format);
 		mutex_unlock(&spec->pcm_lock);
@@ -2257,8 +2324,10 @@  static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 		 * which can cause a resumed audio playback become silent
 		 * after S3.
 		 */
+		snd_hda_set_dev_select(codec, pin_nid, per_pin->dev_id);
 		intel_verify_pin_cvt_connect(codec, per_pin);
-		intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
+		intel_not_share_assigned_cvt(codec, pin_nid, per_pin->dev_id,
+				     per_pin->mux_idx);
 	}
 
 	/* Call sync_audio_rate to set the N/CTS/M manually if necessary */
@@ -2280,6 +2349,7 @@  static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 				    pinctl | PIN_OUT);
 	}
 
+	//need call snd_hda_set_dev_select()???
 	err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
 				 stream_tag, format);
 	mutex_unlock(&spec->pcm_lock);
@@ -2533,17 +2603,22 @@  static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
 static int generic_hdmi_build_pcms(struct hda_codec *codec)
 {
 	struct hdmi_spec *spec = codec->spec;
-	int pin_idx;
+	int idx;
 
-	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+	/* for non-mst mode, pcm number is the same as before
+	 * for DP MST mode, pcm number is (nid number + dev_num - 1)
+	 *  dev_num is the device entry number in a pin
+	 *
+	 */
+	for (idx = 0; idx < spec->num_nids + spec->dev_num - 1; idx++) {
 		struct hda_pcm *info;
 		struct hda_pcm_stream *pstr;
 
-		info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
+		info = snd_hda_codec_pcm_new(codec, "HDMI %d", idx);
 		if (!info)
 			return -ENOMEM;
 
-		spec->pcm_rec[pin_idx].pcm = info;
+		spec->pcm_rec[idx].pcm = info;
 		spec->pcm_used++;
 		info->pcm_type = HDA_PCM_TYPE_HDMI;
 		info->own_chmap = true;
@@ -2551,6 +2626,9 @@  static int generic_hdmi_build_pcms(struct hda_codec *codec)
 		pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
 		pstr->substreams = 1;
 		pstr->ops = generic_ops;
+		/* pcm number is less than 16 */
+		if (spec->pcm_used >= 16)
+			break;
 		/* other pstr fields are set in open */
 	}
 
@@ -2720,7 +2798,9 @@  static int generic_hdmi_init(struct hda_codec *codec)
 	for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
 		struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
 		hda_nid_t pin_nid = per_pin->pin_nid;
+		int dev_id = per_pin->dev_id;
 
+		snd_hda_set_dev_select(codec, pin_nid, dev_id);
 		hdmi_init_pin(codec, pin_nid);
 		if (!codec_has_acomp(codec))
 			snd_hda_jack_detect_enable_callback(codec, pin_nid,
@@ -2813,21 +2893,21 @@  static const struct hdmi_ops generic_standard_hdmi_ops = {
 
 
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
-					     hda_nid_t nid)
+					     hda_nid_t nid, int dev_id)
 {
 	struct hdmi_spec *spec = codec->spec;
 	hda_nid_t conns[4];
 	int nconns;
 
-	nconns = snd_hda_get_connections(codec, nid, 0, conns,
-					 ARRAY_SIZE(conns));
+	nconns = snd_hda_get_connections(codec, nid, dev_id,
+					 conns, ARRAY_SIZE(conns));
 	if (nconns == spec->num_cvts &&
 	    !memcmp(conns, spec->cvt_nids, spec->num_cvts * sizeof(hda_nid_t)))
 		return;
 
 	/* override pins connection list */
 	codec_dbg(codec, "hdmi: haswell: override pin connection 0x%x\n", nid);
-	snd_hda_override_conn_list(codec, nid, 0, spec->num_cvts,
+	snd_hda_override_conn_list(codec, nid, dev_id, spec->num_cvts,
 				   spec->cvt_nids);
 }
 
@@ -2914,6 +2994,7 @@  static int patch_generic_hdmi(struct hda_codec *codec)
 		return -ENOMEM;
 
 	spec->ops = generic_standard_hdmi_ops;
+	spec->dev_num = 1;	/* initialize to 1 */
 	mutex_init(&spec->pcm_lock);
 	codec->spec = spec;
 	hdmi_array_init(spec, 4);
@@ -2927,6 +3008,8 @@  static int patch_generic_hdmi(struct hda_codec *codec)
 	if (is_haswell_plus(codec)) {
 		intel_haswell_enable_all_pins(codec, true);
 		intel_haswell_fixup_enable_dp12(codec);
+		codec->dp_mst = true;
+		spec->dyn_pcm_assign = true;
 	}
 
 	/* For Valleyview/Cherryview, only the display codec is in the display
@@ -2947,10 +3030,8 @@  static int patch_generic_hdmi(struct hda_codec *codec)
 		return -EINVAL;
 	}
 	codec->patch_ops = generic_hdmi_patch_ops;
-	if (is_haswell_plus(codec)) {
+	if (is_haswell_plus(codec))
 		codec->patch_ops.set_power_state = haswell_set_power_state;
-		codec->dp_mst = true;
-	}
 
 	/* Enable runtime pm for HDMI audio codec of HSW/BDW/SKL/BYT/BSW */
 	if (is_haswell_plus(codec) || is_valleyview_plus(codec))