summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoao Paulo Goncalves <joao.goncalves@toradex.com>2024-02-19 17:31:21 -0300
committerFrancesco Dolcini <francesco.dolcini@toradex.com>2024-02-23 14:00:06 +0000
commite63f274c8b6ecab8d94cfc53aa58744ffac97f8c (patch)
tree8da5e478ab13412a67b366ea1487c78a35a24973
parent02c0357802380cbd760432bca2b2ed54151bb7db (diff)
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 <joao.goncalves@toradex.com>
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mp.dtsi1
-rw-r--r--drivers/gpu/drm/imx/dw_hdmi-imx.c11
-rw-r--r--drivers/gpu/imx/lcdifv3/lcdifv3-common.c13
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;
}