[PATCH] drm/bridge: cdns-mhdp8546: Add suspend resume support to the bridge driver

Abhash Kumar Jha posted 1 patch 1 week, 2 days ago
There is a newer version of this series
.../drm/bridge/cadence/cdns-mhdp8546-core.c   | 136 +++++++++++++++++-
.../drm/bridge/cadence/cdns-mhdp8546-core.h   |   4 +
2 files changed, 139 insertions(+), 1 deletion(-)
[PATCH] drm/bridge: cdns-mhdp8546: Add suspend resume support to the bridge driver
Posted by Abhash Kumar Jha 1 week, 2 days ago
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
Re: [PATCH] drm/bridge: cdns-mhdp8546: Add suspend resume support to the bridge driver
Posted by kernel test robot 1 week, 1 day ago
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