PATCH [2/2] - KFB power management

Jordan Crouse jordan.crouse at amd.com
Thu Mar 1 18:19:15 EST 2007


This is the very early beginnings of KFB power management.  By using the
previous patch to keep track of KD_TEXT and KD_GRAPHICS mode, we can
make assumptions about what we need to do to save the state of the
system.  To get the ball rolling, we do what it takes to restore
the system back to the console prompt (ie, when the current VT is in
KD_TEXT mode).  If we are in KD_TEXT mode, we are lucky, in that we
can just blast the current mode back into the registers when we come back
up - so no state needs to be saved.  As you can tell by the printks() and
FIXMEs - that won't work when X is involved - when we aren't in control of
the mode, we'll need to save off an incredible number of registers, including
ones we don't even access in the KFB, like the GP and VID registers.  I don't
want that extensive work to gate the simple suspend/resume path, so this
patch will do in the mean time.

Jordan
-------------- next part --------------
[PATCH] gxfb:  Add basic power management support

From: Jordan Crouse <jordan.crouse at amd.com>

When we "own" the framebuffer, we don't need to save any settings on
the way down, and we can just set the current mode on the way back up.
---

 drivers/video/geode/geodefb.h   |    1 
 drivers/video/geode/gxfb_core.c |  123 ++++++++++++++++++++++++++++++++++-----
 2 files changed, 107 insertions(+), 17 deletions(-)

diff --git a/drivers/video/geode/geodefb.h b/drivers/video/geode/geodefb.h
index 979bc2b..7b383fc 100644
--- a/drivers/video/geode/geodefb.h
+++ b/drivers/video/geode/geodefb.h
@@ -28,6 +28,7 @@ struct geode_vid_ops {
 
 struct geodefb_par {
 	int enable_crt;
+	int fbactive;  /* True if the current console is in KD_GRAPHICS mode */
 	int panel_x; /* dimensions of an attached flat panel, non-zero => enable panel */
 	int panel_y;
 	unsigned int curdclk;  /* Used by GX to avoid unnessesary clock switching */
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c
index 6646c25..e996f74 100644
--- a/drivers/video/geode/gxfb_core.c
+++ b/drivers/video/geode/gxfb_core.c
@@ -30,6 +30,9 @@ #include <linux/delay.h>
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/notifier.h>
+#include <linux/vt_kern.h>
+#include <linux/console.h>
 #include <asm/uaccess.h>
 #include <asm/olpc.h>
 
@@ -42,6 +45,7 @@ #define FBIOGGAMMA		_IOW('F', 0x21, void
 
 static char *mode_option;
 static int noclear;
+static struct fb_info *fbinfo;
 
 /* Modes relevant to the GX (taken from modedb.c) */
 static const struct fb_videomode gx_modedb[] __initdata = {
@@ -388,28 +392,106 @@ static struct fb_info * __init gxfb_init
 	return info;
 }
 
+static int gxfb_console_notify(struct notifier_block *self,
+				unsigned long action, void *data)
+{
+	if (fbinfo != NULL) {
+		struct geodefb_par *par = fbinfo->par;
+		par->fbactive = (action == CONSOLE_EVENT_SWITCH_TEXT) ? 0 : 1;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block gxfb_console_notifier = {
+	.notifier_call = gxfb_console_notify
+};
+
+#ifdef CONFIG_PM
+static int gxfb_suspend(struct pci_dev *pdev,  pm_message_t state)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	struct geodefb_par *par = info->par;
+
+	if (pdev->dev.power.power_state.event == state.event)
+		return 0;
+
+	if (state.event == PM_SUSPEND_MEM) {
+
+		/* If the FB is active, then somebody other then the console
+		 * owns it - we need to save the entire state of the
+		 * graphics engine to put back upon resume.  Otherwise,
+		 * we know what mode we are in right now, so we do nothing
+		 * here and resume that mode upon resume
+		 */
+
+		if (par->fbactive) {
+			printk(KERN_INFO "gxfb:  FIXME - I need to save the state!\n");
+		}
+
+
+		acquire_console_sem();
+		/* FIXME: Blank the screen here? */
+		fb_set_suspend(info, 1);
+		release_console_sem();
+	}
+
+	pdev->dev.power.power_state = state;
+	return 0;
+}
+
+static int gxfb_resume(struct pci_dev *pdev)
+{
+	struct fb_info *info = pci_get_drvdata(pdev);
+	struct geodefb_par *par = info->par;
+
+	/* If the FB was active, then restore the saved state -
+	 * otherwise, just set the PAR again - that will write
+	 * all the correct registers.
+	 */
+
+	if (par->fbactive) {
+		printk(KERN_INFO "gxfb: FIXME - I need to restore the state\n");
+	}
+	else {
+		gxfb_set_par(info);
+	}
+
+	/* Note:  If we end up blanking the screen during suspend,
+	 * we don't need to unblank it during resume - both of the
+	 * above paths will restore the screen to unblanked
+	 */
+
+	acquire_console_sem();
+	fb_set_suspend(info, 0);
+	release_console_sem();
+
+	pdev->dev.power.power_state = PMSG_ON;
+        return 0;
+}
+#endif
+
 static int __init gxfb_probe(struct pci_dev *pdev,
 			     const struct pci_device_id *id)
 {
 	struct geodefb_par *par;
-	struct fb_info *info;
 	int ret;
 	unsigned long val;
 
 	struct fb_videomode *modedb_ptr;
 	int modedb_size;
 
-	info = gxfb_init_fbinfo(&pdev->dev);
-	if (!info)
+	fbinfo = gxfb_init_fbinfo(&pdev->dev);
+	if (fbinfo == NULL)
 		return -ENOMEM;
 
-	par = info->par;
+	par = fbinfo->par;
 
 	/* GX display controller and GX video device. */
 	par->dc_ops  = &gx_dc_ops;
 	par->vid_ops = &gx_vid_ops;
 
-	if ((ret = gxfb_map_video_memory(info, pdev)) < 0) {
+	if ((ret = gxfb_map_video_memory(fbinfo, pdev)) < 0) {
 		dev_err(&pdev->dev, "failed to map frame buffer or controller registers\n");
 		goto err;
 	}
@@ -425,7 +507,7 @@ static int __init gxfb_probe(struct pci_
 
 	/* Get the current dotclock */
 
-	par->curdclk = (par->vid_ops->get_dclk) ? par->vid_ops->get_dclk(info) : 0;
+	par->curdclk = (par->vid_ops->get_dclk) ? par->vid_ops->get_dclk(fbinfo) : 0;
 
 	/* We need to determine a display mode right now, so we will
 	 * check to see if the DCON was previously detected by the BIOS
@@ -442,7 +524,7 @@ #ifdef CONFIG_OLPC
 	}
 #endif
 
-	ret = fb_find_mode(&info->var, info, mode_option,
+	ret = fb_find_mode(&fbinfo->var, fbinfo, mode_option,
 			   modedb_ptr, modedb_size, NULL, 16);
 
 	if (ret == 0 || ret == 4) {
@@ -455,22 +537,24 @@ #endif
 	 * in which case we assume the user knows what he is doing */
 
 	if (!noclear)
-		memset_io(info->screen_base, 0, info->fix.smem_len);
+		memset_io(fbinfo->screen_base, 0, fbinfo->fix.smem_len);
+
+	gxfb_check_var(&fbinfo->var, fbinfo);
+	gxfb_set_par(fbinfo);
 
-	gxfb_check_var(&info->var, info);
-	gxfb_set_par(info);
+	console_event_register(&gxfb_console_notifier);
 
-	if (register_framebuffer(info) < 0) {
+	if (register_framebuffer(fbinfo) < 0) {
 		ret = -EINVAL;
 		goto err;
 	}
-	pci_set_drvdata(pdev, info);
-	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
+	pci_set_drvdata(pdev, fbinfo);
+	printk(KERN_INFO "fb%d: %s frame buffer device\n", fbinfo->node, fbinfo->fix.id);
 	return 0;
 
   err:
-	if (info->screen_base) {
-		iounmap(info->screen_base);
+	if (fbinfo->screen_base) {
+		iounmap(fbinfo->screen_base);
 		pci_release_region(pdev, 0);
 	}
 	if (par->vid_regs) {
@@ -482,8 +566,9 @@ #endif
 		pci_release_region(pdev, 2);
 	}
 
-	if (info)
-		framebuffer_release(info);
+	if (fbinfo)
+		framebuffer_release(fbinfo);
+
 	return ret;
 }
 
@@ -522,6 +607,10 @@ static struct pci_driver gxfb_driver = {
 	.id_table	= gxfb_id_table,
 	.probe		= gxfb_probe,
 	.remove		= gxfb_remove,
+#ifdef CONFIG_PM
+	.suspend        = gxfb_suspend,
+	.resume         = gxfb_resume
+#endif
 };
 
 #ifndef MODULE


More information about the Devel mailing list