[alsa-devel] [PATCH] ALSA: hda - Fix DP-MST support for NVIDIA codecs

Takashi Iwai tiwai at suse.de
Mon Feb 3 11:40:08 CET 2020


On Mon, 03 Feb 2020 11:06:17 +0100,
Nikhil Mahale wrote:
> 
> If dyn_pcm_assign is set, different jack objects are being created
> for pcm and pins.
> 
> If dyn_pcm_assign is set, generic_hdmi_build_jack() calls into
> add_hdmi_jack_kctl() to create and track separate jack object for
> pcm. Like sync_eld_via_acomp(), hdmi_present_sense_via_verbs() also
> need to report status change of the pcm jack.
> 
> Rename pin_idx_to_jack() to pin_idx_to_pcm_jack(). The code to
> report status change of pcm jack, move it to update_eld() which is
> common for acomp and !acomp code paths.

Thanks, that's the cause of the regression.
However, this needs yet more careful handling, I'm afraid.

- hdmi_present_sense_via_verbs() may return true, and its callers
(both check_presence_and_report() and hdmi_repoll_eld()) do call
snd_hda_jack_report_sync() again.

- For non-dyn_pcm_assign case, we shouldn't call jack report there,
but rather simply return true for calling report sync.

- There is another workaround to block the jack report in
hdmi_present_sense_via_verbs() which is applied after update_eld(),
and this will be ignored.

So, I guess that we need the conditional application of the individual
snd_jack_report() only for spec->dyn_pcm_assign==true, and assure that
hdmi_present() returns false.

The last item (the jack report block) is still unclear to me; it's a
workaround that was needed for Nvidia drivers in the past due to
instability.  If this is still needed for DP-MST case, we have to
reconsider how to deal with it.  Otherwise, this can be applied only
for non-dyn_pcm_assign case.

BTW, the condition for jack->block_report and return value in
hdmi_present_sense_via_verbs() looks currently complicated, but it
could have been simplified like:

-- 8< --
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1569,7 +1569,6 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
 	 * the unsolicited response to avoid custom WARs.
 	 */
 	int present;
-	bool ret;
 	bool do_repoll = false;
 
 	present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);
@@ -1603,16 +1602,14 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
 	else
 		update_eld(codec, per_pin, eld);
 
-	ret = !repoll || !eld->monitor_present || eld->eld_valid;
-
 	jack = snd_hda_jack_tbl_get_mst(codec, pin_nid, per_pin->dev_id);
 	if (jack) {
-		jack->block_report = !ret;
+		jack->block_report = do_repoll;
 		jack->pin_sense = (eld->monitor_present && eld->eld_valid) ?
 			AC_PINSENSE_PRESENCE : 0;
 	}
 	mutex_unlock(&per_pin->lock);
-	return ret;
+	return !do_repoll;
 }
 
 static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec,
-- 8< --


Takashi

> 
> Fixes: 5398e94fb753 ALSA: hda - Add DP-MST support for NVIDIA codecs
> Signed-off-by: Nikhil Mahale <nmahale at nvidia.com>
> ---
>  sound/pci/hda/patch_hdmi.c | 87 ++++++++++++++++++++++++----------------------
>  1 file changed, 45 insertions(+), 42 deletions(-)
> 
> diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
> index 48bddc218829..7b5360037217 100644
> --- a/sound/pci/hda/patch_hdmi.c
> +++ b/sound/pci/hda/patch_hdmi.c
> @@ -1480,6 +1480,35 @@ static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
>  	per_pin->channels = 0;
>  }
>  
> +static struct snd_jack *pin_idx_to_pcm_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 doesn't support DP MST
> +		 * DP MST will use dyn_pcm_assign,
> +		 * so DP MST will never come here
> +		 */
> +		jack_tbl = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
> +						    per_pin->dev_id);
> +		if (jack_tbl)
> +			jack = jack_tbl->jack;
> +	}
> +	return jack;
> +}
> +
>  /* update per_pin ELD from the given new ELD;
>   * setup info frame and notification accordingly
>   */
> @@ -1490,9 +1519,15 @@ static bool update_eld(struct hda_codec *codec,
>  	struct hdmi_eld *pin_eld = &per_pin->sink_eld;
>  	struct hdmi_spec *spec = codec->spec;
>  	bool old_eld_valid = pin_eld->eld_valid;
> +	struct snd_jack *pcm_jack;
>  	bool eld_changed;
>  	int pcm_idx;
>  
> +	/* pcm_idx >=0 before update_eld() means it is in monitor
> +	 * disconnected event. Jack must be fetched before update_eld()
> +	 */
> +	pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
> +
>  	/* for monitor disconnection, save pcm_idx firstly */
>  	pcm_idx = per_pin->pcm_idx;
>  	if (spec->dyn_pcm_assign) {
> @@ -1547,6 +1582,14 @@ static bool update_eld(struct hda_codec *codec,
>  			       SNDRV_CTL_EVENT_MASK_VALUE |
>  			       SNDRV_CTL_EVENT_MASK_INFO,
>  			       &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
> +
> +	if (!pcm_jack)
> +		pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
> +	if (eld_changed && pcm_jack)
> +		snd_jack_report(pcm_jack,
> +				(eld->monitor_present && eld->eld_valid) ?
> +				SND_JACK_AVOUT : 0);
> +
>  	return eld_changed;
>  }
>  
> @@ -1615,43 +1658,12 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
>  	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 doesn't support DP MST
> -		 * DP MST will use dyn_pcm_assign,
> -		 * so DP MST will never come here
> -		 */
> -		jack_tbl = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
> -						    per_pin->dev_id);
> -		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;
> -	bool changed;
>  	int size;
>  
>  	mutex_lock(&per_pin->lock);
> @@ -1674,17 +1686,8 @@ 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);
> -	changed = update_eld(codec, per_pin, eld);
> -	if (jack == NULL)
> -		jack = pin_idx_to_jack(codec, per_pin);
> -	if (changed && jack)
> -		snd_jack_report(jack,
> -				(eld->monitor_present && eld->eld_valid) ?
> -				SND_JACK_AVOUT : 0);
> +	update_eld(codec, per_pin, eld);
> +
>  	mutex_unlock(&per_pin->lock);
>  }
>  
> -- 
> 2.16.4
> 


More information about the Alsa-devel mailing list