From db689c7b95613237cec904c3f6ee27e8c2bf7ce0 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 12 Jun 2007 10:44:21 -0700 Subject: Initial checkin of vblank rework. Code attempts to reduce the number of vblank interrupt in order to save power. --- linux-core/drmP.h | 12 ++-- linux-core/drm_irq.c | 69 +++++++++++++++----- linux-core/i915_drv.c | 5 +- shared-core/i915_dma.c | 14 ++++ shared-core/i915_drm.h | 2 + shared-core/i915_drv.h | 39 ++++++++++- shared-core/i915_irq.c | 164 +++++++++++++++++++++++++++++++++++------------ shared-core/radeon_drv.h | 10 ++- shared-core/radeon_irq.c | 75 ++++++++++++++-------- 9 files changed, 297 insertions(+), 93 deletions(-) diff --git a/linux-core/drmP.h b/linux-core/drmP.h index dd3a69df..c3f20311 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -627,8 +627,9 @@ struct drm_driver { int (*kernel_context_switch) (struct drm_device * dev, int old, int new); void (*kernel_context_switch_unlock) (struct drm_device * dev); - int (*vblank_wait) (struct drm_device * dev, unsigned int *sequence); - int (*vblank_wait2) (struct drm_device * dev, unsigned int *sequence); + u32 (*get_vblank_counter) (struct drm_device *dev, int crtc); + void (*enable_vblank) (struct drm_device *dev, int crtc); + void (*disable_vblank) (struct drm_device *dev, int crtc); int (*dri_library_name) (struct drm_device * dev, char * buf); /** @@ -783,12 +784,13 @@ typedef struct drm_device { /*@{ */ wait_queue_head_t vbl_queue; /**< VBLANK wait queue */ - atomic_t vbl_received; - atomic_t vbl_received2; /**< number of secondary VBLANK interrupts */ + atomic_t *vblank_count; /**< number of VBLANK interrupts (driver must alloc the right number of counters) */ spinlock_t vbl_lock; struct list_head vbl_sigs; /**< signal list to send on VBLANK */ struct list_head vbl_sigs2; /**< signals to send on secondary VBLANK */ - unsigned int vbl_pending; + atomic_t *vbl_pending; + u32 *last_vblank; /* protected by dev->vbl_lock */ + unsigned long max_vblank_count; /**< size of vblank counter register */ spinlock_t tasklet_lock; /**< For drm_locked_tasklet */ void (*locked_tasklet_func)(struct drm_device *dev); diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 88716712..f229f778 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -221,6 +221,44 @@ int drm_control(struct inode *inode, struct file *filp, } } +static void drm_vblank_get(drm_device_t *dev, int crtc) +{ + unsigned long irqflags; + u32 cur_vblank, diff; + + if (atomic_add_return(1, &dev->vbl_pending[crtc]) != 1) + return; + + /* + * Interrupts were disabled prior to this call, so deal with counter + * wrap if needed. + * NOTE! It's possible we lost a full dev->max_vblank_count events + * here if the register is small or we had vblank interrupts off for + * a long time. + */ + cur_vblank = dev->driver->get_vblank_counter(dev, crtc); + spin_lock_irqsave(&dev->vbl_lock, irqflags); + if (cur_vblank < dev->last_vblank[crtc]) { + diff = dev->max_vblank_count - + dev->last_vblank[crtc]; + diff += cur_vblank; + } else { + diff = cur_vblank - last_vblank; + } + dev->last_vblank[crtc] = cur_vblank; + spin_lock_irqrestore(&dev->vbl_lock, irqflags); + + atomic_add(diff, &dev->vblank_count[crtc]); + dev->driver->enable_vblank(dev, crtc); +} + +static void drm_vblank_put(drm_device_t *dev, int crtc) +{ + if (atomic_dec_and_test(&dev->vbl_pending[crtc])) + dev->driver->disable_vblank(dev, crtc); +} + + /** * Wait for VBLANK. * @@ -248,7 +286,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) drm_wait_vblank_t vblwait; struct timeval now; int ret = 0; - unsigned int flags, seq; + unsigned int flags, seq, crtc; if ((!dev->irq) || (!dev->irq_enabled)) return -EINVAL; @@ -265,13 +303,14 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) } flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; + crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ? DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL)) return -EINVAL; - seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 - : &dev->vbl_received); + drm_vblank_get(dev, crtc); + seq = atomic_read(&dev->vblank_count[crtc]); switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) { case _DRM_VBLANK_RELATIVE: @@ -307,22 +346,23 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev->vbl_lock, irqflags); vblwait.reply.sequence = seq; + drm_vblank_put(dev, crtc); goto done; } } - if (dev->vbl_pending >= 100) { + if (atomic_read(&dev->vbl_pending[crtc]) >= 100) { spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + drm_vblank_put(dev, crtc); return -EBUSY; } - dev->vbl_pending++; - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); if (! (vbl_sig = drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) { + drm_vblank_put(dev, crtc); return -ENOMEM; } @@ -340,14 +380,12 @@ int drm_wait_vblank(DRM_IOCTL_ARGS) vblwait.reply.sequence = seq; } else { - if (flags & _DRM_VBLANK_SECONDARY) { - if (dev->driver->vblank_wait2) - ret = dev->driver->vblank_wait2(dev, &vblwait.request.sequence); - } else if (dev->driver->vblank_wait) - ret = - dev->driver->vblank_wait(dev, - &vblwait.request.sequence); + unsigned long cur_vblank; + DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, + (((cur_vblank = atomic_read(&dev->vblank_count[crtc])) + - seq) <= (1 << 23))); + drm_vblank_put(dev, crtc); do_gettimeofday(&now); vblwait.reply.tval_sec = now.tv_sec; vblwait.reply.tval_usec = now.tv_usec; @@ -379,8 +417,7 @@ void drm_vbl_send_signals(drm_device_t * dev) for (i = 0; i < 2; i++) { drm_vbl_sig_t *vbl_sig, *tmp; struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs; - unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 : - &dev->vbl_received); + unsigned int vbl_seq = atomic_read(&dev->vblank_count[i]); list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { @@ -393,7 +430,7 @@ void drm_vbl_send_signals(drm_device_t * dev) drm_free(vbl_sig, sizeof(*vbl_sig), DRM_MEM_DRIVER); - dev->vbl_pending--; + drm_vblank_put(dev, i); } } } diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index 7fdb0839..e7a9bfdc 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -83,8 +83,9 @@ static struct drm_driver driver = { .lastclose = i915_driver_lastclose, .preclose = i915_driver_preclose, .device_is_agp = i915_driver_device_is_agp, - .vblank_wait = i915_driver_vblank_wait, - .vblank_wait2 = i915_driver_vblank_wait2, + .get_vblank_counter = i915_get_vblank_counter, + .enable_vblank = i915_enable_vblank, + .disable_vblank = i915_disable_vblank, .irq_preinstall = i915_driver_irq_preinstall, .irq_postinstall = i915_driver_irq_postinstall, .irq_uninstall = i915_driver_irq_uninstall, diff --git a/shared-core/i915_dma.c b/shared-core/i915_dma.c index ebb184cc..73972d56 100644 --- a/shared-core/i915_dma.c +++ b/shared-core/i915_dma.c @@ -833,6 +833,20 @@ static int i915_setparam(DRM_IOCTL_ARGS) case I915_SETPARAM_ALLOW_BATCHBUFFER: dev_priv->allow_batchbuffer = param.value; break; + case I915_SETPARAM_PREMODESET: + if (param.value > 1) { + DRM_ERROR("bad crtc\n"); + return -EINVAL; + } + i915_premodeset(dev, param.value); + break; + case I915_SETPARAM_POSTMODESET: + if (param.value > 1) { + DRM_ERROR("bad crtc\n"); + return -EINVAL; + } + i915_postmodeset(dev, param.value); + break; default: DRM_ERROR("unknown parameter %d\n", param.param); return DRM_ERR(EINVAL); diff --git a/shared-core/i915_drm.h b/shared-core/i915_drm.h index 1c6ff4d3..aea048fa 100644 --- a/shared-core/i915_drm.h +++ b/shared-core/i915_drm.h @@ -235,6 +235,8 @@ typedef struct drm_i915_getparam { #define I915_SETPARAM_USE_MI_BATCHBUFFER_START 1 #define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY 2 #define I915_SETPARAM_ALLOW_BATCHBUFFER 3 +#define I915_SETPARAM_PREMODESET 4 +#define I915_SETPARAM_POSTMODESET 5 typedef struct drm_i915_setparam { int param; diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index 9deee8ec..88949ad0 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -132,6 +132,8 @@ typedef struct drm_i915_private { spinlock_t swaps_lock; drm_i915_vbl_swap_t vbl_swaps; unsigned int swaps_pending; + unsigned long vblank_offset[2]; + unsigned long vblank_premodeset[2]; } drm_i915_private_t; enum intel_chip_family { @@ -161,8 +163,6 @@ extern int i915_driver_firstopen(struct drm_device *dev); extern int i915_irq_emit(DRM_IOCTL_ARGS); extern int i915_irq_wait(DRM_IOCTL_ARGS); -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); extern void i915_driver_irq_preinstall(drm_device_t * dev); extern void i915_driver_irq_postinstall(drm_device_t * dev); @@ -173,6 +173,11 @@ extern int i915_emit_irq(drm_device_t * dev); extern void i915_user_irq_on(drm_i915_private_t *dev_priv); extern void i915_user_irq_off(drm_i915_private_t *dev_priv); extern int i915_vblank_swap(DRM_IOCTL_ARGS); +extern void i915_enable_vblank(drm_device_t *dev, int crtc); +extern void i915_disable_vblank(drm_device_t *dev, int crtc); +extern u32 i915_get_vblank_counter(drm_device_t *dev, int crtc); +extern void i915_premodeset(drm_device_t *dev, int crtc); +extern void i915_postmodeset(drm_device_t *dev, int crtc); /* i915_mem.c */ extern int i915_mem_alloc(DRM_IOCTL_ARGS); @@ -271,6 +276,36 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller); #define I915REG_PIPEASTAT 0x70024 #define I915REG_PIPEBSTAT 0x71024 +/* + * The two pipe frame counter registers are not synchronized, so + * reading a stable value is somewhat tricky. The following code + * should work: + * + * do { + * high1 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> + * PIPE_FRAME_HIGH_SHIFT; + * low1 = ((INREG(PIPEAFRAMEPIXEL) & PIPE_FRAME_LOW_MASK) >> + * PIPE_FRAME_LOW_SHIFT); + * high2 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> + * PIPE_FRAME_HIGH_SHIFT); + * } while (high1 != high2); + * frame = (high1 << 8) | low1; + */ +#define PIPEAFRAMEHIGH 0x70040 +#define PIPEBFRAMEHIGH 0x71040 +#define PIPE_FRAME_HIGH_MASK 0x0000ffff +#define PIPE_FRAME_HIGH_SHIFT 0 +#define PIPEAFRAMEPIXEL 0x70044 +#define PIPEBFRAMEPIXEL 0x71044 + +#define PIPE_FRAME_LOW_MASK 0xff000000 +#define PIPE_FRAME_LOW_SHIFT 24 +/* + * Pixel within the current frame is counted in the PIPEAFRAMEPIXEL register + * and is 24 bits wide. + */ +#define PIPE_PIXEL_MASK 0x00ffffff +#define PIPE_PIXEL_SHIFT 0 #define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17) #define I915_VBLANK_CLEAR (1UL<<1) diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index dc00f983..1019c942 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -92,8 +92,8 @@ static void i915_vblank_tasklet(drm_device_t *dev) unsigned long irqflags; struct list_head *list, *tmp, hits, *hit; int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages; - unsigned counter[2] = { atomic_read(&dev->vbl_received), - atomic_read(&dev->vbl_received2) }; + unsigned counter[2] = { atomic_read(&dev->vblank_count[0]), + atomic_read(&dev->vblank_count[1]) }; drm_drawable_info_t *drw; drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; u32 cpp = dev_priv->cpp, offsets[3]; @@ -313,14 +313,14 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { if (temp & VSYNC_PIPEA_FLAG) - atomic_inc(&dev->vbl_received); + atomic_inc(&dev->vblank_count[0]); if (temp & VSYNC_PIPEB_FLAG) - atomic_inc(&dev->vbl_received2); + atomic_inc(&dev->vblank_count[1]); } else if (((temp & VSYNC_PIPEA_FLAG) && (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) || ((temp & VSYNC_PIPEB_FLAG) && (vblank_pipe & DRM_I915_VBLANK_PIPE_B))) - atomic_inc(&dev->vbl_received); + atomic_inc(&dev->vblank_count[0]); DRM_WAKEUP(&dev->vbl_queue); drm_vbl_send_signals(dev); @@ -410,37 +410,6 @@ static int i915_wait_irq(drm_device_t * dev, int irq_nr) return ret; } -static int i915_driver_vblank_do_wait(drm_device_t *dev, unsigned int *sequence, - atomic_t *counter) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - unsigned int cur_vblank; - int ret = 0; - - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(counter)) - - *sequence) <= (1<<23))); - - *sequence = cur_vblank; - - return ret; -} - -int i915_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) -{ - return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received); -} - -int i915_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence) -{ - return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2); -} - /* Needs the lock as it touches the ring. */ int i915_irq_emit(DRM_IOCTL_ARGS) @@ -489,15 +458,99 @@ int i915_irq_wait(DRM_IOCTL_ARGS) return i915_wait_irq(dev, irqwait.irq_seq); } -static void i915_enable_interrupt (drm_device_t *dev) +void i915_enable_vblank(drm_device_t *dev, int crtc) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - dev_priv->irq_enable_reg = USER_INT_FLAG; - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A) + switch (crtc) { + case 0: dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG; - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) + break; + case 1: dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG; + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + break; + } + + I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); +} + +void i915_disable_vblank(drm_device_t *dev, int crtc) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + switch (crtc) { + case 0: + dev_priv->irq_enable_reg &= ~VSYNC_PIPEA_FLAG; + break; + case 1: + dev_priv->irq_enable_reg &= ~VSYNC_PIPEB_FLAG; + break; + default: + DRM_ERROR("tried to disable vblank on non-existent crtc %d\n", + crtc); + break; + } + + I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); +} + +static u32 i915_vblank_counter(drm_device_t *dev, int crtc) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + unsigned long high_frame = crtc ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; + unsigned long low_frame = crtc ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; + u32 high1, high2, low, count; + + /* + * High & low register fields aren't synchronized, so make sure + * we get a low value that's stable across two reads of the high + * register. + */ + do { + high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> + PIPE_FRAME_HIGH_SHIFT); + low = ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> + PIPE_FRAME_LOW_SHIFT); + high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> + PIPE_FRAME_HIGH_SHIFT); + } while (high1 != high2); + + count = (high1 << 8) | low; + + return count; +} + +u32 i915_get_vblank_counter(drm_device_t *dev, int crtc) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + return i915_vblank_counter(dev, crtc) + dev_priv->vblank_offset[crtc]; +} + +void i915_premodeset(drm_device_t *dev, int crtc) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + dev_priv->vblank_premodeset[crtc] = i915_vblank_counter(dev, crtc); +} + +void i915_postmodeset(drm_device_t *dev, int crtc) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 new; + + new = i915_vblank_counter(dev, crtc); + dev_priv->vblank_offset[crtc] = dev_priv->vblank_premodeset[crtc] - new; +} + +static void i915_enable_interrupt (drm_device_t *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + dev_priv->irq_enable_reg |= USER_INT_FLAG; I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); dev_priv->irq_enabled = 1; @@ -607,7 +660,7 @@ int i915_vblank_swap(DRM_IOCTL_ARGS) spin_unlock_irqrestore(&dev->drw_lock, irqflags); - curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); + curseq = atomic_read(&dev->vblank_count[pipe]); if (seqtype == _DRM_VBLANK_RELATIVE) swap.sequence += curseq; @@ -714,6 +767,7 @@ void i915_driver_irq_preinstall(drm_device_t * dev) void i915_driver_irq_postinstall(drm_device_t * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int i; spin_lock_init(&dev_priv->swaps_lock); INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); @@ -721,6 +775,34 @@ void i915_driver_irq_postinstall(drm_device_t * dev) dev_priv->user_irq_lock = SPIN_LOCK_UNLOCKED; dev_priv->user_irq_refcount = 0; + dev_priv->irq_enable_reg = 0; + dev->vblank_count = kmalloc(sizeof(atomic_t) * 2, GFP_KERNEL); + if (!dev->vblank_count) { + DRM_ERROR("out of memory\n"); + return; + } + dev->vbl_pending = kmalloc(sizeof(atomic_t) * 2, GFP_KERNEL); + if (!dev->vbl_pending) { + DRM_ERROR("out of memory\n"); + kfree(dev->vblank_count); + return; + } + dev->last_vblank = kmalloc(sizeof(atomic_t) * 2, GFP_KERNEL); + if (!dev->last_vblank) { + DRM_ERROR("out of memory\n"); + kfree(dev->vblank_count); + return; + } + + /* Zero per-crtc vblank stuff */ + for (i = 0; i < 2; i++) { + atomic_set(&dev->vbl_pending[i], 0); + atomic_set(&dev->vblank_count[i], 0); + dev->last_vblank[i] = 0; + dev_priv->vblank_offset[i] = 0; + } + + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ i915_enable_interrupt(dev); DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); diff --git a/shared-core/radeon_drv.h b/shared-core/radeon_drv.h index 92a9b65e..283dee33 100644 --- a/shared-core/radeon_drv.h +++ b/shared-core/radeon_drv.h @@ -299,6 +299,9 @@ typedef struct drm_radeon_private { u32 scratch_ages[5]; + unsigned int crtc_last_cnt; + unsigned int crtc2_last_cnt; + /* starting from here on, data is preserved accross an open */ uint32_t flags; /* see radeon_chip_flags */ @@ -364,9 +367,9 @@ extern int radeon_irq_wait(DRM_IOCTL_ARGS); extern void radeon_do_release(drm_device_t * dev); extern int radeon_driver_vblank_wait(drm_device_t * dev, - unsigned int *sequence); + unsigned int *sequence, int relative); extern int radeon_driver_vblank_wait2(drm_device_t * dev, - unsigned int *sequence); + unsigned int *sequence, int relative); extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS); extern void radeon_driver_irq_preinstall(drm_device_t * dev); extern void radeon_driver_irq_postinstall(drm_device_t * dev); @@ -507,6 +510,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 0x0314 + #define RADEON_GEN_INT_CNTL 0x0040 # define RADEON_CRTC_VBLANK_MASK (1 << 0) # define RADEON_CRTC2_VBLANK_MASK (1 << 9) diff --git a/shared-core/radeon_irq.c b/shared-core/radeon_irq.c index 5151b4d6..2534ff10 100644 --- a/shared-core/radeon_irq.c +++ b/shared-core/radeon_irq.c @@ -35,6 +35,18 @@ #include "radeon_drm.h" #include "radeon_drv.h" +static void radeon_irq_set_state(drm_device_t *dev, u32 mask, int state) +{ + drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; + + if (state) + dev_priv->irq_enable_reg |= mask; + else + dev_priv->irq_enable_reg &= ~mask; + + RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); +} + static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv, u32 mask) { @@ -141,14 +153,17 @@ static int radeon_wait_irq(drm_device_t * dev, int swi_nr) } int radeon_driver_vblank_do_wait(drm_device_t * dev, unsigned int *sequence, - int crtc) + int crtc, int relative) { drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; - unsigned int cur_vblank; + unsigned int cur_vblank, diff, irqflags, current_cnt; int ret = 0; int ack = 0; atomic_t *counter; + unsigned int *last_cnt; + int crtc_cnt_reg; + if (!dev_priv) { DRM_ERROR("%s called with no initialization\n", __FUNCTION__); return DRM_ERR(EINVAL); @@ -156,10 +171,14 @@ int radeon_driver_vblank_do_wait(drm_device_t * dev, unsigned int *sequence, if (crtc == DRM_RADEON_VBLANK_CRTC1) { counter = &dev->vbl_received; - ack |= RADEON_CRTC_VBLANK_STAT; + ack = RADEON_CRTC_VBLANK_STAT; + last_cnt = &dev_priv->crtc_last_cnt; + crtc_cnt_reg = RADEON_CRTC_CRNT_FRAME; } else if (crtc == DRM_RADEON_VBLANK_CRTC2) { counter = &dev->vbl_received2; - ack |= RADEON_CRTC2_VBLANK_STAT; + ack = RADEON_CRTC2_VBLANK_STAT; + last_cnt = &dev_priv->crtc2_last_cnt; + crtc_cnt_reg = RADEON_CRTC2_CRNT_FRAME; } else return DRM_ERR(EINVAL); @@ -167,27 +186,46 @@ int radeon_driver_vblank_do_wait(drm_device_t * dev, unsigned int *sequence, dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; + if (!relative) { + /* + * 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(crtc_cnt_reg); + if (current_cnt < *last_cnt) { + current_cnt += (1 << 21) - *last_cnt; + *last_cnt = 0; + } + diff = current_cnt - *last_cnt; + *last_cnt = RADEON_READ(crtc_cnt_reg); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + atomic_add(diff, counter); + } + /* 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_irq_set_state(dev, ack, 1); DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, (((cur_vblank = atomic_read(counter)) - *sequence) <= (1 << 23))); - + radeon_irq_set_state(dev, ack, 0); *sequence = cur_vblank; return ret; } -int radeon_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence) +int radeon_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence, int relative) { - return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1); + return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1, relative); } -int radeon_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence) +int radeon_driver_vblank_wait2(drm_device_t *dev, unsigned int *sequence, int relative) { - return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2); + return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2, relative); } /* Needs the lock as it touches the ring. @@ -238,20 +276,7 @@ int radeon_irq_wait(DRM_IOCTL_ARGS) return radeon_wait_irq(dev, irqwait.irq_seq); } -static void radeon_enable_interrupt(drm_device_t *dev) -{ - drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; - - dev_priv->irq_enable_reg = RADEON_SW_INT_ENABLE; - if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC1) - dev_priv->irq_enable_reg |= RADEON_CRTC_VBLANK_MASK; - - if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC2) - dev_priv->irq_enable_reg |= RADEON_CRTC2_VBLANK_MASK; - RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); - dev_priv->irq_enabled = 1; -} /* drm_dma.h hooks */ @@ -265,7 +290,8 @@ void radeon_driver_irq_preinstall(drm_device_t * dev) /* Clear bits if they're already high */ radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK | - RADEON_CRTC_VBLANK_STAT)); + RADEON_CRTC_VBLANK_STAT | + RADEON_CRTC2_VBLANK_STAT)); } void radeon_driver_irq_postinstall(drm_device_t * dev) @@ -276,7 +302,7 @@ void radeon_driver_irq_postinstall(drm_device_t * dev) atomic_set(&dev_priv->swi_emitted, 0); DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); - radeon_enable_interrupt(dev); + radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); } void radeon_driver_irq_uninstall(drm_device_t * dev) @@ -318,6 +344,5 @@ int radeon_vblank_crtc_set(drm_device_t *dev, int64_t value) return DRM_ERR(EINVAL); } dev_priv->vblank_crtc = (unsigned int)value; - radeon_enable_interrupt(dev); return 0; } -- cgit v1.2.3