diff options
Diffstat (limited to 'arch/arm/mach-omap2/display.c')
| -rw-r--r-- | arch/arm/mach-omap2/display.c | 159 | 
1 files changed, 159 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c index adb2756e242..dce9905d64b 100644 --- a/arch/arm/mach-omap2/display.c +++ b/arch/arm/mach-omap2/display.c @@ -27,8 +27,35 @@  #include <plat/omap_hwmod.h>  #include <plat/omap_device.h>  #include <plat/omap-pm.h> +#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", @@ -172,3 +199,135 @@ 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) +{ +	struct omap_hwmod_opt_clk *oc; +	int c = 0; +	int i, r; + +	if (!(oh->class->sysc->sysc_flags & SYSS_HAS_RESET_STATUS)) { +		pr_err("dss_core: hwmod data doesn't contain reset data\n"); +		return -EINVAL; +	} + +	for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) +		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); + +	if (c == MAX_MODULE_SOFTRESET_WAIT) +		pr_warning("dss_core: waiting for reset to finish failed\n"); +	else +		pr_debug("dss_core: softreset done\n"); + +	for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) +		if (oc->_clk) +			clk_disable(oc->_clk); + +	r = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0; + +	return r; +}  |