diff options
Diffstat (limited to 'drivers/video/omap2/dss/hdmi.c')
-rw-r--r-- | drivers/video/omap2/dss/hdmi.c | 161 |
1 files changed, 121 insertions, 40 deletions
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index b630c988fd51..635cc002a438 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c @@ -30,6 +30,8 @@ #include <linux/delay.h> #include <linux/string.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/clk.h> #include <video/omapdss.h> #if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) @@ -52,6 +54,9 @@ static struct { u8 edid_set; bool custom_set; struct hdmi_config cfg; + + struct clk *sys_clk; + struct clk *hdmi_clk; } hdmi; /* @@ -163,6 +168,27 @@ static inline int hdmi_wait_for_bit_change(const struct hdmi_reg idx, return val; } +static int hdmi_runtime_get(void) +{ + int r; + + DSSDBG("hdmi_runtime_get\n"); + + r = pm_runtime_get_sync(&hdmi.pdev->dev); + WARN_ON(r < 0); + return r < 0 ? r : 0; +} + +static void hdmi_runtime_put(void) +{ + int r; + + DSSDBG("hdmi_runtime_put\n"); + + r = pm_runtime_put(&hdmi.pdev->dev); + WARN_ON(r < 0); +} + int hdmi_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); @@ -312,30 +338,11 @@ static int hdmi_phy_init(void) return 0; } -static int hdmi_wait_softreset(void) -{ - /* reset W1 */ - REG_FLD_MOD(HDMI_WP_SYSCONFIG, 0x1, 0, 0); - - /* wait till SOFTRESET == 0 */ - if (hdmi_wait_for_bit_change(HDMI_WP_SYSCONFIG, 0, 0, 0) != 0) { - DSSERR("sysconfig reset failed\n"); - return -ETIMEDOUT; - } - - return 0; -} - static int hdmi_pll_program(struct hdmi_pll_info *fmt) { u16 r = 0; enum hdmi_clk_refsel refsel; - /* wait for wrapper reset */ - r = hdmi_wait_softreset(); - if (r) - return r; - r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF); if (r) return r; @@ -1065,7 +1072,7 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, unsigned long clkin, refclk; u32 mf; - clkin = dss_clk_get_rate(DSS_CLK_SYSCK) / 10000; + clkin = clk_get_rate(hdmi.sys_clk) / 10000; /* * Input clock is predivided by N + 1 * out put of which is reference clk @@ -1099,16 +1106,6 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); } -static void hdmi_enable_clocks(int enable) -{ - if (enable) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK | - DSS_CLK_SYSCK | DSS_CLK_VIDFCK); - else - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK | - DSS_CLK_SYSCK | DSS_CLK_VIDFCK); -} - static int hdmi_power_on(struct omap_dss_device *dssdev) { int r, code = 0; @@ -1116,7 +1113,9 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) struct omap_video_timings *p; unsigned long phy; - hdmi_enable_clocks(1); + r = hdmi_runtime_get(); + if (r) + return r; dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0); @@ -1181,7 +1180,7 @@ static int hdmi_power_on(struct omap_dss_device *dssdev) return 0; err: - hdmi_enable_clocks(0); + hdmi_runtime_put(); return -EIO; } @@ -1192,7 +1191,7 @@ static void hdmi_power_off(struct omap_dss_device *dssdev) hdmi_wp_video_start(0); hdmi_phy_off(); hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF); - hdmi_enable_clocks(0); + hdmi_runtime_put(); hdmi.edid_set = 0; } @@ -1687,14 +1686,43 @@ static struct snd_soc_dai_driver hdmi_codec_dai_drv = { }; #endif +static int hdmi_get_clocks(struct platform_device *pdev) +{ + struct clk *clk; + + clk = clk_get(&pdev->dev, "sys_clk"); + if (IS_ERR(clk)) { + DSSERR("can't get sys_clk\n"); + return PTR_ERR(clk); + } + + hdmi.sys_clk = clk; + + clk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(clk)) { + DSSERR("can't get fck\n"); + clk_put(hdmi.sys_clk); + return PTR_ERR(clk); + } + + hdmi.hdmi_clk = clk; + + return 0; +} + +static void hdmi_put_clocks(void) +{ + if (hdmi.sys_clk) + clk_put(hdmi.sys_clk); + if (hdmi.hdmi_clk) + clk_put(hdmi.hdmi_clk); +} + /* HDMI HW IP initialisation */ static int omapdss_hdmihw_probe(struct platform_device *pdev) { struct resource *hdmi_mem; -#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ - defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) - int ret; -#endif + int r; hdmi.pdata = pdev->dev.platform_data; hdmi.pdev = pdev; @@ -1714,17 +1742,25 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) return -ENOMEM; } + r = hdmi_get_clocks(pdev); + if (r) { + iounmap(hdmi.base_wp); + return r; + } + + pm_runtime_enable(&pdev->dev); + hdmi_panel_init(); #if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) /* Register ASoC codec DAI */ - ret = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv, + r = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv, &hdmi_codec_dai_drv, 1); - if (ret) { + if (r) { DSSERR("can't register ASoC HDMI audio codec\n"); - return ret; + return r; } #endif return 0; @@ -1739,17 +1775,62 @@ static int omapdss_hdmihw_remove(struct platform_device *pdev) snd_soc_unregister_codec(&pdev->dev); #endif + pm_runtime_disable(&pdev->dev); + + hdmi_put_clocks(); + iounmap(hdmi.base_wp); return 0; } +static int hdmi_runtime_suspend(struct device *dev) +{ + clk_disable(hdmi.hdmi_clk); + clk_disable(hdmi.sys_clk); + + dispc_runtime_put(); + dss_runtime_put(); + + return 0; +} + +static int hdmi_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(hdmi.sys_clk); + clk_enable(hdmi.hdmi_clk); + + return 0; + +err_get_dispc: + dss_runtime_put(); +err_get_dss: + return r; +} + +static const struct dev_pm_ops hdmi_pm_ops = { + .runtime_suspend = hdmi_runtime_suspend, + .runtime_resume = hdmi_runtime_resume, +}; + static struct platform_driver omapdss_hdmihw_driver = { .probe = omapdss_hdmihw_probe, .remove = omapdss_hdmihw_remove, .driver = { .name = "omapdss_hdmi", .owner = THIS_MODULE, + .pm = &hdmi_pm_ops, }, }; |