diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 00db300..4a00319 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -844,6 +844,7 @@ struct drm_device { once per disable */ u32 *vblank_premodeset; /* for compensation of spurious wraparounds */ int *vblank_suspend; /* Don't wait while crtc is likely disabled */ + u32 *vblank_presuspend; /* for compensation of spurious wraparounds */ struct timer_list vblank_disable_timer; u32 max_vblank_count; /**< size of vblank counter register */ @@ -1149,6 +1150,8 @@ extern void drm_update_vblank_count(struct drm_device *dev, int crtc); extern void drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); +extern void drm_vblank_suspend(struct drm_device *dev); +extern void drm_vblank_resume(struct drm_device *dev); /* Modesetting support */ extern int drm_modeset_ctl(struct drm_device *dev, void *data, diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index abedbe7..408586c 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -114,6 +114,8 @@ static void drm_vblank_cleanup(struct drm_device *dev) dev->num_crtcs, DRM_MEM_DRIVER); drm_free(dev->vblank_suspend, sizeof(*dev->vblank_suspend) * dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_presuspend, sizeof(*dev->vblank_presuspend) * + dev->num_crtcs, DRM_MEM_DRIVER); dev->num_crtcs = 0; } @@ -167,6 +169,11 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) if (!dev->vblank_suspend) goto err; + dev->vblank_presuspend = drm_calloc(num_crtcs, sizeof(u32), + DRM_MEM_DRIVER); + if (!dev->vblank_presuspend) + goto err; + /* Zero per-crtc vblank stuff */ for (i = 0; i < num_crtcs; i++) { init_waitqueue_head(&dev->vbl_queue[i]); @@ -434,6 +441,55 @@ void drm_vblank_put(struct drm_device *dev, int crtc) EXPORT_SYMBOL(drm_vblank_put); /** + * drm_vblank_suspend - handle lost vblank events during suspend + * @dev: DRM device + * + * Basically the same thing as the pre- & post-modeset ioctl, suspend/resume + * handles lost events while the device is suspended. + */ +void drm_vblank_suspend(struct drm_device *dev) +{ + int crtc; + + for (crtc = 0; crtc < dev->num_crtcs; crtc++) + dev->vblank_presuspend[crtc] = + dev->driver->get_vblank_counter(dev, crtc); +} + +/** + * drm_vblank_resume - account for lost vblank events during suspend + * @dev: DRM device + * + * At resume time, the vblank counter will likely have been reset, so deal + * with potential wraparound here just like we do for mode setting. + */ +void drm_vblank_resume(struct drm_device *dev) +{ + int crtc; + + for (crtc = 0; crtc < dev->num_crtcs; crtc++) { + u32 new; + + drm_update_vblank_count(dev, crtc); + new = dev->driver->get_vblank_counter(dev, crtc); + + /* + * Compensate for spurious wraparound in update_vblank_count + */ + if (new < dev->vblank_presuspend[crtc]) { + atomic_sub(dev->max_vblank_count + new - + dev->vblank_presuspend[crtc], + &dev->_vblank_count[crtc]); + DRM_ERROR("vblank_resume[%d]=0x%x, new=0x%x" + " => _vblank_count[%d]-=0x%x\n", crtc, + dev->vblank_presuspend[crtc], new, + crtc, dev->max_vblank_count + new - + dev->vblank_presuspend[crtc]); + } + } +} + +/** * drm_modeset_ctl - handle vblank event counter changes across mode switch * @DRM_IOCTL_ARGS: standard ioctl arguments * @@ -457,24 +513,34 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, dev->vblank_premodeset[crtc] = dev->driver->get_vblank_counter(dev, crtc); dev->vblank_suspend[crtc] = 1; + DRM_ERROR("got pre-modeset count of %u\n", + dev->vblank_premodeset[crtc]); break; case _DRM_POST_MODESET: if (dev->vblank_suspend[crtc]) { - u32 new = dev->driver->get_vblank_counter(dev, crtc); - - /* Compensate for spurious wraparound */ + u32 new; + + dev->vblank_suspend[crtc] = 0; + drm_update_vblank_count(dev, crtc); + new = dev->driver->get_vblank_counter(dev, crtc); + + /* + * Compensate for spurious wraparound + * Note that drm_update_vblank_count() will have been + * called already, so we subtract most of the + * wraparound value it added here. + */ if (new < dev->vblank_premodeset[crtc]) { atomic_sub(dev->max_vblank_count + new - dev->vblank_premodeset[crtc], &dev->_vblank_count[crtc]); - DRM_DEBUG("vblank_premodeset[%d]=0x%x, new=0x%x" + DRM_ERROR("vblank_premodeset[%d]=0x%x, new=0x%x" " => _vblank_count[%d]-=0x%x\n", crtc, dev->vblank_premodeset[crtc], new, crtc, dev->max_vblank_count + new - dev->vblank_premodeset[crtc]); } } - dev->vblank_suspend[crtc] = 0; break; default: ret = -EINVAL; @@ -541,6 +607,10 @@ int drm_wait_vblank(struct drm_device *dev, void *data, return -EINVAL; } + if (vblwait->request.sequence < seq) + DRM_ERROR("vblank wait on bad seq! req %u, cur %u\n", + vblwait->request.sequence, seq); + if ((flags & _DRM_VBLANK_NEXTONMISS) && (seq - vblwait->request.sequence) <= (1<<23)) { vblwait->request.sequence = seq + 1; @@ -607,8 +677,10 @@ int drm_wait_vblank(struct drm_device *dev, void *data, } else { if (!dev->vblank_suspend[crtc]) { ret = drm_vblank_get(dev, crtc); - if (ret) + if (ret) { + DRM_ERROR("get vblank failed: %d\n", ret); return ret; + } DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, ((drm_vblank_count(dev, crtc) - vblwait->request.sequence) <= (1 << 23))); diff --git a/linux-core/drm_sysfs.c b/linux-core/drm_sysfs.c index 3275942..55146c5 100644 --- a/linux-core/drm_sysfs.c +++ b/linux-core/drm_sysfs.c @@ -36,6 +36,8 @@ static int drm_sysfs_suspend(struct device *dev, pm_message_t state) printk(KERN_ERR "%s\n", __FUNCTION__); + drm_vblank_suspend(drm_dev); + if (drm_dev->driver->suspend) return drm_dev->driver->suspend(drm_dev, state); @@ -57,6 +59,8 @@ static int drm_sysfs_resume(struct device *dev) if (drm_dev->driver->resume) return drm_dev->driver->resume(drm_dev); + drm_vblank_resume(drm_dev); + return 0; } diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index a1964f4..a17bfa7 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -98,6 +98,8 @@ static int i915_resume(struct drm_device *dev) i915_restore_state(dev); + pci_set_master(dev->pdev); + return 0; } diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index 27d152c..140113b 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -260,7 +260,7 @@ static int i915_initialize(struct drm_device * dev, memset(dev_priv->hw_status_page, 0, PAGE_SIZE); - I915_WRITE(0x02080, dev_priv->dma_status_page); + I915_WRITE(HWS_PGA, dev_priv->dma_status_page); } DRM_DEBUG("Enabled hardware status page\n"); #ifdef I915_HAVE_BUFFER diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 3ba74db..0e21fee 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -231,6 +231,7 @@ typedef struct drm_i915_private { u32 saveIER; u32 saveIIR; u32 saveIMR; + u32 saveHWS; u32 saveCACHE_MODE_0; u32 saveD_STATE; u32 saveCG_2D_DIS; diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 0bf01bd..046019b 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -621,6 +621,8 @@ int i915_enable_vblank(struct drm_device *dev, int plane) u32 pipestat_reg = 0; u32 pipestat; + DRM_ERROR("enabling vblank on pipe %d\n", pipe); + switch (pipe) { case 0: pipestat_reg = PIPEASTAT; @@ -666,6 +668,8 @@ void i915_disable_vblank(struct drm_device *dev, int plane) u32 pipestat_reg = 0; u32 pipestat; + DRM_ERROR("disabling vblank on pipe %d\n", pipe); + switch (pipe) { case 0: pipestat_reg = PIPEASTAT; diff --git a/shared-core/i915_suspend.c b/shared-core/i915_suspend.c index 63cd54a..580f425 100644 --- a/shared-core/i915_suspend.c +++ b/shared-core/i915_suspend.c @@ -335,6 +335,7 @@ int i915_save_state(struct drm_device *dev) dev_priv->saveIIR = I915_READ(IIR); dev_priv->saveIER = I915_READ(IER); dev_priv->saveIMR = I915_READ(IMR); + dev_priv->saveHWS = I915_READ(HWS_PGA); /* VGA state */ dev_priv->saveVGA0 = I915_READ(VGA0); @@ -415,6 +416,7 @@ int i915_restore_state(struct drm_device *dev) } I915_WRITE(PIPEACONF, dev_priv->savePIPEACONF); + I915_WRITE(PIPEASTAT, dev_priv->savePIPEASTAT); i915_restore_palette(dev, PIPE_A); /* Enable the plane */ @@ -457,6 +459,7 @@ int i915_restore_state(struct drm_device *dev) } I915_WRITE(PIPEBCONF, dev_priv->savePIPEBCONF); + I915_WRITE(PIPEBSTAT, dev_priv->savePIPEBSTAT); i915_restore_palette(dev, PIPE_B); /* Enable the plane */ @@ -489,6 +492,12 @@ int i915_restore_state(struct drm_device *dev) I915_WRITE(FBC_CONTROL2, dev_priv->saveFBC_CONTROL2); I915_WRITE(FBC_CONTROL, dev_priv->saveFBC_CONTROL); + /* Interrupt state */ + I915_WRITE(IIR, dev_priv->saveIIR); + I915_WRITE(IER, dev_priv->saveIER); + I915_WRITE(IMR, dev_priv->saveIMR); + I915_WRITE(HWS_PGA, dev_priv->saveHWS); + /* VGA state */ I915_WRITE(VGACNTRL, dev_priv->saveVGACNTRL); I915_WRITE(VGA0, dev_priv->saveVGA0);