PATCH: suspend and poweroff (#3603 candidate)

Bernardo Innocenti bernie at codewiz.org
Wed Sep 19 18:59:18 EDT 2007


Here's a proposed patch for the green icons bug.  I'm afraid it came
up a bit long and somewhat invasive.

Tested on B4: OK!
Tested on B2: KO (does not unfreeze the screen when Sugar comes up)

I must have broken something in the GX part when I merged the two powerup()
and powerdown() functions, but I'm way too tired now to reiterate it.

Tomorrow I'll try to fix it and submit an updated patch.  Meanwhile, could
you please do a preliminary review of it, Jordan?


diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 61d7659..a2ecb1d 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -736,49 +736,43 @@ static void try_to_load(int fb)
 #endif /* CONFIG_KMOD */
 
 int
-fb_powerup(struct fb_info *info)
+fb_power(struct fb_info *info, int state)
 {
 	int ret = 0;
 
-	if (!info || info->state == FBINFO_STATE_RUNNING)
-		return 0;
-
-	if (info->fbops->fb_powerup)
-		ret = info->fbops->fb_powerup(info);
-
-	if (!ret) {
-		acquire_console_sem();
-		fb_set_suspend(info, 0);
-		release_console_sem();
-	}
+	/* Maybe unwise */
+	if (!info)
+		return -EINVAL;
 
-	return ret;
-}
+	if (state < 0 || state > 3)
+		return -EINVAL;
 
-int
-fb_powerdown(struct fb_info *info)
-{
-	int ret = 0;
+	acquire_console_sem();
 
-	if (!info || info->state == FBINFO_STATE_SUSPENDED)
-		return 0;
+	/* Powerup? */
+	if (state < 3) {
 
-	/* Tell everybody that the fbdev is going down */
-	acquire_console_sem();
-	fb_set_suspend(info, 1);
-	release_console_sem();
+		if (info->fbops->fb_power) {
+			ret = info->fbops->fb_power(info, state);
+			if (!ret)
+				fb_set_suspend(info, 0);
+		}
+	}
+	/* Powerdown? */
+	else {
+		/* Tell everybody that the fbdev is going down */
+		fb_set_suspend(info, 1);
 
-	if (info->fbops->fb_powerdown)
-		ret = info->fbops->fb_powerdown(info);
+		if (info->fbops->fb_power)
+			ret = info->fbops->fb_power(info, state);
 
-	/* If the power down failed, then un-notify */
+		/* If the power down failed, then un-notify */
+		if (ret)
+			fb_set_suspend(info, 0);
 
-	if (ret) {
-		acquire_console_sem();
-		fb_set_suspend(info, 0);
-		release_console_sem();
 	}
 
+	release_console_sem();
 	return ret;
 }
 
@@ -1474,10 +1468,10 @@ void fb_set_suspend(struct fb_info *info, int state)
 	struct fb_event event;
 
 	event.info = info;
-	if (state) {
+	if (state && info->state != FBINFO_STATE_SUSPENDED) {
 		fb_notifier_call_chain(FB_EVENT_SUSPEND, &event);
 		info->state = FBINFO_STATE_SUSPENDED;
-	} else {
+	} else if (info->state != FBINFO_STATE_RUNNING) {
 		info->state = FBINFO_STATE_RUNNING;
 		fb_notifier_call_chain(FB_EVENT_RESUME, &event);
 	}
diff --git a/drivers/video/geode/geodefb.h b/drivers/video/geode/geodefb.h
index 0214d11..cd1ce6e 100644
--- a/drivers/video/geode/geodefb.h
+++ b/drivers/video/geode/geodefb.h
@@ -12,12 +12,14 @@
 #ifndef __GEODEFB_H__
 #define __GEODEFB_H__
 
-#define FB_POWER_STATE_OFF      0
-#define FB_POWER_STATE_SUSPEND  1
-#define FB_POWER_STATE_ON       2
-
 struct geodefb_info;
 
+/* For geodefb_par->power_state */
+#define FB_POWER_STATE_OFF      3
+#define FB_POWER_STATE_SUSPEND  2
+#define FB_POWER_STATE_UNUSED   1 /* Reserved */
+#define FB_POWER_STATE_ON       0
+
 struct geode_dc_ops {
 	void (*set_mode)(struct fb_info *);
 	void (*set_palette_reg)(struct fb_info *, unsigned, unsigned, unsigned, unsigned);
@@ -42,7 +44,8 @@ struct geodefb_par {
 	struct geode_dc_ops  *dc_ops;
 	struct geode_vid_ops *vid_ops;
 
-	int state;
+	/* See FB_POWER_STATE_* definitions above */
+	int power_state;
 };
 
 #endif /* !__GEODEFB_H__ */
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c
index 3eabc53..cd43c8e 100644
--- a/drivers/video/geode/gxfb_core.c
+++ b/drivers/video/geode/gxfb_core.c
@@ -383,8 +383,7 @@ static struct fb_ops gxfb_ops = {
 	.fb_fillrect	= cfb_fillrect,
 	.fb_copyarea	= cfb_copyarea,
 	.fb_imageblit	= cfb_imageblit,
-	.fb_powerdown   = gxfb_powerdown,
-	.fb_powerup     = gxfb_powerup,
+	.fb_power       = gxfb_power,
 };
 
 static struct fb_info * __init gxfb_init_fbinfo(struct device *dev)
@@ -452,13 +451,13 @@ static int gxfb_suspend(struct pci_dev *pdev,  pm_message_t state)
 		return 0;
 
 	if (state.event == PM_EVENT_SUSPEND) {
-	 
+
 		acquire_console_sem();
-		gxfb_powerdown(info);
+		gxfb_power(info, 3);
 
-		par->state = FB_POWER_STATE_OFF;
+		par->power_state = FB_POWER_STATE_OFF;
 		fb_set_suspend(info, 1);
-		
+
 		release_console_sem();
 	}
 
@@ -471,10 +470,10 @@ static int gxfb_resume(struct pci_dev *pdev)
 	struct fb_info *info = pci_get_drvdata(pdev);
 
 	acquire_console_sem();
-	
+
 	/* Turn the engine completely on */
 
-	if (gxfb_powerup(info))
+	if (gxfb_power(info, 0))
 	  printk(KERN_ERR "gxfb:  Powerup failed\n");
 
 	fb_set_suspend(info, 0);
@@ -557,7 +556,7 @@ static int __init gxfb_probe(struct pci_dev *pdev,
 	gxfb_set_par(gxfb_info);
 
 	/* We are powered up */
-	par->state = FB_POWER_STATE_ON;
+	par->power_state = FB_POWER_STATE_ON;
 
 
 	console_event_register(&gxfb_console_notifier);
diff --git a/drivers/video/geode/lxfb.h b/drivers/video/geode/lxfb.h
index 28d8ff3..f886efd 100644
--- a/drivers/video/geode/lxfb.h
+++ b/drivers/video/geode/lxfb.h
@@ -25,8 +25,7 @@ void lx_set_mode(struct fb_info *);
 void lx_get_gamma(struct fb_info *, unsigned int *, int);
 void lx_set_gamma(struct fb_info *, unsigned int *, int);
 unsigned int lx_framebuffer_size(void);
-int lx_shutdown(struct fb_info *);
-int lx_powerup(struct fb_info *);
+int lx_power(struct fb_info *, int level);
 int lx_blank_display(struct fb_info *, int);
 void lx_set_palette_reg(struct fb_info *, unsigned int, unsigned int,
 			unsigned int, unsigned int);
diff --git a/drivers/video/geode/lxfb_core.c b/drivers/video/geode/lxfb_core.c
index 29197e2..b4019d5 100644
--- a/drivers/video/geode/lxfb_core.c
+++ b/drivers/video/geode/lxfb_core.c
@@ -302,8 +302,7 @@ static struct fb_ops lxfb_ops = {
 	.fb_fillrect	= cfb_fillrect,
 	.fb_copyarea	= cfb_copyarea,
 	.fb_imageblit	= cfb_imageblit,
-	.fb_powerdown	= lx_shutdown,
-	.fb_powerup	= lx_powerup,
+	.fb_power	= lx_power,
 };
 
 static struct fb_info * __init lxfb_init_fbinfo(struct device *dev)
@@ -356,8 +355,9 @@ static int lxfb_suspend(struct pci_dev *pdev,  pm_message_t state)
 
 	if (state.event == PM_EVENT_SUSPEND) {
 
+		/* Turn the engine completely off */
 		acquire_console_sem();
-		lx_shutdown(info);
+		lx_power(info, 3);
 		fb_set_suspend(info, 1);
 		release_console_sem();
 	}
@@ -370,11 +370,9 @@ static int lxfb_resume(struct pci_dev *pdev)
 {
 	struct fb_info *info = pci_get_drvdata(pdev);
 
-	acquire_console_sem();
-
 	/* Turn the engine completely on */
-
-	lx_powerup(info);
+	acquire_console_sem();
+	lx_power(info, 0);
 	fb_set_suspend(info, 0);
 	release_console_sem();
 
diff --git a/drivers/video/geode/lxfb_ops.c b/drivers/video/geode/lxfb_ops.c
index b2ecb4d..b380238 100644
--- a/drivers/video/geode/lxfb_ops.c
+++ b/drivers/video/geode/lxfb_ops.c
@@ -829,33 +829,42 @@ static void lx_restore_regs(struct fb_info *info, struct geoderegs *regs)
 	writel(regs->dc.r.dcfg, par->dc_regs + 0x08);
 }
 
-static int lx_power_on = 1;
+/*
+ * Level can be:
+ *  0 - fully powered on
+ *  1 - reserved/undefined
+ *  2 - power save (preserving state)
+ *  3 - fully powered off
+ */
+static int lx_power_state = 0;
 
-int lx_shutdown(struct fb_info *info)
+int lx_power(struct fb_info *info, int state)
 {
 	struct lxfb_par *par = info->par;
 
-	if (lx_power_on == 0)
-		return 0;
-
-	writel(DC_UNLOCK_CODE, par->dc_regs + DC_UNLOCK);
-	lx_save_regs(info, &saved_regs);
-	lx_graphics_disable(info);
+	if (state < 0 || state > 3)
+		return -EINVAL;
 
-	lx_power_on = 0;
-	return 0;
-}
+	/* Going into power save */
+	if (state >= 2 && lx_power_state < 2)
+		lx_graphics_disable(info);
 
-int lx_powerup(struct fb_info *info)
-{
-	struct lxfb_par *par = info->par;
+	/* Powering down */
+	if (state >= 3 && lx_power_state < 3) {
+		writel(DC_UNLOCK_CODE, par->dc_regs + DC_UNLOCK);
+		lx_save_regs(info, &saved_regs);
+	}
 
-	if (lx_power_on == 1)
-		return 0;
+	/* Restoring from power save */
+	if (state < 3 && lx_power_state >= 3) {
+		lx_restore_regs(info, &saved_regs);
+		writel(0, par->dc_regs + DC_UNLOCK);
+	}
 
-	lx_restore_regs(info, &saved_regs);
-	writel(0, par->dc_regs + DC_UNLOCK);
+	/* Powering up */
+	if (state < 2 && lx_power_state >= 2)
+		lx_graphics_enable(info);
 
-	lx_power_on = 1;
+	lx_power_state = state;
 	return 0;
 }
diff --git a/drivers/video/geode/video_gx.c b/drivers/video/geode/video_gx.c
index e282e74..7999f05 100644
--- a/drivers/video/geode/video_gx.c
+++ b/drivers/video/geode/video_gx.c
@@ -348,46 +348,46 @@ static void gx_configure_display(struct fb_info *info)
 		gx_configure_tft(info);
 }
 
-int gxfb_powerdown(struct fb_info *info) 
+int gxfb_power(struct fb_info *info, int state)
 {
 	struct geodefb_par *par = info->par;
 
-	/* We're already suspended */
-
-	if (par->state != FB_POWER_STATE_ON)
+	/* No state change */
+	if (par->power_state == state)
 		return 0;
 
-	/* Save the registers */
-	gx_save_regs(info, &gx_saved_regs);
-
-	/* Shut down the engine */
-
-	writel(gx_saved_regs.vp.r.vcfg & ~0x01, par->vid_regs + GX_VCFG);
-	writel(gx_saved_regs.vp.r.dcfg & ~0x0F, par->vid_regs + GX_DCFG);
+	/* Going into power save */
+	if (state >= FB_POWER_STATE_SUSPEND && par->power_state < FB_POWER_STATE_SUSPEND) {
+		/* Shut down the engine */
+		writel(gx_saved_regs.vp.r.vcfg & ~0x01, par->vid_regs + GX_VCFG);
+		writel(gx_saved_regs.vp.r.dcfg & ~0x0F, par->vid_regs + GX_DCFG);
 
-	/* Turn off the flat panel unless we are attached to a DCON */
-	if (!olpc_has_dcon())
-		writel(gx_saved_regs.fp.r.pm & ~GX_FP_PM_P, par->vid_regs + GX_FP_PM);
+		/* Turn off the flat panel unless we are attached to a DCON */
+		if (!olpc_has_dcon())
+			writel(gx_saved_regs.fp.r.pm & ~GX_FP_PM_P, par->vid_regs + GX_FP_PM);
 
-	writel(0x4758, par->dc_regs + DC_UNLOCK);
+		writel(0x4758, par->dc_regs + DC_UNLOCK);
 
-	writel(gx_saved_regs.dc.r.gcfg & ~0x0F,
-	       par->dc_regs + DC_GENERAL_CFG);
+		writel(gx_saved_regs.dc.r.gcfg & ~0x0F,
+		par->dc_regs + DC_GENERAL_CFG);
 
-	writel(gx_saved_regs.dc.r.dcfg & ~0x19,
-	       par->dc_regs + DC_DISPLAY_CFG);
-	
-	par->state = FB_POWER_STATE_SUSPEND;
+		writel(gx_saved_regs.dc.r.dcfg & ~0x19,
+		par->dc_regs + DC_DISPLAY_CFG);
+	}
 
-	return 0;
-}
+	/* Powering down */
+	if (state >= FB_POWER_STATE_OFF && par->power_state < FB_POWER_STATE_OFF) {
+		/* Save the registers */
+		gx_save_regs(info, &gx_saved_regs);
+	}
 
-int gxfb_powerup(struct fb_info *info)
-{
-	struct geodefb_par *par = info->par;
-	u32 val;
+	/* Restoring from power save */
+	if (state < FB_POWER_STATE_OFF && par->power_state >= FB_POWER_STATE_OFF) {
+		gx_restore_regs(info, &gx_saved_regs);
+	}
 
-	if (par->state == FB_POWER_STATE_SUSPEND) {
+	if (state < FB_POWER_STATE_SUSPEND && par->power_state >= FB_POWER_STATE_SUSPEND) {
+		u32 val;
 
 		writel(gx_saved_regs.dc.r.dcfg,
 		       par->dc_regs + DC_DISPLAY_CFG);
@@ -402,43 +402,30 @@ int gxfb_powerup(struct fb_info *info)
 			writel(gx_saved_regs.fp.r.pm, par->vid_regs + GX_FP_PM);
 			mdelay(64);
 		}
-	}
 
-	/* If the panel is currently on its way up, then wait up to 100ms
-	   for it */
-	
-	if (readl(par->vid_regs + GX_FP_PM) & 0x08) {
-		int i;
-		
-		for(i = 0; i < 10; i++) {
-			if (readl(par->vid_regs + GX_FP_PM) & 0x01)
-				break;
-
-			mdelay(10);
-		}
+		/* If the panel is currently on its way up, then wait up to 100ms for it */
+		if (readl(par->vid_regs + GX_FP_PM) & 0x08) {
+			int i;
 
-		if (i == 10) 
-			printk(KERN_ERR "gxfb:  Panel power up timed out\n");
-	}
+			for(i = 0; i < 10; i++) {
+				if (readl(par->vid_regs + GX_FP_PM) & 0x01)
+					break;
 
-	if (par->state == FB_POWER_STATE_ON)
-		return 0;
-	
-	switch(par->state) {
-	case FB_POWER_STATE_OFF:
-		gx_restore_regs(info, &gx_saved_regs);
-		break;
+				mdelay(10);
+			}
+
+			if (i == 10)
+				printk(KERN_ERR "gxfb:  Panel power up timed out\n");
+		}
 
-	case FB_POWER_STATE_SUSPEND:
 		/* Do this because it will turn on the FIFO which will
-	   	   start the line count */
+		   start the line count */
 		writel(gx_saved_regs.dc.r.gcfg,
 		       par->dc_regs + DC_GENERAL_CFG);
 		writel(0x0, par->dc_regs + DC_UNLOCK);
-		break;
 	}
 
-	par->state = FB_POWER_STATE_ON;
+	par->power_state = state;
 	return 0;
 }
 
diff --git a/drivers/video/geode/video_gx.h b/drivers/video/geode/video_gx.h
index c57b36b..f9efaa7 100644
--- a/drivers/video/geode/video_gx.h
+++ b/drivers/video/geode/video_gx.h
@@ -81,8 +81,7 @@ extern struct geode_vid_ops gx_vid_ops;
 #  define MSR_GLCP_DOTPLL_BYPASS		(0x0000000000008000ull)
 #  define MSR_GLCP_DOTPLL_LOCK			(0x0000000002000000ull)
 
-int gxfb_powerdown(struct fb_info *info);
-int gxfb_powerup(struct fb_info *info);
+int gxfb_power(struct fb_info *info, int state);
 
 void gx_set_dclk_frequency(struct fb_info *info);
 unsigned int gx_get_dclk(struct fb_info *info);
diff --git a/drivers/video/olpc_dcon.c b/drivers/video/olpc_dcon.c
index 47ade47..bb421f0 100644
--- a/drivers/video/olpc_dcon.c
+++ b/drivers/video/olpc_dcon.c
@@ -210,13 +210,13 @@ static void dcon_source_switch(struct work_struct *work)
 
 		/*
 		 * Ideally we'd like to disable interrupts here so that the
-		 * fb_powerup and DCON turn on happen at a known time value;
+		 * fb_power() and DCON turn on happen at a known time value;
 		 * however, we can't do that right now with fb_set_suspend
 		 * messing with semaphores.
 		 *
 		 * For now, we just hope..
 		 */
-		if (fb_powerup(fbinfo)) {
+		if (fb_power(fbinfo, 0)) {
 			printk(KERN_ERR "olpc-dcon:  Failed to enter CPU mode\n");
 			dcon_pending = DCON_SOURCE_DCON;
 			return;
@@ -250,8 +250,8 @@ static void dcon_source_switch(struct work_struct *work)
 		if (!dcon_switched)
 			printk(KERN_ERR "olpc-dcon: Timeout entering DCON mode; expect a screen glitch.\n");
 
-		/* Turn off the graphics engine completely */
-		fb_powerdown(fbinfo);
+		/* Turn off the video engine only */
+		fb_power(fbinfo, 2);
 
 		printk(KERN_INFO "olpc-dcon: The DCON has control\n");
 		break;
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 5a82c83..0acbd27 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -661,11 +661,8 @@ struct fb_ops {
 	/* restore saved state */
 	void (*fb_restore_state)(struct fb_info *info);
 
-	/* Shut down the graphics engine to save power */
-	int (*fb_powerdown)(struct fb_info *info);
-
-	/* Power it back up */
-	int (*fb_powerup)(struct fb_info *info);
+	/* Change the power state of the graphics engine (0 = fully on, 3 = fully off */
+	int (*fb_power)(struct fb_info *info, int state);
 
 	/* get capability given var */
 	void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
diff --git a/include/linux/vt.h b/include/linux/vt.h
index ba806e8..d8c5d65 100644
--- a/include/linux/vt.h
+++ b/include/linux/vt.h
@@ -6,8 +6,8 @@
  * resizing).
  */
 #define MIN_NR_CONSOLES 1       /* must be at least 1 */
-#define MAX_NR_CONSOLES	63	/* serial lines start at 64 */
-#define MAX_NR_USER_CONSOLES 63	/* must be root to allocate above this */
+#define MAX_NR_CONSOLES	15	/* serial lines start at 64 */
+#define MAX_NR_USER_CONSOLES 15	/* must be root to allocate above this */
 		/* Note: the ioctl VT_GETSTATE does not work for
 		   consoles 16 and higher (since it returns a short) */
 
diff --git a/kernel/sys.c b/kernel/sys.c


-- 
   //  Bernardo Innocenti - http://www.codewiz.org/
 \X/ One Laptop Per Child - http://www.laptop.org/



More information about the Devel mailing list