[PATCH v2] OMAP2PLUS: DSS: Ensure DSS works correctly if display is enabled in bootloader

Paul Walmsley paul at pwsan.com
Mon Oct 3 00:45:03 EDT 2011


Hi

some comments:

On Mon, 12 Sep 2011, Archit Taneja wrote:

> 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 <tomi.valkeinen at ti.com>
> Tested-by: R, Sricharan <r.sricharan at ti.com>
> Signed-off-by: Archit Taneja <archit at ti.com>
> ---
> 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

These should definitely not be needed -- they can be obtained from the 
hwmod data and there is clearly something wrong if any IP block addresses 
exist outside of those data files.

> +
> +#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;
> +	}

base should not be needed here.  The num_mgrs should come from the hwmod 
data.  We're trying to get rid of cpu_is_omap*() functions wherever 
possible.

> +
> +	/* store value of LCDENABLE and DIGITENABLE bits */
> +	val = omap_readl(DISPC_REG(base, DISPC_CONTROL));

omap_{read,write}l() are deprecated and should no longer be used.  This 
code can use omap_hwmod_{read,write}() instead.  You can pass the struct 
omap_hwmod * into this function from the caller.

> +	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) {

Rather than this massive if block, please test the inverse condition and 
bail out early.  This avoids unnecessary indentation levels that make code 
harder to read.

> +		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;

Rather than a cpu_is_omap*() test, the presence of a working FRAMEDONETV 
interrupt bit should be passed through the hwmod data.

> +
> +		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) {

Please hoist this constant up to the top of this file, and use a macro 
with a comment.

> +				printk(KERN_ERR "didn't get FRAMEDONE1/2 or TV"
> +					" interrupt\n");

pr_err() is shorter and better here.

> +				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();

Pass the struct omap_hwmod *oh in here.

> +
> +	/* 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

In the interest of expediency, I've made the above changes to the patch -- 
updated patch below.  The following Compile-tested only, so could you 
review it please and make sure I haven't broken anything?  For future 
patches, please keep the comments above in mind. 

thanks,


- Paul

From: Archit Taneja <archit at ti.com>
Date: Mon, 12 Sep 2011 12:38:26 +0530
Subject: [PATCH 1/2] ARM: OMAP2PLUS: DSS: Ensure DSS works correctly if
 display is enabled in bootloader

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 <tomi.valkeinen at ti.com>
Tested-by: R, Sricharan <r.sricharan at ti.com>
Signed-off-by: Archit Taneja <archit at ti.com>
[paul at pwsan.com: restructured code, removed omap_{read,write}l(), removed
 cpu_is_omap*() calls and converted to dev_attr]
Signed-off-by: Paul Walmsley <paul at pwsan.com>
---
 arch/arm/mach-omap2/display.c                |  118 ++++++++++++++++++++++++++
 arch/arm/mach-omap2/display.h                |   29 ++++++
 arch/arm/mach-omap2/omap_hwmod_2420_data.c   |    1 +
 arch/arm/mach-omap2/omap_hwmod_2430_data.c   |    1 +
 arch/arm/mach-omap2/omap_hwmod_3xxx_data.c   |    1 +
 arch/arm/mach-omap2/omap_hwmod_44xx_data.c   |    6 ++
 arch/arm/mach-omap2/omap_hwmod_common_data.c |    4 +
 arch/arm/mach-omap2/omap_hwmod_common_data.h |    4 +
 8 files changed, 164 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap2/display.h

diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c
index cdb675a..fcd2c3a 100644
--- a/arch/arm/mach-omap2/display.c
+++ b/arch/arm/mach-omap2/display.c
@@ -28,6 +28,33 @@
 #include <plat/omap-pm.h>
 #include <plat/common.h>
 
+#include "display.h"
+
+#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
+
+/*
+ * FRAMEDONE_IRQ_TIMEOUT: how long (in milliseconds) to wait during DISPC
+ *     reset before deciding that something has gone wrong
+ */
+#define FRAMEDONE_IRQ_TIMEOUT		100
+
 static struct platform_device omap_display_device = {
 	.name          = "omapdss",
 	.id            = -1,
@@ -128,6 +155,83 @@ int __init omap_display_init(struct omap_dss_board_info *board_data)
 	return r;
 }
 
+static void dispc_disable_outputs(struct omap_hwmod *oh)
+{
+	u32 v, irq_mask = 0;
+	bool lcd_en, digit_en, lcd2_en = false;
+	int i;
+	struct omap_dss_dispc_dev_attr *da;
+
+	if (!oh->dev_attr) {
+		pr_err("display: could not disable outputs during reset due to missing dev_attr\n");
+		return;
+	}
+
+	da = (struct omap_dss_dispc_dev_attr *)oh->dev_attr;
+
+	/* store value of LCDENABLE and DIGITENABLE bits */
+	v = omap_hwmod_read(oh, DISPC_CONTROL);
+	lcd_en = v & LCD_EN_MASK;
+	digit_en = v & DIGIT_EN_MASK;
+
+	/* store value of LCDENABLE for LCD2 */
+	if (da->manager_count > 2) {
+		v = omap_hwmod_read(oh, DISPC_CONTROL2);
+		lcd2_en = v & LCD_EN_MASK;
+	}
+
+	if (!(lcd_en | digit_en | lcd2_en))
+		return; /* no managers currently enabled */
+
+	/*
+	 * If any manager was enabled, we need to disable it before
+	 * DSS clocks are disabled or DISPC module is reset
+	 */
+	if (lcd_en)
+		irq_mask |= 1 << FRAMEDONE_IRQ_SHIFT;
+
+	if (digit_en) {
+		if (da->has_framedonetv_irq) {
+			irq_mask |= 1 << FRAMEDONETV_IRQ_SHIFT;
+		} else {
+			irq_mask |= 1 << EVSYNC_EVEN_IRQ_SHIFT |
+				1 << EVSYNC_ODD_IRQ_SHIFT;
+		}
+	}
+
+	if (lcd2_en)
+		irq_mask |= 1 << FRAMEDONE2_IRQ_SHIFT;
+
+	/*
+	 * clear any previous FRAMEDONE, FRAMEDONETV,
+	 * EVSYNC_EVEN/ODD or FRAMEDONE2 interrupts
+	 */
+	omap_hwmod_write(irq_mask, oh, DISPC_IRQSTATUS);
+
+	/* disable LCD and TV managers */
+	v = omap_hwmod_read(oh, DISPC_CONTROL);
+	v &= ~(LCD_EN_MASK | DIGIT_EN_MASK);
+	omap_hwmod_write(v, oh, DISPC_CONTROL);
+
+	/* disable LCD2 manager */
+	if (da->manager_count > 2) {
+		v = omap_hwmod_read(oh, DISPC_CONTROL2);
+		v &= ~LCD_EN_MASK;
+		omap_hwmod_write(v, oh, DISPC_CONTROL2);
+	}
+
+	i = 0;
+	while ((omap_hwmod_read(oh, DISPC_IRQSTATUS) & irq_mask) !=
+	       irq_mask) {
+		i++;
+		if (i > FRAMEDONE_IRQ_TIMEOUT) {
+			pr_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)
 {
@@ -144,6 +248,20 @@ int omap_dss_reset(struct omap_hwmod *oh)
 		if (oc->_clk)
 			clk_enable(oc->_clk);
 
+	dispc_disable_outputs(oh);
+
+	/* 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);
diff --git a/arch/arm/mach-omap2/display.h b/arch/arm/mach-omap2/display.h
new file mode 100644
index 0000000..b871b01
--- /dev/null
+++ b/arch/arm/mach-omap2/display.h
@@ -0,0 +1,29 @@
+/*
+ * display.h - OMAP2+ integration-specific DSS header
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * 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 __ARCH_ARM_MACH_OMAP2_DISPLAY_H
+#define __ARCH_ARM_MACH_OMAP2_DISPLAY_H
+
+#include <linux/kernel.h>
+
+struct omap_dss_dispc_dev_attr {
+	u8	manager_count;
+	bool	has_framedonetv_irq;
+};
+
+#endif
diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
index 09d9395..8e32cb3 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c
@@ -945,6 +945,7 @@ static struct omap_hwmod omap2420_dss_dispc_hwmod = {
 	.slaves_cnt	= ARRAY_SIZE(omap2420_dss_dispc_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP2420),
 	.flags		= HWMOD_NO_IDLEST,
+	.dev_attr	= &omap2_3_dss_dispc_dev_attr
 };
 
 /* l4_core -> dss_rfbi */
diff --git a/arch/arm/mach-omap2/omap_hwmod_2430_data.c b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
index 67aff19..6e8ef12 100644
--- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c
@@ -1005,6 +1005,7 @@ static struct omap_hwmod omap2430_dss_dispc_hwmod = {
 	.slaves_cnt	= ARRAY_SIZE(omap2430_dss_dispc_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP2430),
 	.flags		= HWMOD_NO_IDLEST,
+	.dev_attr	= &omap2_3_dss_dispc_dev_attr
 };
 
 /* l4_core -> dss_rfbi */
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 4a02cc3..12988fe 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -1465,6 +1465,7 @@ static struct omap_hwmod omap3xxx_dss_dispc_hwmod = {
 				CHIP_GE_OMAP3430ES2 | CHIP_IS_OMAP3630ES1 |
 				CHIP_GE_OMAP3630ES1_1),
 	.flags		= HWMOD_NO_IDLEST,
+	.dev_attr	= &omap2_3_dss_dispc_dev_attr
 };
 
 /*
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 7a7489e..17adfb3 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -1345,6 +1345,11 @@ static struct omap_hwmod_addr_space omap44xx_dss_dispc_addrs[] = {
 	{ }
 };
 
+static struct omap_dss_dispc_dev_attr omap44xx_dss_dispc_dev_attr = {
+	.manager_count		= 3,
+	.has_framedonetv_irq	= 1
+};
+
 /* l4_per -> dss_dispc */
 static struct omap_hwmod_ocp_if omap44xx_l4_per__dss_dispc = {
 	.master		= &omap44xx_l4_per_hwmod,
@@ -1376,6 +1381,7 @@ static struct omap_hwmod omap44xx_dss_dispc_hwmod = {
 	.slaves		= omap44xx_dss_dispc_slaves,
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_dss_dispc_slaves),
 	.omap_chip	= OMAP_CHIP_INIT(CHIP_IS_OMAP4430),
+	.dev_attr	= &omap44xx_dss_dispc_dev_attr
 };
 
 /*
diff --git a/arch/arm/mach-omap2/omap_hwmod_common_data.c b/arch/arm/mach-omap2/omap_hwmod_common_data.c
index de832eb..51e5418 100644
--- a/arch/arm/mach-omap2/omap_hwmod_common_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_common_data.c
@@ -49,3 +49,7 @@ struct omap_hwmod_sysc_fields omap_hwmod_sysc_type2 = {
 	.srst_shift	= SYSC_TYPE2_SOFTRESET_SHIFT,
 };
 
+struct omap_dss_dispc_dev_attr omap2_3_dss_dispc_dev_attr = {
+	.manager_count		= 2,
+	.has_framedonetv_irq	= 0
+};
diff --git a/arch/arm/mach-omap2/omap_hwmod_common_data.h b/arch/arm/mach-omap2/omap_hwmod_common_data.h
index 39a7c37..ad5d8f0 100644
--- a/arch/arm/mach-omap2/omap_hwmod_common_data.h
+++ b/arch/arm/mach-omap2/omap_hwmod_common_data.h
@@ -16,6 +16,8 @@
 
 #include <plat/omap_hwmod.h>
 
+#include "display.h"
+
 /* Common address space across OMAP2xxx */
 extern struct omap_hwmod_addr_space omap2xxx_uart1_addr_space[];
 extern struct omap_hwmod_addr_space omap2xxx_uart2_addr_space[];
@@ -111,4 +113,6 @@ extern struct omap_hwmod_class omap2xxx_dma_hwmod_class;
 extern struct omap_hwmod_class omap2xxx_mailbox_hwmod_class;
 extern struct omap_hwmod_class omap2xxx_mcspi_class;
 
+extern struct omap_dss_dispc_dev_attr omap2_3_dss_dispc_dev_attr;
+
 #endif
-- 
1.7.6.3




More information about the linux-arm-kernel mailing list