blob: 7a060438264fc2eb2270d88bc31cc7738b1fc541 [file] [log] [blame]
From 50fafc38c436b7d77cc2a0beba931f14f1347b7f Mon Sep 17 00:00:00 2001
From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Date: Mon, 6 Mar 2017 01:35:39 +0200
Subject: [PATCH 228/286] drm: bridge: dw-hdmi: Fix the PHY power down sequence
The PHY requires us to wait for the PHY to switch to low power mode
after deasserting TXPWRON and before asserting PDDQ in the power down
sequence, otherwise power down will fail.
The PHY power down can be monitored though the TX_READY bit, available
through I2C in the PHY registers, or the TX_PHY_LOCK bit, available
through the HDMI TX registers. As the two are equivalent, let's pick the
easier solution of polling the TX_PHY_LOCK bit.
The power down code is currently duplicated in multiple places. To avoid
spreading multiple calls to a TX_PHY_LOCK poll function, we have to
refactor the power down code and group it all in a single function.
Tests showed that one poll iteration was enough for TX_PHY_LOCK to
become low, without requiring any additional delay. Retrying the read
five times with a 1ms to 2ms delay between each attempt should thus be
more than enough.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Tested-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Jose Abreu <joabreu@synopsys.com>
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Link: http://patchwork.freedesktop.org/patch/msgid/20170305233539.11898-1-laurent.pinchart+renesas@ideasonboard.com
(cherry picked from commit b0e583e5b6b90eed40456c394410c154a5160814)
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
---
drivers/gpu/drm/bridge/dw-hdmi.c | 52 ++++++++++++++++++++++++++++++++-------
1 file changed, 43 insertions(+), 9 deletions(-)
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -116,6 +116,7 @@ struct dw_hdmi_i2c {
struct dw_hdmi_phy_data {
enum dw_hdmi_phy_type type;
const char *name;
+ unsigned int gen;
bool has_svsret;
};
@@ -914,6 +915,40 @@ static void dw_hdmi_phy_sel_interface_co
HDMI_PHY_CONF0_SELDIPIF_MASK);
}
+static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
+{
+ const struct dw_hdmi_phy_data *phy = hdmi->phy;
+ unsigned int i;
+ u16 val;
+
+ if (phy->gen == 1) {
+ dw_hdmi_phy_enable_tmds(hdmi, 0);
+ dw_hdmi_phy_enable_powerdown(hdmi, true);
+ return;
+ }
+
+ dw_hdmi_phy_gen2_txpwron(hdmi, 0);
+
+ /*
+ * Wait for TX_PHY_LOCK to be deasserted to indicate that the PHY went
+ * to low power mode.
+ */
+ for (i = 0; i < 5; ++i) {
+ val = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+ if (!(val & HDMI_PHY_TX_PHY_LOCK))
+ break;
+
+ usleep_range(1000, 2000);
+ }
+
+ if (val & HDMI_PHY_TX_PHY_LOCK)
+ dev_warn(hdmi->dev, "PHY failed to power down\n");
+ else
+ dev_dbg(hdmi->dev, "PHY powered down in %u iterations\n", i);
+
+ dw_hdmi_phy_gen2_pddq(hdmi, 1);
+}
+
static int hdmi_phy_configure(struct dw_hdmi *hdmi)
{
u8 val, msec;
@@ -946,11 +981,7 @@ static int hdmi_phy_configure(struct dw_
return -EINVAL;
}
- /* gen2 tx power off */
- dw_hdmi_phy_gen2_txpwron(hdmi, 0);
-
- /* gen2 pddq */
- dw_hdmi_phy_gen2_pddq(hdmi, 1);
+ dw_hdmi_phy_power_off(hdmi);
/* Leave low power consumption mode by asserting SVSRET. */
if (hdmi->phy->has_svsret)
@@ -1025,8 +1056,6 @@ static int dw_hdmi_phy_init(struct dw_hd
for (i = 0; i < 2; i++) {
dw_hdmi_phy_sel_data_en_pol(hdmi, 1);
dw_hdmi_phy_sel_interface_control(hdmi, 0);
- dw_hdmi_phy_enable_tmds(hdmi, 0);
- dw_hdmi_phy_enable_powerdown(hdmi, true);
ret = hdmi_phy_configure(hdmi);
if (ret)
@@ -1256,8 +1285,7 @@ static void dw_hdmi_phy_disable(struct d
if (!hdmi->phy_enabled)
return;
- dw_hdmi_phy_enable_tmds(hdmi, 0);
- dw_hdmi_phy_enable_powerdown(hdmi, true);
+ dw_hdmi_phy_power_off(hdmi);
hdmi->phy_enabled = false;
}
@@ -1827,23 +1855,29 @@ static const struct dw_hdmi_phy_data dw_
{
.type = DW_HDMI_PHY_DWC_HDMI_TX_PHY,
.name = "DWC HDMI TX PHY",
+ .gen = 1,
}, {
.type = DW_HDMI_PHY_DWC_MHL_PHY_HEAC,
.name = "DWC MHL PHY + HEAC PHY",
+ .gen = 2,
.has_svsret = true,
}, {
.type = DW_HDMI_PHY_DWC_MHL_PHY,
.name = "DWC MHL PHY",
+ .gen = 2,
.has_svsret = true,
}, {
.type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC,
.name = "DWC HDMI 3D TX PHY + HEAC PHY",
+ .gen = 2,
}, {
.type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY,
.name = "DWC HDMI 3D TX PHY",
+ .gen = 2,
}, {
.type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY,
.name = "DWC HDMI 2.0 TX PHY",
+ .gen = 2,
.has_svsret = true,
}
};