[PATCH 3/3] [PATCH] gxfb: Add basic power management support

Jordan Crouse jordan.crouse at amd.com
Fri Mar 2 16:36:49 EST 2007


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