diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-09-30 23:14:26 +0400 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2008-10-18 01:10:11 +0400 |
commit | 0a3e67a4caac273a3bfc4ced3da364830b1ab241 (patch) | |
tree | 02a2e5e76d9dffcb556d09b0eee4d34ebe5d81cb /drivers/gpu/drm/radeon/radeon_irq.c | |
parent | 2df68b439fcb97a4c55f81516206ef4ee325e28d (diff) | |
download | linux-0a3e67a4caac273a3bfc4ced3da364830b1ab241.tar.xz |
drm: Rework vblank-wait handling to allow interrupt reduction.
Previously, drivers supporting vblank interrupt waits would run the interrupt
all the time, or all the time that any 3d client was running, preventing the
CPU from sleeping for long when the system was otherwise idle. Now, interrupts
are disabled any time that no client is waiting on a vblank event. The new
method uses vblank counters on the chipsets when the interrupts are turned
off, rather than counting interrupts, so that we can continue to present
accurate vblank numbers.
Co-author: Michel Dänzer <michel@tungstengraphics.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_irq.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_irq.c | 268 |
1 files changed, 168 insertions, 100 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c index ee40d197deb7..5079f7054a2f 100644 --- a/drivers/gpu/drm/radeon/radeon_irq.c +++ b/drivers/gpu/drm/radeon/radeon_irq.c @@ -27,7 +27,7 @@ * * Authors: * Keith Whitwell <keith@tungstengraphics.com> - * Michel Dänzer <michel@daenzer.net> + * Michel D�zer <michel@daenzer.net> */ #include "drmP.h" @@ -35,12 +35,128 @@ #include "radeon_drm.h" #include "radeon_drv.h" -static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv, - u32 mask) +void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state) { - u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS) & mask; + drm_radeon_private_t *dev_priv = 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 void r500_vbl_irq_set_state(struct drm_device *dev, u32 mask, int state) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if (state) + dev_priv->r500_disp_irq_reg |= mask; + else + dev_priv->r500_disp_irq_reg &= ~mask; + + RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg); +} + +int radeon_enable_vblank(struct drm_device *dev, int crtc) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) { + switch (crtc) { + case 0: + r500_vbl_irq_set_state(dev, R500_D1MODE_INT_MASK, 1); + break; + case 1: + r500_vbl_irq_set_state(dev, R500_D2MODE_INT_MASK, 1); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + return EINVAL; + } + } else { + switch (crtc) { + case 0: + radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 1); + break; + case 1: + radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 1); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + return EINVAL; + } + } + + return 0; +} + +void radeon_disable_vblank(struct drm_device *dev, int crtc) +{ + drm_radeon_private_t *dev_priv = dev->dev_private; + + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) { + switch (crtc) { + case 0: + r500_vbl_irq_set_state(dev, R500_D1MODE_INT_MASK, 0); + break; + case 1: + r500_vbl_irq_set_state(dev, R500_D2MODE_INT_MASK, 0); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + break; + } + } else { + switch (crtc) { + case 0: + radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 0); + break; + case 1: + radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 0); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", + crtc); + break; + } + } +} + +static inline u32 radeon_acknowledge_irqs(drm_radeon_private_t *dev_priv, u32 *r500_disp_int) +{ + u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS); + u32 irq_mask = RADEON_SW_INT_TEST; + + *r500_disp_int = 0; + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) { + /* vbl interrupts in a different place */ + + if (irqs & R500_DISPLAY_INT_STATUS) { + /* if a display interrupt */ + u32 disp_irq; + + disp_irq = RADEON_READ(R500_DISP_INTERRUPT_STATUS); + + *r500_disp_int = disp_irq; + if (disp_irq & R500_D1_VBLANK_INTERRUPT) + RADEON_WRITE(R500_D1MODE_VBLANK_STATUS, R500_VBLANK_ACK); + if (disp_irq & R500_D2_VBLANK_INTERRUPT) + RADEON_WRITE(R500_D2MODE_VBLANK_STATUS, R500_VBLANK_ACK); + } + irq_mask |= R500_DISPLAY_INT_STATUS; + } else + irq_mask |= RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT; + + irqs &= irq_mask; + if (irqs) RADEON_WRITE(RADEON_GEN_INT_STATUS, irqs); + return irqs; } @@ -68,44 +184,33 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS) drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; u32 stat; + u32 r500_disp_int; /* Only consider the bits we're interested in - others could be used * outside the DRM */ - stat = radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK | - RADEON_CRTC_VBLANK_STAT | - RADEON_CRTC2_VBLANK_STAT)); + stat = radeon_acknowledge_irqs(dev_priv, &r500_disp_int); if (!stat) return IRQ_NONE; stat &= dev_priv->irq_enable_reg; /* SW interrupt */ - if (stat & RADEON_SW_INT_TEST) { + if (stat & RADEON_SW_INT_TEST) DRM_WAKEUP(&dev_priv->swi_queue); - } /* VBLANK interrupt */ - if (stat & (RADEON_CRTC_VBLANK_STAT|RADEON_CRTC2_VBLANK_STAT)) { - int vblank_crtc = dev_priv->vblank_crtc; - - if ((vblank_crtc & - (DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) == - (DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) { - if (stat & RADEON_CRTC_VBLANK_STAT) - atomic_inc(&dev->vbl_received); - if (stat & RADEON_CRTC2_VBLANK_STAT) - atomic_inc(&dev->vbl_received2); - } else if (((stat & RADEON_CRTC_VBLANK_STAT) && - (vblank_crtc & DRM_RADEON_VBLANK_CRTC1)) || - ((stat & RADEON_CRTC2_VBLANK_STAT) && - (vblank_crtc & DRM_RADEON_VBLANK_CRTC2))) - atomic_inc(&dev->vbl_received); - - DRM_WAKEUP(&dev->vbl_queue); - drm_vbl_send_signals(dev); + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) { + if (r500_disp_int & R500_D1_VBLANK_INTERRUPT) + drm_handle_vblank(dev, 0); + if (r500_disp_int & R500_D2_VBLANK_INTERRUPT) + drm_handle_vblank(dev, 1); + } else { + if (stat & RADEON_CRTC_VBLANK_STAT) + drm_handle_vblank(dev, 0); + if (stat & RADEON_CRTC2_VBLANK_STAT) + drm_handle_vblank(dev, 1); } - return IRQ_HANDLED; } @@ -144,54 +249,31 @@ static int radeon_wait_irq(struct drm_device * dev, int swi_nr) return ret; } -static int radeon_driver_vblank_do_wait(struct drm_device * dev, - unsigned int *sequence, int crtc) +u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc) { - drm_radeon_private_t *dev_priv = - (drm_radeon_private_t *) dev->dev_private; - unsigned int cur_vblank; - int ret = 0; - int ack = 0; - atomic_t *counter; + drm_radeon_private_t *dev_priv = dev->dev_private; + if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } - if (crtc == DRM_RADEON_VBLANK_CRTC1) { - counter = &dev->vbl_received; - ack |= RADEON_CRTC_VBLANK_STAT; - } else if (crtc == DRM_RADEON_VBLANK_CRTC2) { - counter = &dev->vbl_received2; - ack |= RADEON_CRTC2_VBLANK_STAT; - } else + if (crtc < 0 || crtc > 1) { + DRM_ERROR("Invalid crtc %d\n", crtc); return -EINVAL; + } - radeon_acknowledge_irqs(dev_priv, ack); - - dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; - - /* 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... - */ - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(counter)) - - *sequence) <= (1 << 23))); - - *sequence = cur_vblank; - - return ret; -} - -int radeon_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence) -{ - return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1); -} - -int radeon_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence) -{ - return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2); + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) { + if (crtc == 0) + return RADEON_READ(R500_D1CRTC_FRAME_COUNT); + else + return RADEON_READ(R500_D2CRTC_FRAME_COUNT); + } else { + if (crtc == 0) + return RADEON_READ(RADEON_CRTC_CRNT_FRAME); + else + return RADEON_READ(RADEON_CRTC2_CRNT_FRAME); + } } /* Needs the lock as it touches the ring. @@ -234,46 +316,41 @@ int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_pr return radeon_wait_irq(dev, irqwait->irq_seq); } -void radeon_enable_interrupt(struct drm_device *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 */ void radeon_driver_irq_preinstall(struct drm_device * dev) { drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; + u32 dummy; /* Disable *all* interrupts */ + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) + RADEON_WRITE(R500_DxMODE_INT_MASK, 0); RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); /* Clear bits if they're already high */ - radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK | - RADEON_CRTC_VBLANK_STAT | - RADEON_CRTC2_VBLANK_STAT)); + radeon_acknowledge_irqs(dev_priv, &dummy); } -void radeon_driver_irq_postinstall(struct drm_device * dev) +int radeon_driver_irq_postinstall(struct drm_device *dev) { drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; + int ret; atomic_set(&dev_priv->swi_emitted, 0); DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); - radeon_enable_interrupt(dev); + ret = drm_vblank_init(dev, 2); + if (ret) + return ret; + + dev->max_vblank_count = 0x001fffff; + + radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); + + return 0; } void radeon_driver_irq_uninstall(struct drm_device * dev) @@ -285,6 +362,8 @@ void radeon_driver_irq_uninstall(struct drm_device * dev) dev_priv->irq_enabled = 0; + if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) + RADEON_WRITE(R500_DxMODE_INT_MASK, 0); /* Disable *all* interrupts */ RADEON_WRITE(RADEON_GEN_INT_CNTL, 0); } @@ -293,18 +372,8 @@ void radeon_driver_irq_uninstall(struct drm_device * dev) int radeon_vblank_crtc_get(struct drm_device *dev) { drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; - u32 flag; - u32 value; - - flag = RADEON_READ(RADEON_GEN_INT_CNTL); - value = 0; - - if (flag & RADEON_CRTC_VBLANK_MASK) - value |= DRM_RADEON_VBLANK_CRTC1; - if (flag & RADEON_CRTC2_VBLANK_MASK) - value |= DRM_RADEON_VBLANK_CRTC2; - return value; + return dev_priv->vblank_crtc; } int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value) @@ -315,6 +384,5 @@ int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value) return -EINVAL; } dev_priv->vblank_crtc = (unsigned int)value; - radeon_enable_interrupt(dev); return 0; } |