linux-rockchip.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: Russell King <rmk+kernel@arm.linux.org.uk>
To: linux-rockchip@lists.infradead.org,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org
Cc: Philipp Zabel <p.zabel@pengutronix.de>,
	Andy Yan <andy.yan@rock-chips.com>,
	Yakir Yang <ykk@rock-chips.com>,
	Fabio Estevam <fabio.estevam@freescale.com>,
	Sascha Hauer <s.hauer@pengutronix.de>,
	Jon Nettleton <jon.nettleton@gmail.com>,
	David Airlie <airlied@linux.ie>
Subject: [PATCH 12/12] drm: bridge/dw_hdmi: improve HDMI enable/disable handling
Date: Sat, 08 Aug 2015 17:04:21 +0100	[thread overview]
Message-ID: <E1ZO6bd-0002vx-DL@rmk-PC.arm.linux.org.uk> (raw)
In-Reply-To: <20150808160251.GM7557@n2100.arm.linux.org.uk>

HDMI sinks are permitted to de-assert and re-assert the HPD signal to
indicate that their EDID has been updated, which may not involve a
change of video information.

An example of where such a situation can arise is when an AV receiver
is connected between the source and the display device.  Events which
can cause the HPD to be deasserted include:

 * turning on or switching to standby the AV receiver.
 * turning on or switching to standby the display device.

Each of these can change the entire EDID data, or just a part of the
EDID data - it's up to the connected HDMI sink to do what they desire
here.  For example

 - with the AV receiver and display device both in standby, a source
   connected to the AV receiver may provide its own EDID to the source.
 - turning on the display device causes the display device's EDID to be
   made available in an unmodified form to the source.
 - subsequently turning on the AV receiver then provides a modified
   version of the display device's EDID.

Moreover, HPD doesn't tell us whether something is actually listening
on the HDMI TDMS signals.  The phy gives us a set of RXSENSE indications
which tell us whether there is a sink connected to the TMDS signals.

Currently, we use the HPD signal to enable or disable the HDMI block,
which is questionable when HPD is used in this manner.  Using the
RXSENSE would be more appropriate, but there is some bad behaviour
which needs to be coped with.  The iMX6 implementation lets the TMDS
signals float when the phy is "powered down", which cause spurious
interrupts.  Rather than just using RXSENSE, use RXSENSE and HPD
becoming both active to signal the presence of a device, but loss
of RXSENSE to indicate that the device has been unplugged.

The side effect of this change is that a sink deasserting the HPD
signal to cause a re-read of the EDID data will not cause the bridge
to immediately disable the video signal.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/gpu/drm/bridge/dw_hdmi.c | 124 ++++++++++++++++++++++++++++++++-------
 1 file changed, 102 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw_hdmi.c b/drivers/gpu/drm/bridge/dw_hdmi.c
index 2d1c7e4ec086..fba25607ef88 100644
--- a/drivers/gpu/drm/bridge/dw_hdmi.c
+++ b/drivers/gpu/drm/bridge/dw_hdmi.c
@@ -129,6 +129,8 @@ struct dw_hdmi {
 	enum drm_connector_force force;	/* mutex-protected force state */
 	bool disabled;			/* DRM has disabled our bridge */
 	bool bridge_is_on;		/* indicates the bridge is on */
+	bool rxsense;			/* rxsense state */
+	u8 phy_mask;			/* desired phy int mask settings */
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
@@ -142,6 +144,14 @@ struct dw_hdmi {
 	u8 (*read)(struct dw_hdmi *hdmi, int offset);
 };
 
+#define HDMI_IH_PHY_STAT0_RX_SENSE \
+	(HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \
+	 HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3)
+
+#define HDMI_PHY_RX_SENSE \
+	(HDMI_PHY_RX_SENSE0 | HDMI_PHY_RX_SENSE1 | \
+	 HDMI_PHY_RX_SENSE2 | HDMI_PHY_RX_SENSE3)
+
 static void dw_hdmi_writel(struct dw_hdmi *hdmi, u8 val, int offset)
 {
 	writel(val, hdmi->regs + (offset << 2));
@@ -1318,10 +1328,11 @@ static int dw_hdmi_fb_registered(struct dw_hdmi *hdmi)
 		    HDMI_PHY_I2CM_CTLINT_ADDR);
 
 	/* enable cable hot plug irq */
-	hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+	hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+		    HDMI_IH_PHY_STAT0);
 
 	return 0;
 }
@@ -1397,7 +1408,7 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
 	if (hdmi->disabled) {
 		force = DRM_FORCE_OFF;
 	} else if (force == DRM_FORCE_UNSPECIFIED) {
-		if (hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD)
+		if (hdmi->rxsense)
 			force = DRM_FORCE_ON;
 		else
 			force = DRM_FORCE_OFF;
@@ -1412,6 +1423,31 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
 	}
 }
 
+/*
+ * Adjust the detection of RXSENSE according to whether we have a forced
+ * connection mode enabled, or whether we have been disabled.  There is
+ * no point processing RXSENSE interrupts if we have a forced connection
+ * state, or DRM has us disabled.
+ *
+ * We also disable rxsense interrupts when we think we're disconnected
+ * to avoid floating TDMS signals giving false rxsense interrupts.
+ *
+ * Note: we still need to listen for HPD interrupts even when DRM has us
+ * disabled so that we can detect a connect event.
+ */
+static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi)
+{
+	u8 old_mask = hdmi->phy_mask;
+
+	if (hdmi->force || hdmi->disabled || !hdmi->rxsense)
+		hdmi->phy_mask |= HDMI_PHY_RX_SENSE;
+	else
+		hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE;
+
+	if (old_mask != hdmi->phy_mask)
+		hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+}
+
 static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
 				    struct drm_display_mode *orig_mode,
 				    struct drm_display_mode *mode)
@@ -1440,6 +1476,7 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = true;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1450,6 +1487,7 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = false;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1467,6 +1505,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
 	mutex_lock(&hdmi->mutex);
 	hdmi->force = DRM_FORCE_UNSPECIFIED;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 
 	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
@@ -1541,6 +1580,7 @@ static void dw_hdmi_connector_force(struct drm_connector *connector)
 	mutex_lock(&hdmi->mutex);
 	hdmi->force = connector->force;
 	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -1582,33 +1622,69 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
 static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 {
 	struct dw_hdmi *hdmi = dev_id;
-	u8 intr_stat;
-	u8 phy_int_pol;
+	u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat;
 
 	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
-
 	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
+	phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+
+	phy_pol_mask = 0;
+	if (intr_stat & HDMI_IH_PHY_STAT0_HPD)
+		phy_pol_mask |= HDMI_PHY_HPD;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE0)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE0;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE1)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE1;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE2)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE2;
+	if (intr_stat & HDMI_IH_PHY_STAT0_RX_SENSE3)
+		phy_pol_mask |= HDMI_PHY_RX_SENSE3;
+
+	if (phy_pol_mask)
+		hdmi_modb(hdmi, ~phy_int_pol, phy_pol_mask, HDMI_PHY_POL0);
 
-	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
-		hdmi_modb(hdmi, ~phy_int_pol, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	/*
+	 * RX sense tells us whether the TDMS transmitters are detecting
+	 * load - in other words, there's something listening on the
+	 * other end of the link.  Use this to decide whether we should
+	 * power on the phy as HPD may be toggled by the sink to merely
+	 * ask the source to re-read the EDID.
+	 */
+	if (intr_stat &
+	    (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) {
 		mutex_lock(&hdmi->mutex);
-		if (phy_int_pol & HDMI_PHY_HPD) {
-			dev_dbg(hdmi->dev, "EVENT=plugin\n");
-
-			if (!hdmi->disabled && !hdmi->force)
-				dw_hdmi_poweron(hdmi);
-		} else {
-			dev_dbg(hdmi->dev, "EVENT=plugout\n");
-
-			if (!hdmi->disabled && !hdmi->force)
-				dw_hdmi_poweroff(hdmi);
+		if (!hdmi->disabled && !hdmi->force) {
+			/*
+			 * If the RX sense status indicates we're disconnected,
+			 * clear the software rxsense status.
+			 */
+			if (!(phy_stat & HDMI_PHY_RX_SENSE))
+				hdmi->rxsense = false;
+
+			/*
+			 * Only set the software rxsense status when both
+			 * rxsense and hpd indicates we're connected.
+			 * This avoids what seems to be bad behaviour in
+			 * at least iMX6S versions of the phy.
+			 */
+			if (phy_stat & HDMI_PHY_HPD)
+				hdmi->rxsense = true;
+
+			dw_hdmi_update_power(hdmi);
+			dw_hdmi_update_phy_mask(hdmi);
 		}
 		mutex_unlock(&hdmi->mutex);
+	}
+
+	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
+		dev_dbg(hdmi->dev, "EVENT=%s\n",
+			phy_int_pol & HDMI_PHY_HPD ? "plugin" : "plugout");
 		drm_helper_hpd_irq_event(hdmi->bridge->dev);
 	}
 
 	hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+		    HDMI_IH_MUTE_PHY_STAT0);
 
 	return IRQ_HANDLED;
 }
@@ -1674,6 +1750,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	hdmi->ratio = 100;
 	hdmi->encoder = encoder;
 	hdmi->disabled = true;
+	hdmi->rxsense = true;
+	hdmi->phy_mask = (u8)~(HDMI_PHY_HPD | HDMI_PHY_RX_SENSE);
 
 	mutex_init(&hdmi->mutex);
 	mutex_init(&hdmi->audio_mutex);
@@ -1764,10 +1842,11 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 	 * Configure registers related to HDMI interrupt
 	 * generation before registering IRQ.
 	 */
-	hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+		    HDMI_IH_PHY_STAT0);
 
 	ret = dw_hdmi_fb_registered(hdmi);
 	if (ret)
@@ -1778,7 +1857,8 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
 		goto err_iahb;
 
 	/* Unmute interrupts */
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+		    HDMI_IH_MUTE_PHY_STAT0);
 
 	dev_set_drvdata(dev, hdmi);
 
-- 
2.1.0

  parent reply	other threads:[~2015-08-08 16:04 UTC|newest]

Thread overview: 82+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-08-08 16:02 [PATCH 00/12] dw-hdmi development Russell King - ARM Linux
2015-08-08 16:03 ` [PATCH 01/12] drm: bridge/dw_hdmi: remove pixel repetition setting for all VICs Russell King
2015-08-08 16:03 ` [PATCH 02/12] drm: bridge/dw_hdmi: don't support any pixel doubled modes Russell King
2015-08-08 16:03 ` [PATCH 03/12] gpu: imx: simplify sync polarity setting Russell King
2015-10-06 17:57   ` Fabio Estevam
2015-08-08 16:03 ` [PATCH 04/12] gpu: imx: fix support for interlaced modes Russell King
     [not found]   ` <E1ZO6ay-0002qh-6E-eh5Bv4kxaXIANfyc6IWni62ZND6+EDdj@public.gmane.org>
2015-08-27  8:39     ` Philipp Zabel
2015-08-27  8:54       ` Russell King - ARM Linux
2015-08-27  9:40         ` Philipp Zabel
2015-10-06 17:59   ` Fabio Estevam
2015-08-08 16:03 ` [PATCH 05/12] drm: bridge/dw_hdmi: add support for interlaced video modes Russell King
2015-10-06 18:00   ` Fabio Estevam
2015-08-08 16:03 ` [PATCH 06/12] drm: bridge/dw_hdmi: clean up HDMI vs DVI mode handling Russell King
2015-10-07  3:40   ` Yakir Yang
2015-08-08 16:03 ` [PATCH 07/12] drm: bridge/dw_hdmi: enable audio only if sink supports audio Russell King
2015-10-07  3:42   ` Yakir Yang
2015-08-08 16:04 ` [PATCH 08/12] drm: bridge/dw_hdmi: avoid enabling interface in mode_set Russell King
2015-10-07  3:50   ` Yakir Yang
2015-10-07  9:18     ` Russell King - ARM Linux
2015-10-07  9:35       ` Yakir Yang
2015-08-08 16:04 ` [PATCH 09/12] drm: bridge/dw_hdmi: rename dw_hdmi_phy_enable_power() Russell King
2015-10-07  4:00   ` Yakir Yang
     [not found] ` <20150808160251.GM7557-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
2015-08-08 16:04   ` [PATCH 10/12] drm: bridge/dw_hdmi: fix phy enable/disable handling Russell King
2015-10-07  4:05     ` Yakir Yang
2015-10-07  9:48       ` Russell King - ARM Linux
2015-10-07 10:40         ` Yakir Yang
2015-10-07 19:17           ` Russell King - ARM Linux
2015-10-08  8:35             ` Yakir Yang
2015-08-08 16:04 ` [PATCH 11/12] drm: bridge/dw_hdmi: add connector mode forcing Russell King
2015-10-06 18:01   ` Fabio Estevam
2015-08-08 16:04 ` Russell King [this message]
2015-10-06 18:03   ` [PATCH 12/12] drm: bridge/dw_hdmi: improve HDMI enable/disable handling Fabio Estevam
2015-08-08 16:09 ` [PATCH 0/9] dw-hdmi audio support Russell King - ARM Linux
2015-08-08 16:10   ` [PATCH 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver Russell King
2015-08-10 10:05     ` Takashi Iwai
2015-08-10 10:39       ` Russell King - ARM Linux
2015-08-10 12:23         ` Takashi Iwai
2015-08-10 16:49           ` Russell King - ARM Linux
2015-08-10 18:16             ` Mark Brown
2015-08-14 13:54       ` [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver David Airlie <airlied@linux.ie>, Sascha Hauer <s.hauer@pengutronix.de>, linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org, Jaroslav Kysela <perex@perex.cz>, linux-rockchip@lists.infradead.org, Mark Brown <broonie@kernel.org>, Philipp Zabel <p.zabel@pengutronix.de>, Yakir Yang <ykk@rock-chips.com>, Andy Yan <andy.yan@rock-chips.com>, Jon Nettleton <jon.nettleton@gmail.com>, linux-arm-kernel@lists.infradead.org Russell King
2015-08-14 14:04       ` [PATCH v2 1/9] drm: bridge/dw_hdmi-ahb-audio: add audio driver Russell King
2015-08-14 14:34         ` [alsa-devel] " Takashi Iwai
2015-10-06 18:07     ` [PATCH " Fabio Estevam
2015-10-06 18:18       ` Russell King - ARM Linux
2015-10-06 18:45         ` Fabio Estevam
2015-10-06 18:54           ` Russell King - ARM Linux
2015-10-06 20:25             ` Fabio Estevam
2015-10-09 16:00               ` Russell King - ARM Linux
2015-10-09 16:02                 ` Fabio Estevam
2015-10-09 16:11                   ` Russell King - ARM Linux
2015-08-08 16:10   ` [PATCH 2/9] drm: bridge/dw_hdmi-ahb-audio: parse ELD from HDMI driver Russell King
2015-08-08 16:10   ` [PATCH 3/9] drm: bridge/dw_hdmi-ahb-audio: basic support for multi-channel PCM audio Russell King
2015-08-08 16:10   ` [PATCH 4/9] drm: bridge/dw_hdmi-ahb-audio: allow larger buffer sizes Russell King
2015-08-08 16:10   ` [PATCH 5/9] drm: bridge/dw_hdmi: avoid being recursive in N calculation Russell King
2015-09-04 17:50     ` Doug Anderson
2015-08-08 16:10   ` [PATCH 6/9] drm: bridge/dw_hdmi: adjust pixel clock values " Russell King
2015-09-04 18:21     ` Doug Anderson
2015-09-04 19:48       ` Doug Anderson
2015-09-04 21:24         ` Russell King - ARM Linux
2015-09-04 23:50           ` Doug Anderson
2015-09-05  0:27             ` Russell King - ARM Linux
2015-09-05  2:03               ` Doug Anderson
2015-09-05  8:31                 ` Russell King - ARM Linux
2015-09-05 13:46                   ` Doug Anderson
2015-09-05 14:01                     ` Russell King - ARM Linux
2015-09-05 19:44                       ` Doug Anderson
2015-09-05  8:34                 ` Russell King - ARM Linux
2015-09-05 13:50                   ` Doug Anderson
2015-08-08 16:10   ` [PATCH 7/9] drm: bridge/dw_hdmi: remove ratio support from ACR code Russell King
2015-09-04 18:24     ` Doug Anderson
2015-08-08 16:10   ` [PATCH 8/9] drm: bridge/dw_hdmi: replace CTS calculation for the ACR Russell King
2015-09-04 20:00     ` Doug Anderson
2015-08-08 16:10   ` [PATCH 9/9] drm: bridge/dw_hdmi-i2s-audio: add audio driver Russell King
2015-08-10 15:48     ` Russell King - ARM Linux
2015-08-10 16:26       ` Yakir Yang
2015-08-27  8:42   ` [PATCH 0/9] dw-hdmi audio support Philipp Zabel
2016-01-05 15:40     ` [alsa-devel] " Jean-Michel Hautbois
2016-01-05 15:54       ` Fabio Estevam
2016-01-05 16:04       ` Russell King - ARM Linux
2016-01-07  8:21         ` Jean-Michel Hautbois
2015-08-10 12:21 ` [PATCH 00/12] dw-hdmi development Thierry Reding
2015-08-18 10:37   ` Russell King - ARM Linux

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=E1ZO6bd-0002vx-DL@rmk-PC.arm.linux.org.uk \
    --to=rmk+kernel@arm.linux.org.uk \
    --cc=airlied@linux.ie \
    --cc=andy.yan@rock-chips.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=fabio.estevam@freescale.com \
    --cc=jon.nettleton@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=p.zabel@pengutronix.de \
    --cc=s.hauer@pengutronix.de \
    --cc=ykk@rock-chips.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).