diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 00db300..4d87258 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -1149,6 +1149,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..26b9ec3 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -434,6 +434,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_premodeset[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_premodeset[crtc]) { + atomic_sub(dev->max_vblank_count + new - + dev->vblank_premodeset[crtc], + &dev->_vblank_count[crtc]); + DRM_DEBUG("vblank_resumet[%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]); + } + } +} + +/** * drm_modeset_ctl - handle vblank event counter changes across mode switch * @DRM_IOCTL_ARGS: standard ioctl arguments * @@ -462,7 +511,12 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, if (dev->vblank_suspend[crtc]) { u32 new = dev->driver->get_vblank_counter(dev, crtc); - /* Compensate for spurious wraparound */ + /* + * 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], 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; }