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;
|
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 */
|
/* update ELD and jack state via HD-audio verbs */
|
||||||
static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
||||||
int repoll)
|
int repoll)
|
||||||
@ -1571,6 +1599,7 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
|
|||||||
int present;
|
int present;
|
||||||
bool ret;
|
bool ret;
|
||||||
bool do_repoll = false;
|
bool do_repoll = false;
|
||||||
|
struct snd_jack *pcm_jack = NULL;
|
||||||
|
|
||||||
present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id);
|
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;
|
do_repoll = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_repoll)
|
if (do_repoll) {
|
||||||
schedule_delayed_work(&per_pin->work, msecs_to_jiffies(300));
|
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);
|
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;
|
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->block_report = !ret;
|
||||||
jack->pin_sense = (eld->monitor_present && eld->eld_valid) ?
|
jack->pin_sense = (eld->monitor_present && eld->eld_valid) ?
|
||||||
AC_PINSENSE_PRESENCE : 0;
|
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);
|
mutex_unlock(&per_pin->lock);
|
||||||
return ret;
|
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 */
|
/* update ELD and jack state via audio component */
|
||||||
static void sync_eld_via_acomp(struct hda_codec *codec,
|
static void sync_eld_via_acomp(struct hda_codec *codec,
|
||||||
struct hdmi_spec_per_pin *per_pin)
|
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
|
/* pcm_idx >=0 before update_eld() means it is in monitor
|
||||||
* disconnected event. Jack must be fetched before update_eld()
|
* 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);
|
changed = update_eld(codec, per_pin, eld);
|
||||||
if (jack == NULL)
|
if (jack == NULL)
|
||||||
jack = pin_idx_to_jack(codec, per_pin);
|
jack = pin_idx_to_pcm_jack(codec, per_pin);
|
||||||
if (changed && jack)
|
if (changed && jack)
|
||||||
snd_jack_report(jack,
|
snd_jack_report(jack,
|
||||||
(eld->monitor_present && eld->eld_valid) ?
|
(eld->monitor_present && eld->eld_valid) ?
|
||||||
|
Loading…
Reference in New Issue
Block a user