H Haavard Skinnemoen #
Message details and related content
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
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=/