diff --git a/shared-core/radeon_drv.h b/shared-core/radeon_drv.h index 4a36ea7..f5529bd 100644 --- a/shared-core/radeon_drv.h +++ b/shared-core/radeon_drv.h @@ -493,6 +493,9 @@ extern int r300_do_cp_cmdbuf(drm_device_t *dev, DRMFILE filp, ? DRM_READ32( dev_priv->ring_rptr, RADEON_SCRATCHOFF(x) ) \ : RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x) ) ) +#define RADEON_CRTC_CRNT_FRAME 0x0214 +#define RADEON_CRTC2_CRNT_FRAME 0x0214 + #define RADEON_GEN_INT_CNTL 0x0040 # define RADEON_CRTC_VBLANK_MASK (1 << 0) # define RADEON_GUI_IDLE_INT_ENABLE (1 << 19) diff --git a/shared-core/radeon_irq.c b/shared-core/radeon_irq.c index 3ff0baa..cf71f01 100644 --- a/shared-core/radeon_irq.c +++ b/shared-core/radeon_irq.c @@ -127,11 +127,35 @@ static int radeon_wait_irq(drm_device_t * dev, int swi_nr) return ret; } +static void radeon_enable_vblank(drm_device_t *dev) +{ + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *) dev->dev_private; + unsigned long int_cntl; + + int_cntl = RADEON_READ(RADEON_GEN_INT_CNTL); + int_cntl |= RADEON_CRTC_VBLANK_MASK; + RADEON_WRITE(RADEON_GEN_INT_CNTL, int_cntl); +} + +static void radeon_disable_vblank(drm_device_t *dev) +{ + drm_radeon_private_t *dev_priv = + (drm_radeon_private_t *) dev->dev_private; + unsigned long int_cntl; + + int_cntl = RADEON_READ(RADEON_GEN_INT_CNTL); + int_cntl &= ~RADEON_CRTC_VBLANK_MASK; + RADEON_WRITE(RADEON_GEN_INT_CNTL, int_cntl); +} + +static unsigned long last_cnt; /* protected by dev->vbl_lock */ + int radeon_driver_vblank_wait(drm_device_t * dev, unsigned int *sequence) { drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; - unsigned int cur_vblank; + unsigned int cur_vblank, current_cnt, diff, irqflags; int ret = 0; if (!dev_priv) { @@ -143,13 +167,32 @@ int radeon_driver_vblank_wait(drm_device_t * dev, unsigned int *sequence) dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + /* + * Assume we haven't missed more than several hours of vblank + * events, or that it won't matter if they're not accounted + * for in the master counter. + */ + spin_lock_irqsave(&dev->vbl_lock, irqflags); + current_cnt = RADEON_READ(RADEON_CRTC_CRNT_FRAME); + if (current_cnt < last_cnt) { + current_cnt += (1 << 21) - last_cnt; + last_cnt = 0; + } + diff = current_cnt - last_cnt; + last_cnt = RADEON_READ(RADEON_CRTC_CRNT_FRAME); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + + atomic_add(diff, &dev->vbl_received); + /* Assume that the user has missed the current sequence number * by about a day rather than she wants to wait for years * using vertical blanks... */ + radeon_enable_vblank(dev); DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, (((cur_vblank = atomic_read(&dev->vbl_received)) - *sequence) <= (1 << 23))); + radeon_disable_vblank(dev); *sequence = cur_vblank; @@ -228,8 +271,7 @@ void radeon_driver_irq_postinstall(drm_device_t * dev) DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); /* Turn on SW and VBL ints */ - RADEON_WRITE(RADEON_GEN_INT_CNTL, - RADEON_CRTC_VBLANK_MASK | RADEON_SW_INT_ENABLE); + RADEON_WRITE(RADEON_GEN_INT_CNTL, RADEON_SW_INT_ENABLE); } void radeon_driver_irq_uninstall(drm_device_t * dev)