From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Let pm_runtime handle the enabling/disabling of the device with proper refcnt instead of rely on specific flags to track the enabled state.
Changelog v3: - revive suspended to keep current dpms mode
Changelog v2: - Modify CONFIG_PM_SLEEP -> CONFIG_PM
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk Signed-off-by: Inki Dae inki.dae@samsung.com --- drivers/gpu/drm/exynos/exynos7_drm_decon.c | 93 ++++++++++++++++++------------ 1 file changed, 55 insertions(+), 38 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index ead2b16..38d7762 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -119,13 +119,8 @@ static void decon_clear_channels(struct exynos_drm_crtc *crtc) }
/* Wait for vsync, as disable channel takes effect at next vsync */ - if (ch_enabled) { - unsigned int state = ctx->suspended; - - ctx->suspended = 0; + if (ch_enabled) decon_wait_for_vblank(ctx->crtc); - ctx->suspended = state; - } }
static int decon_ctx_initialize(struct decon_context *ctx, @@ -555,39 +550,12 @@ static void decon_init(struct decon_context *ctx) static void decon_enable(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; - int ret;
if (!ctx->suspended) return;
- ctx->suspended = false; - pm_runtime_get_sync(ctx->dev);
- ret = clk_prepare_enable(ctx->pclk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret); - return; - } - - ret = clk_prepare_enable(ctx->aclk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret); - return; - } - - ret = clk_prepare_enable(ctx->eclk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret); - return; - } - - ret = clk_prepare_enable(ctx->vclk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret); - return; - } - decon_init(ctx);
/* if vblank was enabled status, enable it again. */ @@ -595,6 +563,8 @@ static void decon_enable(struct exynos_drm_crtc *crtc) decon_enable_vblank(ctx->crtc);
decon_commit(ctx->crtc); + + ctx->suspended = false; }
static void decon_disable(struct exynos_drm_crtc *crtc) @@ -613,11 +583,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc) for (i = 0; i < WINDOWS_NR; i++) decon_disable_plane(crtc, &ctx->planes[i]);
- clk_disable_unprepare(ctx->vclk); - clk_disable_unprepare(ctx->eclk); - clk_disable_unprepare(ctx->aclk); - clk_disable_unprepare(ctx->pclk); - pm_runtime_put_sync(ctx->dev);
ctx->suspended = true; @@ -843,11 +808,63 @@ static int decon_remove(struct platform_device *pdev) return 0; }
+#ifdef CONFIG_PM +static int exynos7_decon_suspend(struct device *dev) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + + clk_disable_unprepare(ctx->vclk); + clk_disable_unprepare(ctx->eclk); + clk_disable_unprepare(ctx->aclk); + clk_disable_unprepare(ctx->pclk); + + return 0; +} + +static int exynos7_decon_resume(struct device *dev) +{ + struct decon_context *ctx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(ctx->pclk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the pclk [%d]\n", ret); + return ret; + } + + ret = clk_prepare_enable(ctx->aclk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the aclk [%d]\n", ret); + return ret; + } + + ret = clk_prepare_enable(ctx->eclk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the eclk [%d]\n", ret); + return ret; + } + + ret = clk_prepare_enable(ctx->vclk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the vclk [%d]\n", ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos7_decon_pm_ops = { + SET_RUNTIME_PM_OPS(exynos7_decon_suspend, exynos7_decon_resume, + NULL) +}; + struct platform_driver decon_driver = { .probe = decon_probe, .remove = decon_remove, .driver = { .name = "exynos-decon", + .pm = &exynos7_decon_pm_ops, .of_match_table = decon_driver_dt_match, }, };
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Let pm_runtime handle the enabling/disabling of the device with proper refcnt instead of rely on specific flags to track the enabled state.
Chnagelog v3: - revive dpms_mode to keep current dpms mode.
Changelog v2: - no change
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk Signed-off-by: Inki Dae inki.dae@samsung.com --- drivers/gpu/drm/exynos/exynos_dp_core.c | 51 +++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index e4d32a1..94f02a0 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -1061,6 +1061,8 @@ static void exynos_dp_bridge_enable(struct drm_bridge *bridge) if (dp->dpms_mode == DRM_MODE_DPMS_ON) return;
+ pm_runtime_get_sync(dp->dev); + if (dp->panel) { if (drm_panel_prepare(dp->panel)) { DRM_ERROR("failed to setup the panel\n"); @@ -1071,7 +1073,6 @@ static void exynos_dp_bridge_enable(struct drm_bridge *bridge) if (crtc->ops->clock_enable) crtc->ops->clock_enable(dp_to_crtc(dp), true);
- clk_prepare_enable(dp->clock); phy_power_on(dp->phy); exynos_dp_init_dp(dp); enable_irq(dp->irq); @@ -1098,7 +1099,6 @@ static void exynos_dp_bridge_disable(struct drm_bridge *bridge) disable_irq(dp->irq); flush_work(&dp->hotplug_work); phy_power_off(dp->phy); - clk_disable_unprepare(dp->clock);
if (crtc->ops->clock_enable) crtc->ops->clock_enable(dp_to_crtc(dp), false); @@ -1108,6 +1108,8 @@ static void exynos_dp_bridge_disable(struct drm_bridge *bridge) DRM_ERROR("failed to turnoff the panel\n"); }
+ pm_runtime_put_sync(dp->dev); + dp->dpms_mode = DRM_MODE_DPMS_OFF; }
@@ -1392,6 +1394,7 @@ static int exynos_dp_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *panel_node, *bridge_node, *endpoint; struct exynos_dp_device *dp; + int ret;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), GFP_KERNEL); @@ -1420,16 +1423,57 @@ static int exynos_dp_probe(struct platform_device *pdev) return -EPROBE_DEFER; }
- return component_add(&pdev->dev, &exynos_dp_ops); + pm_runtime_enable(dev); + + ret = component_add(&pdev->dev, &exynos_dp_ops); + if (ret) + goto err_disable_pm_runtime; + + return ret; + +err_disable_pm_runtime: + pm_runtime_disable(dev); + + return ret; }
static int exynos_dp_remove(struct platform_device *pdev) { + pm_runtime_disable(&pdev->dev); component_del(&pdev->dev, &exynos_dp_ops);
return 0; }
+#ifdef CONFIG_PM +static int exynos_dp_suspend(struct device *dev) +{ + struct exynos_dp_device *dp = dev_get_drvdata(dev); + + clk_disable_unprepare(dp->clock); + + return 0; +} + +static int exynos_dp_resume(struct device *dev) +{ + struct exynos_dp_device *dp = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(dp->clock); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_dp_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL) +}; + static const struct of_device_id exynos_dp_match[] = { { .compatible = "samsung,exynos5-dp" }, {}, @@ -1442,6 +1486,7 @@ struct platform_driver dp_driver = { .driver = { .name = "exynos-dp", .owner = THIS_MODULE, + .pm = &exynos_dp_pm_ops, .of_match_table = exynos_dp_match, }, };
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Let pm_runtime handle the enabling/disabling of the device with proper refcnt instead of rely on specific flags to track the enabled state.
Chnagelog v3: - Revive suspended varable to check the suspend status.
Changelog v2: - Remove unnecessary changes which removed commit callback from decon drivers and modify CONFIG_PM_SLEEP -> CONFIG_PM
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk Signed-off-by: Inki Dae inki.dae@samsung.com --- drivers/gpu/drm/exynos/exynos_drm_fimd.c | 54 ++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index bd75c15..2c38323 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -745,7 +745,6 @@ static void fimd_disable_plane(struct exynos_drm_crtc *crtc, static void fimd_enable(struct exynos_drm_crtc *crtc) { struct fimd_context *ctx = crtc->ctx; - int ret;
if (!ctx->suspended) return; @@ -754,18 +753,6 @@ static void fimd_enable(struct exynos_drm_crtc *crtc)
pm_runtime_get_sync(ctx->dev);
- ret = clk_prepare_enable(ctx->bus_clk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret); - return; - } - - ret = clk_prepare_enable(ctx->lcd_clk); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret); - return; - } - /* if vblank was enabled status, enable it again. */ if (test_and_clear_bit(0, &ctx->irq_flags)) fimd_enable_vblank(ctx->crtc); @@ -795,11 +782,7 @@ static void fimd_disable(struct exynos_drm_crtc *crtc)
writel(0, ctx->regs + VIDCON0);
- clk_disable_unprepare(ctx->lcd_clk); - clk_disable_unprepare(ctx->bus_clk); - pm_runtime_put_sync(ctx->dev); - ctx->suspended = true; }
@@ -1121,12 +1104,49 @@ static int fimd_remove(struct platform_device *pdev) return 0; }
+#ifdef CONFIG_PM +static int exynos_fimd_suspend(struct device *dev) +{ + struct fimd_context *ctx = dev_get_drvdata(dev); + + clk_disable_unprepare(ctx->lcd_clk); + clk_disable_unprepare(ctx->bus_clk); + + return 0; +} + +static int exynos_fimd_resume(struct device *dev) +{ + struct fimd_context *ctx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(ctx->bus_clk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the bus clk [%d]\n", ret); + return ret; + } + + ret = clk_prepare_enable(ctx->lcd_clk); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the lcd clk [%d]\n", ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_fimd_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_fimd_suspend, exynos_fimd_resume, NULL) +}; + struct platform_driver fimd_driver = { .probe = fimd_probe, .remove = fimd_remove, .driver = { .name = "exynos4-fb", .owner = THIS_MODULE, + .pm = &exynos_fimd_pm_ops, .of_match_table = fimd_driver_dt_match, }, };
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Let pm_runtime handle the enabling/disabling of the device with proper refcnt instead of rely on specific flags to track the enabled state.
Changelog v3: - revive powered flag to keep current dpms mode
Changelog v2: - Mofidy CONFIG_PM_SLEEP -> CONFIG_PM
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk Signed-off-by: Inki Dae inki.dae@samsung.com --- drivers/gpu/drm/exynos/exynos_hdmi.c | 49 +++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 57b6755..9a955d2 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -113,7 +113,7 @@ struct hdmi_context { void __iomem *regs_hdmiphy; struct i2c_client *hdmiphy_port; struct i2c_adapter *ddc_adpt; - struct gpio_desc *hpd_gpio; + struct gpio_desc *hpd_gpio; int irq; struct regmap *pmureg; struct clk *hdmi; @@ -1588,8 +1588,6 @@ static void hdmi_enable(struct drm_encoder *encoder) if (hdata->powered) return;
- hdata->powered = true; - pm_runtime_get_sync(hdata->dev);
if (regulator_bulk_enable(ARRAY_SIZE(supply), hdata->regul_bulk)) @@ -1599,10 +1597,9 @@ static void hdmi_enable(struct drm_encoder *encoder) regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL, PMU_HDMI_PHY_ENABLE_BIT, 1);
- clk_prepare_enable(hdata->hdmi); - clk_prepare_enable(hdata->sclk_hdmi); - hdmi_conf_apply(hdata); + + hdata->powered = true; }
static void hdmi_disable(struct drm_encoder *encoder) @@ -1633,9 +1630,6 @@ static void hdmi_disable(struct drm_encoder *encoder)
cancel_delayed_work(&hdata->hotplug_work);
- clk_disable_unprepare(hdata->sclk_hdmi); - clk_disable_unprepare(hdata->hdmi); - /* reset pmu hdmiphy control bit to disable hdmiphy */ regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL, PMU_HDMI_PHY_ENABLE_BIT, 0); @@ -1978,12 +1972,49 @@ static int hdmi_remove(struct platform_device *pdev) return 0; }
+#ifdef CONFIG_PM +static int exynos_hdmi_suspend(struct device *dev) +{ + struct hdmi_context *hdata = dev_get_drvdata(dev); + + clk_disable_unprepare(hdata->sclk_hdmi); + clk_disable_unprepare(hdata->hdmi); + + return 0; +} + +static int exynos_hdmi_resume(struct device *dev) +{ + struct hdmi_context *hdata = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(hdata->hdmi); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret); + return ret; + } + ret = clk_prepare_enable(hdata->sclk_hdmi); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the sclk_mixer clk [%d]\n", + ret); + return ret; + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_hdmi_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL) +}; + struct platform_driver hdmi_driver = { .probe = hdmi_probe, .remove = hdmi_remove, .driver = { .name = "exynos-hdmi", .owner = THIS_MODULE, + .pm = &exynos_hdmi_pm_ops, .of_match_table = hdmi_match_types, }, };
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Let pm_runtime handle the enabling/disabling of the device with proper refcnt instead of rely on specific flags to track the enabled state.
Changelog v3: - revive MXR_BIT_POWERED flag to keep current dpms mode.
Changelog v2: - no change
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk Signed-off-by: Inki Dae inki.dae@samsung.com --- drivers/gpu/drm/exynos/exynos_mixer.c | 106 ++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 42 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index d09f8f9..47777be 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1020,43 +1020,12 @@ static void mixer_enable(struct exynos_drm_crtc *crtc) { struct mixer_context *ctx = crtc->ctx; struct mixer_resources *res = &ctx->mixer_res; - int ret;
if (test_bit(MXR_BIT_POWERED, &ctx->flags)) return;
pm_runtime_get_sync(ctx->dev);
- ret = clk_prepare_enable(res->mixer); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret); - return; - } - ret = clk_prepare_enable(res->hdmi); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret); - return; - } - if (ctx->vp_enabled) { - ret = clk_prepare_enable(res->vp); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n", - ret); - return; - } - if (ctx->has_sclk) { - ret = clk_prepare_enable(res->sclk_mixer); - if (ret < 0) { - DRM_ERROR("Failed to prepare_enable the " \ - "sclk_mixer clk [%d]\n", - ret); - return; - } - } - } - - set_bit(MXR_BIT_POWERED, &ctx->flags); - mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET);
if (test_bit(MXR_BIT_VSYNC, &ctx->flags)) { @@ -1064,12 +1033,13 @@ static void mixer_enable(struct exynos_drm_crtc *crtc) mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC); } mixer_win_reset(ctx); + + set_bit(MXR_BIT_POWERED, &ctx->flags); }
static void mixer_disable(struct exynos_drm_crtc *crtc) { struct mixer_context *ctx = crtc->ctx; - struct mixer_resources *res = &ctx->mixer_res; int i;
if (!test_bit(MXR_BIT_POWERED, &ctx->flags)) @@ -1081,17 +1051,9 @@ static void mixer_disable(struct exynos_drm_crtc *crtc) for (i = 0; i < MIXER_WIN_NR; i++) mixer_disable_plane(crtc, &ctx->planes[i]);
- clear_bit(MXR_BIT_POWERED, &ctx->flags); + pm_runtime_put(ctx->dev);
- clk_disable_unprepare(res->hdmi); - clk_disable_unprepare(res->mixer); - if (ctx->vp_enabled) { - clk_disable_unprepare(res->vp); - if (ctx->has_sclk) - clk_disable_unprepare(res->sclk_mixer); - } - - pm_runtime_put_sync(ctx->dev); + clear_bit(MXR_BIT_POWERED, &ctx->flags); }
/* Only valid for Mixer version 16.0.33.0 */ @@ -1293,10 +1255,70 @@ static int mixer_remove(struct platform_device *pdev) return 0; }
+#ifdef CONFIG_PM_SLEEP +static int exynos_mixer_suspend(struct device *dev) +{ + struct mixer_context *ctx = dev_get_drvdata(dev); + struct mixer_resources *res = &ctx->mixer_res; + + clk_disable_unprepare(res->hdmi); + clk_disable_unprepare(res->mixer); + if (ctx->vp_enabled) { + clk_disable_unprepare(res->vp); + if (ctx->has_sclk) + clk_disable_unprepare(res->sclk_mixer); + } + + return 0; +} + +static int exynos_mixer_resume(struct device *dev) +{ + struct mixer_context *ctx = dev_get_drvdata(dev); + struct mixer_resources *res = &ctx->mixer_res; + int ret; + + ret = clk_prepare_enable(res->mixer); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the mixer clk [%d]\n", ret); + return ret; + } + ret = clk_prepare_enable(res->hdmi); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret); + return ret; + } + if (ctx->vp_enabled) { + ret = clk_prepare_enable(res->vp); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n", + ret); + return ret; + } + if (ctx->has_sclk) { + ret = clk_prepare_enable(res->sclk_mixer); + if (ret < 0) { + DRM_ERROR("Failed to prepare_enable the " \ + "sclk_mixer clk [%d]\n", + ret); + return ret; + } + } + } + + return 0; +} +#endif + +static const struct dev_pm_ops exynos_mixer_pm_ops = { + SET_RUNTIME_PM_OPS(exynos_mixer_suspend, exynos_mixer_resume, NULL) +}; + struct platform_driver mixer_driver = { .driver = { .name = "exynos-mixer", .owner = THIS_MODULE, + .pm = &exynos_mixer_pm_ops, .of_match_table = mixer_match_types, }, .probe = mixer_probe,
dri-devel@lists.freedesktop.org