From 462d5a0dfc80dfa02da3d24d30ad90ad0387f0a2 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 22 May 2007 17:49:04 -0700 Subject: [PATCH] Suspend/resume support (incomplete). --- linux-core/i915_drv.c | 231 +++++++++++++++++++++++++++++++++++++++++ shared-core/i915_drv.h | 86 +++++++++++++++ shared-core/i915_irq.c | 13 +++ 3 files changed, 330 insertions(+) diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index 4d4029aa..ffdbab06 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -70,6 +70,235 @@ static drm_bo_driver_t i915_bo_driver = { }; #endif +static int i915_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_output *output; + int i; + + pci_save_state(pdev); + + /* Save video mode information for native mode-setting. */ + dev_priv->saveDSPACNTR = I915_READ(DSPACNTR); + dev_priv->savePIPEACONF = I915_READ(PIPEACONF); + dev_priv->savePIPEASRC = I915_READ(PIPEASRC); + dev_priv->saveFPA0 = I915_READ(FPA0); + dev_priv->saveFPA1 = I915_READ(FPA1); + dev_priv->saveDPLL_A = I915_READ(DPLL_A); + if (IS_I965G(dev)) + dev_priv->saveDPLL_A_MD = I915_READ(DPLL_A_MD); + dev_priv->saveHTOTAL_A = I915_READ(HTOTAL_A); + dev_priv->saveHBLANK_A = I915_READ(HBLANK_A); + dev_priv->saveHSYNC_A = I915_READ(HSYNC_A); + dev_priv->saveVTOTAL_A = I915_READ(VTOTAL_A); + dev_priv->saveVBLANK_A = I915_READ(VBLANK_A); + dev_priv->saveVSYNC_A = I915_READ(VSYNC_A); + dev_priv->saveDSPASTRIDE = I915_READ(DSPASTRIDE); + dev_priv->saveDSPASIZE = I915_READ(DSPASIZE); + dev_priv->saveDSPAPOS = I915_READ(DSPAPOS); + dev_priv->saveDSPABASE = I915_READ(DSPABASE); + + for(i= 0; i < 256; i++) + dev_priv->savePaletteA[i] = I915_READ(PALETTE_A + (i << 2)); + + if(dev->mode_config.num_crtc == 2) { + dev_priv->savePIPEBCONF = I915_READ(PIPEBCONF); + dev_priv->savePIPEBSRC = I915_READ(PIPEBSRC); + dev_priv->saveDSPBCNTR = I915_READ(DSPBCNTR); + dev_priv->saveFPB0 = I915_READ(FPB0); + dev_priv->saveFPB1 = I915_READ(FPB1); + dev_priv->saveDPLL_B = I915_READ(DPLL_B); + if (IS_I965G(dev)) + dev_priv->saveDPLL_B_MD = I915_READ(DPLL_B_MD); + dev_priv->saveHTOTAL_B = I915_READ(HTOTAL_B); + dev_priv->saveHBLANK_B = I915_READ(HBLANK_B); + dev_priv->saveHSYNC_B = I915_READ(HSYNC_B); + dev_priv->saveVTOTAL_B = I915_READ(VTOTAL_B); + dev_priv->saveVBLANK_B = I915_READ(VBLANK_B); + dev_priv->saveVSYNC_B = I915_READ(VSYNC_B); + dev_priv->saveDSPBSTRIDE = I915_READ(DSPBSTRIDE); + dev_priv->saveDSPBSIZE = I915_READ(DSPBSIZE); + dev_priv->saveDSPBPOS = I915_READ(DSPBPOS); + dev_priv->saveDSPBBASE = I915_READ(DSPBBASE); + for(i= 0; i < 256; i++) + dev_priv->savePaletteB[i] = + I915_READ(PALETTE_B + (i << 2)); + } + + if (IS_I965G(dev)) { + dev_priv->saveDSPASURF = I915_READ(DSPASURF); + dev_priv->saveDSPBSURF = I915_READ(DSPBSURF); + } + + dev_priv->saveVCLK_DIVISOR_VGA0 = I915_READ(VCLK_DIVISOR_VGA0); + dev_priv->saveVCLK_DIVISOR_VGA1 = I915_READ(VCLK_DIVISOR_VGA1); + dev_priv->saveVCLK_POST_DIV = I915_READ(VCLK_POST_DIV); + dev_priv->saveVGACNTRL = I915_READ(VGACNTRL); + + for(i = 0; i < 7; i++) { + dev_priv->saveSWF[i] = I915_READ(SWF0 + (i << 2)); + dev_priv->saveSWF[i+7] = I915_READ(SWF00 + (i << 2)); + } + dev_priv->saveSWF[14] = I915_READ(SWF30); + dev_priv->saveSWF[15] = I915_READ(SWF31); + dev_priv->saveSWF[16] = I915_READ(SWF32); + + if (IS_MOBILE(dev) && !IS_I830(dev)) + dev_priv->saveLVDS = I915_READ(LVDS); + dev_priv->savePFIT_CONTROL = I915_READ(PFIT_CONTROL); + + list_for_each_entry(output, &dev->mode_config.output_list, head) + if (output->funcs->save) + (*output->funcs->save) (output); + +#if 0 /* FIXME: save VGA bits */ + vgaHWUnlock(hwp); + vgaHWSave(pScrn, vgaReg, VGA_SR_FONTS); +#endif + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int i915_resume(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_output *output; + struct drm_crtc *crtc; + int i; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_device(pdev); + + /* Disable outputs */ + list_for_each_entry(output, &dev->mode_config.output_list, head) + output->funcs->dpms(output, DPMSModeOff); + + i915_driver_wait_next_vblank(dev, 0); + + /* Disable pipes */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + crtc->funcs->dpms(crtc, DPMSModeOff); + + /* FIXME: wait for vblank on each pipe? */ + i915_driver_wait_next_vblank(dev, 0); + + if (IS_MOBILE(dev) && !IS_I830(dev)) + I915_WRITE(LVDS, dev_priv->saveLVDS); + + if (!IS_I830(dev) && !IS_845G(dev)) + I915_WRITE(PFIT_CONTROL, dev_priv->savePFIT_CONTROL); + + if (dev_priv->saveDPLL_A & DPLL_VCO_ENABLE) { + I915_WRITE(DPLL_A, dev_priv->saveDPLL_A & ~DPLL_VCO_ENABLE); + udelay(150); + } + I915_WRITE(FPA0, dev_priv->saveFPA0); + I915_WRITE(FPA1, dev_priv->saveFPA1); + I915_WRITE(DPLL_A, dev_priv->saveDPLL_A); + udelay(150); + if (IS_I965G(dev)) + I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD); + else + I915_WRITE(DPLL_A, dev_priv->saveDPLL_A); + udelay(150); + + I915_WRITE(HTOTAL_A, dev_priv->saveHTOTAL_A); + I915_WRITE(HBLANK_A, dev_priv->saveHBLANK_A); + I915_WRITE(HSYNC_A, dev_priv->saveHSYNC_A); + I915_WRITE(VTOTAL_A, dev_priv->saveVTOTAL_A); + I915_WRITE(VBLANK_A, dev_priv->saveVBLANK_A); + I915_WRITE(VSYNC_A, dev_priv->saveVSYNC_A); + + I915_WRITE(DSPASTRIDE, dev_priv->saveDSPASTRIDE); + I915_WRITE(DSPASIZE, dev_priv->saveDSPASIZE); + I915_WRITE(DSPAPOS, dev_priv->saveDSPAPOS); + I915_WRITE(PIPEASRC, dev_priv->savePIPEASRC); + I915_WRITE(DSPABASE, dev_priv->saveDSPABASE); + if (IS_I965G(dev)) + I915_WRITE(DSPASURF, dev_priv->saveDSPASURF); + I915_WRITE(PIPEACONF, dev_priv->savePIPEACONF); + i915_driver_wait_next_vblank(dev, 0); + I915_WRITE(DSPACNTR, dev_priv->saveDSPACNTR); + I915_WRITE(DSPABASE, I915_READ(DSPABASE)); + i915_driver_wait_next_vblank(dev, 0); + + if(dev->mode_config.num_crtc == 2) { + if (dev_priv->saveDPLL_B & DPLL_VCO_ENABLE) { + I915_WRITE(DPLL_B, dev_priv->saveDPLL_B & ~DPLL_VCO_ENABLE); + udelay(150); + } + I915_WRITE(FPB0, dev_priv->saveFPB0); + I915_WRITE(FPB1, dev_priv->saveFPB1); + I915_WRITE(DPLL_B, dev_priv->saveDPLL_B); + udelay(150); + if (IS_I965G(dev)) + I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD); + else + I915_WRITE(DPLL_B, dev_priv->saveDPLL_B); + udelay(150); + + I915_WRITE(HTOTAL_B, dev_priv->saveHTOTAL_B); + I915_WRITE(HBLANK_B, dev_priv->saveHBLANK_B); + I915_WRITE(HSYNC_B, dev_priv->saveHSYNC_B); + I915_WRITE(VTOTAL_B, dev_priv->saveVTOTAL_B); + I915_WRITE(VBLANK_B, dev_priv->saveVBLANK_B); + I915_WRITE(VSYNC_B, dev_priv->saveVSYNC_B); + I915_WRITE(DSPBSTRIDE, dev_priv->saveDSPBSTRIDE); + I915_WRITE(DSPBSIZE, dev_priv->saveDSPBSIZE); + I915_WRITE(DSPBPOS, dev_priv->saveDSPBPOS); + I915_WRITE(PIPEBSRC, dev_priv->savePIPEBSRC); + I915_WRITE(DSPBBASE, dev_priv->saveDSPBBASE); + if (IS_I965G(dev)) + I915_WRITE(DSPBSURF, dev_priv->saveDSPBSURF); + I915_WRITE(PIPEBCONF, dev_priv->savePIPEBCONF); + i915_driver_wait_next_vblank(dev, 0); + I915_WRITE(DSPBCNTR, dev_priv->saveDSPBCNTR); + I915_WRITE(DSPBBASE, I915_READ(DSPBBASE)); + i915_driver_wait_next_vblank(dev, 0); + } + + /* Restore outputs */ + list_for_each_entry(output, &dev->mode_config.output_list, head) + if (output->funcs->restore) + output->funcs->restore(output); + + I915_WRITE(VGACNTRL, dev_priv->saveVGACNTRL); + + I915_WRITE(VCLK_DIVISOR_VGA0, dev_priv->saveVCLK_DIVISOR_VGA0); + I915_WRITE(VCLK_DIVISOR_VGA1, dev_priv->saveVCLK_DIVISOR_VGA1); + I915_WRITE(VCLK_POST_DIV, dev_priv->saveVCLK_POST_DIV); + + for(i = 0; i < 256; i++) + I915_WRITE(PALETTE_A + (i << 2), dev_priv->savePaletteA[i]); + + if(dev->mode_config.num_crtc == 2) + for(i= 0; i < 256; i++) + I915_WRITE(PALETTE_B + (i << 2), dev_priv->savePaletteB[i]); + + for(i = 0; i < 7; i++) { + I915_WRITE(SWF0 + (i << 2), dev_priv->saveSWF[i]); + I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF[i+7]); + } + + I915_WRITE(SWF30, dev_priv->saveSWF[14]); + I915_WRITE(SWF31, dev_priv->saveSWF[15]); + I915_WRITE(SWF32, dev_priv->saveSWF[16]); + +#if 0 /* FIXME: restore VGA bits */ + vgaHWRestore(pScrn, vgaReg, VGA_SR_FONTS); + vgaHWLock(hwp); +#endif + + drm_initial_config(dev, 0); + + return 0; +} + static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); static struct drm_driver driver = { /* don't use mtrr's here, the Xserver or user space app should @@ -113,6 +342,8 @@ static struct drm_driver driver = { .id_table = pciidlist, .probe = probe, .remove = __devexit_p(drm_cleanup_pci), + .suspend = i915_suspend, + .resume = i915_resume, }, #ifdef I915_HAVE_FENCE .fence_driver = &i915_fence_driver, diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index c41fbbc5..b0aa5df1 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -227,6 +227,7 @@ extern int i915_dma_cleanup(drm_device_t * dev); extern int i915_irq_emit(DRM_IOCTL_ARGS); extern int i915_irq_wait(DRM_IOCTL_ARGS); +extern void i915_driver_wait_next_vblank(drm_device_t *dev, int pipe); extern int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence); extern int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence); extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); @@ -395,6 +396,39 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller); # define GPIO_DATA_VAL_IN (1 << 12) # define GPIO_DATA_PULLUP_DISABLE (1 << 13) +/* p317, 319 + */ +#define VCLK2_VCO_M 0x6008 /* treat as 16 bit? (includes msbs) */ +#define VCLK2_VCO_N 0x600a +#define VCLK2_VCO_DIV_SEL 0x6012 + +#define VCLK_DIVISOR_VGA0 0x6000 +#define VCLK_DIVISOR_VGA1 0x6004 +#define VCLK_POST_DIV 0x6010 +/** Selects a post divisor of 4 instead of 2. */ +# define VGA1_PD_P2_DIV_4 (1 << 15) +/** Overrides the p2 post divisor field */ +# define VGA1_PD_P1_DIV_2 (1 << 13) +# define VGA1_PD_P1_SHIFT 8 +/** P1 value is 2 greater than this field */ +# define VGA1_PD_P1_MASK (0x1f << 8) +/** Selects a post divisor of 4 instead of 2. */ +# define VGA0_PD_P2_DIV_4 (1 << 7) +/** Overrides the p2 post divisor field */ +# define VGA0_PD_P1_DIV_2 (1 << 5) +# define VGA0_PD_P1_SHIFT 0 +/** P1 value is 2 greater than this field */ +# define VGA0_PD_P1_MASK (0x1f << 0) + +#define POST_DIV_SELECT 0x70 +#define POST_DIV_1 0x00 +#define POST_DIV_2 0x10 +#define POST_DIV_4 0x20 +#define POST_DIV_8 0x30 +#define POST_DIV_16 0x40 +#define POST_DIV_32 0x50 +#define VCO_LOOP_DIV_BY_4M 0x00 +#define VCO_LOOP_DIV_BY_16M 0x04 #define SRX_INDEX 0x3c4 #define SRX_DATA 0x3c5 @@ -906,6 +940,58 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller); # define VGA_2X_MODE (1 << 30) # define VGA_PIPE_B_SELECT (1 << 29) +/* + * Some BIOS scratch area registers. The 845 (and 830?) store the amount + * of video memory available to the BIOS in SWF1. + */ + +#define SWF0 0x71410 +#define SWF1 0x71414 +#define SWF2 0x71418 +#define SWF3 0x7141c +#define SWF4 0x71420 +#define SWF5 0x71424 +#define SWF6 0x71428 + +/* + * 855 scratch registers. + */ +#define SWF00 0x70410 +#define SWF01 0x70414 +#define SWF02 0x70418 +#define SWF03 0x7041c +#define SWF04 0x70420 +#define SWF05 0x70424 +#define SWF06 0x70428 + +#define SWF10 SWF0 +#define SWF11 SWF1 +#define SWF12 SWF2 +#define SWF13 SWF3 +#define SWF14 SWF4 +#define SWF15 SWF5 +#define SWF16 SWF6 + +#define SWF30 0x72414 +#define SWF31 0x72418 +#define SWF32 0x7241c + +/* + * Overlay registers. These are overlay registers accessed via MMIO. + * Those loaded via the overlay register page are defined in i830_video.c. + */ +#define OVADD 0x30000 + +#define DOVSTA 0x30008 +#define OC_BUF (0x3<<20) + +#define OGAMC5 0x30010 +#define OGAMC4 0x30014 +#define OGAMC3 0x30018 +#define OGAMC2 0x3001c +#define OGAMC1 0x30020 +#define OGAMC0 0x30024 + /* * Palette registers */ diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 4ceed3e3..2d4df76e 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -438,6 +438,19 @@ static int i915_driver_vblank_do_wait(drm_device_t *dev, unsigned int *sequence, return ret; } +void i915_driver_wait_next_vblank(drm_device_t *dev, int pipe) +{ + unsigned int seq; + + seq = pipe ? atomic_read(&dev->vbl_received2) + 1 : + atomic_read(&dev->vbl_received) + 1; + + if (!pipe) + i915_driver_vblank_do_wait(dev, &seq, &dev->vbl_received); + else + i915_driver_vblank_do_wait(dev, &seq, &dev->vbl_received2); +} + int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) { return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received);