Resetting DISPC when a DISPC output is enabled causes the DSS to go into an
inconsistent state. Thus if the bootloader has enabled a display, the hwmod code
cannot reset the DISPC module just like that, but the outputs need to be
disabled first.

Add function dispc_disable_outputs() which disables all active overlay manager
and ensure all frame transfers are completed.

Modify omap_dss_reset() to call this function and clear DSS_CONTROL,
DSS_SDI_CONTROL and DSS_PLL_CONTROL so that DSS is in a clean state when the
DSS2 driver starts.

This resolves the hang issue(caused by a L3 error during boot) seen on the
beagle board C3, which has a factory bootloader that enables display. The issue
is resolved with this patch.

Acked-by: Tomi Valkeinen <[email protected]>
Tested-by: R, Sricharan <[email protected]>
Signed-off-by: Archit Taneja <[email protected]>
---
v2:

- Added more info in the commit message, fixed some typos.
 
The patch depends on a HWMOD patch series which has been posted by Tomi, it can
be tested by applying over the following branch:

https://gitorious.org/linux-omap-dss2/linux/commits/master

 arch/arm/mach-omap2/display.c |  110 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 110 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c
index 93db7c1..eab81f4 100644
--- a/arch/arm/mach-omap2/display.c
+++ b/arch/arm/mach-omap2/display.c
@@ -30,6 +30,30 @@
 
 #include "control.h"
 
+#define DISPC_BASE_OMAP2       0x48050400
+#define DISPC_BASE_OMAP4       0x48041000
+
+#define DISPC_REG(base, offset)        (base + offset)
+
+#define DISPC_CONTROL          0x0040
+#define DISPC_CONTROL2         0x0238
+#define DISPC_IRQSTATUS                0x0018
+
+#define DSS_SYSCONFIG          0x10
+#define DSS_SYSSTATUS          0x14
+#define DSS_CONTROL            0x40
+#define DSS_SDI_CONTROL                0x44
+#define DSS_PLL_CONTROL                0x48
+
+#define LCD_EN_MASK            (0x1 << 0)
+#define DIGIT_EN_MASK          (0x1 << 1)
+
+#define FRAMEDONE_IRQ_SHIFT    0
+#define EVSYNC_EVEN_IRQ_SHIFT  2
+#define EVSYNC_ODD_IRQ_SHIFT   3
+#define FRAMEDONE2_IRQ_SHIFT   22
+#define FRAMEDONETV_IRQ_SHIFT  24
+
 static struct platform_device omap_display_device = {
        .name          = "omapdss",
        .id            = -1,
@@ -182,6 +206,78 @@ int __init omap_display_init(struct omap_dss_board_info 
*board_data)
        return r;
 }
 
+static void dispc_disable_outputs(void)
+{
+       u32 val, irq_mask, base;
+       bool lcd_en, digit_en, lcd2_en = false;
+       int i, num_mgrs;
+
+       if (cpu_is_omap44xx()) {
+               base = DISPC_BASE_OMAP4;
+               num_mgrs = 3;
+       } else {
+               base = DISPC_BASE_OMAP2;
+               num_mgrs = 2;
+       }
+
+       /* store value of LCDENABLE and DIGITENABLE bits */
+       val = omap_readl(DISPC_REG(base, DISPC_CONTROL));
+       lcd_en = val & LCD_EN_MASK;
+       digit_en = val & DIGIT_EN_MASK;
+
+       /* store value of LCDENABLE for LCD2 */
+       if (num_mgrs > 2) {
+               val = omap_readl(DISPC_REG(base, DISPC_CONTROL2));
+               lcd2_en = val & LCD_EN_MASK;
+       }
+
+       /*
+        * If any manager was enabled, we need to disable it before DSS clocks
+        * are disabled or DISPC module is reset
+        */
+       if (lcd_en || digit_en || lcd2_en) {
+               irq_mask = (lcd_en ? 1 : 0) << FRAMEDONE_IRQ_SHIFT;
+
+               if (cpu_is_omap44xx())
+                       irq_mask |= (digit_en ? 1 : 0) << FRAMEDONETV_IRQ_SHIFT;
+               else
+                       irq_mask |= (digit_en ? 1 : 0) << EVSYNC_EVEN_IRQ_SHIFT 
|
+                               (digit_en ? 1 : 0) << EVSYNC_ODD_IRQ_SHIFT;
+
+               irq_mask |= (lcd2_en ? 1 : 0) << FRAMEDONE2_IRQ_SHIFT;
+
+               /*
+                * clear any previous FRAMEDONE, FRAMEDONETV, EVSYNC_EVEN/ODD
+                * or FRAMEDONE2 interrupts
+                */
+               omap_writel(irq_mask, DISPC_REG(base, DISPC_IRQSTATUS));
+
+               /* disable LCD and TV managers */
+               val = omap_readl(DISPC_REG(base, DISPC_CONTROL));
+               val &= ~(LCD_EN_MASK | DIGIT_EN_MASK);
+               omap_writel(val, DISPC_REG(base, DISPC_CONTROL));
+
+               /* disable LCD2 manager */
+               if (num_mgrs > 2) {
+                       val = omap_readl(DISPC_REG(base, DISPC_CONTROL2));
+                       val &= ~LCD_EN_MASK;
+                       omap_writel(val, DISPC_REG(base, DISPC_CONTROL2));
+               }
+
+               i = 0;
+               while ((omap_readl(DISPC_REG(base, DISPC_IRQSTATUS)) & 
irq_mask) !=
+                               irq_mask) {
+                       i++;
+                       if (i > 100) {
+                               printk(KERN_ERR "didn't get FRAMEDONE1/2 or TV"
+                                       " interrupt\n");
+                               break;
+                       }
+                       mdelay(1);
+               }
+       }
+}
+
 #define MAX_MODULE_SOFTRESET_WAIT      10000
 int omap_dss_reset(struct omap_hwmod *oh)
 {
@@ -198,6 +294,20 @@ int omap_dss_reset(struct omap_hwmod *oh)
                if (oc->_clk)
                        clk_enable(oc->_clk);
 
+       dispc_disable_outputs();
+
+       /* clear SDI registers */
+       if (cpu_is_omap3430()) {
+               omap_hwmod_write(0x0, oh, DSS_SDI_CONTROL);
+               omap_hwmod_write(0x0, oh, DSS_PLL_CONTROL);
+       }
+
+       /*
+        * clear DSS_CONTROL register to switch DSS clock sources to
+        * PRCM clock, if any
+        */
+       omap_hwmod_write(0x0, oh, DSS_CONTROL);
+
        omap_test_timeout((omap_hwmod_read(oh, oh->class->sysc->syss_offs)
                                & SYSS_RESETDONE_MASK),
                        MAX_MODULE_SOFTRESET_WAIT, c);
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to