summaryrefslogtreecommitdiffstats
path: root/drivers/video/omap2/dss/dss.c
diff options
context:
space:
mode:
authorSenthilvadivu Guruswamy <svadivu@ti.com>2011-01-24 06:21:58 +0000
committerTomi Valkeinen <tomi.valkeinen@ti.com>2011-03-11 15:46:19 +0200
commit8b9cb3a8f39d0864c925c5cd5c2c54cfd85ad551 (patch)
treeb7ebabcc80701ace88f0ee305d84fb2d24650ecd /drivers/video/omap2/dss/dss.c
parent96c401bcb83a182a4f332f2f64ee6530ba35511a (diff)
downloadop-kernel-dev-8b9cb3a8f39d0864c925c5cd5c2c54cfd85ad551.zip
op-kernel-dev-8b9cb3a8f39d0864c925c5cd5c2c54cfd85ad551.tar.gz
OMAP2, 3: DSS2: Move clocks from core driver to dss driver
All clock management is moved to dss platform driver. clk_get/put APIs use dss device instead of core platform device. Hwmod adaptation design requires each of the DSS HW IP to be a platform driver. So the device name is changed from omapdss to omapdss_dss in 2420, 2430, 3xxx clock database files. Now the core driver "omapdss" only takes care of panel registration with the custom bus. core driver also uses the clk_enable() / clk_disable() APIs exposed by DSS for clock management. DSS driver would do clock management of clocks needed by DISPC, RFBI, DSI, VENC TODO: The clock content would be adapted to omap_hwmod in a seperate series. Signed-off-by: Senthilvadivu Guruswamy <svadivu@ti.com> Signed-off-by: Sumit Semwal <sumit.semwal@ti.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video/omap2/dss/dss.c')
-rw-r--r--drivers/video/omap2/dss/dss.c334
1 files changed, 333 insertions, 1 deletions
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index 01406f4..72b395d 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -31,6 +31,7 @@
#include <linux/clk.h>
#include <plat/display.h>
+#include <plat/clock.h>
#include "dss.h"
#define DSS_BASE 0x48050000
@@ -61,8 +62,15 @@ struct dss_reg {
static struct {
struct platform_device *pdev;
void __iomem *base;
+ int ctx_id;
struct clk *dpll4_m4_ck;
+ struct clk *dss_ick;
+ struct clk *dss1_fck;
+ struct clk *dss2_fck;
+ struct clk *dss_54m_fck;
+ struct clk *dss_96m_fck;
+ unsigned num_clks_enabled;
unsigned long cache_req_pck;
unsigned long cache_prate;
@@ -75,6 +83,11 @@ static struct {
u32 ctx[DSS_SZ_REGS / sizeof(u32)];
} dss;
+static void dss_clk_enable_all_no_ctx(void);
+static void dss_clk_disable_all_no_ctx(void);
+static void dss_clk_enable_no_ctx(enum dss_clock clks);
+static void dss_clk_disable_no_ctx(enum dss_clock clks);
+
static int _omap_dss_wait_reset(void);
static inline void dss_write_reg(const struct dss_reg idx, u32 val)
@@ -640,6 +653,301 @@ static void dss_exit(void)
iounmap(dss.base);
}
+/* CONTEXT */
+static int dss_get_ctx_id(void)
+{
+ struct omap_display_platform_data *pdata = dss.pdev->dev.platform_data;
+ int r;
+
+ if (!pdata->board_data->get_last_off_on_transaction_id)
+ return 0;
+ r = pdata->board_data->get_last_off_on_transaction_id(&dss.pdev->dev);
+ if (r < 0) {
+ dev_err(&dss.pdev->dev, "getting transaction ID failed, "
+ "will force context restore\n");
+ r = -1;
+ }
+ return r;
+}
+
+int dss_need_ctx_restore(void)
+{
+ int id = dss_get_ctx_id();
+
+ if (id < 0 || id != dss.ctx_id) {
+ DSSDBG("ctx id %d -> id %d\n",
+ dss.ctx_id, id);
+ dss.ctx_id = id;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static void save_all_ctx(void)
+{
+ DSSDBG("save context\n");
+
+ dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1);
+
+ dss_save_context();
+ dispc_save_context();
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_save_context();
+#endif
+
+ dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1);
+}
+
+static void restore_all_ctx(void)
+{
+ DSSDBG("restore context\n");
+
+ dss_clk_enable_all_no_ctx();
+
+ dss_restore_context();
+ dispc_restore_context();
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_restore_context();
+#endif
+
+ dss_clk_disable_all_no_ctx();
+}
+
+static int dss_get_clock(struct clk **clock, const char *clk_name)
+{
+ struct clk *clk;
+
+ clk = clk_get(&dss.pdev->dev, clk_name);
+
+ if (IS_ERR(clk)) {
+ DSSERR("can't get clock %s", clk_name);
+ return PTR_ERR(clk);
+ }
+
+ *clock = clk;
+
+ DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk));
+
+ return 0;
+}
+
+static int dss_get_clocks(void)
+{
+ int r;
+
+ dss.dss_ick = NULL;
+ dss.dss1_fck = NULL;
+ dss.dss2_fck = NULL;
+ dss.dss_54m_fck = NULL;
+ dss.dss_96m_fck = NULL;
+
+ r = dss_get_clock(&dss.dss_ick, "ick");
+ if (r)
+ goto err;
+
+ r = dss_get_clock(&dss.dss1_fck, "dss1_fck");
+ if (r)
+ goto err;
+
+ r = dss_get_clock(&dss.dss2_fck, "dss2_fck");
+ if (r)
+ goto err;
+
+ r = dss_get_clock(&dss.dss_54m_fck, "tv_fck");
+ if (r)
+ goto err;
+
+ r = dss_get_clock(&dss.dss_96m_fck, "video_fck");
+ if (r)
+ goto err;
+
+ return 0;
+
+err:
+ if (dss.dss_ick)
+ clk_put(dss.dss_ick);
+ if (dss.dss1_fck)
+ clk_put(dss.dss1_fck);
+ if (dss.dss2_fck)
+ clk_put(dss.dss2_fck);
+ if (dss.dss_54m_fck)
+ clk_put(dss.dss_54m_fck);
+ if (dss.dss_96m_fck)
+ clk_put(dss.dss_96m_fck);
+
+ return r;
+}
+
+static void dss_put_clocks(void)
+{
+ if (dss.dss_96m_fck)
+ clk_put(dss.dss_96m_fck);
+ clk_put(dss.dss_54m_fck);
+ clk_put(dss.dss1_fck);
+ clk_put(dss.dss2_fck);
+ clk_put(dss.dss_ick);
+}
+
+unsigned long dss_clk_get_rate(enum dss_clock clk)
+{
+ switch (clk) {
+ case DSS_CLK_ICK:
+ return clk_get_rate(dss.dss_ick);
+ case DSS_CLK_FCK1:
+ return clk_get_rate(dss.dss1_fck);
+ case DSS_CLK_FCK2:
+ return clk_get_rate(dss.dss2_fck);
+ case DSS_CLK_54M:
+ return clk_get_rate(dss.dss_54m_fck);
+ case DSS_CLK_96M:
+ return clk_get_rate(dss.dss_96m_fck);
+ }
+
+ BUG();
+ return 0;
+}
+
+static unsigned count_clk_bits(enum dss_clock clks)
+{
+ unsigned num_clks = 0;
+
+ if (clks & DSS_CLK_ICK)
+ ++num_clks;
+ if (clks & DSS_CLK_FCK1)
+ ++num_clks;
+ if (clks & DSS_CLK_FCK2)
+ ++num_clks;
+ if (clks & DSS_CLK_54M)
+ ++num_clks;
+ if (clks & DSS_CLK_96M)
+ ++num_clks;
+
+ return num_clks;
+}
+
+static void dss_clk_enable_no_ctx(enum dss_clock clks)
+{
+ unsigned num_clks = count_clk_bits(clks);
+
+ if (clks & DSS_CLK_ICK)
+ clk_enable(dss.dss_ick);
+ if (clks & DSS_CLK_FCK1)
+ clk_enable(dss.dss1_fck);
+ if (clks & DSS_CLK_FCK2)
+ clk_enable(dss.dss2_fck);
+ if (clks & DSS_CLK_54M)
+ clk_enable(dss.dss_54m_fck);
+ if (clks & DSS_CLK_96M)
+ clk_enable(dss.dss_96m_fck);
+
+ dss.num_clks_enabled += num_clks;
+}
+
+void dss_clk_enable(enum dss_clock clks)
+{
+ bool check_ctx = dss.num_clks_enabled == 0;
+
+ dss_clk_enable_no_ctx(clks);
+
+ if (check_ctx && cpu_is_omap34xx() && dss_need_ctx_restore())
+ restore_all_ctx();
+}
+
+static void dss_clk_disable_no_ctx(enum dss_clock clks)
+{
+ unsigned num_clks = count_clk_bits(clks);
+
+ if (clks & DSS_CLK_ICK)
+ clk_disable(dss.dss_ick);
+ if (clks & DSS_CLK_FCK1)
+ clk_disable(dss.dss1_fck);
+ if (clks & DSS_CLK_FCK2)
+ clk_disable(dss.dss2_fck);
+ if (clks & DSS_CLK_54M)
+ clk_disable(dss.dss_54m_fck);
+ if (clks & DSS_CLK_96M)
+ clk_disable(dss.dss_96m_fck);
+
+ dss.num_clks_enabled -= num_clks;
+}
+
+void dss_clk_disable(enum dss_clock clks)
+{
+ if (cpu_is_omap34xx()) {
+ unsigned num_clks = count_clk_bits(clks);
+
+ BUG_ON(dss.num_clks_enabled < num_clks);
+
+ if (dss.num_clks_enabled == num_clks)
+ save_all_ctx();
+ }
+
+ dss_clk_disable_no_ctx(clks);
+}
+
+static void dss_clk_enable_all_no_ctx(void)
+{
+ enum dss_clock clks;
+
+ clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
+ if (cpu_is_omap34xx())
+ clks |= DSS_CLK_96M;
+ dss_clk_enable_no_ctx(clks);
+}
+
+static void dss_clk_disable_all_no_ctx(void)
+{
+ enum dss_clock clks;
+
+ clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M;
+ if (cpu_is_omap34xx())
+ clks |= DSS_CLK_96M;
+ dss_clk_disable_no_ctx(clks);
+}
+
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+/* CLOCKS */
+static void core_dump_clocks(struct seq_file *s)
+{
+ int i;
+ struct clk *clocks[5] = {
+ dss.dss_ick,
+ dss.dss1_fck,
+ dss.dss2_fck,
+ dss.dss_54m_fck,
+ dss.dss_96m_fck
+ };
+
+ seq_printf(s, "- CORE -\n");
+
+ seq_printf(s, "internal clk count\t\t%u\n", dss.num_clks_enabled);
+
+ for (i = 0; i < 5; i++) {
+ if (!clocks[i])
+ continue;
+ seq_printf(s, "%-15s\t%lu\t%d\n",
+ clocks[i]->name,
+ clk_get_rate(clocks[i]),
+ clocks[i]->usecount);
+ }
+}
+#endif /* defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) */
+
+/* DEBUGFS */
+#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
+void dss_debug_dump_clocks(struct seq_file *s)
+{
+ core_dump_clocks(s);
+ dss_dump_clocks(s);
+ dispc_dump_clocks(s);
+#ifdef CONFIG_OMAP2_DSS_DSI
+ dsi_dump_clocks(s);
+#endif
+}
+#endif
+
+
/* DSS HW IP initialisation */
static int omap_dsshw_probe(struct platform_device *pdev)
{
@@ -648,6 +956,15 @@ static int omap_dsshw_probe(struct platform_device *pdev)
dss.pdev = pdev;
+ r = dss_get_clocks();
+ if (r)
+ goto err_clocks;
+
+ dss_clk_enable_all_no_ctx();
+
+ dss.ctx_id = dss_get_ctx_id();
+ DSSDBG("initial ctx id %u\n", dss.ctx_id);
+
#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
/* DISPC_CONTROL */
if (omap_readl(0x48050440) & 1) /* LCD enabled? */
@@ -660,15 +977,30 @@ static int omap_dsshw_probe(struct platform_device *pdev)
goto err_dss;
}
-err_dss:
+ dss_clk_disable_all_no_ctx();
+ return 0;
+err_dss:
+ dss_clk_disable_all_no_ctx();
+ dss_put_clocks();
+err_clocks:
return r;
}
static int omap_dsshw_remove(struct platform_device *pdev)
{
+
dss_exit();
+ /*
+ * As part of hwmod changes, DSS is not the only controller of dss
+ * clocks; hwmod framework itself will also enable clocks during hwmod
+ * init for dss, and autoidle is set in h/w for DSS. Hence, there's no
+ * need to disable clocks if their usecounts > 1.
+ */
+ WARN_ON(dss.num_clks_enabled > 0);
+
+ dss_put_clocks();
return 0;
}
OpenPOWER on IntegriCloud