Merge tag 'asoc-v5.3' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[linux-block.git] / sound / soc / codecs / hdac_hdmi.c
index 6302ad5b712807dffe305402956ebfaa521e02ad..29918954e74078bd8a14ffbe63017a99ae9e38b6 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  *  hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
  *
@@ -6,15 +7,6 @@
  *         Subhransu S. Prusty <subhransu.s.prusty@intel.com>
  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  General Public License for more details.
- *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 #include <linux/init.h>
@@ -546,6 +538,29 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt(
        return NULL;
 }
 
+/*
+ * Go through all converters and ensure connection is set to
+ * the correct pin as set via kcontrols.
+ */
+static void hdac_hdmi_verify_connect_sel_all_pins(struct hdac_device *hdev)
+{
+       struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
+       struct hdac_hdmi_port *port;
+       struct hdac_hdmi_cvt *cvt;
+       int cvt_idx = 0;
+
+       list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+               port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt);
+               if (port && port->pin) {
+                       snd_hdac_codec_write(hdev, port->pin->nid, 0,
+                                            AC_VERB_SET_CONNECT_SEL, cvt_idx);
+                       dev_dbg(&hdev->dev, "%s: %s set connect %d -> %d\n",
+                               __func__, cvt->name, port->pin->nid, cvt_idx);
+               }
+               ++cvt_idx;
+       }
+}
+
 /*
  * This tries to get a valid pin and set the HW constraints based on the
  * ELD. Even if a valid pin is not found return success so that device open
@@ -806,6 +821,14 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w,
                                AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag);
                snd_hdac_codec_write(hdev, cvt->nid, 0,
                                AC_VERB_SET_STREAM_FORMAT, pcm->format);
+
+               /*
+                * The connection indices are shared by all converters and
+                * may interfere with each other. Ensure correct
+                * routing for all converters at stream start.
+                */
+               hdac_hdmi_verify_connect_sel_all_pins(hdev);
+
                break;
 
        case SND_SOC_DAPM_POST_PMD:
@@ -1867,6 +1890,12 @@ static void hdmi_codec_remove(struct snd_soc_component *component)
 {
        struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
        struct hdac_device *hdev = hdmi->hdev;
+       int ret;
+
+       ret = snd_hdac_acomp_register_notifier(hdev->bus, NULL);
+       if (ret < 0)
+               dev_err(&hdev->dev, "notifier unregister failed: err: %d\n",
+                               ret);
 
        pm_runtime_disable(&hdev->dev);
 }
@@ -2090,6 +2119,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
                return -EIO;
        }
 
+       snd_hdac_codec_link_down(hdev);
        snd_hdac_ext_bus_link_put(bus, hlink);
 
        snd_hdac_display_power(bus, hdev->addr, false);
@@ -2116,6 +2146,7 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
        }
 
        snd_hdac_ext_bus_link_get(bus, hlink);
+       snd_hdac_codec_link_up(hdev);
 
        snd_hdac_display_power(bus, hdev->addr, true);