.../drm/bridge/cadence/cdns-mhdp8546-core.c | 136 +++++++++++++++++- .../drm/bridge/cadence/cdns-mhdp8546-core.h | 4 + 2 files changed, 139 insertions(+), 1 deletion(-)
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@ti.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
Hi Abhash,
kernel test robot noticed the following build warnings:
[auto build test WARNING on drm-misc/drm-misc-next]
[also build test WARNING on linus/master v6.19-rc7 next-20260129]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Abhash-Kumar-Jha/drm-bridge-cdns-mhdp8546-Add-suspend-resume-support-to-the-bridge-driver/20260129-193145
base: https://gitlab.freedesktop.org/drm/misc/kernel.git drm-misc-next
patch link: https://lore.kernel.org/r/20260129112016.2448037-1-a-kumar2%40ti.com
patch subject: [PATCH] drm/bridge: cdns-mhdp8546: Add suspend resume support to the bridge driver
config: parisc-randconfig-002-20260130 (https://download.01.org/0day-ci/archive/20260130/202601301031.VutflAtp-lkp@intel.com/config)
compiler: hppa-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260130/202601301031.VutflAtp-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601301031.VutflAtp-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c:2447:12: warning: 'cdns_mhdp_suspend' defined but not used [-Wunused-function]
static int cdns_mhdp_suspend(struct device *dev)
^~~~~~~~~~~~~~~~~
>> drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c:2387:12: warning: 'cdns_mhdp_resume' defined but not used [-Wunused-function]
static int cdns_mhdp_resume(struct device *dev)
^~~~~~~~~~~~~~~~
vim +/cdns_mhdp_suspend +2447 drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
2386
> 2387 static int cdns_mhdp_resume(struct device *dev)
2388 {
2389 struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
2390 unsigned long rate;
2391 int ret;
2392
2393 ret = clk_prepare_enable(mhdp->clk);
2394 if (ret)
2395 return ret;
2396
2397 rate = clk_get_rate(mhdp->clk);
2398 writel(rate % 1000000, mhdp->regs + CDNS_SW_CLK_L);
2399 writel(rate / 1000000, mhdp->regs + CDNS_SW_CLK_H);
2400 writel(~0, mhdp->regs + CDNS_APB_INT_MASK);
2401
2402 ret = phy_init(mhdp->phy);
2403 if (ret) {
2404 dev_err(mhdp->dev, "Failed to initialize PHY: %d\n", ret);
2405 goto disable_clk;
2406 }
2407 ret = phy_power_on(mhdp->phy);
2408 if (ret < 0) {
2409 dev_err(mhdp->dev, "Failed to power on PHY: %d\n", ret);
2410 goto error;
2411 }
2412
2413 if (mhdp->powered_off) {
2414 ret = cdns_mhdp_load_firmware(mhdp);
2415 if (ret)
2416 goto phy_off;
2417
2418 ret = wait_event_timeout(mhdp->fw_load_wq,
2419 mhdp->hw_state == MHDP_HW_READY,
2420 msecs_to_jiffies(1000));
2421 if (ret == 0) {
2422 dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n",
2423 __func__);
2424 ret = -ETIMEDOUT;
2425 goto phy_off;
2426 }
2427 } else {
2428 ret = cdns_mhdp_set_firmware_active(mhdp, true);
2429 if (ret) {
2430 dev_err(mhdp->dev, "Failed to activate firmware (%pe)\n", ERR_PTR(ret));
2431 goto phy_off;
2432 }
2433 }
2434
2435 return 0;
2436
2437 phy_off:
2438 phy_power_off(mhdp->phy);
2439 error:
2440 phy_exit(mhdp->phy);
2441 disable_clk:
2442 clk_disable_unprepare(mhdp->clk);
2443
2444 return ret;
2445 }
2446
> 2447 static int cdns_mhdp_suspend(struct device *dev)
2448 {
2449 struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
2450 unsigned long timeout = msecs_to_jiffies(100);
2451 int ret = 0;
2452
2453 cancel_work_sync(&mhdp->hpd_work);
2454 ret = wait_event_timeout(mhdp->fw_load_wq,
2455 mhdp->hw_state == MHDP_HW_READY,
2456 timeout);
2457
2458 spin_lock(&mhdp->start_lock);
2459 if (mhdp->hw_state != MHDP_HW_READY) {
2460 spin_unlock(&mhdp->start_lock);
2461 return -EINVAL;
2462 }
2463 mhdp->hw_state = MHDP_HW_STOPPED;
2464 spin_unlock(&mhdp->start_lock);
2465
2466 if (ret == 0) {
2467 dev_err(mhdp->dev, "%s: Timeout waiting for fw loading\n", __func__);
2468 ret = -ETIMEDOUT;
2469 goto error;
2470 } else {
2471 ret = cdns_mhdp_set_firmware_active(mhdp, false);
2472 if (ret) {
2473 dev_err(mhdp->dev, "Failed to stop firmware (%pe)\n", ERR_PTR(ret));
2474 goto error;
2475 }
2476 }
2477
2478 phy_power_off(mhdp->phy);
2479 phy_exit(mhdp->phy);
2480 clk_disable_unprepare(mhdp->clk);
2481
2482 error:
2483 return ret;
2484 }
2485
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.