Commit c7e661a1 authored by Nikhil Mahale's avatar Nikhil Mahale Committed by Takashi Iwai
Browse files

ALSA: hda - Fix DP-MST support for NVIDIA codecs



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(). Update
hdmi_present_sense_via_verbs() to report plug state of pcm jack
object. Unlike sync_eld_via_acomp(), for !acomp drivers the pcm
jack's plug state must be consistent with plug state
of pin's jack.

Fixes: 5398e94f ("ALSA: hda - Add DP-MST support for NVIDIA codecs")
Reported-and-tested-by: default avatarMartin Regner <martin@larkos.de>
Signed-off-by: default avatarNikhil Mahale <nmahale@nvidia.com>
Reviewed-by: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200204102746.1356-1-nmahale@nvidia.com


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 112e3f5a
Loading
Loading
Loading
Loading
+63 −31
Original line number Diff line number Diff line
@@ -1550,6 +1550,34 @@ static bool update_eld(struct hda_codec *codec,
	return eld_changed;
}

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 ELD and jack state via HD-audio verbs */
static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
					 int repoll)
@@ -1571,6 +1599,7 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
	int present;
	bool ret;
	bool do_repoll = false;
	struct snd_jack *pcm_jack = NULL;

	present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);

@@ -1598,10 +1627,19 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
			do_repoll = true;
	}

	if (do_repoll)
	if (do_repoll) {
		schedule_delayed_work(&per_pin->work, msecs_to_jiffies(300));
	else
	} else {
		/*
		 * 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);
		update_eld(codec, per_pin, eld);
		if (!pcm_jack)
			pcm_jack = pin_idx_to_pcm_jack(codec, per_pin);
	}

	ret = !repoll || !eld->monitor_present || eld->eld_valid;

@@ -1610,38 +1648,32 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
		jack->block_report = !ret;
		jack->pin_sense = (eld->monitor_present && eld->eld_valid) ?
			AC_PINSENSE_PRESENCE : 0;
	}
	mutex_unlock(&per_pin->lock);
	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 (spec->dyn_pcm_assign && pcm_jack && !do_repoll) {
			int state = 0;

			if (jack->pin_sense & AC_PINSENSE_PRESENCE)
				state = SND_JACK_AVOUT;
			snd_jack_report(pcm_jack, state);
		}

	/* 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
		 * snd_hda_jack_pin_sense() call at the beginning of this
		 * function, updates jack->pins_sense and clears
		 * jack->jack_dirty, therefore snd_hda_jack_report_sync() will
		 * not override the jack->pin_sense.
		 *
		 * snd_hda_jack_report_sync() is superfluous for dyn_pcm_assign
		 * case. The jack->pin_sense update was already performed, and
		 * hda_jack->jack is NULL for dyn_pcm_assign.
		 *
		 * Don't call snd_hda_jack_report_sync() for
		 * dyn_pcm_assign.
		 */
		jack_tbl = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid,
						    per_pin->dev_id);
		if (jack_tbl)
			jack = jack_tbl->jack;
		ret = ret && !spec->dyn_pcm_assign;
	}
	return jack;
	mutex_unlock(&per_pin->lock);
	return ret;
}

/* update ELD and jack state via audio component */
@@ -1677,10 +1709,10 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
	/* 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);
	jack = pin_idx_to_pcm_jack(codec, per_pin);
	changed = update_eld(codec, per_pin, eld);
	if (jack == NULL)
		jack = pin_idx_to_jack(codec, per_pin);
		jack = pin_idx_to_pcm_jack(codec, per_pin);
	if (changed && jack)
		snd_jack_report(jack,
				(eld->monitor_present && eld->eld_valid) ?