]> Pileus Git - ~andy/linux/blobdiff - drivers/video/omap2/dss/venc.c
OMAP: DSS2: Use PM runtime & HWMOD support
[~andy/linux] / drivers / video / omap2 / dss / venc.c
index bf0431f788c1e75c483407a68dedb401c2defc94..71e005df175944e80c8c8ffc5c0fed008ac9eee9 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/seq_file.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
 
 #include <video/omapdss.h>
 #include <plat/cpu.h>
@@ -293,6 +294,9 @@ static struct {
        struct mutex venc_lock;
        u32 wss_data;
        struct regulator *vdda_dac_reg;
+
+       struct clk      *tv_clk;
+       struct clk      *tv_dac_clk;
 } venc;
 
 static inline void venc_write_reg(int idx, u32 val)
@@ -381,17 +385,25 @@ static void venc_reset(void)
 #endif
 }
 
-static void venc_enable_clocks(int enable)
+static int venc_runtime_get(void)
 {
-       if (enable) {
-               dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK);
-               if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK))
-                       dss_clk_enable(DSS_CLK_VIDFCK);
-       } else {
-               dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK | DSS_CLK_TVFCK);
-               if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK))
-                       dss_clk_disable(DSS_CLK_VIDFCK);
-       }
+       int r;
+
+       DSSDBG("venc_runtime_get\n");
+
+       r = pm_runtime_get_sync(&venc.pdev->dev);
+       WARN_ON(r < 0);
+       return r < 0 ? r : 0;
+}
+
+static void venc_runtime_put(void)
+{
+       int r;
+
+       DSSDBG("venc_runtime_put\n");
+
+       r = pm_runtime_put(&venc.pdev->dev);
+       WARN_ON(r < 0);
 }
 
 static const struct venc_config *venc_timings_to_config(
@@ -410,8 +422,6 @@ static void venc_power_on(struct omap_dss_device *dssdev)
 {
        u32 l;
 
-       venc_enable_clocks(1);
-
        venc_reset();
        venc_write_config(venc_timings_to_config(&dssdev->panel.timings));
 
@@ -452,8 +462,6 @@ static void venc_power_off(struct omap_dss_device *dssdev)
                dssdev->platform_disable(dssdev);
 
        regulator_disable(venc.vdda_dac_reg);
-
-       venc_enable_clocks(0);
 }
 
 
@@ -491,6 +499,10 @@ static int venc_panel_enable(struct omap_dss_device *dssdev)
                goto err1;
        }
 
+       r = venc_runtime_get();
+       if (r)
+               goto err1;
+
        venc_power_on(dssdev);
 
        venc.wss_data = 0;
@@ -524,6 +536,8 @@ static void venc_panel_disable(struct omap_dss_device *dssdev)
 
        venc_power_off(dssdev);
 
+       venc_runtime_put();
+
        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 
        omap_dss_stop_device(dssdev);
@@ -588,6 +602,7 @@ static u32 venc_get_wss(struct omap_dss_device *dssdev)
 static int venc_set_wss(struct omap_dss_device *dssdev,        u32 wss)
 {
        const struct venc_config *config;
+       int r;
 
        DSSDBG("venc_set_wss\n");
 
@@ -598,16 +613,19 @@ static int venc_set_wss(struct omap_dss_device *dssdev,   u32 wss)
        /* Invert due to VENC_L21_WC_CTL:INV=1 */
        venc.wss_data = (wss ^ 0xfffff) << 8;
 
-       venc_enable_clocks(1);
+       r = venc_runtime_get();
+       if (r)
+               goto err;
 
        venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data |
                        venc.wss_data);
 
-       venc_enable_clocks(0);
+       venc_runtime_put();
 
+err:
        mutex_unlock(&venc.venc_lock);
 
-       return 0;
+       return r;
 }
 
 static struct omap_dss_driver venc_driver = {
@@ -660,7 +678,8 @@ void venc_dump_regs(struct seq_file *s)
 {
 #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r))
 
-       venc_enable_clocks(1);
+       if (venc_runtime_get())
+               return;
 
        DUMPREG(VENC_F_CONTROL);
        DUMPREG(VENC_VIDOUT_CTRL);
@@ -704,16 +723,53 @@ void venc_dump_regs(struct seq_file *s)
        DUMPREG(VENC_OUTPUT_CONTROL);
        DUMPREG(VENC_OUTPUT_TEST);
 
-       venc_enable_clocks(0);
+       venc_runtime_put();
 
 #undef DUMPREG
 }
 
+static int venc_get_clocks(struct platform_device *pdev)
+{
+       struct clk *clk;
+
+       clk = clk_get(&pdev->dev, "fck");
+       if (IS_ERR(clk)) {
+               DSSERR("can't get fck\n");
+               return PTR_ERR(clk);
+       }
+
+       venc.tv_clk = clk;
+
+       if (dss_has_feature(FEAT_VENC_REQUIRES_TV_DAC_CLK)) {
+               clk = clk_get(&pdev->dev, "tv_dac_clk");
+               if (IS_ERR(clk)) {
+                       DSSERR("can't get tv_dac_clk\n");
+                       clk_put(venc.tv_clk);
+                       return PTR_ERR(clk);
+               }
+       } else {
+               clk = NULL;
+       }
+
+       venc.tv_dac_clk = clk;
+
+       return 0;
+}
+
+static void venc_put_clocks(void)
+{
+       if (venc.tv_clk)
+               clk_put(venc.tv_clk);
+       if (venc.tv_dac_clk)
+               clk_put(venc.tv_dac_clk);
+}
+
 /* VENC HW IP initialisation */
 static int omap_venchw_probe(struct platform_device *pdev)
 {
        u8 rev_id;
        struct resource *venc_mem;
+       int r;
 
        venc.pdev = pdev;
 
@@ -724,22 +780,40 @@ static int omap_venchw_probe(struct platform_device *pdev)
        venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0);
        if (!venc_mem) {
                DSSERR("can't get IORESOURCE_MEM VENC\n");
-               return -EINVAL;
+               r = -EINVAL;
+               goto err_ioremap;
        }
        venc.base = ioremap(venc_mem->start, resource_size(venc_mem));
        if (!venc.base) {
                DSSERR("can't ioremap VENC\n");
-               return -ENOMEM;
+               r = -ENOMEM;
+               goto err_ioremap;
        }
 
-       venc_enable_clocks(1);
+       r = venc_get_clocks(pdev);
+       if (r)
+               goto err_get_clk;
+
+       pm_runtime_enable(&pdev->dev);
+
+       r = venc_runtime_get();
+       if (r)
+               goto err_get_venc;
 
        rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);
        dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id);
 
-       venc_enable_clocks(0);
+       venc_runtime_put();
 
        return omap_dss_register_driver(&venc_driver);
+
+err_get_venc:
+       pm_runtime_disable(&pdev->dev);
+       venc_put_clocks();
+err_get_clk:
+       iounmap(venc.base);
+err_ioremap:
+       return r;
 }
 
 static int omap_venchw_remove(struct platform_device *pdev)
@@ -750,16 +824,61 @@ static int omap_venchw_remove(struct platform_device *pdev)
        }
        omap_dss_unregister_driver(&venc_driver);
 
+       pm_runtime_disable(&pdev->dev);
+       venc_put_clocks();
+
        iounmap(venc.base);
        return 0;
 }
 
+static int venc_runtime_suspend(struct device *dev)
+{
+       if (venc.tv_dac_clk)
+               clk_disable(venc.tv_dac_clk);
+       clk_disable(venc.tv_clk);
+
+       dispc_runtime_put();
+       dss_runtime_put();
+
+       return 0;
+}
+
+static int venc_runtime_resume(struct device *dev)
+{
+       int r;
+
+       r = dss_runtime_get();
+       if (r < 0)
+               goto err_get_dss;
+
+       r = dispc_runtime_get();
+       if (r < 0)
+               goto err_get_dispc;
+
+       clk_enable(venc.tv_clk);
+       if (venc.tv_dac_clk)
+               clk_enable(venc.tv_dac_clk);
+
+       return 0;
+
+err_get_dispc:
+       dss_runtime_put();
+err_get_dss:
+       return r;
+}
+
+static const struct dev_pm_ops venc_pm_ops = {
+       .runtime_suspend = venc_runtime_suspend,
+       .runtime_resume = venc_runtime_resume,
+};
+
 static struct platform_driver omap_venchw_driver = {
        .probe          = omap_venchw_probe,
        .remove         = omap_venchw_remove,
        .driver         = {
                .name   = "omapdss_venc",
                .owner  = THIS_MODULE,
+               .pm     = &venc_pm_ops,
        },
 };