[PATCH] atmel_lcdfb: Disable LCD and DMA engines when suspending

Subsystems: framebuffer layer, the rest

STALE6564d

2 messages, 2 authors, 2008-08-17 · open the first message on its own page

[PATCH] atmel_lcdfb: Disable LCD and DMA engines when suspending

From: Haavard Skinnemoen <hidden>
Date: 2008-08-11 12:34:52

When suspending the system with atmel_lcdfb enabled, I sometimes see
this:

[17179610.896000] atmel_lcdfb atmel_lcdfb.0: FIFO underflow 0x10

After this happens, the serial console is unresponsive for a long time,
but eventually recovers (after a minute or so).  SysRq-W shows that the
events thread is blocked trying to recover from the underflow:

SysRq : Show Blocked State
  task                PC stack   pid father
events/0      D 90169e50     0     5      2
Stack: (0x93833ebc to 0x93834000)
3ea0:                                                                93832008
3ec0: 90169fd8 90169fd8 93833f18 ffff11ce 93833ee8 00000000 901fb560 00000004
3ee0: 901c7788 00000000 902254f8 902254f8 ffff11ce 90023884 9381b900 90225460
3f00: 90169fd6 6576656e 74732f30 00000000 00000000 00000005 9016a012 93833f3c
3f20: 9388ca40 93821000 00000000 93832000 00000000 901c7788 00000000 90023d16
3f40: 93833f50 9388ca40 93821000 00000000 900c7ed2 93833f64 9388ca40 93821000
3f60: 00000000 90028156 93833f90 9388ca6c 93821000 00000000 90028110 900c7eb8
3f80: 904b7754 00000000 901c7788 00000000 900285a8 93833fc8 93832000 93832000
3fa0: 00000000 93821000 ffffe000 9002a7bc 93817eec 00000000 9381b900 9002aa14
3fc0: 93833fc0 93833fc0 9002a7e8 93833fec 93821000 900284f8 00000000 00000000
3fe0: 9001f718 9002a7bc 93817eec 9001f718 00000000 00000000 00000000 00000000
Call trace:
 [<90169fd8>] schedule_timeout+0x60/0x88
 [<9016a012>] schedule_timeout_uninterruptible+0x12/0x14
 [<90023d16>] msleep+0x12/0x1c
 [<900c7ed2>] atmel_lcdfb_task+0x1a/0x5c
 [<90028156>] run_workqueue+0xba/0x168
 [<900285a8>] worker_thread+0xb0/0xbc
 [<9002a7e8>] kthread+0x2c/0x44
 [<9001f718>] do_exit+0x0/0x508

Which is understandable since stopping the bus clock while the LCD
controller is doing a DMA transfer is a thoroughly nasty thing to do,
and the strange thing isn't that the controller gets confused, but that
it somehow actually manages to recover after a while.

Avoid this issue by shutting down the LCD controller before entering
suspend (and restarting it when resuming). This prevents the underrun
from happening in the first place, and prevents whatever nastiness is
happening when the bus clock stops in the middle of a DMA transfer.

Signed-off-by: Haavard Skinnemoen <redacted>
---
I think this patch is 2.6.27 and possibly -stable material. I have
only tested it on a prototype board which doesn't have a working LCD
panel yet, so I can't say for sure that it's doing everything
correctly, but it does appear to prevent the events thread from
hanging.

I'll probably get around to testing it more thoroughly in a couple of
days, but I'm posting it now (a) to make people aware of the problem,
and (b) so that others can test it.

 drivers/video/atmel_lcdfb.c |   84 +++++++++++++++++++++++++------------------
 1 files changed, 49 insertions(+), 35 deletions(-)
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index 763f9ae..a42406a 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -206,6 +206,36 @@ static unsigned long compute_hozval(unsigned long xres, unsigned long lcdcon2)
 	return value;
 }
 
+static void atmel_lcdfb_stop_nowait(struct atmel_lcdfb_info *sinfo)
+{
+	/* Turn off the LCD controller and the DMA controller */
+	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON,
+			sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET);
+
+	/* Wait for the LCDC core to become idle */
+	while (lcdc_readl(sinfo, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
+		msleep(10);
+
+	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0);
+}
+
+static void atmel_lcdfb_stop(struct atmel_lcdfb_info *sinfo)
+{
+	atmel_lcdfb_stop_nowait(sinfo);
+
+	/* Wait for DMA engine to become idle... */
+	while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY)
+		msleep(10);
+}
+
+static void atmel_lcdfb_start(struct atmel_lcdfb_info *sinfo)
+{
+	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon);
+	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON,
+		(sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET)
+		| ATMEL_LCDC_PWR);
+}
+
 static void atmel_lcdfb_update_dma(struct fb_info *info,
 			       struct fb_var_screeninfo *var)
 {
@@ -416,26 +446,8 @@ static void atmel_lcdfb_reset(struct atmel_lcdfb_info *sinfo)
 {
 	might_sleep();
 
-	/* LCD power off */
-	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET);
-
-	/* wait for the LCDC core to become idle */
-	while (lcdc_readl(sinfo, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
-		msleep(10);
-
-	/* DMA disable */
-	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0);
-
-	/* wait for DMA engine to become idle */
-	while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY)
-		msleep(10);
-
-	/* LCD power on */
-	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON,
-		(sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
-
-	/* DMA enable */
-	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon);
+	atmel_lcdfb_stop(sinfo);
+	atmel_lcdfb_start(sinfo);
 }
 
 /**
@@ -467,14 +479,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info)
 		 info->var.xres, info->var.yres,
 		 info->var.xres_virtual, info->var.yres_virtual);
 
-	/* Turn off the LCD controller and the DMA controller */
-	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET);
-
-	/* Wait for the LCDC core to become idle */
-	while (lcdc_readl(sinfo, ATMEL_LCDC_PWRCON) & ATMEL_LCDC_BUSY)
-		msleep(10);
-
-	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0);
+	atmel_lcdfb_stop_nowait(sinfo);
 
 	if (info->var.bits_per_pixel == 1)
 		info->fix.visual = FB_VISUAL_MONO01;
@@ -579,13 +584,7 @@ static int atmel_lcdfb_set_par(struct fb_info *info)
 	while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY)
 		msleep(10);
 
-	dev_dbg(info->device, "  * re-enable DMA engine\n");
-	/* ...and enable it with updated configuration */
-	lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon);
-
-	dev_dbg(info->device, "  * re-enable LCDC core\n");
-	lcdc_writel(sinfo, ATMEL_LCDC_PWRCON,
-		(sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR);
+	atmel_lcdfb_start(sinfo);
 
 	dev_dbg(info->device, "  * DONE\n");
 
@@ -1026,11 +1025,20 @@ static int atmel_lcdfb_suspend(struct platform_device *pdev, pm_message_t mesg)
 	struct fb_info *info = platform_get_drvdata(pdev);
 	struct atmel_lcdfb_info *sinfo = info->par;
 
+	/*
+	 * We don't want to handle interrupts while the clock is
+	 * stopped. It may take forever.
+	 */
+	lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL);
+
 	sinfo->saved_lcdcon = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL);
 	lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, 0);
 	if (sinfo->atmel_lcdfb_power_control)
 		sinfo->atmel_lcdfb_power_control(0);
+
+	atmel_lcdfb_stop(sinfo);
 	atmel_lcdfb_stop_clock(sinfo);
+
 	return 0;
 }
 
@@ -1040,9 +1048,15 @@ static int atmel_lcdfb_resume(struct platform_device *pdev)
 	struct atmel_lcdfb_info *sinfo = info->par;
 
 	atmel_lcdfb_start_clock(sinfo);
+	atmel_lcdfb_start(sinfo);
 	if (sinfo->atmel_lcdfb_power_control)
 		sinfo->atmel_lcdfb_power_control(1);
 	lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, sinfo->saved_lcdcon);
+
+	/* Enable FIFO & DMA errors */
+	lcdc_writel(sinfo, ATMEL_LCDC_IER, ATMEL_LCDC_UFLWI
+			| ATMEL_LCDC_OWRI | ATMEL_LCDC_MERI);
+
 	return 0;
 }
 
-- 
1.5.6.3


-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/

Re: [PATCH] atmel_lcdfb: Disable LCD and DMA engines when suspending

From: Krzysztof Helt <hidden>
Date: 2008-08-17 19:02:43

On Mon, 11 Aug 2008 14:34:28 +0200
Haavard Skinnemoen [off-list ref] wrote:
When suspending the system with atmel_lcdfb enabled, I sometimes see
this:

[17179610.896000] atmel_lcdfb atmel_lcdfb.0: FIFO underflow 0x10

After this happens, the serial console is unresponsive for a long time,
but eventually recovers (after a minute or so).  SysRq-W shows that the
events thread is blocked trying to recover from the underflow:

SysRq : Show Blocked State
  task                PC stack   pid father
events/0      D 90169e50     0     5      2
Stack: (0x93833ebc to 0x93834000)
3ea0:                                                                93832008
3ec0: 90169fd8 90169fd8 93833f18 ffff11ce 93833ee8 00000000 901fb560 00000004
3ee0: 901c7788 00000000 902254f8 902254f8 ffff11ce 90023884 9381b900 90225460
3f00: 90169fd6 6576656e 74732f30 00000000 00000000 00000005 9016a012 93833f3c
3f20: 9388ca40 93821000 00000000 93832000 00000000 901c7788 00000000 90023d16
3f40: 93833f50 9388ca40 93821000 00000000 900c7ed2 93833f64 9388ca40 93821000
3f60: 00000000 90028156 93833f90 9388ca6c 93821000 00000000 90028110 900c7eb8
3f80: 904b7754 00000000 901c7788 00000000 900285a8 93833fc8 93832000 93832000
3fa0: 00000000 93821000 ffffe000 9002a7bc 93817eec 00000000 9381b900 9002aa14
3fc0: 93833fc0 93833fc0 9002a7e8 93833fec 93821000 900284f8 00000000 00000000
3fe0: 9001f718 9002a7bc 93817eec 9001f718 00000000 00000000 00000000 00000000
Call trace:
 [<90169fd8>] schedule_timeout+0x60/0x88
 [<9016a012>] schedule_timeout_uninterruptible+0x12/0x14
 [<90023d16>] msleep+0x12/0x1c
 [<900c7ed2>] atmel_lcdfb_task+0x1a/0x5c
 [<90028156>] run_workqueue+0xba/0x168
 [<900285a8>] worker_thread+0xb0/0xbc
 [<9002a7e8>] kthread+0x2c/0x44
 [<9001f718>] do_exit+0x0/0x508

Which is understandable since stopping the bus clock while the LCD
controller is doing a DMA transfer is a thoroughly nasty thing to do,
and the strange thing isn't that the controller gets confused, but that
it somehow actually manages to recover after a while.

Avoid this issue by shutting down the LCD controller before entering
suspend (and restarting it when resuming). This prevents the underrun
from happening in the first place, and prevents whatever nastiness is
happening when the bus clock stops in the middle of a DMA transfer.

Signed-off-by: Haavard Skinnemoen <redacted>
---
Acked-by: Krzysztof Helt <redacted>

Please send it to akpm@linux-foundation.org if you do not intend
to merge it through atmel/arm tree.

Regards,
Krzysztof

---------------------------------------------------------------------- 
Igrzyska na Dzikim Zachodzie!
Sprawdz >> http://link.interia.pl/f1edc 
	


-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help