[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20260129112016.2448037-1-a-kumar2@ti.com>
Date: Thu, 29 Jan 2026 16:50:16 +0530
From: Abhash Kumar Jha <a-kumar2@...com>
To: <andrzej.hajda@...el.com>, <neil.armstrong@...aro.org>,
<rfoss@...nel.org>, <mripard@...nel.org>, <tzimmermann@...e.de>,
<airlied@...il.com>, <simona@...ll.ch>, <devarsht@...com>, <u-kumar1@...com>,
<sjakhade@...ence.com>
CC: <linux-kernel@...r.kernel.org>, <dri-devel@...ts.freedesktop.org>,
<Laurent.pinchart@...asonboard.com>, <jonas@...boo.se>,
<jernej.skrabec@...il.com>, <s-jain1@...com>, <p-mantena@...com>,
<tomi.valkeinen@...asonboard.com>
Subject: [PATCH] drm/bridge: cdns-mhdp8546: Add suspend resume support to the bridge driver
Add system suspend and resume hooks to the cdns-mhdp8546 bridge driver.
While resuming we either load the firmware or activate it. Firmware
is loaded only when resuming from a successful suspend-resume cycle.
If resuming due to an aborted suspend, loading the firmware is not
possible because the uCPU's IMEM is only accessible after a reset and the
bridge has not gone through a reset in this case. Hence, Activate the
firmware that is already loaded.
Use GENPD_NOTIFY_OFF genpd_notifier to get the power domain status of
the bridge and accordingly load the firmware.
Additionally, introduce phy_power_off/on to control the power to the phy.
Signed-off-by: Abhash Kumar Jha <a-kumar2@...com>
---
.../drm/bridge/cadence/cdns-mhdp8546-core.c | 136 +++++++++++++++++-
.../drm/bridge/cadence/cdns-mhdp8546-core.h | 4 +
2 files changed, 139 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
index 38726ae1bf150..dd482094bf184 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
@@ -32,6 +32,7 @@
#include <linux/phy/phy.h>
#include <linux/phy/phy-dp.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/wait.h>
@@ -2383,6 +2384,120 @@ static void cdns_mhdp_hpd_work(struct work_struct *work)
}
}
+static int cdns_mhdp_resume(struct device *dev)
+{
+ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
+ unsigned long rate;
+ int ret;
+
+ ret = clk_prepare_enable(mhdp->clk);
+ if (ret)
+ return ret;
+
+ rate = clk_get_rate(mhdp->clk);
+ writel(rate % 1000000, mhdp->regs + CDNS_SW_CLK_L);
+ writel(rate / 1000000, mhdp->regs + CDNS_SW_CLK_H);
+ writel(~0, mhdp->regs + CDNS_APB_INT_MASK);
+
+ ret = phy_init(mhdp->phy);
+ if (ret) {
+ dev_err(mhdp->dev, "Failed to initialize PHY: %d\n", ret);
+ goto disable_clk;
+ }
+ ret = phy_power_on(mhdp->phy);
+ if (ret < 0) {
+ dev_err(mhdp->dev, "Failed to power on PHY: %d\n", ret);
+ goto error;
+ }
+
+ if (mhdp->powered_off) {
+ ret = cdns_mhdp_load_firmware(mhdp);
+ if (ret)
+ goto phy_off;
+
+ ret = wait_event_timeout(mhdp->fw_load_wq,
+ mhdp->hw_state == MHDP_HW_READY,
+ msecs_to_jiffies(1000));
+ if (ret == 0) {
+ dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto phy_off;
+ }
+ } else {
+ ret = cdns_mhdp_set_firmware_active(mhdp, true);
+ if (ret) {
+ dev_err(mhdp->dev, "Failed to activate firmware (%pe)\n", ERR_PTR(ret));
+ goto phy_off;
+ }
+ }
+
+ return 0;
+
+phy_off:
+ phy_power_off(mhdp->phy);
+error:
+ phy_exit(mhdp->phy);
+disable_clk:
+ clk_disable_unprepare(mhdp->clk);
+
+ return ret;
+}
+
+static int cdns_mhdp_suspend(struct device *dev)
+{
+ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
+ unsigned long timeout = msecs_to_jiffies(100);
+ int ret = 0;
+
+ cancel_work_sync(&mhdp->hpd_work);
+ ret = wait_event_timeout(mhdp->fw_load_wq,
+ mhdp->hw_state == MHDP_HW_READY,
+ timeout);
+
+ spin_lock(&mhdp->start_lock);
+ if (mhdp->hw_state != MHDP_HW_READY) {
+ spin_unlock(&mhdp->start_lock);
+ return -EINVAL;
+ }
+ mhdp->hw_state = MHDP_HW_STOPPED;
+ spin_unlock(&mhdp->start_lock);
+
+ if (ret == 0) {
+ dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n", __func__);
+ ret = -ETIMEDOUT;
+ goto error;
+ } else {
+ ret = cdns_mhdp_set_firmware_active(mhdp, false);
+ if (ret) {
+ dev_err(mhdp->dev, "Failed to stop firmware (%pe)\n", ERR_PTR(ret));
+ goto error;
+ }
+ }
+
+ phy_power_off(mhdp->phy);
+ phy_exit(mhdp->phy);
+ clk_disable_unprepare(mhdp->clk);
+
+error:
+ return ret;
+}
+
+static int mhdp_pd_notifier_cb(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct cdns_mhdp_device *mhdp = container_of(nb, struct cdns_mhdp_device, pd_nb);
+
+ if (action == GENPD_NOTIFY_OFF)
+ mhdp->powered_off = true;
+
+ return 0;
+}
+
+static const struct dev_pm_ops cdns_mhdp_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cdns_mhdp_suspend, cdns_mhdp_resume)
+};
+
static int cdns_mhdp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -2494,6 +2609,11 @@ static int cdns_mhdp_probe(struct platform_device *pdev)
dev_err(mhdp->dev, "Failed to initialize PHY: %d\n", ret);
goto plat_fini;
}
+ ret = phy_power_on(mhdp->phy);
+ if (ret < 0) {
+ dev_err(mhdp->dev, "Failed to power on PHY: %d\n", ret);
+ goto phy_exit;
+ }
/* Initialize the work for modeset in case of link train failure */
INIT_WORK(&mhdp->modeset_retry_work, cdns_mhdp_modeset_retry_fn);
@@ -2504,21 +2624,33 @@ static int cdns_mhdp_probe(struct platform_device *pdev)
ret = cdns_mhdp_load_firmware(mhdp);
if (ret)
- goto phy_exit;
+ goto power_off;
if (mhdp->hdcp_supported)
cdns_mhdp_hdcp_init(mhdp);
drm_bridge_add(&mhdp->bridge);
+ mhdp->powered_off = false;
+ mhdp->pd_nb.notifier_call = mhdp_pd_notifier_cb;
+ ret = dev_pm_genpd_add_notifier(mhdp->dev, &mhdp->pd_nb);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to add power domain notifier\n");
+ dev_pm_genpd_remove_notifier(mhdp->dev);
+ goto power_off;
+ }
+
return 0;
+power_off:
+ phy_power_off(mhdp->phy);
phy_exit:
phy_exit(mhdp->phy);
plat_fini:
if (mhdp->info && mhdp->info->ops && mhdp->info->ops->exit)
mhdp->info->ops->exit(mhdp);
runtime_put:
+ mhdp->powered_off = true;
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
@@ -2550,6 +2682,7 @@ static void cdns_mhdp_remove(struct platform_device *pdev)
ERR_PTR(ret));
}
+ phy_power_off(mhdp->phy);
phy_exit(mhdp->phy);
if (mhdp->info && mhdp->info->ops && mhdp->info->ops->exit)
@@ -2581,6 +2714,7 @@ static struct platform_driver mhdp_driver = {
.driver = {
.name = "cdns-mhdp8546",
.of_match_table = mhdp_ids,
+ .pm = &cdns_mhdp_pm_ops,
},
.probe = cdns_mhdp_probe,
.remove = cdns_mhdp_remove,
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h
index bad2fc0c73066..b06dd5e44aafd 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.h
@@ -412,6 +412,10 @@ struct cdns_mhdp_device {
struct cdns_mhdp_hdcp hdcp;
bool hdcp_supported;
+
+ /* Power domain status notifier */
+ struct notifier_block pd_nb;
+ bool powered_off;
};
#define connector_to_mhdp(x) container_of(x, struct cdns_mhdp_device, connector)
--
2.34.1
Powered by blists - more mailing lists