Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b923d40d authored by Archit Taneja's avatar Archit Taneja Committed by Paul Walmsley
Browse files

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.

Thanks to Tomi and Sricharan for some additional testing.

Acked-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
Tested-by: default avatarR, Sricharan <r.sricharan@ti.com>
Signed-off-by: default avatarArchit Taneja <archit@ti.com>
[paul@pwsan.com: restructured code, removed omap_{read,write}l(), removed
 cpu_is_omap*() calls and converted to dev_attr]
Signed-off-by: default avatarPaul Walmsley <paul@pwsan.com>
parent 13662dc5
Loading
Loading
Loading
Loading
+124 −0
Original line number Diff line number Diff line
@@ -30,6 +30,32 @@
#include <plat/common.h>

#include "control.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",
@@ -174,6 +200,90 @@ int __init omap_display_init(struct omap_dss_board_info *board_data)
	return r;
}

static void dispc_disable_outputs(void)
{
	u32 v, irq_mask = 0;
	bool lcd_en, digit_en, lcd2_en = false;
	int i;
	struct omap_dss_dispc_dev_attr *da;
	struct omap_hwmod *oh;

	oh = omap_hwmod_lookup("dss_dispc");
	if (!oh) {
		WARN(1, "display: could not disable outputs during reset - could not find dss_dispc hwmod\n");
		return;
	}

	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)
{
@@ -190,6 +300,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);
+29 −0
Original line number Diff line number Diff line
/*
 * 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
+1 −0
Original line number Diff line number Diff line
@@ -943,6 +943,7 @@ static struct omap_hwmod omap2420_dss_dispc_hwmod = {
	.slaves		= omap2420_dss_dispc_slaves,
	.slaves_cnt	= ARRAY_SIZE(omap2420_dss_dispc_slaves),
	.flags		= HWMOD_NO_IDLEST,
	.dev_attr	= &omap2_3_dss_dispc_dev_attr
};

/* l4_core -> dss_rfbi */
+1 −0
Original line number Diff line number Diff line
@@ -1004,6 +1004,7 @@ static struct omap_hwmod omap2430_dss_dispc_hwmod = {
	.slaves		= omap2430_dss_dispc_slaves,
	.slaves_cnt	= ARRAY_SIZE(omap2430_dss_dispc_slaves),
	.flags		= HWMOD_NO_IDLEST,
	.dev_attr	= &omap2_3_dss_dispc_dev_attr
};

/* l4_core -> dss_rfbi */
+1 −0
Original line number Diff line number Diff line
@@ -1462,6 +1462,7 @@ static struct omap_hwmod omap3xxx_dss_dispc_hwmod = {
	.slaves		= omap3xxx_dss_dispc_slaves,
	.slaves_cnt	= ARRAY_SIZE(omap3xxx_dss_dispc_slaves),
	.flags		= HWMOD_NO_IDLEST,
	.dev_attr	= &omap2_3_dss_dispc_dev_attr
};

/*
Loading