diff options
author | Archit Taneja <archit@ti.com> | 2011-10-06 20:04:08 -0400 |
---|---|---|
committer | Paul Walmsley <paul@pwsan.com> | 2011-11-08 05:16:46 -0500 |
commit | b923d40dd4211c4ef7d4efa2bd81b7ca1d8744c1 (patch) | |
tree | d6814d77e420e4b1fa1e089378c331c9b9f60580 /arch | |
parent | 13662dc5b177d68885695ef513dd4ae0e4d2a099 (diff) |
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: Tomi Valkeinen <tomi.valkeinen@ti.com>
Tested-by: R, Sricharan <r.sricharan@ti.com>
Signed-off-by: Archit 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: Paul Walmsley <paul@pwsan.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-omap2/display.c | 124 | ||||
-rw-r--r-- | arch/arm/mach-omap2/display.h | 29 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod_2420_data.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod_2430_data.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod_44xx_data.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod_common_data.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod_common_data.h | 4 |
8 files changed, 170 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c index 941b5459707f..dce9905d64bb 100644 --- a/arch/arm/mach-omap2/display.c +++ b/arch/arm/mach-omap2/display.c | |||
@@ -30,6 +30,32 @@ | |||
30 | #include <plat/common.h> | 30 | #include <plat/common.h> |
31 | 31 | ||
32 | #include "control.h" | 32 | #include "control.h" |
33 | #include "display.h" | ||
34 | |||
35 | #define DISPC_CONTROL 0x0040 | ||
36 | #define DISPC_CONTROL2 0x0238 | ||
37 | #define DISPC_IRQSTATUS 0x0018 | ||
38 | |||
39 | #define DSS_SYSCONFIG 0x10 | ||
40 | #define DSS_SYSSTATUS 0x14 | ||
41 | #define DSS_CONTROL 0x40 | ||
42 | #define DSS_SDI_CONTROL 0x44 | ||
43 | #define DSS_PLL_CONTROL 0x48 | ||
44 | |||
45 | #define LCD_EN_MASK (0x1 << 0) | ||
46 | #define DIGIT_EN_MASK (0x1 << 1) | ||
47 | |||
48 | #define FRAMEDONE_IRQ_SHIFT 0 | ||
49 | #define EVSYNC_EVEN_IRQ_SHIFT 2 | ||
50 | #define EVSYNC_ODD_IRQ_SHIFT 3 | ||
51 | #define FRAMEDONE2_IRQ_SHIFT 22 | ||
52 | #define FRAMEDONETV_IRQ_SHIFT 24 | ||
53 | |||
54 | /* | ||
55 | * FRAMEDONE_IRQ_TIMEOUT: how long (in milliseconds) to wait during DISPC | ||
56 | * reset before deciding that something has gone wrong | ||
57 | */ | ||
58 | #define FRAMEDONE_IRQ_TIMEOUT 100 | ||
33 | 59 | ||
34 | static struct platform_device omap_display_device = { | 60 | static struct platform_device omap_display_device = { |
35 | .name = "omapdss", | 61 | .name = "omapdss", |
@@ -174,6 +200,90 @@ int __init omap_display_init(struct omap_dss_board_info *board_data) | |||
174 | return r; | 200 | return r; |
175 | } | 201 | } |
176 | 202 | ||
203 | static void dispc_disable_outputs(void) | ||
204 | { | ||
205 | u32 v, irq_mask = 0; | ||
206 | bool lcd_en, digit_en, lcd2_en = false; | ||
207 | int i; | ||
208 | struct omap_dss_dispc_dev_attr *da; | ||
209 | struct omap_hwmod *oh; | ||
210 | |||
211 | oh = omap_hwmod_lookup("dss_dispc"); | ||
212 | if (!oh) { | ||
213 | WARN(1, "display: could not disable outputs during reset - could not find dss_dispc hwmod\n"); | ||
214 | return; | ||
215 | } | ||
216 | |||
217 | if (!oh->dev_attr) { | ||
218 | pr_err("display: could not disable outputs during reset due to missing dev_attr\n"); | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | da = (struct omap_dss_dispc_dev_attr *)oh->dev_attr; | ||
223 | |||
224 | /* store value of LCDENABLE and DIGITENABLE bits */ | ||
225 | v = omap_hwmod_read(oh, DISPC_CONTROL); | ||
226 | lcd_en = v & LCD_EN_MASK; | ||
227 | digit_en = v & DIGIT_EN_MASK; | ||
228 | |||
229 | /* store value of LCDENABLE for LCD2 */ | ||
230 | if (da->manager_count > 2) { | ||
231 | v = omap_hwmod_read(oh, DISPC_CONTROL2); | ||
232 | lcd2_en = v & LCD_EN_MASK; | ||
233 | } | ||
234 | |||
235 | if (!(lcd_en | digit_en | lcd2_en)) | ||
236 | return; /* no managers currently enabled */ | ||
237 | |||
238 | /* | ||
239 | * If any manager was enabled, we need to disable it before | ||
240 | * DSS clocks are disabled or DISPC module is reset | ||
241 | */ | ||
242 | if (lcd_en) | ||
243 | irq_mask |= 1 << FRAMEDONE_IRQ_SHIFT; | ||
244 | |||
245 | if (digit_en) { | ||
246 | if (da->has_framedonetv_irq) { | ||
247 | irq_mask |= 1 << FRAMEDONETV_IRQ_SHIFT; | ||
248 | } else { | ||
249 | irq_mask |= 1 << EVSYNC_EVEN_IRQ_SHIFT | | ||
250 | 1 << EVSYNC_ODD_IRQ_SHIFT; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | if (lcd2_en) | ||
255 | irq_mask |= 1 << FRAMEDONE2_IRQ_SHIFT; | ||
256 | |||
257 | /* | ||
258 | * clear any previous FRAMEDONE, FRAMEDONETV, | ||
259 | * EVSYNC_EVEN/ODD or FRAMEDONE2 interrupts | ||
260 | */ | ||
261 | omap_hwmod_write(irq_mask, oh, DISPC_IRQSTATUS); | ||
262 | |||
263 | /* disable LCD and TV managers */ | ||
264 | v = omap_hwmod_read(oh, DISPC_CONTROL); | ||
265 | v &= ~(LCD_EN_MASK | DIGIT_EN_MASK); | ||
266 | omap_hwmod_write(v, oh, DISPC_CONTROL); | ||
267 | |||
268 | /* disable LCD2 manager */ | ||
269 | if (da->manager_count > 2) { | ||
270 | v = omap_hwmod_read(oh, DISPC_CONTROL2); | ||
271 | v &= ~LCD_EN_MASK; | ||
272 | omap_hwmod_write(v, oh, DISPC_CONTROL2); | ||
273 | } | ||
274 | |||
275 | i = 0; | ||
276 | while ((omap_hwmod_read(oh, DISPC_IRQSTATUS) & irq_mask) != | ||
277 | irq_mask) { | ||
278 | i++; | ||
279 | if (i > FRAMEDONE_IRQ_TIMEOUT) { | ||
280 | pr_err("didn't get FRAMEDONE1/2 or TV interrupt\n"); | ||
281 | break; | ||
282 | } | ||
283 | mdelay(1); | ||
284 | } | ||
285 | } | ||
286 | |||
177 | #define MAX_MODULE_SOFTRESET_WAIT 10000 | 287 | #define MAX_MODULE_SOFTRESET_WAIT 10000 |
178 | int omap_dss_reset(struct omap_hwmod *oh) | 288 | int omap_dss_reset(struct omap_hwmod *oh) |
179 | { | 289 | { |
@@ -190,6 +300,20 @@ int omap_dss_reset(struct omap_hwmod *oh) | |||
190 | if (oc->_clk) | 300 | if (oc->_clk) |
191 | clk_enable(oc->_clk); | 301 | clk_enable(oc->_clk); |
192 | 302 | ||
303 | dispc_disable_outputs(); | ||
304 | |||
305 | /* clear SDI registers */ | ||
306 | if (cpu_is_omap3430()) { | ||
307 | omap_hwmod_write(0x0, oh, DSS_SDI_CONTROL); | ||
308 | omap_hwmod_write(0x0, oh, DSS_PLL_CONTROL); | ||
309 | } | ||
310 | |||
311 | /* | ||
312 | * clear DSS_CONTROL register to switch DSS clock sources to | ||
313 | * PRCM clock, if any | ||
314 | */ | ||
315 | omap_hwmod_write(0x0, oh, DSS_CONTROL); | ||
316 | |||
193 | omap_test_timeout((omap_hwmod_read(oh, oh->class->sysc->syss_offs) | 317 | omap_test_timeout((omap_hwmod_read(oh, oh->class->sysc->syss_offs) |
194 | & SYSS_RESETDONE_MASK), | 318 | & SYSS_RESETDONE_MASK), |
195 | MAX_MODULE_SOFTRESET_WAIT, c); | 319 | 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 000000000000..b871b017b352 --- /dev/null +++ b/arch/arm/mach-omap2/display.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * display.h - OMAP2+ integration-specific DSS header | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments, Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License version 2 as published by | ||
8 | * the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along with | ||
16 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #ifndef __ARCH_ARM_MACH_OMAP2_DISPLAY_H | ||
20 | #define __ARCH_ARM_MACH_OMAP2_DISPLAY_H | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | |||
24 | struct omap_dss_dispc_dev_attr { | ||
25 | u8 manager_count; | ||
26 | bool has_framedonetv_irq; | ||
27 | }; | ||
28 | |||
29 | #endif | ||
diff --git a/arch/arm/mach-omap2/omap_hwmod_2420_data.c b/arch/arm/mach-omap2/omap_hwmod_2420_data.c index 74037a5807af..a5409ce3f323 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2420_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2420_data.c | |||
@@ -943,6 +943,7 @@ static struct omap_hwmod omap2420_dss_dispc_hwmod = { | |||
943 | .slaves = omap2420_dss_dispc_slaves, | 943 | .slaves = omap2420_dss_dispc_slaves, |
944 | .slaves_cnt = ARRAY_SIZE(omap2420_dss_dispc_slaves), | 944 | .slaves_cnt = ARRAY_SIZE(omap2420_dss_dispc_slaves), |
945 | .flags = HWMOD_NO_IDLEST, | 945 | .flags = HWMOD_NO_IDLEST, |
946 | .dev_attr = &omap2_3_dss_dispc_dev_attr | ||
946 | }; | 947 | }; |
947 | 948 | ||
948 | /* l4_core -> dss_rfbi */ | 949 | /* 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 6f0365388dfc..c4f56cb60d7d 100644 --- a/arch/arm/mach-omap2/omap_hwmod_2430_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_2430_data.c | |||
@@ -1004,6 +1004,7 @@ static struct omap_hwmod omap2430_dss_dispc_hwmod = { | |||
1004 | .slaves = omap2430_dss_dispc_slaves, | 1004 | .slaves = omap2430_dss_dispc_slaves, |
1005 | .slaves_cnt = ARRAY_SIZE(omap2430_dss_dispc_slaves), | 1005 | .slaves_cnt = ARRAY_SIZE(omap2430_dss_dispc_slaves), |
1006 | .flags = HWMOD_NO_IDLEST, | 1006 | .flags = HWMOD_NO_IDLEST, |
1007 | .dev_attr = &omap2_3_dss_dispc_dev_attr | ||
1007 | }; | 1008 | }; |
1008 | 1009 | ||
1009 | /* l4_core -> dss_rfbi */ | 1010 | /* 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 4aaf0caaf3cc..7f8915ad5099 100644 --- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c | |||
@@ -1462,6 +1462,7 @@ static struct omap_hwmod omap3xxx_dss_dispc_hwmod = { | |||
1462 | .slaves = omap3xxx_dss_dispc_slaves, | 1462 | .slaves = omap3xxx_dss_dispc_slaves, |
1463 | .slaves_cnt = ARRAY_SIZE(omap3xxx_dss_dispc_slaves), | 1463 | .slaves_cnt = ARRAY_SIZE(omap3xxx_dss_dispc_slaves), |
1464 | .flags = HWMOD_NO_IDLEST, | 1464 | .flags = HWMOD_NO_IDLEST, |
1465 | .dev_attr = &omap2_3_dss_dispc_dev_attr | ||
1465 | }; | 1466 | }; |
1466 | 1467 | ||
1467 | /* | 1468 | /* |
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index 3b04d63316f5..daaf165af696 100644 --- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c | |||
@@ -1327,6 +1327,11 @@ static struct omap_hwmod_addr_space omap44xx_dss_dispc_addrs[] = { | |||
1327 | { } | 1327 | { } |
1328 | }; | 1328 | }; |
1329 | 1329 | ||
1330 | static struct omap_dss_dispc_dev_attr omap44xx_dss_dispc_dev_attr = { | ||
1331 | .manager_count = 3, | ||
1332 | .has_framedonetv_irq = 1 | ||
1333 | }; | ||
1334 | |||
1330 | /* l4_per -> dss_dispc */ | 1335 | /* l4_per -> dss_dispc */ |
1331 | static struct omap_hwmod_ocp_if omap44xx_l4_per__dss_dispc = { | 1336 | static struct omap_hwmod_ocp_if omap44xx_l4_per__dss_dispc = { |
1332 | .master = &omap44xx_l4_per_hwmod, | 1337 | .master = &omap44xx_l4_per_hwmod, |
@@ -1357,6 +1362,7 @@ static struct omap_hwmod omap44xx_dss_dispc_hwmod = { | |||
1357 | }, | 1362 | }, |
1358 | .slaves = omap44xx_dss_dispc_slaves, | 1363 | .slaves = omap44xx_dss_dispc_slaves, |
1359 | .slaves_cnt = ARRAY_SIZE(omap44xx_dss_dispc_slaves), | 1364 | .slaves_cnt = ARRAY_SIZE(omap44xx_dss_dispc_slaves), |
1365 | .dev_attr = &omap44xx_dss_dispc_dev_attr | ||
1360 | }; | 1366 | }; |
1361 | 1367 | ||
1362 | /* | 1368 | /* |
diff --git a/arch/arm/mach-omap2/omap_hwmod_common_data.c b/arch/arm/mach-omap2/omap_hwmod_common_data.c index de832ebc93a9..51e5418899fb 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 = { | |||
49 | .srst_shift = SYSC_TYPE2_SOFTRESET_SHIFT, | 49 | .srst_shift = SYSC_TYPE2_SOFTRESET_SHIFT, |
50 | }; | 50 | }; |
51 | 51 | ||
52 | struct omap_dss_dispc_dev_attr omap2_3_dss_dispc_dev_attr = { | ||
53 | .manager_count = 2, | ||
54 | .has_framedonetv_irq = 0 | ||
55 | }; | ||
diff --git a/arch/arm/mach-omap2/omap_hwmod_common_data.h b/arch/arm/mach-omap2/omap_hwmod_common_data.h index 39a7c37f4587..ad5d8f04c0b8 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 @@ | |||
16 | 16 | ||
17 | #include <plat/omap_hwmod.h> | 17 | #include <plat/omap_hwmod.h> |
18 | 18 | ||
19 | #include "display.h" | ||
20 | |||
19 | /* Common address space across OMAP2xxx */ | 21 | /* Common address space across OMAP2xxx */ |
20 | extern struct omap_hwmod_addr_space omap2xxx_uart1_addr_space[]; | 22 | extern struct omap_hwmod_addr_space omap2xxx_uart1_addr_space[]; |
21 | extern struct omap_hwmod_addr_space omap2xxx_uart2_addr_space[]; | 23 | extern struct omap_hwmod_addr_space omap2xxx_uart2_addr_space[]; |
@@ -111,4 +113,6 @@ extern struct omap_hwmod_class omap2xxx_dma_hwmod_class; | |||
111 | extern struct omap_hwmod_class omap2xxx_mailbox_hwmod_class; | 113 | extern struct omap_hwmod_class omap2xxx_mailbox_hwmod_class; |
112 | extern struct omap_hwmod_class omap2xxx_mcspi_class; | 114 | extern struct omap_hwmod_class omap2xxx_mcspi_class; |
113 | 115 | ||
116 | extern struct omap_dss_dispc_dev_attr omap2_3_dss_dispc_dev_attr; | ||
117 | |||
114 | #endif | 118 | #endif |