diff options
Diffstat (limited to 'drivers/video/omap2/dss')
-rw-r--r-- | drivers/video/omap2/dss/Kconfig | 35 | ||||
-rw-r--r-- | drivers/video/omap2/dss/Makefile | 2 | ||||
-rw-r--r-- | drivers/video/omap2/dss/core.c | 492 | ||||
-rw-r--r-- | drivers/video/omap2/dss/dispc.c | 2169 | ||||
-rw-r--r-- | drivers/video/omap2/dss/dispc.h | 691 | ||||
-rw-r--r-- | drivers/video/omap2/dss/display.c | 132 | ||||
-rw-r--r-- | drivers/video/omap2/dss/dpi.c | 203 | ||||
-rw-r--r-- | drivers/video/omap2/dss/dsi.c | 3086 | ||||
-rw-r--r-- | drivers/video/omap2/dss/dss.c | 592 | ||||
-rw-r--r-- | drivers/video/omap2/dss/dss.h | 253 | ||||
-rw-r--r-- | drivers/video/omap2/dss/dss_features.c | 270 | ||||
-rw-r--r-- | drivers/video/omap2/dss/dss_features.h | 61 | ||||
-rw-r--r-- | drivers/video/omap2/dss/hdmi.c | 1845 | ||||
-rw-r--r-- | drivers/video/omap2/dss/hdmi.h | 631 | ||||
-rw-r--r-- | drivers/video/omap2/dss/hdmi_omap4_panel.c | 222 | ||||
-rw-r--r-- | drivers/video/omap2/dss/manager.c | 368 | ||||
-rw-r--r-- | drivers/video/omap2/dss/overlay.c | 76 | ||||
-rw-r--r-- | drivers/video/omap2/dss/rfbi.c | 374 | ||||
-rw-r--r-- | drivers/video/omap2/dss/sdi.c | 98 | ||||
-rw-r--r-- | drivers/video/omap2/dss/venc.c | 316 |
20 files changed, 8734 insertions, 3182 deletions
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig index 43b64403eaa..0d12524db14 100644 --- a/drivers/video/omap2/dss/Kconfig +++ b/drivers/video/omap2/dss/Kconfig @@ -1,8 +1,8 @@ menuconfig OMAP2_DSS - tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)" - depends on ARCH_OMAP2 || ARCH_OMAP3 + tristate "OMAP2+ Display Subsystem support (EXPERIMENTAL)" + depends on ARCH_OMAP2PLUS help - OMAP2/3 Display Subsystem support. + OMAP2+ Display Subsystem support. if OMAP2_DSS @@ -60,6 +60,14 @@ config OMAP2_DSS_VENC help OMAP Video Encoder support for S-Video and composite TV-out. +config OMAP4_DSS_HDMI + bool "HDMI support" + depends on ARCH_OMAP4 + default y + help + HDMI Interface. This adds the High Definition Multimedia Interface. + See http://www.hdmi.org/ for HDMI specification. + config OMAP2_DSS_SDI bool "SDI support" depends on ARCH_OMAP3 @@ -72,7 +80,7 @@ config OMAP2_DSS_SDI config OMAP2_DSS_DSI bool "DSI support" - depends on ARCH_OMAP3 + depends on ARCH_OMAP3 || ARCH_OMAP4 default n help MIPI DSI (Display Serial Interface) support. @@ -82,14 +90,6 @@ config OMAP2_DSS_DSI See http://www.mipi.org/ for DSI spesifications. -config OMAP2_DSS_USE_DSI_PLL - bool "Use DSI PLL for PCLK (EXPERIMENTAL)" - default n - depends on OMAP2_DSS_DSI - help - Use DSI PLL to generate pixel clock. Currently only for DPI output. - DSI PLL can be used to generate higher and more precise pixel clocks. - config OMAP2_DSS_FAKE_VSYNC bool "Fake VSYNC irq from manual update displays" default n @@ -117,4 +117,15 @@ config OMAP2_DSS_MIN_FCK_PER_PCK Max FCK is 173MHz, so this doesn't work if your PCK is very high. +config OMAP2_DSS_SLEEP_AFTER_VENC_RESET + bool "Sleep 20ms after VENC reset" + default y + help + There is a 20ms sleep after VENC reset which seemed to fix the + reset. The reason for the bug is unclear, and it's also unclear + on what platforms this happens. + + This option enables the sleep, and is enabled by default. You can + disable the sleep if it doesn't cause problems on your platform. + endif diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile index 7db17b5e570..10d9d3bb3e2 100644 --- a/drivers/video/omap2/dss/Makefile +++ b/drivers/video/omap2/dss/Makefile @@ -5,3 +5,5 @@ omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o +omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o \ + hdmi_omap4_panel.o diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 8e89f604928..76821fefce9 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -33,332 +33,29 @@ #include <linux/device.h> #include <linux/regulator/consumer.h> -#include <plat/display.h> -#include <plat/clock.h> +#include <video/omapdss.h> #include "dss.h" #include "dss_features.h" static struct { struct platform_device *pdev; - int ctx_id; - - 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; struct regulator *vdds_dsi_reg; struct regulator *vdds_sdi_reg; - struct regulator *vdda_dac_reg; } core; -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 char *def_disp_name; module_param_named(def_disp, def_disp_name, charp, 0); -MODULE_PARM_DESC(def_disp_name, "default display name"); +MODULE_PARM_DESC(def_disp, "default display name"); #ifdef DEBUG unsigned int dss_debug; module_param_named(debug, dss_debug, bool, 0644); #endif -/* CONTEXT */ -static int dss_get_ctx_id(void) -{ - struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; - int r; - - if (!pdata->get_last_off_on_transaction_id) - return 0; - r = pdata->get_last_off_on_transaction_id(&core.pdev->dev); - if (r < 0) { - dev_err(&core.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 != core.ctx_id) { - DSSDBG("ctx id %d -> id %d\n", - core.ctx_id, id); - core.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(); -} - -#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] = { - core.dss_ick, - core.dss1_fck, - core.dss2_fck, - core.dss_54m_fck, - core.dss_96m_fck - }; - - seq_printf(s, "- CORE -\n"); - - seq_printf(s, "internal clk count\t\t%u\n", core.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) */ - -static int dss_get_clock(struct clk **clock, const char *clk_name) -{ - struct clk *clk; - - clk = clk_get(&core.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; - - core.dss_ick = NULL; - core.dss1_fck = NULL; - core.dss2_fck = NULL; - core.dss_54m_fck = NULL; - core.dss_96m_fck = NULL; - - r = dss_get_clock(&core.dss_ick, "ick"); - if (r) - goto err; - - r = dss_get_clock(&core.dss1_fck, "dss1_fck"); - if (r) - goto err; - - r = dss_get_clock(&core.dss2_fck, "dss2_fck"); - if (r) - goto err; - - r = dss_get_clock(&core.dss_54m_fck, "tv_fck"); - if (r) - goto err; - - r = dss_get_clock(&core.dss_96m_fck, "video_fck"); - if (r) - goto err; - - return 0; - -err: - if (core.dss_ick) - clk_put(core.dss_ick); - if (core.dss1_fck) - clk_put(core.dss1_fck); - if (core.dss2_fck) - clk_put(core.dss2_fck); - if (core.dss_54m_fck) - clk_put(core.dss_54m_fck); - if (core.dss_96m_fck) - clk_put(core.dss_96m_fck); - - return r; -} - -static void dss_put_clocks(void) -{ - if (core.dss_96m_fck) - clk_put(core.dss_96m_fck); - clk_put(core.dss_54m_fck); - clk_put(core.dss1_fck); - clk_put(core.dss2_fck); - clk_put(core.dss_ick); -} - -unsigned long dss_clk_get_rate(enum dss_clock clk) -{ - switch (clk) { - case DSS_CLK_ICK: - return clk_get_rate(core.dss_ick); - case DSS_CLK_FCK1: - return clk_get_rate(core.dss1_fck); - case DSS_CLK_FCK2: - return clk_get_rate(core.dss2_fck); - case DSS_CLK_54M: - return clk_get_rate(core.dss_54m_fck); - case DSS_CLK_96M: - return clk_get_rate(core.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(core.dss_ick); - if (clks & DSS_CLK_FCK1) - clk_enable(core.dss1_fck); - if (clks & DSS_CLK_FCK2) - clk_enable(core.dss2_fck); - if (clks & DSS_CLK_54M) - clk_enable(core.dss_54m_fck); - if (clks & DSS_CLK_96M) - clk_enable(core.dss_96m_fck); - - core.num_clks_enabled += num_clks; -} - -void dss_clk_enable(enum dss_clock clks) -{ - bool check_ctx = core.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(core.dss_ick); - if (clks & DSS_CLK_FCK1) - clk_disable(core.dss1_fck); - if (clks & DSS_CLK_FCK2) - clk_disable(core.dss2_fck); - if (clks & DSS_CLK_54M) - clk_disable(core.dss_54m_fck); - if (clks & DSS_CLK_96M) - clk_disable(core.dss_96m_fck); - - core.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(core.num_clks_enabled < num_clks); - - if (core.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); -} - -static void dss_clk_disable_all(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(clks); -} +static int omap_dss_register_device(struct omap_dss_device *); +static void omap_dss_unregister_device(struct omap_dss_device *); /* REGULATORS */ @@ -390,32 +87,7 @@ struct regulator *dss_get_vdds_sdi(void) return reg; } -struct regulator *dss_get_vdda_dac(void) -{ - struct regulator *reg; - - if (core.vdda_dac_reg != NULL) - return core.vdda_dac_reg; - - reg = regulator_get(&core.pdev->dev, "vdda_dac"); - if (!IS_ERR(reg)) - core.vdda_dac_reg = reg; - - return reg; -} - -/* DEBUGFS */ #if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) -static 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 -} - static int dss_debug_show(struct seq_file *s, void *unused) { void (*func)(struct seq_file *) = s->private; @@ -455,8 +127,7 @@ static int dss_initialize_debugfs(void) #endif #if defined(CONFIG_OMAP2_DSS_DSI) && defined(CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS) - debugfs_create_file("dsi_irq", S_IRUGO, dss_debugfs_dir, - &dsi_dump_irqs, &dss_debug_fops); + dsi_create_debugfs_files_irq(dss_debugfs_dir, &dss_debug_fops); #endif debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir, @@ -468,8 +139,7 @@ static int dss_initialize_debugfs(void) &rfbi_dump_regs, &dss_debug_fops); #endif #ifdef CONFIG_OMAP2_DSS_DSI - debugfs_create_file("dsi", S_IRUGO, dss_debugfs_dir, - &dsi_dump_regs, &dss_debug_fops); + dsi_create_debugfs_files_reg(dss_debugfs_dir, &dss_debug_fops); #endif #ifdef CONFIG_OMAP2_DSS_VENC debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, @@ -497,7 +167,6 @@ static inline void dss_uninitialize_debugfs(void) static int omap_dss_probe(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; - int skip_init = 0; int r; int i; @@ -508,63 +177,40 @@ static int omap_dss_probe(struct platform_device *pdev) dss_init_overlay_managers(pdev); dss_init_overlays(pdev); - r = dss_get_clocks(); - if (r) - goto err_clocks; - - dss_clk_enable_all_no_ctx(); - - core.ctx_id = dss_get_ctx_id(); - DSSDBG("initial ctx id %u\n", core.ctx_id); - -#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT - /* DISPC_CONTROL */ - if (omap_readl(0x48050440) & 1) /* LCD enabled? */ - skip_init = 1; -#endif - - r = dss_init(skip_init); + r = dss_init_platform_driver(); if (r) { - DSSERR("Failed to initialize DSS\n"); + DSSERR("Failed to initialize DSS platform driver\n"); goto err_dss; } - r = rfbi_init(); + r = dispc_init_platform_driver(); if (r) { - DSSERR("Failed to initialize rfbi\n"); - goto err_rfbi; + DSSERR("Failed to initialize dispc platform driver\n"); + goto err_dispc; } - r = dpi_init(pdev); + r = rfbi_init_platform_driver(); if (r) { - DSSERR("Failed to initialize dpi\n"); - goto err_dpi; + DSSERR("Failed to initialize rfbi platform driver\n"); + goto err_rfbi; } - r = dispc_init(); + r = venc_init_platform_driver(); if (r) { - DSSERR("Failed to initialize dispc\n"); - goto err_dispc; + DSSERR("Failed to initialize venc platform driver\n"); + goto err_venc; } - r = venc_init(pdev); + r = dsi_init_platform_driver(); if (r) { - DSSERR("Failed to initialize venc\n"); - goto err_venc; + DSSERR("Failed to initialize DSI platform driver\n"); + goto err_dsi; } - if (cpu_is_omap34xx()) { - r = sdi_init(skip_init); - if (r) { - DSSERR("Failed to initialize SDI\n"); - goto err_sdi; - } - - r = dsi_init(pdev); - if (r) { - DSSERR("Failed to initialize DSI\n"); - goto err_dsi; - } + r = hdmi_init_platform_driver(); + if (r) { + DSSERR("Failed to initialize hdmi\n"); + goto err_hdmi; } r = dss_initialize_debugfs(); @@ -589,32 +235,23 @@ static int omap_dss_probe(struct platform_device *pdev) pdata->default_device = dssdev; } - dss_clk_disable_all(); - return 0; err_register: dss_uninitialize_debugfs(); err_debugfs: - if (cpu_is_omap34xx()) - dsi_exit(); + hdmi_uninit_platform_driver(); +err_hdmi: + dsi_uninit_platform_driver(); err_dsi: - if (cpu_is_omap34xx()) - sdi_exit(); -err_sdi: - venc_exit(); + venc_uninit_platform_driver(); err_venc: - dispc_exit(); + dispc_uninit_platform_driver(); err_dispc: - dpi_exit(); -err_dpi: - rfbi_exit(); + rfbi_uninit_platform_driver(); err_rfbi: - dss_exit(); + dss_uninit_platform_driver(); err_dss: - dss_clk_disable_all_no_ctx(); - dss_put_clocks(); -err_clocks: return r; } @@ -623,61 +260,15 @@ static int omap_dss_remove(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; int i; - int c; dss_uninitialize_debugfs(); - venc_exit(); - dispc_exit(); - dpi_exit(); - rfbi_exit(); - if (cpu_is_omap34xx()) { - dsi_exit(); - sdi_exit(); - } - - dss_exit(); - - /* these should be removed at some point */ - c = core.dss_ick->usecount; - if (c > 0) { - DSSERR("warning: dss_ick usecount %d, disabling\n", c); - while (c-- > 0) - clk_disable(core.dss_ick); - } - - c = core.dss1_fck->usecount; - if (c > 0) { - DSSERR("warning: dss1_fck usecount %d, disabling\n", c); - while (c-- > 0) - clk_disable(core.dss1_fck); - } - - c = core.dss2_fck->usecount; - if (c > 0) { - DSSERR("warning: dss2_fck usecount %d, disabling\n", c); - while (c-- > 0) - clk_disable(core.dss2_fck); - } - - c = core.dss_54m_fck->usecount; - if (c > 0) { - DSSERR("warning: dss_54m_fck usecount %d, disabling\n", c); - while (c-- > 0) - clk_disable(core.dss_54m_fck); - } - - if (core.dss_96m_fck) { - c = core.dss_96m_fck->usecount; - if (c > 0) { - DSSERR("warning: dss_96m_fck usecount %d, disabling\n", - c); - while (c-- > 0) - clk_disable(core.dss_96m_fck); - } - } - - dss_put_clocks(); + hdmi_uninit_platform_driver(); + dsi_uninit_platform_driver(); + venc_uninit_platform_driver(); + rfbi_uninit_platform_driver(); + dispc_uninit_platform_driver(); + dss_uninit_platform_driver(); dss_uninit_overlays(pdev); dss_uninit_overlay_managers(pdev); @@ -885,7 +476,7 @@ static void omap_dss_dev_release(struct device *dev) reset_device(dev, 0); } -int omap_dss_register_device(struct omap_dss_device *dssdev) +static int omap_dss_register_device(struct omap_dss_device *dssdev) { static int dev_num; @@ -899,7 +490,7 @@ int omap_dss_register_device(struct omap_dss_device *dssdev) return device_register(&dssdev->dev); } -void omap_dss_unregister_device(struct omap_dss_device *dssdev) +static void omap_dss_unregister_device(struct omap_dss_device *dssdev) { device_unregister(&dssdev->dev); } @@ -965,11 +556,6 @@ static void __exit omap_dss_exit(void) core.vdds_sdi_reg = NULL; } - if (core.vdda_dac_reg != NULL) { - regulator_put(core.vdda_dac_reg); - core.vdda_dac_reg = NULL; - } - platform_driver_unregister(&omap_dss_driver); omap_dss_bus_unregister(); diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c index 9f8c69f16e6..0f3961a1ce2 100644 --- a/drivers/video/omap2/dss/dispc.c +++ b/drivers/video/omap2/dss/dispc.c @@ -32,104 +32,22 @@ #include <linux/delay.h> #include <linux/workqueue.h> #include <linux/hardirq.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <plat/sram.h> #include <plat/clock.h> -#include <plat/display.h> +#include <video/omapdss.h> #include "dss.h" #include "dss_features.h" +#include "dispc.h" /* DISPC */ -#define DISPC_BASE 0x48050400 - #define DISPC_SZ_REGS SZ_4K -struct dispc_reg { u16 idx; }; - -#define DISPC_REG(idx) ((const struct dispc_reg) { idx }) - -/* - * DISPC common registers and - * DISPC channel registers , ch = 0 for LCD, ch = 1 for - * DIGIT, and ch = 2 for LCD2 - */ -#define DISPC_REVISION DISPC_REG(0x0000) -#define DISPC_SYSCONFIG DISPC_REG(0x0010) -#define DISPC_SYSSTATUS DISPC_REG(0x0014) -#define DISPC_IRQSTATUS DISPC_REG(0x0018) -#define DISPC_IRQENABLE DISPC_REG(0x001C) -#define DISPC_CONTROL DISPC_REG(0x0040) -#define DISPC_CONTROL2 DISPC_REG(0x0238) -#define DISPC_CONFIG DISPC_REG(0x0044) -#define DISPC_CONFIG2 DISPC_REG(0x0620) -#define DISPC_CAPABLE DISPC_REG(0x0048) -#define DISPC_DEFAULT_COLOR(ch) DISPC_REG(ch == 0 ? 0x004C : \ - (ch == 1 ? 0x0050 : 0x03AC)) -#define DISPC_TRANS_COLOR(ch) DISPC_REG(ch == 0 ? 0x0054 : \ - (ch == 1 ? 0x0058 : 0x03B0)) -#define DISPC_LINE_STATUS DISPC_REG(0x005C) -#define DISPC_LINE_NUMBER DISPC_REG(0x0060) -#define DISPC_TIMING_H(ch) DISPC_REG(ch != 2 ? 0x0064 : 0x0400) -#define DISPC_TIMING_V(ch) DISPC_REG(ch != 2 ? 0x0068 : 0x0404) -#define DISPC_POL_FREQ(ch) DISPC_REG(ch != 2 ? 0x006C : 0x0408) -#define DISPC_DIVISOR(ch) DISPC_REG(ch != 2 ? 0x0070 : 0x040C) -#define DISPC_GLOBAL_ALPHA DISPC_REG(0x0074) -#define DISPC_SIZE_DIG DISPC_REG(0x0078) -#define DISPC_SIZE_LCD(ch) DISPC_REG(ch != 2 ? 0x007C : 0x03CC) - -/* DISPC GFX plane */ -#define DISPC_GFX_BA0 DISPC_REG(0x0080) -#define DISPC_GFX_BA1 DISPC_REG(0x0084) -#define DISPC_GFX_POSITION DISPC_REG(0x0088) -#define DISPC_GFX_SIZE DISPC_REG(0x008C) -#define DISPC_GFX_ATTRIBUTES DISPC_REG(0x00A0) -#define DISPC_GFX_FIFO_THRESHOLD DISPC_REG(0x00A4) -#define DISPC_GFX_FIFO_SIZE_STATUS DISPC_REG(0x00A8) -#define DISPC_GFX_ROW_INC DISPC_REG(0x00AC) -#define DISPC_GFX_PIXEL_INC DISPC_REG(0x00B0) -#define DISPC_GFX_WINDOW_SKIP DISPC_REG(0x00B4) -#define DISPC_GFX_TABLE_BA DISPC_REG(0x00B8) - -#define DISPC_DATA_CYCLE1(ch) DISPC_REG(ch != 2 ? 0x01D4 : 0x03C0) -#define DISPC_DATA_CYCLE2(ch) DISPC_REG(ch != 2 ? 0x01D8 : 0x03C4) -#define DISPC_DATA_CYCLE3(ch) DISPC_REG(ch != 2 ? 0x01DC : 0x03C8) -#define DISPC_CPR_COEF_R(ch) DISPC_REG(ch != 2 ? 0x0220 : 0x03BC) -#define DISPC_CPR_COEF_G(ch) DISPC_REG(ch != 2 ? 0x0224 : 0x03B8) -#define DISPC_CPR_COEF_B(ch) DISPC_REG(ch != 2 ? 0x0228 : 0x03B4) - -#define DISPC_GFX_PRELOAD DISPC_REG(0x022C) - -/* DISPC Video plane, n = 0 for VID1 and n = 1 for VID2 */ -#define DISPC_VID_REG(n, idx) DISPC_REG(0x00BC + (n)*0x90 + idx) - -#define DISPC_VID_BA0(n) DISPC_VID_REG(n, 0x0000) -#define DISPC_VID_BA1(n) DISPC_VID_REG(n, 0x0004) -#define DISPC_VID_POSITION(n) DISPC_VID_REG(n, 0x0008) -#define DISPC_VID_SIZE(n) DISPC_VID_REG(n, 0x000C) -#define DISPC_VID_ATTRIBUTES(n) DISPC_VID_REG(n, 0x0010) -#define DISPC_VID_FIFO_THRESHOLD(n) DISPC_VID_REG(n, 0x0014) -#define DISPC_VID_FIFO_SIZE_STATUS(n) DISPC_VID_REG(n, 0x0018) -#define DISPC_VID_ROW_INC(n) DISPC_VID_REG(n, 0x001C) -#define DISPC_VID_PIXEL_INC(n) DISPC_VID_REG(n, 0x0020) -#define DISPC_VID_FIR(n) DISPC_VID_REG(n, 0x0024) -#define DISPC_VID_PICTURE_SIZE(n) DISPC_VID_REG(n, 0x0028) -#define DISPC_VID_ACCU0(n) DISPC_VID_REG(n, 0x002C) -#define DISPC_VID_ACCU1(n) DISPC_VID_REG(n, 0x0030) - -/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ -#define DISPC_VID_FIR_COEF_H(n, i) DISPC_REG(0x00F0 + (n)*0x90 + (i)*0x8) -/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ -#define DISPC_VID_FIR_COEF_HV(n, i) DISPC_REG(0x00F4 + (n)*0x90 + (i)*0x8) -/* coef index i = {0, 1, 2, 3, 4} */ -#define DISPC_VID_CONV_COEF(n, i) DISPC_REG(0x0130 + (n)*0x90 + (i)*0x4) -/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ -#define DISPC_VID_FIR_COEF_V(n, i) DISPC_REG(0x01E0 + (n)*0x20 + (i)*0x4) - -#define DISPC_VID_PRELOAD(n) DISPC_REG(0x230 + (n)*0x04) - - #define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ DISPC_IRQ_OCP_ERR | \ DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ @@ -161,16 +79,18 @@ struct dispc_v_coef { s8 vc00; }; +enum omap_burst_size { + BURST_SIZE_X2 = 0, + BURST_SIZE_X4 = 1, + BURST_SIZE_X8 = 2, +}; + #define REG_GET(idx, start, end) \ FLD_GET(dispc_read_reg(idx), start, end) #define REG_FLD_MOD(idx, val, start, end) \ dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end)) -static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES, - DISPC_VID_ATTRIBUTES(0), - DISPC_VID_ATTRIBUTES(1) }; - struct dispc_irq_stats { unsigned long last_reset; unsigned irq_count; @@ -178,8 +98,14 @@ struct dispc_irq_stats { }; static struct { + struct platform_device *pdev; void __iomem *base; + int ctx_loss_cnt; + + int irq; + struct clk *dss_clk; + u32 fifo_size[3]; spinlock_t irq_lock; @@ -188,6 +114,7 @@ static struct { u32 error_irqs; struct work_struct error_work; + bool ctx_valid; u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS @@ -196,341 +123,412 @@ static struct { #endif } dispc; +enum omap_color_component { + /* used for all color formats for OMAP3 and earlier + * and for RGB and Y color component on OMAP4 + */ + DISPC_COLOR_COMPONENT_RGB_Y = 1 << 0, + /* used for UV component for + * OMAP_DSS_COLOR_YUV2, OMAP_DSS_COLOR_UYVY, OMAP_DSS_COLOR_NV12 + * color formats on OMAP4 + */ + DISPC_COLOR_COMPONENT_UV = 1 << 1, +}; + static void _omap_dispc_set_irqs(void); -static inline void dispc_write_reg(const struct dispc_reg idx, u32 val) +static inline void dispc_write_reg(const u16 idx, u32 val) +{ + __raw_writel(val, dispc.base + idx); +} + +static inline u32 dispc_read_reg(const u16 idx) { - __raw_writel(val, dispc.base + idx.idx); + return __raw_readl(dispc.base + idx); } -static inline u32 dispc_read_reg(const struct dispc_reg idx) +static int dispc_get_ctx_loss_count(void) { - return __raw_readl(dispc.base + idx.idx); + struct device *dev = &dispc.pdev->dev; + struct omap_display_platform_data *pdata = dev->platform_data; + struct omap_dss_board_info *board_data = pdata->board_data; + int cnt; + + if (!board_data->get_context_loss_count) + return -ENOENT; + + cnt = board_data->get_context_loss_count(dev); + + WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt); + + return cnt; } #define SR(reg) \ - dispc.ctx[(DISPC_##reg).idx / sizeof(u32)] = dispc_read_reg(DISPC_##reg) + dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg) #define RR(reg) \ - dispc_write_reg(DISPC_##reg, dispc.ctx[(DISPC_##reg).idx / sizeof(u32)]) + dispc_write_reg(DISPC_##reg, dispc.ctx[DISPC_##reg / sizeof(u32)]) -void dispc_save_context(void) +static void dispc_save_context(void) { - if (cpu_is_omap24xx()) - return; + int i; + + DSSDBG("dispc_save_context\n"); - SR(SYSCONFIG); SR(IRQENABLE); SR(CONTROL); SR(CONFIG); - SR(DEFAULT_COLOR(0)); - SR(DEFAULT_COLOR(1)); - SR(TRANS_COLOR(0)); - SR(TRANS_COLOR(1)); + SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD)); + SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT)); + SR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD)); + SR(TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT)); SR(LINE_NUMBER); - SR(TIMING_H(0)); - SR(TIMING_V(0)); - SR(POL_FREQ(0)); - SR(DIVISOR(0)); - SR(GLOBAL_ALPHA); - SR(SIZE_DIG); - SR(SIZE_LCD(0)); + SR(TIMING_H(OMAP_DSS_CHANNEL_LCD)); + SR(TIMING_V(OMAP_DSS_CHANNEL_LCD)); + SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD)); + SR(DIVISORo(OMAP_DSS_CHANNEL_LCD)); + if (dss_has_feature(FEAT_GLOBAL_ALPHA)) + SR(GLOBAL_ALPHA); + SR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT)); + SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD)); if (dss_has_feature(FEAT_MGR_LCD2)) { SR(CONTROL2); - SR(DEFAULT_COLOR(2)); - SR(TRANS_COLOR(2)); - SR(SIZE_LCD(2)); - SR(TIMING_H(2)); - SR(TIMING_V(2)); - SR(POL_FREQ(2)); - SR(DIVISOR(2)); + SR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2)); + SR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2)); + SR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD2)); + SR(TIMING_H(OMAP_DSS_CHANNEL_LCD2)); + SR(TIMING_V(OMAP_DSS_CHANNEL_LCD2)); + SR(POL_FREQ(OMAP_DSS_CHANNEL_LCD2)); + SR(DIVISORo(OMAP_DSS_CHANNEL_LCD2)); SR(CONFIG2); } - SR(GFX_BA0); - SR(GFX_BA1); - SR(GFX_POSITION); - SR(GFX_SIZE); - SR(GFX_ATTRIBUTES); - SR(GFX_FIFO_THRESHOLD); - SR(GFX_ROW_INC); - SR(GFX_PIXEL_INC); - SR(GFX_WINDOW_SKIP); - SR(GFX_TABLE_BA); - - SR(DATA_CYCLE1(0)); - SR(DATA_CYCLE2(0)); - SR(DATA_CYCLE3(0)); - - SR(CPR_COEF_R(0)); - SR(CPR_COEF_G(0)); - SR(CPR_COEF_B(0)); + SR(OVL_BA0(OMAP_DSS_GFX)); + SR(OVL_BA1(OMAP_DSS_GFX)); + SR(OVL_POSITION(OMAP_DSS_GFX)); + SR(OVL_SIZE(OMAP_DSS_GFX)); + SR(OVL_ATTRIBUTES(OMAP_DSS_GFX)); + SR(OVL_FIFO_THRESHOLD(OMAP_DSS_GFX)); + SR(OVL_ROW_INC(OMAP_DSS_GFX)); + SR(OVL_PIXEL_INC(OMAP_DSS_GFX)); + SR(OVL_WINDOW_SKIP(OMAP_DSS_GFX)); + SR(OVL_TABLE_BA(OMAP_DSS_GFX)); + + SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD)); + SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD)); + SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); + + if (dss_has_feature(FEAT_CPR)) { + SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); + SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); + SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); + } if (dss_has_feature(FEAT_MGR_LCD2)) { - SR(CPR_COEF_B(2)); - SR(CPR_COEF_G(2)); - SR(CPR_COEF_R(2)); + if (dss_has_feature(FEAT_CPR)) { + SR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); + SR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); + SR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); + } - SR(DATA_CYCLE1(2)); - SR(DATA_CYCLE2(2)); - SR(DATA_CYCLE3(2)); + SR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2)); + SR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2)); + SR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); } - SR(GFX_PRELOAD); + if (dss_has_feature(FEAT_PRELOAD)) + SR(OVL_PRELOAD(OMAP_DSS_GFX)); /* VID1 */ - SR(VID_BA0(0)); - SR(VID_BA1(0)); - SR(VID_POSITION(0)); - SR(VID_SIZE(0)); - SR(VID_ATTRIBUTES(0)); - SR(VID_FIFO_THRESHOLD(0)); - SR(VID_ROW_INC(0)); - SR(VID_PIXEL_INC(0)); - SR(VID_FIR(0)); - SR(VID_PICTURE_SIZE(0)); - SR(VID_ACCU0(0)); - SR(VID_ACCU1(0)); - - SR(VID_FIR_COEF_H(0, 0)); - SR(VID_FIR_COEF_H(0, 1)); - SR(VID_FIR_COEF_H(0, 2)); - SR(VID_FIR_COEF_H(0, 3)); - SR(VID_FIR_COEF_H(0, 4)); - SR(VID_FIR_COEF_H(0, 5)); - SR(VID_FIR_COEF_H(0, 6)); - SR(VID_FIR_COEF_H(0, 7)); - - SR(VID_FIR_COEF_HV(0, 0)); - SR(VID_FIR_COEF_HV(0, 1)); - SR(VID_FIR_COEF_HV(0, 2)); - SR(VID_FIR_COEF_HV(0, 3)); - SR(VID_FIR_COEF_HV(0, 4)); - SR(VID_FIR_COEF_HV(0, 5)); - SR(VID_FIR_COEF_HV(0, 6)); - SR(VID_FIR_COEF_HV(0, 7)); - - SR(VID_CONV_COEF(0, 0)); - SR(VID_CONV_COEF(0, 1)); - SR(VID_CONV_COEF(0, 2)); - SR(VID_CONV_COEF(0, 3)); - SR(VID_CONV_COEF(0, 4)); - - SR(VID_FIR_COEF_V(0, 0)); - SR(VID_FIR_COEF_V(0, 1)); - SR(VID_FIR_COEF_V(0, 2)); - SR(VID_FIR_COEF_V(0, 3)); - SR(VID_FIR_COEF_V(0, 4)); - SR(VID_FIR_COEF_V(0, 5)); - SR(VID_FIR_COEF_V(0, 6)); - SR(VID_FIR_COEF_V(0, 7)); - - SR(VID_PRELOAD(0)); + SR(OVL_BA0(OMAP_DSS_VIDEO1)); + SR(OVL_BA1(OMAP_DSS_VIDEO1)); + SR(OVL_POSITION(OMAP_DSS_VIDEO1)); + SR(OVL_SIZE(OMAP_DSS_VIDEO1)); + SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1)); + SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1)); + SR(OVL_ROW_INC(OMAP_DSS_VIDEO1)); + SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1)); + SR(OVL_FIR(OMAP_DSS_VIDEO1)); + SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1)); + SR(OVL_ACCU0(OMAP_DSS_VIDEO1)); + SR(OVL_ACCU1(OMAP_DSS_VIDEO1)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 5; i++) + SR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i)); + + if (dss_has_feature(FEAT_FIR_COEF_V)) { + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); + } + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + SR(OVL_BA0_UV(OMAP_DSS_VIDEO1)); + SR(OVL_BA1_UV(OMAP_DSS_VIDEO1)); + SR(OVL_FIR2(OMAP_DSS_VIDEO1)); + SR(OVL_ACCU2_0(OMAP_DSS_VIDEO1)); + SR(OVL_ACCU2_1(OMAP_DSS_VIDEO1)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i)); + } + if (dss_has_feature(FEAT_ATTR2)) + SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); + + if (dss_has_feature(FEAT_PRELOAD)) + SR(OVL_PRELOAD(OMAP_DSS_VIDEO1)); /* VID2 */ - SR(VID_BA0(1)); - SR(VID_BA1(1)); - SR(VID_POSITION(1)); - SR(VID_SIZE(1)); - SR(VID_ATTRIBUTES(1)); - SR(VID_FIFO_THRESHOLD(1)); - SR(VID_ROW_INC(1)); - SR(VID_PIXEL_INC(1)); - SR(VID_FIR(1)); - SR(VID_PICTURE_SIZE(1)); - SR(VID_ACCU0(1)); - SR(VID_ACCU1(1)); - - SR(VID_FIR_COEF_H(1, 0)); - SR(VID_FIR_COEF_H(1, 1)); - SR(VID_FIR_COEF_H(1, 2)); - SR(VID_FIR_COEF_H(1, 3)); - SR(VID_FIR_COEF_H(1, 4)); - SR(VID_FIR_COEF_H(1, 5)); - SR(VID_FIR_COEF_H(1, 6)); - SR(VID_FIR_COEF_H(1, 7)); - - SR(VID_FIR_COEF_HV(1, 0)); - SR(VID_FIR_COEF_HV(1, 1)); - SR(VID_FIR_COEF_HV(1, 2)); - SR(VID_FIR_COEF_HV(1, 3)); - SR(VID_FIR_COEF_HV(1, 4)); - SR(VID_FIR_COEF_HV(1, 5)); - SR(VID_FIR_COEF_HV(1, 6)); - SR(VID_FIR_COEF_HV(1, 7)); - - SR(VID_CONV_COEF(1, 0)); - SR(VID_CONV_COEF(1, 1)); - SR(VID_CONV_COEF(1, 2)); - SR(VID_CONV_COEF(1, 3)); - SR(VID_CONV_COEF(1, 4)); - - SR(VID_FIR_COEF_V(1, 0)); - SR(VID_FIR_COEF_V(1, 1)); - SR(VID_FIR_COEF_V(1, 2)); - SR(VID_FIR_COEF_V(1, 3)); - SR(VID_FIR_COEF_V(1, 4)); - SR(VID_FIR_COEF_V(1, 5)); - SR(VID_FIR_COEF_V(1, 6)); - SR(VID_FIR_COEF_V(1, 7)); - - SR(VID_PRELOAD(1)); -} - -void dispc_restore_context(void) -{ - RR(SYSCONFIG); + SR(OVL_BA0(OMAP_DSS_VIDEO2)); + SR(OVL_BA1(OMAP_DSS_VIDEO2)); + SR(OVL_POSITION(OMAP_DSS_VIDEO2)); + SR(OVL_SIZE(OMAP_DSS_VIDEO2)); + SR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2)); + SR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2)); + SR(OVL_ROW_INC(OMAP_DSS_VIDEO2)); + SR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2)); + SR(OVL_FIR(OMAP_DSS_VIDEO2)); + SR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2)); + SR(OVL_ACCU0(OMAP_DSS_VIDEO2)); + SR(OVL_ACCU1(OMAP_DSS_VIDEO2)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 5; i++) + SR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i)); + + if (dss_has_feature(FEAT_FIR_COEF_V)) { + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); + } + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + SR(OVL_BA0_UV(OMAP_DSS_VIDEO2)); + SR(OVL_BA1_UV(OMAP_DSS_VIDEO2)); + SR(OVL_FIR2(OMAP_DSS_VIDEO2)); + SR(OVL_ACCU2_0(OMAP_DSS_VIDEO2)); + SR(OVL_ACCU2_1(OMAP_DSS_VIDEO2)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + SR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i)); + } + if (dss_has_feature(FEAT_ATTR2)) + SR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); + + if (dss_has_feature(FEAT_PRELOAD)) + SR(OVL_PRELOAD(OMAP_DSS_VIDEO2)); + + if (dss_has_feature(FEAT_CORE_CLK_DIV)) + SR(DIVISOR); + + dispc.ctx_loss_cnt = dispc_get_ctx_loss_count(); + dispc.ctx_valid = true; + + DSSDBG("context saved, ctx_loss_count %d\n", dispc.ctx_loss_cnt); +} + +static void dispc_restore_context(void) +{ + int i, ctx; + + DSSDBG("dispc_restore_context\n"); + + if (!dispc.ctx_valid) + return; + + ctx = dispc_get_ctx_loss_count(); + + if (ctx >= 0 && ctx == dispc.ctx_loss_cnt) + return; + + DSSDBG("ctx_loss_count: saved %d, current %d\n", + dispc.ctx_loss_cnt, ctx); + /*RR(IRQENABLE);*/ /*RR(CONTROL);*/ RR(CONFIG); - RR(DEFAULT_COLOR(0)); - RR(DEFAULT_COLOR(1)); - RR(TRANS_COLOR(0)); - RR(TRANS_COLOR(1)); + RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD)); + RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT)); + RR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD)); + RR(TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT)); RR(LINE_NUMBER); - RR(TIMING_H(0)); - RR(TIMING_V(0)); - RR(POL_FREQ(0)); - RR(DIVISOR(0)); - RR(GLOBAL_ALPHA); - RR(SIZE_DIG); - RR(SIZE_LCD(0)); + RR(TIMING_H(OMAP_DSS_CHANNEL_LCD)); + RR(TIMING_V(OMAP_DSS_CHANNEL_LCD)); + RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD)); + RR(DIVISORo(OMAP_DSS_CHANNEL_LCD)); + if (dss_has_feature(FEAT_GLOBAL_ALPHA)) + RR(GLOBAL_ALPHA); + RR(SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT)); + RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD)); if (dss_has_feature(FEAT_MGR_LCD2)) { - RR(DEFAULT_COLOR(2)); - RR(TRANS_COLOR(2)); - RR(SIZE_LCD(2)); - RR(TIMING_H(2)); - RR(TIMING_V(2)); - RR(POL_FREQ(2)); - RR(DIVISOR(2)); + RR(DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2)); + RR(TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2)); + RR(SIZE_MGR(OMAP_DSS_CHANNEL_LCD2)); + RR(TIMING_H(OMAP_DSS_CHANNEL_LCD2)); + RR(TIMING_V(OMAP_DSS_CHANNEL_LCD2)); + RR(POL_FREQ(OMAP_DSS_CHANNEL_LCD2)); + RR(DIVISORo(OMAP_DSS_CHANNEL_LCD2)); RR(CONFIG2); } - RR(GFX_BA0); - RR(GFX_BA1); - RR(GFX_POSITION); - RR(GFX_SIZE); - RR(GFX_ATTRIBUTES); - RR(GFX_FIFO_THRESHOLD); - RR(GFX_ROW_INC); - RR(GFX_PIXEL_INC); - RR(GFX_WINDOW_SKIP); - RR(GFX_TABLE_BA); - - RR(DATA_CYCLE1(0)); - RR(DATA_CYCLE2(0)); - RR(DATA_CYCLE3(0)); - - RR(CPR_COEF_R(0)); - RR(CPR_COEF_G(0)); - RR(CPR_COEF_B(0)); + RR(OVL_BA0(OMAP_DSS_GFX)); + RR(OVL_BA1(OMAP_DSS_GFX)); + RR(OVL_POSITION(OMAP_DSS_GFX)); + RR(OVL_SIZE(OMAP_DSS_GFX)); + RR(OVL_ATTRIBUTES(OMAP_DSS_GFX)); + RR(OVL_FIFO_THRESHOLD(OMAP_DSS_GFX)); + RR(OVL_ROW_INC(OMAP_DSS_GFX)); + RR(OVL_PIXEL_INC(OMAP_DSS_GFX)); + RR(OVL_WINDOW_SKIP(OMAP_DSS_GFX)); + RR(OVL_TABLE_BA(OMAP_DSS_GFX)); + + + RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD)); + RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD)); + RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); + + if (dss_has_feature(FEAT_CPR)) { + RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); + RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); + RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); + } if (dss_has_feature(FEAT_MGR_LCD2)) { - RR(DATA_CYCLE1(2)); - RR(DATA_CYCLE2(2)); - RR(DATA_CYCLE3(2)); - - RR(CPR_COEF_B(2)); - RR(CPR_COEF_G(2)); - RR(CPR_COEF_R(2)); + RR(DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2)); + RR(DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2)); + RR(DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); + + if (dss_has_feature(FEAT_CPR)) { + RR(CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); + RR(CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); + RR(CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); + } } - RR(GFX_PRELOAD); + if (dss_has_feature(FEAT_PRELOAD)) + RR(OVL_PRELOAD(OMAP_DSS_GFX)); /* VID1 */ - RR(VID_BA0(0)); - RR(VID_BA1(0)); - RR(VID_POSITION(0)); - RR(VID_SIZE(0)); - RR(VID_ATTRIBUTES(0)); - RR(VID_FIFO_THRESHOLD(0)); - RR(VID_ROW_INC(0)); - RR(VID_PIXEL_INC(0)); - RR(VID_FIR(0)); - RR(VID_PICTURE_SIZE(0)); - RR(VID_ACCU0(0)); - RR(VID_ACCU1(0)); - - RR(VID_FIR_COEF_H(0, 0)); - RR(VID_FIR_COEF_H(0, 1)); - RR(VID_FIR_COEF_H(0, 2)); - RR(VID_FIR_COEF_H(0, 3)); - RR(VID_FIR_COEF_H(0, 4)); - RR(VID_FIR_COEF_H(0, 5)); - RR(VID_FIR_COEF_H(0, 6)); - RR(VID_FIR_COEF_H(0, 7)); - - RR(VID_FIR_COEF_HV(0, 0)); - RR(VID_FIR_COEF_HV(0, 1)); - RR(VID_FIR_COEF_HV(0, 2)); - RR(VID_FIR_COEF_HV(0, 3)); - RR(VID_FIR_COEF_HV(0, 4)); - RR(VID_FIR_COEF_HV(0, 5)); - RR(VID_FIR_COEF_HV(0, 6)); - RR(VID_FIR_COEF_HV(0, 7)); - - RR(VID_CONV_COEF(0, 0)); - RR(VID_CONV_COEF(0, 1)); - RR(VID_CONV_COEF(0, 2)); - RR(VID_CONV_COEF(0, 3)); - RR(VID_CONV_COEF(0, 4)); - - RR(VID_FIR_COEF_V(0, 0)); - RR(VID_FIR_COEF_V(0, 1)); - RR(VID_FIR_COEF_V(0, 2)); - RR(VID_FIR_COEF_V(0, 3)); - RR(VID_FIR_COEF_V(0, 4)); - RR(VID_FIR_COEF_V(0, 5)); - RR(VID_FIR_COEF_V(0, 6)); - RR(VID_FIR_COEF_V(0, 7)); - - RR(VID_PRELOAD(0)); + RR(OVL_BA0(OMAP_DSS_VIDEO1)); + RR(OVL_BA1(OMAP_DSS_VIDEO1)); + RR(OVL_POSITION(OMAP_DSS_VIDEO1)); + RR(OVL_SIZE(OMAP_DSS_VIDEO1)); + RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO1)); + RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1)); + RR(OVL_ROW_INC(OMAP_DSS_VIDEO1)); + RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO1)); + RR(OVL_FIR(OMAP_DSS_VIDEO1)); + RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1)); + RR(OVL_ACCU0(OMAP_DSS_VIDEO1)); + RR(OVL_ACCU1(OMAP_DSS_VIDEO1)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 5; i++) + RR(OVL_CONV_COEF(OMAP_DSS_VIDEO1, i)); + + if (dss_has_feature(FEAT_FIR_COEF_V)) { + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, i)); + } + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + RR(OVL_BA0_UV(OMAP_DSS_VIDEO1)); + RR(OVL_BA1_UV(OMAP_DSS_VIDEO1)); + RR(OVL_FIR2(OMAP_DSS_VIDEO1)); + RR(OVL_ACCU2_0(OMAP_DSS_VIDEO1)); + RR(OVL_ACCU2_1(OMAP_DSS_VIDEO1)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, i)); + } + if (dss_has_feature(FEAT_ATTR2)) + RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); + + if (dss_has_feature(FEAT_PRELOAD)) + RR(OVL_PRELOAD(OMAP_DSS_VIDEO1)); /* VID2 */ - RR(VID_BA0(1)); - RR(VID_BA1(1)); - RR(VID_POSITION(1)); - RR(VID_SIZE(1)); - RR(VID_ATTRIBUTES(1)); - RR(VID_FIFO_THRESHOLD(1)); - RR(VID_ROW_INC(1)); - RR(VID_PIXEL_INC(1)); - RR(VID_FIR(1)); - RR(VID_PICTURE_SIZE(1)); - RR(VID_ACCU0(1)); - RR(VID_ACCU1(1)); - - RR(VID_FIR_COEF_H(1, 0)); - RR(VID_FIR_COEF_H(1, 1)); - RR(VID_FIR_COEF_H(1, 2)); - RR(VID_FIR_COEF_H(1, 3)); - RR(VID_FIR_COEF_H(1, 4)); - RR(VID_FIR_COEF_H(1, 5)); - RR(VID_FIR_COEF_H(1, 6)); - RR(VID_FIR_COEF_H(1, 7)); - - RR(VID_FIR_COEF_HV(1, 0)); - RR(VID_FIR_COEF_HV(1, 1)); - RR(VID_FIR_COEF_HV(1, 2)); - RR(VID_FIR_COEF_HV(1, 3)); - RR(VID_FIR_COEF_HV(1, 4)); - RR(VID_FIR_COEF_HV(1, 5)); - RR(VID_FIR_COEF_HV(1, 6)); - RR(VID_FIR_COEF_HV(1, 7)); - - RR(VID_CONV_COEF(1, 0)); - RR(VID_CONV_COEF(1, 1)); - RR(VID_CONV_COEF(1, 2)); - RR(VID_CONV_COEF(1, 3)); - RR(VID_CONV_COEF(1, 4)); - - RR(VID_FIR_COEF_V(1, 0)); - RR(VID_FIR_COEF_V(1, 1)); - RR(VID_FIR_COEF_V(1, 2)); - RR(VID_FIR_COEF_V(1, 3)); - RR(VID_FIR_COEF_V(1, 4)); - RR(VID_FIR_COEF_V(1, 5)); - RR(VID_FIR_COEF_V(1, 6)); - RR(VID_FIR_COEF_V(1, 7)); - - RR(VID_PRELOAD(1)); + RR(OVL_BA0(OMAP_DSS_VIDEO2)); + RR(OVL_BA1(OMAP_DSS_VIDEO2)); + RR(OVL_POSITION(OMAP_DSS_VIDEO2)); + RR(OVL_SIZE(OMAP_DSS_VIDEO2)); + RR(OVL_ATTRIBUTES(OMAP_DSS_VIDEO2)); + RR(OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2)); + RR(OVL_ROW_INC(OMAP_DSS_VIDEO2)); + RR(OVL_PIXEL_INC(OMAP_DSS_VIDEO2)); + RR(OVL_FIR(OMAP_DSS_VIDEO2)); + RR(OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2)); + RR(OVL_ACCU0(OMAP_DSS_VIDEO2)); + RR(OVL_ACCU1(OMAP_DSS_VIDEO2)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 5; i++) + RR(OVL_CONV_COEF(OMAP_DSS_VIDEO2, i)); + + if (dss_has_feature(FEAT_FIR_COEF_V)) { + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, i)); + } + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + RR(OVL_BA0_UV(OMAP_DSS_VIDEO2)); + RR(OVL_BA1_UV(OMAP_DSS_VIDEO2)); + RR(OVL_FIR2(OMAP_DSS_VIDEO2)); + RR(OVL_ACCU2_0(OMAP_DSS_VIDEO2)); + RR(OVL_ACCU2_1(OMAP_DSS_VIDEO2)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, i)); + + for (i = 0; i < 8; i++) + RR(OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, i)); + } + if (dss_has_feature(FEAT_ATTR2)) + RR(OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); + + if (dss_has_feature(FEAT_PRELOAD)) + RR(OVL_PRELOAD(OMAP_DSS_VIDEO2)); + + if (dss_has_feature(FEAT_CORE_CLK_DIV)) + RR(DIVISOR); /* enable last, because LCD & DIGIT enable are here */ RR(CONTROL); @@ -544,19 +542,35 @@ void dispc_restore_context(void) * the context is fully restored */ RR(IRQENABLE); + + DSSDBG("context restored\n"); } #undef SR #undef RR -static inline void enable_clocks(bool enable) +int dispc_runtime_get(void) { - if (enable) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - else - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + int r; + + DSSDBG("dispc_runtime_get\n"); + + r = pm_runtime_get_sync(&dispc.pdev->dev); + WARN_ON(r < 0); + return r < 0 ? r : 0; +} + +void dispc_runtime_put(void) +{ + int r; + + DSSDBG("dispc_runtime_put\n"); + + r = pm_runtime_put(&dispc.pdev->dev); + WARN_ON(r < 0); } + bool dispc_go_busy(enum omap_channel channel) { int bit; @@ -578,8 +592,6 @@ void dispc_go(enum omap_channel channel) int bit; bool enable_bit, go_bit; - enable_clocks(1); - if (channel == OMAP_DSS_CHANNEL_LCD || channel == OMAP_DSS_CHANNEL_LCD2) bit = 0; /* LCDENABLE */ @@ -593,7 +605,7 @@ void dispc_go(enum omap_channel channel) enable_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1; if (!enable_bit) - goto end; + return; if (channel == OMAP_DSS_CHANNEL_LCD || channel == OMAP_DSS_CHANNEL_LCD2) @@ -608,7 +620,7 @@ void dispc_go(enum omap_channel channel) if (go_bit) { DSSERR("GO bit not down for channel %d\n", channel); - goto end; + return; } DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : @@ -618,33 +630,47 @@ void dispc_go(enum omap_channel channel) REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit); else REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); -end: - enable_clocks(0); } static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) { + dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value); +} + +static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +{ + dispc_write_reg(DISPC_OVL_FIR_COEF_HV(plane, reg), value); +} + +static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) +{ + dispc_write_reg(DISPC_OVL_FIR_COEF_V(plane, reg), value); +} + +static void _dispc_write_firh2_reg(enum omap_plane plane, int reg, u32 value) +{ BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); + dispc_write_reg(DISPC_OVL_FIR_COEF_H2(plane, reg), value); } -static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) +static void _dispc_write_firhv2_reg(enum omap_plane plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); + dispc_write_reg(DISPC_OVL_FIR_COEF_HV2(plane, reg), value); } -static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) +static void _dispc_write_firv2_reg(enum omap_plane plane, int reg, u32 value) { BUG_ON(plane == OMAP_DSS_GFX); - dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value); + dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value); } static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, - int vscaleup, int five_taps) + int vscaleup, int five_taps, + enum omap_color_component color_comp) { /* Coefficients for horizontal up-sampling */ static const struct dispc_h_coef coef_hup[8] = { @@ -742,8 +768,14 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, | FLD_VAL(v_coef[i].vc1, 23, 16) | FLD_VAL(v_coef[i].vc2, 31, 24); - _dispc_write_firh_reg(plane, i, h); - _dispc_write_firhv_reg(plane, i, hv); + if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) { + _dispc_write_firh_reg(plane, i, h); + _dispc_write_firhv_reg(plane, i, hv); + } else { + _dispc_write_firh2_reg(plane, i, h); + _dispc_write_firhv2_reg(plane, i, hv); + } + } if (five_taps) { @@ -751,7 +783,10 @@ static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, u32 v; v = FLD_VAL(v_coef[i].vc00, 7, 0) | FLD_VAL(v_coef[i].vc22, 15, 8); - _dispc_write_firv_reg(plane, i, v); + if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) + _dispc_write_firv_reg(plane, i, v); + else + _dispc_write_firv2_reg(plane, i, v); } } } @@ -771,72 +806,83 @@ static void _dispc_setup_color_conv_coef(void) ct = &ctbl_bt601_5; - dispc_write_reg(DISPC_VID_CONV_COEF(0, 0), CVAL(ct->rcr, ct->ry)); - dispc_write_reg(DISPC_VID_CONV_COEF(0, 1), CVAL(ct->gy, ct->rcb)); - dispc_write_reg(DISPC_VID_CONV_COEF(0, 2), CVAL(ct->gcb, ct->gcr)); - dispc_write_reg(DISPC_VID_CONV_COEF(0, 3), CVAL(ct->bcr, ct->by)); - dispc_write_reg(DISPC_VID_CONV_COEF(0, 4), CVAL(0, ct->bcb)); - - dispc_write_reg(DISPC_VID_CONV_COEF(1, 0), CVAL(ct->rcr, ct->ry)); - dispc_write_reg(DISPC_VID_CONV_COEF(1, 1), CVAL(ct->gy, ct->rcb)); - dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr)); - dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by)); - dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0, ct->bcb)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0), + CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1), + CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2), + CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3), + CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4), + CVAL(0, ct->bcb)); + + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0), + CVAL(ct->rcr, ct->ry)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1), + CVAL(ct->gy, ct->rcb)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2), + CVAL(ct->gcb, ct->gcr)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3), + CVAL(ct->bcr, ct->by)); + dispc_write_reg(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4), + CVAL(0, ct->bcb)); #undef CVAL - REG_FLD_MOD(DISPC_VID_ATTRIBUTES(0), ct->full_range, 11, 11); - REG_FLD_MOD(DISPC_VID_ATTRIBUTES(1), ct->full_range, 11, 11); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1), + ct->full_range, 11, 11); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2), + ct->full_range, 11, 11); } static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr) { - const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0, - DISPC_VID_BA0(0), - DISPC_VID_BA0(1) }; - - dispc_write_reg(ba0_reg[plane], paddr); + dispc_write_reg(DISPC_OVL_BA0(plane), paddr); } static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr) { - const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1, - DISPC_VID_BA1(0), - DISPC_VID_BA1(1) }; + dispc_write_reg(DISPC_OVL_BA1(plane), paddr); +} - dispc_write_reg(ba1_reg[plane], paddr); +static void _dispc_set_plane_ba0_uv(enum omap_plane plane, u32 paddr) +{ + dispc_write_reg(DISPC_OVL_BA0_UV(plane), paddr); } -static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) +static void _dispc_set_plane_ba1_uv(enum omap_plane plane, u32 paddr) { - const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION, - DISPC_VID_POSITION(0), - DISPC_VID_POSITION(1) }; + dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr); +} +static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) +{ u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); - dispc_write_reg(pos_reg[plane], val); + + dispc_write_reg(DISPC_OVL_POSITION(plane), val); } static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) { - const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE, - DISPC_VID_PICTURE_SIZE(0), - DISPC_VID_PICTURE_SIZE(1) }; u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(siz_reg[plane], val); + + if (plane == OMAP_DSS_GFX) + dispc_write_reg(DISPC_OVL_SIZE(plane), val); + else + dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val); } static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) { u32 val; - const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0), - DISPC_VID_SIZE(1) }; BUG_ON(plane == OMAP_DSS_GFX); val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - dispc_write_reg(vsi_reg[plane-1], val); + + dispc_write_reg(DISPC_OVL_SIZE(plane), val); } static void _dispc_set_pre_mult_alpha(enum omap_plane plane, bool enable) @@ -848,7 +894,7 @@ static void _dispc_set_pre_mult_alpha(enum omap_plane plane, bool enable) plane == OMAP_DSS_VIDEO1) return; - REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 28, 28); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28); } static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) @@ -868,64 +914,96 @@ static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc) { - const struct dispc_reg ri_reg[] = { DISPC_GFX_PIXEL_INC, - DISPC_VID_PIXEL_INC(0), - DISPC_VID_PIXEL_INC(1) }; - - dispc_write_reg(ri_reg[plane], inc); + dispc_write_reg(DISPC_OVL_PIXEL_INC(plane), inc); } static void _dispc_set_row_inc(enum omap_plane plane, s32 inc) { - const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC, - DISPC_VID_ROW_INC(0), - DISPC_VID_ROW_INC(1) }; - - dispc_write_reg(ri_reg[plane], inc); + dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc); } static void _dispc_set_color_mode(enum omap_plane plane, enum omap_color_mode color_mode) { u32 m = 0; - - switch (color_mode) { - case OMAP_DSS_COLOR_CLUT1: - m = 0x0; break; - case OMAP_DSS_COLOR_CLUT2: - m = 0x1; break; - case OMAP_DSS_COLOR_CLUT4: - m = 0x2; break; - case OMAP_DSS_COLOR_CLUT8: - m = 0x3; break; - case OMAP_DSS_COLOR_RGB12U: - m = 0x4; break; - case OMAP_DSS_COLOR_ARGB16: - m = 0x5; break; - case OMAP_DSS_COLOR_RGB16: - m = 0x6; break; - case OMAP_DSS_COLOR_RGB24U: - m = 0x8; break; - case OMAP_DSS_COLOR_RGB24P: - m = 0x9; break; - case OMAP_DSS_COLOR_YUV2: - m = 0xa; break; - case OMAP_DSS_COLOR_UYVY: - m = 0xb; break; - case OMAP_DSS_COLOR_ARGB32: - m = 0xc; break; - case OMAP_DSS_COLOR_RGBA32: - m = 0xd; break; - case OMAP_DSS_COLOR_RGBX32: - m = 0xe; break; - default: - BUG(); break; + if (plane != OMAP_DSS_GFX) { + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + m = 0x0; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x1; break; + case OMAP_DSS_COLOR_RGBA16: + m = 0x2; break; + case OMAP_DSS_COLOR_RGBX16: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_ARGB16_1555: + m = 0x7; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xa; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xb; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xc; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xd; break; + case OMAP_DSS_COLOR_RGBX32: + m = 0xe; break; + case OMAP_DSS_COLOR_XRGB16_1555: + m = 0xf; break; + default: + BUG(); break; + } + } else { + switch (color_mode) { + case OMAP_DSS_COLOR_CLUT1: + m = 0x0; break; + case OMAP_DSS_COLOR_CLUT2: + m = 0x1; break; + case OMAP_DSS_COLOR_CLUT4: + m = 0x2; break; + case OMAP_DSS_COLOR_CLUT8: + m = 0x3; break; + case OMAP_DSS_COLOR_RGB12U: + m = 0x4; break; + case OMAP_DSS_COLOR_ARGB16: + m = 0x5; break; + case OMAP_DSS_COLOR_RGB16: + m = 0x6; break; + case OMAP_DSS_COLOR_ARGB16_1555: + m = 0x7; break; + case OMAP_DSS_COLOR_RGB24U: + m = 0x8; break; + case OMAP_DSS_COLOR_RGB24P: + m = 0x9; break; + case OMAP_DSS_COLOR_YUV2: + m = 0xa; break; + case OMAP_DSS_COLOR_UYVY: + m = 0xb; break; + case OMAP_DSS_COLOR_ARGB32: + m = 0xc; break; + case OMAP_DSS_COLOR_RGBA32: + m = 0xd; break; + case OMAP_DSS_COLOR_RGBX32: + m = 0xe; break; + case OMAP_DSS_COLOR_XRGB16_1555: + m = 0xf; break; + default: + BUG(); break; + } } - REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); } -static void _dispc_set_channel_out(enum omap_plane plane, +void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel) { int shift; @@ -945,7 +1023,7 @@ static void _dispc_set_channel_out(enum omap_plane plane, return; } - val = dispc_read_reg(dispc_reg_att[plane]); + val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); if (dss_has_feature(FEAT_MGR_LCD2)) { switch (channel) { case OMAP_DSS_CHANNEL_LCD: @@ -969,16 +1047,13 @@ static void _dispc_set_channel_out(enum omap_plane plane, } else { val = FLD_MOD(val, channel, shift, shift); } - dispc_write_reg(dispc_reg_att[plane], val); + dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); } -void dispc_set_burst_size(enum omap_plane plane, +static void dispc_set_burst_size(enum omap_plane plane, enum omap_burst_size burst_size) { int shift; - u32 val; - - enable_clocks(1); switch (plane) { case OMAP_DSS_GFX: @@ -993,11 +1068,72 @@ void dispc_set_burst_size(enum omap_plane plane, return; } - val = dispc_read_reg(dispc_reg_att[plane]); - val = FLD_MOD(val, burst_size, shift+1, shift); - dispc_write_reg(dispc_reg_att[plane], val); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), burst_size, shift + 1, shift); +} - enable_clocks(0); +static void dispc_configure_burst_sizes(void) +{ + int i; + const int burst_size = BURST_SIZE_X8; + + /* Configure burst size always to maximum size */ + for (i = 0; i < omap_dss_get_num_overlays(); ++i) + dispc_set_burst_size(i, burst_size); +} + +u32 dispc_get_burst_size(enum omap_plane plane) +{ + unsigned unit = dss_feat_get_burst_size_unit(); + /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ + return unit * 8; +} + +void dispc_enable_gamma_table(bool enable) +{ + /* + * This is partially implemented to support only disabling of + * the gamma table. + */ + if (enable) { + DSSWARN("Gamma table enabling for TV not yet supported"); + return; + } + + REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9); +} + +void dispc_enable_cpr(enum omap_channel channel, bool enable) +{ + u16 reg; + + if (channel == OMAP_DSS_CHANNEL_LCD) + reg = DISPC_CONFIG; + else if (channel == OMAP_DSS_CHANNEL_LCD2) + reg = DISPC_CONFIG2; + else + return; + + REG_FLD_MOD(reg, enable, 15, 15); +} + +void dispc_set_cpr_coef(enum omap_channel channel, + struct omap_dss_cpr_coefs *coefs) +{ + u32 coef_r, coef_g, coef_b; + + if (channel != OMAP_DSS_CHANNEL_LCD && channel != OMAP_DSS_CHANNEL_LCD2) + return; + + coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) | + FLD_VAL(coefs->rb, 9, 0); + coef_g = FLD_VAL(coefs->gr, 31, 22) | FLD_VAL(coefs->gg, 20, 11) | + FLD_VAL(coefs->gb, 9, 0); + coef_b = FLD_VAL(coefs->br, 31, 22) | FLD_VAL(coefs->bg, 20, 11) | + FLD_VAL(coefs->bb, 9, 0); + + dispc_write_reg(DISPC_CPR_COEF_R(channel), coef_r); + dispc_write_reg(DISPC_CPR_COEF_G(channel), coef_g); + dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b); } static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) @@ -1006,9 +1142,9 @@ static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) BUG_ON(plane == OMAP_DSS_GFX); - val = dispc_read_reg(dispc_reg_att[plane]); + val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); val = FLD_MOD(val, enable, 9, 9); - dispc_write_reg(dispc_reg_att[plane], val); + dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val); } void dispc_enable_replication(enum omap_plane plane, bool enable) @@ -1020,9 +1156,7 @@ void dispc_enable_replication(enum omap_plane plane, bool enable) else bit = 10; - enable_clocks(1); - REG_FLD_MOD(dispc_reg_att[plane], enable, bit, bit); - enable_clocks(0); + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit); } void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height) @@ -1030,9 +1164,7 @@ void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height) u32 val; BUG_ON((width > (1 << 11)) || (height > (1 << 11))); val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - enable_clocks(1); - dispc_write_reg(DISPC_SIZE_LCD(channel), val); - enable_clocks(0); + dispc_write_reg(DISPC_SIZE_MGR(channel), val); } void dispc_set_digit_size(u16 width, u16 height) @@ -1040,30 +1172,25 @@ void dispc_set_digit_size(u16 width, u16 height) u32 val; BUG_ON((width > (1 << 11)) || (height > (1 << 11))); val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); - enable_clocks(1); - dispc_write_reg(DISPC_SIZE_DIG, val); - enable_clocks(0); + dispc_write_reg(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT), val); } static void dispc_read_plane_fifo_sizes(void) { - const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, - DISPC_VID_FIFO_SIZE_STATUS(0), - DISPC_VID_FIFO_SIZE_STATUS(1) }; u32 size; int plane; u8 start, end; + u32 unit; - enable_clocks(1); + unit = dss_feat_get_buffer_size_unit(); dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end); for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { - size = FLD_GET(dispc_read_reg(fsz_reg[plane]), start, end); + size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(plane), start, end); + size *= unit; dispc.fifo_size[plane] = size; } - - enable_clocks(0); } u32 dispc_get_plane_fifo_size(enum omap_plane plane) @@ -1071,129 +1198,164 @@ u32 dispc_get_plane_fifo_size(enum omap_plane plane) return dispc.fifo_size[plane]; } -void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) +void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high) { - const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, - DISPC_VID_FIFO_THRESHOLD(0), - DISPC_VID_FIFO_THRESHOLD(1) }; u8 hi_start, hi_end, lo_start, lo_end; + u32 unit; - enable_clocks(1); + unit = dss_feat_get_buffer_size_unit(); - DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", - plane, - REG_GET(ftrs_reg[plane], 11, 0), - REG_GET(ftrs_reg[plane], 27, 16), - low, high); + WARN_ON(low % unit != 0); + WARN_ON(high % unit != 0); + + low /= unit; + high /= unit; dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end); dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end); - dispc_write_reg(ftrs_reg[plane], + DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", + plane, + REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), + lo_start, lo_end), + REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane), + hi_start, hi_end), + low, high); + + dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane), FLD_VAL(high, hi_start, hi_end) | FLD_VAL(low, lo_start, lo_end)); - - enable_clocks(0); } void dispc_enable_fifomerge(bool enable) { - enable_clocks(1); - DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); - - enable_clocks(0); } -static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) +static void _dispc_set_fir(enum omap_plane plane, + int hinc, int vinc, + enum omap_color_component color_comp) { u32 val; - const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0), - DISPC_VID_FIR(1) }; - u8 hinc_start, hinc_end, vinc_start, vinc_end; - - BUG_ON(plane == OMAP_DSS_GFX); - dss_feat_get_reg_field(FEAT_REG_FIRHINC, &hinc_start, &hinc_end); - dss_feat_get_reg_field(FEAT_REG_FIRVINC, &vinc_start, &vinc_end); + if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) { + u8 hinc_start, hinc_end, vinc_start, vinc_end; - val = FLD_VAL(vinc, vinc_start, vinc_end) | - FLD_VAL(hinc, hinc_start, hinc_end); + dss_feat_get_reg_field(FEAT_REG_FIRHINC, + &hinc_start, &hinc_end); + dss_feat_get_reg_field(FEAT_REG_FIRVINC, + &vinc_start, &vinc_end); + val = FLD_VAL(vinc, vinc_start, vinc_end) | + FLD_VAL(hinc, hinc_start, hinc_end); - dispc_write_reg(fir_reg[plane-1], val); + dispc_write_reg(DISPC_OVL_FIR(plane), val); + } else { + val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); + dispc_write_reg(DISPC_OVL_FIR2(plane), val); + } } static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) { u32 val; - const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0), - DISPC_VID_ACCU0(1) }; + u8 hor_start, hor_end, vert_start, vert_end; - BUG_ON(plane == OMAP_DSS_GFX); + dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end); + dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end); - val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); - dispc_write_reg(ac0_reg[plane-1], val); + val = FLD_VAL(vaccu, vert_start, vert_end) | + FLD_VAL(haccu, hor_start, hor_end); + + dispc_write_reg(DISPC_OVL_ACCU0(plane), val); } static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) { u32 val; - const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0), - DISPC_VID_ACCU1(1) }; + u8 hor_start, hor_end, vert_start, vert_end; - BUG_ON(plane == OMAP_DSS_GFX); + dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end); + dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end); - val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); - dispc_write_reg(ac1_reg[plane-1], val); + val = FLD_VAL(vaccu, vert_start, vert_end) | + FLD_VAL(haccu, hor_start, hor_end); + + dispc_write_reg(DISPC_OVL_ACCU1(plane), val); } +static void _dispc_set_vid_accu2_0(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; -static void _dispc_set_scaling(enum omap_plane plane, + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + dispc_write_reg(DISPC_OVL_ACCU2_0(plane), val); +} + +static void _dispc_set_vid_accu2_1(enum omap_plane plane, int haccu, int vaccu) +{ + u32 val; + + val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0); + dispc_write_reg(DISPC_OVL_ACCU2_1(plane), val); +} + +static void _dispc_set_scale_param(enum omap_plane plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, - bool ilace, bool five_taps, - bool fieldmode) + bool five_taps, u8 rotation, + enum omap_color_component color_comp) { - int fir_hinc; - int fir_vinc; + int fir_hinc, fir_vinc; int hscaleup, vscaleup; - int accu0 = 0; - int accu1 = 0; - u32 l; - - BUG_ON(plane == OMAP_DSS_GFX); hscaleup = orig_width <= out_width; vscaleup = orig_height <= out_height; - _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps); + _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps, color_comp); - if (!orig_width || orig_width == out_width) - fir_hinc = 0; - else - fir_hinc = 1024 * orig_width / out_width; + fir_hinc = 1024 * orig_width / out_width; + fir_vinc = 1024 * orig_height / out_height; - if (!orig_height || orig_height == out_height) - fir_vinc = 0; - else - fir_vinc = 1024 * orig_height / out_height; + _dispc_set_fir(plane, fir_hinc, fir_vinc, color_comp); +} - _dispc_set_fir(plane, fir_hinc, fir_vinc); +static void _dispc_set_scaling_common(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode, enum omap_color_mode color_mode, + u8 rotation) +{ + int accu0 = 0; + int accu1 = 0; + u32 l; - l = dispc_read_reg(dispc_reg_att[plane]); - l &= ~((0x0f << 5) | (0x3 << 21)); + _dispc_set_scale_param(plane, orig_width, orig_height, + out_width, out_height, five_taps, + rotation, DISPC_COLOR_COMPONENT_RGB_Y); + l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane)); - l |= fir_hinc ? (1 << 5) : 0; - l |= fir_vinc ? (1 << 6) : 0; + /* RESIZEENABLE and VERTICALTAPS */ + l &= ~((0x3 << 5) | (0x1 << 21)); + l |= (orig_width != out_width) ? (1 << 5) : 0; + l |= (orig_height != out_height) ? (1 << 6) : 0; + l |= five_taps ? (1 << 21) : 0; - l |= hscaleup ? 0 : (1 << 7); - l |= vscaleup ? 0 : (1 << 8); + /* VRESIZECONF and HRESIZECONF */ + if (dss_has_feature(FEAT_RESIZECONF)) { + l &= ~(0x3 << 7); + l |= (orig_width <= out_width) ? 0 : (1 << 7); + l |= (orig_height <= out_height) ? 0 : (1 << 8); + } - l |= five_taps ? (1 << 21) : 0; - l |= five_taps ? (1 << 22) : 0; + /* LINEBUFFERSPLIT */ + if (dss_has_feature(FEAT_LINEBUFFERSPLIT)) { + l &= ~(0x1 << 22); + l |= five_taps ? (1 << 22) : 0; + } - dispc_write_reg(dispc_reg_att[plane], l); + dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l); /* * field 0 = even field = bottom field @@ -1201,7 +1363,7 @@ static void _dispc_set_scaling(enum omap_plane plane, */ if (ilace && !fieldmode) { accu1 = 0; - accu0 = (fir_vinc / 2) & 0x3ff; + accu0 = ((1024 * orig_height / out_height) / 2) & 0x3ff; if (accu0 >= 1024/2) { accu1 = 1024/2; accu0 -= accu1; @@ -1212,12 +1374,101 @@ static void _dispc_set_scaling(enum omap_plane plane, _dispc_set_vid_accu1(plane, 0, accu1); } +static void _dispc_set_scaling_uv(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode, enum omap_color_mode color_mode, + u8 rotation) +{ + int scale_x = out_width != orig_width; + int scale_y = out_height != orig_height; + + if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) + return; + if ((color_mode != OMAP_DSS_COLOR_YUV2 && + color_mode != OMAP_DSS_COLOR_UYVY && + color_mode != OMAP_DSS_COLOR_NV12)) { + /* reset chroma resampling for RGB formats */ + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); + return; + } + switch (color_mode) { + case OMAP_DSS_COLOR_NV12: + /* UV is subsampled by 2 vertically*/ + orig_height >>= 1; + /* UV is subsampled by 2 horz.*/ + orig_width >>= 1; + break; + case OMAP_DSS_COLOR_YUV2: + case OMAP_DSS_COLOR_UYVY: + /*For YUV422 with 90/270 rotation, + *we don't upsample chroma + */ + if (rotation == OMAP_DSS_ROT_0 || + rotation == OMAP_DSS_ROT_180) + /* UV is subsampled by 2 hrz*/ + orig_width >>= 1; + /* must use FIR for YUV422 if rotated */ + if (rotation != OMAP_DSS_ROT_0) + scale_x = scale_y = true; + break; + default: + BUG(); + } + + if (out_width != orig_width) + scale_x = true; + if (out_height != orig_height) + scale_y = true; + + _dispc_set_scale_param(plane, orig_width, orig_height, + out_width, out_height, five_taps, + rotation, DISPC_COLOR_COMPONENT_UV); + + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), + (scale_x || scale_y) ? 1 : 0, 8, 8); + /* set H scaling */ + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5); + /* set V scaling */ + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6); + + _dispc_set_vid_accu2_0(plane, 0x80, 0); + _dispc_set_vid_accu2_1(plane, 0x80, 0); +} + +static void _dispc_set_scaling(enum omap_plane plane, + u16 orig_width, u16 orig_height, + u16 out_width, u16 out_height, + bool ilace, bool five_taps, + bool fieldmode, enum omap_color_mode color_mode, + u8 rotation) +{ + BUG_ON(plane == OMAP_DSS_GFX); + + _dispc_set_scaling_common(plane, + orig_width, orig_height, + out_width, out_height, + ilace, five_taps, + fieldmode, color_mode, + rotation); + + _dispc_set_scaling_uv(plane, + orig_width, orig_height, + out_width, out_height, + ilace, five_taps, + fieldmode, color_mode, + rotation); +} + static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, bool mirroring, enum omap_color_mode color_mode) { + bool row_repeat = false; + int vidrot = 0; + if (color_mode == OMAP_DSS_COLOR_YUV2 || color_mode == OMAP_DSS_COLOR_UYVY) { - int vidrot = 0; if (mirroring) { switch (rotation) { @@ -1251,16 +1502,16 @@ static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, } } - REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12); - if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270) - REG_FLD_MOD(dispc_reg_att[plane], 0x1, 18, 18); + row_repeat = true; else - REG_FLD_MOD(dispc_reg_att[plane], 0x0, 18, 18); - } else { - REG_FLD_MOD(dispc_reg_att[plane], 0, 13, 12); - REG_FLD_MOD(dispc_reg_att[plane], 0, 18, 18); + row_repeat = false; } + + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12); + if (dss_has_feature(FEAT_ROWREPEATENABLE)) + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), + row_repeat ? 1 : 0, 18, 18); } static int color_mode_to_bpp(enum omap_color_mode color_mode) @@ -1273,12 +1524,17 @@ static int color_mode_to_bpp(enum omap_color_mode color_mode) case OMAP_DSS_COLOR_CLUT4: return 4; case OMAP_DSS_COLOR_CLUT8: + case OMAP_DSS_COLOR_NV12: return 8; case OMAP_DSS_COLOR_RGB12U: case OMAP_DSS_COLOR_RGB16: case OMAP_DSS_COLOR_ARGB16: case OMAP_DSS_COLOR_YUV2: case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_RGBA16: + case OMAP_DSS_COLOR_RGBX16: + case OMAP_DSS_COLOR_ARGB16_1555: + case OMAP_DSS_COLOR_XRGB16_1555: return 16; case OMAP_DSS_COLOR_RGB24P: return 24; @@ -1594,14 +1850,7 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width, return dispc_pclk_rate(channel) * vf * hf; } -void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out) -{ - enable_clocks(1); - _dispc_set_channel_out(plane, channel_out); - enable_clocks(0); -} - -static int _dispc_setup_plane(enum omap_plane plane, +int dispc_setup_plane(enum omap_plane plane, u32 paddr, u16 screen_width, u16 pos_x, u16 pos_y, u16 width, u16 height, @@ -1609,9 +1858,9 @@ static int _dispc_setup_plane(enum omap_plane plane, enum omap_color_mode color_mode, bool ilace, enum omap_dss_rotation_type rotation_type, - u8 rotation, int mirror, + u8 rotation, bool mirror, u8 global_alpha, u8 pre_mult_alpha, - enum omap_channel channel) + enum omap_channel channel, u32 puv_addr) { const int maxdownscale = cpu_is_omap34xx() ? 4 : 2; bool five_taps = 0; @@ -1623,6 +1872,14 @@ static int _dispc_setup_plane(enum omap_plane plane, u16 frame_height = height; unsigned int field_offset = 0; + DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " + "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n", + plane, paddr, screen_width, pos_x, pos_y, + width, height, + out_width, out_height, + ilace, color_mode, + rotation, mirror, channel); + if (paddr == 0) return -EINVAL; @@ -1660,7 +1917,8 @@ static int _dispc_setup_plane(enum omap_plane plane, return -EINVAL; if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) + color_mode == OMAP_DSS_COLOR_UYVY || + color_mode == OMAP_DSS_COLOR_NV12) cconv = 1; /* Must use 5-tap filter? */ @@ -1734,6 +1992,12 @@ static int _dispc_setup_plane(enum omap_plane plane, _dispc_set_plane_ba0(plane, paddr + offset0); _dispc_set_plane_ba1(plane, paddr + offset1); + if (OMAP_DSS_COLOR_NV12 == color_mode) { + _dispc_set_plane_ba0_uv(plane, puv_addr + offset0); + _dispc_set_plane_ba1_uv(plane, puv_addr + offset1); + } + + _dispc_set_row_inc(plane, row_inc); _dispc_set_pix_inc(plane, pix_inc); @@ -1747,7 +2011,8 @@ static int _dispc_setup_plane(enum omap_plane plane, if (plane != OMAP_DSS_GFX) { _dispc_set_scaling(plane, width, height, out_width, out_height, - ilace, five_taps, fieldmode); + ilace, five_taps, fieldmode, + color_mode, rotation); _dispc_set_vid_size(plane, out_width, out_height); _dispc_set_vid_color_conv(plane, cconv); } @@ -1760,9 +2025,13 @@ static int _dispc_setup_plane(enum omap_plane plane, return 0; } -static void _dispc_enable_plane(enum omap_plane plane, bool enable) +int dispc_enable_plane(enum omap_plane plane, bool enable) { - REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0); + DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); + + REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0); + + return 0; } static void dispc_disable_isr(void *data, u32 mask) @@ -1786,8 +2055,6 @@ static void dispc_enable_lcd_out(enum omap_channel channel, bool enable) int r; u32 irq; - enable_clocks(1); - /* When we disable LCD output, we need to wait until frame is done. * Otherwise the DSS is still working, and turning off the clocks * prevents DSS from going to OFF mode */ @@ -1821,8 +2088,6 @@ static void dispc_enable_lcd_out(enum omap_channel channel, bool enable) if (r) DSSERR("failed to unregister FRAMEDONE isr\n"); } - - enable_clocks(0); } static void _enable_digit_out(bool enable) @@ -1835,12 +2100,8 @@ static void dispc_enable_digit_out(bool enable) struct completion frame_done_completion; int r; - enable_clocks(1); - - if (REG_GET(DISPC_CONTROL, 1, 1) == enable) { - enable_clocks(0); + if (REG_GET(DISPC_CONTROL, 1, 1) == enable) return; - } if (enable) { unsigned long flags; @@ -1892,8 +2153,6 @@ static void dispc_enable_digit_out(bool enable) _omap_dispc_set_irqs(); spin_unlock_irqrestore(&dispc.irq_lock, flags); } - - enable_clocks(0); } bool dispc_is_channel_enabled(enum omap_channel channel) @@ -1924,9 +2183,7 @@ void dispc_lcd_enable_signal_polarity(bool act_high) if (!dss_has_feature(FEAT_LCDENABLEPOL)) return; - enable_clocks(1); REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); - enable_clocks(0); } void dispc_lcd_enable_signal(bool enable) @@ -1934,9 +2191,7 @@ void dispc_lcd_enable_signal(bool enable) if (!dss_has_feature(FEAT_LCDENABLESIGNAL)) return; - enable_clocks(1); REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); - enable_clocks(0); } void dispc_pck_free_enable(bool enable) @@ -1944,19 +2199,15 @@ void dispc_pck_free_enable(bool enable) if (!dss_has_feature(FEAT_PCKFREEENABLE)) return; - enable_clocks(1); REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); - enable_clocks(0); } void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable) { - enable_clocks(1); if (channel == OMAP_DSS_CHANNEL_LCD2) REG_FLD_MOD(DISPC_CONFIG2, enable ? 1 : 0, 16, 16); else REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16); - enable_clocks(0); } @@ -1979,27 +2230,21 @@ void dispc_set_lcd_display_type(enum omap_channel channel, return; } - enable_clocks(1); if (channel == OMAP_DSS_CHANNEL_LCD2) REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3); else REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); - enable_clocks(0); } void dispc_set_loadmode(enum omap_dss_load_mode mode) { - enable_clocks(1); REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1); - enable_clocks(0); } void dispc_set_default_color(enum omap_channel channel, u32 color) { - enable_clocks(1); dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color); - enable_clocks(0); } u32 dispc_get_default_color(enum omap_channel channel) @@ -2010,9 +2255,7 @@ u32 dispc_get_default_color(enum omap_channel channel) channel != OMAP_DSS_CHANNEL_LCD && channel != OMAP_DSS_CHANNEL_LCD2); - enable_clocks(1); l = dispc_read_reg(DISPC_DEFAULT_COLOR(channel)); - enable_clocks(0); return l; } @@ -2021,7 +2264,6 @@ void dispc_set_trans_key(enum omap_channel ch, enum omap_dss_trans_key_type type, u32 trans_key) { - enable_clocks(1); if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, type, 11, 11); else if (ch == OMAP_DSS_CHANNEL_DIGIT) @@ -2030,14 +2272,12 @@ void dispc_set_trans_key(enum omap_channel ch, REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11); dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key); - enable_clocks(0); } void dispc_get_trans_key(enum omap_channel ch, enum omap_dss_trans_key_type *type, u32 *trans_key) { - enable_clocks(1); if (type) { if (ch == OMAP_DSS_CHANNEL_LCD) *type = REG_GET(DISPC_CONFIG, 11, 11); @@ -2051,33 +2291,28 @@ void dispc_get_trans_key(enum omap_channel ch, if (trans_key) *trans_key = dispc_read_reg(DISPC_TRANS_COLOR(ch)); - enable_clocks(0); } void dispc_enable_trans_key(enum omap_channel ch, bool enable) { - enable_clocks(1); if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10); else if (ch == OMAP_DSS_CHANNEL_DIGIT) REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12); else /* OMAP_DSS_CHANNEL_LCD2 */ REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10); - enable_clocks(0); } void dispc_enable_alpha_blending(enum omap_channel ch, bool enable) { if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) return; - enable_clocks(1); if (ch == OMAP_DSS_CHANNEL_LCD) REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18); else if (ch == OMAP_DSS_CHANNEL_DIGIT) REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19); else /* OMAP_DSS_CHANNEL_LCD2 */ REG_FLD_MOD(DISPC_CONFIG2, enable, 18, 18); - enable_clocks(0); } bool dispc_alpha_blending_enabled(enum omap_channel ch) { @@ -2086,7 +2321,6 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch) if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) return false; - enable_clocks(1); if (ch == OMAP_DSS_CHANNEL_LCD) enabled = REG_GET(DISPC_CONFIG, 18, 18); else if (ch == OMAP_DSS_CHANNEL_DIGIT) @@ -2095,7 +2329,6 @@ bool dispc_alpha_blending_enabled(enum omap_channel ch) enabled = REG_GET(DISPC_CONFIG2, 18, 18); else BUG(); - enable_clocks(0); return enabled; } @@ -2105,7 +2338,6 @@ bool dispc_trans_key_enabled(enum omap_channel ch) { bool enabled; - enable_clocks(1); if (ch == OMAP_DSS_CHANNEL_LCD) enabled = REG_GET(DISPC_CONFIG, 10, 10); else if (ch == OMAP_DSS_CHANNEL_DIGIT) @@ -2114,7 +2346,6 @@ bool dispc_trans_key_enabled(enum omap_channel ch) enabled = REG_GET(DISPC_CONFIG2, 10, 10); else BUG(); - enable_clocks(0); return enabled; } @@ -2142,12 +2373,10 @@ void dispc_set_tft_data_lines(enum omap_channel channel, u8 data_lines) return; } - enable_clocks(1); if (channel == OMAP_DSS_CHANNEL_LCD2) REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8); else REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); - enable_clocks(0); } void dispc_set_parallel_interface_mode(enum omap_channel channel, @@ -2179,8 +2408,6 @@ void dispc_set_parallel_interface_mode(enum omap_channel channel, return; } - enable_clocks(1); - if (channel == OMAP_DSS_CHANNEL_LCD2) { l = dispc_read_reg(DISPC_CONTROL2); l = FLD_MOD(l, stallmode, 11, 11); @@ -2192,8 +2419,6 @@ void dispc_set_parallel_interface_mode(enum omap_channel channel, l = FLD_MOD(l, gpout1, 16, 16); dispc_write_reg(DISPC_CONTROL, l); } - - enable_clocks(0); } static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, @@ -2246,10 +2471,8 @@ static void _dispc_set_lcd_timings(enum omap_channel channel, int hsw, FLD_VAL(vbp, 31, 20); } - enable_clocks(1); dispc_write_reg(DISPC_TIMING_H(channel), timing_h); dispc_write_reg(DISPC_TIMING_V(channel), timing_v); - enable_clocks(0); } /* change name to mode? */ @@ -2292,83 +2515,122 @@ static void dispc_set_lcd_divisor(enum omap_channel channel, u16 lck_div, BUG_ON(lck_div < 1); BUG_ON(pck_div < 2); - enable_clocks(1); - dispc_write_reg(DISPC_DIVISOR(channel), + dispc_write_reg(DISPC_DIVISORo(channel), FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); - enable_clocks(0); } static void dispc_get_lcd_divisor(enum omap_channel channel, int *lck_div, int *pck_div) { u32 l; - l = dispc_read_reg(DISPC_DIVISOR(channel)); + l = dispc_read_reg(DISPC_DIVISORo(channel)); *lck_div = FLD_GET(l, 23, 16); *pck_div = FLD_GET(l, 7, 0); } unsigned long dispc_fclk_rate(void) { + struct platform_device *dsidev; unsigned long r = 0; - if (dss_get_dispc_clk_source() == DSS_SRC_DSS1_ALWON_FCLK) - r = dss_clk_get_rate(DSS_CLK_FCK1); - else -#ifdef CONFIG_OMAP2_DSS_DSI - r = dsi_get_dsi1_pll_rate(); -#else - BUG(); -#endif + switch (dss_get_dispc_clk_source()) { + case OMAP_DSS_CLK_SRC_FCK: + r = clk_get_rate(dispc.dss_clk); + break; + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: + dsidev = dsi_get_dsidev_from_id(0); + r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: + dsidev = dsi_get_dsidev_from_id(1); + r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + break; + default: + BUG(); + } + return r; } unsigned long dispc_lclk_rate(enum omap_channel channel) { + struct platform_device *dsidev; int lcd; unsigned long r; u32 l; - l = dispc_read_reg(DISPC_DIVISOR(channel)); + l = dispc_read_reg(DISPC_DIVISORo(channel)); lcd = FLD_GET(l, 23, 16); - r = dispc_fclk_rate(); + switch (dss_get_lcd_clk_source(channel)) { + case OMAP_DSS_CLK_SRC_FCK: + r = clk_get_rate(dispc.dss_clk); + break; + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: + dsidev = dsi_get_dsidev_from_id(0); + r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: + dsidev = dsi_get_dsidev_from_id(1); + r = dsi_get_pll_hsdiv_dispc_rate(dsidev); + break; + default: + BUG(); + } return r / lcd; } unsigned long dispc_pclk_rate(enum omap_channel channel) { - int lcd, pcd; + int pcd; unsigned long r; u32 l; - l = dispc_read_reg(DISPC_DIVISOR(channel)); + l = dispc_read_reg(DISPC_DIVISORo(channel)); - lcd = FLD_GET(l, 23, 16); pcd = FLD_GET(l, 7, 0); - r = dispc_fclk_rate(); + r = dispc_lclk_rate(channel); - return r / lcd / pcd; + return r / pcd; } void dispc_dump_clocks(struct seq_file *s) { int lcd, pcd; + u32 l; + enum omap_dss_clk_source dispc_clk_src = dss_get_dispc_clk_source(); + enum omap_dss_clk_source lcd_clk_src; - enable_clocks(1); + if (dispc_runtime_get()) + return; seq_printf(s, "- DISPC -\n"); - seq_printf(s, "dispc fclk source = %s\n", - dss_get_dispc_clk_source() == DSS_SRC_DSS1_ALWON_FCLK ? - "dss1_alwon_fclk" : "dsi1_pll_fclk"); + seq_printf(s, "dispc fclk source = %s (%s)\n", + dss_get_generic_clk_source_name(dispc_clk_src), + dss_feat_get_clk_source_name(dispc_clk_src)); seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate()); + if (dss_has_feature(FEAT_CORE_CLK_DIV)) { + seq_printf(s, "- DISPC-CORE-CLK -\n"); + l = dispc_read_reg(DISPC_DIVISOR); + lcd = FLD_GET(l, 23, 16); + + seq_printf(s, "lck\t\t%-16lulck div\t%u\n", + (dispc_fclk_rate()/lcd), lcd); + } seq_printf(s, "- LCD1 -\n"); + lcd_clk_src = dss_get_lcd_clk_source(OMAP_DSS_CHANNEL_LCD); + + seq_printf(s, "lcd1_clk source = %s (%s)\n", + dss_get_generic_clk_source_name(lcd_clk_src), + dss_feat_get_clk_source_name(lcd_clk_src)); + dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd); seq_printf(s, "lck\t\t%-16lulck div\t%u\n", @@ -2378,6 +2640,12 @@ void dispc_dump_clocks(struct seq_file *s) if (dss_has_feature(FEAT_MGR_LCD2)) { seq_printf(s, "- LCD2 -\n"); + lcd_clk_src = dss_get_lcd_clk_source(OMAP_DSS_CHANNEL_LCD2); + + seq_printf(s, "lcd2_clk source = %s (%s)\n", + dss_get_generic_clk_source_name(lcd_clk_src), + dss_feat_get_clk_source_name(lcd_clk_src)); + dispc_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd); seq_printf(s, "lck\t\t%-16lulck div\t%u\n", @@ -2385,7 +2653,8 @@ void dispc_dump_clocks(struct seq_file *s) seq_printf(s, "pck\t\t%-16lupck div\t%u\n", dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd); } - enable_clocks(0); + + dispc_runtime_put(); } #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS @@ -2438,9 +2707,10 @@ void dispc_dump_irqs(struct seq_file *s) void dispc_dump_regs(struct seq_file *s) { -#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dispc_read_reg(r)) +#define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r)) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + if (dispc_runtime_get()) + return; DUMPREG(DISPC_REVISION); DUMPREG(DISPC_SYSCONFIG); @@ -2450,154 +2720,242 @@ void dispc_dump_regs(struct seq_file *s) DUMPREG(DISPC_CONTROL); DUMPREG(DISPC_CONFIG); DUMPREG(DISPC_CAPABLE); - DUMPREG(DISPC_DEFAULT_COLOR(0)); - DUMPREG(DISPC_DEFAULT_COLOR(1)); - DUMPREG(DISPC_TRANS_COLOR(0)); - DUMPREG(DISPC_TRANS_COLOR(1)); + DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_DIGIT)); + DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_DIGIT)); DUMPREG(DISPC_LINE_STATUS); DUMPREG(DISPC_LINE_NUMBER); - DUMPREG(DISPC_TIMING_H(0)); - DUMPREG(DISPC_TIMING_V(0)); - DUMPREG(DISPC_POL_FREQ(0)); - DUMPREG(DISPC_DIVISOR(0)); - DUMPREG(DISPC_GLOBAL_ALPHA); - DUMPREG(DISPC_SIZE_DIG); - DUMPREG(DISPC_SIZE_LCD(0)); + DUMPREG(DISPC_TIMING_H(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD)); + if (dss_has_feature(FEAT_GLOBAL_ALPHA)) + DUMPREG(DISPC_GLOBAL_ALPHA); + DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_DIGIT)); + DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD)); if (dss_has_feature(FEAT_MGR_LCD2)) { DUMPREG(DISPC_CONTROL2); DUMPREG(DISPC_CONFIG2); - DUMPREG(DISPC_DEFAULT_COLOR(2)); - DUMPREG(DISPC_TRANS_COLOR(2)); - DUMPREG(DISPC_TIMING_H(2)); - DUMPREG(DISPC_TIMING_V(2)); - DUMPREG(DISPC_POL_FREQ(2)); - DUMPREG(DISPC_DIVISOR(2)); - DUMPREG(DISPC_SIZE_LCD(2)); - } - - DUMPREG(DISPC_GFX_BA0); - DUMPREG(DISPC_GFX_BA1); - DUMPREG(DISPC_GFX_POSITION); - DUMPREG(DISPC_GFX_SIZE); - DUMPREG(DISPC_GFX_ATTRIBUTES); - DUMPREG(DISPC_GFX_FIFO_THRESHOLD); - DUMPREG(DISPC_GFX_FIFO_SIZE_STATUS); - DUMPREG(DISPC_GFX_ROW_INC); - DUMPREG(DISPC_GFX_PIXEL_INC); - DUMPREG(DISPC_GFX_WINDOW_SKIP); - DUMPREG(DISPC_GFX_TABLE_BA); - - DUMPREG(DISPC_DATA_CYCLE1(0)); - DUMPREG(DISPC_DATA_CYCLE2(0)); - DUMPREG(DISPC_DATA_CYCLE3(0)); - - DUMPREG(DISPC_CPR_COEF_R(0)); - DUMPREG(DISPC_CPR_COEF_G(0)); - DUMPREG(DISPC_CPR_COEF_B(0)); + DUMPREG(DISPC_DEFAULT_COLOR(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_TRANS_COLOR(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_TIMING_H(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_TIMING_V(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_POL_FREQ(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_DIVISORo(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_SIZE_MGR(OMAP_DSS_CHANNEL_LCD2)); + } + + DUMPREG(DISPC_OVL_BA0(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_BA1(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_WINDOW_SKIP(OMAP_DSS_GFX)); + DUMPREG(DISPC_OVL_TABLE_BA(OMAP_DSS_GFX)); + + DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD)); + + if (dss_has_feature(FEAT_CPR)) { + DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD)); + DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD)); + } if (dss_has_feature(FEAT_MGR_LCD2)) { - DUMPREG(DISPC_DATA_CYCLE1(2)); - DUMPREG(DISPC_DATA_CYCLE2(2)); - DUMPREG(DISPC_DATA_CYCLE3(2)); - - DUMPREG(DISPC_CPR_COEF_R(2)); - DUMPREG(DISPC_CPR_COEF_G(2)); - DUMPREG(DISPC_CPR_COEF_B(2)); - } - - DUMPREG(DISPC_GFX_PRELOAD); - - DUMPREG(DISPC_VID_BA0(0)); - DUMPREG(DISPC_VID_BA1(0)); - DUMPREG(DISPC_VID_POSITION(0)); - DUMPREG(DISPC_VID_SIZE(0)); - DUMPREG(DISPC_VID_ATTRIBUTES(0)); - DUMPREG(DISPC_VID_FIFO_THRESHOLD(0)); - DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(0)); - DUMPREG(DISPC_VID_ROW_INC(0)); - DUMPREG(DISPC_VID_PIXEL_INC(0)); - DUMPREG(DISPC_VID_FIR(0)); - DUMPREG(DISPC_VID_PICTURE_SIZE(0)); - DUMPREG(DISPC_VID_ACCU0(0)); - DUMPREG(DISPC_VID_ACCU1(0)); - - DUMPREG(DISPC_VID_BA0(1)); - DUMPREG(DISPC_VID_BA1(1)); - DUMPREG(DISPC_VID_POSITION(1)); - DUMPREG(DISPC_VID_SIZE(1)); - DUMPREG(DISPC_VID_ATTRIBUTES(1)); - DUMPREG(DISPC_VID_FIFO_THRESHOLD(1)); - DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(1)); - DUMPREG(DISPC_VID_ROW_INC(1)); - DUMPREG(DISPC_VID_PIXEL_INC(1)); - DUMPREG(DISPC_VID_FIR(1)); - DUMPREG(DISPC_VID_PICTURE_SIZE(1)); - DUMPREG(DISPC_VID_ACCU0(1)); - DUMPREG(DISPC_VID_ACCU1(1)); - - DUMPREG(DISPC_VID_FIR_COEF_H(0, 0)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 1)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 2)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 3)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 4)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 5)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 6)); - DUMPREG(DISPC_VID_FIR_COEF_H(0, 7)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 0)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 1)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 2)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 3)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 4)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 5)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 6)); - DUMPREG(DISPC_VID_FIR_COEF_HV(0, 7)); - DUMPREG(DISPC_VID_CONV_COEF(0, 0)); - DUMPREG(DISPC_VID_CONV_COEF(0, 1)); - DUMPREG(DISPC_VID_CONV_COEF(0, 2)); - DUMPREG(DISPC_VID_CONV_COEF(0, 3)); - DUMPREG(DISPC_VID_CONV_COEF(0, 4)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 0)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 1)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 2)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 3)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 4)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 5)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 6)); - DUMPREG(DISPC_VID_FIR_COEF_V(0, 7)); - - DUMPREG(DISPC_VID_FIR_COEF_H(1, 0)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 1)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 2)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 3)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 4)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 5)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 6)); - DUMPREG(DISPC_VID_FIR_COEF_H(1, 7)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 0)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 1)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 2)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 3)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 4)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 5)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 6)); - DUMPREG(DISPC_VID_FIR_COEF_HV(1, 7)); - DUMPREG(DISPC_VID_CONV_COEF(1, 0)); - DUMPREG(DISPC_VID_CONV_COEF(1, 1)); - DUMPREG(DISPC_VID_CONV_COEF(1, 2)); - DUMPREG(DISPC_VID_CONV_COEF(1, 3)); - DUMPREG(DISPC_VID_CONV_COEF(1, 4)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 0)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 1)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 2)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 3)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 4)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 5)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 6)); - DUMPREG(DISPC_VID_FIR_COEF_V(1, 7)); - - DUMPREG(DISPC_VID_PRELOAD(0)); - DUMPREG(DISPC_VID_PRELOAD(1)); - - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + DUMPREG(DISPC_DATA_CYCLE1(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_DATA_CYCLE2(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_DATA_CYCLE3(OMAP_DSS_CHANNEL_LCD2)); + + if (dss_has_feature(FEAT_CPR)) { + DUMPREG(DISPC_CPR_COEF_R(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_CPR_COEF_G(OMAP_DSS_CHANNEL_LCD2)); + DUMPREG(DISPC_CPR_COEF_B(OMAP_DSS_CHANNEL_LCD2)); + } + } + + if (dss_has_feature(FEAT_PRELOAD)) + DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_GFX)); + + DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO1)); + + DUMPREG(DISPC_OVL_BA0(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_BA1(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_POSITION(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_SIZE(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ATTRIBUTES(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_FIFO_THRESHOLD(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_FIFO_SIZE_STATUS(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ROW_INC(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_PIXEL_INC(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_FIR(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_PICTURE_SIZE(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ACCU0(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ACCU1(OMAP_DSS_VIDEO2)); + + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO1, 7)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO1, 7)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO1, 4)); + if (dss_has_feature(FEAT_FIR_COEF_V)) { + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO1, 7)); + } + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO1)); + + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO1, 7)); + + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO1, 7)); + + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO1, 7)); + } + if (dss_has_feature(FEAT_ATTR2)) + DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO1)); + + + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_H(OMAP_DSS_VIDEO2, 7)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_HV(OMAP_DSS_VIDEO2, 7)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_CONV_COEF(OMAP_DSS_VIDEO2, 4)); + + if (dss_has_feature(FEAT_FIR_COEF_V)) { + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_V(OMAP_DSS_VIDEO2, 7)); + } + + if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) { + DUMPREG(DISPC_OVL_BA0_UV(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_BA1_UV(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_FIR2(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ACCU2_0(OMAP_DSS_VIDEO2)); + DUMPREG(DISPC_OVL_ACCU2_1(OMAP_DSS_VIDEO2)); + + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_H2(OMAP_DSS_VIDEO2, 7)); + + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_HV2(OMAP_DSS_VIDEO2, 7)); + + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 0)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 1)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 2)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 3)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 4)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 5)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 6)); + DUMPREG(DISPC_OVL_FIR_COEF_V2(OMAP_DSS_VIDEO2, 7)); + } + if (dss_has_feature(FEAT_ATTR2)) + DUMPREG(DISPC_OVL_ATTRIBUTES2(OMAP_DSS_VIDEO2)); + + if (dss_has_feature(FEAT_PRELOAD)) { + DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO1)); + DUMPREG(DISPC_OVL_PRELOAD(OMAP_DSS_VIDEO2)); + } + + dispc_runtime_put(); #undef DUMPREG } @@ -2618,9 +2976,7 @@ static void _dispc_set_pol_freq(enum omap_channel channel, bool onoff, bool rf, l |= FLD_VAL(acbi, 11, 8); l |= FLD_VAL(acb, 7, 0); - enable_clocks(1); dispc_write_reg(DISPC_POL_FREQ(channel), l); - enable_clocks(0); } void dispc_set_pol_freq(enum omap_channel channel, @@ -2713,8 +3069,8 @@ int dispc_get_clock_div(enum omap_channel channel, fck = dispc_fclk_rate(); - cinfo->lck_div = REG_GET(DISPC_DIVISOR(channel), 23, 16); - cinfo->pck_div = REG_GET(DISPC_DIVISOR(channel), 7, 0); + cinfo->lck_div = REG_GET(DISPC_DIVISORo(channel), 23, 16); + cinfo->pck_div = REG_GET(DISPC_DIVISORo(channel), 7, 0); cinfo->lck = fck / cinfo->lck_div; cinfo->pck = cinfo->lck / cinfo->pck_div; @@ -2741,15 +3097,11 @@ static void _omap_dispc_set_irqs(void) mask |= isr_data->mask; } - enable_clocks(1); - old_mask = dispc_read_reg(DISPC_IRQENABLE); /* clear the irqstatus for newly enabled irqs */ dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask); dispc_write_reg(DISPC_IRQENABLE, mask); - - enable_clocks(0); } int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) @@ -2791,6 +3143,9 @@ int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) break; } + if (ret) + goto err; + _omap_dispc_set_irqs(); spin_unlock_irqrestore(&dispc.irq_lock, flags); @@ -2866,10 +3221,10 @@ static void print_irq_status(u32 status) * but we presume they are on because we got an IRQ. However, * an irq handler may turn the clocks off, so we may not have * clock later in the function. */ -void dispc_irq_handler(void) +static irqreturn_t omap_dispc_irq_handler(int irq, void *arg) { int i; - u32 irqstatus; + u32 irqstatus, irqenable; u32 handledirqs = 0; u32 unhandled_errors; struct omap_dispc_isr_data *isr_data; @@ -2878,6 +3233,13 @@ void dispc_irq_handler(void) spin_lock(&dispc.irq_lock); irqstatus = dispc_read_reg(DISPC_IRQSTATUS); + irqenable = dispc_read_reg(DISPC_IRQENABLE); + + /* IRQ is not for us */ + if (!(irqstatus & irqenable)) { + spin_unlock(&dispc.irq_lock); + return IRQ_NONE; + } #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS spin_lock(&dispc.irq_stats_lock); @@ -2929,6 +3291,8 @@ void dispc_irq_handler(void) } spin_unlock(&dispc.irq_lock); + + return IRQ_HANDLED; } static void dispc_error_worker(struct work_struct *work) @@ -3246,12 +3610,14 @@ static void _omap_dispc_initial_config(void) { u32 l; - l = dispc_read_reg(DISPC_SYSCONFIG); - l = FLD_MOD(l, 2, 13, 12); /* MIDLEMODE: smart standby */ - l = FLD_MOD(l, 2, 4, 3); /* SIDLEMODE: smart idle */ - l = FLD_MOD(l, 1, 2, 2); /* ENWAKEUP */ - l = FLD_MOD(l, 1, 0, 0); /* AUTOIDLE */ - dispc_write_reg(DISPC_SYSCONFIG, l); + /* Exclusively enable DISPC_CORE_CLK and set divider to 1 */ + if (dss_has_feature(FEAT_CORE_CLK_DIV)) { + l = dispc_read_reg(DISPC_DIVISOR); + /* Use DISPC_DIVISOR.LCD, instead of DISPC_DIVISOR1.LCD */ + l = FLD_MOD(l, 1, 0, 0); + l = FLD_MOD(l, 1, 23, 16); + dispc_write_reg(DISPC_DIVISOR, l); + } /* FUNCGATED */ if (dss_has_feature(FEAT_FUNCGATED)) @@ -3267,11 +3633,28 @@ static void _omap_dispc_initial_config(void) dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); dispc_read_plane_fifo_sizes(); + + dispc_configure_burst_sizes(); } -int dispc_init(void) +/* DISPC HW IP initialisation */ +static int omap_dispchw_probe(struct platform_device *pdev) { u32 rev; + int r = 0; + struct resource *dispc_mem; + struct clk *clk; + + dispc.pdev = pdev; + + clk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(clk)) { + DSSERR("can't get fck\n"); + r = PTR_ERR(clk); + goto err_get_clk; + } + + dispc.dss_clk = clk; spin_lock_init(&dispc.irq_lock); @@ -3282,80 +3665,116 @@ int dispc_init(void) INIT_WORK(&dispc.error_work, dispc_error_worker); - dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS); + dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0); + if (!dispc_mem) { + DSSERR("can't get IORESOURCE_MEM DISPC\n"); + r = -EINVAL; + goto err_ioremap; + } + dispc.base = ioremap(dispc_mem->start, resource_size(dispc_mem)); if (!dispc.base) { DSSERR("can't ioremap DISPC\n"); - return -ENOMEM; + r = -ENOMEM; + goto err_ioremap; + } + dispc.irq = platform_get_irq(dispc.pdev, 0); + if (dispc.irq < 0) { + DSSERR("platform_get_irq failed\n"); + r = -ENODEV; + goto err_irq; + } + + r = request_irq(dispc.irq, omap_dispc_irq_handler, IRQF_SHARED, + "OMAP DISPC", dispc.pdev); + if (r < 0) { + DSSERR("request_irq failed\n"); + goto err_irq; } - enable_clocks(1); + pm_runtime_enable(&pdev->dev); + + r = dispc_runtime_get(); + if (r) + goto err_runtime_get; _omap_dispc_initial_config(); _omap_dispc_initialize_irq(); - dispc_save_context(); - rev = dispc_read_reg(DISPC_REVISION); - printk(KERN_INFO "OMAP DISPC rev %d.%d\n", + dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); - enable_clocks(0); + dispc_runtime_put(); return 0; + +err_runtime_get: + pm_runtime_disable(&pdev->dev); + free_irq(dispc.irq, dispc.pdev); +err_irq: + iounmap(dispc.base); +err_ioremap: + clk_put(dispc.dss_clk); +err_get_clk: + return r; } -void dispc_exit(void) +static int omap_dispchw_remove(struct platform_device *pdev) { + pm_runtime_disable(&pdev->dev); + + clk_put(dispc.dss_clk); + + free_irq(dispc.irq, dispc.pdev); iounmap(dispc.base); + return 0; } -int dispc_enable_plane(enum omap_plane plane, bool enable) +static int dispc_runtime_suspend(struct device *dev) { - DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); - - enable_clocks(1); - _dispc_enable_plane(plane, enable); - enable_clocks(0); + dispc_save_context(); + clk_disable(dispc.dss_clk); + dss_runtime_put(); return 0; } -int dispc_setup_plane(enum omap_plane plane, - u32 paddr, u16 screen_width, - u16 pos_x, u16 pos_y, - u16 width, u16 height, - u16 out_width, u16 out_height, - enum omap_color_mode color_mode, - bool ilace, - enum omap_dss_rotation_type rotation_type, - u8 rotation, bool mirror, u8 global_alpha, - u8 pre_mult_alpha, enum omap_channel channel) +static int dispc_runtime_resume(struct device *dev) { - int r = 0; + int r; - DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " - "%dx%d, ilace %d, cmode %x, rot %d, mir %d chan %d\n", - plane, paddr, screen_width, pos_x, pos_y, - width, height, - out_width, out_height, - ilace, color_mode, - rotation, mirror, channel); + r = dss_runtime_get(); + if (r < 0) + return r; - enable_clocks(1); + clk_enable(dispc.dss_clk); + dispc_restore_context(); - r = _dispc_setup_plane(plane, - paddr, screen_width, - pos_x, pos_y, - width, height, - out_width, out_height, - color_mode, ilace, - rotation_type, - rotation, mirror, - global_alpha, - pre_mult_alpha, channel); + return 0; +} - enable_clocks(0); +static const struct dev_pm_ops dispc_pm_ops = { + .runtime_suspend = dispc_runtime_suspend, + .runtime_resume = dispc_runtime_resume, +}; - return r; +static struct platform_driver omap_dispchw_driver = { + .probe = omap_dispchw_probe, + .remove = omap_dispchw_remove, + .driver = { + .name = "omapdss_dispc", + .owner = THIS_MODULE, + .pm = &dispc_pm_ops, + }, +}; + +int dispc_init_platform_driver(void) +{ + return platform_driver_register(&omap_dispchw_driver); +} + +void dispc_uninit_platform_driver(void) +{ + return platform_driver_unregister(&omap_dispchw_driver); } diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h new file mode 100644 index 00000000000..6c9ee0a0efb --- /dev/null +++ b/drivers/video/omap2/dss/dispc.h @@ -0,0 +1,691 @@ +/* + * linux/drivers/video/omap2/dss/dispc.h + * + * Copyright (C) 2011 Texas Instruments + * Author: Archit Taneja <archit@ti.com> + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAP2_DISPC_REG_H +#define __OMAP2_DISPC_REG_H + +/* DISPC common registers */ +#define DISPC_REVISION 0x0000 +#define DISPC_SYSCONFIG 0x0010 +#define DISPC_SYSSTATUS 0x0014 +#define DISPC_IRQSTATUS 0x0018 +#define DISPC_IRQENABLE 0x001C +#define DISPC_CONTROL 0x0040 +#define DISPC_CONFIG 0x0044 +#define DISPC_CAPABLE 0x0048 +#define DISPC_LINE_STATUS 0x005C +#define DISPC_LINE_NUMBER 0x0060 +#define DISPC_GLOBAL_ALPHA 0x0074 +#define DISPC_CONTROL2 0x0238 +#define DISPC_CONFIG2 0x0620 +#define DISPC_DIVISOR 0x0804 + +/* DISPC overlay registers */ +#define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \ + DISPC_BA0_OFFSET(n)) +#define DISPC_OVL_BA1(n) (DISPC_OVL_BASE(n) + \ + DISPC_BA1_OFFSET(n)) +#define DISPC_OVL_BA0_UV(n) (DISPC_OVL_BASE(n) + \ + DISPC_BA0_UV_OFFSET(n)) +#define DISPC_OVL_BA1_UV(n) (DISPC_OVL_BASE(n) + \ + DISPC_BA1_UV_OFFSET(n)) +#define DISPC_OVL_POSITION(n) (DISPC_OVL_BASE(n) + \ + DISPC_POS_OFFSET(n)) +#define DISPC_OVL_SIZE(n) (DISPC_OVL_BASE(n) + \ + DISPC_SIZE_OFFSET(n)) +#define DISPC_OVL_ATTRIBUTES(n) (DISPC_OVL_BASE(n) + \ + DISPC_ATTR_OFFSET(n)) +#define DISPC_OVL_ATTRIBUTES2(n) (DISPC_OVL_BASE(n) + \ + DISPC_ATTR2_OFFSET(n)) +#define DISPC_OVL_FIFO_THRESHOLD(n) (DISPC_OVL_BASE(n) + \ + DISPC_FIFO_THRESH_OFFSET(n)) +#define DISPC_OVL_FIFO_SIZE_STATUS(n) (DISPC_OVL_BASE(n) + \ + DISPC_FIFO_SIZE_STATUS_OFFSET(n)) +#define DISPC_OVL_ROW_INC(n) (DISPC_OVL_BASE(n) + \ + DISPC_ROW_INC_OFFSET(n)) +#define DISPC_OVL_PIXEL_INC(n) (DISPC_OVL_BASE(n) + \ + DISPC_PIX_INC_OFFSET(n)) +#define DISPC_OVL_WINDOW_SKIP(n) (DISPC_OVL_BASE(n) + \ + DISPC_WINDOW_SKIP_OFFSET(n)) +#define DISPC_OVL_TABLE_BA(n) (DISPC_OVL_BASE(n) + \ + DISPC_TABLE_BA_OFFSET(n)) +#define DISPC_OVL_FIR(n) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_OFFSET(n)) +#define DISPC_OVL_FIR2(n) (DISPC_OVL_BASE(n) + \ + DISPC_FIR2_OFFSET(n)) +#define DISPC_OVL_PICTURE_SIZE(n) (DISPC_OVL_BASE(n) + \ + DISPC_PIC_SIZE_OFFSET(n)) +#define DISPC_OVL_ACCU0(n) (DISPC_OVL_BASE(n) + \ + DISPC_ACCU0_OFFSET(n)) +#define DISPC_OVL_ACCU1(n) (DISPC_OVL_BASE(n) + \ + DISPC_ACCU1_OFFSET(n)) +#define DISPC_OVL_ACCU2_0(n) (DISPC_OVL_BASE(n) + \ + DISPC_ACCU2_0_OFFSET(n)) +#define DISPC_OVL_ACCU2_1(n) (DISPC_OVL_BASE(n) + \ + DISPC_ACCU2_1_OFFSET(n)) +#define DISPC_OVL_FIR_COEF_H(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_H_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_HV(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_HV_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_H2(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_H2_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_HV2(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_HV2_OFFSET(n, i)) +#define DISPC_OVL_CONV_COEF(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_CONV_COEF_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_V(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_V_OFFSET(n, i)) +#define DISPC_OVL_FIR_COEF_V2(n, i) (DISPC_OVL_BASE(n) + \ + DISPC_FIR_COEF_V2_OFFSET(n, i)) +#define DISPC_OVL_PRELOAD(n) (DISPC_OVL_BASE(n) + \ + DISPC_PRELOAD_OFFSET(n)) + +/* DISPC manager/channel specific registers */ +static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x004C; + case OMAP_DSS_CHANNEL_DIGIT: + return 0x0050; + case OMAP_DSS_CHANNEL_LCD2: + return 0x03AC; + default: + BUG(); + } +} + +static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0054; + case OMAP_DSS_CHANNEL_DIGIT: + return 0x0058; + case OMAP_DSS_CHANNEL_LCD2: + return 0x03B0; + default: + BUG(); + } +} + +static inline u16 DISPC_TIMING_H(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0064; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x0400; + default: + BUG(); + } +} + +static inline u16 DISPC_TIMING_V(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0068; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x0404; + default: + BUG(); + } +} + +static inline u16 DISPC_POL_FREQ(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x006C; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x0408; + default: + BUG(); + } +} + +static inline u16 DISPC_DIVISORo(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0070; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x040C; + default: + BUG(); + } +} + +/* Named as DISPC_SIZE_LCD, DISPC_SIZE_DIGIT and DISPC_SIZE_LCD2 in TRM */ +static inline u16 DISPC_SIZE_MGR(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x007C; + case OMAP_DSS_CHANNEL_DIGIT: + return 0x0078; + case OMAP_DSS_CHANNEL_LCD2: + return 0x03CC; + default: + BUG(); + } +} + +static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x01D4; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03C0; + default: + BUG(); + } +} + +static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x01D8; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03C4; + default: + BUG(); + } +} + +static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x01DC; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03C8; + default: + BUG(); + } +} + +static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0220; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03BC; + default: + BUG(); + } +} + +static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0224; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03B8; + default: + BUG(); + } +} + +static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel) +{ + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + return 0x0228; + case OMAP_DSS_CHANNEL_DIGIT: + BUG(); + case OMAP_DSS_CHANNEL_LCD2: + return 0x03B4; + default: + BUG(); + } +} + +/* DISPC overlay register base addresses */ +static inline u16 DISPC_OVL_BASE(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0080; + case OMAP_DSS_VIDEO1: + return 0x00BC; + case OMAP_DSS_VIDEO2: + return 0x014C; + default: + BUG(); + } +} + +/* DISPC overlay register offsets */ +static inline u16 DISPC_BA0_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0000; + default: + BUG(); + } +} + +static inline u16 DISPC_BA1_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0004; + default: + BUG(); + } +} + +static inline u16 DISPC_BA0_UV_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0544; + case OMAP_DSS_VIDEO2: + return 0x04BC; + default: + BUG(); + } +} + +static inline u16 DISPC_BA1_UV_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0548; + case OMAP_DSS_VIDEO2: + return 0x04C0; + default: + BUG(); + } +} + +static inline u16 DISPC_POS_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0008; + default: + BUG(); + } +} + +static inline u16 DISPC_SIZE_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x000C; + default: + BUG(); + } +} + +static inline u16 DISPC_ATTR_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0020; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0010; + default: + BUG(); + } +} + +static inline u16 DISPC_ATTR2_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0568; + case OMAP_DSS_VIDEO2: + return 0x04DC; + default: + BUG(); + } +} + +static inline u16 DISPC_FIFO_THRESH_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0024; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0014; + default: + BUG(); + } +} + +static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0028; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0018; + default: + BUG(); + } +} + +static inline u16 DISPC_ROW_INC_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x002C; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x001C; + default: + BUG(); + } +} + +static inline u16 DISPC_PIX_INC_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0030; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0020; + default: + BUG(); + } +} + +static inline u16 DISPC_WINDOW_SKIP_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0034; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + BUG(); + default: + BUG(); + } +} + +static inline u16 DISPC_TABLE_BA_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x0038; + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + BUG(); + default: + BUG(); + } +} + +static inline u16 DISPC_FIR_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0024; + default: + BUG(); + } +} + +static inline u16 DISPC_FIR2_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0580; + case OMAP_DSS_VIDEO2: + return 0x055C; + default: + BUG(); + } +} + +static inline u16 DISPC_PIC_SIZE_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0028; + default: + BUG(); + } +} + + +static inline u16 DISPC_ACCU0_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x002C; + default: + BUG(); + } +} + +static inline u16 DISPC_ACCU2_0_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0584; + case OMAP_DSS_VIDEO2: + return 0x0560; + default: + BUG(); + } +} + +static inline u16 DISPC_ACCU1_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0030; + default: + BUG(); + } +} + +static inline u16 DISPC_ACCU2_1_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0588; + case OMAP_DSS_VIDEO2: + return 0x0564; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_H_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0034 + i * 0x8; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_H2_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x058C + i * 0x8; + case OMAP_DSS_VIDEO2: + return 0x0568 + i * 0x8; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_HV_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0038 + i * 0x8; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_HV2_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0590 + i * 8; + case OMAP_DSS_VIDEO2: + return 0x056C + i * 0x8; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4,} */ +static inline u16 DISPC_CONV_COEF_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + case OMAP_DSS_VIDEO2: + return 0x0074 + i * 0x4; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_V_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x0124 + i * 0x4; + case OMAP_DSS_VIDEO2: + return 0x00B4 + i * 0x4; + default: + BUG(); + } +} + +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ +static inline u16 DISPC_FIR_COEF_V2_OFFSET(enum omap_plane plane, u16 i) +{ + switch (plane) { + case OMAP_DSS_GFX: + BUG(); + case OMAP_DSS_VIDEO1: + return 0x05CC + i * 0x4; + case OMAP_DSS_VIDEO2: + return 0x05A8 + i * 0x4; + default: + BUG(); + } +} + +static inline u16 DISPC_PRELOAD_OFFSET(enum omap_plane plane) +{ + switch (plane) { + case OMAP_DSS_GFX: + return 0x01AC; + case OMAP_DSS_VIDEO1: + return 0x0174; + case OMAP_DSS_VIDEO2: + return 0x00E8; + default: + BUG(); + } +} +#endif diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 22dd7a474f7..94495e45ec5 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c @@ -25,13 +25,11 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/jiffies.h> -#include <linux/list.h> #include <linux/platform_device.h> -#include <plat/display.h> +#include <video/omapdss.h> #include "dss.h" - -static LIST_HEAD(display_list); +#include "dss_features.h" static ssize_t display_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -47,9 +45,13 @@ static ssize_t display_enabled_store(struct device *dev, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - bool enabled, r; + int r, enabled; - enabled = simple_strtoul(buf, NULL, 10); + r = kstrtoint(buf, 0, &enabled); + if (r) + return r; + + enabled = !!enabled; if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { if (enabled) { @@ -64,46 +66,6 @@ static ssize_t display_enabled_store(struct device *dev, return size; } -static ssize_t display_upd_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_dss_device *dssdev = to_dss_device(dev); - enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO; - if (dssdev->driver->get_update_mode) - mode = dssdev->driver->get_update_mode(dssdev); - return snprintf(buf, PAGE_SIZE, "%d\n", mode); -} - -static ssize_t display_upd_mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct omap_dss_device *dssdev = to_dss_device(dev); - int val, r; - enum omap_dss_update_mode mode; - - if (!dssdev->driver->set_update_mode) - return -EINVAL; - - val = simple_strtoul(buf, NULL, 10); - - switch (val) { - case OMAP_DSS_UPDATE_DISABLED: - case OMAP_DSS_UPDATE_AUTO: - case OMAP_DSS_UPDATE_MANUAL: - mode = (enum omap_dss_update_mode)val; - break; - default: - return -EINVAL; - } - - r = dssdev->driver->set_update_mode(dssdev, mode); - if (r) - return r; - - return size; -} - static ssize_t display_tear_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -117,13 +79,16 @@ static ssize_t display_tear_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - unsigned long te; - int r; + int te, r; if (!dssdev->driver->enable_te || !dssdev->driver->get_te) return -ENOENT; - te = simple_strtoul(buf, NULL, 0); + r = kstrtoint(buf, 0, &te); + if (r) + return r; + + te = !!te; r = dssdev->driver->enable_te(dssdev, te); if (r) @@ -199,13 +164,14 @@ static ssize_t display_rotate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - unsigned long rot; - int r; + int rot, r; if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) return -ENOENT; - rot = simple_strtoul(buf, NULL, 0); + r = kstrtoint(buf, 0, &rot); + if (r) + return r; r = dssdev->driver->set_rotate(dssdev, rot); if (r) @@ -229,13 +195,16 @@ static ssize_t display_mirror_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - unsigned long mirror; - int r; + int mirror, r; if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) return -ENOENT; - mirror = simple_strtoul(buf, NULL, 0); + r = kstrtoint(buf, 0, &mirror); + if (r) + return r; + + mirror = !!mirror; r = dssdev->driver->set_mirror(dssdev, mirror); if (r) @@ -262,14 +231,15 @@ static ssize_t display_wss_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct omap_dss_device *dssdev = to_dss_device(dev); - unsigned long wss; + u32 wss; int r; if (!dssdev->driver->get_wss || !dssdev->driver->set_wss) return -ENOENT; - if (strict_strtoul(buf, 0, &wss)) - return -EINVAL; + r = kstrtou32(buf, 0, &wss); + if (r) + return r; if (wss > 0xfffff) return -EINVAL; @@ -283,8 +253,6 @@ static ssize_t display_wss_store(struct device *dev, static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, display_enabled_show, display_enabled_store); -static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR, - display_upd_mode_show, display_upd_mode_store); static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, display_tear_show, display_tear_store); static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, @@ -298,7 +266,6 @@ static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, static struct device_attribute *display_sysfs_attrs[] = { &dev_attr_enabled, - &dev_attr_update_mode, &dev_attr_tear_elim, &dev_attr_timings, &dev_attr_rotate, @@ -316,16 +283,13 @@ void omapdss_default_get_resolution(struct omap_dss_device *dssdev, EXPORT_SYMBOL(omapdss_default_get_resolution); void default_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, enum omap_burst_size *burst_size, + u32 fifo_size, u32 burst_size, u32 *fifo_low, u32 *fifo_high) { - unsigned burst_size_bytes; - - *burst_size = OMAP_DSS_BURST_16x32; - burst_size_bytes = 16 * 32 / 8; + unsigned buf_unit = dss_feat_get_buffer_size_unit(); - *fifo_high = fifo_size - 1; - *fifo_low = fifo_size - burst_size_bytes; + *fifo_high = fifo_size - buf_unit; + *fifo_low = fifo_size - burst_size; } int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) @@ -345,6 +309,7 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) return 16; case OMAP_DISPLAY_TYPE_VENC: case OMAP_DISPLAY_TYPE_SDI: + case OMAP_DISPLAY_TYPE_HDMI: return 24; default: BUG(); @@ -371,6 +336,7 @@ bool dss_use_replication(struct omap_dss_device *dssdev, case OMAP_DISPLAY_TYPE_DPI: bpp = dssdev->phy.dpi.data_lines; break; + case OMAP_DISPLAY_TYPE_HDMI: case OMAP_DISPLAY_TYPE_VENC: case OMAP_DISPLAY_TYPE_SDI: bpp = 24; @@ -396,29 +362,6 @@ void dss_init_device(struct platform_device *pdev, switch (dssdev->type) { #ifdef CONFIG_OMAP2_DSS_DPI case OMAP_DISPLAY_TYPE_DPI: -#endif -#ifdef CONFIG_OMAP2_DSS_RFBI - case OMAP_DISPLAY_TYPE_DBI: -#endif -#ifdef CONFIG_OMAP2_DSS_SDI - case OMAP_DISPLAY_TYPE_SDI: -#endif -#ifdef CONFIG_OMAP2_DSS_DSI - case OMAP_DISPLAY_TYPE_DSI: -#endif -#ifdef CONFIG_OMAP2_DSS_VENC - case OMAP_DISPLAY_TYPE_VENC: -#endif - break; - default: - DSSERR("Support for display '%s' not compiled in.\n", - dssdev->name); - return; - } - - switch (dssdev->type) { -#ifdef CONFIG_OMAP2_DSS_DPI - case OMAP_DISPLAY_TYPE_DPI: r = dpi_init_display(dssdev); break; #endif @@ -442,8 +385,13 @@ void dss_init_device(struct platform_device *pdev, r = dsi_init_display(dssdev); break; #endif + case OMAP_DISPLAY_TYPE_HDMI: + r = hdmi_init_display(dssdev); + break; default: - BUG(); + DSSERR("Support for display '%s' not compiled in.\n", + dssdev->name); + return; } if (r) { diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 75fb0a51543..f053b180ecd 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c @@ -23,23 +23,46 @@ #define DSS_SUBSYS_NAME "DPI" #include <linux/kernel.h> -#include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" static struct { struct regulator *vdds_dsi_reg; + struct platform_device *dsidev; } dpi; -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL +static struct platform_device *dpi_get_dsidev(enum omap_dss_clk_source clk) +{ + int dsi_module; + + dsi_module = clk == OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ? 0 : 1; + + return dsi_get_dsidev_from_id(dsi_module); +} + +static bool dpi_use_dsi_pll(struct omap_dss_device *dssdev) +{ + if (dssdev->clocks.dispc.dispc_fclk_src == + OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC || + dssdev->clocks.dispc.dispc_fclk_src == + OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC || + dssdev->clocks.dispc.channel.lcd_clk_src == + OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC || + dssdev->clocks.dispc.channel.lcd_clk_src == + OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC) + return true; + else + return false; +} + static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft, unsigned long pck_req, unsigned long *fck, int *lck_div, int *pck_div) @@ -48,28 +71,28 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft, struct dispc_clock_info dispc_cinfo; int r; - r = dsi_pll_calc_clock_div_pck(is_tft, pck_req, &dsi_cinfo, - &dispc_cinfo); + r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft, pck_req, + &dsi_cinfo, &dispc_cinfo); if (r) return r; - r = dsi_pll_set_clock_div(&dsi_cinfo); + r = dsi_pll_set_clock_div(dpi.dsidev, &dsi_cinfo); if (r) return r; - dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK); + dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) return r; - *fck = dsi_cinfo.dsi1_pll_fclk; + *fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk; *lck_div = dispc_cinfo.lck_div; *pck_div = dispc_cinfo.pck_div; return 0; } -#else + static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft, unsigned long pck_req, unsigned long *fck, int *lck_div, int *pck_div) @@ -96,33 +119,29 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft, return 0; } -#endif static int dpi_set_mode(struct omap_dss_device *dssdev) { struct omap_video_timings *t = &dssdev->panel.timings; - int lck_div, pck_div; - unsigned long fck; + int lck_div = 0, pck_div = 0; + unsigned long fck = 0; unsigned long pck; bool is_tft; int r = 0; - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config, dssdev->panel.acbi, dssdev->panel.acb); is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - r = dpi_set_dsi_clk(dssdev, is_tft, t->pixel_clock * 1000, &fck, - &lck_div, &pck_div); -#else - r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000, &fck, - &lck_div, &pck_div); -#endif + if (dpi_use_dsi_pll(dssdev)) + r = dpi_set_dsi_clk(dssdev, is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); + else + r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000, + &fck, &lck_div, &pck_div); if (r) - goto err0; + return r; pck = fck / lck_div / pck_div / 1000; @@ -136,12 +155,10 @@ static int dpi_set_mode(struct omap_dss_device *dssdev) dispc_set_lcd_timings(dssdev->manager->id, t); -err0: - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); - return r; + return 0; } -static int dpi_basic_init(struct omap_dss_device *dssdev) +static void dpi_basic_init(struct omap_dss_device *dssdev) { bool is_tft; @@ -153,8 +170,6 @@ static int dpi_basic_init(struct omap_dss_device *dssdev) OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN); dispc_set_tft_data_lines(dssdev->manager->id, dssdev->phy.dpi.data_lines); - - return 0; } int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) @@ -164,30 +179,38 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); - goto err0; + goto err_start_dev; } if (cpu_is_omap34xx()) { r = regulator_enable(dpi.vdds_dsi_reg); if (r) - goto err1; + goto err_reg_enable; } - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - - r = dpi_basic_init(dssdev); + r = dss_runtime_get(); if (r) - goto err2; + goto err_get_dss; -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - dss_clk_enable(DSS_CLK_FCK2); - r = dsi_pll_init(dssdev, 0, 1); + r = dispc_runtime_get(); if (r) - goto err3; -#endif + goto err_get_dispc; + + dpi_basic_init(dssdev); + + if (dpi_use_dsi_pll(dssdev)) { + r = dsi_runtime_get(dpi.dsidev); + if (r) + goto err_get_dsi; + + r = dsi_pll_init(dpi.dsidev, 0, 1); + if (r) + goto err_dsi_pll_init; + } + r = dpi_set_mode(dssdev); if (r) - goto err4; + goto err_set_mode; mdelay(2); @@ -195,19 +218,22 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev) return 0; -err4: -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - dsi_pll_uninit(); -err3: - dss_clk_disable(DSS_CLK_FCK2); -#endif -err2: - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); +err_set_mode: + if (dpi_use_dsi_pll(dssdev)) + dsi_pll_uninit(dpi.dsidev, true); +err_dsi_pll_init: + if (dpi_use_dsi_pll(dssdev)) + dsi_runtime_put(dpi.dsidev); +err_get_dsi: + dispc_runtime_put(); +err_get_dispc: + dss_runtime_put(); +err_get_dss: if (cpu_is_omap34xx()) regulator_disable(dpi.vdds_dsi_reg); -err1: +err_reg_enable: omap_dss_stop_device(dssdev); -err0: +err_start_dev: return r; } EXPORT_SYMBOL(omapdss_dpi_display_enable); @@ -216,13 +242,14 @@ void omapdss_dpi_display_disable(struct omap_dss_device *dssdev) { dssdev->manager->disable(dssdev->manager); -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK); - dsi_pll_uninit(); - dss_clk_disable(DSS_CLK_FCK2); -#endif + if (dpi_use_dsi_pll(dssdev)) { + dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); + dsi_pll_uninit(dpi.dsidev, true); + dsi_runtime_put(dpi.dsidev); + } - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + dispc_runtime_put(); + dss_runtime_put(); if (cpu_is_omap34xx()) regulator_disable(dpi.vdds_dsi_reg); @@ -234,11 +261,26 @@ EXPORT_SYMBOL(omapdss_dpi_display_disable); void dpi_set_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { + int r; + DSSDBG("dpi_set_timings\n"); dssdev->panel.timings = *timings; if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + r = dss_runtime_get(); + if (r) + return; + + r = dispc_runtime_get(); + if (r) { + dss_runtime_put(); + return; + } + dpi_set_mode(dssdev); dispc_go(dssdev->manager->id); + + dispc_runtime_put(); + dss_runtime_put(); } } EXPORT_SYMBOL(dpi_set_timings); @@ -251,6 +293,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev, int lck_div, pck_div; unsigned long fck; unsigned long pck; + struct dispc_clock_info dispc_cinfo; if (!dispc_lcd_timings_ok(timings)) return -EINVAL; @@ -260,25 +303,18 @@ int dpi_check_timings(struct omap_dss_device *dssdev, is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; -#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL - { + if (dpi_use_dsi_pll(dssdev)) { struct dsi_clock_info dsi_cinfo; - struct dispc_clock_info dispc_cinfo; - r = dsi_pll_calc_clock_div_pck(is_tft, + r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft, timings->pixel_clock * 1000, &dsi_cinfo, &dispc_cinfo); if (r) return r; - fck = dsi_cinfo.dsi1_pll_fclk; - lck_div = dispc_cinfo.lck_div; - pck_div = dispc_cinfo.pck_div; - } -#else - { + fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk; + } else { struct dss_clock_info dss_cinfo; - struct dispc_clock_info dispc_cinfo; r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo); @@ -286,10 +322,10 @@ int dpi_check_timings(struct omap_dss_device *dssdev, return r; fck = dss_cinfo.fck; - lck_div = dispc_cinfo.lck_div; - pck_div = dispc_cinfo.pck_div; } -#endif + + lck_div = dispc_cinfo.lck_div; + pck_div = dispc_cinfo.pck_div; pck = fck / lck_div / pck_div / 1000; @@ -303,19 +339,30 @@ int dpi_init_display(struct omap_dss_device *dssdev) { DSSDBG("init_display\n"); - return 0; -} + if (cpu_is_omap34xx() && dpi.vdds_dsi_reg == NULL) { + struct regulator *vdds_dsi; -int dpi_init(struct platform_device *pdev) -{ - if (cpu_is_omap34xx()) { - dpi.vdds_dsi_reg = dss_get_vdds_dsi(); - if (IS_ERR(dpi.vdds_dsi_reg)) { + vdds_dsi = dss_get_vdds_dsi(); + + if (IS_ERR(vdds_dsi)) { DSSERR("can't get VDDS_DSI regulator\n"); - return PTR_ERR(dpi.vdds_dsi_reg); + return PTR_ERR(vdds_dsi); } + + dpi.vdds_dsi_reg = vdds_dsi; } + if (dpi_use_dsi_pll(dssdev)) { + enum omap_dss_clk_source dispc_fclk_src = + dssdev->clocks.dispc.dispc_fclk_src; + dpi.dsidev = dpi_get_dsidev(dispc_fclk_src); + } + + return 0; +} + +int dpi_init(void) +{ return 0; } diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index ddf3a056082..7adbbeb8433 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c @@ -33,17 +33,20 @@ #include <linux/regulator/consumer.h> #include <linux/wait.h> #include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/debugfs.h> +#include <linux/pm_runtime.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/clock.h> #include "dss.h" +#include "dss_features.h" /*#define VERBOSE_IRQ*/ #define DSI_CATCH_MISSING_TE -#define DSI_BASE 0x4804FC00 - struct dsi_reg { u16 idx; }; #define DSI_REG(idx) ((const struct dsi_reg) { idx }) @@ -57,6 +60,7 @@ struct dsi_reg { u16 idx; }; #define DSI_IRQSTATUS DSI_REG(0x0018) #define DSI_IRQENABLE DSI_REG(0x001C) #define DSI_CTRL DSI_REG(0x0040) +#define DSI_GNQ DSI_REG(0x0044) #define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048) #define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C) #define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050) @@ -91,6 +95,7 @@ struct dsi_reg { u16 idx; }; #define DSI_DSIPHY_CFG1 DSI_REG(0x200 + 0x0004) #define DSI_DSIPHY_CFG2 DSI_REG(0x200 + 0x0008) #define DSI_DSIPHY_CFG5 DSI_REG(0x200 + 0x0014) +#define DSI_DSIPHY_CFG10 DSI_REG(0x200 + 0x0028) /* DSI_PLL_CTRL_SCP */ @@ -100,11 +105,11 @@ struct dsi_reg { u16 idx; }; #define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C) #define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010) -#define REG_GET(idx, start, end) \ - FLD_GET(dsi_read_reg(idx), start, end) +#define REG_GET(dsidev, idx, start, end) \ + FLD_GET(dsi_read_reg(dsidev, idx), start, end) -#define REG_FLD_MOD(idx, val, start, end) \ - dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end)) +#define REG_FLD_MOD(dsidev, idx, val, start, end) \ + dsi_write_reg(dsidev, idx, FLD_MOD(dsi_read_reg(dsidev, idx), val, start, end)) /* Global interrupts */ #define DSI_IRQ_VC0 (1 << 0) @@ -148,31 +153,50 @@ struct dsi_reg { u16 idx; }; #define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0) #define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1) #define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2) +#define DSI_CIO_IRQ_ERRSYNCESC4 (1 << 3) +#define DSI_CIO_IRQ_ERRSYNCESC5 (1 << 4) #define DSI_CIO_IRQ_ERRESC1 (1 << 5) #define DSI_CIO_IRQ_ERRESC2 (1 << 6) #define DSI_CIO_IRQ_ERRESC3 (1 << 7) +#define DSI_CIO_IRQ_ERRESC4 (1 << 8) +#define DSI_CIO_IRQ_ERRESC5 (1 << 9) #define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10) #define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11) #define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12) +#define DSI_CIO_IRQ_ERRCONTROL4 (1 << 13) +#define DSI_CIO_IRQ_ERRCONTROL5 (1 << 14) #define DSI_CIO_IRQ_STATEULPS1 (1 << 15) #define DSI_CIO_IRQ_STATEULPS2 (1 << 16) #define DSI_CIO_IRQ_STATEULPS3 (1 << 17) +#define DSI_CIO_IRQ_STATEULPS4 (1 << 18) +#define DSI_CIO_IRQ_STATEULPS5 (1 << 19) #define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20) #define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21) #define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22) #define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23) #define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24) #define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_4 (1 << 26) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_4 (1 << 27) +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_5 (1 << 28) +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_5 (1 << 29) #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) #define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) #define DSI_CIO_IRQ_ERROR_MASK \ (DSI_CIO_IRQ_ERRSYNCESC1 | DSI_CIO_IRQ_ERRSYNCESC2 | \ - DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \ - DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRCONTROL1 | \ - DSI_CIO_IRQ_ERRCONTROL2 | DSI_CIO_IRQ_ERRCONTROL3 | \ + DSI_CIO_IRQ_ERRSYNCESC3 | DSI_CIO_IRQ_ERRSYNCESC4 | \ + DSI_CIO_IRQ_ERRSYNCESC5 | \ + DSI_CIO_IRQ_ERRESC1 | DSI_CIO_IRQ_ERRESC2 | \ + DSI_CIO_IRQ_ERRESC3 | DSI_CIO_IRQ_ERRESC4 | \ + DSI_CIO_IRQ_ERRESC5 | \ + DSI_CIO_IRQ_ERRCONTROL1 | DSI_CIO_IRQ_ERRCONTROL2 | \ + DSI_CIO_IRQ_ERRCONTROL3 | DSI_CIO_IRQ_ERRCONTROL4 | \ + DSI_CIO_IRQ_ERRCONTROL5 | \ DSI_CIO_IRQ_ERRCONTENTIONLP0_1 | DSI_CIO_IRQ_ERRCONTENTIONLP1_1 | \ DSI_CIO_IRQ_ERRCONTENTIONLP0_2 | DSI_CIO_IRQ_ERRCONTENTIONLP1_2 | \ - DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3) + DSI_CIO_IRQ_ERRCONTENTIONLP0_3 | DSI_CIO_IRQ_ERRCONTENTIONLP1_3 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_4 | DSI_CIO_IRQ_ERRCONTENTIONLP1_4 | \ + DSI_CIO_IRQ_ERRCONTENTIONLP0_5 | DSI_CIO_IRQ_ERRCONTENTIONLP1_5) #define DSI_DT_DCS_SHORT_WRITE_0 0x05 #define DSI_DT_DCS_SHORT_WRITE_1 0x15 @@ -186,13 +210,15 @@ struct dsi_reg { u16 idx; }; #define DSI_DT_RX_SHORT_READ_1 0x21 #define DSI_DT_RX_SHORT_READ_2 0x22 -#define FINT_MAX 2100000 -#define FINT_MIN 750000 -#define REGN_MAX (1 << 7) -#define REGM_MAX ((1 << 11) - 1) -#define REGM3_MAX (1 << 4) -#define REGM4_MAX (1 << 4) -#define LP_DIV_MAX ((1 << 13) - 1) +typedef void (*omap_dsi_isr_t) (void *arg, u32 mask); + +#define DSI_MAX_NR_ISRS 2 + +struct dsi_isr_data { + omap_dsi_isr_t isr; + void *arg; + u32 mask; +}; enum fifo_size { DSI_FIFO_SIZE_0 = 0, @@ -207,6 +233,19 @@ enum dsi_vc_mode { DSI_VC_MODE_VP, }; +enum dsi_lane { + DSI_CLK_P = 1 << 0, + DSI_CLK_N = 1 << 1, + DSI_DATA1_P = 1 << 2, + DSI_DATA1_N = 1 << 3, + DSI_DATA2_P = 1 << 4, + DSI_DATA2_N = 1 << 5, + DSI_DATA3_P = 1 << 6, + DSI_DATA3_N = 1 << 7, + DSI_DATA4_P = 1 << 8, + DSI_DATA4_N = 1 << 9, +}; + struct dsi_update_region { u16 x, y, w, h; struct omap_dss_device *device; @@ -220,18 +259,33 @@ struct dsi_irq_stats { unsigned cio_irqs[32]; }; -static struct -{ +struct dsi_isr_tables { + struct dsi_isr_data isr_table[DSI_MAX_NR_ISRS]; + struct dsi_isr_data isr_table_vc[4][DSI_MAX_NR_ISRS]; + struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS]; +}; + +struct dsi_data { + struct platform_device *pdev; void __iomem *base; + int irq; + + struct clk *dss_clk; + struct clk *sys_clk; + + void (*dsi_mux_pads)(bool enable); + struct dsi_clock_info current_cinfo; + bool vdds_dsi_enabled; struct regulator *vdds_dsi_reg; struct { enum dsi_vc_mode mode; struct omap_dss_device *dssdev; enum fifo_size fifo_size; + int vc_id; } vc[4]; struct mutex lock; @@ -239,15 +293,16 @@ static struct unsigned pll_locked; - struct completion bta_completion; - void (*bta_callback)(void); + spinlock_t irq_lock; + struct dsi_isr_tables isr_tables; + /* space for a copy used by the interrupt handler */ + struct dsi_isr_tables isr_tables_copy; int update_channel; struct dsi_update_region update_region; bool te_enabled; - - struct workqueue_struct *workqueue; + bool ulps_enabled; void (*framedone_callback)(int, void *); void *framedone_data; @@ -275,55 +330,106 @@ static struct spinlock_t irq_stats_lock; struct dsi_irq_stats irq_stats; #endif -} dsi; + /* DSI PLL Parameter Ranges */ + unsigned long regm_max, regn_max; + unsigned long regm_dispc_max, regm_dsi_max; + unsigned long fint_min, fint_max; + unsigned long lpdiv_max; + + int num_data_lanes; + + unsigned scp_clk_refcount; +}; + +struct dsi_packet_sent_handler_data { + struct platform_device *dsidev; + struct completion *completion; +}; + +static struct platform_device *dsi_pdev_map[MAX_NUM_DSI]; #ifdef DEBUG static unsigned int dsi_perf; module_param_named(dsi_perf, dsi_perf, bool, 0644); #endif -static inline void dsi_write_reg(const struct dsi_reg idx, u32 val) +static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dsidev) { - __raw_writel(val, dsi.base + idx.idx); + return dev_get_drvdata(&dsidev->dev); } -static inline u32 dsi_read_reg(const struct dsi_reg idx) +static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev) { - return __raw_readl(dsi.base + idx.idx); + return dsi_pdev_map[dssdev->phy.dsi.module]; } +struct platform_device *dsi_get_dsidev_from_id(int module) +{ + return dsi_pdev_map[module]; +} -void dsi_save_context(void) +static int dsi_get_dsidev_id(struct platform_device *dsidev) { + /* TEMP: Pass 0 as the dsi module index till the time the dsi platform + * device names aren't changed to the form "omapdss_dsi.0", + * "omapdss_dsi.1" and so on */ + BUG_ON(dsidev->id != -1); + + return 0; } -void dsi_restore_context(void) +static inline void dsi_write_reg(struct platform_device *dsidev, + const struct dsi_reg idx, u32 val) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + __raw_writel(val, dsi->base + idx.idx); +} + +static inline u32 dsi_read_reg(struct platform_device *dsidev, + const struct dsi_reg idx) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return __raw_readl(dsi->base + idx.idx); } -void dsi_bus_lock(void) +void dsi_bus_lock(struct omap_dss_device *dssdev) { - down(&dsi.bus_lock); + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + down(&dsi->bus_lock); } EXPORT_SYMBOL(dsi_bus_lock); -void dsi_bus_unlock(void) +void dsi_bus_unlock(struct omap_dss_device *dssdev) { - up(&dsi.bus_lock); + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + up(&dsi->bus_lock); } EXPORT_SYMBOL(dsi_bus_unlock); -static bool dsi_bus_is_locked(void) +static bool dsi_bus_is_locked(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return dsi->bus_lock.count == 0; +} + +static void dsi_completion_handler(void *data, u32 mask) { - return dsi.bus_lock.count == 0; + complete((struct completion *)data); } -static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, - int value) +static inline int wait_for_bit_change(struct platform_device *dsidev, + const struct dsi_reg idx, int bitnum, int value) { int t = 100000; - while (REG_GET(idx, bitnum, bitnum) != value) { + while (REG_GET(dsidev, idx, bitnum, bitnum) != value) { if (--t == 0) return !value; } @@ -332,18 +438,21 @@ static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, } #ifdef DEBUG -static void dsi_perf_mark_setup(void) +static void dsi_perf_mark_setup(struct platform_device *dsidev) { - dsi.perf_setup_time = ktime_get(); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + dsi->perf_setup_time = ktime_get(); } -static void dsi_perf_mark_start(void) +static void dsi_perf_mark_start(struct platform_device *dsidev) { - dsi.perf_start_time = ktime_get(); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + dsi->perf_start_time = ktime_get(); } -static void dsi_perf_show(const char *name) +static void dsi_perf_show(struct platform_device *dsidev, const char *name) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); ktime_t t, setup_time, trans_time; u32 total_bytes; u32 setup_us, trans_us, total_us; @@ -353,21 +462,21 @@ static void dsi_perf_show(const char *name) t = ktime_get(); - setup_time = ktime_sub(dsi.perf_start_time, dsi.perf_setup_time); + setup_time = ktime_sub(dsi->perf_start_time, dsi->perf_setup_time); setup_us = (u32)ktime_to_us(setup_time); if (setup_us == 0) setup_us = 1; - trans_time = ktime_sub(t, dsi.perf_start_time); + trans_time = ktime_sub(t, dsi->perf_start_time); trans_us = (u32)ktime_to_us(trans_time); if (trans_us == 0) trans_us = 1; total_us = setup_us + trans_us; - total_bytes = dsi.update_region.w * - dsi.update_region.h * - dsi.update_region.device->ctrl.pixel_size / 8; + total_bytes = dsi->update_region.w * + dsi->update_region.h * + dsi->update_region.device->ctrl.pixel_size / 8; printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), " "%u bytes, %u kbytes/sec\n", @@ -380,13 +489,25 @@ static void dsi_perf_show(const char *name) total_bytes * 1000 / total_us); } #else -#define dsi_perf_mark_setup() -#define dsi_perf_mark_start() -#define dsi_perf_show(x) +static inline void dsi_perf_mark_setup(struct platform_device *dsidev) +{ +} + +static inline void dsi_perf_mark_start(struct platform_device *dsidev) +{ +} + +static inline void dsi_perf_show(struct platform_device *dsidev, + const char *name) +{ +} #endif static void print_irq_status(u32 status) { + if (status == 0) + return; + #ifndef VERBOSE_IRQ if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0) return; @@ -422,6 +543,9 @@ static void print_irq_status(u32 status) static void print_irq_status_vc(int channel, u32 status) { + if (status == 0) + return; + #ifndef VERBOSE_IRQ if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0) return; @@ -448,6 +572,9 @@ static void print_irq_status_vc(int channel, u32 status) static void print_irq_status_cio(u32 status) { + if (status == 0) + return; + printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status); #define PIS(x) \ @@ -478,192 +605,490 @@ static void print_irq_status_cio(u32 status) printk("\n"); } -static int debug_irq; - -/* called from dss */ -void dsi_irq_handler(void) +#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS +static void dsi_collect_irq_stats(struct platform_device *dsidev, u32 irqstatus, + u32 *vcstatus, u32 ciostatus) { - u32 irqstatus, vcstatus, ciostatus; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int i; - irqstatus = dsi_read_reg(DSI_IRQSTATUS); + spin_lock(&dsi->irq_stats_lock); -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spin_lock(&dsi.irq_stats_lock); - dsi.irq_stats.irq_count++; - dss_collect_irq_stats(irqstatus, dsi.irq_stats.dsi_irqs); + dsi->irq_stats.irq_count++; + dss_collect_irq_stats(irqstatus, dsi->irq_stats.dsi_irqs); + + for (i = 0; i < 4; ++i) + dss_collect_irq_stats(vcstatus[i], dsi->irq_stats.vc_irqs[i]); + + dss_collect_irq_stats(ciostatus, dsi->irq_stats.cio_irqs); + + spin_unlock(&dsi->irq_stats_lock); +} +#else +#define dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus) #endif +static int debug_irq; + +static void dsi_handle_irq_errors(struct platform_device *dsidev, u32 irqstatus, + u32 *vcstatus, u32 ciostatus) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int i; + if (irqstatus & DSI_IRQ_ERROR_MASK) { DSSERR("DSI error, irqstatus %x\n", irqstatus); print_irq_status(irqstatus); - spin_lock(&dsi.errors_lock); - dsi.errors |= irqstatus & DSI_IRQ_ERROR_MASK; - spin_unlock(&dsi.errors_lock); + spin_lock(&dsi->errors_lock); + dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK; + spin_unlock(&dsi->errors_lock); } else if (debug_irq) { print_irq_status(irqstatus); } -#ifdef DSI_CATCH_MISSING_TE - if (irqstatus & DSI_IRQ_TE_TRIGGER) - del_timer(&dsi.te_timer); -#endif + for (i = 0; i < 4; ++i) { + if (vcstatus[i] & DSI_VC_IRQ_ERROR_MASK) { + DSSERR("DSI VC(%d) error, vc irqstatus %x\n", + i, vcstatus[i]); + print_irq_status_vc(i, vcstatus[i]); + } else if (debug_irq) { + print_irq_status_vc(i, vcstatus[i]); + } + } + + if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) { + DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); + print_irq_status_cio(ciostatus); + } else if (debug_irq) { + print_irq_status_cio(ciostatus); + } +} + +static void dsi_call_isrs(struct dsi_isr_data *isr_array, + unsigned isr_array_size, u32 irqstatus) +{ + struct dsi_isr_data *isr_data; + int i; + + for (i = 0; i < isr_array_size; i++) { + isr_data = &isr_array[i]; + if (isr_data->isr && isr_data->mask & irqstatus) + isr_data->isr(isr_data->arg, irqstatus); + } +} + +static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables, + u32 irqstatus, u32 *vcstatus, u32 ciostatus) +{ + int i; + + dsi_call_isrs(isr_tables->isr_table, + ARRAY_SIZE(isr_tables->isr_table), + irqstatus); for (i = 0; i < 4; ++i) { - if ((irqstatus & (1<<i)) == 0) + if (vcstatus[i] == 0) continue; + dsi_call_isrs(isr_tables->isr_table_vc[i], + ARRAY_SIZE(isr_tables->isr_table_vc[i]), + vcstatus[i]); + } - vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i)); + if (ciostatus != 0) + dsi_call_isrs(isr_tables->isr_table_cio, + ARRAY_SIZE(isr_tables->isr_table_cio), + ciostatus); +} -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - dss_collect_irq_stats(vcstatus, dsi.irq_stats.vc_irqs[i]); -#endif +static irqreturn_t omap_dsi_irq_handler(int irq, void *arg) +{ + struct platform_device *dsidev; + struct dsi_data *dsi; + u32 irqstatus, vcstatus[4], ciostatus; + int i; - if (vcstatus & DSI_VC_IRQ_BTA) { - complete(&dsi.bta_completion); + dsidev = (struct platform_device *) arg; + dsi = dsi_get_dsidrv_data(dsidev); - if (dsi.bta_callback) - dsi.bta_callback(); - } + spin_lock(&dsi->irq_lock); - if (vcstatus & DSI_VC_IRQ_ERROR_MASK) { - DSSERR("DSI VC(%d) error, vc irqstatus %x\n", - i, vcstatus); - print_irq_status_vc(i, vcstatus); - } else if (debug_irq) { - print_irq_status_vc(i, vcstatus); + irqstatus = dsi_read_reg(dsidev, DSI_IRQSTATUS); + + /* IRQ is not for us */ + if (!irqstatus) { + spin_unlock(&dsi->irq_lock); + return IRQ_NONE; + } + + dsi_write_reg(dsidev, DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); + /* flush posted write */ + dsi_read_reg(dsidev, DSI_IRQSTATUS); + + for (i = 0; i < 4; ++i) { + if ((irqstatus & (1 << i)) == 0) { + vcstatus[i] = 0; + continue; } - dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus); + vcstatus[i] = dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i)); + + dsi_write_reg(dsidev, DSI_VC_IRQSTATUS(i), vcstatus[i]); /* flush posted write */ - dsi_read_reg(DSI_VC_IRQSTATUS(i)); + dsi_read_reg(dsidev, DSI_VC_IRQSTATUS(i)); } if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) { - ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + ciostatus = dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS); -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - dss_collect_irq_stats(ciostatus, dsi.irq_stats.cio_irqs); + dsi_write_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS, ciostatus); + /* flush posted write */ + dsi_read_reg(dsidev, DSI_COMPLEXIO_IRQ_STATUS); + } else { + ciostatus = 0; + } + +#ifdef DSI_CATCH_MISSING_TE + if (irqstatus & DSI_IRQ_TE_TRIGGER) + del_timer(&dsi->te_timer); #endif - dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus); - /* flush posted write */ - dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); + /* make a copy and unlock, so that isrs can unregister + * themselves */ + memcpy(&dsi->isr_tables_copy, &dsi->isr_tables, + sizeof(dsi->isr_tables)); - if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) { - DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); - print_irq_status_cio(ciostatus); - } else if (debug_irq) { - print_irq_status_cio(ciostatus); - } + spin_unlock(&dsi->irq_lock); + + dsi_handle_isrs(&dsi->isr_tables_copy, irqstatus, vcstatus, ciostatus); + + dsi_handle_irq_errors(dsidev, irqstatus, vcstatus, ciostatus); + + dsi_collect_irq_stats(dsidev, irqstatus, vcstatus, ciostatus); + + return IRQ_HANDLED; +} + +/* dsi->irq_lock has to be locked by the caller */ +static void _omap_dsi_configure_irqs(struct platform_device *dsidev, + struct dsi_isr_data *isr_array, + unsigned isr_array_size, u32 default_mask, + const struct dsi_reg enable_reg, + const struct dsi_reg status_reg) +{ + struct dsi_isr_data *isr_data; + u32 mask; + u32 old_mask; + int i; + + mask = default_mask; + + for (i = 0; i < isr_array_size; i++) { + isr_data = &isr_array[i]; + + if (isr_data->isr == NULL) + continue; + + mask |= isr_data->mask; } - dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); - /* flush posted write */ - dsi_read_reg(DSI_IRQSTATUS); + old_mask = dsi_read_reg(dsidev, enable_reg); + /* clear the irqstatus for newly enabled irqs */ + dsi_write_reg(dsidev, status_reg, (mask ^ old_mask) & mask); + dsi_write_reg(dsidev, enable_reg, mask); -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spin_unlock(&dsi.irq_stats_lock); + /* flush posted writes */ + dsi_read_reg(dsidev, enable_reg); + dsi_read_reg(dsidev, status_reg); +} + +/* dsi->irq_lock has to be locked by the caller */ +static void _omap_dsi_set_irqs(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + u32 mask = DSI_IRQ_ERROR_MASK; +#ifdef DSI_CATCH_MISSING_TE + mask |= DSI_IRQ_TE_TRIGGER; #endif + _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table, + ARRAY_SIZE(dsi->isr_tables.isr_table), mask, + DSI_IRQENABLE, DSI_IRQSTATUS); +} + +/* dsi->irq_lock has to be locked by the caller */ +static void _omap_dsi_set_irqs_vc(struct platform_device *dsidev, int vc) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_vc[vc], + ARRAY_SIZE(dsi->isr_tables.isr_table_vc[vc]), + DSI_VC_IRQ_ERROR_MASK, + DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc)); } +/* dsi->irq_lock has to be locked by the caller */ +static void _omap_dsi_set_irqs_cio(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + _omap_dsi_configure_irqs(dsidev, dsi->isr_tables.isr_table_cio, + ARRAY_SIZE(dsi->isr_tables.isr_table_cio), + DSI_CIO_IRQ_ERROR_MASK, + DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS); +} -static void _dsi_initialize_irq(void) +static void _dsi_initialize_irq(struct platform_device *dsidev) { - u32 l; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + unsigned long flags; + int vc; + + spin_lock_irqsave(&dsi->irq_lock, flags); + + memset(&dsi->isr_tables, 0, sizeof(dsi->isr_tables)); + + _omap_dsi_set_irqs(dsidev); + for (vc = 0; vc < 4; ++vc) + _omap_dsi_set_irqs_vc(dsidev, vc); + _omap_dsi_set_irqs_cio(dsidev); + + spin_unlock_irqrestore(&dsi->irq_lock, flags); +} + +static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask, + struct dsi_isr_data *isr_array, unsigned isr_array_size) +{ + struct dsi_isr_data *isr_data; + int free_idx; int i; - /* disable all interrupts */ - dsi_write_reg(DSI_IRQENABLE, 0); - for (i = 0; i < 4; ++i) - dsi_write_reg(DSI_VC_IRQENABLE(i), 0); - dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, 0); + BUG_ON(isr == NULL); - /* clear interrupt status */ - l = dsi_read_reg(DSI_IRQSTATUS); - dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK); + /* check for duplicate entry and find a free slot */ + free_idx = -1; + for (i = 0; i < isr_array_size; i++) { + isr_data = &isr_array[i]; - for (i = 0; i < 4; ++i) { - l = dsi_read_reg(DSI_VC_IRQSTATUS(i)); - dsi_write_reg(DSI_VC_IRQSTATUS(i), l); + if (isr_data->isr == isr && isr_data->arg == arg && + isr_data->mask == mask) { + return -EINVAL; + } + + if (isr_data->isr == NULL && free_idx == -1) + free_idx = i; } - l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); - dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l); + if (free_idx == -1) + return -EBUSY; - /* enable error irqs */ - l = DSI_IRQ_ERROR_MASK; -#ifdef DSI_CATCH_MISSING_TE - l |= DSI_IRQ_TE_TRIGGER; -#endif - dsi_write_reg(DSI_IRQENABLE, l); + isr_data = &isr_array[free_idx]; + isr_data->isr = isr; + isr_data->arg = arg; + isr_data->mask = mask; - l = DSI_VC_IRQ_ERROR_MASK; - for (i = 0; i < 4; ++i) - dsi_write_reg(DSI_VC_IRQENABLE(i), l); + return 0; +} + +static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask, + struct dsi_isr_data *isr_array, unsigned isr_array_size) +{ + struct dsi_isr_data *isr_data; + int i; + + for (i = 0; i < isr_array_size; i++) { + isr_data = &isr_array[i]; + if (isr_data->isr != isr || isr_data->arg != arg || + isr_data->mask != mask) + continue; + + isr_data->isr = NULL; + isr_data->arg = NULL; + isr_data->mask = 0; + + return 0; + } - l = DSI_CIO_IRQ_ERROR_MASK; - dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, l); + return -EINVAL; } -static u32 dsi_get_errors(void) +static int dsi_register_isr(struct platform_device *dsidev, omap_dsi_isr_t isr, + void *arg, u32 mask) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; - u32 e; - spin_lock_irqsave(&dsi.errors_lock, flags); - e = dsi.errors; - dsi.errors = 0; - spin_unlock_irqrestore(&dsi.errors_lock, flags); - return e; + int r; + + spin_lock_irqsave(&dsi->irq_lock, flags); + + r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table, + ARRAY_SIZE(dsi->isr_tables.isr_table)); + + if (r == 0) + _omap_dsi_set_irqs(dsidev); + + spin_unlock_irqrestore(&dsi->irq_lock, flags); + + return r; } -static void dsi_vc_enable_bta_irq(int channel) +static int dsi_unregister_isr(struct platform_device *dsidev, + omap_dsi_isr_t isr, void *arg, u32 mask) { - u32 l; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + unsigned long flags; + int r; + + spin_lock_irqsave(&dsi->irq_lock, flags); + + r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table, + ARRAY_SIZE(dsi->isr_tables.isr_table)); - dsi_write_reg(DSI_VC_IRQSTATUS(channel), DSI_VC_IRQ_BTA); + if (r == 0) + _omap_dsi_set_irqs(dsidev); - l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); - l |= DSI_VC_IRQ_BTA; - dsi_write_reg(DSI_VC_IRQENABLE(channel), l); + spin_unlock_irqrestore(&dsi->irq_lock, flags); + + return r; } -static void dsi_vc_disable_bta_irq(int channel) +static int dsi_register_isr_vc(struct platform_device *dsidev, int channel, + omap_dsi_isr_t isr, void *arg, u32 mask) { - u32 l; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + unsigned long flags; + int r; + + spin_lock_irqsave(&dsi->irq_lock, flags); + + r = _dsi_register_isr(isr, arg, mask, + dsi->isr_tables.isr_table_vc[channel], + ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel])); + + if (r == 0) + _omap_dsi_set_irqs_vc(dsidev, channel); - l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); - l &= ~DSI_VC_IRQ_BTA; - dsi_write_reg(DSI_VC_IRQENABLE(channel), l); + spin_unlock_irqrestore(&dsi->irq_lock, flags); + + return r; } -/* DSI func clock. this could also be DSI2_PLL_FCLK */ -static inline void enable_clocks(bool enable) +static int dsi_unregister_isr_vc(struct platform_device *dsidev, int channel, + omap_dsi_isr_t isr, void *arg, u32 mask) { - if (enable) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - else - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + unsigned long flags; + int r; + + spin_lock_irqsave(&dsi->irq_lock, flags); + + r = _dsi_unregister_isr(isr, arg, mask, + dsi->isr_tables.isr_table_vc[channel], + ARRAY_SIZE(dsi->isr_tables.isr_table_vc[channel])); + + if (r == 0) + _omap_dsi_set_irqs_vc(dsidev, channel); + + spin_unlock_irqrestore(&dsi->irq_lock, flags); + + return r; +} + +static int dsi_register_isr_cio(struct platform_device *dsidev, + omap_dsi_isr_t isr, void *arg, u32 mask) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + unsigned long flags; + int r; + + spin_lock_irqsave(&dsi->irq_lock, flags); + + r = _dsi_register_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio, + ARRAY_SIZE(dsi->isr_tables.isr_table_cio)); + + if (r == 0) + _omap_dsi_set_irqs_cio(dsidev); + + spin_unlock_irqrestore(&dsi->irq_lock, flags); + + return r; +} + +static int dsi_unregister_isr_cio(struct platform_device *dsidev, + omap_dsi_isr_t isr, void *arg, u32 mask) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + unsigned long flags; + int r; + + spin_lock_irqsave(&dsi->irq_lock, flags); + + r = _dsi_unregister_isr(isr, arg, mask, dsi->isr_tables.isr_table_cio, + ARRAY_SIZE(dsi->isr_tables.isr_table_cio)); + + if (r == 0) + _omap_dsi_set_irqs_cio(dsidev); + + spin_unlock_irqrestore(&dsi->irq_lock, flags); + + return r; +} + +static u32 dsi_get_errors(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + unsigned long flags; + u32 e; + spin_lock_irqsave(&dsi->errors_lock, flags); + e = dsi->errors; + dsi->errors = 0; + spin_unlock_irqrestore(&dsi->errors_lock, flags); + return e; +} + +int dsi_runtime_get(struct platform_device *dsidev) +{ + int r; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + DSSDBG("dsi_runtime_get\n"); + + r = pm_runtime_get_sync(&dsi->pdev->dev); + WARN_ON(r < 0); + return r < 0 ? r : 0; +} + +void dsi_runtime_put(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int r; + + DSSDBG("dsi_runtime_put\n"); + + r = pm_runtime_put(&dsi->pdev->dev); + WARN_ON(r < 0); } /* source clock for DSI PLL. this could also be PCLKFREE */ -static inline void dsi_enable_pll_clock(bool enable) +static inline void dsi_enable_pll_clock(struct platform_device *dsidev, + bool enable) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + if (enable) - dss_clk_enable(DSS_CLK_FCK2); + clk_enable(dsi->sys_clk); else - dss_clk_disable(DSS_CLK_FCK2); + clk_disable(dsi->sys_clk); - if (enable && dsi.pll_locked) { - if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) + if (enable && dsi->pll_locked) { + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1) DSSERR("cannot lock PLL when enabling clocks\n"); } } #ifdef DEBUG -static void _dsi_print_reset_status(void) +static void _dsi_print_reset_status(struct platform_device *dsidev) { u32 l; + int b0, b1, b2; if (!dss_debug) return; @@ -671,35 +1096,47 @@ static void _dsi_print_reset_status(void) /* A dummy read using the SCP interface to any DSIPHY register is * required after DSIPHY reset to complete the reset of the DSI complex * I/O. */ - l = dsi_read_reg(DSI_DSIPHY_CFG5); + l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); printk(KERN_DEBUG "DSI resets: "); - l = dsi_read_reg(DSI_PLL_STATUS); + l = dsi_read_reg(dsidev, DSI_PLL_STATUS); printk("PLL (%d) ", FLD_GET(l, 0, 0)); - l = dsi_read_reg(DSI_COMPLEXIO_CFG1); + l = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1); printk("CIO (%d) ", FLD_GET(l, 29, 29)); - l = dsi_read_reg(DSI_DSIPHY_CFG5); - printk("PHY (%x, %d, %d, %d)\n", - FLD_GET(l, 28, 26), + if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) { + b0 = 28; + b1 = 27; + b2 = 26; + } else { + b0 = 24; + b1 = 25; + b2 = 26; + } + + l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); + printk("PHY (%x%x%x, %d, %d, %d)\n", + FLD_GET(l, b0, b0), + FLD_GET(l, b1, b1), + FLD_GET(l, b2, b2), FLD_GET(l, 29, 29), FLD_GET(l, 30, 30), FLD_GET(l, 31, 31)); } #else -#define _dsi_print_reset_status() +#define _dsi_print_reset_status(x) #endif -static inline int dsi_if_enable(bool enable) +static inline int dsi_if_enable(struct platform_device *dsidev, bool enable) { DSSDBG("dsi_if_enable(%d)\n", enable); enable = enable ? 1 : 0; - REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */ + REG_FLD_MOD(dsidev, DSI_CTRL, enable, 0, 0); /* IF_EN */ - if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) { + if (wait_for_bit_change(dsidev, DSI_CTRL, 0, enable) != enable) { DSSERR("Failed to set dsi_if_enable to %d\n", enable); return -EIO; } @@ -707,31 +1144,39 @@ static inline int dsi_if_enable(bool enable) return 0; } -unsigned long dsi_get_dsi1_pll_rate(void) +unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) { - return dsi.current_cinfo.dsi1_pll_fclk; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk; } -static unsigned long dsi_get_dsi2_pll_rate(void) +static unsigned long dsi_get_pll_hsdiv_dsi_rate(struct platform_device *dsidev) { - return dsi.current_cinfo.dsi2_pll_fclk; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk; } -static unsigned long dsi_get_txbyteclkhs(void) +static unsigned long dsi_get_txbyteclkhs(struct platform_device *dsidev) { - return dsi.current_cinfo.clkin4ddr / 16; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + return dsi->current_cinfo.clkin4ddr / 16; } -static unsigned long dsi_fclk_rate(void) +static unsigned long dsi_fclk_rate(struct platform_device *dsidev) { unsigned long r; + int dsi_module = dsi_get_dsidev_id(dsidev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - if (dss_get_dsi_clk_source() == DSS_SRC_DSS1_ALWON_FCLK) { - /* DSI FCLK source is DSS1_ALWON_FCK, which is dss1_fck */ - r = dss_clk_get_rate(DSS_CLK_FCK1); + if (dss_get_dsi_clk_source(dsi_module) == OMAP_DSS_CLK_SRC_FCK) { + /* DSI FCLK source is DSS_CLK_FCK */ + r = clk_get_rate(dsi->dss_clk); } else { - /* DSI FCLK source is DSI2_PLL_FCLK */ - r = dsi_get_dsi2_pll_rate(); + /* DSI FCLK source is dsi_pll_hsdiv_dsi_clk */ + r = dsi_get_pll_hsdiv_dsi_rate(dsidev); } return r; @@ -739,31 +1184,50 @@ static unsigned long dsi_fclk_rate(void) static int dsi_set_lp_clk_divisor(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long dsi_fclk; unsigned lp_clk_div; unsigned long lp_clk; - lp_clk_div = dssdev->phy.dsi.div.lp_clk_div; + lp_clk_div = dssdev->clocks.dsi.lp_clk_div; - if (lp_clk_div == 0 || lp_clk_div > LP_DIV_MAX) + if (lp_clk_div == 0 || lp_clk_div > dsi->lpdiv_max) return -EINVAL; - dsi_fclk = dsi_fclk_rate(); + dsi_fclk = dsi_fclk_rate(dsidev); lp_clk = dsi_fclk / 2 / lp_clk_div; DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk); - dsi.current_cinfo.lp_clk = lp_clk; - dsi.current_cinfo.lp_clk_div = lp_clk_div; + dsi->current_cinfo.lp_clk = lp_clk; + dsi->current_cinfo.lp_clk_div = lp_clk_div; - REG_FLD_MOD(DSI_CLK_CTRL, lp_clk_div, 12, 0); /* LP_CLK_DIVISOR */ + /* LP_CLK_DIVISOR */ + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, lp_clk_div, 12, 0); - REG_FLD_MOD(DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, - 21, 21); /* LP_RX_SYNCHRO_ENABLE */ + /* LP_RX_SYNCHRO_ENABLE */ + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, dsi_fclk > 30000000 ? 1 : 0, 21, 21); return 0; } +static void dsi_enable_scp_clk(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->scp_clk_refcount++ == 0) + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 14, 14); /* CIO_CLK_ICG */ +} + +static void dsi_disable_scp_clk(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + WARN_ON(dsi->scp_clk_refcount == 0); + if (--dsi->scp_clk_refcount == 0) + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 14, 14); /* CIO_CLK_ICG */ +} enum dsi_pll_power_state { DSI_PLL_POWER_OFF = 0x0, @@ -772,14 +1236,21 @@ enum dsi_pll_power_state { DSI_PLL_POWER_ON_DIV = 0x3, }; -static int dsi_pll_power(enum dsi_pll_power_state state) +static int dsi_pll_power(struct platform_device *dsidev, + enum dsi_pll_power_state state) { int t = 0; - REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */ + /* DSI-PLL power command 0x3 is not working */ + if (dss_has_feature(FEAT_DSI_PLL_PWR_BUG) && + state == DSI_PLL_POWER_ON_DIV) + state = DSI_PLL_POWER_ON_ALL; + + /* PLL_PWR_CMD */ + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_STATUS */ - while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) { + while (FLD_GET(dsi_read_reg(dsidev, DSI_CLK_CTRL), 29, 28) != state) { if (++t > 1000) { DSSERR("Failed to set DSI PLL power mode to %d\n", state); @@ -795,22 +1266,25 @@ static int dsi_pll_power(enum dsi_pll_power_state state) static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, struct dsi_clock_info *cinfo) { - if (cinfo->regn == 0 || cinfo->regn > REGN_MAX) + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (cinfo->regn == 0 || cinfo->regn > dsi->regn_max) return -EINVAL; - if (cinfo->regm == 0 || cinfo->regm > REGM_MAX) + if (cinfo->regm == 0 || cinfo->regm > dsi->regm_max) return -EINVAL; - if (cinfo->regm3 > REGM3_MAX) + if (cinfo->regm_dispc > dsi->regm_dispc_max) return -EINVAL; - if (cinfo->regm4 > REGM4_MAX) + if (cinfo->regm_dsi > dsi->regm_dsi_max) return -EINVAL; - if (cinfo->use_dss2_fck) { - cinfo->clkin = dss_clk_get_rate(DSS_CLK_FCK2); + if (cinfo->use_sys_clk) { + cinfo->clkin = clk_get_rate(dsi->sys_clk); /* XXX it is unclear if highfreq should be used - * with DSS2_FCK source also */ + * with DSS_SYS_CLK source also */ cinfo->highfreq = 0; } else { cinfo->clkin = dispc_pclk_rate(dssdev->manager->id); @@ -823,7 +1297,7 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, cinfo->fint = cinfo->clkin / (cinfo->regn * (cinfo->highfreq ? 2 : 1)); - if (cinfo->fint > FINT_MAX || cinfo->fint < FINT_MIN) + if (cinfo->fint > dsi->fint_max || cinfo->fint < dsi->fint_min) return -EINVAL; cinfo->clkin4ddr = 2 * cinfo->regm * cinfo->fint; @@ -831,44 +1305,49 @@ static int dsi_calc_clock_rates(struct omap_dss_device *dssdev, if (cinfo->clkin4ddr > 1800 * 1000 * 1000) return -EINVAL; - if (cinfo->regm3 > 0) - cinfo->dsi1_pll_fclk = cinfo->clkin4ddr / cinfo->regm3; + if (cinfo->regm_dispc > 0) + cinfo->dsi_pll_hsdiv_dispc_clk = + cinfo->clkin4ddr / cinfo->regm_dispc; else - cinfo->dsi1_pll_fclk = 0; + cinfo->dsi_pll_hsdiv_dispc_clk = 0; - if (cinfo->regm4 > 0) - cinfo->dsi2_pll_fclk = cinfo->clkin4ddr / cinfo->regm4; + if (cinfo->regm_dsi > 0) + cinfo->dsi_pll_hsdiv_dsi_clk = + cinfo->clkin4ddr / cinfo->regm_dsi; else - cinfo->dsi2_pll_fclk = 0; + cinfo->dsi_pll_hsdiv_dsi_clk = 0; return 0; } -int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, - struct dsi_clock_info *dsi_cinfo, +int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft, + unsigned long req_pck, struct dsi_clock_info *dsi_cinfo, struct dispc_clock_info *dispc_cinfo) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct dsi_clock_info cur, best; struct dispc_clock_info best_dispc; int min_fck_per_pck; int match = 0; - unsigned long dss_clk_fck2; + unsigned long dss_sys_clk, max_dss_fck; + + dss_sys_clk = clk_get_rate(dsi->sys_clk); - dss_clk_fck2 = dss_clk_get_rate(DSS_CLK_FCK2); + max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); - if (req_pck == dsi.cache_req_pck && - dsi.cache_cinfo.clkin == dss_clk_fck2) { + if (req_pck == dsi->cache_req_pck && + dsi->cache_cinfo.clkin == dss_sys_clk) { DSSDBG("DSI clock info found from cache\n"); - *dsi_cinfo = dsi.cache_cinfo; - dispc_find_clk_divs(is_tft, req_pck, dsi_cinfo->dsi1_pll_fclk, - dispc_cinfo); + *dsi_cinfo = dsi->cache_cinfo; + dispc_find_clk_divs(is_tft, req_pck, + dsi_cinfo->dsi_pll_hsdiv_dispc_clk, dispc_cinfo); return 0; } min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; if (min_fck_per_pck && - req_pck * min_fck_per_pck > DISPC_MAX_FCK) { + req_pck * min_fck_per_pck > max_dss_fck) { DSSERR("Requested pixel clock not possible with the current " "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " "the constraint off.\n"); @@ -882,24 +1361,24 @@ retry: memset(&best_dispc, 0, sizeof(best_dispc)); memset(&cur, 0, sizeof(cur)); - cur.clkin = dss_clk_fck2; - cur.use_dss2_fck = 1; + cur.clkin = dss_sys_clk; + cur.use_sys_clk = 1; cur.highfreq = 0; /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ - for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) { + for (cur.regn = 1; cur.regn < dsi->regn_max; ++cur.regn) { if (cur.highfreq == 0) cur.fint = cur.clkin / cur.regn; else cur.fint = cur.clkin / (2 * cur.regn); - if (cur.fint > FINT_MAX || cur.fint < FINT_MIN) + if (cur.fint > dsi->fint_max || cur.fint < dsi->fint_min) continue; /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ - for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) { + for (cur.regm = 1; cur.regm < dsi->regm_max; ++cur.regm) { unsigned long a, b; a = 2 * cur.regm * (cur.clkin/1000); @@ -909,30 +1388,32 @@ retry: if (cur.clkin4ddr > 1800 * 1000 * 1000) break; - /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */ - for (cur.regm3 = 1; cur.regm3 < REGM3_MAX; - ++cur.regm3) { + /* dsi_pll_hsdiv_dispc_clk(MHz) = + * DSIPHY(MHz) / regm_dispc < 173MHz/186Mhz */ + for (cur.regm_dispc = 1; cur.regm_dispc < + dsi->regm_dispc_max; ++cur.regm_dispc) { struct dispc_clock_info cur_dispc; - cur.dsi1_pll_fclk = cur.clkin4ddr / cur.regm3; + cur.dsi_pll_hsdiv_dispc_clk = + cur.clkin4ddr / cur.regm_dispc; /* this will narrow down the search a bit, * but still give pixclocks below what was * requested */ - if (cur.dsi1_pll_fclk < req_pck) + if (cur.dsi_pll_hsdiv_dispc_clk < req_pck) break; - if (cur.dsi1_pll_fclk > DISPC_MAX_FCK) + if (cur.dsi_pll_hsdiv_dispc_clk > max_dss_fck) continue; if (min_fck_per_pck && - cur.dsi1_pll_fclk < + cur.dsi_pll_hsdiv_dispc_clk < req_pck * min_fck_per_pck) continue; match = 1; dispc_find_clk_divs(is_tft, req_pck, - cur.dsi1_pll_fclk, + cur.dsi_pll_hsdiv_dispc_clk, &cur_dispc); if (abs(cur_dispc.pck - req_pck) < @@ -961,44 +1442,53 @@ found: return -EINVAL; } - /* DSI2_PLL_FCLK (regm4) is not used */ - best.regm4 = 0; - best.dsi2_pll_fclk = 0; + /* dsi_pll_hsdiv_dsi_clk (regm_dsi) is not used */ + best.regm_dsi = 0; + best.dsi_pll_hsdiv_dsi_clk = 0; if (dsi_cinfo) *dsi_cinfo = best; if (dispc_cinfo) *dispc_cinfo = best_dispc; - dsi.cache_req_pck = req_pck; - dsi.cache_clk_freq = 0; - dsi.cache_cinfo = best; + dsi->cache_req_pck = req_pck; + dsi->cache_clk_freq = 0; + dsi->cache_cinfo = best; return 0; } -int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) +int dsi_pll_set_clock_div(struct platform_device *dsidev, + struct dsi_clock_info *cinfo) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r = 0; u32 l; - int f; + int f = 0; + u8 regn_start, regn_end, regm_start, regm_end; + u8 regm_dispc_start, regm_dispc_end, regm_dsi_start, regm_dsi_end; DSSDBGF(); - dsi.current_cinfo.fint = cinfo->fint; - dsi.current_cinfo.clkin4ddr = cinfo->clkin4ddr; - dsi.current_cinfo.dsi1_pll_fclk = cinfo->dsi1_pll_fclk; - dsi.current_cinfo.dsi2_pll_fclk = cinfo->dsi2_pll_fclk; + dsi->current_cinfo.use_sys_clk = cinfo->use_sys_clk; + dsi->current_cinfo.highfreq = cinfo->highfreq; + + dsi->current_cinfo.fint = cinfo->fint; + dsi->current_cinfo.clkin4ddr = cinfo->clkin4ddr; + dsi->current_cinfo.dsi_pll_hsdiv_dispc_clk = + cinfo->dsi_pll_hsdiv_dispc_clk; + dsi->current_cinfo.dsi_pll_hsdiv_dsi_clk = + cinfo->dsi_pll_hsdiv_dsi_clk; - dsi.current_cinfo.regn = cinfo->regn; - dsi.current_cinfo.regm = cinfo->regm; - dsi.current_cinfo.regm3 = cinfo->regm3; - dsi.current_cinfo.regm4 = cinfo->regm4; + dsi->current_cinfo.regn = cinfo->regn; + dsi->current_cinfo.regm = cinfo->regm; + dsi->current_cinfo.regm_dispc = cinfo->regm_dispc; + dsi->current_cinfo.regm_dsi = cinfo->regm_dsi; DSSDBG("DSI Fint %ld\n", cinfo->fint); DSSDBG("clkin (%s) rate %ld, highfreq %d\n", - cinfo->use_dss2_fck ? "dss2_fck" : "pclkfree", + cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree", cinfo->clkin, cinfo->highfreq); @@ -1015,63 +1505,79 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) DSSDBG("Clock lane freq %ld Hz\n", cinfo->clkin4ddr / 4); - DSSDBG("regm3 = %d, dsi1_pll_fclk = %lu\n", - cinfo->regm3, cinfo->dsi1_pll_fclk); - DSSDBG("regm4 = %d, dsi2_pll_fclk = %lu\n", - cinfo->regm4, cinfo->dsi2_pll_fclk); - - REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */ - - l = dsi_read_reg(DSI_PLL_CONFIGURATION1); + DSSDBG("regm_dispc = %d, %s (%s) = %lu\n", cinfo->regm_dispc, + dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), + dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), + cinfo->dsi_pll_hsdiv_dispc_clk); + DSSDBG("regm_dsi = %d, %s (%s) = %lu\n", cinfo->regm_dsi, + dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), + dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), + cinfo->dsi_pll_hsdiv_dsi_clk); + + dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGN, ®n_start, ®n_end); + dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM, ®m_start, ®m_end); + dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM_DISPC, ®m_dispc_start, + ®m_dispc_end); + dss_feat_get_reg_field(FEAT_REG_DSIPLL_REGM_DSI, ®m_dsi_start, + ®m_dsi_end); + + /* DSI_PLL_AUTOMODE = manual */ + REG_FLD_MOD(dsidev, DSI_PLL_CONTROL, 0, 0, 0); + + l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION1); l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */ - l = FLD_MOD(l, cinfo->regn - 1, 7, 1); /* DSI_PLL_REGN */ - l = FLD_MOD(l, cinfo->regm, 18, 8); /* DSI_PLL_REGM */ - l = FLD_MOD(l, cinfo->regm3 > 0 ? cinfo->regm3 - 1 : 0, - 22, 19); /* DSI_CLOCK_DIV */ - l = FLD_MOD(l, cinfo->regm4 > 0 ? cinfo->regm4 - 1 : 0, - 26, 23); /* DSIPROTO_CLOCK_DIV */ - dsi_write_reg(DSI_PLL_CONFIGURATION1, l); - - BUG_ON(cinfo->fint < 750000 || cinfo->fint > 2100000); - if (cinfo->fint < 1000000) - f = 0x3; - else if (cinfo->fint < 1250000) - f = 0x4; - else if (cinfo->fint < 1500000) - f = 0x5; - else if (cinfo->fint < 1750000) - f = 0x6; - else - f = 0x7; + /* DSI_PLL_REGN */ + l = FLD_MOD(l, cinfo->regn - 1, regn_start, regn_end); + /* DSI_PLL_REGM */ + l = FLD_MOD(l, cinfo->regm, regm_start, regm_end); + /* DSI_CLOCK_DIV */ + l = FLD_MOD(l, cinfo->regm_dispc > 0 ? cinfo->regm_dispc - 1 : 0, + regm_dispc_start, regm_dispc_end); + /* DSIPROTO_CLOCK_DIV */ + l = FLD_MOD(l, cinfo->regm_dsi > 0 ? cinfo->regm_dsi - 1 : 0, + regm_dsi_start, regm_dsi_end); + dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION1, l); + + BUG_ON(cinfo->fint < dsi->fint_min || cinfo->fint > dsi->fint_max); + + if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) { + f = cinfo->fint < 1000000 ? 0x3 : + cinfo->fint < 1250000 ? 0x4 : + cinfo->fint < 1500000 ? 0x5 : + cinfo->fint < 1750000 ? 0x6 : + 0x7; + } - l = dsi_read_reg(DSI_PLL_CONFIGURATION2); - l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ - l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1, + l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2); + + if (dss_has_feature(FEAT_DSI_PLL_FREQSEL)) + l = FLD_MOD(l, f, 4, 1); /* DSI_PLL_FREQSEL */ + l = FLD_MOD(l, cinfo->use_sys_clk ? 0 : 1, 11, 11); /* DSI_PLL_CLKSEL */ l = FLD_MOD(l, cinfo->highfreq, 12, 12); /* DSI_PLL_HIGHFREQ */ l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ - dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l); - REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ + REG_FLD_MOD(dsidev, DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ - if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) { + if (wait_for_bit_change(dsidev, DSI_PLL_GO, 0, 0) != 0) { DSSERR("dsi pll go bit not going down.\n"); r = -EIO; goto err; } - if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) { + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1) { DSSERR("cannot lock PLL\n"); r = -EIO; goto err; } - dsi.pll_locked = 1; + dsi->pll_locked = 1; - l = dsi_read_reg(DSI_PLL_CONFIGURATION2); + l = dsi_read_reg(dsidev, DSI_PLL_CONFIGURATION2); l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */ l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */ l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */ @@ -1086,32 +1592,52 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo) l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */ l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */ l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */ - dsi_write_reg(DSI_PLL_CONFIGURATION2, l); + dsi_write_reg(dsidev, DSI_PLL_CONFIGURATION2, l); DSSDBG("PLL config done\n"); err: return r; } -int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, +int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, bool enable_hsdiv) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r = 0; enum dsi_pll_power_state pwstate; DSSDBG("PLL init\n"); - enable_clocks(1); - dsi_enable_pll_clock(1); + if (dsi->vdds_dsi_reg == NULL) { + struct regulator *vdds_dsi; - r = regulator_enable(dsi.vdds_dsi_reg); - if (r) - goto err0; + vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi"); + + if (IS_ERR(vdds_dsi)) { + DSSERR("can't get VDDS_DSI regulator\n"); + return PTR_ERR(vdds_dsi); + } + + dsi->vdds_dsi_reg = vdds_dsi; + } + + dsi_enable_pll_clock(dsidev, 1); + /* + * Note: SCP CLK is not required on OMAP3, but it is required on OMAP4. + */ + dsi_enable_scp_clk(dsidev); + + if (!dsi->vdds_dsi_enabled) { + r = regulator_enable(dsi->vdds_dsi_reg); + if (r) + goto err0; + dsi->vdds_dsi_enabled = true; + } /* XXX PLL does not come out of reset without this... */ dispc_pck_free_enable(1); - if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) { + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 0, 1) != 1) { DSSERR("PLL not coming out of reset.\n"); r = -ENODEV; dispc_pck_free_enable(0); @@ -1131,7 +1657,7 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, else pwstate = DSI_PLL_POWER_OFF; - r = dsi_pll_power(pwstate); + r = dsi_pll_power(dsidev, pwstate); if (r) goto err1; @@ -1140,92 +1666,120 @@ int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, return 0; err1: - regulator_disable(dsi.vdds_dsi_reg); + if (dsi->vdds_dsi_enabled) { + regulator_disable(dsi->vdds_dsi_reg); + dsi->vdds_dsi_enabled = false; + } err0: - enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_disable_scp_clk(dsidev); + dsi_enable_pll_clock(dsidev, 0); return r; } -void dsi_pll_uninit(void) +void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes) { - enable_clocks(0); - dsi_enable_pll_clock(0); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi->pll_locked = 0; + dsi_pll_power(dsidev, DSI_PLL_POWER_OFF); + if (disconnect_lanes) { + WARN_ON(!dsi->vdds_dsi_enabled); + regulator_disable(dsi->vdds_dsi_reg); + dsi->vdds_dsi_enabled = false; + } + + dsi_disable_scp_clk(dsidev); + dsi_enable_pll_clock(dsidev, 0); - dsi.pll_locked = 0; - dsi_pll_power(DSI_PLL_POWER_OFF); - regulator_disable(dsi.vdds_dsi_reg); DSSDBG("PLL uninit done\n"); } -void dsi_dump_clocks(struct seq_file *s) +static void dsi_dump_dsidev_clocks(struct platform_device *dsidev, + struct seq_file *s) { - int clksel; - struct dsi_clock_info *cinfo = &dsi.current_cinfo; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct dsi_clock_info *cinfo = &dsi->current_cinfo; + enum omap_dss_clk_source dispc_clk_src, dsi_clk_src; + int dsi_module = dsi_get_dsidev_id(dsidev); - enable_clocks(1); + dispc_clk_src = dss_get_dispc_clk_source(); + dsi_clk_src = dss_get_dsi_clk_source(dsi_module); - clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11); + if (dsi_runtime_get(dsidev)) + return; - seq_printf(s, "- DSI PLL -\n"); + seq_printf(s, "- DSI%d PLL -\n", dsi_module + 1); seq_printf(s, "dsi pll source = %s\n", - clksel == 0 ? - "dss2_alwon_fclk" : "pclkfree"); + cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree"); seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn); seq_printf(s, "CLKIN4DDR\t%-16luregm %u\n", cinfo->clkin4ddr, cinfo->regm); - seq_printf(s, "dsi1_pll_fck\t%-16luregm3 %u\t(%s)\n", - cinfo->dsi1_pll_fclk, - cinfo->regm3, - dss_get_dispc_clk_source() == DSS_SRC_DSS1_ALWON_FCLK ? + seq_printf(s, "%s (%s)\t%-16luregm_dispc %u\t(%s)\n", + dss_get_generic_clk_source_name(dispc_clk_src), + dss_feat_get_clk_source_name(dispc_clk_src), + cinfo->dsi_pll_hsdiv_dispc_clk, + cinfo->regm_dispc, + dispc_clk_src == OMAP_DSS_CLK_SRC_FCK ? "off" : "on"); - seq_printf(s, "dsi2_pll_fck\t%-16luregm4 %u\t(%s)\n", - cinfo->dsi2_pll_fclk, - cinfo->regm4, - dss_get_dsi_clk_source() == DSS_SRC_DSS1_ALWON_FCLK ? + seq_printf(s, "%s (%s)\t%-16luregm_dsi %u\t(%s)\n", + dss_get_generic_clk_source_name(dsi_clk_src), + dss_feat_get_clk_source_name(dsi_clk_src), + cinfo->dsi_pll_hsdiv_dsi_clk, + cinfo->regm_dsi, + dsi_clk_src == OMAP_DSS_CLK_SRC_FCK ? "off" : "on"); - seq_printf(s, "- DSI -\n"); + seq_printf(s, "- DSI%d -\n", dsi_module + 1); - seq_printf(s, "dsi fclk source = %s\n", - dss_get_dsi_clk_source() == DSS_SRC_DSS1_ALWON_FCLK ? - "dss1_alwon_fclk" : "dsi2_pll_fclk"); + seq_printf(s, "dsi fclk source = %s (%s)\n", + dss_get_generic_clk_source_name(dsi_clk_src), + dss_feat_get_clk_source_name(dsi_clk_src)); - seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate()); + seq_printf(s, "DSI_FCLK\t%lu\n", dsi_fclk_rate(dsidev)); seq_printf(s, "DDR_CLK\t\t%lu\n", cinfo->clkin4ddr / 4); - seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs()); + seq_printf(s, "TxByteClkHS\t%lu\n", dsi_get_txbyteclkhs(dsidev)); seq_printf(s, "LP_CLK\t\t%lu\n", cinfo->lp_clk); - seq_printf(s, "VP_CLK\t\t%lu\n" - "VP_PCLK\t\t%lu\n", - dispc_lclk_rate(OMAP_DSS_CHANNEL_LCD), - dispc_pclk_rate(OMAP_DSS_CHANNEL_LCD)); + dsi_runtime_put(dsidev); +} - enable_clocks(0); +void dsi_dump_clocks(struct seq_file *s) +{ + struct platform_device *dsidev; + int i; + + for (i = 0; i < MAX_NUM_DSI; i++) { + dsidev = dsi_get_dsidev_from_id(i); + if (dsidev) + dsi_dump_dsidev_clocks(dsidev, s); + } } #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS -void dsi_dump_irqs(struct seq_file *s) +static void dsi_dump_dsidev_irqs(struct platform_device *dsidev, + struct seq_file *s) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned long flags; struct dsi_irq_stats stats; + int dsi_module = dsi_get_dsidev_id(dsidev); - spin_lock_irqsave(&dsi.irq_stats_lock, flags); + spin_lock_irqsave(&dsi->irq_stats_lock, flags); - stats = dsi.irq_stats; - memset(&dsi.irq_stats, 0, sizeof(dsi.irq_stats)); - dsi.irq_stats.last_reset = jiffies; + stats = dsi->irq_stats; + memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats)); + dsi->irq_stats.last_reset = jiffies; - spin_unlock_irqrestore(&dsi.irq_stats_lock, flags); + spin_unlock_irqrestore(&dsi->irq_stats_lock, flags); seq_printf(s, "period %u ms\n", jiffies_to_msecs(jiffies - stats.last_reset)); @@ -1234,7 +1788,7 @@ void dsi_dump_irqs(struct seq_file *s) #define PIS(x) \ seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]); - seq_printf(s, "-- DSI interrupts --\n"); + seq_printf(s, "-- DSI%d interrupts --\n", dsi_module + 1); PIS(VC0); PIS(VC1); PIS(VC2); @@ -1300,13 +1854,46 @@ void dsi_dump_irqs(struct seq_file *s) PIS(ULPSACTIVENOT_ALL1); #undef PIS } + +static void dsi1_dump_irqs(struct seq_file *s) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_id(0); + + dsi_dump_dsidev_irqs(dsidev, s); +} + +static void dsi2_dump_irqs(struct seq_file *s) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_id(1); + + dsi_dump_dsidev_irqs(dsidev, s); +} + +void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, + const struct file_operations *debug_fops) +{ + struct platform_device *dsidev; + + dsidev = dsi_get_dsidev_from_id(0); + if (dsidev) + debugfs_create_file("dsi1_irqs", S_IRUGO, debugfs_dir, + &dsi1_dump_irqs, debug_fops); + + dsidev = dsi_get_dsidev_from_id(1); + if (dsidev) + debugfs_create_file("dsi2_irqs", S_IRUGO, debugfs_dir, + &dsi2_dump_irqs, debug_fops); +} #endif -void dsi_dump_regs(struct seq_file *s) +static void dsi_dump_dsidev_regs(struct platform_device *dsidev, + struct seq_file *s) { -#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(r)) +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(dsidev, r)) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + if (dsi_runtime_get(dsidev)) + return; + dsi_enable_scp_clk(dsidev); DUMPREG(DSI_REVISION); DUMPREG(DSI_SYSCONFIG); @@ -1378,25 +1965,57 @@ void dsi_dump_regs(struct seq_file *s) DUMPREG(DSI_PLL_CONFIGURATION1); DUMPREG(DSI_PLL_CONFIGURATION2); - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + dsi_disable_scp_clk(dsidev); + dsi_runtime_put(dsidev); #undef DUMPREG } -enum dsi_complexio_power_state { +static void dsi1_dump_regs(struct seq_file *s) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_id(0); + + dsi_dump_dsidev_regs(dsidev, s); +} + +static void dsi2_dump_regs(struct seq_file *s) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_id(1); + + dsi_dump_dsidev_regs(dsidev, s); +} + +void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, + const struct file_operations *debug_fops) +{ + struct platform_device *dsidev; + + dsidev = dsi_get_dsidev_from_id(0); + if (dsidev) + debugfs_create_file("dsi1_regs", S_IRUGO, debugfs_dir, + &dsi1_dump_regs, debug_fops); + + dsidev = dsi_get_dsidev_from_id(1); + if (dsidev) + debugfs_create_file("dsi2_regs", S_IRUGO, debugfs_dir, + &dsi2_dump_regs, debug_fops); +} +enum dsi_cio_power_state { DSI_COMPLEXIO_POWER_OFF = 0x0, DSI_COMPLEXIO_POWER_ON = 0x1, DSI_COMPLEXIO_POWER_ULPS = 0x2, }; -static int dsi_complexio_power(enum dsi_complexio_power_state state) +static int dsi_cio_power(struct platform_device *dsidev, + enum dsi_cio_power_state state) { int t = 0; /* PWR_CMD */ - REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27); + REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG1, state, 28, 27); /* PWR_STATUS */ - while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) { + while (FLD_GET(dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1), + 26, 25) != state) { if (++t > 1000) { DSSERR("failed to set complexio power state to " "%d\n", state); @@ -1408,9 +2027,70 @@ static int dsi_complexio_power(enum dsi_complexio_power_state state) return 0; } -static void dsi_complexio_config(struct omap_dss_device *dssdev) +/* Number of data lanes present on DSI interface */ +static inline int dsi_get_num_data_lanes(struct platform_device *dsidev) +{ + /* DSI on OMAP3 doesn't have register DSI_GNQ, set number + * of data lanes as 2 by default */ + if (dss_has_feature(FEAT_DSI_GNQ)) + return REG_GET(dsidev, DSI_GNQ, 11, 9); /* NB_DATA_LANES */ + else + return 2; +} + +/* Number of data lanes used by the dss device */ +static inline int dsi_get_num_data_lanes_dssdev(struct omap_dss_device *dssdev) +{ + int num_data_lanes = 0; + + if (dssdev->phy.dsi.data1_lane != 0) + num_data_lanes++; + if (dssdev->phy.dsi.data2_lane != 0) + num_data_lanes++; + if (dssdev->phy.dsi.data3_lane != 0) + num_data_lanes++; + if (dssdev->phy.dsi.data4_lane != 0) + num_data_lanes++; + + return num_data_lanes; +} + +static unsigned dsi_get_line_buf_size(struct platform_device *dsidev) { + int val; + + /* line buffer on OMAP3 is 1024 x 24bits */ + /* XXX: for some reason using full buffer size causes + * considerable TX slowdown with update sizes that fill the + * whole buffer */ + if (!dss_has_feature(FEAT_DSI_GNQ)) + return 1023 * 3; + + val = REG_GET(dsidev, DSI_GNQ, 14, 12); /* VP1_LINE_BUFFER_SIZE */ + + switch (val) { + case 1: + return 512 * 3; /* 512x24 bits */ + case 2: + return 682 * 3; /* 682x24 bits */ + case 3: + return 853 * 3; /* 853x24 bits */ + case 4: + return 1024 * 3; /* 1024x24 bits */ + case 5: + return 1194 * 3; /* 1194x24 bits */ + case 6: + return 1365 * 3; /* 1365x24 bits */ + default: + BUG(); + } +} + +static void dsi_set_lane_config(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); u32 r; + int num_data_lanes_dssdev = dsi_get_num_data_lanes_dssdev(dssdev); int clk_lane = dssdev->phy.dsi.clk_lane; int data1_lane = dssdev->phy.dsi.data1_lane; @@ -1419,14 +2099,28 @@ static void dsi_complexio_config(struct omap_dss_device *dssdev) int data1_pol = dssdev->phy.dsi.data1_pol; int data2_pol = dssdev->phy.dsi.data2_pol; - r = dsi_read_reg(DSI_COMPLEXIO_CFG1); + r = dsi_read_reg(dsidev, DSI_COMPLEXIO_CFG1); r = FLD_MOD(r, clk_lane, 2, 0); r = FLD_MOD(r, clk_pol, 3, 3); r = FLD_MOD(r, data1_lane, 6, 4); r = FLD_MOD(r, data1_pol, 7, 7); r = FLD_MOD(r, data2_lane, 10, 8); r = FLD_MOD(r, data2_pol, 11, 11); - dsi_write_reg(DSI_COMPLEXIO_CFG1, r); + if (num_data_lanes_dssdev > 2) { + int data3_lane = dssdev->phy.dsi.data3_lane; + int data3_pol = dssdev->phy.dsi.data3_pol; + + r = FLD_MOD(r, data3_lane, 14, 12); + r = FLD_MOD(r, data3_pol, 15, 15); + } + if (num_data_lanes_dssdev > 3) { + int data4_lane = dssdev->phy.dsi.data4_lane; + int data4_pol = dssdev->phy.dsi.data4_pol; + + r = FLD_MOD(r, data4_lane, 18, 16); + r = FLD_MOD(r, data4_pol, 19, 19); + } + dsi_write_reg(dsidev, DSI_COMPLEXIO_CFG1, r); /* The configuration of the DSI complex I/O (number of data lanes, position, differential order) should not be changed while @@ -1440,27 +2134,31 @@ static void dsi_complexio_config(struct omap_dss_device *dssdev) DSI complex I/O configuration is unknown. */ /* - REG_FLD_MOD(DSI_CTRL, 1, 0, 0); - REG_FLD_MOD(DSI_CTRL, 0, 0, 0); - REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); - REG_FLD_MOD(DSI_CTRL, 1, 0, 0); + REG_FLD_MOD(dsidev, DSI_CTRL, 1, 0, 0); + REG_FLD_MOD(dsidev, DSI_CTRL, 0, 0, 0); + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); + REG_FLD_MOD(dsidev, DSI_CTRL, 1, 0, 0); */ } -static inline unsigned ns2ddr(unsigned ns) +static inline unsigned ns2ddr(struct platform_device *dsidev, unsigned ns) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + /* convert time in ns to ddr ticks, rounding up */ - unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4; return (ns * (ddr_clk / 1000 / 1000) + 999) / 1000; } -static inline unsigned ddr2ns(unsigned ddr) +static inline unsigned ddr2ns(struct platform_device *dsidev, unsigned ddr) { - unsigned long ddr_clk = dsi.current_cinfo.clkin4ddr / 4; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + unsigned long ddr_clk = dsi->current_cinfo.clkin4ddr / 4; return ddr * 1000 * 1000 / (ddr_clk / 1000); } -static void dsi_complexio_timings(void) +static void dsi_cio_timings(struct platform_device *dsidev) { u32 r; u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit; @@ -1472,185 +2170,335 @@ static void dsi_complexio_timings(void) /* 1 * DDR_CLK = 2 * UI */ /* min 40ns + 4*UI max 85ns + 6*UI */ - ths_prepare = ns2ddr(70) + 2; + ths_prepare = ns2ddr(dsidev, 70) + 2; /* min 145ns + 10*UI */ - ths_prepare_ths_zero = ns2ddr(175) + 2; + ths_prepare_ths_zero = ns2ddr(dsidev, 175) + 2; /* min max(8*UI, 60ns+4*UI) */ - ths_trail = ns2ddr(60) + 5; + ths_trail = ns2ddr(dsidev, 60) + 5; /* min 100ns */ - ths_exit = ns2ddr(145); + ths_exit = ns2ddr(dsidev, 145); /* tlpx min 50n */ - tlpx_half = ns2ddr(25); + tlpx_half = ns2ddr(dsidev, 25); /* min 60ns */ - tclk_trail = ns2ddr(60) + 2; + tclk_trail = ns2ddr(dsidev, 60) + 2; /* min 38ns, max 95ns */ - tclk_prepare = ns2ddr(65); + tclk_prepare = ns2ddr(dsidev, 65); /* min tclk-prepare + tclk-zero = 300ns */ - tclk_zero = ns2ddr(260); + tclk_zero = ns2ddr(dsidev, 260); DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", - ths_prepare, ddr2ns(ths_prepare), - ths_prepare_ths_zero, ddr2ns(ths_prepare_ths_zero)); + ths_prepare, ddr2ns(dsidev, ths_prepare), + ths_prepare_ths_zero, ddr2ns(dsidev, ths_prepare_ths_zero)); DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n", - ths_trail, ddr2ns(ths_trail), - ths_exit, ddr2ns(ths_exit)); + ths_trail, ddr2ns(dsidev, ths_trail), + ths_exit, ddr2ns(dsidev, ths_exit)); DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), " "tclk_zero %u (%uns)\n", - tlpx_half, ddr2ns(tlpx_half), - tclk_trail, ddr2ns(tclk_trail), - tclk_zero, ddr2ns(tclk_zero)); + tlpx_half, ddr2ns(dsidev, tlpx_half), + tclk_trail, ddr2ns(dsidev, tclk_trail), + tclk_zero, ddr2ns(dsidev, tclk_zero)); DSSDBG("tclk_prepare %u (%uns)\n", - tclk_prepare, ddr2ns(tclk_prepare)); + tclk_prepare, ddr2ns(dsidev, tclk_prepare)); /* program timings */ - r = dsi_read_reg(DSI_DSIPHY_CFG0); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); r = FLD_MOD(r, ths_prepare, 31, 24); r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16); r = FLD_MOD(r, ths_trail, 15, 8); r = FLD_MOD(r, ths_exit, 7, 0); - dsi_write_reg(DSI_DSIPHY_CFG0, r); + dsi_write_reg(dsidev, DSI_DSIPHY_CFG0, r); - r = dsi_read_reg(DSI_DSIPHY_CFG1); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); r = FLD_MOD(r, tlpx_half, 22, 16); r = FLD_MOD(r, tclk_trail, 15, 8); r = FLD_MOD(r, tclk_zero, 7, 0); - dsi_write_reg(DSI_DSIPHY_CFG1, r); + dsi_write_reg(dsidev, DSI_DSIPHY_CFG1, r); - r = dsi_read_reg(DSI_DSIPHY_CFG2); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2); r = FLD_MOD(r, tclk_prepare, 7, 0); - dsi_write_reg(DSI_DSIPHY_CFG2, r); + dsi_write_reg(dsidev, DSI_DSIPHY_CFG2, r); } +static void dsi_cio_enable_lane_override(struct omap_dss_device *dssdev, + enum dsi_lane lanes) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int clk_lane = dssdev->phy.dsi.clk_lane; + int data1_lane = dssdev->phy.dsi.data1_lane; + int data2_lane = dssdev->phy.dsi.data2_lane; + int data3_lane = dssdev->phy.dsi.data3_lane; + int data4_lane = dssdev->phy.dsi.data4_lane; + int clk_pol = dssdev->phy.dsi.clk_pol; + int data1_pol = dssdev->phy.dsi.data1_pol; + int data2_pol = dssdev->phy.dsi.data2_pol; + int data3_pol = dssdev->phy.dsi.data3_pol; + int data4_pol = dssdev->phy.dsi.data4_pol; + + u32 l = 0; + u8 lptxscp_start = dsi->num_data_lanes == 2 ? 22 : 26; + + if (lanes & DSI_CLK_P) + l |= 1 << ((clk_lane - 1) * 2 + (clk_pol ? 0 : 1)); + if (lanes & DSI_CLK_N) + l |= 1 << ((clk_lane - 1) * 2 + (clk_pol ? 1 : 0)); + + if (lanes & DSI_DATA1_P) + l |= 1 << ((data1_lane - 1) * 2 + (data1_pol ? 0 : 1)); + if (lanes & DSI_DATA1_N) + l |= 1 << ((data1_lane - 1) * 2 + (data1_pol ? 1 : 0)); + + if (lanes & DSI_DATA2_P) + l |= 1 << ((data2_lane - 1) * 2 + (data2_pol ? 0 : 1)); + if (lanes & DSI_DATA2_N) + l |= 1 << ((data2_lane - 1) * 2 + (data2_pol ? 1 : 0)); + + if (lanes & DSI_DATA3_P) + l |= 1 << ((data3_lane - 1) * 2 + (data3_pol ? 0 : 1)); + if (lanes & DSI_DATA3_N) + l |= 1 << ((data3_lane - 1) * 2 + (data3_pol ? 1 : 0)); + + if (lanes & DSI_DATA4_P) + l |= 1 << ((data4_lane - 1) * 2 + (data4_pol ? 0 : 1)); + if (lanes & DSI_DATA4_N) + l |= 1 << ((data4_lane - 1) * 2 + (data4_pol ? 1 : 0)); + /* + * Bits in REGLPTXSCPDAT4TO0DXDY: + * 17: DY0 18: DX0 + * 19: DY1 20: DX1 + * 21: DY2 22: DX2 + * 23: DY3 24: DX3 + * 25: DY4 26: DX4 + */ + + /* Set the lane override configuration */ + + /* REGLPTXSCPDAT4TO0DXDY */ + REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, l, lptxscp_start, 17); + + /* Enable lane override */ -static int dsi_complexio_init(struct omap_dss_device *dssdev) + /* ENLPTXSCPDAT */ + REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 1, 27, 27); +} + +static void dsi_cio_disable_lane_override(struct platform_device *dsidev) { - int r = 0; + /* Disable lane override */ + REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 27, 27); /* ENLPTXSCPDAT */ + /* Reset the lane override configuration */ + /* REGLPTXSCPDAT4TO0DXDY */ + REG_FLD_MOD(dsidev, DSI_DSIPHY_CFG10, 0, 22, 17); +} - DSSDBG("dsi_complexio_init\n"); +static int dsi_cio_wait_tx_clk_esc_reset(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + int t; + int bits[3]; + bool in_use[3]; + + if (dss_has_feature(FEAT_DSI_REVERSE_TXCLKESC)) { + bits[0] = 28; + bits[1] = 27; + bits[2] = 26; + } else { + bits[0] = 24; + bits[1] = 25; + bits[2] = 26; + } - /* CIO_CLK_ICG, enable L3 clk to CIO */ - REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14); + in_use[0] = false; + in_use[1] = false; + in_use[2] = false; + + if (dssdev->phy.dsi.clk_lane != 0) + in_use[dssdev->phy.dsi.clk_lane - 1] = true; + if (dssdev->phy.dsi.data1_lane != 0) + in_use[dssdev->phy.dsi.data1_lane - 1] = true; + if (dssdev->phy.dsi.data2_lane != 0) + in_use[dssdev->phy.dsi.data2_lane - 1] = true; + + t = 100000; + while (true) { + u32 l; + int i; + int ok; + + l = dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); + + ok = 0; + for (i = 0; i < 3; ++i) { + if (!in_use[i] || (l & (1 << bits[i]))) + ok++; + } + + if (ok == 3) + break; + + if (--t == 0) { + for (i = 0; i < 3; ++i) { + if (!in_use[i] || (l & (1 << bits[i]))) + continue; + + DSSERR("CIO TXCLKESC%d domain not coming " \ + "out of reset\n", i); + } + return -EIO; + } + } + + return 0; +} + +static int dsi_cio_init(struct omap_dss_device *dssdev) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int r; + int num_data_lanes_dssdev = dsi_get_num_data_lanes_dssdev(dssdev); + u32 l; + + DSSDBGF(); + + if (dsi->dsi_mux_pads) + dsi->dsi_mux_pads(true); + + dsi_enable_scp_clk(dsidev); /* A dummy read using the SCP interface to any DSIPHY register is * required after DSIPHY reset to complete the reset of the DSI complex * I/O. */ - dsi_read_reg(DSI_DSIPHY_CFG5); + dsi_read_reg(dsidev, DSI_DSIPHY_CFG5); - if (wait_for_bit_change(DSI_DSIPHY_CFG5, 30, 1) != 1) { - DSSERR("ComplexIO PHY not coming out of reset.\n"); - r = -ENODEV; - goto err; + if (wait_for_bit_change(dsidev, DSI_DSIPHY_CFG5, 30, 1) != 1) { + DSSERR("CIO SCP Clock domain not coming out of reset.\n"); + r = -EIO; + goto err_scp_clk_dom; } - dsi_complexio_config(dssdev); + dsi_set_lane_config(dssdev); - r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON); + /* set TX STOP MODE timer to maximum for this operation */ + l = dsi_read_reg(dsidev, DSI_TIMING1); + l = FLD_MOD(l, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ + l = FLD_MOD(l, 1, 14, 14); /* STOP_STATE_X16_IO */ + l = FLD_MOD(l, 1, 13, 13); /* STOP_STATE_X4_IO */ + l = FLD_MOD(l, 0x1fff, 12, 0); /* STOP_STATE_COUNTER_IO */ + dsi_write_reg(dsidev, DSI_TIMING1, l); - if (r) - goto err; + if (dsi->ulps_enabled) { + u32 lane_mask = DSI_CLK_P | DSI_DATA1_P | DSI_DATA2_P; - if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) { - DSSERR("ComplexIO not coming out of reset.\n"); - r = -ENODEV; - goto err; + DSSDBG("manual ulps exit\n"); + + /* ULPS is exited by Mark-1 state for 1ms, followed by + * stop state. DSS HW cannot do this via the normal + * ULPS exit sequence, as after reset the DSS HW thinks + * that we are not in ULPS mode, and refuses to send the + * sequence. So we need to send the ULPS exit sequence + * manually. + */ + + if (num_data_lanes_dssdev > 2) + lane_mask |= DSI_DATA3_P; + + if (num_data_lanes_dssdev > 3) + lane_mask |= DSI_DATA4_P; + + dsi_cio_enable_lane_override(dssdev, lane_mask); } - if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) { - DSSERR("ComplexIO LDO power down.\n"); + r = dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ON); + if (r) + goto err_cio_pwr; + + if (wait_for_bit_change(dsidev, DSI_COMPLEXIO_CFG1, 29, 1) != 1) { + DSSERR("CIO PWR clock domain not coming out of reset.\n"); r = -ENODEV; - goto err; + goto err_cio_pwr_dom; } - dsi_complexio_timings(); + dsi_if_enable(dsidev, true); + dsi_if_enable(dsidev, false); + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ - /* - The configuration of the DSI complex I/O (number of data lanes, - position, differential order) should not be changed while - DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the - hardware to recognize a new configuration of the complex I/O (done - in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow - this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next - reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20] - LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN - bit to 1. If the sequence is not followed, the DSi complex I/O - configuration is undetermined. - */ - dsi_if_enable(1); - dsi_if_enable(0); - REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ - dsi_if_enable(1); - dsi_if_enable(0); + r = dsi_cio_wait_tx_clk_esc_reset(dssdev); + if (r) + goto err_tx_clk_esc_rst; - DSSDBG("CIO init done\n"); -err: - return r; -} + if (dsi->ulps_enabled) { + /* Keep Mark-1 state for 1ms (as per DSI spec) */ + ktime_t wait = ns_to_ktime(1000 * 1000); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_hrtimeout(&wait, HRTIMER_MODE_REL); -static void dsi_complexio_uninit(void) -{ - dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF); -} + /* Disable the override. The lanes should be set to Mark-11 + * state by the HW */ + dsi_cio_disable_lane_override(dsidev); + } -static int _dsi_wait_reset(void) -{ - int t = 0; + /* FORCE_TX_STOP_MODE_IO */ + REG_FLD_MOD(dsidev, DSI_TIMING1, 0, 15, 15); - while (REG_GET(DSI_SYSSTATUS, 0, 0) == 0) { - if (++t > 5) { - DSSERR("soft reset failed\n"); - return -ENODEV; - } - udelay(1); - } + dsi_cio_timings(dsidev); + + dsi->ulps_enabled = false; + + DSSDBG("CIO init done\n"); return 0; -} -static int _dsi_reset(void) -{ - /* Soft reset */ - REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1); - return _dsi_wait_reset(); +err_tx_clk_esc_rst: + REG_FLD_MOD(dsidev, DSI_CLK_CTRL, 0, 20, 20); /* LP_CLK_ENABLE */ +err_cio_pwr_dom: + dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); +err_cio_pwr: + if (dsi->ulps_enabled) + dsi_cio_disable_lane_override(dsidev); +err_scp_clk_dom: + dsi_disable_scp_clk(dsidev); + if (dsi->dsi_mux_pads) + dsi->dsi_mux_pads(false); + return r; } -static void dsi_reset_tx_fifo(int channel) +static void dsi_cio_uninit(struct platform_device *dsidev) { - u32 mask; - u32 l; + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - /* set fifosize of the channel to 0, then return the old size */ - l = dsi_read_reg(DSI_TX_FIFO_VC_SIZE); - - mask = FLD_MASK((8 * channel) + 7, (8 * channel) + 4); - dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l & ~mask); - - dsi_write_reg(DSI_TX_FIFO_VC_SIZE, l); + dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_OFF); + dsi_disable_scp_clk(dsidev); + if (dsi->dsi_mux_pads) + dsi->dsi_mux_pads(false); } -static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, +static void dsi_config_tx_fifo(struct platform_device *dsidev, + enum fifo_size size1, enum fifo_size size2, enum fifo_size size3, enum fifo_size size4) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r = 0; int add = 0; int i; - dsi.vc[0].fifo_size = size1; - dsi.vc[1].fifo_size = size2; - dsi.vc[2].fifo_size = size3; - dsi.vc[3].fifo_size = size4; + dsi->vc[0].fifo_size = size1; + dsi->vc[1].fifo_size = size2; + dsi->vc[2].fifo_size = size3; + dsi->vc[3].fifo_size = size4; for (i = 0; i < 4; i++) { u8 v; - int size = dsi.vc[i].fifo_size; + int size = dsi->vc[i].fifo_size; if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); @@ -1663,24 +2511,26 @@ static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, add += size; } - dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r); + dsi_write_reg(dsidev, DSI_TX_FIFO_VC_SIZE, r); } -static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, +static void dsi_config_rx_fifo(struct platform_device *dsidev, + enum fifo_size size1, enum fifo_size size2, enum fifo_size size3, enum fifo_size size4) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r = 0; int add = 0; int i; - dsi.vc[0].fifo_size = size1; - dsi.vc[1].fifo_size = size2; - dsi.vc[2].fifo_size = size3; - dsi.vc[3].fifo_size = size4; + dsi->vc[0].fifo_size = size1; + dsi->vc[1].fifo_size = size2; + dsi->vc[2].fifo_size = size3; + dsi->vc[3].fifo_size = size4; for (i = 0; i < 4; i++) { u8 v; - int size = dsi.vc[i].fifo_size; + int size = dsi->vc[i].fifo_size; if (add + size > 4) { DSSERR("Illegal FIFO configuration\n"); @@ -1693,18 +2543,18 @@ static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, add += size; } - dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r); + dsi_write_reg(dsidev, DSI_RX_FIFO_VC_SIZE, r); } -static int dsi_force_tx_stop_mode_io(void) +static int dsi_force_tx_stop_mode_io(struct platform_device *dsidev) { u32 r; - r = dsi_read_reg(DSI_TIMING1); + r = dsi_read_reg(dsidev, DSI_TIMING1); r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ - dsi_write_reg(DSI_TIMING1, r); + dsi_write_reg(dsidev, DSI_TIMING1, r); - if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) { + if (wait_for_bit_change(dsidev, DSI_TIMING1, 15, 0) != 0) { DSSERR("TX_STOP bit not going down\n"); return -EIO; } @@ -1712,16 +2562,135 @@ static int dsi_force_tx_stop_mode_io(void) return 0; } -static int dsi_vc_enable(int channel, bool enable) +static bool dsi_vc_is_enabled(struct platform_device *dsidev, int channel) +{ + return REG_GET(dsidev, DSI_VC_CTRL(channel), 0, 0); +} + +static void dsi_packet_sent_handler_vp(void *data, u32 mask) +{ + struct dsi_packet_sent_handler_data *vp_data = + (struct dsi_packet_sent_handler_data *) data; + struct dsi_data *dsi = dsi_get_dsidrv_data(vp_data->dsidev); + const int channel = dsi->update_channel; + u8 bit = dsi->te_enabled ? 30 : 31; + + if (REG_GET(vp_data->dsidev, DSI_VC_TE(channel), bit, bit) == 0) + complete(vp_data->completion); +} + +static int dsi_sync_vc_vp(struct platform_device *dsidev, int channel) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + DECLARE_COMPLETION_ONSTACK(completion); + struct dsi_packet_sent_handler_data vp_data = { dsidev, &completion }; + int r = 0; + u8 bit; + + bit = dsi->te_enabled ? 30 : 31; + + r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, + &vp_data, DSI_VC_IRQ_PACKET_SENT); + if (r) + goto err0; + + /* Wait for completion only if TE_EN/TE_START is still set */ + if (REG_GET(dsidev, DSI_VC_TE(channel), bit, bit)) { + if (wait_for_completion_timeout(&completion, + msecs_to_jiffies(10)) == 0) { + DSSERR("Failed to complete previous frame transfer\n"); + r = -EIO; + goto err1; + } + } + + dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, + &vp_data, DSI_VC_IRQ_PACKET_SENT); + + return 0; +err1: + dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_vp, + &vp_data, DSI_VC_IRQ_PACKET_SENT); +err0: + return r; +} + +static void dsi_packet_sent_handler_l4(void *data, u32 mask) +{ + struct dsi_packet_sent_handler_data *l4_data = + (struct dsi_packet_sent_handler_data *) data; + struct dsi_data *dsi = dsi_get_dsidrv_data(l4_data->dsidev); + const int channel = dsi->update_channel; + + if (REG_GET(l4_data->dsidev, DSI_VC_CTRL(channel), 5, 5) == 0) + complete(l4_data->completion); +} + +static int dsi_sync_vc_l4(struct platform_device *dsidev, int channel) +{ + DECLARE_COMPLETION_ONSTACK(completion); + struct dsi_packet_sent_handler_data l4_data = { dsidev, &completion }; + int r = 0; + + r = dsi_register_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, + &l4_data, DSI_VC_IRQ_PACKET_SENT); + if (r) + goto err0; + + /* Wait for completion only if TX_FIFO_NOT_EMPTY is still set */ + if (REG_GET(dsidev, DSI_VC_CTRL(channel), 5, 5)) { + if (wait_for_completion_timeout(&completion, + msecs_to_jiffies(10)) == 0) { + DSSERR("Failed to complete previous l4 transfer\n"); + r = -EIO; + goto err1; + } + } + + dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, + &l4_data, DSI_VC_IRQ_PACKET_SENT); + + return 0; +err1: + dsi_unregister_isr_vc(dsidev, channel, dsi_packet_sent_handler_l4, + &l4_data, DSI_VC_IRQ_PACKET_SENT); +err0: + return r; +} + +static int dsi_sync_vc(struct platform_device *dsidev, int channel) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + WARN_ON(!dsi_bus_is_locked(dsidev)); + + WARN_ON(in_interrupt()); + + if (!dsi_vc_is_enabled(dsidev, channel)) + return 0; + + switch (dsi->vc[channel].mode) { + case DSI_VC_MODE_VP: + return dsi_sync_vc_vp(dsidev, channel); + case DSI_VC_MODE_L4: + return dsi_sync_vc_l4(dsidev, channel); + default: + BUG(); + } +} + +static int dsi_vc_enable(struct platform_device *dsidev, int channel, + bool enable) { DSSDBG("dsi_vc_enable channel %d, enable %d\n", channel, enable); enable = enable ? 1 : 0; - REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0); + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 0, 0); - if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) { + if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), + 0, enable) != enable) { DSSERR("Failed to set dsi_vc_enable to %d\n", enable); return -EIO; } @@ -1729,13 +2698,13 @@ static int dsi_vc_enable(int channel, bool enable) return 0; } -static void dsi_vc_initial_config(int channel) +static void dsi_vc_initial_config(struct platform_device *dsidev, int channel) { u32 r; DSSDBGF("%d", channel); - r = dsi_read_reg(DSI_VC_CTRL(channel)); + r = dsi_read_reg(dsidev, DSI_VC_CTRL(channel)); if (FLD_GET(r, 15, 15)) /* VC_BUSY */ DSSERR("VC(%d) busy when trying to configure it!\n", @@ -1748,87 +2717,107 @@ static void dsi_vc_initial_config(int channel) r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */ + if (dss_has_feature(FEAT_DSI_VC_OCP_WIDTH)) + r = FLD_MOD(r, 3, 11, 10); /* OCP_WIDTH = 32 bit */ r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ - dsi_write_reg(DSI_VC_CTRL(channel), r); - - dsi.vc[channel].mode = DSI_VC_MODE_L4; + dsi_write_reg(dsidev, DSI_VC_CTRL(channel), r); } -static int dsi_vc_config_l4(int channel) +static int dsi_vc_config_l4(struct platform_device *dsidev, int channel) { - if (dsi.vc[channel].mode == DSI_VC_MODE_L4) + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->vc[channel].mode == DSI_VC_MODE_L4) return 0; DSSDBGF("%d", channel); - dsi_vc_enable(channel, 0); + dsi_sync_vc(dsidev, channel); + + dsi_vc_enable(dsidev, channel, 0); /* VC_BUSY */ - if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) { + if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) { DSSERR("vc(%d) busy when trying to config for L4\n", channel); return -EIO; } - REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ - dsi_vc_enable(channel, 1); + /* DCS_CMD_ENABLE */ + if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 0, 30, 30); - dsi.vc[channel].mode = DSI_VC_MODE_L4; + dsi_vc_enable(dsidev, channel, 1); + + dsi->vc[channel].mode = DSI_VC_MODE_L4; return 0; } -static int dsi_vc_config_vp(int channel) +static int dsi_vc_config_vp(struct platform_device *dsidev, int channel) { - if (dsi.vc[channel].mode == DSI_VC_MODE_VP) + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->vc[channel].mode == DSI_VC_MODE_VP) return 0; DSSDBGF("%d", channel); - dsi_vc_enable(channel, 0); + dsi_sync_vc(dsidev, channel); + + dsi_vc_enable(dsidev, channel, 0); /* VC_BUSY */ - if (wait_for_bit_change(DSI_VC_CTRL(channel), 15, 0) != 0) { + if (wait_for_bit_change(dsidev, DSI_VC_CTRL(channel), 15, 0) != 0) { DSSERR("vc(%d) busy when trying to config for VP\n", channel); return -EIO; } - REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */ + /* SOURCE, 1 = video port */ + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 1, 1); + + /* DCS_CMD_ENABLE */ + if (dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 30, 30); - dsi_vc_enable(channel, 1); + dsi_vc_enable(dsidev, channel, 1); - dsi.vc[channel].mode = DSI_VC_MODE_VP; + dsi->vc[channel].mode = DSI_VC_MODE_VP; return 0; } -void omapdss_dsi_vc_enable_hs(int channel, bool enable) +void omapdss_dsi_vc_enable_hs(struct omap_dss_device *dssdev, int channel, + bool enable) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - dsi_vc_enable(channel, 0); - dsi_if_enable(0); + dsi_vc_enable(dsidev, channel, 0); + dsi_if_enable(dsidev, 0); - REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9); + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), enable, 9, 9); - dsi_vc_enable(channel, 1); - dsi_if_enable(1); + dsi_vc_enable(dsidev, channel, 1); + dsi_if_enable(dsidev, 1); - dsi_force_tx_stop_mode_io(); + dsi_force_tx_stop_mode_io(dsidev); } EXPORT_SYMBOL(omapdss_dsi_vc_enable_hs); -static void dsi_vc_flush_long_data(int channel) +static void dsi_vc_flush_long_data(struct platform_device *dsidev, int channel) { - while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { u32 val; - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", (val >> 0) & 0xff, (val >> 8) & 0xff, @@ -1874,13 +2863,14 @@ static void dsi_show_rx_ack_with_err(u16 err) DSSERR("\t\tDSI Protocol Violation\n"); } -static u16 dsi_vc_flush_receive_data(int channel) +static u16 dsi_vc_flush_receive_data(struct platform_device *dsidev, + int channel) { /* RX_FIFO_NOT_EMPTY */ - while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { + while (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { u32 val; u8 dt; - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); + val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); DSSERR("\trawval %#08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == DSI_DT_RX_ACK_WITH_ERR) { @@ -1895,7 +2885,7 @@ static u16 dsi_vc_flush_receive_data(int channel) } else if (dt == DSI_DT_RX_DCS_LONG_READ) { DSSERR("\tDCS long response, len %d\n", FLD_GET(val, 23, 8)); - dsi_vc_flush_long_data(channel); + dsi_vc_flush_long_data(dsidev, channel); } else { DSSERR("\tunknown datatype 0x%02x\n", dt); } @@ -1903,74 +2893,90 @@ static u16 dsi_vc_flush_receive_data(int channel) return 0; } -static int dsi_vc_send_bta(int channel) +static int dsi_vc_send_bta(struct platform_device *dsidev, int channel) { - if (dsi.debug_write || dsi.debug_read) + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->debug_write || dsi->debug_read) DSSDBG("dsi_vc_send_bta %d\n", channel); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); - dsi_vc_flush_receive_data(channel); + dsi_vc_flush_receive_data(dsidev, channel); } - REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ + REG_FLD_MOD(dsidev, DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ return 0; } -int dsi_vc_send_bta_sync(int channel) +int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int channel) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + DECLARE_COMPLETION_ONSTACK(completion); int r = 0; u32 err; - INIT_COMPLETION(dsi.bta_completion); + r = dsi_register_isr_vc(dsidev, channel, dsi_completion_handler, + &completion, DSI_VC_IRQ_BTA); + if (r) + goto err0; - dsi_vc_enable_bta_irq(channel); + r = dsi_register_isr(dsidev, dsi_completion_handler, &completion, + DSI_IRQ_ERROR_MASK); + if (r) + goto err1; - r = dsi_vc_send_bta(channel); + r = dsi_vc_send_bta(dsidev, channel); if (r) - goto err; + goto err2; - if (wait_for_completion_timeout(&dsi.bta_completion, + if (wait_for_completion_timeout(&completion, msecs_to_jiffies(500)) == 0) { DSSERR("Failed to receive BTA\n"); r = -EIO; - goto err; + goto err2; } - err = dsi_get_errors(); + err = dsi_get_errors(dsidev); if (err) { DSSERR("Error while sending BTA: %x\n", err); r = -EIO; - goto err; + goto err2; } -err: - dsi_vc_disable_bta_irq(channel); - +err2: + dsi_unregister_isr(dsidev, dsi_completion_handler, &completion, + DSI_IRQ_ERROR_MASK); +err1: + dsi_unregister_isr_vc(dsidev, channel, dsi_completion_handler, + &completion, DSI_VC_IRQ_BTA); +err0: return r; } EXPORT_SYMBOL(dsi_vc_send_bta_sync); -static inline void dsi_vc_write_long_header(int channel, u8 data_type, - u16 len, u8 ecc) +static inline void dsi_vc_write_long_header(struct platform_device *dsidev, + int channel, u8 data_type, u16 len, u8 ecc) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 val; u8 data_id; - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - data_id = data_type | channel << 6; + data_id = data_type | dsi->vc[channel].vc_id << 6; val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) | FLD_VAL(ecc, 31, 24); - dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val); + dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_HEADER(channel), val); } -static inline void dsi_vc_write_long_payload(int channel, - u8 b1, u8 b2, u8 b3, u8 b4) +static inline void dsi_vc_write_long_payload(struct platform_device *dsidev, + int channel, u8 b1, u8 b2, u8 b3, u8 b4) { u32 val; @@ -1979,34 +2985,35 @@ static inline void dsi_vc_write_long_payload(int channel, /* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n", b1, b2, b3, b4, val); */ - dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val); + dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(channel), val); } -static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, - u8 ecc) +static int dsi_vc_send_long(struct platform_device *dsidev, int channel, + u8 data_type, u8 *data, u16 len, u8 ecc) { /*u32 val; */ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int i; u8 *p; int r = 0; u8 b1, b2, b3, b4; - if (dsi.debug_write) + if (dsi->debug_write) DSSDBG("dsi_vc_send_long, %d bytes\n", len); /* len + header */ - if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) { + if (dsi->vc[channel].fifo_size * 32 * 4 < len + 4) { DSSERR("unable to send long packet: packet too long.\n"); return -EINVAL; } - dsi_vc_config_l4(channel); + dsi_vc_config_l4(dsidev, channel); - dsi_vc_write_long_header(channel, data_type, len, ecc); + dsi_vc_write_long_header(dsidev, channel, data_type, len, ecc); p = data; for (i = 0; i < len >> 2; i++) { - if (dsi.debug_write) + if (dsi->debug_write) DSSDBG("\tsending full packet %d\n", i); b1 = *p++; @@ -2014,14 +3021,14 @@ static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, b3 = *p++; b4 = *p++; - dsi_vc_write_long_payload(channel, b1, b2, b3, b4); + dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, b4); } i = len % 4; if (i) { b1 = 0; b2 = 0; b3 = 0; - if (dsi.debug_write) + if (dsi->debug_write) DSSDBG("\tsending remainder bytes %d\n", i); switch (i) { @@ -2039,62 +3046,69 @@ static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, break; } - dsi_vc_write_long_payload(channel, b1, b2, b3, 0); + dsi_vc_write_long_payload(dsidev, channel, b1, b2, b3, 0); } return r; } -static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc) +static int dsi_vc_send_short(struct platform_device *dsidev, int channel, + u8 data_type, u16 data, u8 ecc) { + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 r; u8 data_id; - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - if (dsi.debug_write) + if (dsi->debug_write) DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n", channel, data_type, data & 0xff, (data >> 8) & 0xff); - dsi_vc_config_l4(channel); + dsi_vc_config_l4(dsidev, channel); - if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) { + if (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(channel)), 16, 16)) { DSSERR("ERROR FIFO FULL, aborting transfer\n"); return -EINVAL; } - data_id = data_type | channel << 6; + data_id = data_type | dsi->vc[channel].vc_id << 6; r = (data_id << 0) | (data << 8) | (ecc << 24); - dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r); + dsi_write_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel), r); return 0; } -int dsi_vc_send_null(int channel) +int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); u8 nullpkg[] = {0, 0, 0, 0}; - return dsi_vc_send_long(channel, DSI_DT_NULL_PACKET, nullpkg, 4, 0); + + return dsi_vc_send_long(dsidev, channel, DSI_DT_NULL_PACKET, nullpkg, + 4, 0); } EXPORT_SYMBOL(dsi_vc_send_null); -int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) +int dsi_vc_dcs_write_nosync(struct omap_dss_device *dssdev, int channel, + u8 *data, int len) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); int r; BUG_ON(len == 0); if (len == 1) { - r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0, + r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_SHORT_WRITE_0, data[0], 0); } else if (len == 2) { - r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1, + r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_SHORT_WRITE_1, data[0] | (data[1] << 8), 0); } else { /* 0x39 = DCS Long Write */ - r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE, + r = dsi_vc_send_long(dsidev, channel, DSI_DT_DCS_LONG_WRITE, data, len, 0); } @@ -2102,21 +3116,24 @@ int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) } EXPORT_SYMBOL(dsi_vc_dcs_write_nosync); -int dsi_vc_dcs_write(int channel, u8 *data, int len) +int dsi_vc_dcs_write(struct omap_dss_device *dssdev, int channel, u8 *data, + int len) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); int r; - r = dsi_vc_dcs_write_nosync(channel, data, len); + r = dsi_vc_dcs_write_nosync(dssdev, channel, data, len); if (r) goto err; - r = dsi_vc_send_bta_sync(channel); + r = dsi_vc_send_bta_sync(dssdev, channel); if (r) goto err; - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ + /* RX_FIFO_NOT_EMPTY */ + if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20)) { DSSERR("rx fifo not empty after write, dumping data:\n"); - dsi_vc_flush_receive_data(channel); + dsi_vc_flush_receive_data(dsidev, channel); r = -EIO; goto err; } @@ -2129,47 +3146,51 @@ err: } EXPORT_SYMBOL(dsi_vc_dcs_write); -int dsi_vc_dcs_write_0(int channel, u8 dcs_cmd) +int dsi_vc_dcs_write_0(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd) { - return dsi_vc_dcs_write(channel, &dcs_cmd, 1); + return dsi_vc_dcs_write(dssdev, channel, &dcs_cmd, 1); } EXPORT_SYMBOL(dsi_vc_dcs_write_0); -int dsi_vc_dcs_write_1(int channel, u8 dcs_cmd, u8 param) +int dsi_vc_dcs_write_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, + u8 param) { u8 buf[2]; buf[0] = dcs_cmd; buf[1] = param; - return dsi_vc_dcs_write(channel, buf, 2); + return dsi_vc_dcs_write(dssdev, channel, buf, 2); } EXPORT_SYMBOL(dsi_vc_dcs_write_1); -int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) +int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, + u8 *buf, int buflen) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); u32 val; u8 dt; int r; - if (dsi.debug_read) + if (dsi->debug_read) DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %x)\n", channel, dcs_cmd); - r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0); + r = dsi_vc_send_short(dsidev, channel, DSI_DT_DCS_READ, dcs_cmd, 0); if (r) goto err; - r = dsi_vc_send_bta_sync(channel); + r = dsi_vc_send_bta_sync(dssdev, channel); if (r) goto err; /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(DSI_VC_CTRL(channel), 20, 20) == 0) { + if (REG_GET(dsidev, DSI_VC_CTRL(channel), 20, 20) == 0) { DSSERR("RX fifo empty when trying to read.\n"); r = -EIO; goto err; } - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); - if (dsi.debug_read) + val = dsi_read_reg(dsidev, DSI_VC_SHORT_PACKET_HEADER(channel)); + if (dsi->debug_read) DSSDBG("\theader: %08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == DSI_DT_RX_ACK_WITH_ERR) { @@ -2180,7 +3201,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) } else if (dt == DSI_DT_RX_SHORT_READ_1) { u8 data = FLD_GET(val, 15, 8); - if (dsi.debug_read) + if (dsi->debug_read) DSSDBG("\tDCS short response, 1 byte: %02x\n", data); if (buflen < 1) { @@ -2193,7 +3214,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) return 1; } else if (dt == DSI_DT_RX_SHORT_READ_2) { u16 data = FLD_GET(val, 23, 8); - if (dsi.debug_read) + if (dsi->debug_read) DSSDBG("\tDCS short response, 2 byte: %04x\n", data); if (buflen < 2) { @@ -2208,7 +3229,7 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) } else if (dt == DSI_DT_RX_DCS_LONG_READ) { int w; int len = FLD_GET(val, 23, 8); - if (dsi.debug_read) + if (dsi->debug_read) DSSDBG("\tDCS long response, len %d\n", len); if (len > buflen) { @@ -2219,8 +3240,9 @@ int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) /* two byte checksum ends the packet, not included in len */ for (w = 0; w < len + 2;) { int b; - val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); - if (dsi.debug_read) + val = dsi_read_reg(dsidev, + DSI_VC_SHORT_PACKET_HEADER(channel)); + if (dsi->debug_read) DSSDBG("\t\t%02x %02x %02x %02x\n", (val >> 0) & 0xff, (val >> 8) & 0xff, @@ -2251,11 +3273,12 @@ err: } EXPORT_SYMBOL(dsi_vc_dcs_read); -int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data) +int dsi_vc_dcs_read_1(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, + u8 *data) { int r; - r = dsi_vc_dcs_read(channel, dcs_cmd, data, 1); + r = dsi_vc_dcs_read(dssdev, channel, dcs_cmd, data, 1); if (r < 0) return r; @@ -2267,12 +3290,13 @@ int dsi_vc_dcs_read_1(int channel, u8 dcs_cmd, u8 *data) } EXPORT_SYMBOL(dsi_vc_dcs_read_1); -int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u8 *data1, u8 *data2) +int dsi_vc_dcs_read_2(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, + u8 *data1, u8 *data2) { u8 buf[2]; int r; - r = dsi_vc_dcs_read(channel, dcs_cmd, buf, 2); + r = dsi_vc_dcs_read(dssdev, channel, dcs_cmd, buf, 2); if (r < 0) return r; @@ -2287,14 +3311,98 @@ int dsi_vc_dcs_read_2(int channel, u8 dcs_cmd, u8 *data1, u8 *data2) } EXPORT_SYMBOL(dsi_vc_dcs_read_2); -int dsi_vc_set_max_rx_packet_size(int channel, u16 len) +int dsi_vc_set_max_rx_packet_size(struct omap_dss_device *dssdev, int channel, + u16 len) { - return dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + + return dsi_vc_send_short(dsidev, channel, DSI_DT_SET_MAX_RET_PKG_SIZE, len, 0); } EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); -static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16) +static int dsi_enter_ulps(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + DECLARE_COMPLETION_ONSTACK(completion); + int r; + + DSSDBGF(); + + WARN_ON(!dsi_bus_is_locked(dsidev)); + + WARN_ON(dsi->ulps_enabled); + + if (dsi->ulps_enabled) + return 0; + + if (REG_GET(dsidev, DSI_CLK_CTRL, 13, 13)) { + DSSERR("DDR_CLK_ALWAYS_ON enabled when entering ULPS\n"); + return -EIO; + } + + dsi_sync_vc(dsidev, 0); + dsi_sync_vc(dsidev, 1); + dsi_sync_vc(dsidev, 2); + dsi_sync_vc(dsidev, 3); + + dsi_force_tx_stop_mode_io(dsidev); + + dsi_vc_enable(dsidev, 0, false); + dsi_vc_enable(dsidev, 1, false); + dsi_vc_enable(dsidev, 2, false); + dsi_vc_enable(dsidev, 3, false); + + if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 16, 16)) { /* HS_BUSY */ + DSSERR("HS busy when enabling ULPS\n"); + return -EIO; + } + + if (REG_GET(dsidev, DSI_COMPLEXIO_CFG2, 17, 17)) { /* LP_BUSY */ + DSSERR("LP busy when enabling ULPS\n"); + return -EIO; + } + + r = dsi_register_isr_cio(dsidev, dsi_completion_handler, &completion, + DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + if (r) + return r; + + /* Assert TxRequestEsc for data lanes and TxUlpsClk for clk lane */ + /* LANEx_ULPS_SIG2 */ + REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, (1 << 0) | (1 << 1) | (1 << 2), + 7, 5); + + if (wait_for_completion_timeout(&completion, + msecs_to_jiffies(1000)) == 0) { + DSSERR("ULPS enable timeout\n"); + r = -EIO; + goto err; + } + + dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion, + DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + + /* Reset LANEx_ULPS_SIG2 */ + REG_FLD_MOD(dsidev, DSI_COMPLEXIO_CFG2, (0 << 0) | (0 << 1) | (0 << 2), + 7, 5); + + dsi_cio_power(dsidev, DSI_COMPLEXIO_POWER_ULPS); + + dsi_if_enable(dsidev, false); + + dsi->ulps_enabled = true; + + return 0; + +err: + dsi_unregister_isr_cio(dsidev, dsi_completion_handler, &completion, + DSI_CIO_IRQ_ULPSACTIVENOT_ALL0); + return r; +} + +static void dsi_set_lp_rx_timeout(struct platform_device *dsidev, + unsigned ticks, bool x4, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -2303,14 +3411,14 @@ static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16) BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(); + fck = dsi_fclk_rate(dsidev); - r = dsi_read_reg(DSI_TIMING2); + r = dsi_read_reg(dsidev, DSI_TIMING2); r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* LP_RX_TO_X16 */ r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* LP_RX_TO_X4 */ r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ - dsi_write_reg(DSI_TIMING2, r); + dsi_write_reg(dsidev, DSI_TIMING2, r); total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); @@ -2320,7 +3428,8 @@ static void dsi_set_lp_rx_timeout(unsigned ticks, bool x4, bool x16) (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_ta_timeout(unsigned ticks, bool x8, bool x16) +static void dsi_set_ta_timeout(struct platform_device *dsidev, unsigned ticks, + bool x8, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -2329,14 +3438,14 @@ static void dsi_set_ta_timeout(unsigned ticks, bool x8, bool x16) BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(); + fck = dsi_fclk_rate(dsidev); - r = dsi_read_reg(DSI_TIMING1); + r = dsi_read_reg(dsidev, DSI_TIMING1); r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* TA_TO_X16 */ r = FLD_MOD(r, x8 ? 1 : 0, 29, 29); /* TA_TO_X8 */ r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ - dsi_write_reg(DSI_TIMING1, r); + dsi_write_reg(dsidev, DSI_TIMING1, r); total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1); @@ -2346,7 +3455,8 @@ static void dsi_set_ta_timeout(unsigned ticks, bool x8, bool x16) (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_stop_state_counter(unsigned ticks, bool x4, bool x16) +static void dsi_set_stop_state_counter(struct platform_device *dsidev, + unsigned ticks, bool x4, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -2355,14 +3465,14 @@ static void dsi_set_stop_state_counter(unsigned ticks, bool x4, bool x16) BUG_ON(ticks > 0x1fff); /* ticks in DSI_FCK */ - fck = dsi_fclk_rate(); + fck = dsi_fclk_rate(dsidev); - r = dsi_read_reg(DSI_TIMING1); + r = dsi_read_reg(dsidev, DSI_TIMING1); r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ r = FLD_MOD(r, x16 ? 1 : 0, 14, 14); /* STOP_STATE_X16_IO */ r = FLD_MOD(r, x4 ? 1 : 0, 13, 13); /* STOP_STATE_X4_IO */ r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ - dsi_write_reg(DSI_TIMING1, r); + dsi_write_reg(dsidev, DSI_TIMING1, r); total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); @@ -2372,7 +3482,8 @@ static void dsi_set_stop_state_counter(unsigned ticks, bool x4, bool x16) (total_ticks * 1000) / (fck / 1000 / 1000)); } -static void dsi_set_hs_tx_timeout(unsigned ticks, bool x4, bool x16) +static void dsi_set_hs_tx_timeout(struct platform_device *dsidev, + unsigned ticks, bool x4, bool x16) { unsigned long fck; unsigned long total_ticks; @@ -2381,14 +3492,14 @@ static void dsi_set_hs_tx_timeout(unsigned ticks, bool x4, bool x16) BUG_ON(ticks > 0x1fff); /* ticks in TxByteClkHS */ - fck = dsi_get_txbyteclkhs(); + fck = dsi_get_txbyteclkhs(dsidev); - r = dsi_read_reg(DSI_TIMING2); + r = dsi_read_reg(dsidev, DSI_TIMING2); r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ r = FLD_MOD(r, x16 ? 1 : 0, 30, 30); /* HS_TX_TO_X16 */ r = FLD_MOD(r, x4 ? 1 : 0, 29, 29); /* HS_TX_TO_X8 (4 really) */ r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ - dsi_write_reg(DSI_TIMING2, r); + dsi_write_reg(dsidev, DSI_TIMING2, r); total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); @@ -2399,24 +3510,25 @@ static void dsi_set_hs_tx_timeout(unsigned ticks, bool x4, bool x16) } static int dsi_proto_config(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); u32 r; int buswidth = 0; - dsi_config_tx_fifo(DSI_FIFO_SIZE_32, + dsi_config_tx_fifo(dsidev, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32); - dsi_config_rx_fifo(DSI_FIFO_SIZE_32, + dsi_config_rx_fifo(dsidev, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32, DSI_FIFO_SIZE_32); /* XXX what values for the timeouts? */ - dsi_set_stop_state_counter(0x1000, false, false); - dsi_set_ta_timeout(0x1fff, true, true); - dsi_set_lp_rx_timeout(0x1fff, true, true); - dsi_set_hs_tx_timeout(0x1fff, true, true); + dsi_set_stop_state_counter(dsidev, 0x1000, false, false); + dsi_set_ta_timeout(dsidev, 0x1fff, true, true); + dsi_set_lp_rx_timeout(dsidev, 0x1fff, true, true); + dsi_set_hs_tx_timeout(dsidev, 0x1fff, true, true); switch (dssdev->ctrl.pixel_size) { case 16: @@ -2432,7 +3544,7 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) BUG(); } - r = dsi_read_reg(DSI_CTRL); + r = dsi_read_reg(dsidev, DSI_CTRL); r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */ r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */ r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */ @@ -2442,21 +3554,25 @@ static int dsi_proto_config(struct omap_dss_device *dssdev) r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */ r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ - r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ - r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE, 1=start, 0=continue */ + if (!dss_has_feature(FEAT_DSI_DCS_CMD_CONFIG_VC)) { + r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ + /* DCS_CMD_CODE, 1=start, 0=continue */ + r = FLD_MOD(r, 0, 25, 25); + } - dsi_write_reg(DSI_CTRL, r); + dsi_write_reg(dsidev, DSI_CTRL, r); - dsi_vc_initial_config(0); - dsi_vc_initial_config(1); - dsi_vc_initial_config(2); - dsi_vc_initial_config(3); + dsi_vc_initial_config(dsidev, 0); + dsi_vc_initial_config(dsidev, 1); + dsi_vc_initial_config(dsidev, 2); + dsi_vc_initial_config(dsidev, 3); return 0; } static void dsi_proto_timings(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail; unsigned tclk_pre, tclk_post; unsigned ths_prepare, ths_prepare_ths_zero, ths_zero; @@ -2466,32 +3582,27 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) unsigned ths_eot; u32 r; - r = dsi_read_reg(DSI_DSIPHY_CFG0); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG0); ths_prepare = FLD_GET(r, 31, 24); ths_prepare_ths_zero = FLD_GET(r, 23, 16); ths_zero = ths_prepare_ths_zero - ths_prepare; ths_trail = FLD_GET(r, 15, 8); ths_exit = FLD_GET(r, 7, 0); - r = dsi_read_reg(DSI_DSIPHY_CFG1); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG1); tlpx = FLD_GET(r, 22, 16) * 2; tclk_trail = FLD_GET(r, 15, 8); tclk_zero = FLD_GET(r, 7, 0); - r = dsi_read_reg(DSI_DSIPHY_CFG2); + r = dsi_read_reg(dsidev, DSI_DSIPHY_CFG2); tclk_prepare = FLD_GET(r, 7, 0); /* min 8*UI */ tclk_pre = 20; /* min 60ns + 52*UI */ - tclk_post = ns2ddr(60) + 26; + tclk_post = ns2ddr(dsidev, 60) + 26; - /* ths_eot is 2 for 2 datalanes and 4 for 1 datalane */ - if (dssdev->phy.dsi.data1_lane != 0 && - dssdev->phy.dsi.data2_lane != 0) - ths_eot = 2; - else - ths_eot = 4; + ths_eot = DIV_ROUND_UP(4, dsi_get_num_data_lanes_dssdev(dssdev)); ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare, 4); @@ -2500,10 +3611,10 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255); BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255); - r = dsi_read_reg(DSI_CLK_TIMING); + r = dsi_read_reg(dsidev, DSI_CLK_TIMING); r = FLD_MOD(r, ddr_clk_pre, 15, 8); r = FLD_MOD(r, ddr_clk_post, 7, 0); - dsi_write_reg(DSI_CLK_TIMING, r); + dsi_write_reg(dsidev, DSI_CLK_TIMING, r); DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n", ddr_clk_pre, @@ -2517,7 +3628,7 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) r = FLD_VAL(enter_hs_mode_lat, 31, 16) | FLD_VAL(exit_hs_mode_lat, 15, 0); - dsi_write_reg(DSI_VM_TIMING7, r); + dsi_write_reg(dsidev, DSI_VM_TIMING7, r); DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", enter_hs_mode_lat, exit_hs_mode_lat); @@ -2527,25 +3638,27 @@ static void dsi_proto_timings(struct omap_dss_device *dssdev) #define DSI_DECL_VARS \ int __dsi_cb = 0; u32 __dsi_cv = 0; -#define DSI_FLUSH(ch) \ +#define DSI_FLUSH(dsidev, ch) \ if (__dsi_cb > 0) { \ /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \ - dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ + dsi_write_reg(dsidev, DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ __dsi_cb = __dsi_cv = 0; \ } -#define DSI_PUSH(ch, data) \ +#define DSI_PUSH(dsidev, ch, data) \ do { \ __dsi_cv |= (data) << (__dsi_cb * 8); \ /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \ if (++__dsi_cb > 3) \ - DSI_FLUSH(ch); \ + DSI_FLUSH(dsidev, ch); \ } while (0) static int dsi_update_screen_l4(struct omap_dss_device *dssdev, int x, int y, int w, int h) { /* Note: supports only 24bit colors in 32bit container */ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int first = 1; int fifo_stalls = 0; int max_dsi_packet_size; @@ -2584,7 +3697,7 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, * in fifo */ /* When using CPU, max long packet size is TX buffer size */ - max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4; + max_dsi_packet_size = dsi->vc[0].fifo_size * 32 * 4; /* we seem to get better perf if we divide the tx fifo to half, and while the other half is being sent, we fill the other half @@ -2613,35 +3726,36 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, #if 1 /* using fifo not empty */ /* TX_FIFO_NOT_EMPTY */ - while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) { + while (FLD_GET(dsi_read_reg(dsidev, DSI_VC_CTRL(0)), 5, 5)) { fifo_stalls++; if (fifo_stalls > 0xfffff) { DSSERR("fifo stalls overflow, pixels left %d\n", pixels_left); - dsi_if_enable(0); + dsi_if_enable(dsidev, 0); return -EIO; } udelay(1); } #elif 1 /* using fifo emptiness */ - while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 < + while ((REG_GET(dsidev, DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 < max_dsi_packet_size) { fifo_stalls++; if (fifo_stalls > 0xfffff) { DSSERR("fifo stalls overflow, pixels left %d\n", pixels_left); - dsi_if_enable(0); + dsi_if_enable(dsidev, 0); return -EIO; } } #else - while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 == 0) { + while ((REG_GET(dsidev, DSI_TX_FIFO_VC_EMPTINESS, + 7, 0) + 1) * 4 == 0) { fifo_stalls++; if (fifo_stalls > 0xfffff) { DSSERR("fifo stalls overflow, pixels left %d\n", pixels_left); - dsi_if_enable(0); + dsi_if_enable(dsidev, 0); return -EIO; } } @@ -2650,17 +3764,17 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, pixels_left -= pixels; - dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE, + dsi_vc_write_long_header(dsidev, 0, DSI_DT_DCS_LONG_WRITE, 1 + pixels * bytespp, 0); - DSI_PUSH(0, dcs_cmd); + DSI_PUSH(dsidev, 0, dcs_cmd); while (pixels-- > 0) { u32 pix = __raw_readl(data++); - DSI_PUSH(0, (pix >> 16) & 0xff); - DSI_PUSH(0, (pix >> 8) & 0xff); - DSI_PUSH(0, (pix >> 0) & 0xff); + DSI_PUSH(dsidev, 0, (pix >> 16) & 0xff); + DSI_PUSH(dsidev, 0, (pix >> 8) & 0xff); + DSI_PUSH(dsidev, 0, (pix >> 0) & 0xff); current_x++; if (current_x == x+w) { @@ -2669,7 +3783,7 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, } } - DSI_FLUSH(0); + DSI_FLUSH(dsidev, 0); } return 0; @@ -2678,6 +3792,8 @@ static int dsi_update_screen_l4(struct omap_dss_device *dssdev, static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, u16 x, u16 y, u16 w, u16 h) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); unsigned bytespp; unsigned bytespl; unsigned bytespf; @@ -2686,16 +3802,13 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, unsigned packet_len; u32 l; int r; - const unsigned channel = dsi.update_channel; - /* line buffer is 1024 x 24bits */ - /* XXX: for some reason using full buffer size causes considerable TX - * slowdown with update sizes that fill the whole buffer */ - const unsigned line_buf_size = 1023 * 3; + const unsigned channel = dsi->update_channel; + const unsigned line_buf_size = dsi_get_line_buf_size(dsidev); DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n", x, y, w, h); - dsi_vc_config_vp(channel); + dsi_vc_config_vp(dsidev, channel); bytespp = dssdev->ctrl.pixel_size / 8; bytespl = w * bytespp; @@ -2716,15 +3829,16 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, total_len += (bytespf % packet_payload) + 1; l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ - dsi_write_reg(DSI_VC_TE(channel), l); + dsi_write_reg(dsidev, DSI_VC_TE(channel), l); - dsi_vc_write_long_header(channel, DSI_DT_DCS_LONG_WRITE, packet_len, 0); + dsi_vc_write_long_header(dsidev, channel, DSI_DT_DCS_LONG_WRITE, + packet_len, 0); - if (dsi.te_enabled) + if (dsi->te_enabled) l = FLD_MOD(l, 1, 30, 30); /* TE_EN */ else l = FLD_MOD(l, 1, 31, 31); /* TE_START */ - dsi_write_reg(DSI_VC_TE(channel), l); + dsi_write_reg(dsidev, DSI_VC_TE(channel), l); /* We put SIDLEMODE to no-idle for the duration of the transfer, * because DSS interrupts are not capable of waking up the CPU and the @@ -2734,23 +3848,23 @@ static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, */ dispc_disable_sidle(); - dsi_perf_mark_start(); + dsi_perf_mark_start(dsidev); - r = queue_delayed_work(dsi.workqueue, &dsi.framedone_timeout_work, - msecs_to_jiffies(250)); + r = schedule_delayed_work(&dsi->framedone_timeout_work, + msecs_to_jiffies(250)); BUG_ON(r == 0); dss_start_update(dssdev); - if (dsi.te_enabled) { + if (dsi->te_enabled) { /* disable LP_RX_TO, so that we can receive TE. Time to wait * for TE is longer than the timer allows */ - REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ + REG_FLD_MOD(dsidev, DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ - dsi_vc_send_bta(channel); + dsi_vc_send_bta(dsidev, channel); #ifdef DSI_CATCH_MISSING_TE - mod_timer(&dsi.te_timer, jiffies + msecs_to_jiffies(250)); + mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250)); #endif } } @@ -2762,40 +3876,28 @@ static void dsi_te_timeout(unsigned long arg) } #endif -static void dsi_handle_framedone(int error) +static void dsi_handle_framedone(struct platform_device *dsidev, int error) { - const int channel = dsi.update_channel; - - cancel_delayed_work(&dsi.framedone_timeout_work); - - dsi_vc_disable_bta_irq(channel); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); /* SIDLEMODE back to smart-idle */ dispc_enable_sidle(); - dsi.bta_callback = NULL; - - if (dsi.te_enabled) { + if (dsi->te_enabled) { /* enable LP_RX_TO again after the TE */ - REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ - } - - /* RX_FIFO_NOT_EMPTY */ - if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { - DSSERR("Received error during frame transfer:\n"); - dsi_vc_flush_receive_data(channel); - if (!error) - error = -EIO; + REG_FLD_MOD(dsidev, DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ } - dsi.framedone_callback(error, dsi.framedone_data); + dsi->framedone_callback(error, dsi->framedone_data); if (!error) - dsi_perf_show("DISPC"); + dsi_perf_show(dsidev, "DISPC"); } static void dsi_framedone_timeout_work_callback(struct work_struct *work) { + struct dsi_data *dsi = container_of(work, struct dsi_data, + framedone_timeout_work.work); /* XXX While extremely unlikely, we could get FRAMEDONE interrupt after * 250ms which would conflict with this timeout work. What should be * done is first cancel the transfer on the HW, and then cancel the @@ -2805,66 +3907,34 @@ static void dsi_framedone_timeout_work_callback(struct work_struct *work) DSSERR("Framedone not received for 250ms!\n"); - dsi_handle_framedone(-ETIMEDOUT); -} - -static void dsi_framedone_bta_callback(void) -{ - dsi_handle_framedone(0); - -#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC - dispc_fake_vsync_irq(); -#endif + dsi_handle_framedone(dsi->pdev, -ETIMEDOUT); } static void dsi_framedone_irq_callback(void *data, u32 mask) { - const int channel = dsi.update_channel; - int r; + struct omap_dss_device *dssdev = (struct omap_dss_device *) data; + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); /* Note: We get FRAMEDONE when DISPC has finished sending pixels and * turns itself off. However, DSI still has the pixels in its buffers, * and is sending the data. */ - if (dsi.te_enabled) { - /* enable LP_RX_TO again after the TE */ - REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ - } - - /* Send BTA after the frame. We need this for the TE to work, as TE - * trigger is only sent for BTAs without preceding packet. Thus we need - * to BTA after the pixel packets so that next BTA will cause TE - * trigger. - * - * This is not needed when TE is not in use, but we do it anyway to - * make sure that the transfer has been completed. It would be more - * optimal, but more complex, to wait only just before starting next - * transfer. - * - * Also, as there's no interrupt telling when the transfer has been - * done and the channel could be reconfigured, the only way is to - * busyloop until TE_SIZE is zero. With BTA we can do this - * asynchronously. - * */ - - dsi.bta_callback = dsi_framedone_bta_callback; + __cancel_delayed_work(&dsi->framedone_timeout_work); - barrier(); + dsi_handle_framedone(dsidev, 0); - dsi_vc_enable_bta_irq(channel); - - r = dsi_vc_send_bta(channel); - if (r) { - DSSERR("BTA after framedone failed\n"); - dsi_handle_framedone(-EIO); - } +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC + dispc_fake_vsync_irq(); +#endif } int omap_dsi_prepare_update(struct omap_dss_device *dssdev, u16 *x, u16 *y, u16 *w, u16 *h, bool enlarge_update_area) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); u16 dw, dh; dssdev->driver->get_resolution(dssdev, &dw, &dh); @@ -2884,7 +3954,7 @@ int omap_dsi_prepare_update(struct omap_dss_device *dssdev, if (*w == 0 || *h == 0) return -EINVAL; - dsi_perf_mark_setup(); + dsi_perf_mark_setup(dsidev); if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { dss_setup_partial_planes(dssdev, x, y, w, h, @@ -2901,7 +3971,10 @@ int omap_dsi_update(struct omap_dss_device *dssdev, u16 x, u16 y, u16 w, u16 h, void (*callback)(int, void *), void *data) { - dsi.update_channel = channel; + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi->update_channel = channel; /* OMAP DSS cannot send updates of odd widths. * omap_dsi_prepare_update() makes the widths even, but add a BUG_ON @@ -2910,14 +3983,14 @@ int omap_dsi_update(struct omap_dss_device *dssdev, BUG_ON(x % 2 == 1); if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { - dsi.framedone_callback = callback; - dsi.framedone_data = data; + dsi->framedone_callback = callback; + dsi->framedone_data = data; - dsi.update_region.x = x; - dsi.update_region.y = y; - dsi.update_region.w = w; - dsi.update_region.h = h; - dsi.update_region.device = dssdev; + dsi->update_region.x = x; + dsi->update_region.y = y; + dsi->update_region.w = w; + dsi->update_region.h = h; + dsi->update_region.device = dssdev; dsi_update_screen_dispc(dssdev, x, y, w, h); } else { @@ -2927,7 +4000,7 @@ int omap_dsi_update(struct omap_dss_device *dssdev, if (r) return r; - dsi_perf_show("L4"); + dsi_perf_show(dsidev, "L4"); callback(0, data); } @@ -2940,9 +4013,13 @@ EXPORT_SYMBOL(omap_dsi_update); static int dsi_display_init_dispc(struct omap_dss_device *dssdev) { int r; + u32 irq; + + irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ? + DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2; - r = omap_dispc_register_isr(dsi_framedone_irq_callback, NULL, - DISPC_IRQ_FRAMEDONE); + r = omap_dispc_register_isr(dsi_framedone_irq_callback, (void *) dssdev, + irq); if (r) { DSSERR("can't get FRAMEDONE irq\n"); return r; @@ -2975,28 +4052,34 @@ static int dsi_display_init_dispc(struct omap_dss_device *dssdev) static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev) { - omap_dispc_unregister_isr(dsi_framedone_irq_callback, NULL, - DISPC_IRQ_FRAMEDONE); + u32 irq; + + irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ? + DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2; + + omap_dispc_unregister_isr(dsi_framedone_irq_callback, (void *) dssdev, + irq); } static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_clock_info cinfo; int r; - /* we always use DSS2_FCK as input clock */ - cinfo.use_dss2_fck = true; - cinfo.regn = dssdev->phy.dsi.div.regn; - cinfo.regm = dssdev->phy.dsi.div.regm; - cinfo.regm3 = dssdev->phy.dsi.div.regm3; - cinfo.regm4 = dssdev->phy.dsi.div.regm4; + /* we always use DSS_CLK_SYSCK as input clock */ + cinfo.use_sys_clk = true; + cinfo.regn = dssdev->clocks.dsi.regn; + cinfo.regm = dssdev->clocks.dsi.regm; + cinfo.regm_dispc = dssdev->clocks.dsi.regm_dispc; + cinfo.regm_dsi = dssdev->clocks.dsi.regm_dsi; r = dsi_calc_clock_rates(dssdev, &cinfo); if (r) { DSSERR("Failed to calc dsi clocks\n"); return r; } - r = dsi_pll_set_clock_div(&cinfo); + r = dsi_pll_set_clock_div(dsidev, &cinfo); if (r) { DSSERR("Failed to set dsi clocks\n"); return r; @@ -3007,14 +4090,15 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev) static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dispc_clock_info dispc_cinfo; int r; unsigned long long fck; - fck = dsi_get_dsi1_pll_rate(); + fck = dsi_get_pll_hsdiv_dispc_rate(dsidev); - dispc_cinfo.lck_div = dssdev->phy.dsi.div.lck_div; - dispc_cinfo.pck_div = dssdev->phy.dsi.div.pck_div; + dispc_cinfo.lck_div = dssdev->clocks.dispc.channel.lck_div; + dispc_cinfo.pck_div = dssdev->clocks.dispc.channel.pck_div; r = dispc_calc_clock_rates(fck, &dispc_cinfo); if (r) { @@ -3033,11 +4117,11 @@ static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev) static int dsi_display_init_dsi(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + int dsi_module = dsi_get_dsidev_id(dsidev); int r; - _dsi_print_reset_status(); - - r = dsi_pll_init(dssdev, true, true); + r = dsi_pll_init(dsidev, true, true); if (r) goto err0; @@ -3045,8 +4129,10 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) if (r) goto err1; - dss_select_dispc_clk_source(DSS_SRC_DSI1_PLL_FCLK); - dss_select_dsi_clk_source(DSS_SRC_DSI2_PLL_FCLK); + dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); + dss_select_dsi_clk_source(dsi_module, dssdev->clocks.dsi.dsi_fclk_src); + dss_select_lcd_clk_source(dssdev->manager->id, + dssdev->clocks.dispc.channel.lcd_clk_src); DSSDBG("PLL OK\n"); @@ -3054,256 +4140,522 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev) if (r) goto err2; - r = dsi_complexio_init(dssdev); + r = dsi_cio_init(dssdev); if (r) goto err2; - _dsi_print_reset_status(); + _dsi_print_reset_status(dsidev); dsi_proto_timings(dssdev); dsi_set_lp_clk_divisor(dssdev); if (1) - _dsi_print_reset_status(); + _dsi_print_reset_status(dsidev); r = dsi_proto_config(dssdev); if (r) goto err3; /* enable interface */ - dsi_vc_enable(0, 1); - dsi_vc_enable(1, 1); - dsi_vc_enable(2, 1); - dsi_vc_enable(3, 1); - dsi_if_enable(1); - dsi_force_tx_stop_mode_io(); + dsi_vc_enable(dsidev, 0, 1); + dsi_vc_enable(dsidev, 1, 1); + dsi_vc_enable(dsidev, 2, 1); + dsi_vc_enable(dsidev, 3, 1); + dsi_if_enable(dsidev, 1); + dsi_force_tx_stop_mode_io(dsidev); return 0; err3: - dsi_complexio_uninit(); + dsi_cio_uninit(dsidev); err2: - dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK); - dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK); + dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); err1: - dsi_pll_uninit(); + dsi_pll_uninit(dsidev, true); err0: return r; } -static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev) -{ - /* disable interface */ - dsi_if_enable(0); - dsi_vc_enable(0, 0); - dsi_vc_enable(1, 0); - dsi_vc_enable(2, 0); - dsi_vc_enable(3, 0); - - dss_select_dispc_clk_source(DSS_SRC_DSS1_ALWON_FCLK); - dss_select_dsi_clk_source(DSS_SRC_DSS1_ALWON_FCLK); - dsi_complexio_uninit(); - dsi_pll_uninit(); -} - -static int dsi_core_init(void) +static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev, + bool disconnect_lanes, bool enter_ulps) { - /* Autoidle */ - REG_FLD_MOD(DSI_SYSCONFIG, 1, 0, 0); + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int dsi_module = dsi_get_dsidev_id(dsidev); - /* ENWAKEUP */ - REG_FLD_MOD(DSI_SYSCONFIG, 1, 2, 2); + if (enter_ulps && !dsi->ulps_enabled) + dsi_enter_ulps(dsidev); - /* SIDLEMODE smart-idle */ - REG_FLD_MOD(DSI_SYSCONFIG, 2, 4, 3); - - _dsi_initialize_irq(); - - return 0; + /* disable interface */ + dsi_if_enable(dsidev, 0); + dsi_vc_enable(dsidev, 0, 0); + dsi_vc_enable(dsidev, 1, 0); + dsi_vc_enable(dsidev, 2, 0); + dsi_vc_enable(dsidev, 3, 0); + + dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK); + dss_select_dsi_clk_source(dsi_module, OMAP_DSS_CLK_SRC_FCK); + dsi_cio_uninit(dsidev); + dsi_pll_uninit(dsidev, disconnect_lanes); } int omapdss_dsi_display_enable(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r = 0; DSSDBG("dsi_display_enable\n"); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - mutex_lock(&dsi.lock); + mutex_lock(&dsi->lock); r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); - goto err0; + goto err_start_dev; } - enable_clocks(1); - dsi_enable_pll_clock(1); - - r = _dsi_reset(); + r = dsi_runtime_get(dsidev); if (r) - goto err1; + goto err_get_dsi; + + dsi_enable_pll_clock(dsidev, 1); - dsi_core_init(); + _dsi_initialize_irq(dsidev); r = dsi_display_init_dispc(dssdev); if (r) - goto err1; + goto err_init_dispc; r = dsi_display_init_dsi(dssdev); if (r) - goto err2; + goto err_init_dsi; - mutex_unlock(&dsi.lock); + mutex_unlock(&dsi->lock); return 0; -err2: +err_init_dsi: dsi_display_uninit_dispc(dssdev); -err1: - enable_clocks(0); - dsi_enable_pll_clock(0); +err_init_dispc: + dsi_enable_pll_clock(dsidev, 0); + dsi_runtime_put(dsidev); +err_get_dsi: omap_dss_stop_device(dssdev); -err0: - mutex_unlock(&dsi.lock); +err_start_dev: + mutex_unlock(&dsi->lock); DSSDBG("dsi_display_enable FAILED\n"); return r; } EXPORT_SYMBOL(omapdss_dsi_display_enable); -void omapdss_dsi_display_disable(struct omap_dss_device *dssdev) +void omapdss_dsi_display_disable(struct omap_dss_device *dssdev, + bool disconnect_lanes, bool enter_ulps) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + DSSDBG("dsi_display_disable\n"); - WARN_ON(!dsi_bus_is_locked()); + WARN_ON(!dsi_bus_is_locked(dsidev)); - mutex_lock(&dsi.lock); + mutex_lock(&dsi->lock); + + dsi_sync_vc(dsidev, 0); + dsi_sync_vc(dsidev, 1); + dsi_sync_vc(dsidev, 2); + dsi_sync_vc(dsidev, 3); dsi_display_uninit_dispc(dssdev); - dsi_display_uninit_dsi(dssdev); + dsi_display_uninit_dsi(dssdev, disconnect_lanes, enter_ulps); - enable_clocks(0); - dsi_enable_pll_clock(0); + dsi_runtime_put(dsidev); + dsi_enable_pll_clock(dsidev, 0); omap_dss_stop_device(dssdev); - mutex_unlock(&dsi.lock); + mutex_unlock(&dsi->lock); } EXPORT_SYMBOL(omapdss_dsi_display_disable); int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable) { - dsi.te_enabled = enable; + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi->te_enabled = enable; return 0; } EXPORT_SYMBOL(omapdss_dsi_enable_te); void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, enum omap_burst_size *burst_size, + u32 fifo_size, u32 burst_size, u32 *fifo_low, u32 *fifo_high) { - unsigned burst_size_bytes; - - *burst_size = OMAP_DSS_BURST_16x32; - burst_size_bytes = 16 * 32 / 8; - - *fifo_high = fifo_size - burst_size_bytes; - *fifo_low = fifo_size - burst_size_bytes * 2; + *fifo_high = fifo_size - burst_size; + *fifo_low = fifo_size - burst_size * 2; } int dsi_init_display(struct omap_dss_device *dssdev) { + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int dsi_module = dsi_get_dsidev_id(dsidev); + DSSDBG("DSI init\n"); /* XXX these should be figured out dynamically */ dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; - dsi.vc[0].dssdev = dssdev; - dsi.vc[1].dssdev = dssdev; + if (dsi->vdds_dsi_reg == NULL) { + struct regulator *vdds_dsi; + + vdds_dsi = regulator_get(&dsi->pdev->dev, "vdds_dsi"); + + if (IS_ERR(vdds_dsi)) { + DSSERR("can't get VDDS_DSI regulator\n"); + return PTR_ERR(vdds_dsi); + } + + dsi->vdds_dsi_reg = vdds_dsi; + } + + if (dsi_get_num_data_lanes_dssdev(dssdev) > dsi->num_data_lanes) { + DSSERR("DSI%d can't support more than %d data lanes\n", + dsi_module + 1, dsi->num_data_lanes); + return -EINVAL; + } + + return 0; +} + +int omap_dsi_request_vc(struct omap_dss_device *dssdev, int *channel) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + int i; + + for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { + if (!dsi->vc[i].dssdev) { + dsi->vc[i].dssdev = dssdev; + *channel = i; + return 0; + } + } + + DSSERR("cannot get VC for display %s", dssdev->name); + return -ENOSPC; +} +EXPORT_SYMBOL(omap_dsi_request_vc); + +int omap_dsi_set_vc_id(struct omap_dss_device *dssdev, int channel, int vc_id) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (vc_id < 0 || vc_id > 3) { + DSSERR("VC ID out of range\n"); + return -EINVAL; + } + + if (channel < 0 || channel > 3) { + DSSERR("Virtual Channel out of range\n"); + return -EINVAL; + } + + if (dsi->vc[channel].dssdev != dssdev) { + DSSERR("Virtual Channel not allocated to display %s\n", + dssdev->name); + return -EINVAL; + } + + dsi->vc[channel].vc_id = vc_id; return 0; } +EXPORT_SYMBOL(omap_dsi_set_vc_id); + +void omap_dsi_release_vc(struct omap_dss_device *dssdev, int channel) +{ + struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if ((channel >= 0 && channel <= 3) && + dsi->vc[channel].dssdev == dssdev) { + dsi->vc[channel].dssdev = NULL; + dsi->vc[channel].vc_id = 0; + } +} +EXPORT_SYMBOL(omap_dsi_release_vc); -void dsi_wait_dsi1_pll_active(void) +void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev) { - if (wait_for_bit_change(DSI_PLL_STATUS, 7, 1) != 1) - DSSERR("DSI1 PLL clock not active\n"); + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 7, 1) != 1) + DSSERR("%s (%s) not active\n", + dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC), + dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC)); +} + +void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev) +{ + if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 8, 1) != 1) + DSSERR("%s (%s) not active\n", + dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI), + dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI)); +} + +static void dsi_calc_clock_param_ranges(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + dsi->regn_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGN); + dsi->regm_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM); + dsi->regm_dispc_max = + dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DISPC); + dsi->regm_dsi_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_REGM_DSI); + dsi->fint_min = dss_feat_get_param_min(FEAT_PARAM_DSIPLL_FINT); + dsi->fint_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_FINT); + dsi->lpdiv_max = dss_feat_get_param_max(FEAT_PARAM_DSIPLL_LPDIV); +} + +static int dsi_get_clocks(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + struct clk *clk; + + clk = clk_get(&dsidev->dev, "fck"); + if (IS_ERR(clk)) { + DSSERR("can't get fck\n"); + return PTR_ERR(clk); + } + + dsi->dss_clk = clk; + + if (cpu_is_omap34xx() || cpu_is_omap3630()) + clk = clk_get(&dsidev->dev, "dss2_alwon_fck"); + else + clk = clk_get(&dsidev->dev, "sys_clk"); + if (IS_ERR(clk)) { + DSSERR("can't get sys_clk\n"); + clk_put(dsi->dss_clk); + dsi->dss_clk = NULL; + return PTR_ERR(clk); + } + + dsi->sys_clk = clk; + + return 0; } -void dsi_wait_dsi2_pll_active(void) +static void dsi_put_clocks(struct platform_device *dsidev) { - if (wait_for_bit_change(DSI_PLL_STATUS, 8, 1) != 1) - DSSERR("DSI2 PLL clock not active\n"); + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + if (dsi->dss_clk) + clk_put(dsi->dss_clk); + if (dsi->sys_clk) + clk_put(dsi->sys_clk); } -int dsi_init(struct platform_device *pdev) +/* DSI1 HW IP initialisation */ +static int omap_dsi1hw_probe(struct platform_device *dsidev) { + struct omap_display_platform_data *dss_plat_data; + struct omap_dss_board_info *board_info; u32 rev; - int r; + int r, i, dsi_module = dsi_get_dsidev_id(dsidev); + struct resource *dsi_mem; + struct dsi_data *dsi; + + dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); + if (!dsi) { + r = -ENOMEM; + goto err_alloc; + } + + dsi->pdev = dsidev; + dsi_pdev_map[dsi_module] = dsidev; + dev_set_drvdata(&dsidev->dev, dsi); + + dss_plat_data = dsidev->dev.platform_data; + board_info = dss_plat_data->board_data; + dsi->dsi_mux_pads = board_info->dsi_mux_pads; - spin_lock_init(&dsi.errors_lock); - dsi.errors = 0; + spin_lock_init(&dsi->irq_lock); + spin_lock_init(&dsi->errors_lock); + dsi->errors = 0; #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spin_lock_init(&dsi.irq_stats_lock); - dsi.irq_stats.last_reset = jiffies; + spin_lock_init(&dsi->irq_stats_lock); + dsi->irq_stats.last_reset = jiffies; #endif - init_completion(&dsi.bta_completion); + mutex_init(&dsi->lock); + sema_init(&dsi->bus_lock, 1); - mutex_init(&dsi.lock); - sema_init(&dsi.bus_lock, 1); + r = dsi_get_clocks(dsidev); + if (r) + goto err_get_clk; - dsi.workqueue = create_singlethread_workqueue("dsi"); - if (dsi.workqueue == NULL) - return -ENOMEM; + pm_runtime_enable(&dsidev->dev); - INIT_DELAYED_WORK_DEFERRABLE(&dsi.framedone_timeout_work, + INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work, dsi_framedone_timeout_work_callback); #ifdef DSI_CATCH_MISSING_TE - init_timer(&dsi.te_timer); - dsi.te_timer.function = dsi_te_timeout; - dsi.te_timer.data = 0; + init_timer(&dsi->te_timer); + dsi->te_timer.function = dsi_te_timeout; + dsi->te_timer.data = 0; #endif - dsi.base = ioremap(DSI_BASE, DSI_SZ_REGS); - if (!dsi.base) { + dsi_mem = platform_get_resource(dsi->pdev, IORESOURCE_MEM, 0); + if (!dsi_mem) { + DSSERR("can't get IORESOURCE_MEM DSI\n"); + r = -EINVAL; + goto err_ioremap; + } + dsi->base = ioremap(dsi_mem->start, resource_size(dsi_mem)); + if (!dsi->base) { DSSERR("can't ioremap DSI\n"); r = -ENOMEM; - goto err1; + goto err_ioremap; + } + dsi->irq = platform_get_irq(dsi->pdev, 0); + if (dsi->irq < 0) { + DSSERR("platform_get_irq failed\n"); + r = -ENODEV; + goto err_get_irq; } - dsi.vdds_dsi_reg = dss_get_vdds_dsi(); - if (IS_ERR(dsi.vdds_dsi_reg)) { - DSSERR("can't get VDDS_DSI regulator\n"); - r = PTR_ERR(dsi.vdds_dsi_reg); - goto err2; + r = request_irq(dsi->irq, omap_dsi_irq_handler, IRQF_SHARED, + dev_name(&dsidev->dev), dsi->pdev); + if (r < 0) { + DSSERR("request_irq failed\n"); + goto err_get_irq; + } + + /* DSI VCs initialization */ + for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { + dsi->vc[i].mode = DSI_VC_MODE_L4; + dsi->vc[i].dssdev = NULL; + dsi->vc[i].vc_id = 0; } - enable_clocks(1); + dsi_calc_clock_param_ranges(dsidev); - rev = dsi_read_reg(DSI_REVISION); - printk(KERN_INFO "OMAP DSI rev %d.%d\n", + r = dsi_runtime_get(dsidev); + if (r) + goto err_get_dsi; + + rev = dsi_read_reg(dsidev, DSI_REVISION); + dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); - enable_clocks(0); + dsi->num_data_lanes = dsi_get_num_data_lanes(dsidev); + + dsi_runtime_put(dsidev); return 0; -err2: - iounmap(dsi.base); -err1: - destroy_workqueue(dsi.workqueue); + +err_get_dsi: + free_irq(dsi->irq, dsi->pdev); +err_get_irq: + iounmap(dsi->base); +err_ioremap: + pm_runtime_disable(&dsidev->dev); +err_get_clk: + kfree(dsi); +err_alloc: return r; } -void dsi_exit(void) +static int omap_dsi1hw_remove(struct platform_device *dsidev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); + + WARN_ON(dsi->scp_clk_refcount > 0); + + pm_runtime_disable(&dsidev->dev); + + dsi_put_clocks(dsidev); + + if (dsi->vdds_dsi_reg != NULL) { + if (dsi->vdds_dsi_enabled) { + regulator_disable(dsi->vdds_dsi_reg); + dsi->vdds_dsi_enabled = false; + } + + regulator_put(dsi->vdds_dsi_reg); + dsi->vdds_dsi_reg = NULL; + } + + free_irq(dsi->irq, dsi->pdev); + iounmap(dsi->base); + + kfree(dsi); + + return 0; +} + +static int dsi_runtime_suspend(struct device *dev) +{ + struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev)); + + clk_disable(dsi->dss_clk); + + dispc_runtime_put(); + dss_runtime_put(); + + return 0; +} + +static int dsi_runtime_resume(struct device *dev) { - iounmap(dsi.base); + struct dsi_data *dsi = dsi_get_dsidrv_data(to_platform_device(dev)); + int r; + + r = dss_runtime_get(); + if (r) + goto err_get_dss; + + r = dispc_runtime_get(); + if (r) + goto err_get_dispc; + + clk_enable(dsi->dss_clk); - destroy_workqueue(dsi.workqueue); + return 0; - DSSDBG("omap_dsi_exit\n"); +err_get_dispc: + dss_runtime_put(); +err_get_dss: + return r; } +static const struct dev_pm_ops dsi_pm_ops = { + .runtime_suspend = dsi_runtime_suspend, + .runtime_resume = dsi_runtime_resume, +}; + +static struct platform_driver omap_dsi1hw_driver = { + .probe = omap_dsi1hw_probe, + .remove = omap_dsi1hw_remove, + .driver = { + .name = "omapdss_dsi1", + .owner = THIS_MODULE, + .pm = &dsi_pm_ops, + }, +}; + +int dsi_init_platform_driver(void) +{ + return platform_driver_register(&omap_dsi1hw_driver); +} + +void dsi_uninit_platform_driver(void) +{ + return platform_driver_unregister(&omap_dsi1hw_driver); +} diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c index 77c3621c917..0f9c3a6457a 100644 --- a/drivers/video/omap2/dss/dss.c +++ b/drivers/video/omap2/dss/dss.c @@ -26,14 +26,15 @@ #include <linux/io.h> #include <linux/err.h> #include <linux/delay.h> -#include <linux/interrupt.h> #include <linux/seq_file.h> #include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> -#include <plat/display.h> +#include <video/omapdss.h> +#include <plat/clock.h> #include "dss.h" - -#define DSS_BASE 0x48050000 +#include "dss_features.h" #define DSS_SZ_REGS SZ_512 @@ -46,7 +47,6 @@ struct dss_reg { #define DSS_REVISION DSS_REG(0x0000) #define DSS_SYSCONFIG DSS_REG(0x0010) #define DSS_SYSSTATUS DSS_REG(0x0014) -#define DSS_IRQSTATUS DSS_REG(0x0018) #define DSS_CONTROL DSS_REG(0x0040) #define DSS_SDI_CONTROL DSS_REG(0x0044) #define DSS_PLL_CONTROL DSS_REG(0x0048) @@ -59,22 +59,30 @@ struct dss_reg { dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) static struct { + struct platform_device *pdev; void __iomem *base; struct clk *dpll4_m4_ck; + struct clk *dss_clk; unsigned long cache_req_pck; unsigned long cache_prate; struct dss_clock_info cache_dss_cinfo; struct dispc_clock_info cache_dispc_cinfo; - enum dss_clk_source dsi_clk_source; - enum dss_clk_source dispc_clk_source; + enum omap_dss_clk_source dsi_clk_source[MAX_NUM_DSI]; + enum omap_dss_clk_source dispc_clk_source; + enum omap_dss_clk_source lcd_clk_source[MAX_DSS_LCD_MANAGERS]; + bool ctx_valid; u32 ctx[DSS_SZ_REGS / sizeof(u32)]; } dss; -static int _omap_dss_wait_reset(void); +static const char * const dss_generic_clk_source_names[] = { + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI_PLL_HSDIV_DISPC", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI_PLL_HSDIV_DSI", + [OMAP_DSS_CLK_SRC_FCK] = "DSS_FCK", +}; static inline void dss_write_reg(const struct dss_reg idx, u32 val) { @@ -91,32 +99,39 @@ static inline u32 dss_read_reg(const struct dss_reg idx) #define RR(reg) \ dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) -void dss_save_context(void) +static void dss_save_context(void) { - if (cpu_is_omap24xx()) - return; + DSSDBG("dss_save_context\n"); - SR(SYSCONFIG); SR(CONTROL); -#ifdef CONFIG_OMAP2_DSS_SDI - SR(SDI_CONTROL); - SR(PLL_CONTROL); -#endif + if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & + OMAP_DISPLAY_TYPE_SDI) { + SR(SDI_CONTROL); + SR(PLL_CONTROL); + } + + dss.ctx_valid = true; + + DSSDBG("context saved\n"); } -void dss_restore_context(void) +static void dss_restore_context(void) { - if (_omap_dss_wait_reset()) - DSSERR("DSS not coming out of reset after sleep\n"); + DSSDBG("dss_restore_context\n"); + + if (!dss.ctx_valid) + return; - RR(SYSCONFIG); RR(CONTROL); -#ifdef CONFIG_OMAP2_DSS_SDI - RR(SDI_CONTROL); - RR(PLL_CONTROL); -#endif + if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & + OMAP_DISPLAY_TYPE_SDI) { + RR(SDI_CONTROL); + RR(PLL_CONTROL); + } + + DSSDBG("context restored\n"); } #undef SR @@ -209,125 +224,238 @@ void dss_sdi_disable(void) REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ } +const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src) +{ + return dss_generic_clk_source_names[clk_src]; +} + + void dss_dump_clocks(struct seq_file *s) { unsigned long dpll4_ck_rate; unsigned long dpll4_m4_ck_rate; + const char *fclk_name, *fclk_real_name; + unsigned long fclk_rate; - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - - dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); - dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck); + if (dss_runtime_get()) + return; seq_printf(s, "- DSS -\n"); - seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate); + fclk_name = dss_get_generic_clk_source_name(OMAP_DSS_CLK_SRC_FCK); + fclk_real_name = dss_feat_get_clk_source_name(OMAP_DSS_CLK_SRC_FCK); + fclk_rate = clk_get_rate(dss.dss_clk); - if (cpu_is_omap3630()) - seq_printf(s, "dss1_alwon_fclk = %lu / %lu = %lu\n", - dpll4_ck_rate, - dpll4_ck_rate / dpll4_m4_ck_rate, - dss_clk_get_rate(DSS_CLK_FCK1)); - else - seq_printf(s, "dss1_alwon_fclk = %lu / %lu * 2 = %lu\n", - dpll4_ck_rate, - dpll4_ck_rate / dpll4_m4_ck_rate, - dss_clk_get_rate(DSS_CLK_FCK1)); + if (dss.dpll4_m4_ck) { + dpll4_ck_rate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + dpll4_m4_ck_rate = clk_get_rate(dss.dpll4_m4_ck); + + seq_printf(s, "dpll4_ck %lu\n", dpll4_ck_rate); + + if (cpu_is_omap3630() || cpu_is_omap44xx()) + seq_printf(s, "%s (%s) = %lu / %lu = %lu\n", + fclk_name, fclk_real_name, + dpll4_ck_rate, + dpll4_ck_rate / dpll4_m4_ck_rate, + fclk_rate); + else + seq_printf(s, "%s (%s) = %lu / %lu * 2 = %lu\n", + fclk_name, fclk_real_name, + dpll4_ck_rate, + dpll4_ck_rate / dpll4_m4_ck_rate, + fclk_rate); + } else { + seq_printf(s, "%s (%s) = %lu\n", + fclk_name, fclk_real_name, + fclk_rate); + } - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + dss_runtime_put(); } void dss_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + if (dss_runtime_get()) + return; DUMPREG(DSS_REVISION); DUMPREG(DSS_SYSCONFIG); DUMPREG(DSS_SYSSTATUS); - DUMPREG(DSS_IRQSTATUS); DUMPREG(DSS_CONTROL); - DUMPREG(DSS_SDI_CONTROL); - DUMPREG(DSS_PLL_CONTROL); - DUMPREG(DSS_SDI_STATUS); - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + if (dss_feat_get_supported_displays(OMAP_DSS_CHANNEL_LCD) & + OMAP_DISPLAY_TYPE_SDI) { + DUMPREG(DSS_SDI_CONTROL); + DUMPREG(DSS_PLL_CONTROL); + DUMPREG(DSS_SDI_STATUS); + } + + dss_runtime_put(); #undef DUMPREG } -void dss_select_dispc_clk_source(enum dss_clk_source clk_src) +void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src) { + struct platform_device *dsidev; int b; + u8 start, end; + + switch (clk_src) { + case OMAP_DSS_CLK_SRC_FCK: + b = 0; + break; + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: + b = 1; + dsidev = dsi_get_dsidev_from_id(0); + dsi_wait_pll_hsdiv_dispc_active(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: + b = 2; + dsidev = dsi_get_dsidev_from_id(1); + dsi_wait_pll_hsdiv_dispc_active(dsidev); + break; + default: + BUG(); + } - BUG_ON(clk_src != DSS_SRC_DSI1_PLL_FCLK && - clk_src != DSS_SRC_DSS1_ALWON_FCLK); - - b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1; - - if (clk_src == DSS_SRC_DSI1_PLL_FCLK) - dsi_wait_dsi1_pll_active(); + dss_feat_get_reg_field(FEAT_REG_DISPC_CLK_SWITCH, &start, &end); - REG_FLD_MOD(DSS_CONTROL, b, 0, 0); /* DISPC_CLK_SWITCH */ + REG_FLD_MOD(DSS_CONTROL, b, start, end); /* DISPC_CLK_SWITCH */ dss.dispc_clk_source = clk_src; } -void dss_select_dsi_clk_source(enum dss_clk_source clk_src) +void dss_select_dsi_clk_source(int dsi_module, + enum omap_dss_clk_source clk_src) { + struct platform_device *dsidev; int b; - BUG_ON(clk_src != DSS_SRC_DSI2_PLL_FCLK && - clk_src != DSS_SRC_DSS1_ALWON_FCLK); + switch (clk_src) { + case OMAP_DSS_CLK_SRC_FCK: + b = 0; + break; + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI: + BUG_ON(dsi_module != 0); + b = 1; + dsidev = dsi_get_dsidev_from_id(0); + dsi_wait_pll_hsdiv_dsi_active(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI: + BUG_ON(dsi_module != 1); + b = 1; + dsidev = dsi_get_dsidev_from_id(1); + dsi_wait_pll_hsdiv_dsi_active(dsidev); + break; + default: + BUG(); + } - b = clk_src == DSS_SRC_DSS1_ALWON_FCLK ? 0 : 1; + REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ - if (clk_src == DSS_SRC_DSI2_PLL_FCLK) - dsi_wait_dsi2_pll_active(); + dss.dsi_clk_source[dsi_module] = clk_src; +} - REG_FLD_MOD(DSS_CONTROL, b, 1, 1); /* DSI_CLK_SWITCH */ +void dss_select_lcd_clk_source(enum omap_channel channel, + enum omap_dss_clk_source clk_src) +{ + struct platform_device *dsidev; + int b, ix, pos; + + if (!dss_has_feature(FEAT_LCD_CLK_SRC)) + return; + + switch (clk_src) { + case OMAP_DSS_CLK_SRC_FCK: + b = 0; + break; + case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: + BUG_ON(channel != OMAP_DSS_CHANNEL_LCD); + b = 1; + dsidev = dsi_get_dsidev_from_id(0); + dsi_wait_pll_hsdiv_dispc_active(dsidev); + break; + case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: + BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2); + b = 1; + dsidev = dsi_get_dsidev_from_id(1); + dsi_wait_pll_hsdiv_dispc_active(dsidev); + break; + default: + BUG(); + } - dss.dsi_clk_source = clk_src; + pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12; + REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* LCDx_CLK_SWITCH */ + + ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1; + dss.lcd_clk_source[ix] = clk_src; } -enum dss_clk_source dss_get_dispc_clk_source(void) +enum omap_dss_clk_source dss_get_dispc_clk_source(void) { return dss.dispc_clk_source; } -enum dss_clk_source dss_get_dsi_clk_source(void) +enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module) +{ + return dss.dsi_clk_source[dsi_module]; +} + +enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel) { - return dss.dsi_clk_source; + if (dss_has_feature(FEAT_LCD_CLK_SRC)) { + int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1; + return dss.lcd_clk_source[ix]; + } else { + /* LCD_CLK source is the same as DISPC_FCLK source for + * OMAP2 and OMAP3 */ + return dss.dispc_clk_source; + } } /* calculate clock rates using dividers in cinfo */ int dss_calc_clock_rates(struct dss_clock_info *cinfo) { - unsigned long prate; + if (dss.dpll4_m4_ck) { + unsigned long prate; + u16 fck_div_max = 16; - if (cinfo->fck_div > (cpu_is_omap3630() ? 32 : 16) || - cinfo->fck_div == 0) - return -EINVAL; + if (cpu_is_omap3630() || cpu_is_omap44xx()) + fck_div_max = 32; - prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + if (cinfo->fck_div > fck_div_max || cinfo->fck_div == 0) + return -EINVAL; - cinfo->fck = prate / cinfo->fck_div; + prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); + + cinfo->fck = prate / cinfo->fck_div; + } else { + if (cinfo->fck_div != 0) + return -EINVAL; + cinfo->fck = clk_get_rate(dss.dss_clk); + } return 0; } int dss_set_clock_div(struct dss_clock_info *cinfo) { - unsigned long prate; - int r; + if (dss.dpll4_m4_ck) { + unsigned long prate; + int r; - if (cpu_is_omap34xx()) { prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); DSSDBG("dpll4_m4 = %ld\n", prate); r = clk_set_rate(dss.dpll4_m4_ck, prate / cinfo->fck_div); if (r) return r; + } else { + if (cinfo->fck_div != 0) + return -EINVAL; } DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div); @@ -337,12 +465,14 @@ int dss_set_clock_div(struct dss_clock_info *cinfo) int dss_get_clock_div(struct dss_clock_info *cinfo) { - cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK1); + cinfo->fck = clk_get_rate(dss.dss_clk); - if (cpu_is_omap34xx()) { + if (dss.dpll4_m4_ck) { unsigned long prate; + prate = clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); - if (cpu_is_omap3630()) + + if (cpu_is_omap3630() || cpu_is_omap44xx()) cinfo->fck_div = prate / (cinfo->fck); else cinfo->fck_div = prate / (cinfo->fck / 2); @@ -355,7 +485,7 @@ int dss_get_clock_div(struct dss_clock_info *cinfo) unsigned long dss_get_dpll4_rate(void) { - if (cpu_is_omap34xx()) + if (dss.dpll4_m4_ck) return clk_get_rate(clk_get_parent(dss.dpll4_m4_ck)); else return 0; @@ -369,16 +499,18 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck, struct dss_clock_info best_dss; struct dispc_clock_info best_dispc; - unsigned long fck; + unsigned long fck, max_dss_fck; - u16 fck_div; + u16 fck_div, fck_div_max = 16; int match = 0; int min_fck_per_pck; prate = dss_get_dpll4_rate(); - fck = dss_clk_get_rate(DSS_CLK_FCK1); + max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK); + + fck = clk_get_rate(dss.dss_clk); if (req_pck == dss.cache_req_pck && ((cpu_is_omap34xx() && prate == dss.cache_prate) || dss.cache_dss_cinfo.fck == fck)) { @@ -391,7 +523,7 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck, min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; if (min_fck_per_pck && - req_pck * min_fck_per_pck > DISPC_MAX_FCK) { + req_pck * min_fck_per_pck > max_dss_fck) { DSSERR("Requested pixel clock not possible with the current " "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " "the constraint off.\n"); @@ -402,10 +534,10 @@ retry: memset(&best_dss, 0, sizeof(best_dss)); memset(&best_dispc, 0, sizeof(best_dispc)); - if (cpu_is_omap24xx()) { + if (dss.dpll4_m4_ck == NULL) { struct dispc_clock_info cur_dispc; /* XXX can we change the clock on omap2? */ - fck = dss_clk_get_rate(DSS_CLK_FCK1); + fck = clk_get_rate(dss.dss_clk); fck_div = 1; dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc); @@ -417,17 +549,19 @@ retry: best_dispc = cur_dispc; goto found; - } else if (cpu_is_omap34xx()) { - for (fck_div = (cpu_is_omap3630() ? 32 : 16); - fck_div > 0; --fck_div) { + } else { + if (cpu_is_omap3630() || cpu_is_omap44xx()) + fck_div_max = 32; + + for (fck_div = fck_div_max; fck_div > 0; --fck_div) { struct dispc_clock_info cur_dispc; - if (cpu_is_omap3630()) + if (fck_div_max == 32) fck = prate / fck_div; else fck = prate / fck_div * 2; - if (fck > DISPC_MAX_FCK) + if (fck > max_dss_fck) continue; if (min_fck_per_pck && @@ -450,8 +584,6 @@ retry: goto found; } } - } else { - BUG(); } found: @@ -482,104 +614,152 @@ found: return 0; } - - -static irqreturn_t dss_irq_handler_omap2(int irq, void *arg) +void dss_set_venc_output(enum omap_dss_venc_type type) { - dispc_irq_handler(); + int l = 0; - return IRQ_HANDLED; + if (type == OMAP_DSS_VENC_TYPE_COMPOSITE) + l = 0; + else if (type == OMAP_DSS_VENC_TYPE_SVIDEO) + l = 1; + else + BUG(); + + /* venc out selection. 0 = comp, 1 = svideo */ + REG_FLD_MOD(DSS_CONTROL, l, 6, 6); } -static irqreturn_t dss_irq_handler_omap3(int irq, void *arg) +void dss_set_dac_pwrdn_bgz(bool enable) { - u32 irqstatus; - - irqstatus = dss_read_reg(DSS_IRQSTATUS); - - if (irqstatus & (1<<0)) /* DISPC_IRQ */ - dispc_irq_handler(); -#ifdef CONFIG_OMAP2_DSS_DSI - if (irqstatus & (1<<1)) /* DSI_IRQ */ - dsi_irq_handler(); -#endif + REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ +} - return IRQ_HANDLED; +void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select hdmi) +{ + REG_FLD_MOD(DSS_CONTROL, hdmi, 15, 15); /* VENC_HDMI_SWITCH */ } -static int _omap_dss_wait_reset(void) +static int dss_get_clocks(void) { - int t = 0; + struct clk *clk; + int r; - while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) { - if (++t > 1000) { - DSSERR("soft reset failed\n"); - return -ENODEV; + clk = clk_get(&dss.pdev->dev, "fck"); + if (IS_ERR(clk)) { + DSSERR("can't get clock fck\n"); + r = PTR_ERR(clk); + goto err; + } + + dss.dss_clk = clk; + + if (cpu_is_omap34xx()) { + clk = clk_get(NULL, "dpll4_m4_ck"); + if (IS_ERR(clk)) { + DSSERR("Failed to get dpll4_m4_ck\n"); + r = PTR_ERR(clk); + goto err; } - udelay(1); + } else if (cpu_is_omap44xx()) { + clk = clk_get(NULL, "dpll_per_m5x2_ck"); + if (IS_ERR(clk)) { + DSSERR("Failed to get dpll_per_m5x2_ck\n"); + r = PTR_ERR(clk); + goto err; + } + } else { /* omap24xx */ + clk = NULL; } + dss.dpll4_m4_ck = clk; + return 0; + +err: + if (dss.dss_clk) + clk_put(dss.dss_clk); + if (dss.dpll4_m4_ck) + clk_put(dss.dpll4_m4_ck); + + return r; } -static int _omap_dss_reset(void) +static void dss_put_clocks(void) { - /* Soft reset */ - REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1); - return _omap_dss_wait_reset(); + if (dss.dpll4_m4_ck) + clk_put(dss.dpll4_m4_ck); + clk_put(dss.dss_clk); } -void dss_set_venc_output(enum omap_dss_venc_type type) +struct clk *dss_get_ick(void) { - int l = 0; + return clk_get(&dss.pdev->dev, "ick"); +} - if (type == OMAP_DSS_VENC_TYPE_COMPOSITE) - l = 0; - else if (type == OMAP_DSS_VENC_TYPE_SVIDEO) - l = 1; - else - BUG(); +int dss_runtime_get(void) +{ + int r; - /* venc out selection. 0 = comp, 1 = svideo */ - REG_FLD_MOD(DSS_CONTROL, l, 6, 6); + DSSDBG("dss_runtime_get\n"); + + r = pm_runtime_get_sync(&dss.pdev->dev); + WARN_ON(r < 0); + return r < 0 ? r : 0; } -void dss_set_dac_pwrdn_bgz(bool enable) +void dss_runtime_put(void) { - REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ + int r; + + DSSDBG("dss_runtime_put\n"); + + r = pm_runtime_put(&dss.pdev->dev); + WARN_ON(r < 0); } -int dss_init(bool skip_init) +/* DEBUGFS */ +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) +void dss_debug_dump_clocks(struct seq_file *s) { - int r; + 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) +{ + struct resource *dss_mem; u32 rev; + int r; - dss.base = ioremap(DSS_BASE, DSS_SZ_REGS); + dss.pdev = pdev; + + dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); + if (!dss_mem) { + DSSERR("can't get IORESOURCE_MEM DSS\n"); + r = -EINVAL; + goto err_ioremap; + } + dss.base = ioremap(dss_mem->start, resource_size(dss_mem)); if (!dss.base) { DSSERR("can't ioremap DSS\n"); r = -ENOMEM; - goto fail0; + goto err_ioremap; } - if (!skip_init) { - /* disable LCD and DIGIT output. This seems to fix the synclost - * problem that we get, if the bootloader starts the DSS and - * the kernel resets it */ - omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440); - - /* We need to wait here a bit, otherwise we sometimes start to - * get synclost errors, and after that only power cycle will - * restore DSS functionality. I have no idea why this happens. - * And we have to wait _before_ resetting the DSS, but after - * enabling clocks. - */ - msleep(50); - - _omap_dss_reset(); - } + r = dss_get_clocks(); + if (r) + goto err_clocks; - /* autoidle */ - REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0); + pm_runtime_enable(&pdev->dev); + + r = dss_runtime_get(); + if (r) + goto err_runtime_get; /* Select DPLL */ REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); @@ -589,53 +769,93 @@ int dss_init(bool skip_init) REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ #endif - - r = request_irq(INT_24XX_DSS_IRQ, - cpu_is_omap24xx() - ? dss_irq_handler_omap2 - : dss_irq_handler_omap3, - 0, "OMAP DSS", NULL); - - if (r < 0) { - DSSERR("omap2 dss: request_irq failed\n"); - goto fail1; + dss.dsi_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; + dss.dsi_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; + dss.dispc_clk_source = OMAP_DSS_CLK_SRC_FCK; + dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK; + dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK; + + r = dpi_init(); + if (r) { + DSSERR("Failed to initialize DPI\n"); + goto err_dpi; } - if (cpu_is_omap34xx()) { - dss.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck"); - if (IS_ERR(dss.dpll4_m4_ck)) { - DSSERR("Failed to get dpll4_m4_ck\n"); - r = PTR_ERR(dss.dpll4_m4_ck); - goto fail2; - } + r = sdi_init(); + if (r) { + DSSERR("Failed to initialize SDI\n"); + goto err_sdi; } - dss.dsi_clk_source = DSS_SRC_DSS1_ALWON_FCLK; - dss.dispc_clk_source = DSS_SRC_DSS1_ALWON_FCLK; - - dss_save_context(); - rev = dss_read_reg(DSS_REVISION); printk(KERN_INFO "OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); - return 0; + dss_runtime_put(); -fail2: - free_irq(INT_24XX_DSS_IRQ, NULL); -fail1: + return 0; +err_sdi: + dpi_exit(); +err_dpi: + dss_runtime_put(); +err_runtime_get: + pm_runtime_disable(&pdev->dev); + dss_put_clocks(); +err_clocks: iounmap(dss.base); -fail0: +err_ioremap: return r; } -void dss_exit(void) +static int omap_dsshw_remove(struct platform_device *pdev) { - if (cpu_is_omap34xx()) - clk_put(dss.dpll4_m4_ck); - - free_irq(INT_24XX_DSS_IRQ, NULL); + dpi_exit(); + sdi_exit(); iounmap(dss.base); + + pm_runtime_disable(&pdev->dev); + + dss_put_clocks(); + + return 0; +} + +static int dss_runtime_suspend(struct device *dev) +{ + dss_save_context(); + clk_disable(dss.dss_clk); + return 0; +} + +static int dss_runtime_resume(struct device *dev) +{ + clk_enable(dss.dss_clk); + dss_restore_context(); + return 0; +} + +static const struct dev_pm_ops dss_pm_ops = { + .runtime_suspend = dss_runtime_suspend, + .runtime_resume = dss_runtime_resume, +}; + +static struct platform_driver omap_dsshw_driver = { + .probe = omap_dsshw_probe, + .remove = omap_dsshw_remove, + .driver = { + .name = "omapdss_dss", + .owner = THIS_MODULE, + .pm = &dss_pm_ops, + }, +}; + +int dss_init_platform_driver(void) +{ + return platform_driver_register(&omap_dsshw_driver); } +void dss_uninit_platform_driver(void) +{ + return platform_driver_unregister(&omap_dsshw_driver); +} diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index b394951120a..9c94b1152c2 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h @@ -97,32 +97,15 @@ extern unsigned int dss_debug; #define FLD_MOD(orig, val, start, end) \ (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) -#define DISPC_MAX_FCK 173000000 - -enum omap_burst_size { - OMAP_DSS_BURST_4x32 = 0, - OMAP_DSS_BURST_8x32 = 1, - OMAP_DSS_BURST_16x32 = 2, -}; - enum omap_parallel_interface_mode { OMAP_DSS_PARALLELMODE_BYPASS, /* MIPI DPI */ OMAP_DSS_PARALLELMODE_RFBI, /* MIPI DBI */ OMAP_DSS_PARALLELMODE_DSI, }; -enum dss_clock { - DSS_CLK_ICK = 1 << 0, - DSS_CLK_FCK1 = 1 << 1, - DSS_CLK_FCK2 = 1 << 2, - DSS_CLK_54M = 1 << 3, - DSS_CLK_96M = 1 << 4, -}; - -enum dss_clk_source { - DSS_SRC_DSI1_PLL_FCLK, - DSS_SRC_DSI2_PLL_FCLK, - DSS_SRC_DSS1_ALWON_FCLK, +enum dss_hdmi_venc_clk_source_select { + DSS_VENC_TV_CLK = 0, + DSS_HDMI_M_PCLK = 1, }; struct dss_clock_info { @@ -148,36 +131,42 @@ struct dsi_clock_info { unsigned long fint; unsigned long clkin4ddr; unsigned long clkin; - unsigned long dsi1_pll_fclk; - unsigned long dsi2_pll_fclk; - + unsigned long dsi_pll_hsdiv_dispc_clk; /* OMAP3: DSI1_PLL_CLK + * OMAP4: PLLx_CLK1 */ + unsigned long dsi_pll_hsdiv_dsi_clk; /* OMAP3: DSI2_PLL_CLK + * OMAP4: PLLx_CLK2 */ unsigned long lp_clk; /* dividers */ u16 regn; u16 regm; - u16 regm3; - u16 regm4; - + u16 regm_dispc; /* OMAP3: REGM3 + * OMAP4: REGM4 */ + u16 regm_dsi; /* OMAP3: REGM4 + * OMAP4: REGM5 */ u16 lp_clk_div; u8 highfreq; - bool use_dss2_fck; + bool use_sys_clk; +}; + +/* HDMI PLL structure */ +struct hdmi_pll_info { + u16 regn; + u16 regm; + u32 regmf; + u16 regm2; + u16 regsd; + u16 dcofreq; }; struct seq_file; struct platform_device; /* core */ -void dss_clk_enable(enum dss_clock clks); -void dss_clk_disable(enum dss_clock clks); -unsigned long dss_clk_get_rate(enum dss_clock clk); -int dss_need_ctx_restore(void); -void dss_dump_clocks(struct seq_file *s); struct bus_type *dss_get_bus(void); struct regulator *dss_get_vdds_dsi(void); struct regulator *dss_get_vdds_sdi(void); -struct regulator *dss_get_vdda_dac(void); /* display */ int dss_suspend_all_devices(void); @@ -191,7 +180,7 @@ void dss_uninit_device(struct platform_device *pdev, bool dss_use_replication(struct omap_dss_device *dssdev, enum omap_color_mode mode); void default_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, enum omap_burst_size *burst_size, + u32 fifo_size, u32 burst_size, u32 *fifo_low, u32 *fifo_high); /* manager */ @@ -214,22 +203,35 @@ void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr); void dss_recheck_connections(struct omap_dss_device *dssdev, bool force); /* DSS */ -int dss_init(bool skip_init); -void dss_exit(void); +int dss_init_platform_driver(void); +void dss_uninit_platform_driver(void); + +int dss_runtime_get(void); +void dss_runtime_put(void); -void dss_save_context(void); -void dss_restore_context(void); +struct clk *dss_get_ick(void); + +void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); +const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src); +void dss_dump_clocks(struct seq_file *s); void dss_dump_regs(struct seq_file *s); +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) +void dss_debug_dump_clocks(struct seq_file *s); +#endif void dss_sdi_init(u8 datapairs); int dss_sdi_enable(void); void dss_sdi_disable(void); -void dss_select_dispc_clk_source(enum dss_clk_source clk_src); -void dss_select_dsi_clk_source(enum dss_clk_source clk_src); -enum dss_clk_source dss_get_dispc_clk_source(void); -enum dss_clk_source dss_get_dsi_clk_source(void); +void dss_select_dispc_clk_source(enum omap_dss_clk_source clk_src); +void dss_select_dsi_clk_source(int dsi_module, + enum omap_dss_clk_source clk_src); +void dss_select_lcd_clk_source(enum omap_channel channel, + enum omap_dss_clk_source clk_src); +enum omap_dss_clk_source dss_get_dispc_clk_source(void); +enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module); +enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel); void dss_set_venc_output(enum omap_dss_venc_type type); void dss_set_dac_pwrdn_bgz(bool enable); @@ -244,11 +246,11 @@ int dss_calc_clock_div(bool is_tft, unsigned long req_pck, /* SDI */ #ifdef CONFIG_OMAP2_DSS_SDI -int sdi_init(bool skip_init); +int sdi_init(void); void sdi_exit(void); int sdi_init_display(struct omap_dss_device *display); #else -static inline int sdi_init(bool skip_init) +static inline int sdi_init(void) { return 0; } @@ -259,54 +261,104 @@ static inline void sdi_exit(void) /* DSI */ #ifdef CONFIG_OMAP2_DSS_DSI -int dsi_init(struct platform_device *pdev); -void dsi_exit(void); -void dsi_dump_clocks(struct seq_file *s); -void dsi_dump_irqs(struct seq_file *s); -void dsi_dump_regs(struct seq_file *s); +struct dentry; +struct file_operations; + +int dsi_init_platform_driver(void); +void dsi_uninit_platform_driver(void); + +int dsi_runtime_get(struct platform_device *dsidev); +void dsi_runtime_put(struct platform_device *dsidev); -void dsi_save_context(void); -void dsi_restore_context(void); +void dsi_dump_clocks(struct seq_file *s); +void dsi_create_debugfs_files_irq(struct dentry *debugfs_dir, + const struct file_operations *debug_fops); +void dsi_create_debugfs_files_reg(struct dentry *debugfs_dir, + const struct file_operations *debug_fops); int dsi_init_display(struct omap_dss_device *display); void dsi_irq_handler(void); -unsigned long dsi_get_dsi1_pll_rate(void); -int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo); -int dsi_pll_calc_clock_div_pck(bool is_tft, unsigned long req_pck, - struct dsi_clock_info *cinfo, +unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev); +int dsi_pll_set_clock_div(struct platform_device *dsidev, + struct dsi_clock_info *cinfo); +int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft, + unsigned long req_pck, struct dsi_clock_info *cinfo, struct dispc_clock_info *dispc_cinfo); -int dsi_pll_init(struct omap_dss_device *dssdev, bool enable_hsclk, +int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk, bool enable_hsdiv); -void dsi_pll_uninit(void); +void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes); void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, - u32 fifo_size, enum omap_burst_size *burst_size, + u32 fifo_size, u32 burst_size, u32 *fifo_low, u32 *fifo_high); -void dsi_wait_dsi1_pll_active(void); -void dsi_wait_dsi2_pll_active(void); +void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev); +void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev); +struct platform_device *dsi_get_dsidev_from_id(int module); #else -static inline int dsi_init(struct platform_device *pdev) +static inline int dsi_init_platform_driver(void) +{ + return 0; +} +static inline void dsi_uninit_platform_driver(void) +{ +} +static inline int dsi_runtime_get(struct platform_device *dsidev) { return 0; } -static inline void dsi_exit(void) +static inline void dsi_runtime_put(struct platform_device *dsidev) { } -static inline void dsi_wait_dsi1_pll_active(void) +static inline unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev) +{ + WARN("%s: DSI not compiled in, returning rate as 0\n", __func__); + return 0; +} +static inline int dsi_pll_set_clock_div(struct platform_device *dsidev, + struct dsi_clock_info *cinfo) +{ + WARN("%s: DSI not compiled in\n", __func__); + return -ENODEV; +} +static inline int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, + bool is_tft, unsigned long req_pck, + struct dsi_clock_info *dsi_cinfo, + struct dispc_clock_info *dispc_cinfo) +{ + WARN("%s: DSI not compiled in\n", __func__); + return -ENODEV; +} +static inline int dsi_pll_init(struct platform_device *dsidev, + bool enable_hsclk, bool enable_hsdiv) +{ + WARN("%s: DSI not compiled in\n", __func__); + return -ENODEV; +} +static inline void dsi_pll_uninit(struct platform_device *dsidev, + bool disconnect_lanes) +{ +} +static inline void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev) +{ +} +static inline void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev) { } -static inline void dsi_wait_dsi2_pll_active(void) +static inline struct platform_device *dsi_get_dsidev_from_id(int module) { + WARN("%s: DSI not compiled in, returning platform device as NULL\n", + __func__); + return NULL; } #endif /* DPI */ #ifdef CONFIG_OMAP2_DSS_DPI -int dpi_init(struct platform_device *pdev); +int dpi_init(void); void dpi_exit(void); int dpi_init_display(struct omap_dss_device *dssdev); #else -static inline int dpi_init(struct platform_device *pdev) +static inline int dpi_init(void) { return 0; } @@ -316,16 +368,16 @@ static inline void dpi_exit(void) #endif /* DISPC */ -int dispc_init(void); -void dispc_exit(void); +int dispc_init_platform_driver(void); +void dispc_uninit_platform_driver(void); void dispc_dump_clocks(struct seq_file *s); void dispc_dump_irqs(struct seq_file *s); void dispc_dump_regs(struct seq_file *s); void dispc_irq_handler(void); void dispc_fake_vsync_irq(void); -void dispc_save_context(void); -void dispc_restore_context(void); +int dispc_runtime_get(void); +void dispc_runtime_put(void); void dispc_enable_sidle(void); void dispc_disable_sidle(void); @@ -338,10 +390,12 @@ void dispc_enable_fifohandcheck(enum omap_channel channel, bool enable); void dispc_set_lcd_size(enum omap_channel channel, u16 width, u16 height); void dispc_set_digit_size(u16 width, u16 height); u32 dispc_get_plane_fifo_size(enum omap_plane plane); -void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high); +void dispc_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high); void dispc_enable_fifomerge(bool enable); -void dispc_set_burst_size(enum omap_plane plane, - enum omap_burst_size burst_size); +u32 dispc_get_burst_size(enum omap_plane plane); +void dispc_enable_cpr(enum omap_channel channel, bool enable); +void dispc_set_cpr_coef(enum omap_channel channel, + struct omap_dss_cpr_coefs *coefs); void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr); void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr); @@ -350,6 +404,7 @@ void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height); void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out); +void dispc_enable_gamma_table(bool enable); int dispc_setup_plane(enum omap_plane plane, u32 paddr, u16 screen_width, u16 pos_x, u16 pos_y, @@ -360,7 +415,8 @@ int dispc_setup_plane(enum omap_plane plane, enum omap_dss_rotation_type rotation_type, u8 rotation, bool mirror, u8 global_alpha, u8 pre_mult_alpha, - enum omap_channel channel); + enum omap_channel channel, + u32 puv_addr); bool dispc_go_busy(enum omap_channel channel); void dispc_go(enum omap_channel channel); @@ -409,39 +465,58 @@ int dispc_get_clock_div(enum omap_channel channel, /* VENC */ #ifdef CONFIG_OMAP2_DSS_VENC -int venc_init(struct platform_device *pdev); -void venc_exit(void); +int venc_init_platform_driver(void); +void venc_uninit_platform_driver(void); void venc_dump_regs(struct seq_file *s); int venc_init_display(struct omap_dss_device *display); #else -static inline int venc_init(struct platform_device *pdev) +static inline int venc_init_platform_driver(void) { return 0; } -static inline void venc_exit(void) +static inline void venc_uninit_platform_driver(void) { } #endif +/* HDMI */ +#ifdef CONFIG_OMAP4_DSS_HDMI +int hdmi_init_platform_driver(void); +void hdmi_uninit_platform_driver(void); +int hdmi_init_display(struct omap_dss_device *dssdev); +#else +static inline int hdmi_init_display(struct omap_dss_device *dssdev) +{ + return 0; +} +static inline int hdmi_init_platform_driver(void) +{ + return 0; +} +static inline void hdmi_uninit_platform_driver(void) +{ +} +#endif +int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev); +void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev); +void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev); +int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev, + struct omap_video_timings *timings); +int hdmi_panel_init(void); +void hdmi_panel_exit(void); + /* RFBI */ #ifdef CONFIG_OMAP2_DSS_RFBI -int rfbi_init(void); -void rfbi_exit(void); +int rfbi_init_platform_driver(void); +void rfbi_uninit_platform_driver(void); void rfbi_dump_regs(struct seq_file *s); - -int rfbi_configure(int rfbi_module, int bpp, int lines); -void rfbi_enable_rfbi(bool enable); -void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, - u16 height, void (callback)(void *data), void *data); -void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t); -unsigned long rfbi_get_max_tx_rate(void); int rfbi_init_display(struct omap_dss_device *display); #else -static inline int rfbi_init(void) +static inline int rfbi_init_platform_driver(void) { return 0; } -static inline void rfbi_exit(void) +static inline void rfbi_uninit_platform_driver(void) { } #endif diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c index cf3ef696e14..b415c4ee621 100644 --- a/drivers/video/omap2/dss/dss_features.c +++ b/drivers/video/omap2/dss/dss_features.c @@ -22,17 +22,21 @@ #include <linux/err.h> #include <linux/slab.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> +#include "dss.h" #include "dss_features.h" /* Defines a generic omap register field */ struct dss_reg_field { - enum dss_feat_reg_field id; u8 start, end; }; +struct dss_param_range { + int min, max; +}; + struct omap_dss_features { const struct dss_reg_field *reg_fields; const int num_reg_fields; @@ -43,29 +47,71 @@ struct omap_dss_features { const int num_ovls; const enum omap_display_type *supported_displays; const enum omap_color_mode *supported_color_modes; + const char * const *clksrc_names; + const struct dss_param_range *dss_params; + + const u32 buffer_size_unit; + const u32 burst_size_unit; }; /* This struct is assigned to one of the below during initialization */ -static struct omap_dss_features *omap_current_dss_features; +static const struct omap_dss_features *omap_current_dss_features; static const struct dss_reg_field omap2_dss_reg_fields[] = { - { FEAT_REG_FIRHINC, 11, 0 }, - { FEAT_REG_FIRVINC, 27, 16 }, - { FEAT_REG_FIFOLOWTHRESHOLD, 8, 0 }, - { FEAT_REG_FIFOHIGHTHRESHOLD, 24, 16 }, - { FEAT_REG_FIFOSIZE, 8, 0 }, + [FEAT_REG_FIRHINC] = { 11, 0 }, + [FEAT_REG_FIRVINC] = { 27, 16 }, + [FEAT_REG_FIFOLOWTHRESHOLD] = { 8, 0 }, + [FEAT_REG_FIFOHIGHTHRESHOLD] = { 24, 16 }, + [FEAT_REG_FIFOSIZE] = { 8, 0 }, + [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, + [FEAT_REG_VERTICALACCU] = { 25, 16 }, + [FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 }, + [FEAT_REG_DSIPLL_REGN] = { 0, 0 }, + [FEAT_REG_DSIPLL_REGM] = { 0, 0 }, + [FEAT_REG_DSIPLL_REGM_DISPC] = { 0, 0 }, + [FEAT_REG_DSIPLL_REGM_DSI] = { 0, 0 }, }; static const struct dss_reg_field omap3_dss_reg_fields[] = { - { FEAT_REG_FIRHINC, 12, 0 }, - { FEAT_REG_FIRVINC, 28, 16 }, - { FEAT_REG_FIFOLOWTHRESHOLD, 11, 0 }, - { FEAT_REG_FIFOHIGHTHRESHOLD, 27, 16 }, - { FEAT_REG_FIFOSIZE, 10, 0 }, + [FEAT_REG_FIRHINC] = { 12, 0 }, + [FEAT_REG_FIRVINC] = { 28, 16 }, + [FEAT_REG_FIFOLOWTHRESHOLD] = { 11, 0 }, + [FEAT_REG_FIFOHIGHTHRESHOLD] = { 27, 16 }, + [FEAT_REG_FIFOSIZE] = { 10, 0 }, + [FEAT_REG_HORIZONTALACCU] = { 9, 0 }, + [FEAT_REG_VERTICALACCU] = { 25, 16 }, + [FEAT_REG_DISPC_CLK_SWITCH] = { 0, 0 }, + [FEAT_REG_DSIPLL_REGN] = { 7, 1 }, + [FEAT_REG_DSIPLL_REGM] = { 18, 8 }, + [FEAT_REG_DSIPLL_REGM_DISPC] = { 22, 19 }, + [FEAT_REG_DSIPLL_REGM_DSI] = { 26, 23 }, +}; + +static const struct dss_reg_field omap4_dss_reg_fields[] = { + [FEAT_REG_FIRHINC] = { 12, 0 }, + [FEAT_REG_FIRVINC] = { 28, 16 }, + [FEAT_REG_FIFOLOWTHRESHOLD] = { 15, 0 }, + [FEAT_REG_FIFOHIGHTHRESHOLD] = { 31, 16 }, + [FEAT_REG_FIFOSIZE] = { 15, 0 }, + [FEAT_REG_HORIZONTALACCU] = { 10, 0 }, + [FEAT_REG_VERTICALACCU] = { 26, 16 }, + [FEAT_REG_DISPC_CLK_SWITCH] = { 9, 8 }, + [FEAT_REG_DSIPLL_REGN] = { 8, 1 }, + [FEAT_REG_DSIPLL_REGM] = { 20, 9 }, + [FEAT_REG_DSIPLL_REGM_DISPC] = { 25, 21 }, + [FEAT_REG_DSIPLL_REGM_DSI] = { 30, 26 }, }; static const enum omap_display_type omap2_dss_supported_displays[] = { /* OMAP_DSS_CHANNEL_LCD */ + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI, + + /* OMAP_DSS_CHANNEL_DIGIT */ + OMAP_DISPLAY_TYPE_VENC, +}; + +static const enum omap_display_type omap3430_dss_supported_displays[] = { + /* OMAP_DSS_CHANNEL_LCD */ OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI, @@ -73,10 +119,10 @@ static const enum omap_display_type omap2_dss_supported_displays[] = { OMAP_DISPLAY_TYPE_VENC, }; -static const enum omap_display_type omap3_dss_supported_displays[] = { +static const enum omap_display_type omap3630_dss_supported_displays[] = { /* OMAP_DSS_CHANNEL_LCD */ OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | - OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI, + OMAP_DISPLAY_TYPE_DSI, /* OMAP_DSS_CHANNEL_DIGIT */ OMAP_DISPLAY_TYPE_VENC, @@ -87,7 +133,7 @@ static const enum omap_display_type omap4_dss_supported_displays[] = { OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI, /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DISPLAY_TYPE_VENC, + OMAP_DISPLAY_TYPE_VENC | OMAP_DISPLAY_TYPE_HDMI, /* OMAP_DSS_CHANNEL_LCD2 */ OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | @@ -134,65 +180,200 @@ static const enum omap_color_mode omap3_dss_supported_color_modes[] = { OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, }; +static const enum omap_color_mode omap4_dss_supported_color_modes[] = { + /* OMAP_DSS_GFX */ + OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | + OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 | + OMAP_DSS_COLOR_ARGB16_1555, + + /* OMAP_DSS_VIDEO1 */ + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 | + OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY | + OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | + OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | + OMAP_DSS_COLOR_RGBX32, + + /* OMAP_DSS_VIDEO2 */ + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 | + OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY | + OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | + OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | + OMAP_DSS_COLOR_RGBX32, +}; + +static const char * const omap2_dss_clk_source_names[] = { + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "N/A", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "N/A", + [OMAP_DSS_CLK_SRC_FCK] = "DSS_FCLK1", +}; + +static const char * const omap3_dss_clk_source_names[] = { + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "DSI1_PLL_FCLK", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "DSI2_PLL_FCLK", + [OMAP_DSS_CLK_SRC_FCK] = "DSS1_ALWON_FCLK", +}; + +static const char * const omap4_dss_clk_source_names[] = { + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC] = "PLL1_CLK1", + [OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI] = "PLL1_CLK2", + [OMAP_DSS_CLK_SRC_FCK] = "DSS_FCLK", + [OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC] = "PLL2_CLK1", + [OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DSI] = "PLL2_CLK2", +}; + +static const struct dss_param_range omap2_dss_param_range[] = { + [FEAT_PARAM_DSS_FCK] = { 0, 173000000 }, + [FEAT_PARAM_DSIPLL_REGN] = { 0, 0 }, + [FEAT_PARAM_DSIPLL_REGM] = { 0, 0 }, + [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, 0 }, + [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, 0 }, + [FEAT_PARAM_DSIPLL_FINT] = { 0, 0 }, + [FEAT_PARAM_DSIPLL_LPDIV] = { 0, 0 }, +}; + +static const struct dss_param_range omap3_dss_param_range[] = { + [FEAT_PARAM_DSS_FCK] = { 0, 173000000 }, + [FEAT_PARAM_DSIPLL_REGN] = { 0, (1 << 7) - 1 }, + [FEAT_PARAM_DSIPLL_REGM] = { 0, (1 << 11) - 1 }, + [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, (1 << 4) - 1 }, + [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, (1 << 4) - 1 }, + [FEAT_PARAM_DSIPLL_FINT] = { 750000, 2100000 }, + [FEAT_PARAM_DSIPLL_LPDIV] = { 1, (1 << 13) - 1}, +}; + +static const struct dss_param_range omap4_dss_param_range[] = { + [FEAT_PARAM_DSS_FCK] = { 0, 186000000 }, + [FEAT_PARAM_DSIPLL_REGN] = { 0, (1 << 8) - 1 }, + [FEAT_PARAM_DSIPLL_REGM] = { 0, (1 << 12) - 1 }, + [FEAT_PARAM_DSIPLL_REGM_DISPC] = { 0, (1 << 5) - 1 }, + [FEAT_PARAM_DSIPLL_REGM_DSI] = { 0, (1 << 5) - 1 }, + [FEAT_PARAM_DSIPLL_FINT] = { 500000, 2500000 }, + [FEAT_PARAM_DSIPLL_LPDIV] = { 0, (1 << 13) - 1 }, +}; + /* OMAP2 DSS Features */ -static struct omap_dss_features omap2_dss_features = { +static const struct omap_dss_features omap2_dss_features = { .reg_fields = omap2_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields), .has_feature = FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | - FEAT_PCKFREEENABLE | FEAT_FUNCGATED, + FEAT_PCKFREEENABLE | FEAT_FUNCGATED | + FEAT_ROWREPEATENABLE | FEAT_RESIZECONF, .num_mgrs = 2, .num_ovls = 3, .supported_displays = omap2_dss_supported_displays, .supported_color_modes = omap2_dss_supported_color_modes, + .clksrc_names = omap2_dss_clk_source_names, + .dss_params = omap2_dss_param_range, + .buffer_size_unit = 1, + .burst_size_unit = 8, }; /* OMAP3 DSS Features */ -static struct omap_dss_features omap3430_dss_features = { +static const struct omap_dss_features omap3430_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), .has_feature = FEAT_GLOBAL_ALPHA | FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | - FEAT_FUNCGATED, + FEAT_FUNCGATED | FEAT_ROWREPEATENABLE | + FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF | + FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC | + FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD | + FEAT_FIR_COEF_V, .num_mgrs = 2, .num_ovls = 3, - .supported_displays = omap3_dss_supported_displays, + .supported_displays = omap3430_dss_supported_displays, .supported_color_modes = omap3_dss_supported_color_modes, + .clksrc_names = omap3_dss_clk_source_names, + .dss_params = omap3_dss_param_range, + .buffer_size_unit = 1, + .burst_size_unit = 8, }; -static struct omap_dss_features omap3630_dss_features = { +static const struct omap_dss_features omap3630_dss_features = { .reg_fields = omap3_dss_reg_fields, .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), .has_feature = FEAT_GLOBAL_ALPHA | FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE | - FEAT_PRE_MULT_ALPHA | FEAT_FUNCGATED, + FEAT_PRE_MULT_ALPHA | FEAT_FUNCGATED | + FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT | + FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG | + FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD | + FEAT_FIR_COEF_V, .num_mgrs = 2, .num_ovls = 3, - .supported_displays = omap3_dss_supported_displays, + .supported_displays = omap3630_dss_supported_displays, .supported_color_modes = omap3_dss_supported_color_modes, + .clksrc_names = omap3_dss_clk_source_names, + .dss_params = omap3_dss_param_range, + .buffer_size_unit = 1, + .burst_size_unit = 8, }; /* OMAP4 DSS Features */ -static struct omap_dss_features omap4_dss_features = { - .reg_fields = omap3_dss_reg_fields, - .num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields), +/* For OMAP4430 ES 1.0 revision */ +static const struct omap_dss_features omap4430_es1_0_dss_features = { + .reg_fields = omap4_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), .has_feature = FEAT_GLOBAL_ALPHA | FEAT_PRE_MULT_ALPHA | - FEAT_MGR_LCD2, + FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 | + FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | + FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | + FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | + FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V, .num_mgrs = 3, .num_ovls = 3, .supported_displays = omap4_dss_supported_displays, - .supported_color_modes = omap3_dss_supported_color_modes, + .supported_color_modes = omap4_dss_supported_color_modes, + .clksrc_names = omap4_dss_clk_source_names, + .dss_params = omap4_dss_param_range, + .buffer_size_unit = 16, + .burst_size_unit = 16, +}; + +/* For all the other OMAP4 versions */ +static const struct omap_dss_features omap4_dss_features = { + .reg_fields = omap4_dss_reg_fields, + .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields), + + .has_feature = + FEAT_GLOBAL_ALPHA | FEAT_PRE_MULT_ALPHA | + FEAT_MGR_LCD2 | FEAT_GLOBAL_ALPHA_VID1 | + FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC | + FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH | + FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE | + FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR | + FEAT_PRELOAD | FEAT_FIR_COEF_V, + + .num_mgrs = 3, + .num_ovls = 3, + .supported_displays = omap4_dss_supported_displays, + .supported_color_modes = omap4_dss_supported_color_modes, + .clksrc_names = omap4_dss_clk_source_names, + .dss_params = omap4_dss_param_range, + .buffer_size_unit = 16, + .burst_size_unit = 16, }; /* Functions returning values related to a DSS feature */ @@ -206,6 +387,16 @@ int dss_feat_get_num_ovls(void) return omap_current_dss_features->num_ovls; } +unsigned long dss_feat_get_param_min(enum dss_range_param param) +{ + return omap_current_dss_features->dss_params[param].min; +} + +unsigned long dss_feat_get_param_max(enum dss_range_param param) +{ + return omap_current_dss_features->dss_params[param].max; +} + enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel) { return omap_current_dss_features->supported_displays[channel]; @@ -223,6 +414,21 @@ bool dss_feat_color_mode_supported(enum omap_plane plane, color_mode; } +const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id) +{ + return omap_current_dss_features->clksrc_names[id]; +} + +u32 dss_feat_get_buffer_size_unit(void) +{ + return omap_current_dss_features->buffer_size_unit; +} + +u32 dss_feat_get_burst_size_unit(void) +{ + return omap_current_dss_features->burst_size_unit; +} + /* DSS has_feature check */ bool dss_has_feature(enum dss_feat_id id) { @@ -246,6 +452,10 @@ void dss_features_init(void) omap_current_dss_features = &omap3630_dss_features; else if (cpu_is_omap34xx()) omap_current_dss_features = &omap3430_dss_features; - else + else if (omap_rev() == OMAP4430_REV_ES1_0) + omap_current_dss_features = &omap4430_es1_0_dss_features; + else if (cpu_is_omap44xx()) omap_current_dss_features = &omap4_dss_features; + else + DSSWARN("Unsupported OMAP version"); } diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h index b9c70be9258..b7398cbcda5 100644 --- a/drivers/video/omap2/dss/dss_features.h +++ b/drivers/video/omap2/dss/dss_features.h @@ -22,17 +22,39 @@ #define MAX_DSS_MANAGERS 3 #define MAX_DSS_OVERLAYS 3 +#define MAX_DSS_LCD_MANAGERS 2 +#define MAX_NUM_DSI 2 /* DSS has feature id */ enum dss_feat_id { - FEAT_GLOBAL_ALPHA = 1 << 0, - FEAT_GLOBAL_ALPHA_VID1 = 1 << 1, - FEAT_PRE_MULT_ALPHA = 1 << 2, - FEAT_LCDENABLEPOL = 1 << 3, - FEAT_LCDENABLESIGNAL = 1 << 4, - FEAT_PCKFREEENABLE = 1 << 5, - FEAT_FUNCGATED = 1 << 6, - FEAT_MGR_LCD2 = 1 << 7, + FEAT_GLOBAL_ALPHA = 1 << 0, + FEAT_GLOBAL_ALPHA_VID1 = 1 << 1, + FEAT_PRE_MULT_ALPHA = 1 << 2, + FEAT_LCDENABLEPOL = 1 << 3, + FEAT_LCDENABLESIGNAL = 1 << 4, + FEAT_PCKFREEENABLE = 1 << 5, + FEAT_FUNCGATED = 1 << 6, + FEAT_MGR_LCD2 = 1 << 7, + FEAT_LINEBUFFERSPLIT = 1 << 8, + FEAT_ROWREPEATENABLE = 1 << 9, + FEAT_RESIZECONF = 1 << 10, + /* Independent core clk divider */ + FEAT_CORE_CLK_DIV = 1 << 11, + FEAT_LCD_CLK_SRC = 1 << 12, + /* DSI-PLL power command 0x3 is not working */ + FEAT_DSI_PLL_PWR_BUG = 1 << 13, + FEAT_DSI_PLL_FREQSEL = 1 << 14, + FEAT_DSI_DCS_CMD_CONFIG_VC = 1 << 15, + FEAT_DSI_VC_OCP_WIDTH = 1 << 16, + FEAT_DSI_REVERSE_TXCLKESC = 1 << 17, + FEAT_DSI_GNQ = 1 << 18, + FEAT_HDMI_CTS_SWMODE = 1 << 19, + FEAT_HANDLE_UV_SEPARATE = 1 << 20, + FEAT_ATTR2 = 1 << 21, + FEAT_VENC_REQUIRES_TV_DAC_CLK = 1 << 22, + FEAT_CPR = 1 << 23, + FEAT_PRELOAD = 1 << 24, + FEAT_FIR_COEF_V = 1 << 25, }; /* DSS register field id */ @@ -42,15 +64,38 @@ enum dss_feat_reg_field { FEAT_REG_FIFOHIGHTHRESHOLD, FEAT_REG_FIFOLOWTHRESHOLD, FEAT_REG_FIFOSIZE, + FEAT_REG_HORIZONTALACCU, + FEAT_REG_VERTICALACCU, + FEAT_REG_DISPC_CLK_SWITCH, + FEAT_REG_DSIPLL_REGN, + FEAT_REG_DSIPLL_REGM, + FEAT_REG_DSIPLL_REGM_DISPC, + FEAT_REG_DSIPLL_REGM_DSI, +}; + +enum dss_range_param { + FEAT_PARAM_DSS_FCK, + FEAT_PARAM_DSIPLL_REGN, + FEAT_PARAM_DSIPLL_REGM, + FEAT_PARAM_DSIPLL_REGM_DISPC, + FEAT_PARAM_DSIPLL_REGM_DSI, + FEAT_PARAM_DSIPLL_FINT, + FEAT_PARAM_DSIPLL_LPDIV, }; /* DSS Feature Functions */ int dss_feat_get_num_mgrs(void); int dss_feat_get_num_ovls(void); +unsigned long dss_feat_get_param_min(enum dss_range_param param); +unsigned long dss_feat_get_param_max(enum dss_range_param param); enum omap_display_type dss_feat_get_supported_displays(enum omap_channel channel); enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane plane); bool dss_feat_color_mode_supported(enum omap_plane plane, enum omap_color_mode color_mode); +const char *dss_feat_get_clk_source_name(enum omap_dss_clk_source id); + +u32 dss_feat_get_buffer_size_unit(void); /* in bytes */ +u32 dss_feat_get_burst_size_unit(void); /* in bytes */ bool dss_has_feature(enum dss_feat_id id); void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c new file mode 100644 index 00000000000..256f27a9064 --- /dev/null +++ b/drivers/video/omap2/dss/hdmi.c @@ -0,0 +1,1845 @@ +/* + * hdmi.c + * + * HDMI interface DSS driver setting for TI's OMAP4 family of processor. + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Authors: Yong Zhi + * Mythri pk <mythripk@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define DSS_SUBSYS_NAME "HDMI" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#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) +#include <sound/soc.h> +#include <sound/pcm_params.h> +#endif + +#include "dss.h" +#include "hdmi.h" +#include "dss_features.h" + +static struct { + struct mutex lock; + struct omap_display_platform_data *pdata; + struct platform_device *pdev; + void __iomem *base_wp; /* HDMI wrapper */ + int code; + int mode; + u8 edid[HDMI_EDID_MAX_LENGTH]; + u8 edid_set; + bool custom_set; + struct hdmi_config cfg; + + struct clk *sys_clk; + struct clk *hdmi_clk; +} hdmi; + +/* + * Logic for the below structure : + * user enters the CEA or VESA timings by specifying the HDMI/DVI code. + * There is a correspondence between CEA/VESA timing and code, please + * refer to section 6.3 in HDMI 1.3 specification for timing code. + * + * In the below structure, cea_vesa_timings corresponds to all OMAP4 + * supported CEA and VESA timing values.code_cea corresponds to the CEA + * code, It is used to get the timing from cea_vesa_timing array.Similarly + * with code_vesa. Code_index is used for back mapping, that is once EDID + * is read from the TV, EDID is parsed to find the timing values and then + * map it to corresponding CEA or VESA index. + */ + +static const struct hdmi_timings cea_vesa_timings[OMAP_HDMI_TIMINGS_NB] = { + { {640, 480, 25200, 96, 16, 48, 2, 10, 33} , 0 , 0}, + { {1280, 720, 74250, 40, 440, 220, 5, 5, 20}, 1, 1}, + { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1}, + { {720, 480, 27027, 62, 16, 60, 6, 9, 30}, 0, 0}, + { {2880, 576, 108000, 256, 48, 272, 5, 5, 39}, 0, 0}, + { {1440, 240, 27027, 124, 38, 114, 3, 4, 15}, 0, 0}, + { {1440, 288, 27000, 126, 24, 138, 3, 2, 19}, 0, 0}, + { {1920, 540, 74250, 44, 528, 148, 5, 2, 15}, 1, 1}, + { {1920, 540, 74250, 44, 88, 148, 5, 2, 15}, 1, 1}, + { {1920, 1080, 148500, 44, 88, 148, 5, 4, 36}, 1, 1}, + { {720, 576, 27000, 64, 12, 68, 5, 5, 39}, 0, 0}, + { {1440, 576, 54000, 128, 24, 136, 5, 5, 39}, 0, 0}, + { {1920, 1080, 148500, 44, 528, 148, 5, 4, 36}, 1, 1}, + { {2880, 480, 108108, 248, 64, 240, 6, 9, 30}, 0, 0}, + { {1920, 1080, 74250, 44, 638, 148, 5, 4, 36}, 1, 1}, + /* VESA From Here */ + { {640, 480, 25175, 96, 16, 48, 2 , 11, 31}, 0, 0}, + { {800, 600, 40000, 128, 40, 88, 4 , 1, 23}, 1, 1}, + { {848, 480, 33750, 112, 16, 112, 8 , 6, 23}, 1, 1}, + { {1280, 768, 79500, 128, 64, 192, 7 , 3, 20}, 1, 0}, + { {1280, 800, 83500, 128, 72, 200, 6 , 3, 22}, 1, 0}, + { {1360, 768, 85500, 112, 64, 256, 6 , 3, 18}, 1, 1}, + { {1280, 960, 108000, 112, 96, 312, 3 , 1, 36}, 1, 1}, + { {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38}, 1, 1}, + { {1024, 768, 65000, 136, 24, 160, 6, 3, 29}, 0, 0}, + { {1400, 1050, 121750, 144, 88, 232, 4, 3, 32}, 1, 0}, + { {1440, 900, 106500, 152, 80, 232, 6, 3, 25}, 1, 0}, + { {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30}, 1, 0}, + { {1366, 768, 85500, 143, 70, 213, 3, 3, 24}, 1, 1}, + { {1920, 1080, 148500, 44, 148, 80, 5, 4, 36}, 1, 1}, + { {1280, 768, 68250, 32, 48, 80, 7, 3, 12}, 0, 1}, + { {1400, 1050, 101000, 32, 48, 80, 4, 3, 23}, 0, 1}, + { {1680, 1050, 119000, 32, 48, 80, 6, 3, 21}, 0, 1}, + { {1280, 800, 79500, 32, 48, 80, 6, 3, 14}, 0, 1}, + { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1} +}; + +/* + * This is a static mapping array which maps the timing values + * with corresponding CEA / VESA code + */ +static const int code_index[OMAP_HDMI_TIMINGS_NB] = { + 1, 19, 4, 2, 37, 6, 21, 20, 5, 16, 17, 29, 31, 35, 32, + /* <--15 CEA 17--> vesa*/ + 4, 9, 0xE, 0x17, 0x1C, 0x27, 0x20, 0x23, 0x10, 0x2A, + 0X2F, 0x3A, 0X51, 0X52, 0x16, 0x29, 0x39, 0x1B +}; + +/* + * This is reverse static mapping which maps the CEA / VESA code + * to the corresponding timing values + */ +static const int code_cea[39] = { + -1, 0, 3, 3, 2, 8, 5, 5, -1, -1, + -1, -1, -1, -1, -1, -1, 9, 10, 10, 1, + 7, 6, 6, -1, -1, -1, -1, -1, -1, 11, + 11, 12, 14, -1, -1, 13, 13, 4, 4 +}; + +static const int code_vesa[85] = { + -1, -1, -1, -1, 15, -1, -1, -1, -1, 16, + -1, -1, -1, -1, 17, -1, 23, -1, -1, -1, + -1, -1, 29, 18, -1, -1, -1, 32, 19, -1, + -1, -1, 21, -1, -1, 22, -1, -1, -1, 20, + -1, 30, 24, -1, -1, -1, -1, 25, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 31, 26, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 27, 28, -1, 33}; + +static const u8 edid_header[8] = {0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0}; + +static inline void hdmi_write_reg(const struct hdmi_reg idx, u32 val) +{ + __raw_writel(val, hdmi.base_wp + idx.idx); +} + +static inline u32 hdmi_read_reg(const struct hdmi_reg idx) +{ + return __raw_readl(hdmi.base_wp + idx.idx); +} + +static inline int hdmi_wait_for_bit_change(const struct hdmi_reg idx, + int b2, int b1, u32 val) +{ + u32 t = 0; + while (val != REG_GET(idx, b2, b1)) { + udelay(1); + if (t++ > 10000) + return !val; + } + 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"); + + return 0; +} + +static int hdmi_pll_init(enum hdmi_clk_refsel refsel, int dcofreq, + struct hdmi_pll_info *fmt, u16 sd) +{ + u32 r; + + /* PLL start always use manual mode */ + REG_FLD_MOD(PLLCTRL_PLL_CONTROL, 0x0, 0, 0); + + r = hdmi_read_reg(PLLCTRL_CFG1); + r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ + r = FLD_MOD(r, fmt->regn, 8, 1); /* CFG1_PLL_REGN */ + + hdmi_write_reg(PLLCTRL_CFG1, r); + + r = hdmi_read_reg(PLLCTRL_CFG2); + + r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ + r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ + r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ + + if (dcofreq) { + /* divider programming for frequency beyond 1000Mhz */ + REG_FLD_MOD(PLLCTRL_CFG3, sd, 17, 10); + r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ + } else { + r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ + } + + hdmi_write_reg(PLLCTRL_CFG2, r); + + r = hdmi_read_reg(PLLCTRL_CFG4); + r = FLD_MOD(r, fmt->regm2, 24, 18); + r = FLD_MOD(r, fmt->regmf, 17, 0); + + hdmi_write_reg(PLLCTRL_CFG4, r); + + /* go now */ + REG_FLD_MOD(PLLCTRL_PLL_GO, 0x1, 0, 0); + + /* wait for bit change */ + if (hdmi_wait_for_bit_change(PLLCTRL_PLL_GO, 0, 0, 1) != 1) { + DSSERR("PLL GO bit not set\n"); + return -ETIMEDOUT; + } + + /* Wait till the lock bit is set in PLL status */ + if (hdmi_wait_for_bit_change(PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { + DSSWARN("cannot lock PLL\n"); + DSSWARN("CFG1 0x%x\n", + hdmi_read_reg(PLLCTRL_CFG1)); + DSSWARN("CFG2 0x%x\n", + hdmi_read_reg(PLLCTRL_CFG2)); + DSSWARN("CFG4 0x%x\n", + hdmi_read_reg(PLLCTRL_CFG4)); + return -ETIMEDOUT; + } + + DSSDBG("PLL locked!\n"); + + return 0; +} + +/* PHY_PWR_CMD */ +static int hdmi_set_phy_pwr(enum hdmi_phy_pwr val) +{ + /* Command for power control of HDMI PHY */ + REG_FLD_MOD(HDMI_WP_PWR_CTRL, val, 7, 6); + + /* Status of the power control of HDMI PHY */ + if (hdmi_wait_for_bit_change(HDMI_WP_PWR_CTRL, 5, 4, val) != val) { + DSSERR("Failed to set PHY power mode to %d\n", val); + return -ETIMEDOUT; + } + + return 0; +} + +/* PLL_PWR_CMD */ +static int hdmi_set_pll_pwr(enum hdmi_pll_pwr val) +{ + /* Command for power control of HDMI PLL */ + REG_FLD_MOD(HDMI_WP_PWR_CTRL, val, 3, 2); + + /* wait till PHY_PWR_STATUS is set */ + if (hdmi_wait_for_bit_change(HDMI_WP_PWR_CTRL, 1, 0, val) != val) { + DSSERR("Failed to set PHY_PWR_STATUS\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int hdmi_pll_reset(void) +{ + /* SYSRESET controlled by power FSM */ + REG_FLD_MOD(PLLCTRL_PLL_CONTROL, 0x0, 3, 3); + + /* READ 0x0 reset is in progress */ + if (hdmi_wait_for_bit_change(PLLCTRL_PLL_STATUS, 0, 0, 1) != 1) { + DSSERR("Failed to sysreset PLL\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int hdmi_phy_init(void) +{ + u16 r = 0; + + r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_LDOON); + if (r) + return r; + + r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_TXON); + if (r) + return r; + + /* + * Read address 0 in order to get the SCP reset done completed + * Dummy access performed to make sure reset is done + */ + hdmi_read_reg(HDMI_TXPHY_TX_CTRL); + + /* + * Write to phy address 0 to configure the clock + * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field + */ + REG_FLD_MOD(HDMI_TXPHY_TX_CTRL, 0x1, 31, 30); + + /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ + hdmi_write_reg(HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); + + /* Setup max LDO voltage */ + REG_FLD_MOD(HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); + + /* Write to phy address 3 to change the polarity control */ + REG_FLD_MOD(HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); + + return 0; +} + +static int hdmi_pll_program(struct hdmi_pll_info *fmt) +{ + u16 r = 0; + enum hdmi_clk_refsel refsel; + + r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF); + if (r) + return r; + + r = hdmi_set_pll_pwr(HDMI_PLLPWRCMD_BOTHON_ALLCLKS); + if (r) + return r; + + r = hdmi_pll_reset(); + if (r) + return r; + + refsel = HDMI_REFSEL_SYSCLK; + + r = hdmi_pll_init(refsel, fmt->dcofreq, fmt, fmt->regsd); + if (r) + return r; + + return 0; +} + +static void hdmi_phy_off(void) +{ + hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF); +} + +static int hdmi_core_ddc_edid(u8 *pedid, int ext) +{ + u32 i, j; + char checksum = 0; + u32 offset = 0; + + /* Turn on CLK for DDC */ + REG_FLD_MOD(HDMI_CORE_AV_DPD, 0x7, 2, 0); + + /* + * SW HACK : Without the Delay DDC(i2c bus) reads 0 values / + * right shifted values( The behavior is not consistent and seen only + * with some TV's) + */ + usleep_range(800, 1000); + + if (!ext) { + /* Clk SCL Devices */ + REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0xA, 3, 0); + + /* HDMI_CORE_DDC_STATUS_IN_PROG */ + if (hdmi_wait_for_bit_change(HDMI_CORE_DDC_STATUS, + 4, 4, 0) != 0) { + DSSERR("Failed to program DDC\n"); + return -ETIMEDOUT; + } + + /* Clear FIFO */ + REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x9, 3, 0); + + /* HDMI_CORE_DDC_STATUS_IN_PROG */ + if (hdmi_wait_for_bit_change(HDMI_CORE_DDC_STATUS, + 4, 4, 0) != 0) { + DSSERR("Failed to program DDC\n"); + return -ETIMEDOUT; + } + + } else { + if (ext % 2 != 0) + offset = 0x80; + } + + /* Load Segment Address Register */ + REG_FLD_MOD(HDMI_CORE_DDC_SEGM, ext/2, 7, 0); + + /* Load Slave Address Register */ + REG_FLD_MOD(HDMI_CORE_DDC_ADDR, 0xA0 >> 1, 7, 1); + + /* Load Offset Address Register */ + REG_FLD_MOD(HDMI_CORE_DDC_OFFSET, offset, 7, 0); + + /* Load Byte Count */ + REG_FLD_MOD(HDMI_CORE_DDC_COUNT1, 0x80, 7, 0); + REG_FLD_MOD(HDMI_CORE_DDC_COUNT2, 0x0, 1, 0); + + /* Set DDC_CMD */ + if (ext) + REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x4, 3, 0); + else + REG_FLD_MOD(HDMI_CORE_DDC_CMD, 0x2, 3, 0); + + /* HDMI_CORE_DDC_STATUS_BUS_LOW */ + if (REG_GET(HDMI_CORE_DDC_STATUS, 6, 6) == 1) { + DSSWARN("I2C Bus Low?\n"); + return -EIO; + } + /* HDMI_CORE_DDC_STATUS_NO_ACK */ + if (REG_GET(HDMI_CORE_DDC_STATUS, 5, 5) == 1) { + DSSWARN("I2C No Ack\n"); + return -EIO; + } + + i = ext * 128; + j = 0; + while (((REG_GET(HDMI_CORE_DDC_STATUS, 4, 4) == 1) || + (REG_GET(HDMI_CORE_DDC_STATUS, 2, 2) == 0)) && + j < 128) { + + if (REG_GET(HDMI_CORE_DDC_STATUS, 2, 2) == 0) { + /* FIFO not empty */ + pedid[i++] = REG_GET(HDMI_CORE_DDC_DATA, 7, 0); + j++; + } + } + + for (j = 0; j < 128; j++) + checksum += pedid[j]; + + if (checksum != 0) { + DSSERR("E-EDID checksum failed!!\n"); + return -EIO; + } + + return 0; +} + +static int read_edid(u8 *pedid, u16 max_length) +{ + int r = 0, n = 0, i = 0; + int max_ext_blocks = (max_length / 128) - 1; + + r = hdmi_core_ddc_edid(pedid, 0); + if (r) { + return r; + } else { + n = pedid[0x7e]; + + /* + * README: need to comply with max_length set by the caller. + * Better implementation should be to allocate necessary + * memory to store EDID according to nb_block field found + * in first block + */ + if (n > max_ext_blocks) + n = max_ext_blocks; + + for (i = 1; i <= n; i++) { + r = hdmi_core_ddc_edid(pedid, i); + if (r) + return r; + } + } + return 0; +} + +static int get_timings_index(void) +{ + int code; + + if (hdmi.mode == 0) + code = code_vesa[hdmi.code]; + else + code = code_cea[hdmi.code]; + + if (code == -1) { + /* HDMI code 4 corresponds to 640 * 480 VGA */ + hdmi.code = 4; + /* DVI mode 1 corresponds to HDMI 0 to DVI */ + hdmi.mode = HDMI_DVI; + + code = code_vesa[hdmi.code]; + } + return code; +} + +static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing) +{ + int i = 0, code = -1, temp_vsync = 0, temp_hsync = 0; + int timing_vsync = 0, timing_hsync = 0; + struct omap_video_timings temp; + struct hdmi_cm cm = {-1}; + DSSDBG("hdmi_get_code\n"); + + for (i = 0; i < OMAP_HDMI_TIMINGS_NB; i++) { + temp = cea_vesa_timings[i].timings; + if ((temp.pixel_clock == timing->pixel_clock) && + (temp.x_res == timing->x_res) && + (temp.y_res == timing->y_res)) { + + temp_hsync = temp.hfp + temp.hsw + temp.hbp; + timing_hsync = timing->hfp + timing->hsw + timing->hbp; + temp_vsync = temp.vfp + temp.vsw + temp.vbp; + timing_vsync = timing->vfp + timing->vsw + timing->vbp; + + DSSDBG("temp_hsync = %d , temp_vsync = %d" + "timing_hsync = %d, timing_vsync = %d\n", + temp_hsync, temp_hsync, + timing_hsync, timing_vsync); + + if ((temp_hsync == timing_hsync) && + (temp_vsync == timing_vsync)) { + code = i; + cm.code = code_index[i]; + if (code < 14) + cm.mode = HDMI_HDMI; + else + cm.mode = HDMI_DVI; + DSSDBG("Hdmi_code = %d mode = %d\n", + cm.code, cm.mode); + break; + } + } + } + + return cm; +} + +static void get_horz_vert_timing_info(int current_descriptor_addrs, u8 *edid , + struct omap_video_timings *timings) +{ + /* X and Y resolution */ + timings->x_res = (((edid[current_descriptor_addrs + 4] & 0xF0) << 4) | + edid[current_descriptor_addrs + 2]); + timings->y_res = (((edid[current_descriptor_addrs + 7] & 0xF0) << 4) | + edid[current_descriptor_addrs + 5]); + + timings->pixel_clock = ((edid[current_descriptor_addrs + 1] << 8) | + edid[current_descriptor_addrs]); + + timings->pixel_clock = 10 * timings->pixel_clock; + + /* HORIZONTAL FRONT PORCH */ + timings->hfp = edid[current_descriptor_addrs + 8] | + ((edid[current_descriptor_addrs + 11] & 0xc0) << 2); + /* HORIZONTAL SYNC WIDTH */ + timings->hsw = edid[current_descriptor_addrs + 9] | + ((edid[current_descriptor_addrs + 11] & 0x30) << 4); + /* HORIZONTAL BACK PORCH */ + timings->hbp = (((edid[current_descriptor_addrs + 4] & 0x0F) << 8) | + edid[current_descriptor_addrs + 3]) - + (timings->hfp + timings->hsw); + /* VERTICAL FRONT PORCH */ + timings->vfp = ((edid[current_descriptor_addrs + 10] & 0xF0) >> 4) | + ((edid[current_descriptor_addrs + 11] & 0x0f) << 2); + /* VERTICAL SYNC WIDTH */ + timings->vsw = (edid[current_descriptor_addrs + 10] & 0x0F) | + ((edid[current_descriptor_addrs + 11] & 0x03) << 4); + /* VERTICAL BACK PORCH */ + timings->vbp = (((edid[current_descriptor_addrs + 7] & 0x0F) << 8) | + edid[current_descriptor_addrs + 6]) - + (timings->vfp + timings->vsw); + +} + +/* Description : This function gets the resolution information from EDID */ +static void get_edid_timing_data(u8 *edid) +{ + u8 count; + u16 current_descriptor_addrs; + struct hdmi_cm cm; + struct omap_video_timings edid_timings; + + /* search block 0, there are 4 DTDs arranged in priority order */ + for (count = 0; count < EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR; count++) { + current_descriptor_addrs = + EDID_DESCRIPTOR_BLOCK0_ADDRESS + + count * EDID_TIMING_DESCRIPTOR_SIZE; + get_horz_vert_timing_info(current_descriptor_addrs, + edid, &edid_timings); + cm = hdmi_get_code(&edid_timings); + DSSDBG("Block0[%d] value matches code = %d , mode = %d\n", + count, cm.code, cm.mode); + if (cm.code == -1) { + continue; + } else { + hdmi.code = cm.code; + hdmi.mode = cm.mode; + DSSDBG("code = %d , mode = %d\n", + hdmi.code, hdmi.mode); + return; + } + } + if (edid[0x7e] != 0x00) { + for (count = 0; count < EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR; + count++) { + current_descriptor_addrs = + EDID_DESCRIPTOR_BLOCK1_ADDRESS + + count * EDID_TIMING_DESCRIPTOR_SIZE; + get_horz_vert_timing_info(current_descriptor_addrs, + edid, &edid_timings); + cm = hdmi_get_code(&edid_timings); + DSSDBG("Block1[%d] value matches code = %d, mode = %d", + count, cm.code, cm.mode); + if (cm.code == -1) { + continue; + } else { + hdmi.code = cm.code; + hdmi.mode = cm.mode; + DSSDBG("code = %d , mode = %d\n", + hdmi.code, hdmi.mode); + return; + } + } + } + + DSSINFO("no valid timing found , falling back to VGA\n"); + hdmi.code = 4; /* setting default value of 640 480 VGA */ + hdmi.mode = HDMI_DVI; +} + +static void hdmi_read_edid(struct omap_video_timings *dp) +{ + int ret = 0, code; + + memset(hdmi.edid, 0, HDMI_EDID_MAX_LENGTH); + + if (!hdmi.edid_set) + ret = read_edid(hdmi.edid, HDMI_EDID_MAX_LENGTH); + + if (!ret) { + if (!memcmp(hdmi.edid, edid_header, sizeof(edid_header))) { + /* search for timings of default resolution */ + get_edid_timing_data(hdmi.edid); + hdmi.edid_set = true; + } + } else { + DSSWARN("failed to read E-EDID\n"); + } + + if (!hdmi.edid_set) { + DSSINFO("fallback to VGA\n"); + hdmi.code = 4; /* setting default value of 640 480 VGA */ + hdmi.mode = HDMI_DVI; + } + + code = get_timings_index(); + + *dp = cea_vesa_timings[code].timings; +} + +static void hdmi_core_init(struct hdmi_core_video_config *video_cfg, + struct hdmi_core_infoframe_avi *avi_cfg, + struct hdmi_core_packet_enable_repeat *repeat_cfg) +{ + DSSDBG("Enter hdmi_core_init\n"); + + /* video core */ + video_cfg->ip_bus_width = HDMI_INPUT_8BIT; + video_cfg->op_dither_truc = HDMI_OUTPUTTRUNCATION_8BIT; + video_cfg->deep_color_pkt = HDMI_DEEPCOLORPACKECTDISABLE; + video_cfg->pkt_mode = HDMI_PACKETMODERESERVEDVALUE; + video_cfg->hdmi_dvi = HDMI_DVI; + video_cfg->tclk_sel_clkmult = HDMI_FPLL10IDCK; + + /* info frame */ + avi_cfg->db1_format = 0; + avi_cfg->db1_active_info = 0; + avi_cfg->db1_bar_info_dv = 0; + avi_cfg->db1_scan_info = 0; + avi_cfg->db2_colorimetry = 0; + avi_cfg->db2_aspect_ratio = 0; + avi_cfg->db2_active_fmt_ar = 0; + avi_cfg->db3_itc = 0; + avi_cfg->db3_ec = 0; + avi_cfg->db3_q_range = 0; + avi_cfg->db3_nup_scaling = 0; + avi_cfg->db4_videocode = 0; + avi_cfg->db5_pixel_repeat = 0; + avi_cfg->db6_7_line_eoftop = 0 ; + avi_cfg->db8_9_line_sofbottom = 0; + avi_cfg->db10_11_pixel_eofleft = 0; + avi_cfg->db12_13_pixel_sofright = 0; + + /* packet enable and repeat */ + repeat_cfg->audio_pkt = 0; + repeat_cfg->audio_pkt_repeat = 0; + repeat_cfg->avi_infoframe = 0; + repeat_cfg->avi_infoframe_repeat = 0; + repeat_cfg->gen_cntrl_pkt = 0; + repeat_cfg->gen_cntrl_pkt_repeat = 0; + repeat_cfg->generic_pkt = 0; + repeat_cfg->generic_pkt_repeat = 0; +} + +static void hdmi_core_powerdown_disable(void) +{ + DSSDBG("Enter hdmi_core_powerdown_disable\n"); + REG_FLD_MOD(HDMI_CORE_CTRL1, 0x0, 0, 0); +} + +static void hdmi_core_swreset_release(void) +{ + DSSDBG("Enter hdmi_core_swreset_release\n"); + REG_FLD_MOD(HDMI_CORE_SYS_SRST, 0x0, 0, 0); +} + +static void hdmi_core_swreset_assert(void) +{ + DSSDBG("Enter hdmi_core_swreset_assert\n"); + REG_FLD_MOD(HDMI_CORE_SYS_SRST, 0x1, 0, 0); +} + +/* DSS_HDMI_CORE_VIDEO_CONFIG */ +static void hdmi_core_video_config(struct hdmi_core_video_config *cfg) +{ + u32 r = 0; + + /* sys_ctrl1 default configuration not tunable */ + r = hdmi_read_reg(HDMI_CORE_CTRL1); + r = FLD_MOD(r, HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC, 5, 5); + r = FLD_MOD(r, HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC, 4, 4); + r = FLD_MOD(r, HDMI_CORE_CTRL1_BSEL_24BITBUS, 2, 2); + r = FLD_MOD(r, HDMI_CORE_CTRL1_EDGE_RISINGEDGE, 1, 1); + hdmi_write_reg(HDMI_CORE_CTRL1, r); + + REG_FLD_MOD(HDMI_CORE_SYS_VID_ACEN, cfg->ip_bus_width, 7, 6); + + /* Vid_Mode */ + r = hdmi_read_reg(HDMI_CORE_SYS_VID_MODE); + + /* dither truncation configuration */ + if (cfg->op_dither_truc > HDMI_OUTPUTTRUNCATION_12BIT) { + r = FLD_MOD(r, cfg->op_dither_truc - 3, 7, 6); + r = FLD_MOD(r, 1, 5, 5); + } else { + r = FLD_MOD(r, cfg->op_dither_truc, 7, 6); + r = FLD_MOD(r, 0, 5, 5); + } + hdmi_write_reg(HDMI_CORE_SYS_VID_MODE, r); + + /* HDMI_Ctrl */ + r = hdmi_read_reg(HDMI_CORE_AV_HDMI_CTRL); + r = FLD_MOD(r, cfg->deep_color_pkt, 6, 6); + r = FLD_MOD(r, cfg->pkt_mode, 5, 3); + r = FLD_MOD(r, cfg->hdmi_dvi, 0, 0); + hdmi_write_reg(HDMI_CORE_AV_HDMI_CTRL, r); + + /* TMDS_CTRL */ + REG_FLD_MOD(HDMI_CORE_SYS_TMDS_CTRL, + cfg->tclk_sel_clkmult, 6, 5); +} + +static void hdmi_core_aux_infoframe_avi_config( + struct hdmi_core_infoframe_avi info_avi) +{ + u32 val; + char sum = 0, checksum = 0; + + sum += 0x82 + 0x002 + 0x00D; + hdmi_write_reg(HDMI_CORE_AV_AVI_TYPE, 0x082); + hdmi_write_reg(HDMI_CORE_AV_AVI_VERS, 0x002); + hdmi_write_reg(HDMI_CORE_AV_AVI_LEN, 0x00D); + + val = (info_avi.db1_format << 5) | + (info_avi.db1_active_info << 4) | + (info_avi.db1_bar_info_dv << 2) | + (info_avi.db1_scan_info); + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(0), val); + sum += val; + + val = (info_avi.db2_colorimetry << 6) | + (info_avi.db2_aspect_ratio << 4) | + (info_avi.db2_active_fmt_ar); + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(1), val); + sum += val; + + val = (info_avi.db3_itc << 7) | + (info_avi.db3_ec << 4) | + (info_avi.db3_q_range << 2) | + (info_avi.db3_nup_scaling); + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(2), val); + sum += val; + + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(3), info_avi.db4_videocode); + sum += info_avi.db4_videocode; + + val = info_avi.db5_pixel_repeat; + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(4), val); + sum += val; + + val = info_avi.db6_7_line_eoftop & 0x00FF; + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(5), val); + sum += val; + + val = ((info_avi.db6_7_line_eoftop >> 8) & 0x00FF); + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(6), val); + sum += val; + + val = info_avi.db8_9_line_sofbottom & 0x00FF; + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(7), val); + sum += val; + + val = ((info_avi.db8_9_line_sofbottom >> 8) & 0x00FF); + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(8), val); + sum += val; + + val = info_avi.db10_11_pixel_eofleft & 0x00FF; + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(9), val); + sum += val; + + val = ((info_avi.db10_11_pixel_eofleft >> 8) & 0x00FF); + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(10), val); + sum += val; + + val = info_avi.db12_13_pixel_sofright & 0x00FF; + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(11), val); + sum += val; + + val = ((info_avi.db12_13_pixel_sofright >> 8) & 0x00FF); + hdmi_write_reg(HDMI_CORE_AV_AVI_DBYTE(12), val); + sum += val; + + checksum = 0x100 - sum; + hdmi_write_reg(HDMI_CORE_AV_AVI_CHSUM, checksum); +} + +static void hdmi_core_av_packet_config( + struct hdmi_core_packet_enable_repeat repeat_cfg) +{ + /* enable/repeat the infoframe */ + hdmi_write_reg(HDMI_CORE_AV_PB_CTRL1, + (repeat_cfg.audio_pkt << 5) | + (repeat_cfg.audio_pkt_repeat << 4) | + (repeat_cfg.avi_infoframe << 1) | + (repeat_cfg.avi_infoframe_repeat)); + + /* enable/repeat the packet */ + hdmi_write_reg(HDMI_CORE_AV_PB_CTRL2, + (repeat_cfg.gen_cntrl_pkt << 3) | + (repeat_cfg.gen_cntrl_pkt_repeat << 2) | + (repeat_cfg.generic_pkt << 1) | + (repeat_cfg.generic_pkt_repeat)); +} + +static void hdmi_wp_init(struct omap_video_timings *timings, + struct hdmi_video_format *video_fmt, + struct hdmi_video_interface *video_int) +{ + DSSDBG("Enter hdmi_wp_init\n"); + + timings->hbp = 0; + timings->hfp = 0; + timings->hsw = 0; + timings->vbp = 0; + timings->vfp = 0; + timings->vsw = 0; + + video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444; + video_fmt->y_res = 0; + video_fmt->x_res = 0; + + video_int->vsp = 0; + video_int->hsp = 0; + + video_int->interlacing = 0; + video_int->tm = 0; /* HDMI_TIMING_SLAVE */ + +} + +static void hdmi_wp_video_start(bool start) +{ + REG_FLD_MOD(HDMI_WP_VIDEO_CFG, start, 31, 31); +} + +static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt, + struct omap_video_timings *timings, struct hdmi_config *param) +{ + DSSDBG("Enter hdmi_wp_video_init_format\n"); + + video_fmt->y_res = param->timings.timings.y_res; + video_fmt->x_res = param->timings.timings.x_res; + + timings->hbp = param->timings.timings.hbp; + timings->hfp = param->timings.timings.hfp; + timings->hsw = param->timings.timings.hsw; + timings->vbp = param->timings.timings.vbp; + timings->vfp = param->timings.timings.vfp; + timings->vsw = param->timings.timings.vsw; +} + +static void hdmi_wp_video_config_format( + struct hdmi_video_format *video_fmt) +{ + u32 l = 0; + + REG_FLD_MOD(HDMI_WP_VIDEO_CFG, video_fmt->packing_mode, 10, 8); + + l |= FLD_VAL(video_fmt->y_res, 31, 16); + l |= FLD_VAL(video_fmt->x_res, 15, 0); + hdmi_write_reg(HDMI_WP_VIDEO_SIZE, l); +} + +static void hdmi_wp_video_config_interface( + struct hdmi_video_interface *video_int) +{ + u32 r; + DSSDBG("Enter hdmi_wp_video_config_interface\n"); + + r = hdmi_read_reg(HDMI_WP_VIDEO_CFG); + r = FLD_MOD(r, video_int->vsp, 7, 7); + r = FLD_MOD(r, video_int->hsp, 6, 6); + r = FLD_MOD(r, video_int->interlacing, 3, 3); + r = FLD_MOD(r, video_int->tm, 1, 0); + hdmi_write_reg(HDMI_WP_VIDEO_CFG, r); +} + +static void hdmi_wp_video_config_timing( + struct omap_video_timings *timings) +{ + u32 timing_h = 0; + u32 timing_v = 0; + + DSSDBG("Enter hdmi_wp_video_config_timing\n"); + + timing_h |= FLD_VAL(timings->hbp, 31, 20); + timing_h |= FLD_VAL(timings->hfp, 19, 8); + timing_h |= FLD_VAL(timings->hsw, 7, 0); + hdmi_write_reg(HDMI_WP_VIDEO_TIMING_H, timing_h); + + timing_v |= FLD_VAL(timings->vbp, 31, 20); + timing_v |= FLD_VAL(timings->vfp, 19, 8); + timing_v |= FLD_VAL(timings->vsw, 7, 0); + hdmi_write_reg(HDMI_WP_VIDEO_TIMING_V, timing_v); +} + +static void hdmi_basic_configure(struct hdmi_config *cfg) +{ + /* HDMI */ + struct omap_video_timings video_timing; + struct hdmi_video_format video_format; + struct hdmi_video_interface video_interface; + /* HDMI core */ + struct hdmi_core_infoframe_avi avi_cfg; + struct hdmi_core_video_config v_core_cfg; + struct hdmi_core_packet_enable_repeat repeat_cfg; + + hdmi_wp_init(&video_timing, &video_format, + &video_interface); + + hdmi_core_init(&v_core_cfg, + &avi_cfg, + &repeat_cfg); + + hdmi_wp_video_init_format(&video_format, + &video_timing, cfg); + + hdmi_wp_video_config_timing(&video_timing); + + /* video config */ + video_format.packing_mode = HDMI_PACK_24b_RGB_YUV444_YUV422; + + hdmi_wp_video_config_format(&video_format); + + video_interface.vsp = cfg->timings.vsync_pol; + video_interface.hsp = cfg->timings.hsync_pol; + video_interface.interlacing = cfg->interlace; + video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */ + + hdmi_wp_video_config_interface(&video_interface); + + /* + * configure core video part + * set software reset in the core + */ + hdmi_core_swreset_assert(); + + /* power down off */ + hdmi_core_powerdown_disable(); + + v_core_cfg.pkt_mode = HDMI_PACKETMODE24BITPERPIXEL; + v_core_cfg.hdmi_dvi = cfg->cm.mode; + + hdmi_core_video_config(&v_core_cfg); + + /* release software reset in the core */ + hdmi_core_swreset_release(); + + /* + * configure packet + * info frame video see doc CEA861-D page 65 + */ + avi_cfg.db1_format = HDMI_INFOFRAME_AVI_DB1Y_RGB; + avi_cfg.db1_active_info = + HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF; + avi_cfg.db1_bar_info_dv = HDMI_INFOFRAME_AVI_DB1B_NO; + avi_cfg.db1_scan_info = HDMI_INFOFRAME_AVI_DB1S_0; + avi_cfg.db2_colorimetry = HDMI_INFOFRAME_AVI_DB2C_NO; + avi_cfg.db2_aspect_ratio = HDMI_INFOFRAME_AVI_DB2M_NO; + avi_cfg.db2_active_fmt_ar = HDMI_INFOFRAME_AVI_DB2R_SAME; + avi_cfg.db3_itc = HDMI_INFOFRAME_AVI_DB3ITC_NO; + avi_cfg.db3_ec = HDMI_INFOFRAME_AVI_DB3EC_XVYUV601; + avi_cfg.db3_q_range = HDMI_INFOFRAME_AVI_DB3Q_DEFAULT; + avi_cfg.db3_nup_scaling = HDMI_INFOFRAME_AVI_DB3SC_NO; + avi_cfg.db4_videocode = cfg->cm.code; + avi_cfg.db5_pixel_repeat = HDMI_INFOFRAME_AVI_DB5PR_NO; + avi_cfg.db6_7_line_eoftop = 0; + avi_cfg.db8_9_line_sofbottom = 0; + avi_cfg.db10_11_pixel_eofleft = 0; + avi_cfg.db12_13_pixel_sofright = 0; + + hdmi_core_aux_infoframe_avi_config(avi_cfg); + + /* enable/repeat the infoframe */ + repeat_cfg.avi_infoframe = HDMI_PACKETENABLE; + repeat_cfg.avi_infoframe_repeat = HDMI_PACKETREPEATON; + /* wakeup */ + repeat_cfg.audio_pkt = HDMI_PACKETENABLE; + repeat_cfg.audio_pkt_repeat = HDMI_PACKETREPEATON; + hdmi_core_av_packet_config(repeat_cfg); +} + +static void update_hdmi_timings(struct hdmi_config *cfg, + struct omap_video_timings *timings, int code) +{ + cfg->timings.timings.x_res = timings->x_res; + cfg->timings.timings.y_res = timings->y_res; + cfg->timings.timings.hbp = timings->hbp; + cfg->timings.timings.hfp = timings->hfp; + cfg->timings.timings.hsw = timings->hsw; + cfg->timings.timings.vbp = timings->vbp; + cfg->timings.timings.vfp = timings->vfp; + cfg->timings.timings.vsw = timings->vsw; + cfg->timings.timings.pixel_clock = timings->pixel_clock; + cfg->timings.vsync_pol = cea_vesa_timings[code].vsync_pol; + cfg->timings.hsync_pol = cea_vesa_timings[code].hsync_pol; +} + +static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy, + struct hdmi_pll_info *pi) +{ + unsigned long clkin, refclk; + u32 mf; + + clkin = clk_get_rate(hdmi.sys_clk) / 10000; + /* + * Input clock is predivided by N + 1 + * out put of which is reference clk + */ + pi->regn = dssdev->clocks.hdmi.regn; + refclk = clkin / (pi->regn + 1); + + /* + * multiplier is pixel_clk/ref_clk + * Multiplying by 100 to avoid fractional part removal + */ + pi->regm = (phy * 100 / (refclk)) / 100; + pi->regm2 = dssdev->clocks.hdmi.regm2; + + /* + * fractional multiplier is remainder of the difference between + * multiplier and actual phy(required pixel clock thus should be + * multiplied by 2^18(262144) divided by the reference clock + */ + mf = (phy - pi->regm * refclk) * 262144; + pi->regmf = mf / (refclk); + + /* + * Dcofreq should be set to 1 if required pixel clock + * is greater than 1000MHz + */ + pi->dcofreq = phy > 1000 * 100; + pi->regsd = ((pi->regm * clkin / 10) / ((pi->regn + 1) * 250) + 5) / 10; + + DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); + DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); +} + +static int hdmi_power_on(struct omap_dss_device *dssdev) +{ + int r, code = 0; + struct hdmi_pll_info pll_data; + struct omap_video_timings *p; + unsigned long phy; + + r = hdmi_runtime_get(); + if (r) + return r; + + dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0); + + p = &dssdev->panel.timings; + + DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", + dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res); + + if (!hdmi.custom_set) { + DSSDBG("Read EDID as no EDID is not set on poweron\n"); + hdmi_read_edid(p); + } + code = get_timings_index(); + dssdev->panel.timings = cea_vesa_timings[code].timings; + update_hdmi_timings(&hdmi.cfg, p, code); + + phy = p->pixel_clock; + + hdmi_compute_pll(dssdev, phy, &pll_data); + + hdmi_wp_video_start(0); + + /* config the PLL and PHY first */ + r = hdmi_pll_program(&pll_data); + if (r) { + DSSDBG("Failed to lock PLL\n"); + goto err; + } + + r = hdmi_phy_init(); + if (r) { + DSSDBG("Failed to start PHY\n"); + goto err; + } + + hdmi.cfg.cm.mode = hdmi.mode; + hdmi.cfg.cm.code = hdmi.code; + hdmi_basic_configure(&hdmi.cfg); + + /* Make selection of HDMI in DSS */ + dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); + + /* Select the dispc clock source as PRCM clock, to ensure that it is not + * DSI PLL source as the clock selected by DSI PLL might not be + * sufficient for the resolution selected / that can be changed + * dynamically by user. This can be moved to single location , say + * Boardfile. + */ + dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src); + + /* bypass TV gamma table */ + dispc_enable_gamma_table(0); + + /* tv size */ + dispc_set_digit_size(dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res); + + dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 1); + + hdmi_wp_video_start(1); + + return 0; +err: + hdmi_runtime_put(); + return -EIO; +} + +static void hdmi_power_off(struct omap_dss_device *dssdev) +{ + dispc_enable_channel(OMAP_DSS_CHANNEL_DIGIT, 0); + + hdmi_wp_video_start(0); + hdmi_phy_off(); + hdmi_set_pll_pwr(HDMI_PLLPWRCMD_ALLOFF); + hdmi_runtime_put(); + + hdmi.edid_set = 0; +} + +int omapdss_hdmi_display_check_timing(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct hdmi_cm cm; + + cm = hdmi_get_code(timings); + if (cm.code == -1) { + DSSERR("Invalid timing entered\n"); + return -EINVAL; + } + + return 0; + +} + +void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev) +{ + struct hdmi_cm cm; + + hdmi.custom_set = 1; + cm = hdmi_get_code(&dssdev->panel.timings); + hdmi.code = cm.code; + hdmi.mode = cm.mode; + omapdss_hdmi_display_enable(dssdev); + hdmi.custom_set = 0; +} + +int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + + DSSDBG("ENTER hdmi_display_enable\n"); + + mutex_lock(&hdmi.lock); + + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + + if (dssdev->platform_enable) { + r = dssdev->platform_enable(dssdev); + if (r) { + DSSERR("failed to enable GPIO's\n"); + goto err1; + } + } + + r = hdmi_power_on(dssdev); + if (r) { + DSSERR("failed to power on device\n"); + goto err2; + } + + mutex_unlock(&hdmi.lock); + return 0; + +err2: + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); +err1: + omap_dss_stop_device(dssdev); +err0: + mutex_unlock(&hdmi.lock); + return r; +} + +void omapdss_hdmi_display_disable(struct omap_dss_device *dssdev) +{ + DSSDBG("Enter hdmi_display_disable\n"); + + mutex_lock(&hdmi.lock); + + hdmi_power_off(dssdev); + + if (dssdev->platform_disable) + dssdev->platform_disable(dssdev); + + omap_dss_stop_device(dssdev); + + mutex_unlock(&hdmi.lock); +} + +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) +static void hdmi_wp_audio_config_format( + struct hdmi_audio_format *aud_fmt) +{ + u32 r; + + DSSDBG("Enter hdmi_wp_audio_config_format\n"); + + r = hdmi_read_reg(HDMI_WP_AUDIO_CFG); + r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); + r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); + r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); + r = FLD_MOD(r, aud_fmt->type, 4, 4); + r = FLD_MOD(r, aud_fmt->justification, 3, 3); + r = FLD_MOD(r, aud_fmt->sample_order, 2, 2); + r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1); + r = FLD_MOD(r, aud_fmt->sample_size, 0, 0); + hdmi_write_reg(HDMI_WP_AUDIO_CFG, r); +} + +static void hdmi_wp_audio_config_dma(struct hdmi_audio_dma *aud_dma) +{ + u32 r; + + DSSDBG("Enter hdmi_wp_audio_config_dma\n"); + + r = hdmi_read_reg(HDMI_WP_AUDIO_CFG2); + r = FLD_MOD(r, aud_dma->transfer_size, 15, 8); + r = FLD_MOD(r, aud_dma->block_size, 7, 0); + hdmi_write_reg(HDMI_WP_AUDIO_CFG2, r); + + r = hdmi_read_reg(HDMI_WP_AUDIO_CTRL); + r = FLD_MOD(r, aud_dma->mode, 9, 9); + r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0); + hdmi_write_reg(HDMI_WP_AUDIO_CTRL, r); +} + +static void hdmi_core_audio_config(struct hdmi_core_audio_config *cfg) +{ + u32 r; + + /* audio clock recovery parameters */ + r = hdmi_read_reg(HDMI_CORE_AV_ACR_CTRL); + r = FLD_MOD(r, cfg->use_mclk, 2, 2); + r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1); + r = FLD_MOD(r, cfg->cts_mode, 0, 0); + hdmi_write_reg(HDMI_CORE_AV_ACR_CTRL, r); + + REG_FLD_MOD(HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0); + + if (cfg->cts_mode == HDMI_AUDIO_CTS_MODE_SW) { + REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL1, cfg->cts, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL2, cfg->cts >> 8, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0); + } else { + /* + * HDMI IP uses this configuration to divide the MCLK to + * update CTS value. + */ + REG_FLD_MOD(HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0); + + /* Configure clock for audio packets */ + REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_1, + cfg->aud_par_busclk, 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_2, + (cfg->aud_par_busclk >> 8), 7, 0); + REG_FLD_MOD(HDMI_CORE_AV_AUD_PAR_BUSCLK_3, + (cfg->aud_par_busclk >> 16), 7, 0); + } + + /* Override of SPDIF sample frequency with value in I2S_CHST4 */ + REG_FLD_MOD(HDMI_CORE_AV_SPDIF_CTRL, cfg->fs_override, 1, 1); + + /* I2S parameters */ + REG_FLD_MOD(HDMI_CORE_AV_I2S_CHST4, cfg->freq_sample, 3, 0); + + r = hdmi_read_reg(HDMI_CORE_AV_I2S_IN_CTRL); + r = FLD_MOD(r, cfg->i2s_cfg.en_high_bitrate_aud, 7, 7); + r = FLD_MOD(r, cfg->i2s_cfg.sck_edge_mode, 6, 6); + r = FLD_MOD(r, cfg->i2s_cfg.cbit_order, 5, 5); + r = FLD_MOD(r, cfg->i2s_cfg.vbit, 4, 4); + r = FLD_MOD(r, cfg->i2s_cfg.ws_polarity, 3, 3); + r = FLD_MOD(r, cfg->i2s_cfg.justification, 2, 2); + r = FLD_MOD(r, cfg->i2s_cfg.direction, 1, 1); + r = FLD_MOD(r, cfg->i2s_cfg.shift, 0, 0); + hdmi_write_reg(HDMI_CORE_AV_I2S_IN_CTRL, r); + + r = hdmi_read_reg(HDMI_CORE_AV_I2S_CHST5); + r = FLD_MOD(r, cfg->freq_sample, 7, 4); + r = FLD_MOD(r, cfg->i2s_cfg.word_length, 3, 1); + r = FLD_MOD(r, cfg->i2s_cfg.word_max_length, 0, 0); + hdmi_write_reg(HDMI_CORE_AV_I2S_CHST5, r); + + REG_FLD_MOD(HDMI_CORE_AV_I2S_IN_LEN, cfg->i2s_cfg.in_length_bits, 3, 0); + + /* Audio channels and mode parameters */ + REG_FLD_MOD(HDMI_CORE_AV_HDMI_CTRL, cfg->layout, 2, 1); + r = hdmi_read_reg(HDMI_CORE_AV_AUD_MODE); + r = FLD_MOD(r, cfg->i2s_cfg.active_sds, 7, 4); + r = FLD_MOD(r, cfg->en_dsd_audio, 3, 3); + r = FLD_MOD(r, cfg->en_parallel_aud_input, 2, 2); + r = FLD_MOD(r, cfg->en_spdif, 1, 1); + hdmi_write_reg(HDMI_CORE_AV_AUD_MODE, r); +} + +static void hdmi_core_audio_infoframe_config( + struct hdmi_core_infoframe_audio *info_aud) +{ + u8 val; + u8 sum = 0, checksum = 0; + + /* + * Set audio info frame type, version and length as + * described in HDMI 1.4a Section 8.2.2 specification. + * Checksum calculation is defined in Section 5.3.5. + */ + hdmi_write_reg(HDMI_CORE_AV_AUDIO_TYPE, 0x84); + hdmi_write_reg(HDMI_CORE_AV_AUDIO_VERS, 0x01); + hdmi_write_reg(HDMI_CORE_AV_AUDIO_LEN, 0x0a); + sum += 0x84 + 0x001 + 0x00a; + + val = (info_aud->db1_coding_type << 4) + | (info_aud->db1_channel_count - 1); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(0), val); + sum += val; + + val = (info_aud->db2_sample_freq << 2) | info_aud->db2_sample_size; + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(1), val); + sum += val; + + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(2), 0x00); + + val = info_aud->db4_channel_alloc; + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(3), val); + sum += val; + + val = (info_aud->db5_downmix_inh << 7) | (info_aud->db5_lsv << 3); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(4), val); + sum += val; + + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(5), 0x00); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(6), 0x00); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(7), 0x00); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(8), 0x00); + hdmi_write_reg(HDMI_CORE_AV_AUD_DBYTE(9), 0x00); + + checksum = 0x100 - sum; + hdmi_write_reg(HDMI_CORE_AV_AUDIO_CHSUM, checksum); + + /* + * TODO: Add MPEG and SPD enable and repeat cfg when EDID parsing + * is available. + */ +} + +static int hdmi_config_audio_acr(u32 sample_freq, u32 *n, u32 *cts) +{ + u32 r; + u32 deep_color = 0; + u32 pclk = hdmi.cfg.timings.timings.pixel_clock; + + if (n == NULL || cts == NULL) + return -EINVAL; + /* + * Obtain current deep color configuration. This needed + * to calculate the TMDS clock based on the pixel clock. + */ + r = REG_GET(HDMI_WP_VIDEO_CFG, 1, 0); + switch (r) { + case 1: /* No deep color selected */ + deep_color = 100; + break; + case 2: /* 10-bit deep color selected */ + deep_color = 125; + break; + case 3: /* 12-bit deep color selected */ + deep_color = 150; + break; + default: + return -EINVAL; + } + + switch (sample_freq) { + case 32000: + if ((deep_color == 125) && ((pclk == 54054) + || (pclk == 74250))) + *n = 8192; + else + *n = 4096; + break; + case 44100: + *n = 6272; + break; + case 48000: + if ((deep_color == 125) && ((pclk == 54054) + || (pclk == 74250))) + *n = 8192; + else + *n = 6144; + break; + default: + *n = 0; + return -EINVAL; + } + + /* Calculate CTS. See HDMI 1.3a or 1.4a specifications */ + *cts = pclk * (*n / 128) * deep_color / (sample_freq / 10); + + return 0; +} + +static int hdmi_audio_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdmi_audio_format audio_format; + struct hdmi_audio_dma audio_dma; + struct hdmi_core_audio_config core_cfg; + struct hdmi_core_infoframe_audio aud_if_cfg; + int err, n, cts; + enum hdmi_core_audio_sample_freq sample_freq; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + core_cfg.i2s_cfg.word_max_length = + HDMI_AUDIO_I2S_MAX_WORD_20BITS; + core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_16_BITS; + core_cfg.i2s_cfg.in_length_bits = + HDMI_AUDIO_I2S_INPUT_LENGTH_16; + core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_LEFT; + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_TWOSAMPLES; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_16BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_LEFT; + audio_dma.transfer_size = 0x10; + break; + case SNDRV_PCM_FORMAT_S24_LE: + core_cfg.i2s_cfg.word_max_length = + HDMI_AUDIO_I2S_MAX_WORD_24BITS; + core_cfg.i2s_cfg.word_length = HDMI_AUDIO_I2S_CHST_WORD_24_BITS; + core_cfg.i2s_cfg.in_length_bits = + HDMI_AUDIO_I2S_INPUT_LENGTH_24; + audio_format.samples_per_word = HDMI_AUDIO_ONEWORD_ONESAMPLE; + audio_format.sample_size = HDMI_AUDIO_SAMPLE_24BITS; + audio_format.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + core_cfg.i2s_cfg.justification = HDMI_AUDIO_JUSTIFY_RIGHT; + audio_dma.transfer_size = 0x20; + break; + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 32000: + sample_freq = HDMI_AUDIO_FS_32000; + break; + case 44100: + sample_freq = HDMI_AUDIO_FS_44100; + break; + case 48000: + sample_freq = HDMI_AUDIO_FS_48000; + break; + default: + return -EINVAL; + } + + err = hdmi_config_audio_acr(params_rate(params), &n, &cts); + if (err < 0) + return err; + + /* Audio wrapper config */ + audio_format.stereo_channels = HDMI_AUDIO_STEREO_ONECHANNEL; + audio_format.active_chnnls_msk = 0x03; + audio_format.type = HDMI_AUDIO_TYPE_LPCM; + audio_format.sample_order = HDMI_AUDIO_SAMPLE_LEFT_FIRST; + /* Disable start/stop signals of IEC 60958 blocks */ + audio_format.en_sig_blk_strt_end = HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF; + + audio_dma.block_size = 0xC0; + audio_dma.mode = HDMI_AUDIO_TRANSF_DMA; + audio_dma.fifo_threshold = 0x20; /* in number of samples */ + + hdmi_wp_audio_config_dma(&audio_dma); + hdmi_wp_audio_config_format(&audio_format); + + /* + * I2S config + */ + core_cfg.i2s_cfg.en_high_bitrate_aud = false; + /* Only used with high bitrate audio */ + core_cfg.i2s_cfg.cbit_order = false; + /* Serial data and word select should change on sck rising edge */ + core_cfg.i2s_cfg.sck_edge_mode = HDMI_AUDIO_I2S_SCK_EDGE_RISING; + core_cfg.i2s_cfg.vbit = HDMI_AUDIO_I2S_VBIT_FOR_PCM; + /* Set I2S word select polarity */ + core_cfg.i2s_cfg.ws_polarity = HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT; + core_cfg.i2s_cfg.direction = HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST; + /* Set serial data to word select shift. See Phillips spec. */ + core_cfg.i2s_cfg.shift = HDMI_AUDIO_I2S_FIRST_BIT_SHIFT; + /* Enable one of the four available serial data channels */ + core_cfg.i2s_cfg.active_sds = HDMI_AUDIO_I2S_SD0_EN; + + /* Core audio config */ + core_cfg.freq_sample = sample_freq; + core_cfg.n = n; + core_cfg.cts = cts; + if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) { + core_cfg.aud_par_busclk = 0; + core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW; + core_cfg.use_mclk = false; + } else { + core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8); + core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW; + core_cfg.use_mclk = true; + core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS; + } + core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH; + core_cfg.en_spdif = false; + /* Use sample frequency from channel status word */ + core_cfg.fs_override = true; + /* Enable ACR packets */ + core_cfg.en_acr_pkt = true; + /* Disable direct streaming digital audio */ + core_cfg.en_dsd_audio = false; + /* Use parallel audio interface */ + core_cfg.en_parallel_aud_input = true; + + hdmi_core_audio_config(&core_cfg); + + /* + * Configure packet + * info frame audio see doc CEA861-D page 74 + */ + aud_if_cfg.db1_coding_type = HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM; + aud_if_cfg.db1_channel_count = 2; + aud_if_cfg.db2_sample_freq = HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM; + aud_if_cfg.db2_sample_size = HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM; + aud_if_cfg.db4_channel_alloc = 0x00; + aud_if_cfg.db5_downmix_inh = false; + aud_if_cfg.db5_lsv = 0; + + hdmi_core_audio_infoframe_config(&aud_if_cfg); + return 0; +} + +static int hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int err = 0; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 1, 0, 0); + REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 31, 31); + REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 1, 30, 30); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + REG_FLD_MOD(HDMI_CORE_AV_AUD_MODE, 0, 0, 0); + REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 30, 30); + REG_FLD_MOD(HDMI_WP_AUDIO_CTRL, 0, 31, 31); + break; + default: + err = -EINVAL; + } + return err; +} + +static int hdmi_audio_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + if (!hdmi.mode) { + pr_err("Current video settings do not support audio.\n"); + return -EIO; + } + return 0; +} + +static struct snd_soc_codec_driver hdmi_audio_codec_drv = { +}; + +static struct snd_soc_dai_ops hdmi_audio_codec_ops = { + .hw_params = hdmi_audio_hw_params, + .trigger = hdmi_audio_trigger, + .startup = hdmi_audio_startup, +}; + +static struct snd_soc_dai_driver hdmi_codec_dai_drv = { + .name = "hdmi-audio-codec", + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &hdmi_audio_codec_ops, +}; +#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, "dss_48mhz_clk"); + if (IS_ERR(clk)) { + DSSERR("can't get hdmi_clk\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; + int r; + + hdmi.pdata = pdev->dev.platform_data; + hdmi.pdev = pdev; + + mutex_init(&hdmi.lock); + + hdmi_mem = platform_get_resource(hdmi.pdev, IORESOURCE_MEM, 0); + if (!hdmi_mem) { + DSSERR("can't get IORESOURCE_MEM HDMI\n"); + return -EINVAL; + } + + /* Base address taken from platform */ + hdmi.base_wp = ioremap(hdmi_mem->start, resource_size(hdmi_mem)); + if (!hdmi.base_wp) { + DSSERR("can't ioremap WP\n"); + 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 */ + r = snd_soc_register_codec(&pdev->dev, &hdmi_audio_codec_drv, + &hdmi_codec_dai_drv, 1); + if (r) { + DSSERR("can't register ASoC HDMI audio codec\n"); + return r; + } +#endif + return 0; +} + +static int omapdss_hdmihw_remove(struct platform_device *pdev) +{ + hdmi_panel_exit(); + +#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ + defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) + 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, + }, +}; + +int hdmi_init_platform_driver(void) +{ + return platform_driver_register(&omapdss_hdmihw_driver); +} + +void hdmi_uninit_platform_driver(void) +{ + return platform_driver_unregister(&omapdss_hdmihw_driver); +} diff --git a/drivers/video/omap2/dss/hdmi.h b/drivers/video/omap2/dss/hdmi.h new file mode 100644 index 00000000000..c885f9cb065 --- /dev/null +++ b/drivers/video/omap2/dss/hdmi.h @@ -0,0 +1,631 @@ +/* + * hdmi.h + * + * HDMI driver definition for TI OMAP4 processors. + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _OMAP4_DSS_HDMI_H_ +#define _OMAP4_DSS_HDMI_H_ + +#include <linux/string.h> +#include <video/omapdss.h> + +#define HDMI_WP 0x0 +#define HDMI_CORE_SYS 0x400 +#define HDMI_CORE_AV 0x900 +#define HDMI_PLLCTRL 0x200 +#define HDMI_PHY 0x300 + +struct hdmi_reg { u16 idx; }; + +#define HDMI_REG(idx) ((const struct hdmi_reg) { idx }) + +/* HDMI Wrapper */ +#define HDMI_WP_REG(idx) HDMI_REG(HDMI_WP + idx) + +#define HDMI_WP_REVISION HDMI_WP_REG(0x0) +#define HDMI_WP_SYSCONFIG HDMI_WP_REG(0x10) +#define HDMI_WP_IRQSTATUS_RAW HDMI_WP_REG(0x24) +#define HDMI_WP_IRQSTATUS HDMI_WP_REG(0x28) +#define HDMI_WP_PWR_CTRL HDMI_WP_REG(0x40) +#define HDMI_WP_IRQENABLE_SET HDMI_WP_REG(0x2C) +#define HDMI_WP_VIDEO_CFG HDMI_WP_REG(0x50) +#define HDMI_WP_VIDEO_SIZE HDMI_WP_REG(0x60) +#define HDMI_WP_VIDEO_TIMING_H HDMI_WP_REG(0x68) +#define HDMI_WP_VIDEO_TIMING_V HDMI_WP_REG(0x6C) +#define HDMI_WP_WP_CLK HDMI_WP_REG(0x70) +#define HDMI_WP_AUDIO_CFG HDMI_WP_REG(0x80) +#define HDMI_WP_AUDIO_CFG2 HDMI_WP_REG(0x84) +#define HDMI_WP_AUDIO_CTRL HDMI_WP_REG(0x88) +#define HDMI_WP_AUDIO_DATA HDMI_WP_REG(0x8C) + +/* HDMI IP Core System */ +#define HDMI_CORE_SYS_REG(idx) HDMI_REG(HDMI_CORE_SYS + idx) + +#define HDMI_CORE_SYS_VND_IDL HDMI_CORE_SYS_REG(0x0) +#define HDMI_CORE_SYS_DEV_IDL HDMI_CORE_SYS_REG(0x8) +#define HDMI_CORE_SYS_DEV_IDH HDMI_CORE_SYS_REG(0xC) +#define HDMI_CORE_SYS_DEV_REV HDMI_CORE_SYS_REG(0x10) +#define HDMI_CORE_SYS_SRST HDMI_CORE_SYS_REG(0x14) +#define HDMI_CORE_CTRL1 HDMI_CORE_SYS_REG(0x20) +#define HDMI_CORE_SYS_SYS_STAT HDMI_CORE_SYS_REG(0x24) +#define HDMI_CORE_SYS_VID_ACEN HDMI_CORE_SYS_REG(0x124) +#define HDMI_CORE_SYS_VID_MODE HDMI_CORE_SYS_REG(0x128) +#define HDMI_CORE_SYS_INTR_STATE HDMI_CORE_SYS_REG(0x1C0) +#define HDMI_CORE_SYS_INTR1 HDMI_CORE_SYS_REG(0x1C4) +#define HDMI_CORE_SYS_INTR2 HDMI_CORE_SYS_REG(0x1C8) +#define HDMI_CORE_SYS_INTR3 HDMI_CORE_SYS_REG(0x1CC) +#define HDMI_CORE_SYS_INTR4 HDMI_CORE_SYS_REG(0x1D0) +#define HDMI_CORE_SYS_UMASK1 HDMI_CORE_SYS_REG(0x1D4) +#define HDMI_CORE_SYS_TMDS_CTRL HDMI_CORE_SYS_REG(0x208) +#define HDMI_CORE_SYS_DE_DLY HDMI_CORE_SYS_REG(0xC8) +#define HDMI_CORE_SYS_DE_CTRL HDMI_CORE_SYS_REG(0xCC) +#define HDMI_CORE_SYS_DE_TOP HDMI_CORE_SYS_REG(0xD0) +#define HDMI_CORE_SYS_DE_CNTL HDMI_CORE_SYS_REG(0xD8) +#define HDMI_CORE_SYS_DE_CNTH HDMI_CORE_SYS_REG(0xDC) +#define HDMI_CORE_SYS_DE_LINL HDMI_CORE_SYS_REG(0xE0) +#define HDMI_CORE_SYS_DE_LINH_1 HDMI_CORE_SYS_REG(0xE4) +#define HDMI_CORE_CTRL1_VEN_FOLLOWVSYNC 0x1 +#define HDMI_CORE_CTRL1_HEN_FOLLOWHSYNC 0x1 +#define HDMI_CORE_CTRL1_BSEL_24BITBUS 0x1 +#define HDMI_CORE_CTRL1_EDGE_RISINGEDGE 0x1 + +/* HDMI DDC E-DID */ +#define HDMI_CORE_DDC_CMD HDMI_CORE_SYS_REG(0x3CC) +#define HDMI_CORE_DDC_STATUS HDMI_CORE_SYS_REG(0x3C8) +#define HDMI_CORE_DDC_ADDR HDMI_CORE_SYS_REG(0x3B4) +#define HDMI_CORE_DDC_OFFSET HDMI_CORE_SYS_REG(0x3BC) +#define HDMI_CORE_DDC_COUNT1 HDMI_CORE_SYS_REG(0x3C0) +#define HDMI_CORE_DDC_COUNT2 HDMI_CORE_SYS_REG(0x3C4) +#define HDMI_CORE_DDC_DATA HDMI_CORE_SYS_REG(0x3D0) +#define HDMI_CORE_DDC_SEGM HDMI_CORE_SYS_REG(0x3B8) + +/* HDMI IP Core Audio Video */ +#define HDMI_CORE_AV_REG(idx) HDMI_REG(HDMI_CORE_AV + idx) + +#define HDMI_CORE_AV_HDMI_CTRL HDMI_CORE_AV_REG(0xBC) +#define HDMI_CORE_AV_DPD HDMI_CORE_AV_REG(0xF4) +#define HDMI_CORE_AV_PB_CTRL1 HDMI_CORE_AV_REG(0xF8) +#define HDMI_CORE_AV_PB_CTRL2 HDMI_CORE_AV_REG(0xFC) +#define HDMI_CORE_AV_AVI_TYPE HDMI_CORE_AV_REG(0x100) +#define HDMI_CORE_AV_AVI_VERS HDMI_CORE_AV_REG(0x104) +#define HDMI_CORE_AV_AVI_LEN HDMI_CORE_AV_REG(0x108) +#define HDMI_CORE_AV_AVI_CHSUM HDMI_CORE_AV_REG(0x10C) +#define HDMI_CORE_AV_AVI_DBYTE(n) HDMI_CORE_AV_REG(n * 4 + 0x110) +#define HDMI_CORE_AV_AVI_DBYTE_NELEMS HDMI_CORE_AV_REG(15) +#define HDMI_CORE_AV_SPD_DBYTE HDMI_CORE_AV_REG(0x190) +#define HDMI_CORE_AV_SPD_DBYTE_NELEMS HDMI_CORE_AV_REG(27) +#define HDMI_CORE_AV_AUD_DBYTE(n) HDMI_CORE_AV_REG(n * 4 + 0x210) +#define HDMI_CORE_AV_AUD_DBYTE_NELEMS HDMI_CORE_AV_REG(10) +#define HDMI_CORE_AV_MPEG_DBYTE HDMI_CORE_AV_REG(0x290) +#define HDMI_CORE_AV_MPEG_DBYTE_NELEMS HDMI_CORE_AV_REG(27) +#define HDMI_CORE_AV_GEN_DBYTE HDMI_CORE_AV_REG(0x300) +#define HDMI_CORE_AV_GEN_DBYTE_NELEMS HDMI_CORE_AV_REG(31) +#define HDMI_CORE_AV_GEN2_DBYTE HDMI_CORE_AV_REG(0x380) +#define HDMI_CORE_AV_GEN2_DBYTE_NELEMS HDMI_CORE_AV_REG(31) +#define HDMI_CORE_AV_ACR_CTRL HDMI_CORE_AV_REG(0x4) +#define HDMI_CORE_AV_FREQ_SVAL HDMI_CORE_AV_REG(0x8) +#define HDMI_CORE_AV_N_SVAL1 HDMI_CORE_AV_REG(0xC) +#define HDMI_CORE_AV_N_SVAL2 HDMI_CORE_AV_REG(0x10) +#define HDMI_CORE_AV_N_SVAL3 HDMI_CORE_AV_REG(0x14) +#define HDMI_CORE_AV_CTS_SVAL1 HDMI_CORE_AV_REG(0x18) +#define HDMI_CORE_AV_CTS_SVAL2 HDMI_CORE_AV_REG(0x1C) +#define HDMI_CORE_AV_CTS_SVAL3 HDMI_CORE_AV_REG(0x20) +#define HDMI_CORE_AV_CTS_HVAL1 HDMI_CORE_AV_REG(0x24) +#define HDMI_CORE_AV_CTS_HVAL2 HDMI_CORE_AV_REG(0x28) +#define HDMI_CORE_AV_CTS_HVAL3 HDMI_CORE_AV_REG(0x2C) +#define HDMI_CORE_AV_AUD_MODE HDMI_CORE_AV_REG(0x50) +#define HDMI_CORE_AV_SPDIF_CTRL HDMI_CORE_AV_REG(0x54) +#define HDMI_CORE_AV_HW_SPDIF_FS HDMI_CORE_AV_REG(0x60) +#define HDMI_CORE_AV_SWAP_I2S HDMI_CORE_AV_REG(0x64) +#define HDMI_CORE_AV_SPDIF_ERTH HDMI_CORE_AV_REG(0x6C) +#define HDMI_CORE_AV_I2S_IN_MAP HDMI_CORE_AV_REG(0x70) +#define HDMI_CORE_AV_I2S_IN_CTRL HDMI_CORE_AV_REG(0x74) +#define HDMI_CORE_AV_I2S_CHST0 HDMI_CORE_AV_REG(0x78) +#define HDMI_CORE_AV_I2S_CHST1 HDMI_CORE_AV_REG(0x7C) +#define HDMI_CORE_AV_I2S_CHST2 HDMI_CORE_AV_REG(0x80) +#define HDMI_CORE_AV_I2S_CHST4 HDMI_CORE_AV_REG(0x84) +#define HDMI_CORE_AV_I2S_CHST5 HDMI_CORE_AV_REG(0x88) +#define HDMI_CORE_AV_ASRC HDMI_CORE_AV_REG(0x8C) +#define HDMI_CORE_AV_I2S_IN_LEN HDMI_CORE_AV_REG(0x90) +#define HDMI_CORE_AV_HDMI_CTRL HDMI_CORE_AV_REG(0xBC) +#define HDMI_CORE_AV_AUDO_TXSTAT HDMI_CORE_AV_REG(0xC0) +#define HDMI_CORE_AV_AUD_PAR_BUSCLK_1 HDMI_CORE_AV_REG(0xCC) +#define HDMI_CORE_AV_AUD_PAR_BUSCLK_2 HDMI_CORE_AV_REG(0xD0) +#define HDMI_CORE_AV_AUD_PAR_BUSCLK_3 HDMI_CORE_AV_REG(0xD4) +#define HDMI_CORE_AV_TEST_TXCTRL HDMI_CORE_AV_REG(0xF0) +#define HDMI_CORE_AV_DPD HDMI_CORE_AV_REG(0xF4) +#define HDMI_CORE_AV_PB_CTRL1 HDMI_CORE_AV_REG(0xF8) +#define HDMI_CORE_AV_PB_CTRL2 HDMI_CORE_AV_REG(0xFC) +#define HDMI_CORE_AV_AVI_TYPE HDMI_CORE_AV_REG(0x100) +#define HDMI_CORE_AV_AVI_VERS HDMI_CORE_AV_REG(0x104) +#define HDMI_CORE_AV_AVI_LEN HDMI_CORE_AV_REG(0x108) +#define HDMI_CORE_AV_AVI_CHSUM HDMI_CORE_AV_REG(0x10C) +#define HDMI_CORE_AV_SPD_TYPE HDMI_CORE_AV_REG(0x180) +#define HDMI_CORE_AV_SPD_VERS HDMI_CORE_AV_REG(0x184) +#define HDMI_CORE_AV_SPD_LEN HDMI_CORE_AV_REG(0x188) +#define HDMI_CORE_AV_SPD_CHSUM HDMI_CORE_AV_REG(0x18C) +#define HDMI_CORE_AV_AUDIO_TYPE HDMI_CORE_AV_REG(0x200) +#define HDMI_CORE_AV_AUDIO_VERS HDMI_CORE_AV_REG(0x204) +#define HDMI_CORE_AV_AUDIO_LEN HDMI_CORE_AV_REG(0x208) +#define HDMI_CORE_AV_AUDIO_CHSUM HDMI_CORE_AV_REG(0x20C) +#define HDMI_CORE_AV_MPEG_TYPE HDMI_CORE_AV_REG(0x280) +#define HDMI_CORE_AV_MPEG_VERS HDMI_CORE_AV_REG(0x284) +#define HDMI_CORE_AV_MPEG_LEN HDMI_CORE_AV_REG(0x288) +#define HDMI_CORE_AV_MPEG_CHSUM HDMI_CORE_AV_REG(0x28C) +#define HDMI_CORE_AV_CP_BYTE1 HDMI_CORE_AV_REG(0x37C) +#define HDMI_CORE_AV_CEC_ADDR_ID HDMI_CORE_AV_REG(0x3FC) +#define HDMI_CORE_AV_SPD_DBYTE_ELSIZE 0x4 +#define HDMI_CORE_AV_GEN2_DBYTE_ELSIZE 0x4 +#define HDMI_CORE_AV_MPEG_DBYTE_ELSIZE 0x4 +#define HDMI_CORE_AV_GEN_DBYTE_ELSIZE 0x4 + +/* PLL */ +#define HDMI_PLL_REG(idx) HDMI_REG(HDMI_PLLCTRL + idx) + +#define PLLCTRL_PLL_CONTROL HDMI_PLL_REG(0x0) +#define PLLCTRL_PLL_STATUS HDMI_PLL_REG(0x4) +#define PLLCTRL_PLL_GO HDMI_PLL_REG(0x8) +#define PLLCTRL_CFG1 HDMI_PLL_REG(0xC) +#define PLLCTRL_CFG2 HDMI_PLL_REG(0x10) +#define PLLCTRL_CFG3 HDMI_PLL_REG(0x14) +#define PLLCTRL_CFG4 HDMI_PLL_REG(0x20) + +/* HDMI PHY */ +#define HDMI_PHY_REG(idx) HDMI_REG(HDMI_PHY + idx) + +#define HDMI_TXPHY_TX_CTRL HDMI_PHY_REG(0x0) +#define HDMI_TXPHY_DIGITAL_CTRL HDMI_PHY_REG(0x4) +#define HDMI_TXPHY_POWER_CTRL HDMI_PHY_REG(0x8) +#define HDMI_TXPHY_PAD_CFG_CTRL HDMI_PHY_REG(0xC) + +/* HDMI EDID Length */ +#define HDMI_EDID_MAX_LENGTH 256 +#define EDID_TIMING_DESCRIPTOR_SIZE 0x12 +#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36 +#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80 +#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 +#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 + +#define OMAP_HDMI_TIMINGS_NB 34 + +#define REG_FLD_MOD(idx, val, start, end) \ + hdmi_write_reg(idx, FLD_MOD(hdmi_read_reg(idx), val, start, end)) +#define REG_GET(idx, start, end) \ + FLD_GET(hdmi_read_reg(idx), start, end) + +/* HDMI timing structure */ +struct hdmi_timings { + struct omap_video_timings timings; + int vsync_pol; + int hsync_pol; +}; + +enum hdmi_phy_pwr { + HDMI_PHYPWRCMD_OFF = 0, + HDMI_PHYPWRCMD_LDOON = 1, + HDMI_PHYPWRCMD_TXON = 2 +}; + +enum hdmi_pll_pwr { + HDMI_PLLPWRCMD_ALLOFF = 0, + HDMI_PLLPWRCMD_PLLONLY = 1, + HDMI_PLLPWRCMD_BOTHON_ALLCLKS = 2, + HDMI_PLLPWRCMD_BOTHON_NOPHYCLK = 3 +}; + +enum hdmi_clk_refsel { + HDMI_REFSEL_PCLK = 0, + HDMI_REFSEL_REF1 = 1, + HDMI_REFSEL_REF2 = 2, + HDMI_REFSEL_SYSCLK = 3 +}; + +enum hdmi_core_inputbus_width { + HDMI_INPUT_8BIT = 0, + HDMI_INPUT_10BIT = 1, + HDMI_INPUT_12BIT = 2 +}; + +enum hdmi_core_dither_trunc { + HDMI_OUTPUTTRUNCATION_8BIT = 0, + HDMI_OUTPUTTRUNCATION_10BIT = 1, + HDMI_OUTPUTTRUNCATION_12BIT = 2, + HDMI_OUTPUTDITHER_8BIT = 3, + HDMI_OUTPUTDITHER_10BIT = 4, + HDMI_OUTPUTDITHER_12BIT = 5 +}; + +enum hdmi_core_deepcolor_ed { + HDMI_DEEPCOLORPACKECTDISABLE = 0, + HDMI_DEEPCOLORPACKECTENABLE = 1 +}; + +enum hdmi_core_packet_mode { + HDMI_PACKETMODERESERVEDVALUE = 0, + HDMI_PACKETMODE24BITPERPIXEL = 4, + HDMI_PACKETMODE30BITPERPIXEL = 5, + HDMI_PACKETMODE36BITPERPIXEL = 6, + HDMI_PACKETMODE48BITPERPIXEL = 7 +}; + +enum hdmi_core_hdmi_dvi { + HDMI_DVI = 0, + HDMI_HDMI = 1 +}; + +enum hdmi_core_tclkselclkmult { + HDMI_FPLL05IDCK = 0, + HDMI_FPLL10IDCK = 1, + HDMI_FPLL20IDCK = 2, + HDMI_FPLL40IDCK = 3 +}; + +enum hdmi_core_packet_ctrl { + HDMI_PACKETENABLE = 1, + HDMI_PACKETDISABLE = 0, + HDMI_PACKETREPEATON = 1, + HDMI_PACKETREPEATOFF = 0 +}; + +/* INFOFRAME_AVI_ and INFOFRAME_AUDIO_ definitions */ +enum hdmi_core_infoframe { + HDMI_INFOFRAME_AVI_DB1Y_RGB = 0, + HDMI_INFOFRAME_AVI_DB1Y_YUV422 = 1, + HDMI_INFOFRAME_AVI_DB1Y_YUV444 = 2, + HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_OFF = 0, + HDMI_INFOFRAME_AVI_DB1A_ACTIVE_FORMAT_ON = 1, + HDMI_INFOFRAME_AVI_DB1B_NO = 0, + HDMI_INFOFRAME_AVI_DB1B_VERT = 1, + HDMI_INFOFRAME_AVI_DB1B_HORI = 2, + HDMI_INFOFRAME_AVI_DB1B_VERTHORI = 3, + HDMI_INFOFRAME_AVI_DB1S_0 = 0, + HDMI_INFOFRAME_AVI_DB1S_1 = 1, + HDMI_INFOFRAME_AVI_DB1S_2 = 2, + HDMI_INFOFRAME_AVI_DB2C_NO = 0, + HDMI_INFOFRAME_AVI_DB2C_ITU601 = 1, + HDMI_INFOFRAME_AVI_DB2C_ITU709 = 2, + HDMI_INFOFRAME_AVI_DB2C_EC_EXTENDED = 3, + HDMI_INFOFRAME_AVI_DB2M_NO = 0, + HDMI_INFOFRAME_AVI_DB2M_43 = 1, + HDMI_INFOFRAME_AVI_DB2M_169 = 2, + HDMI_INFOFRAME_AVI_DB2R_SAME = 8, + HDMI_INFOFRAME_AVI_DB2R_43 = 9, + HDMI_INFOFRAME_AVI_DB2R_169 = 10, + HDMI_INFOFRAME_AVI_DB2R_149 = 11, + HDMI_INFOFRAME_AVI_DB3ITC_NO = 0, + HDMI_INFOFRAME_AVI_DB3ITC_YES = 1, + HDMI_INFOFRAME_AVI_DB3EC_XVYUV601 = 0, + HDMI_INFOFRAME_AVI_DB3EC_XVYUV709 = 1, + HDMI_INFOFRAME_AVI_DB3Q_DEFAULT = 0, + HDMI_INFOFRAME_AVI_DB3Q_LR = 1, + HDMI_INFOFRAME_AVI_DB3Q_FR = 2, + HDMI_INFOFRAME_AVI_DB3SC_NO = 0, + HDMI_INFOFRAME_AVI_DB3SC_HORI = 1, + HDMI_INFOFRAME_AVI_DB3SC_VERT = 2, + HDMI_INFOFRAME_AVI_DB3SC_HORIVERT = 3, + HDMI_INFOFRAME_AVI_DB5PR_NO = 0, + HDMI_INFOFRAME_AVI_DB5PR_2 = 1, + HDMI_INFOFRAME_AVI_DB5PR_3 = 2, + HDMI_INFOFRAME_AVI_DB5PR_4 = 3, + HDMI_INFOFRAME_AVI_DB5PR_5 = 4, + HDMI_INFOFRAME_AVI_DB5PR_6 = 5, + HDMI_INFOFRAME_AVI_DB5PR_7 = 6, + HDMI_INFOFRAME_AVI_DB5PR_8 = 7, + HDMI_INFOFRAME_AVI_DB5PR_9 = 8, + HDMI_INFOFRAME_AVI_DB5PR_10 = 9, + HDMI_INFOFRAME_AUDIO_DB1CT_FROM_STREAM = 0, + HDMI_INFOFRAME_AUDIO_DB1CT_IEC60958 = 1, + HDMI_INFOFRAME_AUDIO_DB1CT_AC3 = 2, + HDMI_INFOFRAME_AUDIO_DB1CT_MPEG1 = 3, + HDMI_INFOFRAME_AUDIO_DB1CT_MP3 = 4, + HDMI_INFOFRAME_AUDIO_DB1CT_MPEG2_MULTICH = 5, + HDMI_INFOFRAME_AUDIO_DB1CT_AAC = 6, + HDMI_INFOFRAME_AUDIO_DB1CT_DTS = 7, + HDMI_INFOFRAME_AUDIO_DB1CT_ATRAC = 8, + HDMI_INFOFRAME_AUDIO_DB1CT_ONEBIT = 9, + HDMI_INFOFRAME_AUDIO_DB1CT_DOLBY_DIGITAL_PLUS = 10, + HDMI_INFOFRAME_AUDIO_DB1CT_DTS_HD = 11, + HDMI_INFOFRAME_AUDIO_DB1CT_MAT = 12, + HDMI_INFOFRAME_AUDIO_DB1CT_DST = 13, + HDMI_INFOFRAME_AUDIO_DB1CT_WMA_PRO = 14, + HDMI_INFOFRAME_AUDIO_DB2SF_FROM_STREAM = 0, + HDMI_INFOFRAME_AUDIO_DB2SF_32000 = 1, + HDMI_INFOFRAME_AUDIO_DB2SF_44100 = 2, + HDMI_INFOFRAME_AUDIO_DB2SF_48000 = 3, + HDMI_INFOFRAME_AUDIO_DB2SF_88200 = 4, + HDMI_INFOFRAME_AUDIO_DB2SF_96000 = 5, + HDMI_INFOFRAME_AUDIO_DB2SF_176400 = 6, + HDMI_INFOFRAME_AUDIO_DB2SF_192000 = 7, + HDMI_INFOFRAME_AUDIO_DB2SS_FROM_STREAM = 0, + HDMI_INFOFRAME_AUDIO_DB2SS_16BIT = 1, + HDMI_INFOFRAME_AUDIO_DB2SS_20BIT = 2, + HDMI_INFOFRAME_AUDIO_DB2SS_24BIT = 3, + HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PERMITTED = 0, + HDMI_INFOFRAME_AUDIO_DB5_DM_INH_PROHIBITED = 1 +}; + +enum hdmi_packing_mode { + HDMI_PACK_10b_RGB_YUV444 = 0, + HDMI_PACK_24b_RGB_YUV444_YUV422 = 1, + HDMI_PACK_20b_YUV422 = 2, + HDMI_PACK_ALREADYPACKED = 7 +}; + +enum hdmi_core_audio_sample_freq { + HDMI_AUDIO_FS_32000 = 0x3, + HDMI_AUDIO_FS_44100 = 0x0, + HDMI_AUDIO_FS_48000 = 0x2, + HDMI_AUDIO_FS_88200 = 0x8, + HDMI_AUDIO_FS_96000 = 0xA, + HDMI_AUDIO_FS_176400 = 0xC, + HDMI_AUDIO_FS_192000 = 0xE, + HDMI_AUDIO_FS_NOT_INDICATED = 0x1 +}; + +enum hdmi_core_audio_layout { + HDMI_AUDIO_LAYOUT_2CH = 0, + HDMI_AUDIO_LAYOUT_8CH = 1 +}; + +enum hdmi_core_cts_mode { + HDMI_AUDIO_CTS_MODE_HW = 0, + HDMI_AUDIO_CTS_MODE_SW = 1 +}; + +enum hdmi_stereo_channels { + HDMI_AUDIO_STEREO_NOCHANNELS = 0, + HDMI_AUDIO_STEREO_ONECHANNEL = 1, + HDMI_AUDIO_STEREO_TWOCHANNELS = 2, + HDMI_AUDIO_STEREO_THREECHANNELS = 3, + HDMI_AUDIO_STEREO_FOURCHANNELS = 4 +}; + +enum hdmi_audio_type { + HDMI_AUDIO_TYPE_LPCM = 0, + HDMI_AUDIO_TYPE_IEC = 1 +}; + +enum hdmi_audio_justify { + HDMI_AUDIO_JUSTIFY_LEFT = 0, + HDMI_AUDIO_JUSTIFY_RIGHT = 1 +}; + +enum hdmi_audio_sample_order { + HDMI_AUDIO_SAMPLE_RIGHT_FIRST = 0, + HDMI_AUDIO_SAMPLE_LEFT_FIRST = 1 +}; + +enum hdmi_audio_samples_perword { + HDMI_AUDIO_ONEWORD_ONESAMPLE = 0, + HDMI_AUDIO_ONEWORD_TWOSAMPLES = 1 +}; + +enum hdmi_audio_sample_size { + HDMI_AUDIO_SAMPLE_16BITS = 0, + HDMI_AUDIO_SAMPLE_24BITS = 1 +}; + +enum hdmi_audio_transf_mode { + HDMI_AUDIO_TRANSF_DMA = 0, + HDMI_AUDIO_TRANSF_IRQ = 1 +}; + +enum hdmi_audio_blk_strt_end_sig { + HDMI_AUDIO_BLOCK_SIG_STARTEND_ON = 0, + HDMI_AUDIO_BLOCK_SIG_STARTEND_OFF = 1 +}; + +enum hdmi_audio_i2s_config { + HDMI_AUDIO_I2S_WS_POLARITY_LOW_IS_LEFT = 0, + HDMI_AUDIO_I2S_WS_POLARIT_YLOW_IS_RIGHT = 1, + HDMI_AUDIO_I2S_MSB_SHIFTED_FIRST = 0, + HDMI_AUDIO_I2S_LSB_SHIFTED_FIRST = 1, + HDMI_AUDIO_I2S_MAX_WORD_20BITS = 0, + HDMI_AUDIO_I2S_MAX_WORD_24BITS = 1, + HDMI_AUDIO_I2S_CHST_WORD_NOT_SPECIFIED = 0, + HDMI_AUDIO_I2S_CHST_WORD_16_BITS = 1, + HDMI_AUDIO_I2S_CHST_WORD_17_BITS = 6, + HDMI_AUDIO_I2S_CHST_WORD_18_BITS = 2, + HDMI_AUDIO_I2S_CHST_WORD_19_BITS = 4, + HDMI_AUDIO_I2S_CHST_WORD_20_BITS_20MAX = 5, + HDMI_AUDIO_I2S_CHST_WORD_20_BITS_24MAX = 1, + HDMI_AUDIO_I2S_CHST_WORD_21_BITS = 6, + HDMI_AUDIO_I2S_CHST_WORD_22_BITS = 2, + HDMI_AUDIO_I2S_CHST_WORD_23_BITS = 4, + HDMI_AUDIO_I2S_CHST_WORD_24_BITS = 5, + HDMI_AUDIO_I2S_SCK_EDGE_FALLING = 0, + HDMI_AUDIO_I2S_SCK_EDGE_RISING = 1, + HDMI_AUDIO_I2S_VBIT_FOR_PCM = 0, + HDMI_AUDIO_I2S_VBIT_FOR_COMPRESSED = 1, + HDMI_AUDIO_I2S_INPUT_LENGTH_NA = 0, + HDMI_AUDIO_I2S_INPUT_LENGTH_16 = 2, + HDMI_AUDIO_I2S_INPUT_LENGTH_17 = 12, + HDMI_AUDIO_I2S_INPUT_LENGTH_18 = 4, + HDMI_AUDIO_I2S_INPUT_LENGTH_19 = 8, + HDMI_AUDIO_I2S_INPUT_LENGTH_20 = 10, + HDMI_AUDIO_I2S_INPUT_LENGTH_21 = 13, + HDMI_AUDIO_I2S_INPUT_LENGTH_22 = 5, + HDMI_AUDIO_I2S_INPUT_LENGTH_23 = 9, + HDMI_AUDIO_I2S_INPUT_LENGTH_24 = 11, + HDMI_AUDIO_I2S_FIRST_BIT_SHIFT = 0, + HDMI_AUDIO_I2S_FIRST_BIT_NO_SHIFT = 1, + HDMI_AUDIO_I2S_SD0_EN = 1, + HDMI_AUDIO_I2S_SD1_EN = 1 << 1, + HDMI_AUDIO_I2S_SD2_EN = 1 << 2, + HDMI_AUDIO_I2S_SD3_EN = 1 << 3, +}; + +enum hdmi_audio_mclk_mode { + HDMI_AUDIO_MCLK_128FS = 0, + HDMI_AUDIO_MCLK_256FS = 1, + HDMI_AUDIO_MCLK_384FS = 2, + HDMI_AUDIO_MCLK_512FS = 3, + HDMI_AUDIO_MCLK_768FS = 4, + HDMI_AUDIO_MCLK_1024FS = 5, + HDMI_AUDIO_MCLK_1152FS = 6, + HDMI_AUDIO_MCLK_192FS = 7 +}; + +struct hdmi_core_video_config { + enum hdmi_core_inputbus_width ip_bus_width; + enum hdmi_core_dither_trunc op_dither_truc; + enum hdmi_core_deepcolor_ed deep_color_pkt; + enum hdmi_core_packet_mode pkt_mode; + enum hdmi_core_hdmi_dvi hdmi_dvi; + enum hdmi_core_tclkselclkmult tclk_sel_clkmult; +}; + +/* + * Refer to section 8.2 in HDMI 1.3 specification for + * details about infoframe databytes + */ +struct hdmi_core_infoframe_avi { + u8 db1_format; + /* Y0, Y1 rgb,yCbCr */ + u8 db1_active_info; + /* A0 Active information Present */ + u8 db1_bar_info_dv; + /* B0, B1 Bar info data valid */ + u8 db1_scan_info; + /* S0, S1 scan information */ + u8 db2_colorimetry; + /* C0, C1 colorimetry */ + u8 db2_aspect_ratio; + /* M0, M1 Aspect ratio (4:3, 16:9) */ + u8 db2_active_fmt_ar; + /* R0...R3 Active format aspect ratio */ + u8 db3_itc; + /* ITC IT content. */ + u8 db3_ec; + /* EC0, EC1, EC2 Extended colorimetry */ + u8 db3_q_range; + /* Q1, Q0 Quantization range */ + u8 db3_nup_scaling; + /* SC1, SC0 Non-uniform picture scaling */ + u8 db4_videocode; + /* VIC0..6 Video format identification */ + u8 db5_pixel_repeat; + /* PR0..PR3 Pixel repetition factor */ + u16 db6_7_line_eoftop; + /* Line number end of top bar */ + u16 db8_9_line_sofbottom; + /* Line number start of bottom bar */ + u16 db10_11_pixel_eofleft; + /* Pixel number end of left bar */ + u16 db12_13_pixel_sofright; + /* Pixel number start of right bar */ +}; +/* + * Refer to section 8.2 in HDMI 1.3 specification for + * details about infoframe databytes + */ +struct hdmi_core_infoframe_audio { + u8 db1_coding_type; + u8 db1_channel_count; + u8 db2_sample_freq; + u8 db2_sample_size; + u8 db4_channel_alloc; + bool db5_downmix_inh; + u8 db5_lsv; /* Level shift values for downmix */ +}; + +struct hdmi_core_packet_enable_repeat { + u32 audio_pkt; + u32 audio_pkt_repeat; + u32 avi_infoframe; + u32 avi_infoframe_repeat; + u32 gen_cntrl_pkt; + u32 gen_cntrl_pkt_repeat; + u32 generic_pkt; + u32 generic_pkt_repeat; +}; + +struct hdmi_video_format { + enum hdmi_packing_mode packing_mode; + u32 y_res; /* Line per panel */ + u32 x_res; /* pixel per line */ +}; + +struct hdmi_video_interface { + int vsp; /* Vsync polarity */ + int hsp; /* Hsync polarity */ + int interlacing; + int tm; /* Timing mode */ +}; + +struct hdmi_cm { + int code; + int mode; +}; + +struct hdmi_config { + struct hdmi_timings timings; + u16 interlace; + struct hdmi_cm cm; +}; + +struct hdmi_audio_format { + enum hdmi_stereo_channels stereo_channels; + u8 active_chnnls_msk; + enum hdmi_audio_type type; + enum hdmi_audio_justify justification; + enum hdmi_audio_sample_order sample_order; + enum hdmi_audio_samples_perword samples_per_word; + enum hdmi_audio_sample_size sample_size; + enum hdmi_audio_blk_strt_end_sig en_sig_blk_strt_end; +}; + +struct hdmi_audio_dma { + u8 transfer_size; + u8 block_size; + enum hdmi_audio_transf_mode mode; + u16 fifo_threshold; +}; + +struct hdmi_core_audio_i2s_config { + u8 word_max_length; + u8 word_length; + u8 in_length_bits; + u8 justification; + u8 en_high_bitrate_aud; + u8 sck_edge_mode; + u8 cbit_order; + u8 vbit; + u8 ws_polarity; + u8 direction; + u8 shift; + u8 active_sds; +}; + +struct hdmi_core_audio_config { + struct hdmi_core_audio_i2s_config i2s_cfg; + enum hdmi_core_audio_sample_freq freq_sample; + bool fs_override; + u32 n; + u32 cts; + u32 aud_par_busclk; + enum hdmi_core_audio_layout layout; + enum hdmi_core_cts_mode cts_mode; + bool use_mclk; + enum hdmi_audio_mclk_mode mclk_mode; + bool en_acr_pkt; + bool en_dsd_audio; + bool en_parallel_aud_input; + bool en_spdif; +}; +#endif diff --git a/drivers/video/omap2/dss/hdmi_omap4_panel.c b/drivers/video/omap2/dss/hdmi_omap4_panel.c new file mode 100644 index 00000000000..7d4f2bd7c50 --- /dev/null +++ b/drivers/video/omap2/dss/hdmi_omap4_panel.c @@ -0,0 +1,222 @@ +/* + * hdmi_omap4_panel.c + * + * HDMI library support functions for TI OMAP4 processors. + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Authors: Mythri P k <mythripk@ti.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <video/omapdss.h> + +#include "dss.h" + +static struct { + struct mutex hdmi_lock; +} hdmi; + + +static int hdmi_panel_probe(struct omap_dss_device *dssdev) +{ + DSSDBG("ENTER hdmi_panel_probe\n"); + + dssdev->panel.config = OMAP_DSS_LCD_TFT | + OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; + + /* + * Initialize the timings to 640 * 480 + * This is only for framebuffer update not for TV timing setting + * Setting TV timing will be done only on enable + */ + dssdev->panel.timings.x_res = 640; + dssdev->panel.timings.y_res = 480; + + DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n", + dssdev->panel.timings.x_res, + dssdev->panel.timings.y_res); + return 0; +} + +static void hdmi_panel_remove(struct omap_dss_device *dssdev) +{ + +} + +static int hdmi_panel_enable(struct omap_dss_device *dssdev) +{ + int r = 0; + DSSDBG("ENTER hdmi_panel_enable\n"); + + mutex_lock(&hdmi.hdmi_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { + r = -EINVAL; + goto err; + } + + r = omapdss_hdmi_display_enable(dssdev); + if (r) { + DSSERR("failed to power on\n"); + goto err; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + +err: + mutex_unlock(&hdmi.hdmi_lock); + + return r; +} + +static void hdmi_panel_disable(struct omap_dss_device *dssdev) +{ + mutex_lock(&hdmi.hdmi_lock); + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + omapdss_hdmi_display_disable(dssdev); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + mutex_unlock(&hdmi.hdmi_lock); +} + +static int hdmi_panel_suspend(struct omap_dss_device *dssdev) +{ + int r = 0; + + mutex_lock(&hdmi.hdmi_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { + r = -EINVAL; + goto err; + } + + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; + + omapdss_hdmi_display_disable(dssdev); + +err: + mutex_unlock(&hdmi.hdmi_lock); + + return r; +} + +static int hdmi_panel_resume(struct omap_dss_device *dssdev) +{ + int r = 0; + + mutex_lock(&hdmi.hdmi_lock); + + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { + r = -EINVAL; + goto err; + } + + r = omapdss_hdmi_display_enable(dssdev); + if (r) { + DSSERR("failed to power on\n"); + goto err; + } + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + +err: + mutex_unlock(&hdmi.hdmi_lock); + + return r; +} + +static void hdmi_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + mutex_lock(&hdmi.hdmi_lock); + + *timings = dssdev->panel.timings; + + mutex_unlock(&hdmi.hdmi_lock); +} + +static void hdmi_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + DSSDBG("hdmi_set_timings\n"); + + mutex_lock(&hdmi.hdmi_lock); + + dssdev->panel.timings = *timings; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + /* turn the hdmi off and on to get new timings to use */ + omapdss_hdmi_display_disable(dssdev); + omapdss_hdmi_display_set_timing(dssdev); + } + + mutex_unlock(&hdmi.hdmi_lock); +} + +static int hdmi_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + int r = 0; + + DSSDBG("hdmi_check_timings\n"); + + mutex_lock(&hdmi.hdmi_lock); + + r = omapdss_hdmi_display_check_timing(dssdev, timings); + if (r) { + DSSERR("Timing cannot be applied\n"); + goto err; + } +err: + mutex_unlock(&hdmi.hdmi_lock); + return r; +} + +static struct omap_dss_driver hdmi_driver = { + .probe = hdmi_panel_probe, + .remove = hdmi_panel_remove, + .enable = hdmi_panel_enable, + .disable = hdmi_panel_disable, + .suspend = hdmi_panel_suspend, + .resume = hdmi_panel_resume, + .get_timings = hdmi_get_timings, + .set_timings = hdmi_set_timings, + .check_timings = hdmi_check_timings, + .driver = { + .name = "hdmi_panel", + .owner = THIS_MODULE, + }, +}; + +int hdmi_panel_init(void) +{ + mutex_init(&hdmi.hdmi_lock); + + omap_dss_register_driver(&hdmi_driver); + + return 0; +} + +void hdmi_panel_exit(void) +{ + omap_dss_unregister_driver(&hdmi_driver); + +} diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c index 172d4e69730..13d72d5c714 100644 --- a/drivers/video/omap2/dss/manager.c +++ b/drivers/video/omap2/dss/manager.c @@ -29,7 +29,7 @@ #include <linux/spinlock.h> #include <linux/jiffies.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" @@ -275,6 +275,108 @@ static ssize_t manager_alpha_blending_enabled_store( return size; } +static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.cpr_enable); +} + +static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + int v; + int r; + bool enable; + + if (!dss_has_feature(FEAT_CPR)) + return -ENODEV; + + r = kstrtoint(buf, 0, &v); + if (r) + return r; + + enable = !!v; + + mgr->get_manager_info(mgr, &info); + + if (info.cpr_enable == enable) + return size; + + info.cpr_enable = enable; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + +static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr, + char *buf) +{ + struct omap_overlay_manager_info info; + + mgr->get_manager_info(mgr, &info); + + return snprintf(buf, PAGE_SIZE, + "%d %d %d %d %d %d %d %d %d\n", + info.cpr_coefs.rr, + info.cpr_coefs.rg, + info.cpr_coefs.rb, + info.cpr_coefs.gr, + info.cpr_coefs.gg, + info.cpr_coefs.gb, + info.cpr_coefs.br, + info.cpr_coefs.bg, + info.cpr_coefs.bb); +} + +static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr, + const char *buf, size_t size) +{ + struct omap_overlay_manager_info info; + struct omap_dss_cpr_coefs coefs; + int r, i; + s16 *arr; + + if (!dss_has_feature(FEAT_CPR)) + return -ENODEV; + + if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd", + &coefs.rr, &coefs.rg, &coefs.rb, + &coefs.gr, &coefs.gg, &coefs.gb, + &coefs.br, &coefs.bg, &coefs.bb) != 9) + return -EINVAL; + + arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb, + coefs.gr, coefs.gg, coefs.gb, + coefs.br, coefs.bg, coefs.bb }; + + for (i = 0; i < 9; ++i) { + if (arr[i] < -512 || arr[i] > 511) + return -EINVAL; + } + + mgr->get_manager_info(mgr, &info); + + info.cpr_coefs = coefs; + + r = mgr->set_manager_info(mgr, &info); + if (r) + return r; + + r = mgr->apply(mgr); + if (r) + return r; + + return size; +} + struct manager_attribute { struct attribute attr; ssize_t (*show)(struct omap_overlay_manager *, char *); @@ -300,6 +402,12 @@ static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, manager_alpha_blending_enabled_show, manager_alpha_blending_enabled_store); +static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR, + manager_cpr_enable_show, + manager_cpr_enable_store); +static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR, + manager_cpr_coef_show, + manager_cpr_coef_store); static struct attribute *manager_sysfs_attrs[] = { @@ -310,6 +418,8 @@ static struct attribute *manager_sysfs_attrs[] = { &manager_attr_trans_key_value.attr, &manager_attr_trans_key_enabled.attr, &manager_attr_alpha_blending_enabled.attr, + &manager_attr_cpr_enable.attr, + &manager_attr_cpr_coef.attr, NULL }; @@ -391,32 +501,14 @@ struct overlay_cache_data { bool enabled; - u32 paddr; - void __iomem *vaddr; - u16 screen_width; - u16 width; - u16 height; - enum omap_color_mode color_mode; - u8 rotation; - enum omap_dss_rotation_type rotation_type; - bool mirror; - - u16 pos_x; - u16 pos_y; - u16 out_width; /* if 0, out_width == width */ - u16 out_height; /* if 0, out_height == height */ - u8 global_alpha; - u8 pre_mult_alpha; + struct omap_overlay_info info; enum omap_channel channel; bool replication; bool ilace; - enum omap_burst_size burst_size; u32 fifo_low; u32 fifo_high; - - bool manual_update; }; struct manager_cache_data { @@ -428,15 +520,8 @@ struct manager_cache_data { * VSYNC/EVSYNC */ bool shadow_dirty; - u32 default_color; - - enum omap_dss_trans_key_type trans_key_type; - u32 trans_key; - bool trans_enabled; - - bool alpha_enabled; + struct omap_overlay_manager_info info; - bool manual_upd_display; bool manual_update; bool do_manual_update; @@ -515,6 +600,8 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { irq = DISPC_IRQ_EVSYNC_ODD; + } else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) { + irq = DISPC_IRQ_EVSYNC_EVEN; } else { if (mgr->id == OMAP_DSS_CHANNEL_LCD) irq = DISPC_IRQ_VSYNC; @@ -536,23 +623,15 @@ static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) return 0; - if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) + return 0; + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC + || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; } else { - if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { - enum omap_dss_update_mode mode; - mode = dssdev->driver->get_update_mode(dssdev); - if (mode != OMAP_DSS_UPDATE_AUTO) - return 0; - - irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? - DISPC_IRQ_FRAMEDONE - : DISPC_IRQ_FRAMEDONE2; - } else { - irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? - DISPC_IRQ_VSYNC - : DISPC_IRQ_VSYNC2; - } + irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2; } mc = &dss_cache.manager_cache[mgr->id]; @@ -613,23 +692,15 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) if (!dssdev || dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) return 0; - if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) + return 0; + + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC + || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; } else { - if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { - enum omap_dss_update_mode mode; - mode = dssdev->driver->get_update_mode(dssdev); - if (mode != OMAP_DSS_UPDATE_AUTO) - return 0; - - irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? - DISPC_IRQ_FRAMEDONE - : DISPC_IRQ_FRAMEDONE2; - } else { - irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? - DISPC_IRQ_VSYNC - : DISPC_IRQ_VSYNC2; - } + irq = (dssdev->manager->id == OMAP_DSS_CHANNEL_LCD) ? + DISPC_IRQ_VSYNC : DISPC_IRQ_VSYNC2; } oc = &dss_cache.overlay_cache[ovl->id]; @@ -715,10 +786,12 @@ static bool rectangle_intersects(int x1, int y1, int w1, int h1, static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc) { - if (oc->out_width != 0 && oc->width != oc->out_width) + struct omap_overlay_info *oi = &oc->info; + + if (oi->out_width != 0 && oi->width != oi->out_width) return true; - if (oc->out_height != 0 && oc->height != oc->out_height) + if (oi->out_height != 0 && oi->height != oi->out_height) return true; return false; @@ -728,6 +801,8 @@ static int configure_overlay(enum omap_plane plane) { struct overlay_cache_data *c; struct manager_cache_data *mc; + struct omap_overlay_info *oi; + struct omap_overlay_manager_info *mi; u16 outw, outh; u16 x, y, w, h; u32 paddr; @@ -737,6 +812,7 @@ static int configure_overlay(enum omap_plane plane) DSSDBGF("%d", plane); c = &dss_cache.overlay_cache[plane]; + oi = &c->info; if (!c->enabled) { dispc_enable_plane(plane, 0); @@ -744,21 +820,22 @@ static int configure_overlay(enum omap_plane plane) } mc = &dss_cache.manager_cache[c->channel]; + mi = &mc->info; - x = c->pos_x; - y = c->pos_y; - w = c->width; - h = c->height; - outw = c->out_width == 0 ? c->width : c->out_width; - outh = c->out_height == 0 ? c->height : c->out_height; - paddr = c->paddr; + x = oi->pos_x; + y = oi->pos_y; + w = oi->width; + h = oi->height; + outw = oi->out_width == 0 ? oi->width : oi->out_width; + outh = oi->out_height == 0 ? oi->height : oi->out_height; + paddr = oi->paddr; orig_w = w; orig_h = h; orig_outw = outw; orig_outh = outh; - if (c->manual_update && mc->do_manual_update) { + if (mc->manual_update && mc->do_manual_update) { unsigned bpp; unsigned scale_x_m = w, scale_x_d = outw; unsigned scale_y_m = h, scale_y_d = outh; @@ -770,11 +847,18 @@ static int configure_overlay(enum omap_plane plane) return 0; } - switch (c->color_mode) { + switch (oi->color_mode) { + case OMAP_DSS_COLOR_NV12: + bpp = 8; + break; case OMAP_DSS_COLOR_RGB16: case OMAP_DSS_COLOR_ARGB16: case OMAP_DSS_COLOR_YUV2: case OMAP_DSS_COLOR_UYVY: + case OMAP_DSS_COLOR_RGBA16: + case OMAP_DSS_COLOR_RGBX16: + case OMAP_DSS_COLOR_ARGB16_1555: + case OMAP_DSS_COLOR_XRGB16_1555: bpp = 16; break; @@ -793,23 +877,23 @@ static int configure_overlay(enum omap_plane plane) BUG(); } - if (mc->x > c->pos_x) { + if (mc->x > oi->pos_x) { x = 0; - outw -= (mc->x - c->pos_x); - paddr += (mc->x - c->pos_x) * + outw -= (mc->x - oi->pos_x); + paddr += (mc->x - oi->pos_x) * scale_x_m / scale_x_d * bpp / 8; } else { - x = c->pos_x - mc->x; + x = oi->pos_x - mc->x; } - if (mc->y > c->pos_y) { + if (mc->y > oi->pos_y) { y = 0; - outh -= (mc->y - c->pos_y); - paddr += (mc->y - c->pos_y) * + outh -= (mc->y - oi->pos_y); + paddr += (mc->y - oi->pos_y) * scale_y_m / scale_y_d * - c->screen_width * bpp / 8; + oi->screen_width * bpp / 8; } else { - y = c->pos_y - mc->y; + y = oi->pos_y - mc->y; } if (mc->w < (x + outw)) @@ -828,8 +912,8 @@ static int configure_overlay(enum omap_plane plane) * the width if the original width was bigger. */ if ((w & 1) && - (c->color_mode == OMAP_DSS_COLOR_YUV2 || - c->color_mode == OMAP_DSS_COLOR_UYVY)) { + (oi->color_mode == OMAP_DSS_COLOR_YUV2 || + oi->color_mode == OMAP_DSS_COLOR_UYVY)) { if (orig_w > w) w += 1; else @@ -839,18 +923,19 @@ static int configure_overlay(enum omap_plane plane) r = dispc_setup_plane(plane, paddr, - c->screen_width, + oi->screen_width, x, y, w, h, outw, outh, - c->color_mode, + oi->color_mode, c->ilace, - c->rotation_type, - c->rotation, - c->mirror, - c->global_alpha, - c->pre_mult_alpha, - c->channel); + oi->rotation_type, + oi->rotation, + oi->mirror, + oi->global_alpha, + oi->pre_mult_alpha, + c->channel, + oi->p_uv_addr); if (r) { /* this shouldn't happen */ @@ -861,8 +946,7 @@ static int configure_overlay(enum omap_plane plane) dispc_enable_replication(plane, c->replication); - dispc_set_burst_size(plane, c->burst_size); - dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); + dispc_set_fifo_threshold(plane, c->fifo_low, c->fifo_high); dispc_enable_plane(plane, 1); @@ -871,16 +955,21 @@ static int configure_overlay(enum omap_plane plane) static void configure_manager(enum omap_channel channel) { - struct manager_cache_data *c; + struct omap_overlay_manager_info *mi; DSSDBGF("%d", channel); - c = &dss_cache.manager_cache[channel]; + /* picking info from the cache */ + mi = &dss_cache.manager_cache[channel].info; - dispc_set_default_color(channel, c->default_color); - dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); - dispc_enable_trans_key(channel, c->trans_enabled); - dispc_enable_alpha_blending(channel, c->alpha_enabled); + dispc_set_default_color(channel, mi->default_color); + dispc_set_trans_key(channel, mi->trans_key_type, mi->trans_key); + dispc_enable_trans_key(channel, mi->trans_enabled); + dispc_enable_alpha_blending(channel, mi->alpha_enabled); + if (dss_has_feature(FEAT_CPR)) { + dispc_enable_cpr(channel, mi->cpr_enable); + dispc_set_cpr_coef(channel, &mi->cpr_coefs); + } } /* configure_dispc() tries to write values from cache to shadow registers. @@ -915,7 +1004,7 @@ static int configure_dispc(void) if (!oc->dirty) continue; - if (oc->manual_update && !mc->do_manual_update) + if (mc->manual_update && !mc->do_manual_update) continue; if (mgr_busy[oc->channel]) { @@ -963,7 +1052,7 @@ static int configure_dispc(void) /* We don't need GO with manual update display. LCD iface will * always be turned off after frame, and new settings will be * taken in to use at next update */ - if (!mc->manual_upd_display) + if (!mc->manual_update) dispc_go(i); } @@ -998,6 +1087,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, { struct overlay_cache_data *oc; struct manager_cache_data *mc; + struct omap_overlay_info *oi; const int num_ovls = dss_feat_get_num_ovls(); struct omap_overlay_manager *mgr; int i; @@ -1040,6 +1130,7 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, unsigned outw, outh; oc = &dss_cache.overlay_cache[i]; + oi = &oc->info; if (oc->channel != mgr->id) continue; @@ -1055,39 +1146,39 @@ void dss_setup_partial_planes(struct omap_dss_device *dssdev, if (!dispc_is_overlay_scaled(oc)) continue; - outw = oc->out_width == 0 ? - oc->width : oc->out_width; - outh = oc->out_height == 0 ? - oc->height : oc->out_height; + outw = oi->out_width == 0 ? + oi->width : oi->out_width; + outh = oi->out_height == 0 ? + oi->height : oi->out_height; /* is the overlay outside the update region? */ if (!rectangle_intersects(x, y, w, h, - oc->pos_x, oc->pos_y, + oi->pos_x, oi->pos_y, outw, outh)) continue; /* if the overlay totally inside the update region? */ - if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, + if (rectangle_subset(oi->pos_x, oi->pos_y, outw, outh, x, y, w, h)) continue; - if (x > oc->pos_x) - x1 = oc->pos_x; + if (x > oi->pos_x) + x1 = oi->pos_x; else x1 = x; - if (y > oc->pos_y) - y1 = oc->pos_y; + if (y > oi->pos_y) + y1 = oi->pos_y; else y1 = y; - if ((x + w) < (oc->pos_x + outw)) - x2 = oc->pos_x + outw; + if ((x + w) < (oi->pos_x + outw)) + x2 = oi->pos_x + outw; else x2 = x + w; - if ((y + h) < (oc->pos_y + outh)) - y2 = oc->pos_y + outh; + if ((y + h) < (oi->pos_y + outh)) + y2 = oi->pos_y + outh; else y2 = y + h; @@ -1223,6 +1314,10 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); + r = dispc_runtime_get(); + if (r) + return r; + spin_lock_irqsave(&dss_cache.lock, flags); /* Configure overlays */ @@ -1262,22 +1357,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) ovl->info_dirty = false; oc->dirty = true; - - oc->paddr = ovl->info.paddr; - oc->vaddr = ovl->info.vaddr; - oc->screen_width = ovl->info.screen_width; - oc->width = ovl->info.width; - oc->height = ovl->info.height; - oc->color_mode = ovl->info.color_mode; - oc->rotation = ovl->info.rotation; - oc->rotation_type = ovl->info.rotation_type; - oc->mirror = ovl->info.mirror; - oc->pos_x = ovl->info.pos_x; - oc->pos_y = ovl->info.pos_y; - oc->out_width = ovl->info.out_width; - oc->out_height = ovl->info.out_height; - oc->global_alpha = ovl->info.global_alpha; - oc->pre_mult_alpha = ovl->info.pre_mult_alpha; + oc->info = ovl->info; oc->replication = dss_use_replication(dssdev, ovl->info.color_mode); @@ -1288,11 +1368,6 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) oc->enabled = true; - oc->manual_update = - dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && - dssdev->driver->get_update_mode(dssdev) != - OMAP_DSS_UPDATE_AUTO; - ++num_planes_enabled; } @@ -1320,20 +1395,10 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) mgr->info_dirty = false; mc->dirty = true; - - mc->default_color = mgr->info.default_color; - mc->trans_key_type = mgr->info.trans_key_type; - mc->trans_key = mgr->info.trans_key; - mc->trans_enabled = mgr->info.trans_enabled; - mc->alpha_enabled = mgr->info.alpha_enabled; - - mc->manual_upd_display = - dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; + mc->info = mgr->info; mc->manual_update = - dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && - dssdev->driver->get_update_mode(dssdev) != - OMAP_DSS_UPDATE_AUTO; + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; } /* XXX TODO: Try to get fifomerge working. The problem is that it @@ -1354,7 +1419,7 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) /* Configure overlay fifos */ for (i = 0; i < omap_dss_get_num_overlays(); ++i) { struct omap_dss_device *dssdev; - u32 size; + u32 size, burst_size; ovl = omap_dss_get_overlay(i); @@ -1372,19 +1437,22 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) if (use_fifomerge) size *= 3; + burst_size = dispc_get_burst_size(ovl->id); + switch (dssdev->type) { case OMAP_DISPLAY_TYPE_DPI: case OMAP_DISPLAY_TYPE_DBI: case OMAP_DISPLAY_TYPE_SDI: case OMAP_DISPLAY_TYPE_VENC: + case OMAP_DISPLAY_TYPE_HDMI: default_get_overlay_fifo_thresholds(ovl->id, size, - &oc->burst_size, &oc->fifo_low, + burst_size, &oc->fifo_low, &oc->fifo_high); break; #ifdef CONFIG_OMAP2_DSS_DSI case OMAP_DISPLAY_TYPE_DSI: dsi_get_overlay_fifo_thresholds(ovl->id, size, - &oc->burst_size, &oc->fifo_low, + burst_size, &oc->fifo_low, &oc->fifo_high); break; #endif @@ -1394,7 +1462,6 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) } r = 0; - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); if (!dss_cache.irq_enabled) { u32 mask; @@ -1407,10 +1474,11 @@ static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) dss_cache.irq_enabled = true; } configure_dispc(); - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); spin_unlock_irqrestore(&dss_cache.lock, flags); + dispc_runtime_put(); + return r; } diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c index 456efef03c2..c84380c53c3 100644 --- a/drivers/video/omap2/dss/overlay.c +++ b/drivers/video/omap2/dss/overlay.c @@ -31,7 +31,7 @@ #include <linux/delay.h> #include <linux/slab.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" @@ -84,32 +84,42 @@ static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, old_mgr = ovl->manager; + r = dispc_runtime_get(); + if (r) + return r; + /* detach old manager */ if (old_mgr) { r = ovl->unset_manager(ovl); if (r) { DSSERR("detach failed\n"); - return r; + goto err; } r = old_mgr->apply(old_mgr); if (r) - return r; + goto err; } if (mgr) { r = ovl->set_manager(ovl, mgr); if (r) { DSSERR("Failed to attach overlay\n"); - return r; + goto err; } r = mgr->apply(mgr); if (r) - return r; + goto err; } + dispc_runtime_put(); + return size; + +err: + dispc_runtime_put(); + return r; } static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) @@ -201,12 +211,16 @@ static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, size_t size) { - int r; + int r, enable; struct omap_overlay_info info; ovl->get_overlay_info(ovl, &info); - info.enabled = simple_strtoul(buf, NULL, 10); + r = kstrtoint(buf, 0, &enable); + if (r) + return r; + + info.enabled = !!enable; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -231,8 +245,16 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, const char *buf, size_t size) { int r; + u8 alpha; struct omap_overlay_info info; + if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) + return -ENODEV; + + r = kstrtou8(buf, 0, &alpha); + if (r) + return r; + ovl->get_overlay_info(ovl, &info); /* Video1 plane does not support global alpha @@ -242,7 +264,7 @@ static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, ovl->id == OMAP_DSS_VIDEO1) info.global_alpha = 255; else - info.global_alpha = simple_strtoul(buf, NULL, 10); + info.global_alpha = alpha; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -268,8 +290,13 @@ static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl, const char *buf, size_t size) { int r; + u8 alpha; struct omap_overlay_info info; + r = kstrtou8(buf, 0, &alpha); + if (r) + return r; + ovl->get_overlay_info(ovl, &info); /* only GFX and Video2 plane support pre alpha multiplied @@ -279,7 +306,7 @@ static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl, ovl->id == OMAP_DSS_VIDEO1) info.pre_mult_alpha = 0; else - info.pre_mult_alpha = simple_strtoul(buf, NULL, 10); + info.pre_mult_alpha = alpha; r = ovl->set_overlay_info(ovl, &info); if (r) @@ -490,16 +517,19 @@ static int omap_dss_set_manager(struct omap_overlay *ovl, ovl->manager = mgr; - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - /* XXX: on manual update display, in auto update mode, a bug happens - * here. When an overlay is first enabled on LCD, then it's disabled, - * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT - * errors. Waiting before changing the channel_out fixes it. I'm - * guessing that the overlay is still somehow being used for the LCD, - * but I don't understand how or why. */ - msleep(40); + /* XXX: When there is an overlay on a DSI manual update display, and + * the overlay is first disabled, then moved to tv, and enabled, we + * seem to get SYNC_LOST_DIGIT error. + * + * Waiting doesn't seem to help, but updating the manual update display + * after disabling the overlay seems to fix this. This hints that the + * overlay is perhaps somehow tied to the LCD output until the output + * is updated. + * + * Userspace workaround for this is to update the LCD after disabling + * the overlay, but before moving the overlay to TV. + */ dispc_set_channel_out(ovl->id, mgr->id); - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); return 0; } @@ -679,7 +709,8 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) lcd2_mgr->set_device(lcd2_mgr, dssdev); mgr = lcd2_mgr; } - } else if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) { + } else if (dssdev->type != OMAP_DISPLAY_TYPE_VENC + && dssdev->type != OMAP_DISPLAY_TYPE_HDMI) { if (!lcd_mgr->device || force) { if (lcd_mgr->device) lcd_mgr->unset_device(lcd_mgr); @@ -688,7 +719,8 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) } } - if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC + || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { if (!tv_mgr->device || force) { if (tv_mgr->device) tv_mgr->unset_device(tv_mgr); @@ -698,6 +730,8 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) } if (mgr) { + dispc_runtime_get(); + for (i = 0; i < dss_feat_get_num_ovls(); i++) { struct omap_overlay *ovl; ovl = omap_dss_get_overlay(i); @@ -707,6 +741,8 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) omap_dss_set_manager(ovl, mgr); } } + + dispc_runtime_put(); } } diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index 10a2ffe0288..39f4c597026 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c @@ -32,12 +32,13 @@ #include <linux/ktime.h> #include <linux/hrtimer.h> #include <linux/seq_file.h> +#include <linux/semaphore.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> -#include <plat/display.h> +#include <video/omapdss.h> #include "dss.h" -#define RFBI_BASE 0x48050800 - struct rfbi_reg { u16 idx; }; #define RFBI_REG(idx) ((const struct rfbi_reg) { idx }) @@ -67,9 +68,6 @@ struct rfbi_reg { u16 idx; }; #define REG_FLD_MOD(idx, val, start, end) \ rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end)) -/* To work around an RFBI transfer rate limitation */ -#define OMAP_RFBI_RATE_LIMIT 1 - enum omap_rfbi_cycleformat { OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0, OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1, @@ -91,15 +89,11 @@ enum omap_rfbi_parallelmode { OMAP_DSS_RFBI_PARALLELMODE_16 = 3, }; -enum update_cmd { - RFBI_CMD_UPDATE = 0, - RFBI_CMD_SYNC = 1, -}; - static int rfbi_convert_timings(struct rfbi_timings *t); static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div); static struct { + struct platform_device *pdev; void __iomem *base; unsigned long l4_khz; @@ -115,20 +109,9 @@ static struct { struct omap_dss_device *dssdev[2]; - struct kfifo cmd_fifo; - spinlock_t cmd_lock; - struct completion cmd_done; - atomic_t cmd_fifo_full; - atomic_t cmd_pending; + struct semaphore bus_lock; } rfbi; -struct update_region { - u16 x; - u16 y; - u16 w; - u16 h; -}; - static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val) { __raw_writel(val, rfbi.base + idx.idx); @@ -139,17 +122,41 @@ static inline u32 rfbi_read_reg(const struct rfbi_reg idx) return __raw_readl(rfbi.base + idx.idx); } -static void rfbi_enable_clocks(bool enable) +static int rfbi_runtime_get(void) { - if (enable) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); - else - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + int r; + + DSSDBG("rfbi_runtime_get\n"); + + r = pm_runtime_get_sync(&rfbi.pdev->dev); + WARN_ON(r < 0); + return r < 0 ? r : 0; } +static void rfbi_runtime_put(void) +{ + int r; + + DSSDBG("rfbi_runtime_put\n"); + + r = pm_runtime_put(&rfbi.pdev->dev); + WARN_ON(r < 0); +} + +void rfbi_bus_lock(void) +{ + down(&rfbi.bus_lock); +} +EXPORT_SYMBOL(rfbi_bus_lock); + +void rfbi_bus_unlock(void) +{ + up(&rfbi.bus_lock); +} +EXPORT_SYMBOL(rfbi_bus_unlock); + void omap_rfbi_write_command(const void *buf, u32 len) { - rfbi_enable_clocks(1); switch (rfbi.parallelmode) { case OMAP_DSS_RFBI_PARALLELMODE_8: { @@ -173,13 +180,11 @@ void omap_rfbi_write_command(const void *buf, u32 len) default: BUG(); } - rfbi_enable_clocks(0); } EXPORT_SYMBOL(omap_rfbi_write_command); void omap_rfbi_read_data(void *buf, u32 len) { - rfbi_enable_clocks(1); switch (rfbi.parallelmode) { case OMAP_DSS_RFBI_PARALLELMODE_8: { @@ -207,13 +212,11 @@ void omap_rfbi_read_data(void *buf, u32 len) default: BUG(); } - rfbi_enable_clocks(0); } EXPORT_SYMBOL(omap_rfbi_read_data); void omap_rfbi_write_data(const void *buf, u32 len) { - rfbi_enable_clocks(1); switch (rfbi.parallelmode) { case OMAP_DSS_RFBI_PARALLELMODE_8: { @@ -238,7 +241,6 @@ void omap_rfbi_write_data(const void *buf, u32 len) BUG(); } - rfbi_enable_clocks(0); } EXPORT_SYMBOL(omap_rfbi_write_data); @@ -250,8 +252,6 @@ void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, int horiz_offset = scr_width - w; int i; - rfbi_enable_clocks(1); - if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { const u16 __iomem *pd = buf; @@ -296,12 +296,10 @@ void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, } else { BUG(); } - - rfbi_enable_clocks(0); } EXPORT_SYMBOL(omap_rfbi_write_pixels); -void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, +static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, u16 height, void (*callback)(void *data), void *data) { u32 l; @@ -318,8 +316,6 @@ void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width, rfbi.framedone_callback = callback; rfbi.framedone_callback_data = data; - rfbi_enable_clocks(1); - rfbi_write_reg(RFBI_PIXEL_CNT, width * height); l = rfbi_read_reg(RFBI_CONTROL); @@ -338,15 +334,11 @@ static void framedone_callback(void *data, u32 mask) REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0); - rfbi_enable_clocks(0); - callback = rfbi.framedone_callback; rfbi.framedone_callback = NULL; if (callback != NULL) callback(rfbi.framedone_callback_data); - - atomic_set(&rfbi.cmd_pending, 0); } #if 1 /* VERBOSE */ @@ -436,7 +428,7 @@ static int calc_extif_timings(struct rfbi_timings *t) } -void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) +static void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) { int r; @@ -448,7 +440,6 @@ void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) BUG_ON(!t->converted); - rfbi_enable_clocks(1); rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]); rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]); @@ -457,7 +448,6 @@ void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) (t->tim[2] ? 1 : 0), 4, 4); rfbi_print_timings(); - rfbi_enable_clocks(0); } static int ps_to_rfbi_ticks(int time, int div) @@ -473,59 +463,6 @@ static int ps_to_rfbi_ticks(int time, int div) return ret; } -#ifdef OMAP_RFBI_RATE_LIMIT -unsigned long rfbi_get_max_tx_rate(void) -{ - unsigned long l4_rate, dss1_rate; - int min_l4_ticks = 0; - int i; - - /* According to TI this can't be calculated so make the - * adjustments for a couple of known frequencies and warn for - * others. - */ - static const struct { - unsigned long l4_clk; /* HZ */ - unsigned long dss1_clk; /* HZ */ - unsigned long min_l4_ticks; - } ftab[] = { - { 55, 132, 7, }, /* 7.86 MPix/s */ - { 110, 110, 12, }, /* 9.16 MPix/s */ - { 110, 132, 10, }, /* 11 Mpix/s */ - { 120, 120, 10, }, /* 12 Mpix/s */ - { 133, 133, 10, }, /* 13.3 Mpix/s */ - }; - - l4_rate = rfbi.l4_khz / 1000; - dss1_rate = dss_clk_get_rate(DSS_CLK_FCK1) / 1000000; - - for (i = 0; i < ARRAY_SIZE(ftab); i++) { - /* Use a window instead of an exact match, to account - * for different DPLL multiplier / divider pairs. - */ - if (abs(ftab[i].l4_clk - l4_rate) < 3 && - abs(ftab[i].dss1_clk - dss1_rate) < 3) { - min_l4_ticks = ftab[i].min_l4_ticks; - break; - } - } - if (i == ARRAY_SIZE(ftab)) { - /* Can't be sure, return anyway the maximum not - * rate-limited. This might cause a problem only for the - * tearing synchronisation. - */ - DSSERR("can't determine maximum RFBI transfer rate\n"); - return rfbi.l4_khz * 1000; - } - return rfbi.l4_khz * 1000 / min_l4_ticks; -} -#else -int rfbi_get_max_tx_rate(void) -{ - return rfbi.l4_khz * 1000; -} -#endif - static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) { *clk_period = 1000000000 / rfbi.l4_khz; @@ -645,7 +582,6 @@ int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n", mode, hs, vs, hs_pol_inv, vs_pol_inv); - rfbi_enable_clocks(1); rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); @@ -658,7 +594,6 @@ int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, l &= ~(1 << 20); else l |= 1 << 20; - rfbi_enable_clocks(0); return 0; } @@ -673,7 +608,6 @@ int omap_rfbi_enable_te(bool enable, unsigned line) if (line > (1 << 11) - 1) return -EINVAL; - rfbi_enable_clocks(1); l = rfbi_read_reg(RFBI_CONFIG(0)); l &= ~(0x3 << 2); if (enable) { @@ -683,50 +617,12 @@ int omap_rfbi_enable_te(bool enable, unsigned line) rfbi.te_enabled = 0; rfbi_write_reg(RFBI_CONFIG(0), l); rfbi_write_reg(RFBI_LINE_NUMBER, line); - rfbi_enable_clocks(0); return 0; } EXPORT_SYMBOL(omap_rfbi_enable_te); -#if 0 -static void rfbi_enable_config(int enable1, int enable2) -{ - u32 l; - int cs = 0; - - if (enable1) - cs |= 1<<0; - if (enable2) - cs |= 1<<1; - - rfbi_enable_clocks(1); - - l = rfbi_read_reg(RFBI_CONTROL); - - l = FLD_MOD(l, cs, 3, 2); - l = FLD_MOD(l, 0, 1, 1); - - rfbi_write_reg(RFBI_CONTROL, l); - - - l = rfbi_read_reg(RFBI_CONFIG(0)); - l = FLD_MOD(l, 0, 3, 2); /* TRIGGERMODE: ITE */ - /*l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ - /*l |= FLD_VAL(0, 8, 7); */ /* L4FORMAT, 1pix/L4 */ - - l = FLD_MOD(l, 0, 16, 16); /* A0POLARITY */ - l = FLD_MOD(l, 1, 20, 20); /* TE_VSYNC_POLARITY */ - l = FLD_MOD(l, 1, 21, 21); /* HSYNCPOLARITY */ - - l = FLD_MOD(l, OMAP_DSS_RFBI_PARALLELMODE_8, 1, 0); - rfbi_write_reg(RFBI_CONFIG(0), l); - - rfbi_enable_clocks(0); -} -#endif - -int rfbi_configure(int rfbi_module, int bpp, int lines) +static int rfbi_configure(int rfbi_module, int bpp, int lines) { u32 l; int cycle1 = 0, cycle2 = 0, cycle3 = 0; @@ -822,8 +718,6 @@ int rfbi_configure(int rfbi_module, int bpp, int lines) break; } - rfbi_enable_clocks(1); - REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */ l = 0; @@ -857,11 +751,15 @@ int rfbi_configure(int rfbi_module, int bpp, int lines) DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n", bpp, lines, cycle1, cycle2, cycle3); - rfbi_enable_clocks(0); - return 0; } -EXPORT_SYMBOL(rfbi_configure); + +int omap_rfbi_configure(struct omap_dss_device *dssdev, int pixel_size, + int data_lines) +{ + return rfbi_configure(dssdev->phy.rfbi.channel, pixel_size, data_lines); +} +EXPORT_SYMBOL(omap_rfbi_configure); int omap_rfbi_prepare_update(struct omap_dss_device *dssdev, u16 *x, u16 *y, u16 *w, u16 *h) @@ -922,7 +820,8 @@ void rfbi_dump_regs(struct seq_file *s) { #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + if (rfbi_runtime_get()) + return; DUMPREG(RFBI_REVISION); DUMPREG(RFBI_SYSCONFIG); @@ -953,58 +852,18 @@ void rfbi_dump_regs(struct seq_file *s) DUMPREG(RFBI_VSYNC_WIDTH); DUMPREG(RFBI_HSYNC_WIDTH); - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + rfbi_runtime_put(); #undef DUMPREG } -int rfbi_init(void) -{ - u32 rev; - u32 l; - - spin_lock_init(&rfbi.cmd_lock); - - init_completion(&rfbi.cmd_done); - atomic_set(&rfbi.cmd_fifo_full, 0); - atomic_set(&rfbi.cmd_pending, 0); - - rfbi.base = ioremap(RFBI_BASE, SZ_256); - if (!rfbi.base) { - DSSERR("can't ioremap RFBI\n"); - return -ENOMEM; - } - - rfbi_enable_clocks(1); - - msleep(10); - - rfbi.l4_khz = dss_clk_get_rate(DSS_CLK_ICK) / 1000; - - /* Enable autoidle and smart-idle */ - l = rfbi_read_reg(RFBI_SYSCONFIG); - l |= (1 << 0) | (2 << 3); - rfbi_write_reg(RFBI_SYSCONFIG, l); - - rev = rfbi_read_reg(RFBI_REVISION); - printk(KERN_INFO "OMAP RFBI rev %d.%d\n", - FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); - - rfbi_enable_clocks(0); - - return 0; -} - -void rfbi_exit(void) -{ - DSSDBG("rfbi_exit\n"); - - iounmap(rfbi.base); -} - int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) { int r; + r = rfbi_runtime_get(); + if (r) + return r; + r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); @@ -1038,6 +897,7 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev) err1: omap_dss_stop_device(dssdev); err0: + rfbi_runtime_put(); return r; } EXPORT_SYMBOL(omapdss_rfbi_display_enable); @@ -1047,6 +907,8 @@ void omapdss_rfbi_display_disable(struct omap_dss_device *dssdev) omap_dispc_unregister_isr(framedone_callback, NULL, DISPC_IRQ_FRAMEDONE); omap_dss_stop_device(dssdev); + + rfbi_runtime_put(); } EXPORT_SYMBOL(omapdss_rfbi_display_disable); @@ -1056,3 +918,127 @@ int rfbi_init_display(struct omap_dss_device *dssdev) dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; return 0; } + +/* RFBI HW IP initialisation */ +static int omap_rfbihw_probe(struct platform_device *pdev) +{ + u32 rev; + struct resource *rfbi_mem; + struct clk *clk; + int r; + + rfbi.pdev = pdev; + + sema_init(&rfbi.bus_lock, 1); + + rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0); + if (!rfbi_mem) { + DSSERR("can't get IORESOURCE_MEM RFBI\n"); + r = -EINVAL; + goto err_ioremap; + } + rfbi.base = ioremap(rfbi_mem->start, resource_size(rfbi_mem)); + if (!rfbi.base) { + DSSERR("can't ioremap RFBI\n"); + r = -ENOMEM; + goto err_ioremap; + } + + pm_runtime_enable(&pdev->dev); + + r = rfbi_runtime_get(); + if (r) + goto err_get_rfbi; + + msleep(10); + + if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap3630()) + clk = dss_get_ick(); + else + clk = clk_get(&pdev->dev, "ick"); + if (IS_ERR(clk)) { + DSSERR("can't get ick\n"); + r = PTR_ERR(clk); + goto err_get_ick; + } + + rfbi.l4_khz = clk_get_rate(clk) / 1000; + + clk_put(clk); + + rev = rfbi_read_reg(RFBI_REVISION); + dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n", + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); + + rfbi_runtime_put(); + + return 0; + +err_get_ick: + rfbi_runtime_put(); +err_get_rfbi: + pm_runtime_disable(&pdev->dev); + iounmap(rfbi.base); +err_ioremap: + return r; +} + +static int omap_rfbihw_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + iounmap(rfbi.base); + return 0; +} + +static int rfbi_runtime_suspend(struct device *dev) +{ + dispc_runtime_put(); + dss_runtime_put(); + + return 0; +} + +static int rfbi_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; + + return 0; + +err_get_dispc: + dss_runtime_put(); +err_get_dss: + return r; +} + +static const struct dev_pm_ops rfbi_pm_ops = { + .runtime_suspend = rfbi_runtime_suspend, + .runtime_resume = rfbi_runtime_resume, +}; + +static struct platform_driver omap_rfbihw_driver = { + .probe = omap_rfbihw_probe, + .remove = omap_rfbihw_remove, + .driver = { + .name = "omapdss_rfbi", + .owner = THIS_MODULE, + .pm = &rfbi_pm_ops, + }, +}; + +int rfbi_init_platform_driver(void) +{ + return platform_driver_register(&omap_rfbihw_driver); +} + +void rfbi_uninit_platform_driver(void) +{ + return platform_driver_unregister(&omap_rfbihw_driver); +} diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index b64adf7dfc8..3a688c871a4 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c @@ -20,17 +20,14 @@ #define DSS_SUBSYS_NAME "SDI" #include <linux/kernel.h> -#include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/regulator/consumer.h> -#include <plat/display.h> -#include <plat/cpu.h> +#include <video/omapdss.h> #include "dss.h" static struct { - bool skip_init; bool update_enabled; struct regulator *vdds_sdi_reg; } sdi; @@ -61,16 +58,20 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) r = omap_dss_start_device(dssdev); if (r) { DSSERR("failed to start device\n"); - goto err0; + goto err_start_dev; } r = regulator_enable(sdi.vdds_sdi_reg); if (r) - goto err1; + goto err_reg_enable; - /* In case of skip_init sdi_init has already enabled the clocks */ - if (!sdi.skip_init) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); + r = dss_runtime_get(); + if (r) + goto err_get_dss; + + r = dispc_runtime_get(); + if (r) + goto err_get_dispc; sdi_basic_init(dssdev); @@ -80,16 +81,10 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) dispc_set_pol_freq(dssdev->manager->id, dssdev->panel.config, dssdev->panel.acbi, dssdev->panel.acb); - if (!sdi.skip_init) { - r = dss_calc_clock_div(1, t->pixel_clock * 1000, - &dss_cinfo, &dispc_cinfo); - } else { - r = dss_get_clock_div(&dss_cinfo); - r = dispc_get_clock_div(dssdev->manager->id, &dispc_cinfo); - } - + r = dss_calc_clock_div(1, t->pixel_clock * 1000, + &dss_cinfo, &dispc_cinfo); if (r) - goto err2; + goto err_calc_clock_div; fck = dss_cinfo.fck; lck_div = dispc_cinfo.lck_div; @@ -110,31 +105,34 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) r = dss_set_clock_div(&dss_cinfo); if (r) - goto err2; + goto err_set_dss_clock_div; r = dispc_set_clock_div(dssdev->manager->id, &dispc_cinfo); if (r) - goto err2; - - if (!sdi.skip_init) { - dss_sdi_init(dssdev->phy.sdi.datapairs); - r = dss_sdi_enable(); - if (r) - goto err1; - mdelay(2); - } + goto err_set_dispc_clock_div; - dssdev->manager->enable(dssdev->manager); + dss_sdi_init(dssdev->phy.sdi.datapairs); + r = dss_sdi_enable(); + if (r) + goto err_sdi_enable; + mdelay(2); - sdi.skip_init = 0; + dssdev->manager->enable(dssdev->manager); return 0; -err2: - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + +err_sdi_enable: +err_set_dispc_clock_div: +err_set_dss_clock_div: +err_calc_clock_div: + dispc_runtime_put(); +err_get_dispc: + dss_runtime_put(); +err_get_dss: regulator_disable(sdi.vdds_sdi_reg); -err1: +err_reg_enable: omap_dss_stop_device(dssdev); -err0: +err_start_dev: return r; } EXPORT_SYMBOL(omapdss_sdi_display_enable); @@ -145,7 +143,8 @@ void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) dss_sdi_disable(); - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); + dispc_runtime_put(); + dss_runtime_put(); regulator_disable(sdi.vdds_sdi_reg); @@ -157,25 +156,24 @@ int sdi_init_display(struct omap_dss_device *dssdev) { DSSDBG("SDI init\n"); + if (sdi.vdds_sdi_reg == NULL) { + struct regulator *vdds_sdi; + + vdds_sdi = dss_get_vdds_sdi(); + + if (IS_ERR(vdds_sdi)) { + DSSERR("can't get VDDS_SDI regulator\n"); + return PTR_ERR(vdds_sdi); + } + + sdi.vdds_sdi_reg = vdds_sdi; + } + return 0; } -int sdi_init(bool skip_init) +int sdi_init(void) { - /* we store this for first display enable, then clear it */ - sdi.skip_init = skip_init; - - sdi.vdds_sdi_reg = dss_get_vdds_sdi(); - if (IS_ERR(sdi.vdds_sdi_reg)) { - DSSERR("can't get VDDS_SDI regulator\n"); - return PTR_ERR(sdi.vdds_sdi_reg); - } - /* - * Enable clocks already here, otherwise there would be a toggle - * of them until sdi_display_enable is called. - */ - if (skip_init) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); return 0; } diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index eff35050e28..173c66430da 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c @@ -33,13 +33,13 @@ #include <linux/seq_file.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> -#include <plat/display.h> +#include <video/omapdss.h> #include <plat/cpu.h> #include "dss.h" - -#define VENC_BASE 0x48050C00 +#include "dss_features.h" /* Venc registers */ #define VENC_REV_ID 0x00 @@ -289,10 +289,14 @@ const struct omap_video_timings omap_dss_ntsc_timings = { EXPORT_SYMBOL(omap_dss_ntsc_timings); static struct { + struct platform_device *pdev; void __iomem *base; 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) @@ -374,18 +378,32 @@ static void venc_reset(void) } } +#ifdef CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET /* the magical sleep that makes things work */ + /* XXX more info? What bug this circumvents? */ msleep(20); +#endif } -static void venc_enable_clocks(int enable) +static int venc_runtime_get(void) { - if (enable) - dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | - DSS_CLK_96M); - else - dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | - DSS_CLK_96M); + 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( @@ -404,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)); @@ -446,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); } @@ -474,21 +488,32 @@ static int venc_panel_enable(struct omap_dss_device *dssdev) mutex_lock(&venc.venc_lock); + r = omap_dss_start_device(dssdev); + if (r) { + DSSERR("failed to start device\n"); + goto err0; + } + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { r = -EINVAL; goto err1; } + r = venc_runtime_get(); + if (r) + goto err1; + venc_power_on(dssdev); venc.wss_data = 0; dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; - /* wait couple of vsyncs until enabling the LCD */ - msleep(50); - + mutex_unlock(&venc.venc_lock); + return 0; err1: + omap_dss_stop_device(dssdev); +err0: mutex_unlock(&venc.venc_lock); return r; @@ -511,10 +536,11 @@ static void venc_panel_disable(struct omap_dss_device *dssdev) venc_power_off(dssdev); - /* wait at least 5 vsyncs after disabling the LCD */ - msleep(100); + venc_runtime_put(); dssdev->state = OMAP_DSS_DISPLAY_DISABLED; + + omap_dss_stop_device(dssdev); end: mutex_unlock(&venc.venc_lock); } @@ -530,20 +556,6 @@ static int venc_panel_resume(struct omap_dss_device *dssdev) return venc_panel_enable(dssdev); } -static enum omap_dss_update_mode venc_get_update_mode( - struct omap_dss_device *dssdev) -{ - return OMAP_DSS_UPDATE_AUTO; -} - -static int venc_set_update_mode(struct omap_dss_device *dssdev, - enum omap_dss_update_mode mode) -{ - if (mode != OMAP_DSS_UPDATE_AUTO) - return -EINVAL; - return 0; -} - static void venc_get_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { @@ -590,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"); @@ -600,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 = { @@ -624,9 +640,6 @@ static struct omap_dss_driver venc_driver = { .get_resolution = omapdss_default_get_resolution, .get_recommended_bpp = omapdss_default_get_recommended_bpp, - .set_update_mode = venc_set_update_mode, - .get_update_mode = venc_get_update_mode, - .get_timings = venc_get_timings, .set_timings = venc_set_timings, .check_timings = venc_check_timings, @@ -641,50 +654,23 @@ static struct omap_dss_driver venc_driver = { }; /* driver end */ - - -int venc_init(struct platform_device *pdev) +int venc_init_display(struct omap_dss_device *dssdev) { - u8 rev_id; + DSSDBG("init_display\n"); - mutex_init(&venc.venc_lock); + if (venc.vdda_dac_reg == NULL) { + struct regulator *vdda_dac; - venc.wss_data = 0; + vdda_dac = regulator_get(&venc.pdev->dev, "vdda_dac"); - venc.base = ioremap(VENC_BASE, SZ_1K); - if (!venc.base) { - DSSERR("can't ioremap VENC\n"); - return -ENOMEM; - } + if (IS_ERR(vdda_dac)) { + DSSERR("can't get VDDA_DAC regulator\n"); + return PTR_ERR(vdda_dac); + } - venc.vdda_dac_reg = dss_get_vdda_dac(); - if (IS_ERR(venc.vdda_dac_reg)) { - iounmap(venc.base); - DSSERR("can't get VDDA_DAC regulator\n"); - return PTR_ERR(venc.vdda_dac_reg); + venc.vdda_dac_reg = vdda_dac; } - venc_enable_clocks(1); - - rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); - printk(KERN_INFO "OMAP VENC rev %d\n", rev_id); - - venc_enable_clocks(0); - - return omap_dss_register_driver(&venc_driver); -} - -void venc_exit(void) -{ - omap_dss_unregister_driver(&venc_driver); - - iounmap(venc.base); -} - -int venc_init_display(struct omap_dss_device *dssdev) -{ - DSSDBG("init_display\n"); - return 0; } @@ -692,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); @@ -736,7 +723,180 @@ 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)) { + if (cpu_is_omap34xx() || cpu_is_omap3630()) + clk = clk_get(&pdev->dev, "dss_96m_fck"); + else + 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; + + mutex_init(&venc.venc_lock); + + venc.wss_data = 0; + + venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0); + if (!venc_mem) { + DSSERR("can't get IORESOURCE_MEM VENC\n"); + r = -EINVAL; + goto err_ioremap; + } + venc.base = ioremap(venc_mem->start, resource_size(venc_mem)); + if (!venc.base) { + DSSERR("can't ioremap VENC\n"); + r = -ENOMEM; + goto err_ioremap; + } + + 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_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) +{ + if (venc.vdda_dac_reg != NULL) { + regulator_put(venc.vdda_dac_reg); + venc.vdda_dac_reg = NULL; + } + 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, + }, +}; + +int venc_init_platform_driver(void) +{ + if (cpu_is_omap44xx()) + return 0; + + return platform_driver_register(&omap_venchw_driver); +} + +void venc_uninit_platform_driver(void) +{ + if (cpu_is_omap44xx()) + return; + + return platform_driver_unregister(&omap_venchw_driver); +} |