[PATCH] nvidiafb: add suspend and resume hooks
authorAntonino A. Daplas <adaplas@gmail.com>
Mon, 27 Mar 2006 09:17:22 +0000 (01:17 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 27 Mar 2006 16:44:55 +0000 (08:44 -0800)
Add suspend and resume hooks to make software suspend more reliable.  Resuming
from standby should generally work.  Resuming from mem and from disk requires
that the GPU is disabled.  Adding these to the suspend script...

fbset -accel false -a
/* suspend here */
fbset -accel true -a

...  should generally work.  In addition, resuming from mem requires that the
video card has to be POSTed by the BIOS or some other utility.

Signed-off-by: Antonino Daplas <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/video/nvidia/nv_accel.c
drivers/video/nvidia/nv_type.h
drivers/video/nvidia/nvidia.c

index f377a29ec97a6c9fc8a7188b5a9ac1ce29bd7c98..4aefb8f41637277c520edad864d369d7fbbe897b 100644 (file)
@@ -300,6 +300,9 @@ int nvidiafb_sync(struct fb_info *info)
 {
        struct nvidia_par *par = info->par;
 
+       if (info->state != FBINFO_STATE_RUNNING)
+               return 0;
+
        if (!par->lockup)
                NVFlush(par);
 
@@ -313,6 +316,9 @@ void nvidiafb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
 {
        struct nvidia_par *par = info->par;
 
+       if (info->state != FBINFO_STATE_RUNNING)
+               return;
+
        if (par->lockup)
                return cfb_copyarea(info, region);
 
@@ -329,6 +335,9 @@ void nvidiafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
        struct nvidia_par *par = info->par;
        u32 color;
 
+       if (info->state != FBINFO_STATE_RUNNING)
+               return;
+
        if (par->lockup)
                return cfb_fillrect(info, rect);
 
@@ -412,6 +421,9 @@ void nvidiafb_imageblit(struct fb_info *info, const struct fb_image *image)
 {
        struct nvidia_par *par = info->par;
 
+       if (info->state != FBINFO_STATE_RUNNING)
+               return;
+
        if (image->depth == 1 && !par->lockup)
                nvidiafb_mono_color_expand(info, image);
        else
index e4a5b1da71c433fff3d0c79de9f8cb28b21bd469..acdc26693402bb4486476bb9c8926ffed579eba9 100644 (file)
@@ -129,6 +129,7 @@ struct nvidia_par {
        int fpHeight;
        int PanelTweak;
        int paneltweak;
+       int pm_state;
        u32 crtcSync_read;
        u32 fpSyncs;
        u32 dmaPut;
index a7c4e5e8ead63781420bdb9d0a8c203d8f97a915..c758b386f146fc6cf11b76d98b46a90f08ef4d87 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/console.h>
 #ifdef CONFIG_MTRR
 #include <asm/mtrr.h>
 #endif
@@ -615,6 +616,30 @@ static int nvidia_panel_tweak(struct nvidia_par *par,
    return tweak;
 }
 
+static void nvidia_vga_protect(struct nvidia_par *par, int on)
+{
+       unsigned char tmp;
+
+       if (on) {
+               /*
+                * Turn off screen and disable sequencer.
+                */
+               tmp = NVReadSeq(par, 0x01);
+
+               NVWriteSeq(par, 0x00, 0x01);            /* Synchronous Reset */
+               NVWriteSeq(par, 0x01, tmp | 0x20);      /* disable the display */
+       } else {
+               /*
+                * Reenable sequencer, then turn on screen.
+                */
+
+               tmp = NVReadSeq(par, 0x01);
+
+               NVWriteSeq(par, 0x01, tmp & ~0x20);     /* reenable display */
+               NVWriteSeq(par, 0x00, 0x03);            /* End Reset */
+       }
+}
+
 static void nvidia_save_vga(struct nvidia_par *par,
                            struct _riva_hw_state *state)
 {
@@ -643,9 +668,9 @@ static void nvidia_save_vga(struct nvidia_par *par,
 
 #undef DUMP_REG
 
-static void nvidia_write_regs(struct nvidia_par *par)
+static void nvidia_write_regs(struct nvidia_par *par,
+                             struct _riva_hw_state *state)
 {
-       struct _riva_hw_state *state = &par->ModeReg;
        int i;
 
        NVTRACE_ENTER();
@@ -694,32 +719,6 @@ static void nvidia_write_regs(struct nvidia_par *par)
        NVTRACE_LEAVE();
 }
 
-static void nvidia_vga_protect(struct nvidia_par *par, int on)
-{
-       unsigned char tmp;
-
-       if (on) {
-               /*
-                * Turn off screen and disable sequencer.
-                */
-               tmp = NVReadSeq(par, 0x01);
-
-               NVWriteSeq(par, 0x00, 0x01);            /* Synchronous Reset */
-               NVWriteSeq(par, 0x01, tmp | 0x20);      /* disable the display */
-       } else {
-               /*
-                * Reenable sequencer, then turn on screen.
-                */
-
-               tmp = NVReadSeq(par, 0x01);
-
-               NVWriteSeq(par, 0x01, tmp & ~0x20);     /* reenable display */
-               NVWriteSeq(par, 0x00, 0x03);            /* End Reset */
-       }
-}
-
-
-
 static int nvidia_calc_regs(struct fb_info *info)
 {
        struct nvidia_par *par = info->par;
@@ -1068,7 +1067,8 @@ static int nvidiafb_set_par(struct fb_info *info)
 
        nvidia_vga_protect(par, 1);
 
-       nvidia_write_regs(par);
+       nvidia_write_regs(par, &par->ModeReg);
+       NVSetStartAddress(par, 0);
 
 #if defined (__BIG_ENDIAN)
        /* turn on LFB swapping */
@@ -1377,6 +1377,57 @@ static struct fb_ops nvidia_fb_ops = {
        .fb_sync        = nvidiafb_sync,
 };
 
+#ifdef CONFIG_PM
+static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t state)
+{
+       struct fb_info *info = pci_get_drvdata(dev);
+       struct nvidia_par *par = info->par;
+
+       acquire_console_sem();
+       par->pm_state = state.event;
+
+       if (state.event == PM_EVENT_FREEZE) {
+               dev->dev.power.power_state = state;
+       } else {
+               fb_set_suspend(info, 1);
+               nvidiafb_blank(FB_BLANK_POWERDOWN, info);
+               nvidia_write_regs(par, &par->SavedReg);
+               pci_save_state(dev);
+               pci_disable_device(dev);
+               pci_set_power_state(dev, pci_choose_state(dev, state));
+       }
+
+       release_console_sem();
+       return 0;
+}
+
+static int nvidiafb_resume(struct pci_dev *dev)
+{
+       struct fb_info *info = pci_get_drvdata(dev);
+       struct nvidia_par *par = info->par;
+
+       acquire_console_sem();
+       pci_set_power_state(dev, PCI_D0);
+
+       if (par->pm_state != PM_EVENT_FREEZE) {
+               pci_restore_state(dev);
+               pci_enable_device(dev);
+               pci_set_master(dev);
+       }
+
+       par->pm_state = PM_EVENT_ON;
+       nvidiafb_set_par(info);
+       fb_set_suspend (info, 0);
+       nvidiafb_blank(FB_BLANK_UNBLANK, info);
+
+       release_console_sem();
+       return 0;
+}
+#else
+#define nvidiafb_suspend NULL
+#define nvidiafb_resume NULL
+#endif
+
 static int __devinit nvidia_set_fbinfo(struct fb_info *info)
 {
        struct fb_monspecs *specs = &info->monspecs;
@@ -1798,8 +1849,10 @@ static int __devinit nvidiafb_setup(char *options)
 static struct pci_driver nvidiafb_driver = {
        .name = "nvidiafb",
        .id_table = nvidiafb_pci_tbl,
-       .probe = nvidiafb_probe,
-       .remove = __exit_p(nvidiafb_remove),
+       .probe    = nvidiafb_probe,
+       .suspend  = nvidiafb_suspend,
+       .resume   = nvidiafb_resume,
+       .remove   = __exit_p(nvidiafb_remove),
 };
 
 /* ------------------------------------------------------------------------- *