<html>
  <head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <br>
    <br>
    <div class="moz-cite-prefix">On 08/09/2015 12:04 AM, Russell King
      wrote:<br>
    </div>
    <blockquote cite="mid:E1ZO6bT-0002vk-3d@rmk-PC.arm.linux.org.uk"
      type="cite">
      <pre wrap="">The dw_hdmi enable/disable handling is particularly weak in several
regards:
* The hotplug interrupt could call hdmi_poweron() or hdmi_poweroff()
  while DRM is setting a mode, which could race with a mode being set.
* Hotplug will always re-enable the phy whenever it detects an active
  hotplug signal, even if DRM has disabled the output.

Resolve all of these by introducing a mutex to prevent races, and a
state-tracking bool so we know whether DRM wishes the output to be
enabled.  We choose to use our own mutex rather than ->struct_mutex
so that we can still process interrupts in a timely fashion.

Signed-off-by: Russell King <a class="moz-txt-link-rfc2396E" href="mailto:rmk+kernel@arm.linux.org.uk"><rmk+kernel@arm.linux.org.uk></a>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 7b8a4e942a71..0ee188930d26 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -125,6 +125,9 @@ struct dw_hdmi {
        bool sink_is_hdmi;
        bool sink_has_audio;
 
+       struct mutex mutex;             /* for state below and previous_mode */
+       bool disabled;                  /* DRM has disabled our bridge */
+
        spinlock_t audio_lock;
        struct mutex audio_mutex;
        unsigned int sample_rate;
@@ -1389,8 +1392,12 @@ static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 {
        struct dw_hdmi *hdmi = bridge->driver_private;
 
+       mutex_lock(&hdmi->mutex);
+
        /* Store the display mode for plugin/DKMS poweron events */
        memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+
+       mutex_unlock(&hdmi->mutex);
 }
 
 static bool dw_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
@@ -1404,14 +1411,20 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 {
        struct dw_hdmi *hdmi = bridge->driver_private;
 
+       mutex_lock(&hdmi->mutex);
+       hdmi->disabled = true;
        dw_hdmi_poweroff(hdmi);
+       mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 {
        struct dw_hdmi *hdmi = bridge->driver_private;
 
+       mutex_lock(&hdmi->mutex);
        dw_hdmi_poweron(hdmi);
+       hdmi->disabled = false;
+       mutex_unlock(&hdmi->mutex);
 }
 
 static void dw_hdmi_bridge_nop(struct drm_bridge *bridge)
@@ -1534,20 +1547,20 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
        phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
 
        if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+               hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
+               mutex_lock(&hdmi->mutex);
                if (phy_int_pol & HDMI_PHY_HPD) {
                        dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-                       hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
-
-                       dw_hdmi_poweron(hdmi);
+                       if (!hdmi->disabled)
+                               dw_hdmi_poweron(hdmi);
                } else {
                        dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-                       hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
-                                 HDMI_PHY_POL0);
-
-                       dw_hdmi_poweroff(hdmi);
+                       if (!hdmi->disabled)
+                               dw_hdmi_poweroff(hdmi);</pre>
    </blockquote>
    <br>
    Just like my reply on
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    08/12, I thought this could be removed, so poweron/poweroff<br>
    would only be called with bridge->enable/bridge->disable, them
    maybe no need<br>
    mutex here.<br>
    <br>
    Best regards,<br>
    - Yakir<br>
    <blockquote cite="mid:E1ZO6bT-0002vk-3d@rmk-PC.arm.linux.org.uk"
      type="cite">
      <pre wrap="">
                }
+               mutex_unlock(&hdmi->mutex);
                drm_helper_hpd_irq_event(hdmi->bridge->dev);
        }
 
@@ -1617,7 +1630,9 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
        hdmi->sample_rate = 48000;
        hdmi->ratio = 100;
        hdmi->encoder = encoder;
+       hdmi->disabled = true;
 
+       mutex_init(&hdmi->mutex);
        mutex_init(&hdmi->audio_mutex);
        spin_lock_init(&hdmi->audio_lock);
 
</pre>
    </blockquote>
    <br>
  </body>
</html>