mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 04:34:08 +08:00
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: 5398e94fb7
("ALSA: hda - Add DP-MST support for NVIDIA codecs")
Reported-and-tested-by: Martin Regner <martin@larkos.de>
Signed-off-by: Nikhil Mahale <nmahale@nvidia.com>
Reviewed-by: Kai 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: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
112e3f5ac5
commit
c7e661a1c2
@ -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,40 +1648,34 @@ 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
ret = ret && !spec->dyn_pcm_assign;
|
||||
}
|
||||
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 !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)
|
||||
@ -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) ?
|
||||
|
Loading…
Reference in New Issue
Block a user