[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