Skip to content

Commit

Permalink
ALSA: hda - Fix DP-MST support for NVIDIA codecs
Browse files Browse the repository at this point in the history
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: 5398e94 ("ALSA: hda - Add DP-MST support for NVIDIA codecs")
Reported-and-tested-by: Martin Regner <[email protected]>
Signed-off-by: Nikhil Mahale <[email protected]>
Reviewed-by: Kai Vehmanen <[email protected]>
Cc: <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Takashi Iwai <[email protected]>
  • Loading branch information
nmahale authored and tiwai committed Feb 4, 2020
1 parent 112e3f5 commit c7e661a
Showing 1 changed file with 63 additions and 31 deletions.
94 changes: 63 additions & 31 deletions sound/pci/hda/patch_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);

Expand Down Expand Up @@ -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;

Expand All @@ -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 */
Expand Down Expand Up @@ -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) ?
Expand Down

0 comments on commit c7e661a

Please sign in to comment.