diff options
Diffstat (limited to 'drivers/video/omap2/dss/venc.c')
-rw-r--r-- | drivers/video/omap2/dss/venc.c | 165 |
1 files changed, 142 insertions, 23 deletions
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index bf0431f788c..71e005df175 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -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, }, }; |