Add runtime PM stubs to the Synopsys DesignWare DisplayPort bridge driver. Support is not enabled automatically and must be hooked up in the vendor specific glue code.
Signed-off-by: Sebastian Reichel <[email protected]> --- drivers/gpu/drm/bridge/synopsys/dw-dp.c | 107 +++++++++++++++++++++++++++++--- include/drm/bridge/dw_dp.h | 3 + 2 files changed, 102 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c index 2042bc00f906..1bbb39b8d0be 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c @@ -328,6 +328,7 @@ struct dw_dp { struct dw_dp_link link; struct dw_dp_plat_data plat_data; u8 pixel_mode; + bool usbc_mode; struct drm_bridge *next_bridge; @@ -1467,6 +1468,11 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux, if (WARN_ON(msg->size > 16)) return -E2BIG; + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dp->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + reinit_completion(&dp->complete); switch (msg->request & ~DP_AUX_I2C_MOT) { @@ -1659,6 +1665,12 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_connector_state *conn_state; int ret; + ret = pm_runtime_get_active(dp->dev, RPM_TRANSPARENT); + if (ret) { + dev_err(dp->dev, "runtime PM failure\n"); + return; + } + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); if (!connector) { dev_err(dp->dev, "failed to get connector\n"); @@ -1713,6 +1725,7 @@ static void dw_dp_bridge_atomic_disable(struct drm_bridge *bridge, dw_dp_link_disable(dp); bitmap_zero(dp->sdp_reg_bank, SDP_REG_BANK_SIZE); dw_dp_reset(dp); + pm_runtime_put_autosuspend(dp->dev); } static bool dw_dp_hpd_detect_link(struct dw_dp *dp, struct drm_connector *connector) @@ -1733,6 +1746,10 @@ static enum drm_connector_status dw_dp_bridge_detect(struct drm_bridge *bridge, { struct dw_dp *dp = bridge_to_dp(bridge); + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dp->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return connector_status_disconnected; + if (!dw_dp_hpd_detect(dp)) return connector_status_disconnected; @@ -1915,6 +1932,11 @@ static void dw_dp_hpd_work(struct work_struct *work) bool long_hpd; int ret; + PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dp->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return; + mutex_lock(&dp->irq_lock); long_hpd = dp->hotplug.long_hpd; mutex_unlock(&dp->irq_lock); @@ -1966,6 +1988,9 @@ static irqreturn_t dw_dp_irq(int irq, void *data) struct dw_dp *dp = data; u32 value; + /* interrupt can only trigger for running device */ + guard(pm_runtime_noresume)(dp->dev); + regmap_read(dp->regmap, DW_DP_GENERAL_INTERRUPT, &value); if (!value) return IRQ_NONE; @@ -2064,25 +2089,51 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder) goto put_next_bridge; } - if (dw_dp_is_routed_to_usb_c(encoder)) { - dev_dbg(dev, "USB-C mode\n"); + dp->usbc_mode = dw_dp_is_routed_to_usb_c(encoder); - if (dp->plat_data.hpd_sw_sel) - dp->plat_data.hpd_sw_sel(dp->plat_data.data, 1); + ret = pm_runtime_get_active(dev, RPM_TRANSPARENT); + if (ret) { + dev_err_probe(dev, ret, "Failed to get runtime PM\n"); + goto put_next_bridge; } - dw_dp_init_hw(dp); + /* resume once if runtime PM is disabled */ + if (!pm_runtime_enabled(dev)) { + ret = dw_dp_runtime_resume(dp); + if (ret) + goto put_runtime_pm; + } + + if (dp->plat_data.hpd_sw_sel) + dp->plat_data.hpd_sw_sel(dp->plat_data.data, dp->usbc_mode); ret = phy_init(dp->phy); if (ret) { dev_err_probe(dev, ret, "phy init failed\n"); - goto put_next_bridge; + goto put_manual_pm; } enable_irq(dp->irq); + /* + * USB-C has out-of-band hotplug detection, so device may enter + * runtime suspend. Native mode needs to be resumed for working + * hotplug detection. + */ + if (dp->usbc_mode) { + dev_dbg(dev, "USB-C mode\n"); + pm_runtime_put_autosuspend(dp->dev); + } + return 0; +put_manual_pm: + if (!pm_runtime_enabled(dev)) + dw_dp_runtime_suspend(dp); + +put_runtime_pm: + pm_runtime_put_sync(dp->dev); + put_next_bridge: drm_bridge_put(dp->next_bridge); @@ -2101,6 +2152,11 @@ void dw_dp_unbind(struct dw_dp *dp) disable_irq(dp->irq); cancel_work_sync(&dp->hpd_work); phy_exit(dp->phy); + if (!dp->usbc_mode) + pm_runtime_put_sync(dp->dev); + /* suspend once if runtime PM is disabled */ + if (!pm_runtime_enabled(dp->dev)) + dw_dp_runtime_suspend(dp); drm_bridge_put(dp->next_bridge); drm_dp_aux_unregister(&dp->aux); drm_bridge_remove(&dp->bridge); @@ -2149,13 +2205,13 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_ return ERR_CAST(dp->phy); } - dp->apb_clk = devm_clk_get_enabled(dev, "apb"); + dp->apb_clk = devm_clk_get(dev, "apb"); if (IS_ERR(dp->apb_clk)) { dev_err_probe(dev, PTR_ERR(dp->apb_clk), "failed to get apb clock\n"); return ERR_CAST(dp->apb_clk); } - dp->aux_clk = devm_clk_get_enabled(dev, "aux"); + dp->aux_clk = devm_clk_get(dev, "aux"); if (IS_ERR(dp->aux_clk)) { dev_err_probe(dev, PTR_ERR(dp->aux_clk), "failed to get aux clock\n"); return ERR_CAST(dp->aux_clk); @@ -2200,6 +2256,41 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_ } EXPORT_SYMBOL_GPL(dw_dp_probe); +int dw_dp_runtime_suspend(struct dw_dp *dp) +{ + clk_disable_unprepare(dp->aux_clk); + clk_disable_unprepare(dp->apb_clk); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_dp_runtime_suspend); + +int dw_dp_runtime_resume(struct dw_dp *dp) +{ + int ret; + + ret = clk_prepare_enable(dp->apb_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(dp->aux_clk); + if (ret) { + clk_disable_unprepare(dp->apb_clk); + return ret; + } + + dw_dp_init_hw(dp); + + /* + * HPD_HOT_PLUG bit is asserted only after the sink holds HPD + * high for at least 100ms. + */ + msleep(110); + + return 0; +} +EXPORT_SYMBOL_GPL(dw_dp_runtime_resume); + MODULE_AUTHOR("Andy Yan <[email protected]>"); MODULE_DESCRIPTION("DW DP Core Library"); MODULE_LICENSE("GPL"); diff --git a/include/drm/bridge/dw_dp.h b/include/drm/bridge/dw_dp.h index 79b2cdf0df99..f20d2c2c40ca 100644 --- a/include/drm/bridge/dw_dp.h +++ b/include/drm/bridge/dw_dp.h @@ -29,4 +29,7 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder); void dw_dp_unbind(struct dw_dp *dp); struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_data *plat_data); + +int dw_dp_runtime_suspend(struct dw_dp *dp); +int dw_dp_runtime_resume(struct dw_dp *dp); #endif /* __DW_DP__ */ -- 2.53.0
