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/i915 | |
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/i915')
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.c | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 395 |
4 files changed, 288 insertions, 143 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 29115450ee46..63c6803d471b 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -673,7 +673,7 @@ static int i915_getparam(struct drm_device *dev, void *data, switch (param->param) { case I915_PARAM_IRQ_ACTIVE: - value = dev->irq_enabled; + value = dev->pdev->irq ? 1 : 0; break; case I915_PARAM_ALLOW_BATCHBUFFER: value = dev_priv->allow_batchbuffer ? 1 : 0; @@ -808,7 +808,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) * and the registers being closely associated. */ if (!IS_I945G(dev) && !IS_I945GM(dev)) - pci_enable_msi(dev->pdev); + if (pci_enable_msi(dev->pdev)) + DRM_ERROR("failed to enable MSI\n"); intel_opregion_init(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index eff66ed3e58e..37af03f4db36 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -85,10 +85,8 @@ static struct drm_driver driver = { /* don't use mtrr's here, the Xserver or user space app should * deal with them for intel hardware. */ - .driver_features = - DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ - DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL | - DRIVER_IRQ_VBL2, + .driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | + DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .load = i915_driver_load, .unload = i915_driver_unload, .lastclose = i915_driver_lastclose, @@ -96,8 +94,9 @@ static struct drm_driver driver = { .suspend = i915_suspend, .resume = i915_resume, .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/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 71326ca9367a..d1a02bead458 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -83,10 +83,15 @@ struct mem_block { typedef struct _drm_i915_vbl_swap { struct list_head head; drm_drawable_t drw_id; - unsigned int pipe; + unsigned int plane; unsigned int sequence; } drm_i915_vbl_swap_t; +struct opregion_header; +struct opregion_acpi; +struct opregion_swsci; +struct opregion_asle; + struct intel_opregion { struct opregion_header *header; struct opregion_acpi *acpi; @@ -105,7 +110,7 @@ typedef struct drm_i915_private { drm_dma_handle_t *status_page_dmah; void *hw_status_page; dma_addr_t dma_status_page; - unsigned long counter; + uint32_t counter; unsigned int status_gfx_addr; drm_local_map_t hws_map; @@ -247,16 +252,17 @@ extern int i915_irq_emit(struct drm_device *dev, void *data, extern int i915_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence); -extern int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence); extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); extern void i915_driver_irq_preinstall(struct drm_device * dev); -extern void i915_driver_irq_postinstall(struct drm_device * dev); +extern int i915_driver_irq_postinstall(struct drm_device *dev); extern void i915_driver_irq_uninstall(struct drm_device * dev); extern int i915_vblank_pipe_set(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int i915_vblank_pipe_get(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int i915_enable_vblank(struct drm_device *dev, int crtc); +extern void i915_disable_vblank(struct drm_device *dev, int crtc); +extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc); extern int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv); extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask); @@ -278,6 +284,10 @@ extern void i915_mem_release(struct drm_device * dev, extern int i915_save_state(struct drm_device *dev); extern int i915_restore_state(struct drm_device *dev); +/* i915_suspend.c */ +extern int i915_save_state(struct drm_device *dev); +extern int i915_restore_state(struct drm_device *dev); + /* i915_opregion.c */ extern int intel_opregion_init(struct drm_device *dev); extern void intel_opregion_free(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ae7d3a82a6d1..f8759597233b 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -35,9 +35,8 @@ /** These are the interrupts used by the driver */ #define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \ - I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \ - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT | \ I915_ASLE_INTERRUPT | \ + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) void @@ -61,6 +60,64 @@ i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask) } /** + * i915_get_pipe - return the the pipe associated with a given plane + * @dev: DRM device + * @plane: plane to look for + * + * The Intel Mesa & 2D drivers call the vblank routines with a plane number + * rather than a pipe number, since they may not always be equal. This routine + * maps the given @plane back to a pipe number. + */ +static int +i915_get_pipe(struct drm_device *dev, int plane) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 dspcntr; + + dspcntr = plane ? I915_READ(DSPBCNTR) : I915_READ(DSPACNTR); + + return dspcntr & DISPPLANE_SEL_PIPE_MASK ? 1 : 0; +} + +/** + * i915_get_plane - return the the plane associated with a given pipe + * @dev: DRM device + * @pipe: pipe to look for + * + * The Intel Mesa & 2D drivers call the vblank routines with a plane number + * rather than a plane number, since they may not always be equal. This routine + * maps the given @pipe back to a plane number. + */ +static int +i915_get_plane(struct drm_device *dev, int pipe) +{ + if (i915_get_pipe(dev, 0) == pipe) + return 0; + return 1; +} + +/** + * i915_pipe_enabled - check if a pipe is enabled + * @dev: DRM device + * @pipe: pipe to check + * + * Reading certain registers when the pipe is disabled can hang the chip. + * Use this routine to make sure the PLL is running and the pipe is active + * before reading such registers if unsure. + */ +static int +i915_pipe_enabled(struct drm_device *dev, int pipe) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + unsigned long pipeconf = pipe ? PIPEBCONF : PIPEACONF; + + if (I915_READ(pipeconf) & PIPEACONF_ENABLE) + return 1; + + return 0; +} + +/** * Emit blits for scheduled buffer swaps. * * This function will be called with the HW lock held. @@ -71,8 +128,7 @@ static void i915_vblank_tasklet(struct drm_device *dev) unsigned long irqflags; struct list_head *list, *tmp, hits, *hit; int nhits, nrects, slice[2], upper[2], lower[2], i; - unsigned counter[2] = { atomic_read(&dev->vbl_received), - atomic_read(&dev->vbl_received2) }; + unsigned counter[2]; struct drm_drawable_info *drw; drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; u32 cpp = dev_priv->cpp; @@ -94,6 +150,9 @@ static void i915_vblank_tasklet(struct drm_device *dev) src_pitch >>= 2; } + counter[0] = drm_vblank_count(dev, 0); + counter[1] = drm_vblank_count(dev, 1); + DRM_DEBUG("\n"); INIT_LIST_HEAD(&hits); @@ -106,12 +165,14 @@ static void i915_vblank_tasklet(struct drm_device *dev) list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { drm_i915_vbl_swap_t *vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); + int pipe = i915_get_pipe(dev, vbl_swap->plane); - if ((counter[vbl_swap->pipe] - vbl_swap->sequence) > (1<<23)) + if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) continue; list_del(list); dev_priv->swaps_pending--; + drm_vblank_put(dev, pipe); spin_unlock(&dev_priv->swaps_lock); spin_lock(&dev->drw_lock); @@ -204,7 +265,7 @@ static void i915_vblank_tasklet(struct drm_device *dev) drm_i915_vbl_swap_t *swap_hit = list_entry(hit, drm_i915_vbl_swap_t, head); struct drm_clip_rect *rect; - int num_rects, pipe; + int num_rects, plane; unsigned short top, bottom; drw = drm_get_drawable_info(dev, swap_hit->drw_id); @@ -213,9 +274,9 @@ static void i915_vblank_tasklet(struct drm_device *dev) continue; rect = drw->rects; - pipe = swap_hit->pipe; - top = upper[pipe]; - bottom = lower[pipe]; + plane = swap_hit->plane; + top = upper[plane]; + bottom = lower[plane]; for (num_rects = drw->num_rects; num_rects--; rect++) { int y1 = max(rect->y1, top); @@ -252,22 +313,54 @@ static void i915_vblank_tasklet(struct drm_device *dev) } } +u32 i915_get_vblank_counter(struct drm_device *dev, int plane) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + unsigned long high_frame; + unsigned long low_frame; + u32 high1, high2, low, count; + int pipe; + + pipe = i915_get_pipe(dev, plane); + high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; + low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; + + if (!i915_pipe_enabled(dev, pipe)) { + DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); + return 0; + } + + /* + * 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; +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 pipea_stats, pipeb_stats; u32 iir; - - pipea_stats = I915_READ(PIPEASTAT); - pipeb_stats = I915_READ(PIPEBSTAT); + u32 pipea_stats, pipeb_stats; + int vblank = 0; if (dev->pdev->msi_enabled) I915_WRITE(IMR, ~0); iir = I915_READ(IIR); - DRM_DEBUG("iir=%08x\n", iir); - if (iir == 0) { if (dev->pdev->msi_enabled) { I915_WRITE(IMR, dev_priv->irq_mask_reg); @@ -276,48 +369,56 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) return IRQ_NONE; } - I915_WRITE(PIPEASTAT, pipea_stats); - I915_WRITE(PIPEBSTAT, pipeb_stats); - - I915_WRITE(IIR, iir); - if (dev->pdev->msi_enabled) - I915_WRITE(IMR, dev_priv->irq_mask_reg); - (void) I915_READ(IIR); /* Flush posted writes */ - - dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - - if (iir & I915_USER_INTERRUPT) - DRM_WAKEUP(&dev_priv->irq_queue); - - if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | - I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) { - int vblank_pipe = dev_priv->vblank_pipe; - - if ((vblank_pipe & - (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) - == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { - if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) - atomic_inc(&dev->vbl_received); - if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) - atomic_inc(&dev->vbl_received2); - } else if (((iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) && - (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) || - ((iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) && - (vblank_pipe & DRM_I915_VBLANK_PIPE_B))) - atomic_inc(&dev->vbl_received); + /* + * Clear the PIPE(A|B)STAT regs before the IIR otherwise + * we may get extra interrupts. + */ + if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { + pipea_stats = I915_READ(PIPEASTAT); + if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)) + pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | + PIPE_VBLANK_INTERRUPT_ENABLE); + else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| + PIPE_VBLANK_INTERRUPT_STATUS)) { + vblank++; + drm_handle_vblank(dev, i915_get_plane(dev, 0)); + } - DRM_WAKEUP(&dev->vbl_queue); - drm_vbl_send_signals(dev); + I915_WRITE(PIPEASTAT, pipea_stats); + } + if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { + pipeb_stats = I915_READ(PIPEBSTAT); + /* Ack the event */ + I915_WRITE(PIPEBSTAT, pipeb_stats); + + /* The vblank interrupt gets enabled even if we didn't ask for + it, so make sure it's shut down again */ + if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) + pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | + PIPE_VBLANK_INTERRUPT_ENABLE); + else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| + PIPE_VBLANK_INTERRUPT_STATUS)) { + vblank++; + drm_handle_vblank(dev, i915_get_plane(dev, 1)); + } - if (dev_priv->swaps_pending > 0) - drm_locked_tasklet(dev, i915_vblank_tasklet); + if (pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) + opregion_asle_intr(dev); + I915_WRITE(PIPEBSTAT, pipeb_stats); } if (iir & I915_ASLE_INTERRUPT) opregion_asle_intr(dev); - if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) - opregion_asle_intr(dev); + dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); + + if (dev->pdev->msi_enabled) + I915_WRITE(IMR, dev_priv->irq_mask_reg); + I915_WRITE(IIR, iir); + (void) I915_READ(IIR); + + if (vblank && dev_priv->swaps_pending > 0) + drm_locked_tasklet(dev, i915_vblank_tasklet); return IRQ_HANDLED; } @@ -358,7 +459,7 @@ static void i915_user_irq_get(struct drm_device *dev) spin_unlock(&dev_priv->user_irq_lock); } -static void i915_user_irq_put(struct drm_device *dev) +void i915_user_irq_put(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -395,41 +496,10 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr) } dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - return ret; -} - -static int i915_driver_vblank_do_wait(struct drm_device *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("called with no initialization\n"); - return -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(struct drm_device *dev, unsigned int *sequence) -{ - return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received); -} - -int i915_driver_vblank_wait2(struct drm_device *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(struct drm_device *dev, void *data, @@ -472,40 +542,88 @@ int i915_irq_wait(struct drm_device *dev, void *data, return i915_wait_irq(dev, irqwait->irq_seq); } +int i915_enable_vblank(struct drm_device *dev, int plane) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe = i915_get_pipe(dev, plane); + u32 pipestat_reg = 0; + u32 pipestat; + + switch (pipe) { + case 0: + pipestat_reg = PIPEASTAT; + i915_enable_irq(dev_priv, I915_DISPLAY_PIPE_A_EVENT_INTERRUPT); + break; + case 1: + pipestat_reg = PIPEBSTAT; + i915_enable_irq(dev_priv, I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); + break; + default: + DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", + pipe); + break; + } + + if (pipestat_reg) { + pipestat = I915_READ(pipestat_reg); + if (IS_I965G(dev)) + pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE; + else + pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE; + /* Clear any stale interrupt status */ + pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | + PIPE_VBLANK_INTERRUPT_STATUS); + I915_WRITE(pipestat_reg, pipestat); + } + + return 0; +} + +void i915_disable_vblank(struct drm_device *dev, int plane) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe = i915_get_pipe(dev, plane); + u32 pipestat_reg = 0; + u32 pipestat; + + switch (pipe) { + case 0: + pipestat_reg = PIPEASTAT; + i915_disable_irq(dev_priv, I915_DISPLAY_PIPE_A_EVENT_INTERRUPT); + break; + case 1: + pipestat_reg = PIPEBSTAT; + i915_disable_irq(dev_priv, I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); + break; + default: + DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", + pipe); + break; + } + + if (pipestat_reg) { + pipestat = I915_READ(pipestat_reg); + pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | + PIPE_VBLANK_INTERRUPT_ENABLE); + /* Clear any stale interrupt status */ + pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | + PIPE_VBLANK_INTERRUPT_STATUS); + I915_WRITE(pipestat_reg, pipestat); + } +} + /* Set the vblank monitor pipe */ int i915_vblank_pipe_set(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_vblank_pipe_t *pipe = data; - u32 enable_mask = 0, disable_mask = 0; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } - if (pipe->pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) { - DRM_ERROR("called with invalid pipe 0x%x\n", pipe->pipe); - return -EINVAL; - } - - if (pipe->pipe & DRM_I915_VBLANK_PIPE_A) - enable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; - else - disable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; - - if (pipe->pipe & DRM_I915_VBLANK_PIPE_B) - enable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - else - disable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; - - i915_enable_irq(dev_priv, enable_mask); - i915_disable_irq(dev_priv, disable_mask); - - dev_priv->vblank_pipe = pipe->pipe; - return 0; } @@ -514,19 +632,13 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, { drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_vblank_pipe_t *pipe = data; - u16 flag; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } - flag = I915_READ(IMR); - pipe->pipe = 0; - if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) - pipe->pipe |= DRM_I915_VBLANK_PIPE_A; - if (flag & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) - pipe->pipe |= DRM_I915_VBLANK_PIPE_B; + pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; return 0; } @@ -540,9 +652,10 @@ int i915_vblank_swap(struct drm_device *dev, void *data, drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_vblank_swap_t *swap = data; drm_i915_vbl_swap_t *vbl_swap; - unsigned int pipe, seqtype, curseq; + unsigned int pipe, seqtype, curseq, plane; unsigned long irqflags; struct list_head *list; + int ret; if (!dev_priv) { DRM_ERROR("%s called with no initialization\n", __func__); @@ -560,7 +673,8 @@ int i915_vblank_swap(struct drm_device *dev, void *data, return -EINVAL; } - pipe = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; + plane = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; + pipe = i915_get_pipe(dev, plane); seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); @@ -579,7 +693,14 @@ int i915_vblank_swap(struct drm_device *dev, void *data, spin_unlock_irqrestore(&dev->drw_lock, irqflags); - curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); + /* + * We take the ref here and put it when the swap actually completes + * in the tasklet. + */ + ret = drm_vblank_get(dev, pipe); + if (ret) + return ret; + curseq = drm_vblank_count(dev, pipe); if (seqtype == _DRM_VBLANK_RELATIVE) swap->sequence += curseq; @@ -589,6 +710,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data, swap->sequence = curseq + 1; } else { DRM_DEBUG("Missed target sequence\n"); + drm_vblank_put(dev, pipe); return -EINVAL; } } @@ -599,7 +721,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data, vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); if (vbl_swap->drw_id == swap->drawable && - vbl_swap->pipe == pipe && + vbl_swap->plane == plane && vbl_swap->sequence == swap->sequence) { spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); DRM_DEBUG("Already scheduled\n"); @@ -611,6 +733,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data, if (dev_priv->swaps_pending >= 100) { DRM_DEBUG("Too many swaps queued\n"); + drm_vblank_put(dev, pipe); return -EBUSY; } @@ -618,13 +741,14 @@ int i915_vblank_swap(struct drm_device *dev, void *data, if (!vbl_swap) { DRM_ERROR("Failed to allocate memory to queue swap\n"); + drm_vblank_put(dev, pipe); return -ENOMEM; } DRM_DEBUG("\n"); vbl_swap->drw_id = swap->drawable; - vbl_swap->pipe = pipe; + vbl_swap->plane = plane; vbl_swap->sequence = swap->sequence; spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); @@ -643,28 +767,32 @@ void i915_driver_irq_preinstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - I915_WRITE(HWSTAM, 0xfffe); - I915_WRITE(IMR, 0x0); + I915_WRITE(HWSTAM, 0xeffe); + I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); } -void i915_driver_irq_postinstall(struct drm_device * dev) +int i915_driver_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int ret, num_pipes = 2; spin_lock_init(&dev_priv->swaps_lock); INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); dev_priv->swaps_pending = 0; - if (!dev_priv->vblank_pipe) - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A; - /* Set initial unmasked IRQs to just the selected vblank pipes. */ dev_priv->irq_mask_reg = ~0; - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A) - dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; - if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) - dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; + + ret = drm_vblank_init(dev, num_pipes); + if (ret) + return ret; + + dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; + dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; + + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK; @@ -673,22 +801,29 @@ void i915_driver_irq_postinstall(struct drm_device * dev) (void) I915_READ(IER); opregion_enable_asle(dev); - DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); + + return 0; } void i915_driver_irq_uninstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u16 temp; + u32 temp; if (!dev_priv) return; - I915_WRITE(HWSTAM, 0xffff); - I915_WRITE(IMR, 0xffff); + dev_priv->vblank_pipe = 0; + + I915_WRITE(HWSTAM, 0xffffffff); + I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); + temp = I915_READ(PIPEASTAT); + I915_WRITE(PIPEASTAT, temp); + temp = I915_READ(PIPEBSTAT); + I915_WRITE(PIPEBSTAT, temp); temp = I915_READ(IIR); I915_WRITE(IIR, temp); } |