| From c1e28d4a3ec725ad18494ac6d51277440b2308ea Mon Sep 17 00:00:00 2001 |
| From: Kai Vehmanen <kai.vehmanen@linux.intel.com> |
| Date: Fri, 3 Jul 2020 18:38:17 +0300 |
| Subject: [PATCH] ALSA: hda/hdmi: fix failures at PCM open on Intel ICL and |
| later |
| |
| commit 56275036d8185f92eceac7479d48b858ee3dab84 upstream. |
| |
| When HDMI PCM devices are opened in a specific order, with at least one |
| HDMI/DP receiver connected, ALSA PCM open fails to -EBUSY on the |
| connected monitor, on recent Intel platforms (ICL/JSL and newer). While |
| this is not a typical sequence, at least Pulseaudio does this every time |
| when it is started, to discover the available PCMs. |
| |
| The rootcause is an invalid assumption in hdmi_add_pin(), where the |
| total number of converters is assumed to be known at the time the |
| function is called. On older Intel platforms this held true, but after |
| ICL/JSL, the order how pins and converters are in the subnode list as |
| returned by snd_hda_get_sub_nodes(), was changed. As a result, |
| information for some converters was not stored to per_pin->mux_nids. |
| And this means some pins cannot be connected to all converters, and |
| application instead gets -EBUSY instead at open. |
| |
| The assumption that converters are always before pins in the subnode |
| list, is not really a valid one. Fix the problem in hdmi_parse_codec() |
| by introducing separate loops for discovering converters and pins. |
| |
| BugLink: https://github.com/thesofproject/linux/issues/1978 |
| BugLink: https://github.com/thesofproject/linux/issues/2216 |
| BugLink: https://github.com/thesofproject/linux/issues/2217 |
| Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> |
| Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> |
| Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> |
| Link: https://lore.kernel.org/r/20200703153818.2808592-1-kai.vehmanen@linux.intel.com |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c |
| index dd770e968548..499e671bc2cc 100644 |
| --- a/sound/pci/hda/patch_hdmi.c |
| +++ b/sound/pci/hda/patch_hdmi.c |
| @@ -1801,33 +1801,43 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) |
| |
| static int hdmi_parse_codec(struct hda_codec *codec) |
| { |
| - hda_nid_t nid; |
| + hda_nid_t start_nid; |
| + unsigned int caps; |
| int i, nodes; |
| |
| - nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &nid); |
| - if (!nid || nodes < 0) { |
| + nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid); |
| + if (!start_nid || nodes < 0) { |
| codec_warn(codec, "HDMI: failed to get afg sub nodes\n"); |
| return -EINVAL; |
| } |
| |
| - for (i = 0; i < nodes; i++, nid++) { |
| - unsigned int caps; |
| - unsigned int type; |
| + /* |
| + * hdmi_add_pin() assumes total amount of converters to |
| + * be known, so first discover all converters |
| + */ |
| + for (i = 0; i < nodes; i++) { |
| + hda_nid_t nid = start_nid + i; |
| |
| caps = get_wcaps(codec, nid); |
| - type = get_wcaps_type(caps); |
| |
| if (!(caps & AC_WCAP_DIGITAL)) |
| continue; |
| |
| - switch (type) { |
| - case AC_WID_AUD_OUT: |
| + if (get_wcaps_type(caps) == AC_WID_AUD_OUT) |
| hdmi_add_cvt(codec, nid); |
| - break; |
| - case AC_WID_PIN: |
| + } |
| + |
| + /* discover audio pins */ |
| + for (i = 0; i < nodes; i++) { |
| + hda_nid_t nid = start_nid + i; |
| + |
| + caps = get_wcaps(codec, nid); |
| + |
| + if (!(caps & AC_WCAP_DIGITAL)) |
| + continue; |
| + |
| + if (get_wcaps_type(caps) == AC_WID_PIN) |
| hdmi_add_pin(codec, nid); |
| - break; |
| - } |
| } |
| |
| return 0; |
| -- |
| 2.27.0 |
| |