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

Reply via email to