drm: bridge: dw-hdmi: Add runtime-pm support

Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 1a4ba49..3c532f6 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -19,6 +19,7 @@
 #include <linux/hdmi.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/spinlock.h>
 
 #include <drm/drm_of.h>
@@ -1750,12 +1751,16 @@ static void dw_hdmi_bridge_disable(struct drm_bridge *bridge)
 	dw_hdmi_update_power(hdmi);
 	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
+
+	pm_runtime_put_sync(hdmi->dev);
 }
 
 static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 
+	pm_runtime_get_sync(hdmi);
+
 	mutex_lock(&hdmi->mutex);
 	hdmi->disabled = false;
 	dw_hdmi_update_power(hdmi);
@@ -1944,6 +1949,63 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
 	return 0;
 }
 
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
+
+static int dw_hdmi_clocks_enable(struct dw_hdmi *hdmi)
+{
+	int ret;
+
+	ret = clk_prepare_enable(hdmi->isfr_clk);
+	if (ret) {
+		dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->iahb_clk);
+	if (ret) {
+		clk_disable_unprepare(hdmi->isfr_clk);
+		dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void dw_hdmi_clocks_disable(struct dw_hdmi *hdmi)
+{
+	clk_disable_unprepare(hdmi->iahb_clk);
+	clk_disable_unprepare(hdmi->isfr_clk);
+}
+
+#ifdef CONFIG_PM
+static int dw_hdmi_pm_runtime_suspend(struct device *dev)
+{
+	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
+
+	dw_hdmi_clocks_disable(hdmi);
+
+	return 0;
+}
+
+static int dw_hdmi_pm_runtime_resume(struct device *dev)
+{
+	struct dw_hdmi *hdmi = dev_get_drvdata(dev);
+
+	return dw_hdmi_clocks_enable(hdmi);
+}
+#endif
+
+const struct dev_pm_ops dw_hdmi_pm_ops = {
+	SET_RUNTIME_PM_OPS(dw_hdmi_pm_runtime_suspend,
+			   dw_hdmi_pm_runtime_resume,
+			   NULL)
+};
+EXPORT_SYMBOL_GPL(dw_hdmi_pm_ops);
+
+
+
 static struct dw_hdmi *
 __dw_hdmi_probe(struct platform_device *pdev,
 		const struct dw_hdmi_plat_data *plat_data)
@@ -2020,24 +2082,14 @@ __dw_hdmi_probe(struct platform_device *pdev,
 		goto err_res;
 	}
 
-	ret = clk_prepare_enable(hdmi->isfr_clk);
-	if (ret) {
-		dev_err(hdmi->dev, "Cannot enable HDMI isfr clock: %d\n", ret);
-		goto err_res;
-	}
-
 	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
 	if (IS_ERR(hdmi->iahb_clk)) {
 		ret = PTR_ERR(hdmi->iahb_clk);
 		dev_err(hdmi->dev, "Unable to get HDMI iahb clk: %d\n", ret);
-		goto err_isfr;
+		goto err_res;
 	}
 
-	ret = clk_prepare_enable(hdmi->iahb_clk);
-	if (ret) {
-		dev_err(hdmi->dev, "Cannot enable HDMI iahb clock: %d\n", ret);
-		goto err_isfr;
-	}
+	pm_runtime_enable(hdmi->dev);
 
 	/* Product and revision IDs */
 	hdmi->version = (hdmi_readb(hdmi, HDMI_DESIGN_ID) << 8)
@@ -2160,9 +2212,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
 		hdmi->ddc = NULL;
 	}
 
-	clk_disable_unprepare(hdmi->iahb_clk);
-err_isfr:
-	clk_disable_unprepare(hdmi->isfr_clk);
+	pm_runtime_disable(hdmi->dev);
+
 err_res:
 	i2c_put_adapter(hdmi->ddc);
 
@@ -2177,8 +2228,7 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi)
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
-	clk_disable_unprepare(hdmi->iahb_clk);
-	clk_disable_unprepare(hdmi->isfr_clk);
+	pm_runtime_disable(hdmi->dev);
 
 	if (hdmi->i2c)
 		i2c_del_adapter(&hdmi->i2c->adap);
diff --git a/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c b/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c
index 7539626..297b484 100644
--- a/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c
+++ b/drivers/gpu/drm/rcar-du/rcar_dw_hdmi.c
@@ -90,6 +90,7 @@ static struct platform_driver rcar_dw_hdmi_platform_driver = {
 	.driver		= {
 		.name	= "rcar-dw-hdmi",
 		.of_match_table = rcar_dw_hdmi_of_table,
+		.pm	= dw_hdmi_pm_ops, /* Provided by dw-hdmi.c */
 	},
 };
 
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index 735a8ab..ece97e1 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -62,6 +62,8 @@ struct dw_hdmi_plat_data {
 					   struct drm_display_mode *mode);
 };
 
+const struct dev_pm_ops dw_hdmi_pm_ops;
+
 int dw_hdmi_probe(struct platform_device *pdev,
 		  const struct dw_hdmi_plat_data *plat_data);
 void dw_hdmi_remove(struct platform_device *pdev);