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 | 71 ++++++++++++++++++++++++++++++++- include/drm/bridge/dw_dp.h | 3 ++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c index 9975df8ea37e..bcf7ad84f217 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_IF_ENABLED_AUTOSUSPEND(dp->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + switch (msg->request & ~DP_AUX_I2C_MOT) { case DP_AUX_NATIVE_WRITE: case DP_AUX_I2C_WRITE: @@ -1720,6 +1726,8 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_connector_state *conn_state; int ret; + pm_runtime_get_sync(dp->dev); + connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); if (!connector) { dev_err(dp->dev, "failed to get connector\n"); @@ -1774,6 +1782,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) @@ -1794,6 +1803,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_IF_ENABLED_AUTOSUSPEND(dp->dev, pm); + if (PM_RUNTIME_ACQUIRE_ERR(&pm)) + return connector_status_disconnected; + if (!dw_dp_hpd_detect(dp)) return connector_status_disconnected; @@ -1973,10 +1986,17 @@ static irqreturn_t dw_dp_irq(int irq, void *data) { struct dw_dp *dp = data; u32 value; + int ret; + + ret = pm_runtime_get_if_active(dp->dev); + if (ret <= 0) + return IRQ_NONE; regmap_read(dp->regmap, DW_DP_GENERAL_INTERRUPT, &value); - if (!value) + if (!value) { + pm_runtime_put_autosuspend(dp->dev); return IRQ_NONE; + } if (value & HPD_EVENT) dw_dp_handle_hpd_event(dp); @@ -1986,6 +2006,7 @@ static irqreturn_t dw_dp_irq(int irq, void *data) complete(&dp->complete); } + pm_runtime_put_autosuspend(dp->dev); return IRQ_HANDLED; } @@ -2073,10 +2094,15 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder) } if (dw_dp_is_routed_to_usb_c(encoder)) { + dp->usbc_mode = true; dev_dbg(dev, "USB-C mode\n"); if (dp->plat_data.hpd_sw_sel) dp->plat_data.hpd_sw_sel(dp->plat_data.data, 1); + } else { + dp->usbc_mode = false; + /* Keep runtime PM enabled to have working native HPD IRQ */ + pm_runtime_get_sync(dp->dev); } dw_dp_init_hw(dp); @@ -2084,13 +2110,17 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder) ret = phy_init(dp->phy); if (ret) { dev_err_probe(dev, ret, "phy init failed\n"); - goto put_next_bridge; + goto put_runtime_pm; } enable_irq(dp->irq); return 0; +put_runtime_pm: + if (!dp->usbc_mode) + pm_runtime_put_sync(dp->dev); + put_next_bridge: drm_bridge_put(dp->next_bridge); @@ -2106,6 +2136,8 @@ EXPORT_SYMBOL_GPL(dw_dp_bind); void dw_dp_unbind(struct dw_dp *dp) { + if (!dp->usbc_mode) + pm_runtime_put_sync(dp->dev); disable_irq(dp->irq); cancel_work_sync(&dp->hpd_work); phy_exit(dp->phy); @@ -2208,6 +2240,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
