diff --git a/drivers/video/nvidia/nv_accel.c b/drivers/video/nvidia/nv_accel.c index f377a29ec97a..4aefb8f41637 100644 --- a/drivers/video/nvidia/nv_accel.c +++ b/drivers/video/nvidia/nv_accel.c @@ -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 diff --git a/drivers/video/nvidia/nv_type.h b/drivers/video/nvidia/nv_type.h index e4a5b1da71c4..acdc26693402 100644 --- a/drivers/video/nvidia/nv_type.h +++ b/drivers/video/nvidia/nv_type.h @@ -129,6 +129,7 @@ struct nvidia_par { int fpHeight; int PanelTweak; int paneltweak; + int pm_state; u32 crtcSync_read; u32 fpSyncs; u32 dmaPut; diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index a7c4e5e8ead6..c758b386f146 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -21,6 +21,7 @@ #include #include #include +#include #ifdef CONFIG_MTRR #include #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), }; /* ------------------------------------------------------------------------- *