Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 5398e94fb753 ("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>

authored by

Nikhil Mahale and committed by
Takashi Iwai
c7e661a1 112e3f5a

+65 -33
+65 -33
sound/pci/hda/patch_hdmi.c
··· 1550 1550 return eld_changed; 1551 1551 } 1552 1552 1553 + static struct snd_jack *pin_idx_to_pcm_jack(struct hda_codec *codec, 1554 + struct hdmi_spec_per_pin *per_pin) 1555 + { 1556 + struct hdmi_spec *spec = codec->spec; 1557 + struct snd_jack *jack = NULL; 1558 + struct hda_jack_tbl *jack_tbl; 1559 + 1560 + /* if !dyn_pcm_assign, get jack from hda_jack_tbl 1561 + * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not 1562 + * NULL even after snd_hda_jack_tbl_clear() is called to 1563 + * free snd_jack. This may cause access invalid memory 1564 + * when calling snd_jack_report 1565 + */ 1566 + if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign) { 1567 + jack = spec->pcm_rec[per_pin->pcm_idx].jack; 1568 + } else if (!spec->dyn_pcm_assign) { 1569 + /* 1570 + * jack tbl doesn't support DP MST 1571 + * DP MST will use dyn_pcm_assign, 1572 + * so DP MST will never come here 1573 + */ 1574 + jack_tbl = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid, 1575 + per_pin->dev_id); 1576 + if (jack_tbl) 1577 + jack = jack_tbl->jack; 1578 + } 1579 + return jack; 1580 + } 1553 1581 /* update ELD and jack state via HD-audio verbs */ 1554 1582 static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, 1555 1583 int repoll) ··· 1599 1571 int present; 1600 1572 bool ret; 1601 1573 bool do_repoll = false; 1574 + struct snd_jack *pcm_jack = NULL; 1602 1575 1603 1576 present = snd_hda_jack_pin_sense(codec, pin_nid, dev_id); 1604 1577 ··· 1627 1598 do_repoll = true; 1628 1599 } 1629 1600 1630 - if (do_repoll) 1601 + if (do_repoll) { 1631 1602 schedule_delayed_work(&per_pin->work, msecs_to_jiffies(300)); 1632 - else 1603 + } else { 1604 + /* 1605 + * pcm_idx >=0 before update_eld() means it is in monitor 1606 + * disconnected event. Jack must be fetched before 1607 + * update_eld(). 1608 + */ 1609 + pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); 1633 1610 update_eld(codec, per_pin, eld); 1611 + if (!pcm_jack) 1612 + pcm_jack = pin_idx_to_pcm_jack(codec, per_pin); 1613 + } 1634 1614 1635 1615 ret = !repoll || !eld->monitor_present || eld->eld_valid; 1636 1616 ··· 1648 1610 jack->block_report = !ret; 1649 1611 jack->pin_sense = (eld->monitor_present && eld->eld_valid) ? 1650 1612 AC_PINSENSE_PRESENCE : 0; 1613 + 1614 + if (spec->dyn_pcm_assign && pcm_jack && !do_repoll) { 1615 + int state = 0; 1616 + 1617 + if (jack->pin_sense & AC_PINSENSE_PRESENCE) 1618 + state = SND_JACK_AVOUT; 1619 + snd_jack_report(pcm_jack, state); 1620 + } 1621 + 1622 + /* 1623 + * snd_hda_jack_pin_sense() call at the beginning of this 1624 + * function, updates jack->pins_sense and clears 1625 + * jack->jack_dirty, therefore snd_hda_jack_report_sync() will 1626 + * not override the jack->pin_sense. 1627 + * 1628 + * snd_hda_jack_report_sync() is superfluous for dyn_pcm_assign 1629 + * case. The jack->pin_sense update was already performed, and 1630 + * hda_jack->jack is NULL for dyn_pcm_assign. 1631 + * 1632 + * Don't call snd_hda_jack_report_sync() for 1633 + * dyn_pcm_assign. 1634 + */ 1635 + ret = ret && !spec->dyn_pcm_assign; 1651 1636 } 1652 1637 mutex_unlock(&per_pin->lock); 1653 1638 return ret; 1654 - } 1655 - 1656 - static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec, 1657 - struct hdmi_spec_per_pin *per_pin) 1658 - { 1659 - struct hdmi_spec *spec = codec->spec; 1660 - struct snd_jack *jack = NULL; 1661 - struct hda_jack_tbl *jack_tbl; 1662 - 1663 - /* if !dyn_pcm_assign, get jack from hda_jack_tbl 1664 - * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not 1665 - * NULL even after snd_hda_jack_tbl_clear() is called to 1666 - * free snd_jack. This may cause access invalid memory 1667 - * when calling snd_jack_report 1668 - */ 1669 - if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign) 1670 - jack = spec->pcm_rec[per_pin->pcm_idx].jack; 1671 - else if (!spec->dyn_pcm_assign) { 1672 - /* 1673 - * jack tbl doesn't support DP MST 1674 - * DP MST will use dyn_pcm_assign, 1675 - * so DP MST will never come here 1676 - */ 1677 - jack_tbl = snd_hda_jack_tbl_get_mst(codec, per_pin->pin_nid, 1678 - per_pin->dev_id); 1679 - if (jack_tbl) 1680 - jack = jack_tbl->jack; 1681 - } 1682 - return jack; 1683 1639 } 1684 1640 1685 1641 /* update ELD and jack state via audio component */ ··· 1709 1677 /* pcm_idx >=0 before update_eld() means it is in monitor 1710 1678 * disconnected event. Jack must be fetched before update_eld() 1711 1679 */ 1712 - jack = pin_idx_to_jack(codec, per_pin); 1680 + jack = pin_idx_to_pcm_jack(codec, per_pin); 1713 1681 changed = update_eld(codec, per_pin, eld); 1714 1682 if (jack == NULL) 1715 - jack = pin_idx_to_jack(codec, per_pin); 1683 + jack = pin_idx_to_pcm_jack(codec, per_pin); 1716 1684 if (changed && jack) 1717 1685 snd_jack_report(jack, 1718 1686 (eld->monitor_present && eld->eld_valid) ?