From patchwork Fri Mar 3 17:20:01 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 9603323 X-Patchwork-Delegate: geert@linux-m68k.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id BB0C260414 for ; Fri, 3 Mar 2017 17:29:24 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AD8C9285C7 for ; Fri, 3 Mar 2017 17:29:24 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A26B2285CE; Fri, 3 Mar 2017 17:29:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 23EDD285CB for ; Fri, 3 Mar 2017 17:29:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752265AbdCCR3T (ORCPT ); Fri, 3 Mar 2017 12:29:19 -0500 Received: from galahad.ideasonboard.com ([185.26.127.97]:40934 "EHLO galahad.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752261AbdCCR3S (ORCPT ); Fri, 3 Mar 2017 12:29:18 -0500 Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by galahad.ideasonboard.com (Postfix) with ESMTPSA id 9B40A204C1; Fri, 3 Mar 2017 18:18:40 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1488561521; bh=kSGMCDOqooLlCO2f6VENHYPp4QSgs6xsYBEIHgX5Nlo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=N2dJuj6O7RA5lUGzRCySaaBv34v+KI4mNBUeNDzuKP3a4BIX8fhjFYOXne/QcUEzl iaxSVll5Myy1fVLKMWkXIOVQsgOfn3r5NYDKTZEtK1gRGDI7kqvLdN5RYwLHd4okIO 8KyCbN30m2DVvTOW9iOUcRLlk+/wED3fqmsUWUdQ= From: Laurent Pinchart To: dri-devel@lists.freedesktop.org Cc: Andy Yan , Archit Taneja , Fabio Estevam , Jose Abreu , Kieran Bingham , Neil Armstrong , Nickey Yang , Russell King , Vladimir Zapolskiy , linux-renesas-soc@vger.kernel.org Subject: [PATCH v5 04/10] drm: bridge: dw-hdmi: Fix the PHY power down sequence Date: Fri, 3 Mar 2017 19:20:01 +0200 Message-Id: <20170303172007.26541-5-laurent.pinchart+renesas@ideasonboard.com> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20170303172007.26541-1-laurent.pinchart+renesas@ideasonboard.com> References: <20170303172007.26541-1-laurent.pinchart+renesas@ideasonboard.com> Sender: linux-renesas-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Tested-by: Neil Armstrong Reviewed-by: Jose Abreu --- drivers/gpu/drm/bridge/dw-hdmi.c | 52 +++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index d863b3393aee..85348ba6bb1c 100644 --- 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_control(struct dw_hdmi *hdmi, u8 enable) 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.data; + 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_hdmi *hdmi) 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_hdmi *hdmi) 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 dw_hdmi *hdmi) 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_hdmi_phys[] = { { .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, } };