From e63f274c8b6ecab8d94cfc53aa58744ffac97f8c Mon Sep 17 00:00:00 2001 From: Joao Paulo Goncalves Date: Mon, 19 Feb 2024 17:31:21 -0300 Subject: gpu: imx: lcdifv3: power down imx8mp hdmi PHY The lcdifv3 video controller has a clock dependency on the HDMI PHY for the imx8mp. Powering down the HDMI PHY before the drm display pipeline finishes after a hot-plug monitor disconnection event causes a vblank timeout error due to the lcdif clock stopping. Likely because of this, the downstream code doesn't power down the HDMI PHY (it was commented), leaving the PHY ON even on a disconnection event, resulting in the HDMI data and clock signals still running. This appears to cause issues when using HDMI with bridge devices (e.g. HDMI-->eDP) to handle HPD (Hot-Plug Detect) events from the bridge. To address the problem, the workaround moves the PHY power-off logic to the suspend of the lcdifv3 driver. At this position, it guarantees that the DRM pipeline has finished and disabled and the PHY can be powered off without problems. Doing this appears to be safe as the PHY is powered on again on imx8mp_hdmi_phy_init() before starting the DRM pipeline. The PHY is controlled by the HDMI TX BLK CTRL peripheral, but the downstream driver for the peripheral is not fully implemented. So, the workaround maps HDMI TX BLK CTRL register space to the lcdifv3 driver and controls it directly, similar to the approach used in the imx HDMI driver. Upstream-Status: Inappropriate [other] The imx8mp already has a fully working implementation on upstream [1] and [2]. Nonetheless, attempting to backport it appears impractical, leading to the adoption of this workaround. [1] https://lore.kernel.org/lkml/20240210204606.11944-1-aford173@gmail.com/ [2] https://lore.kernel.org/lkml/20240203165307.7806-1-aford173@gmail.com/ Signed-off-by: Joao Paulo Goncalves --- arch/arm64/boot/dts/freescale/imx8mp.dtsi | 1 + drivers/gpu/drm/imx/dw_hdmi-imx.c | 11 ----------- drivers/gpu/imx/lcdifv3/lcdifv3-common.c | 13 ++++++++++++- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi index 9e0880992566..863aaad5e667 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi @@ -1968,6 +1968,7 @@ interrupts = <8 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&irqsteer_hdmi>; resets = <&hdmi_blk_ctrl IMX8MP_HDMI_BLK_CTRL_LCDIF_RESET>; + fsl,gpr = <&hdmi_blk_ctrl>; power-domains = <&hdmimix_pd>; status = "disabled"; diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index 5b3c58c7f289..283a9675901f 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -285,7 +285,6 @@ static int imx8mp_hdmi_phy_init(struct dw_hdmi *dw_hdmi, void *data, static void imx8mp_hdmi_phy_disable(struct dw_hdmi *dw_hdmi, void *data) { struct imx_hdmi *hdmi = (struct imx_hdmi *)data; - int val; dev_dbg(hdmi->dev, "%s\n", __func__); if (!hdmi->phy) @@ -294,16 +293,6 @@ static void imx8mp_hdmi_phy_disable(struct dw_hdmi *dw_hdmi, void *data) /* disable PVI */ imx8mp_hdmi_pvi_disable(); imx8mp_hdmi_pavi_powerdown(); - - /* TODO */ - regmap_read(hdmi->regmap, 0x200, &val); - /* Disable CEC */ - val &= ~0x2; - /* Power down HDMI PHY - * TODO move PHY power off to hdmi phy driver - * val |= 0x8; - * regmap_write(hdmi->regmap, 0x200, val); - */ } static int imx8mp_hdmimix_setup(struct imx_hdmi *hdmi) diff --git a/drivers/gpu/imx/lcdifv3/lcdifv3-common.c b/drivers/gpu/imx/lcdifv3/lcdifv3-common.c index 8dab74c3fdad..c61ad6bbc192 100644 --- a/drivers/gpu/imx/lcdifv3/lcdifv3-common.c +++ b/drivers/gpu/imx/lcdifv3/lcdifv3-common.c @@ -734,7 +734,8 @@ static int imx_lcdifv3_probe(struct platform_device *pdev) if (IS_ERR(lcdifv3->base)) return PTR_ERR(lcdifv3->base); - if (of_device_is_compatible(np, "fsl,imx93-lcdif")) { + if (of_device_is_compatible(np, "fsl,imx93-lcdif") || + of_device_is_compatible(np, "fsl,imx8mp-lcdif3")) { lcdifv3->gpr = syscon_regmap_lookup_by_phandle(np, "fsl,gpr"); if (IS_ERR(lcdifv3->gpr)) { ret = PTR_ERR(lcdifv3->gpr); @@ -797,6 +798,7 @@ static int imx_lcdifv3_remove(struct platform_device *pdev) static int imx_lcdifv3_runtime_suspend(struct device *dev) { struct lcdifv3_soc *lcdifv3 = dev_get_drvdata(dev); + int val; if (atomic_inc_return(&lcdifv3->rpm_suspended) > 1) return 0; @@ -809,6 +811,15 @@ static int imx_lcdifv3_runtime_suspend(struct device *dev) if (of_device_is_compatible(dev->of_node, "fsl,imx93-lcdif")) regmap_write(lcdifv3->gpr, 0xc, 0x0); + if (of_device_is_compatible(dev->of_node, "fsl,imx8mp-lcdif3")) { + regmap_read(lcdifv3->gpr, 0x200, &val); + /* Disable CEC */ + val &= ~0x2; + /* Power Down HDMI PHY */ + val |= 0x8; + regmap_write(lcdifv3->gpr, 0x200, val); + } + return 0; } -- cgit v1.2.3