From 7c463586427bbbad726ba561bae4ba5acada2481 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 4 Nov 2008 02:03:27 -0800 Subject: drm/i915: Manage PIPESTAT to control vblank interrupts instead of IMR. The pipestat fields affect reporting of all vblank-related interrupts, so we have to reset them during the irq_handler, and while enabling vblank interrupts. Otherwise, if a pipe status field had been set to non-zero before enabling reporting, we would never see an interrupt again. This patch adds i915_enable_pipestat and i915_disable_pipestat to abstract out the steps needed to change the reported interrupts. Signed-off-by: Keith Packard Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_drv.h | 8 ++ drivers/gpu/drm/i915/i915_irq.c | 211 ++++++++++++++++------------------- drivers/gpu/drm/i915/i915_opregion.c | 18 ++- 3 files changed, 115 insertions(+), 122 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ef1c0b8f8d07..ed1edcf8704f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -132,6 +132,7 @@ typedef struct drm_i915_private { int user_irq_refcount; /** Cached value of IMR to avoid reads in updating the bitfield */ u32 irq_mask_reg; + u32 pipestat[2]; int tex_lru_log_granularity; int allow_batchbuffer; @@ -446,6 +447,13 @@ 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); +void +i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); + +void +i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); + + /* i915_mem.c */ extern int i915_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 82752d6177a4..ca3ed1833908 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -33,11 +33,23 @@ #define MAX_NOPID ((u32)~0) -/** These are the interrupts used by the driver */ -#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \ - I915_ASLE_INTERRUPT | \ - I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) +/** + * Interrupts that are always left unmasked. + * + * Since pipe events are edge-triggered from the PIPESTAT register to IIR, + * we leave them always unmasked in IMR and then control enabling them through + * PIPESTAT alone. + */ +#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) + +/** Interrupts that we mask and unmask at runtime. */ +#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) + +/** These are all of the interrupts used by the driver */ +#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \ + I915_INTERRUPT_ENABLE_VAR) void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) @@ -59,6 +71,41 @@ i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask) } } +static inline u32 +i915_pipestat(int pipe) +{ + if (pipe == 0) + return PIPEASTAT; + if (pipe == 1) + return PIPEBSTAT; + BUG_ON(1); +} + +void +i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != mask) { + u32 reg = i915_pipestat(pipe); + + dev_priv->pipestat[pipe] |= mask; + /* Enable the interrupt, clear any pending status */ + I915_WRITE(reg, dev_priv->pipestat[pipe] | (mask >> 16)); + (void) I915_READ(reg); + } +} + +void +i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != 0) { + u32 reg = i915_pipestat(pipe); + + dev_priv->pipestat[pipe] &= ~mask; + I915_WRITE(reg, dev_priv->pipestat[pipe]); + (void) I915_READ(reg); + } +} + /** * i915_pipe_enabled - check if a pipe is enabled * @dev: DRM device @@ -122,9 +169,11 @@ 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 iir; - u32 pipea_stats, pipeb_stats; + u32 pipea_stats = 0, pipeb_stats = 0; int vblank = 0; + unsigned long irqflags; + spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); atomic_inc(&dev_priv->irq_received); if (dev->pdev->msi_enabled) @@ -136,44 +185,20 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) I915_WRITE(IMR, dev_priv->irq_mask_reg); (void) I915_READ(IMR); } + spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); return IRQ_NONE; } /* - * Clear the PIPE(A|B)STAT regs before the IIR otherwise - * we may get extra interrupts. + * Clear the PIPE(A|B)STAT regs before the IIR */ 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, 0); - } - 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, 1); - } - - if (pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) - opregion_asle_intr(dev); I915_WRITE(PIPEBSTAT, pipeb_stats); } @@ -182,6 +207,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) I915_WRITE(IMR, dev_priv->irq_mask_reg); (void) I915_READ(IIR); /* Flush posted writes */ + spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); + if (dev_priv->sarea_priv) dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); @@ -191,7 +218,18 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) DRM_WAKEUP(&dev_priv->irq_queue); } - if (iir & I915_ASLE_INTERRUPT) + if (pipea_stats & I915_VBLANK_INTERRUPT_STATUS) { + vblank++; + drm_handle_vblank(dev, 0); + } + + if (pipeb_stats & I915_VBLANK_INTERRUPT_STATUS) { + vblank++; + drm_handle_vblank(dev, 1); + } + + if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) || + (iir & I915_ASLE_INTERRUPT)) opregion_asle_intr(dev); return IRQ_HANDLED; @@ -330,48 +368,16 @@ int i915_irq_wait(struct drm_device *dev, void *data, int i915_enable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 pipestat_reg = 0; - u32 pipestat; - u32 interrupt = 0; unsigned long irqflags; - switch (pipe) { - case 0: - pipestat_reg = PIPEASTAT; - interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; - break; - case 1: - pipestat_reg = PIPEBSTAT; - interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; - break; - default: - DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", - pipe); - return 0; - } - spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); - /* Enabling vblank events in IMR comes before PIPESTAT write, or - * there's a race where the PIPESTAT vblank bit gets set to 1, so - * the OR of enabled PIPESTAT bits goes to 1, so the PIPExEVENT in - * ISR flashes to 1, but the IIR bit doesn't get set to 1 because - * IMR masks it. It doesn't ever get set after we clear the masking - * in IMR because the ISR bit is edge, not level-triggered, on the - * OR of PIPESTAT bits. - */ - i915_enable_irq(dev_priv, interrupt); - pipestat = I915_READ(pipestat_reg); if (IS_I965G(dev)) - pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE; + i915_enable_pipestat(dev_priv, pipe, + 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); - (void) I915_READ(pipestat_reg); /* Posting read */ + i915_enable_pipestat(dev_priv, pipe, + PIPE_VBLANK_INTERRUPT_ENABLE); spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); - return 0; } @@ -381,37 +387,12 @@ int i915_enable_vblank(struct drm_device *dev, int pipe) void i915_disable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 pipestat_reg = 0; - u32 pipestat; - u32 interrupt = 0; unsigned long irqflags; - switch (pipe) { - case 0: - pipestat_reg = PIPEASTAT; - interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; - break; - case 1: - pipestat_reg = PIPEBSTAT; - interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; - break; - default: - DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", - pipe); - return; - break; - } - spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); - i915_disable_irq(dev_priv, interrupt); - 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); - (void) I915_READ(pipestat_reg); /* Posting read */ + i915_disable_pipestat(dev_priv, pipe, + PIPE_VBLANK_INTERRUPT_ENABLE | + PIPE_START_VBLANK_INTERRUPT_ENABLE); spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); } @@ -476,8 +457,11 @@ 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, 0xeffe); + I915_WRITE(PIPEASTAT, 0); + I915_WRITE(PIPEBSTAT, 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); + (void) I915_READ(IER); } int i915_driver_irq_postinstall(struct drm_device *dev) @@ -485,23 +469,28 @@ 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; - /* Set initial unmasked IRQs to just the selected vblank pipes. */ - dev_priv->irq_mask_reg = ~0; - 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; + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX; + + dev_priv->pipestat[0] = 0; + dev_priv->pipestat[1] = 0; + + /* Disable pipe interrupt enables, clear pending pipe status */ + I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); + I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); + /* Clear pending interrupt status */ + I915_WRITE(IIR, I915_READ(IIR)); - I915_WRITE(IMR, dev_priv->irq_mask_reg); I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); + I915_WRITE(IMR, dev_priv->irq_mask_reg); (void) I915_READ(IER); opregion_enable_asle(dev); @@ -513,7 +502,6 @@ int i915_driver_irq_postinstall(struct drm_device *dev) void i915_driver_irq_uninstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 temp; if (!dev_priv) return; @@ -521,13 +509,12 @@ void i915_driver_irq_uninstall(struct drm_device * dev) dev_priv->vblank_pipe = 0; I915_WRITE(HWSTAM, 0xffffffff); + I915_WRITE(PIPEASTAT, 0); + I915_WRITE(PIPEBSTAT, 0); 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); + I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); + I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); + I915_WRITE(IIR, I915_READ(IIR)); } diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c index 1787a0c7e3ab..13ae731a33db 100644 --- a/drivers/gpu/drm/i915/i915_opregion.c +++ b/drivers/gpu/drm/i915/i915_opregion.c @@ -235,17 +235,15 @@ void opregion_enable_asle(struct drm_device *dev) struct opregion_asle *asle = dev_priv->opregion.asle; if (asle) { - u32 pipeb_stats = I915_READ(PIPEBSTAT); if (IS_MOBILE(dev)) { - /* Many devices trigger events with a write to the - legacy backlight controller, so we need to ensure - that it's able to generate interrupts */ - I915_WRITE(PIPEBSTAT, pipeb_stats |= - I915_LEGACY_BLC_EVENT_ENABLE); - i915_enable_irq(dev_priv, I915_ASLE_INTERRUPT | - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); - } else - i915_enable_irq(dev_priv, I915_ASLE_INTERRUPT); + unsigned long irqflags; + + spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); + i915_enable_pipestat(dev_priv, 1, + I915_LEGACY_BLC_EVENT_ENABLE); + spin_unlock_irqrestore(&dev_priv->user_irq_lock, + irqflags); + } asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | ASLE_PFMB_EN; -- cgit v1.2.1 From cdfbc41f6d602fc0105fb2b4e0645cc1aa274c12 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 4 Nov 2008 15:50:30 -0800 Subject: drm/i915: Remove IMR masking during interrupt handler, and restart it if needed. The IMR masking was a technique recommended for avoiding getting stuck with no interrupts generated again in MSI mode. It kept new IIR bits from getting set between the IIR read and the IIR write, which would have otherwise prevented an MSI from ever getting generated again. However, this caused a problem for vblank as the IMR mask would keep the pipe event interrupt from getting reflected in IIR, even after the IMR mask was brought back down. Instead, just check the state of IIR after we ack the interrupts we're going to handle, and restart if we didn't get IIR all the way to zero. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_irq.c | 108 +++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 47 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ca3ed1833908..654d42fabec8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -168,69 +168,83 @@ 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 iir; - u32 pipea_stats = 0, pipeb_stats = 0; + u32 iir, new_iir; + u32 pipea_stats, pipeb_stats; int vblank = 0; unsigned long irqflags; - spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); atomic_inc(&dev_priv->irq_received); - if (dev->pdev->msi_enabled) - I915_WRITE(IMR, ~0); iir = I915_READ(IIR); - if (iir == 0) { - if (dev->pdev->msi_enabled) { - I915_WRITE(IMR, dev_priv->irq_mask_reg); - (void) I915_READ(IMR); - } - spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); + if (iir == 0) return IRQ_NONE; - } - - /* - * Clear the PIPE(A|B)STAT regs before the IIR - */ - if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { - pipea_stats = I915_READ(PIPEASTAT); - I915_WRITE(PIPEASTAT, pipea_stats); - } - if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { - pipeb_stats = I915_READ(PIPEBSTAT); - I915_WRITE(PIPEBSTAT, pipeb_stats); - } + do { + pipea_stats = 0; + pipeb_stats = 0; + /* + * Clear the PIPE(A|B)STAT regs before the IIR + */ + if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { + spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); + pipea_stats = I915_READ(PIPEASTAT); + I915_WRITE(PIPEASTAT, pipea_stats); + spin_unlock_irqrestore(&dev_priv->user_irq_lock, + irqflags); + } - I915_WRITE(IIR, iir); - if (dev->pdev->msi_enabled) - I915_WRITE(IMR, dev_priv->irq_mask_reg); - (void) I915_READ(IIR); /* Flush posted writes */ + if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { + spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); + pipeb_stats = I915_READ(PIPEBSTAT); + I915_WRITE(PIPEBSTAT, pipeb_stats); + spin_unlock_irqrestore(&dev_priv->user_irq_lock, + irqflags); + } - spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); + I915_WRITE(IIR, iir); + new_iir = I915_READ(IIR); /* Flush posted writes */ - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); + if (dev_priv->sarea_priv) + dev_priv->sarea_priv->last_dispatch = + READ_BREADCRUMB(dev_priv); - if (iir & I915_USER_INTERRUPT) { - dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); - DRM_WAKEUP(&dev_priv->irq_queue); - } + if (iir & I915_USER_INTERRUPT) { + dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); + DRM_WAKEUP(&dev_priv->irq_queue); + } - if (pipea_stats & I915_VBLANK_INTERRUPT_STATUS) { - vblank++; - drm_handle_vblank(dev, 0); - } + if (pipea_stats & I915_VBLANK_INTERRUPT_STATUS) { + vblank++; + drm_handle_vblank(dev, 0); + } - if (pipeb_stats & I915_VBLANK_INTERRUPT_STATUS) { - vblank++; - drm_handle_vblank(dev, 1); - } + if (pipeb_stats & I915_VBLANK_INTERRUPT_STATUS) { + vblank++; + drm_handle_vblank(dev, 1); + } - if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) || - (iir & I915_ASLE_INTERRUPT)) - opregion_asle_intr(dev); + if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) || + (iir & I915_ASLE_INTERRUPT)) + opregion_asle_intr(dev); + + /* With MSI, interrupts are only generated when iir + * transitions from zero to nonzero. If another bit got + * set while we were handling the existing iir bits, then + * we would never get another interrupt. + * + * This is fine on non-MSI as well, as if we hit this path + * we avoid exiting the interrupt handler only to generate + * another one. + * + * Note that for MSI this could cause a stray interrupt report + * if an interrupt landed in the time between writing IIR and + * the posting read. This should be rare enough to never + * trigger the 99% of 100,000 interrupts test for disabling + * stray interrupts. + */ + iir = new_iir; + } while (iir != 0); return IRQ_HANDLED; } -- cgit v1.2.1 From 28dfe52a6e8a1495067c4331358700a170d0ee86 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 13 Nov 2008 15:00:55 -0800 Subject: drm/i915: Avoid BUG_ONs on VT switch with a wedged chipset. Instead, just warn that bad things are happening and do our best to clean up the mess without the GPU's help. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 53 ++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6b4a2bd20640..edc805afde05 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -31,6 +31,8 @@ #include "i915_drv.h" #include +#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) + static int i915_gem_object_set_domain(struct drm_gem_object *obj, uint32_t read_domains, @@ -2299,29 +2301,52 @@ i915_gem_idle(struct drm_device *dev) i915_gem_retire_requests(dev); - /* Active and flushing should now be empty as we've - * waited for a sequence higher than any pending execbuffer - */ - BUG_ON(!list_empty(&dev_priv->mm.active_list)); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); + if (!dev_priv->mm.wedged) { + /* Active and flushing should now be empty as we've + * waited for a sequence higher than any pending execbuffer + */ + WARN_ON(!list_empty(&dev_priv->mm.active_list)); + WARN_ON(!list_empty(&dev_priv->mm.flushing_list)); + /* Request should now be empty as we've also waited + * for the last request in the list + */ + WARN_ON(!list_empty(&dev_priv->mm.request_list)); + } - /* Request should now be empty as we've also waited - * for the last request in the list + /* Empty the active and flushing lists to inactive. If there's + * anything left at this point, it means that we're wedged and + * nothing good's going to happen by leaving them there. So strip + * the GPU domains and just stuff them onto inactive. */ - BUG_ON(!list_empty(&dev_priv->mm.request_list)); + while (!list_empty(&dev_priv->mm.active_list)) { + struct drm_i915_gem_object *obj_priv; + + obj_priv = list_first_entry(&dev_priv->mm.active_list, + struct drm_i915_gem_object, + list); + obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; + i915_gem_object_move_to_inactive(obj_priv->obj); + } + + while (!list_empty(&dev_priv->mm.flushing_list)) { + struct drm_i915_gem_object *obj_priv; + + obj_priv = list_first_entry(&dev_priv->mm.active_list, + struct drm_i915_gem_object, + list); + obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; + i915_gem_object_move_to_inactive(obj_priv->obj); + } + - /* Move all buffers out of the GTT. */ + /* Move all inactive buffers out of the GTT. */ ret = i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list); + WARN_ON(!list_empty(&dev_priv->mm.inactive_list)); if (ret) { mutex_unlock(&dev->struct_mutex); return ret; } - BUG_ON(!list_empty(&dev_priv->mm.active_list)); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); - BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); - BUG_ON(!list_empty(&dev_priv->mm.request_list)); - i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); -- cgit v1.2.1 From 2678d9d6964b29ecd1975870c7a850242b29bc5c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 20 Nov 2008 22:54:54 -0800 Subject: drm/i915: Subtract total pinned bytes from available aperture size The old code was wandering through the active list looking for pinned buffers; there may be other pinned buffers around. Fortunately, we keep a count of the total amount of pinned memory and can use that instead. Signed-off-by: Keith Packard Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index edc805afde05..6e4c6dd50659 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -85,20 +85,14 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_get_aperture *args = data; - struct drm_i915_gem_object *obj_priv; if (!(dev->driver->driver_features & DRIVER_GEM)) return -ENODEV; args->aper_size = dev->gtt_total; - args->aper_available_size = args->aper_size; - - list_for_each_entry(obj_priv, &dev_priv->mm.active_list, list) { - if (obj_priv->pin_count > 0) - args->aper_available_size -= obj_priv->obj->size; - } + args->aper_available_size = (args->aper_size - + atomic_read(&dev->pin_memory)); return 0; } -- cgit v1.2.1 From 05eff845a28499762075d3a72e238a31f4d2407c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 19 Nov 2008 14:03:05 -0800 Subject: drm/i915: Always read pipestat in irq_handler Because we write pipestat before iir, it's possible that a pipestat interrupt will occur between the pipestat write and the iir write. This leaves pipestat with an interrupt status not visible in iir. This may cause an interrupt flood as we never clear the pipestat event. Signed-off-by: Keith Packard Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_irq.c | 55 +++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 654d42fabec8..c3673581db58 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -170,37 +170,54 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 iir, new_iir; u32 pipea_stats, pipeb_stats; + u32 vblank_status; + u32 vblank_enable; int vblank = 0; unsigned long irqflags; + int irq_received; + int ret = IRQ_NONE; atomic_inc(&dev_priv->irq_received); iir = I915_READ(IIR); - if (iir == 0) - return IRQ_NONE; + if (IS_I965G(dev)) { + vblank_status = I915_START_VBLANK_INTERRUPT_STATUS; + vblank_enable = PIPE_START_VBLANK_INTERRUPT_ENABLE; + } else { + vblank_status = I915_VBLANK_INTERRUPT_STATUS; + vblank_enable = I915_VBLANK_INTERRUPT_ENABLE; + } - do { - pipea_stats = 0; - pipeb_stats = 0; + for (;;) { + irq_received = iir != 0; + + /* Can't rely on pipestat interrupt bit in iir as it might + * have been cleared after the pipestat interrupt was received. + * It doesn't set the bit in iir again, but it still produces + * interrupts (for non-MSI). + */ + spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); + pipea_stats = I915_READ(PIPEASTAT); + pipeb_stats = I915_READ(PIPEBSTAT); /* * Clear the PIPE(A|B)STAT regs before the IIR */ - if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { - spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); - pipea_stats = I915_READ(PIPEASTAT); + if (pipea_stats & 0x8000ffff) { I915_WRITE(PIPEASTAT, pipea_stats); - spin_unlock_irqrestore(&dev_priv->user_irq_lock, - irqflags); + irq_received = 1; } - if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { - spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); - pipeb_stats = I915_READ(PIPEBSTAT); + if (pipeb_stats & 0x8000ffff) { I915_WRITE(PIPEBSTAT, pipeb_stats); - spin_unlock_irqrestore(&dev_priv->user_irq_lock, - irqflags); + irq_received = 1; } + spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); + + if (!irq_received) + break; + + ret = IRQ_HANDLED; I915_WRITE(IIR, iir); new_iir = I915_READ(IIR); /* Flush posted writes */ @@ -214,12 +231,12 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) DRM_WAKEUP(&dev_priv->irq_queue); } - if (pipea_stats & I915_VBLANK_INTERRUPT_STATUS) { + if (pipea_stats & vblank_status) { vblank++; drm_handle_vblank(dev, 0); } - if (pipeb_stats & I915_VBLANK_INTERRUPT_STATUS) { + if (pipeb_stats & vblank_status) { vblank++; drm_handle_vblank(dev, 1); } @@ -244,9 +261,9 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) * stray interrupts. */ iir = new_iir; - } while (iir != 0); + } - return IRQ_HANDLED; + return ret; } static int i915_emit_irq(struct drm_device * dev) -- cgit v1.2.1 From 6133047aa64d2fd5b3b79dff74f696ded45615b2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 20 Nov 2008 23:14:48 -0800 Subject: drm/i915: execbuffer pins objects, no need to ensure they're still in the GTT Before we had the notion of pinning objects, we had a kludge around to make sure all of the objects were still resident in the GTT before we committed to executing a batch buffer. We don't need this any longer, and it sticks an error return in the middle of object domain computations that must be associated with a subsequent flush/invalidate emmission into the ring. Signed-off-by: Keith Packard Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6e4c6dd50659..54bb0d0e49b8 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1866,17 +1866,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; - struct drm_i915_gem_object *obj_priv = obj->driver_private; - - if (obj_priv->gtt_space == NULL) { - /* We evicted the buffer in the process of validating - * our set of buffers in. We could try to recover by - * kicking them everything out and trying again from - * the start. - */ - ret = -ENOMEM; - goto err; - } /* make sure all previous memory operations have passed */ ret = i915_gem_object_set_domain(obj, -- cgit v1.2.1 From 52440211dcdc52c0b757f8b34d122e11b12cdd50 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 18 Nov 2008 09:30:25 -0800 Subject: drm: move drm vblank initialization/cleanup to driver load/unload drm vblank initialization keeps track of the changes in driver-supplied frame counts across vt switch and mode setting, but only if you let it by not tearing down the drm vblank structure. Signed-off-by: Keith Packard Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_drv.c | 2 ++ drivers/gpu/drm/drm_irq.c | 4 +--- drivers/gpu/drm/i915/i915_dma.c | 7 +++++++ drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_irq.c | 5 ----- drivers/gpu/drm/mga/mga_dma.c | 8 ++++++++ drivers/gpu/drm/mga/mga_irq.c | 5 ----- drivers/gpu/drm/r128/r128_drv.c | 6 ++++++ drivers/gpu/drm/r128/r128_drv.h | 1 + drivers/gpu/drm/r128/r128_irq.c | 2 +- drivers/gpu/drm/radeon/radeon_cp.c | 6 ++++++ drivers/gpu/drm/radeon/radeon_irq.c | 5 ----- drivers/gpu/drm/via/via_irq.c | 1 - drivers/gpu/drm/via/via_map.c | 11 ++++++++++- 14 files changed, 44 insertions(+), 21 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3ab1e9cc4692..996097acb5e7 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -305,6 +305,8 @@ static void drm_cleanup(struct drm_device * dev) return; } + drm_vblank_cleanup(dev); + drm_lastclose(dev); if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 15c8dabc3e97..1e787f894b3c 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -94,7 +94,7 @@ static void vblank_disable_fn(unsigned long arg) } } -static void drm_vblank_cleanup(struct drm_device *dev) +void drm_vblank_cleanup(struct drm_device *dev) { /* Bail if the driver didn't call drm_vblank_init() */ if (dev->num_crtcs == 0) @@ -278,8 +278,6 @@ int drm_irq_uninstall(struct drm_device * dev) free_irq(dev->pdev->irq, dev); - drm_vblank_cleanup(dev); - return 0; } EXPORT_SYMBOL(drm_irq_uninstall); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0d215e38606a..ba89b42f790a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -856,6 +856,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) spin_lock_init(&dev_priv->user_irq_lock); + ret = drm_vblank_init(dev, I915_NUM_PIPE); + + if (ret) { + (void) i915_driver_unload(dev); + return ret; + } + return ret; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ed1edcf8704f..b24d522f490c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -47,6 +47,8 @@ enum pipe { PIPE_B, }; +#define I915_NUM_PIPE 2 + /* Interface history: * * 1.1: Original. diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c3673581db58..fe3d9cc72bf5 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -498,11 +498,6 @@ void i915_driver_irq_preinstall(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; - - 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; diff --git a/drivers/gpu/drm/mga/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c index c1d12dbfa8d8..b49c5ff29585 100644 --- a/drivers/gpu/drm/mga/mga_dma.c +++ b/drivers/gpu/drm/mga/mga_dma.c @@ -396,6 +396,7 @@ int mga_freelist_put(struct drm_device * dev, struct drm_buf * buf) int mga_driver_load(struct drm_device * dev, unsigned long flags) { drm_mga_private_t *dev_priv; + int ret; dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER); if (!dev_priv) @@ -415,6 +416,13 @@ int mga_driver_load(struct drm_device * dev, unsigned long flags) dev->types[7] = _DRM_STAT_PRIMARY; dev->types[8] = _DRM_STAT_SECONDARY; + ret = drm_vblank_init(dev, 1); + + if (ret) { + (void) mga_driver_unload(dev); + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/mga/mga_irq.c b/drivers/gpu/drm/mga/mga_irq.c index bab42f41188b..daa6041a483a 100644 --- a/drivers/gpu/drm/mga/mga_irq.c +++ b/drivers/gpu/drm/mga/mga_irq.c @@ -152,11 +152,6 @@ void mga_driver_irq_preinstall(struct drm_device * dev) int mga_driver_irq_postinstall(struct drm_device *dev) { drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; - int ret; - - ret = drm_vblank_init(dev, 1); - if (ret) - return ret; DRM_INIT_WAITQUEUE(&dev_priv->fence_queue); diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index 3265d53ba91f..601f4c0e5da5 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -45,6 +45,7 @@ static struct drm_driver driver = { DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof(drm_r128_buf_priv_t), + .load = r128_driver_load, .preclose = r128_driver_preclose, .lastclose = r128_driver_lastclose, .get_vblank_counter = r128_get_vblank_counter, @@ -84,6 +85,11 @@ static struct drm_driver driver = { .patchlevel = DRIVER_PATCHLEVEL, }; +int r128_driver_load(struct drm_device * dev, unsigned long flags) +{ + return drm_vblank_init(dev, 1); +} + static int __init r128_init(void) { driver.num_ioctls = r128_max_ioctl; diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h index 5898b274279d..797a26c42dab 100644 --- a/drivers/gpu/drm/r128/r128_drv.h +++ b/drivers/gpu/drm/r128/r128_drv.h @@ -159,6 +159,7 @@ extern void r128_driver_irq_preinstall(struct drm_device * dev); extern int r128_driver_irq_postinstall(struct drm_device *dev); extern void r128_driver_irq_uninstall(struct drm_device * dev); extern void r128_driver_lastclose(struct drm_device * dev); +extern int r128_driver_load(struct drm_device * dev, unsigned long flags); extern void r128_driver_preclose(struct drm_device * dev, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/r128/r128_irq.c b/drivers/gpu/drm/r128/r128_irq.c index d7349012a680..69810fb8ac49 100644 --- a/drivers/gpu/drm/r128/r128_irq.c +++ b/drivers/gpu/drm/r128/r128_irq.c @@ -102,7 +102,7 @@ void r128_driver_irq_preinstall(struct drm_device * dev) int r128_driver_irq_postinstall(struct drm_device *dev) { - return drm_vblank_init(dev, 1); + return 0; } void r128_driver_irq_uninstall(struct drm_device * dev) diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index abdc1ae38467..dcebb4bee7aa 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -1757,6 +1757,12 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags) if (ret != 0) return ret; + ret = drm_vblank_init(dev, 2); + if (ret) { + radeon_driver_unload(dev); + return ret; + } + DRM_DEBUG("%s card detected\n", ((dev_priv->flags & RADEON_IS_AGP) ? "AGP" : (((dev_priv->flags & RADEON_IS_PCIE) ? "PCIE" : "PCI")))); return ret; diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c index 5079f7054a2f..97c0599fdb1e 100644 --- a/drivers/gpu/drm/radeon/radeon_irq.c +++ b/drivers/gpu/drm/radeon/radeon_irq.c @@ -337,15 +337,10 @@ 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); - 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); diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c index 665d319b927b..c248c1d37268 100644 --- a/drivers/gpu/drm/via/via_irq.c +++ b/drivers/gpu/drm/via/via_irq.c @@ -314,7 +314,6 @@ int via_driver_irq_postinstall(struct drm_device *dev) if (!dev_priv) return -EINVAL; - drm_vblank_init(dev, 1); status = VIA_READ(VIA_REG_INTERRUPT); VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL | dev_priv->irq_enable_mask); diff --git a/drivers/gpu/drm/via/via_map.c b/drivers/gpu/drm/via/via_map.c index a967556be014..2c4f0b485792 100644 --- a/drivers/gpu/drm/via/via_map.c +++ b/drivers/gpu/drm/via/via_map.c @@ -107,8 +107,17 @@ int via_driver_load(struct drm_device *dev, unsigned long chipset) ret = drm_sman_init(&dev_priv->sman, 2, 12, 8); if (ret) { drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + return ret; } - return ret; + + ret = drm_vblank_init(dev, 1); + if (ret) { + drm_sman_takedown(&dev_priv->sman); + drm_free(dev_priv, sizeof(drm_via_private_t), DRM_MEM_DRIVER); + return ret; + } + + return 0; } int via_driver_unload(struct drm_device *dev) -- cgit v1.2.1 From 461cba2d294fe83297edf8a6556912812903dce1 Mon Sep 17 00:00:00 2001 From: Peng Li Date: Tue, 18 Nov 2008 12:39:02 +0800 Subject: drm/i915: Save/restore HWS_PGA on suspend/resume It fixes suspend/resume failure of xf86-video-intel dri2 branch. As dri2 branch doesn't call I830DRIResume() to restore hardware status page anymore, we need to preserve this register across suspend/resume. Signed-off-by: Peng Li Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_suspend.c | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b24d522f490c..0a4f39b9a0ec 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -150,6 +150,7 @@ typedef struct drm_i915_private { u32 saveDSPBCNTR; u32 saveDSPARB; u32 saveRENDERSTANDBY; + u32 saveHWS; u32 savePIPEACONF; u32 savePIPEBCONF; u32 savePIPEASRC; diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 5ddc6e595c0c..5d84027ee8f3 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -244,6 +244,9 @@ int i915_save_state(struct drm_device *dev) if (IS_I965G(dev) && IS_MOBILE(dev)) dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY); + /* Hardware status page */ + dev_priv->saveHWS = I915_READ(HWS_PGA); + /* Display arbitration control */ dev_priv->saveDSPARB = I915_READ(DSPARB); @@ -373,6 +376,9 @@ int i915_restore_state(struct drm_device *dev) if (IS_I965G(dev) && IS_MOBILE(dev)) I915_WRITE(MCHBAR_RENDER_STANDBY, dev_priv->saveRENDERSTANDBY); + /* Hardware status page */ + I915_WRITE(HWS_PGA, dev_priv->saveHWS); + /* Display arbitration */ I915_WRITE(DSPARB, dev_priv->saveDSPARB); -- cgit v1.2.1 From 151903d5466fbcfb56ce792c3d5ea0ecbae15d07 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 1 Dec 2008 10:23:21 +1000 Subject: drm/i915: Fix copy'n'pasteo that broke VT switch if flushing was non-empty. Introduced in the "Avoid BUG_ONs on VT switch" commit. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 54bb0d0e49b8..d58ddef468f8 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2314,7 +2314,7 @@ i915_gem_idle(struct drm_device *dev) while (!list_empty(&dev_priv->mm.flushing_list)) { struct drm_i915_gem_object *obj_priv; - obj_priv = list_first_entry(&dev_priv->mm.active_list, + obj_priv = list_first_entry(&dev_priv->mm.flushing_list, struct drm_i915_gem_object, list); obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; -- cgit v1.2.1 From 9c84ba4e502184d95ab75128d3166f595ea2dea0 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 1 Dec 2008 13:14:08 -0800 Subject: drivers/gpu/drm/i915/i915_irq.c: fix warning drivers/gpu/drm/i915/i915_irq.c: In function 'i915_disable_pipestat': drivers/gpu/drm/i915/i915_irq.c:101: warning: control may reach end of non-void function 'i915_pipestat' being inlined Cc: Dave Airlie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/i915_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index fe3d9cc72bf5..69b9a42da95e 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -78,7 +78,7 @@ i915_pipestat(int pipe) return PIPEASTAT; if (pipe == 1) return PIPEBSTAT; - BUG_ON(1); + BUG(); } void -- cgit v1.2.1 From a7f014f2de04893f95cfe40fe35f15c8dae4b36e Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 25 Nov 2008 14:02:05 -0800 Subject: drm/i915: Respect GM965/GM45 bit-17-instead-of-bit-11 option for swizzling. This fixes readpixels and buffer corruption when swapped out and in by disabling tiling on them. Now that we know that the bit 17 mode isn't just a mistake of older chipsets, we'll need to work on a clever fix so that we can get the performance of tiling on these chipsets, but that will require intrusive changes targeted at the next kernel release, not this one. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem_tiling.c | 7 ++++--- drivers/gpu/drm/i915/i915_reg.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index e8b85ac4ca04..a8cb69469c64 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -119,9 +119,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) dcc & DCC_CHANNEL_XOR_DISABLE) { swizzle_x = I915_BIT_6_SWIZZLE_9_10; swizzle_y = I915_BIT_6_SWIZZLE_9; - } else if (IS_I965GM(dev) || IS_GM45(dev)) { - /* GM965 only does bit 11-based channel - * randomization + } else if ((IS_I965GM(dev) || IS_GM45(dev)) && + (dcc & DCC_CHANNEL_XOR_BIT_17) == 0) { + /* GM965/GM45 does either bit 11 or bit 17 + * swizzling. */ swizzle_x = I915_BIT_6_SWIZZLE_9_10_11; swizzle_y = I915_BIT_6_SWIZZLE_9_11; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 0e476eba36e6..9d24aaeb8a45 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -522,6 +522,7 @@ #define DCC_ADDRESSING_MODE_DUAL_CHANNEL_INTERLEAVED (2 << 0) #define DCC_ADDRESSING_MODE_MASK (3 << 0) #define DCC_CHANNEL_XOR_DISABLE (1 << 10) +#define DCC_CHANNEL_XOR_BIT_17 (1 << 9) /** 965 MCH register controlling DRAM channel configuration */ #define C0DRB3 0x10206 -- cgit v1.2.1 From ce44b0ea3dc04236d852d78a06f850d1f7b03f3d Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 6 Nov 2008 16:00:31 -0800 Subject: drm/i915: Move flushing list cleanup from flush request retire to request emit. obj_priv->write_domain is "write domain if the GPU went idle now", not "write domain at this moment." By postponing the clear, we confused the concept, required more storage, and potentially emitted more flushes than are required. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_drv.h | 11 ++++-- drivers/gpu/drm/i915/i915_gem.c | 73 ++++++++++++++++++++---------------- drivers/gpu/drm/i915/i915_gem_proc.c | 5 +-- 3 files changed, 50 insertions(+), 39 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0a4f39b9a0ec..76d9a706d8fd 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -244,6 +244,10 @@ typedef struct drm_i915_private { * List of objects currently involved in rendering from the * ringbuffer. * + * Includes buffers having the contents of their GPU caches + * flushed, not necessarily primitives. last_rendering_seqno + * represents when the rendering involved will be completed. + * * A reference is held on the buffer while on this list. */ struct list_head active_list; @@ -253,6 +257,8 @@ typedef struct drm_i915_private { * still have a write_domain which needs to be flushed before * unbinding. * + * last_rendering_seqno is 0 while an object is in this list. + * * A reference is held on the buffer while on this list. */ struct list_head flushing_list; @@ -261,6 +267,8 @@ typedef struct drm_i915_private { * LRU list of objects which are not in the ringbuffer and * are ready to unbind, but are still in the GTT. * + * last_rendering_seqno is 0 while an object is in this list. + * * A reference is not held on the buffer while on this list, * as merely being GTT-bound shouldn't prevent its being * freed, and we'll pull it off the list in the free path. @@ -394,9 +402,6 @@ struct drm_i915_gem_request { /** Time at which this request was emitted, in jiffies. */ unsigned long emitted_jiffies; - /** Cache domains that were flushed at the start of the request. */ - uint32_t flush_domains; - struct list_head list; }; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d58ddef468f8..9fd28ebe0aa3 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -532,7 +532,7 @@ i915_gem_object_free_page_list(struct drm_gem_object *obj) } static void -i915_gem_object_move_to_active(struct drm_gem_object *obj) +i915_gem_object_move_to_active(struct drm_gem_object *obj, uint32_t seqno) { struct drm_device *dev = obj->dev; drm_i915_private_t *dev_priv = dev->dev_private; @@ -546,8 +546,20 @@ i915_gem_object_move_to_active(struct drm_gem_object *obj) /* Move from whatever list we were on to the tail of execution. */ list_move_tail(&obj_priv->list, &dev_priv->mm.active_list); + obj_priv->last_rendering_seqno = seqno; } +static void +i915_gem_object_move_to_flushing(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + BUG_ON(!obj_priv->active); + list_move_tail(&obj_priv->list, &dev_priv->mm.flushing_list); + obj_priv->last_rendering_seqno = 0; +} static void i915_gem_object_move_to_inactive(struct drm_gem_object *obj) @@ -562,6 +574,7 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj) else list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list); + obj_priv->last_rendering_seqno = 0; if (obj_priv->active) { obj_priv->active = 0; drm_gem_object_unreference(obj); @@ -610,10 +623,28 @@ i915_add_request(struct drm_device *dev, uint32_t flush_domains) request->seqno = seqno; request->emitted_jiffies = jiffies; - request->flush_domains = flush_domains; was_empty = list_empty(&dev_priv->mm.request_list); list_add_tail(&request->list, &dev_priv->mm.request_list); + /* Associate any objects on the flushing list matching the write + * domain we're flushing with our flush. + */ + if (flush_domains != 0) { + struct drm_i915_gem_object *obj_priv, *next; + + list_for_each_entry_safe(obj_priv, next, + &dev_priv->mm.flushing_list, list) { + struct drm_gem_object *obj = obj_priv->obj; + + if ((obj->write_domain & flush_domains) == + obj->write_domain) { + obj->write_domain = 0; + i915_gem_object_move_to_active(obj, seqno); + } + } + + } + if (was_empty && !dev_priv->mm.suspended) schedule_delayed_work(&dev_priv->mm.retire_work, HZ); return seqno; @@ -676,30 +707,10 @@ i915_gem_retire_request(struct drm_device *dev, __func__, request->seqno, obj); #endif - if (obj->write_domain != 0) { - list_move_tail(&obj_priv->list, - &dev_priv->mm.flushing_list); - } else { + if (obj->write_domain != 0) + i915_gem_object_move_to_flushing(obj); + else i915_gem_object_move_to_inactive(obj); - } - } - - if (request->flush_domains != 0) { - struct drm_i915_gem_object *obj_priv, *next; - - /* Clear the write domain and activity from any buffers - * that are just waiting for a flush matching the one retired. - */ - list_for_each_entry_safe(obj_priv, next, - &dev_priv->mm.flushing_list, list) { - struct drm_gem_object *obj = obj_priv->obj; - - if (obj->write_domain & request->flush_domains) { - obj->write_domain = 0; - i915_gem_object_move_to_inactive(obj); - } - } - } } @@ -896,17 +907,15 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) * create a new seqno to wait for. */ if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) { - uint32_t write_domain = obj->write_domain; + uint32_t seqno, write_domain = obj->write_domain; #if WATCH_BUF DRM_INFO("%s: flushing object %p from write domain %08x\n", __func__, obj, write_domain); #endif i915_gem_flush(dev, 0, write_domain); - i915_gem_object_move_to_active(obj); - obj_priv->last_rendering_seqno = i915_add_request(dev, - write_domain); - BUG_ON(obj_priv->last_rendering_seqno == 0); + seqno = i915_add_request(dev, write_domain); + i915_gem_object_move_to_active(obj, seqno); #if WATCH_LRU DRM_INFO("%s: flush moves to exec list %p\n", __func__, obj); #endif @@ -1927,10 +1936,8 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, i915_file_priv->mm.last_gem_seqno = seqno; for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; - struct drm_i915_gem_object *obj_priv = obj->driver_private; - i915_gem_object_move_to_active(obj); - obj_priv->last_rendering_seqno = seqno; + i915_gem_object_move_to_active(obj, seqno); #if WATCH_LRU DRM_INFO("%s: move to exec list %p\n", __func__, obj); #endif diff --git a/drivers/gpu/drm/i915/i915_gem_proc.c b/drivers/gpu/drm/i915/i915_gem_proc.c index 93de15b4c9a7..e8d5abe1250e 100644 --- a/drivers/gpu/drm/i915/i915_gem_proc.c +++ b/drivers/gpu/drm/i915/i915_gem_proc.c @@ -166,10 +166,9 @@ static int i915_gem_request_info(char *buf, char **start, off_t offset, list_for_each_entry(gem_request, &dev_priv->mm.request_list, list) { - DRM_PROC_PRINT(" %d @ %d %08x\n", + DRM_PROC_PRINT(" %d @ %d\n", gem_request->seqno, - (int) (jiffies - gem_request->emitted_jiffies), - gem_request->flush_domains); + (int) (jiffies - gem_request->emitted_jiffies)); } if (len > request + offset) return request; -- cgit v1.2.1 From b670d8158283c35842ae1c650f75c375d8710607 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 14 Nov 2008 16:27:47 -0800 Subject: drm/i915: If interrupted while setting object domains, still emit the flush. Otherwise, we would leave the objects in an inconsistent state, such as write_domain == 0 but on the flushing list. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 9fd28ebe0aa3..3fe108b7e2fa 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1880,8 +1880,20 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, ret = i915_gem_object_set_domain(obj, obj->pending_read_domains, obj->pending_write_domain); - if (ret) + if (ret) { + /* As we've partially updated domains on our buffers, + * we have to emit the flush we've accumulated + * before exiting, or we'll have broken the + * active/flushing/inactive invariants. + * + * We'll potentially have some things marked as + * being in write domains that they actually aren't, + * but that should be merely a minor performance loss. + */ + flush_domains = i915_gem_dev_set_domain(dev); + (void)i915_add_request(dev, flush_domains); goto err; + } } i915_verify_inactive(dev, __FILE__, __LINE__); -- cgit v1.2.1 From 2ef7eeaa553d88e78d9a4520271f26a7bc0e2968 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 10 Nov 2008 10:53:25 -0800 Subject: drm/i915: Make a single set-to-gtt-domain path. This fixes failure to flush caches in the relocation update path, and failure to wait in the set_domain ioctl, each of which could lead to incorrect rendering. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 115 +++++++++++++++++++++++++++++++++------- 1 file changed, 96 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 3fe108b7e2fa..62a059dce85f 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -48,6 +48,8 @@ i915_gem_set_domain(struct drm_gem_object *obj, struct drm_file *file_priv, uint32_t read_domains, uint32_t write_domain); +static int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, + int write); static int i915_gem_object_get_page_list(struct drm_gem_object *obj); static void i915_gem_object_free_page_list(struct drm_gem_object *obj); static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); @@ -260,8 +262,7 @@ i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, mutex_unlock(&dev->struct_mutex); return ret; } - ret = i915_gem_set_domain(obj, file_priv, - I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); + ret = i915_gem_object_set_to_gtt_domain(obj, 1); if (ret) goto fail; @@ -397,7 +398,8 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, } /** - * Called when user space prepares to use an object + * Called when user space prepares to use an object with the CPU, either + * through the mmap ioctl's mapping or a GTT mapping. */ int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, @@ -405,11 +407,26 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_set_domain *args = data; struct drm_gem_object *obj; + uint32_t read_domains = args->read_domains; + uint32_t write_domain = args->write_domain; int ret; if (!(dev->driver->driver_features & DRIVER_GEM)) return -ENODEV; + /* Only handle setting domains to types used by the CPU. */ + if (write_domain & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) + return -EINVAL; + + if (read_domains & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) + return -EINVAL; + + /* Having something in the write domain implies it's in the read + * domain, and only that read domain. Enforce that in the request. + */ + if (write_domain != 0 && read_domains != write_domain) + return -EINVAL; + obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) return -EBADF; @@ -417,10 +434,15 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); #if WATCH_BUF DRM_INFO("set_domain_ioctl %p(%d), %08x %08x\n", - obj, obj->size, args->read_domains, args->write_domain); + obj, obj->size, read_domains, write_domain); #endif - ret = i915_gem_set_domain(obj, file_priv, - args->read_domains, args->write_domain); + if (read_domains & I915_GEM_DOMAIN_GTT) { + ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0); + } else { + ret = i915_gem_set_domain(obj, file_priv, + read_domains, write_domain); + } + drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; @@ -1237,6 +1259,69 @@ i915_gem_clflush_object(struct drm_gem_object *obj) drm_clflush_pages(obj_priv->page_list, obj->size / PAGE_SIZE); } +/** + * Moves a single object to the GTT read, and possibly write domain. + * + * This function returns when the move is complete, including waiting on + * flushes to occur. + */ +static int +i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + uint32_t flush_domains; + + /* Figure out what GPU domains we need to flush or invalidate for + * moving to GTT. + */ + flush_domains = obj->write_domain & I915_GEM_GPU_DOMAINS; + + /* Queue the GPU write cache flushing we need. */ + if (flush_domains != 0) { + uint32_t seqno; + + obj->write_domain &= ~I915_GEM_GPU_DOMAINS; + i915_gem_flush(dev, 0, flush_domains); + seqno = i915_add_request(dev, flush_domains); + i915_gem_object_move_to_active(obj, seqno); + } + + /* Wait on any GPU rendering and flushing to occur. */ + if (obj_priv->active) { + int ret; + + ret = i915_wait_request(dev, obj_priv->last_rendering_seqno); + if (ret != 0) + return ret; + } + + /* If we're writing through the GTT domain, then CPU and GPU caches + * will need to be invalidated at next use. + */ + if (write) + obj->read_domains &= ~(I915_GEM_GPU_DOMAINS | + I915_GEM_DOMAIN_CPU); + + /* Flush the CPU domain if it's dirty. */ + if (obj->write_domain & I915_GEM_DOMAIN_CPU) { + i915_gem_clflush_object(obj); + drm_agp_chipset_flush(dev); + + obj->write_domain &= ~I915_GEM_DOMAIN_CPU; + } + + /* It should now be out of any other write domains, and we can update + * the domain values for our changes. + */ + BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0); + obj->read_domains |= I915_GEM_DOMAIN_GTT; + if (write) + obj->write_domain = I915_GEM_DOMAIN_GTT; + + return 0; +} + /* * Set the next domain for the specified object. This * may not actually perform the necessary flushing/invaliding though, @@ -1634,19 +1719,11 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, continue; } - /* Now that we're going to actually write some data in, - * make sure that any rendering using this buffer's contents - * is completed. - */ - i915_gem_object_wait_rendering(obj); - - /* As we're writing through the gtt, flush - * any CPU writes before we write the relocations - */ - if (obj->write_domain & I915_GEM_DOMAIN_CPU) { - i915_gem_clflush_object(obj); - drm_agp_chipset_flush(dev); - obj->write_domain = 0; + ret = i915_gem_object_set_to_gtt_domain(obj, 1); + if (ret != 0) { + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return -EINVAL; } /* Map the page containing the relocation we're going to -- cgit v1.2.1 From e47c68e9c5d71e2faab8c2b82f57c6c73e6456fd Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 14 Nov 2008 13:35:19 -0800 Subject: drm/i915: Make a single set-to-cpu-domain path and use it wherever needed. This fixes several domain management bugs, including potential lack of cache invalidation for pread, potential failure to wait for set_domain(CPU, 0), and more, along with producing more intelligible code. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_drv.h | 4 +- drivers/gpu/drm/i915/i915_gem.c | 363 +++++++++++++++++++++++----------------- 2 files changed, 215 insertions(+), 152 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 76d9a706d8fd..adc972cc6bfc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -379,8 +379,8 @@ struct drm_i915_gem_object { uint32_t agp_type; /** - * Flagging of which individual pages are valid in GEM_DOMAIN_CPU when - * GEM_DOMAIN_CPU is not in the object's read domain. + * If present, while GEM_DOMAIN_CPU is in the read domain this array + * flags which individual pages are valid. */ uint8_t *page_cpu_valid; }; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 62a059dce85f..f7e9f2c2934c 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -37,19 +37,17 @@ static int i915_gem_object_set_domain(struct drm_gem_object *obj, uint32_t read_domains, uint32_t write_domain); -static int -i915_gem_object_set_domain_range(struct drm_gem_object *obj, - uint64_t offset, - uint64_t size, - uint32_t read_domains, - uint32_t write_domain); -static int -i915_gem_set_domain(struct drm_gem_object *obj, - struct drm_file *file_priv, - uint32_t read_domains, - uint32_t write_domain); +static void i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj); +static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj); +static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj); static int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write); +static int i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, + int write); +static int i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj, + uint64_t offset, + uint64_t size); +static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *obj); static int i915_gem_object_get_page_list(struct drm_gem_object *obj); static void i915_gem_object_free_page_list(struct drm_gem_object *obj); static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); @@ -164,8 +162,8 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data, mutex_lock(&dev->struct_mutex); - ret = i915_gem_object_set_domain_range(obj, args->offset, args->size, - I915_GEM_DOMAIN_CPU, 0); + ret = i915_gem_object_set_cpu_read_domain_range(obj, args->offset, + args->size); if (ret != 0) { drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); @@ -321,8 +319,7 @@ i915_gem_shmem_pwrite(struct drm_device *dev, struct drm_gem_object *obj, mutex_lock(&dev->struct_mutex); - ret = i915_gem_set_domain(obj, file_priv, - I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU); + ret = i915_gem_object_set_to_cpu_domain(obj, 1); if (ret) { mutex_unlock(&dev->struct_mutex); return ret; @@ -439,8 +436,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, if (read_domains & I915_GEM_DOMAIN_GTT) { ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0); } else { - ret = i915_gem_set_domain(obj, file_priv, - read_domains, write_domain); + ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0); } drm_gem_object_unreference(obj); @@ -477,10 +473,9 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, obj_priv = obj->driver_private; /* Pinned buffers may be scanout, so flush the cache */ - if ((obj->write_domain & I915_GEM_DOMAIN_CPU) && obj_priv->pin_count) { - i915_gem_clflush_object(obj); - drm_agp_chipset_flush(dev); - } + if (obj_priv->pin_count) + i915_gem_object_flush_cpu_write_domain(obj); + drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; @@ -925,23 +920,10 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj) struct drm_i915_gem_object *obj_priv = obj->driver_private; int ret; - /* If there are writes queued to the buffer, flush and - * create a new seqno to wait for. + /* This function only exists to support waiting for existing rendering, + * not for emitting required flushes. */ - if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) { - uint32_t seqno, write_domain = obj->write_domain; -#if WATCH_BUF - DRM_INFO("%s: flushing object %p from write domain %08x\n", - __func__, obj, write_domain); -#endif - i915_gem_flush(dev, 0, write_domain); - - seqno = i915_add_request(dev, write_domain); - i915_gem_object_move_to_active(obj, seqno); -#if WATCH_LRU - DRM_INFO("%s: flush moves to exec list %p\n", __func__, obj); -#endif - } + BUG_ON((obj->write_domain & I915_GEM_GPU_DOMAINS) != 0); /* If there is rendering queued on the buffer being evicted, wait for * it. @@ -981,24 +963,16 @@ i915_gem_object_unbind(struct drm_gem_object *obj) return -EINVAL; } - /* Wait for any rendering to complete - */ - ret = i915_gem_object_wait_rendering(obj); - if (ret) { - DRM_ERROR("wait_rendering failed: %d\n", ret); - return ret; - } - /* Move the object to the CPU domain to ensure that * any possible CPU writes while it's not in the GTT * are flushed when we go to remap it. This will * also ensure that all pending GPU writes are finished * before we unbind. */ - ret = i915_gem_object_set_domain(obj, I915_GEM_DOMAIN_CPU, - I915_GEM_DOMAIN_CPU); + ret = i915_gem_object_set_to_cpu_domain(obj, 1); if (ret) { - DRM_ERROR("set_domain failed: %d\n", ret); + if (ret != -ERESTARTSYS) + DRM_ERROR("set_domain failed: %d\n", ret); return ret; } @@ -1259,6 +1233,51 @@ i915_gem_clflush_object(struct drm_gem_object *obj) drm_clflush_pages(obj_priv->page_list, obj->size / PAGE_SIZE); } +/** Flushes any GPU write domain for the object if it's dirty. */ +static void +i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + uint32_t seqno; + + if ((obj->write_domain & I915_GEM_GPU_DOMAINS) == 0) + return; + + /* Queue the GPU write cache flushing we need. */ + i915_gem_flush(dev, 0, obj->write_domain); + seqno = i915_add_request(dev, obj->write_domain); + obj->write_domain = 0; + i915_gem_object_move_to_active(obj, seqno); +} + +/** Flushes the GTT write domain for the object if it's dirty. */ +static void +i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj) +{ + if (obj->write_domain != I915_GEM_DOMAIN_GTT) + return; + + /* No actual flushing is required for the GTT write domain. Writes + * to it immediately go to main memory as far as we know, so there's + * no chipset flush. It also doesn't land in render cache. + */ + obj->write_domain = 0; +} + +/** Flushes the CPU write domain for the object if it's dirty. */ +static void +i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + + if (obj->write_domain != I915_GEM_DOMAIN_CPU) + return; + + i915_gem_clflush_object(obj); + drm_agp_chipset_flush(dev); + obj->write_domain = 0; +} + /** * Moves a single object to the GTT read, and possibly write domain. * @@ -1268,56 +1287,81 @@ i915_gem_clflush_object(struct drm_gem_object *obj) static int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) { - struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = obj->driver_private; - uint32_t flush_domains; + int ret; - /* Figure out what GPU domains we need to flush or invalidate for - * moving to GTT. + i915_gem_object_flush_gpu_write_domain(obj); + /* Wait on any GPU rendering and flushing to occur. */ + ret = i915_gem_object_wait_rendering(obj); + if (ret != 0) + return ret; + + /* If we're writing through the GTT domain, then CPU and GPU caches + * will need to be invalidated at next use. */ - flush_domains = obj->write_domain & I915_GEM_GPU_DOMAINS; + if (write) + obj->read_domains &= I915_GEM_DOMAIN_GTT; - /* Queue the GPU write cache flushing we need. */ - if (flush_domains != 0) { - uint32_t seqno; + i915_gem_object_flush_cpu_write_domain(obj); - obj->write_domain &= ~I915_GEM_GPU_DOMAINS; - i915_gem_flush(dev, 0, flush_domains); - seqno = i915_add_request(dev, flush_domains); - i915_gem_object_move_to_active(obj, seqno); + /* It should now be out of any other write domains, and we can update + * the domain values for our changes. + */ + BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0); + obj->read_domains |= I915_GEM_DOMAIN_GTT; + if (write) { + obj->write_domain = I915_GEM_DOMAIN_GTT; + obj_priv->dirty = 1; } + return 0; +} + +/** + * Moves a single object to the CPU read, and possibly write domain. + * + * This function returns when the move is complete, including waiting on + * flushes to occur. + */ +static int +i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) +{ + struct drm_device *dev = obj->dev; + int ret; + + i915_gem_object_flush_gpu_write_domain(obj); /* Wait on any GPU rendering and flushing to occur. */ - if (obj_priv->active) { - int ret; + ret = i915_gem_object_wait_rendering(obj); + if (ret != 0) + return ret; - ret = i915_wait_request(dev, obj_priv->last_rendering_seqno); - if (ret != 0) - return ret; - } + i915_gem_object_flush_gtt_write_domain(obj); - /* If we're writing through the GTT domain, then CPU and GPU caches - * will need to be invalidated at next use. + /* If we have a partially-valid cache of the object in the CPU, + * finish invalidating it and free the per-page flags. */ - if (write) - obj->read_domains &= ~(I915_GEM_GPU_DOMAINS | - I915_GEM_DOMAIN_CPU); + i915_gem_object_set_to_full_cpu_read_domain(obj); - /* Flush the CPU domain if it's dirty. */ - if (obj->write_domain & I915_GEM_DOMAIN_CPU) { + /* Flush the CPU cache if it's still invalid. */ + if ((obj->read_domains & I915_GEM_DOMAIN_CPU) == 0) { i915_gem_clflush_object(obj); drm_agp_chipset_flush(dev); - obj->write_domain &= ~I915_GEM_DOMAIN_CPU; + obj->read_domains |= I915_GEM_DOMAIN_CPU; } /* It should now be out of any other write domains, and we can update * the domain values for our changes. */ - BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0); - obj->read_domains |= I915_GEM_DOMAIN_GTT; - if (write) - obj->write_domain = I915_GEM_DOMAIN_GTT; + BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_CPU) != 0); + + /* If we're writing through the CPU, then the GPU read domains will + * need to be invalidated at next use. + */ + if (write) { + obj->read_domains &= I915_GEM_DOMAIN_CPU; + obj->write_domain = I915_GEM_DOMAIN_CPU; + } return 0; } @@ -1442,7 +1486,9 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, struct drm_i915_gem_object *obj_priv = obj->driver_private; uint32_t invalidate_domains = 0; uint32_t flush_domains = 0; - int ret; + + BUG_ON(read_domains & I915_GEM_DOMAIN_CPU); + BUG_ON(write_domain == I915_GEM_DOMAIN_CPU); #if WATCH_BUF DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n", @@ -1479,34 +1525,11 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, DRM_INFO("%s: CPU domain flush %08x invalidate %08x\n", __func__, flush_domains, invalidate_domains); #endif - /* - * If we're invaliding the CPU cache and flushing a GPU cache, - * then pause for rendering so that the GPU caches will be - * flushed before the cpu cache is invalidated - */ - if ((invalidate_domains & I915_GEM_DOMAIN_CPU) && - (flush_domains & ~(I915_GEM_DOMAIN_CPU | - I915_GEM_DOMAIN_GTT))) { - ret = i915_gem_object_wait_rendering(obj); - if (ret) - return ret; - } i915_gem_clflush_object(obj); } if ((write_domain | flush_domains) != 0) obj->write_domain = write_domain; - - /* If we're invalidating the CPU domain, clear the per-page CPU - * domain list as well. - */ - if (obj_priv->page_cpu_valid != NULL && - (write_domain != 0 || - read_domains & I915_GEM_DOMAIN_CPU)) { - drm_free(obj_priv->page_cpu_valid, obj->size / PAGE_SIZE, - DRM_MEM_DRIVER); - obj_priv->page_cpu_valid = NULL; - } obj->read_domains = read_domains; dev->invalidate_domains |= invalidate_domains; @@ -1521,43 +1544,91 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, } /** - * Set the read/write domain on a range of the object. + * Moves the object from a partially CPU read to a full one. * - * Currently only implemented for CPU reads, otherwise drops to normal - * i915_gem_object_set_domain(). + * Note that this only resolves i915_gem_object_set_cpu_read_domain_range(), + * and doesn't handle transitioning from !(read_domains & I915_GEM_DOMAIN_CPU). */ -static int -i915_gem_object_set_domain_range(struct drm_gem_object *obj, - uint64_t offset, - uint64_t size, - uint32_t read_domains, - uint32_t write_domain) +static void +i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *obj) { + struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = obj->driver_private; - int ret, i; - if (obj->read_domains & I915_GEM_DOMAIN_CPU) - return 0; + if (!obj_priv->page_cpu_valid) + return; - if (read_domains != I915_GEM_DOMAIN_CPU || - write_domain != 0) - return i915_gem_object_set_domain(obj, - read_domains, write_domain); + /* If we're partially in the CPU read domain, finish moving it in. + */ + if (obj->read_domains & I915_GEM_DOMAIN_CPU) { + int i; + + for (i = 0; i <= (obj->size - 1) / PAGE_SIZE; i++) { + if (obj_priv->page_cpu_valid[i]) + continue; + drm_clflush_pages(obj_priv->page_list + i, 1); + } + drm_agp_chipset_flush(dev); + } - /* Wait on any GPU rendering to the object to be flushed. */ + /* Free the page_cpu_valid mappings which are now stale, whether + * or not we've got I915_GEM_DOMAIN_CPU. + */ + drm_free(obj_priv->page_cpu_valid, obj->size / PAGE_SIZE, + DRM_MEM_DRIVER); + obj_priv->page_cpu_valid = NULL; +} + +/** + * Set the CPU read domain on a range of the object. + * + * The object ends up with I915_GEM_DOMAIN_CPU in its read flags although it's + * not entirely valid. The page_cpu_valid member of the object flags which + * pages have been flushed, and will be respected by + * i915_gem_object_set_to_cpu_domain() if it's called on to get a valid mapping + * of the whole object. + * + * This function returns when the move is complete, including waiting on + * flushes to occur. + */ +static int +i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj, + uint64_t offset, uint64_t size) +{ + struct drm_i915_gem_object *obj_priv = obj->driver_private; + int i, ret; + + if (offset == 0 && size == obj->size) + return i915_gem_object_set_to_cpu_domain(obj, 0); + + i915_gem_object_flush_gpu_write_domain(obj); + /* Wait on any GPU rendering and flushing to occur. */ ret = i915_gem_object_wait_rendering(obj); - if (ret) + if (ret != 0) return ret; + i915_gem_object_flush_gtt_write_domain(obj); + + /* If we're already fully in the CPU read domain, we're done. */ + if (obj_priv->page_cpu_valid == NULL && + (obj->read_domains & I915_GEM_DOMAIN_CPU) != 0) + return 0; + /* Otherwise, create/clear the per-page CPU read domain flag if we're + * newly adding I915_GEM_DOMAIN_CPU + */ if (obj_priv->page_cpu_valid == NULL) { obj_priv->page_cpu_valid = drm_calloc(1, obj->size / PAGE_SIZE, DRM_MEM_DRIVER); - } + if (obj_priv->page_cpu_valid == NULL) + return -ENOMEM; + } else if ((obj->read_domains & I915_GEM_DOMAIN_CPU) == 0) + memset(obj_priv->page_cpu_valid, 0, obj->size / PAGE_SIZE); /* Flush the cache on any pages that are still invalid from the CPU's * perspective. */ - for (i = offset / PAGE_SIZE; i <= (offset + size - 1) / PAGE_SIZE; i++) { + for (i = offset / PAGE_SIZE; i <= (offset + size - 1) / PAGE_SIZE; + i++) { if (obj_priv->page_cpu_valid[i]) continue; @@ -1566,6 +1637,13 @@ i915_gem_object_set_domain_range(struct drm_gem_object *obj, obj_priv->page_cpu_valid[i] = 1; } + /* It should now be out of any other write domains, and we can update + * the domain values for our changes. + */ + BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_CPU) != 0); + + obj->read_domains |= I915_GEM_DOMAIN_CPU; + return 0; } @@ -1679,6 +1757,18 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, return -EINVAL; } + if (reloc.write_domain & I915_GEM_DOMAIN_CPU || + reloc.read_domains & I915_GEM_DOMAIN_CPU) { + DRM_ERROR("reloc with read/write CPU domains: " + "obj %p target %d offset %d " + "read %08x write %08x", + obj, reloc.target_handle, + (int) reloc.offset, + reloc.read_domains, + reloc.write_domain); + return -EINVAL; + } + if (reloc.write_domain && target_obj->pending_write_domain && reloc.write_domain != target_obj->pending_write_domain) { DRM_ERROR("Write domain conflict: " @@ -2157,11 +2247,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, /* XXX - flush the CPU caches for pinned objects * as the X server doesn't manage domains yet */ - if (obj->write_domain & I915_GEM_DOMAIN_CPU) { - i915_gem_clflush_object(obj); - drm_agp_chipset_flush(dev); - obj->write_domain = 0; - } + i915_gem_object_flush_cpu_write_domain(obj); args->offset = obj_priv->gtt_offset; drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); @@ -2263,29 +2349,6 @@ void i915_gem_free_object(struct drm_gem_object *obj) drm_free(obj->driver_private, 1, DRM_MEM_DRIVER); } -static int -i915_gem_set_domain(struct drm_gem_object *obj, - struct drm_file *file_priv, - uint32_t read_domains, - uint32_t write_domain) -{ - struct drm_device *dev = obj->dev; - int ret; - uint32_t flush_domains; - - BUG_ON(!mutex_is_locked(&dev->struct_mutex)); - - ret = i915_gem_object_set_domain(obj, read_domains, write_domain); - if (ret) - return ret; - flush_domains = i915_gem_dev_set_domain(obj->dev); - - if (flush_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) - (void) i915_add_request(dev, flush_domains); - - return 0; -} - /** Unbinds all objects that are on the given buffer list. */ static int i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head) -- cgit v1.2.1 From c0d90829288942fa06d7483f9e84059a64605da5 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 20 Nov 2008 23:11:08 -0800 Subject: drm/i915: Rename object_set_domain to object_set_to_gpu_domain Now that the CPU and GTT domain operations are isolated to their own functions, the previously general-purpose set_domain function is now used only to set GPU domains. It also has no failure cases, which is important as this eliminates any possible interruption of the computation of new object domains and subsequent emmission of the flushing instructions into the ring. Signed-off-by: Keith Packard Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 39 ++++++++++++--------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f7e9f2c2934c..50c75327d567 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -33,10 +33,10 @@ #define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) -static int -i915_gem_object_set_domain(struct drm_gem_object *obj, - uint32_t read_domains, - uint32_t write_domain); +static void +i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, + uint32_t read_domains, + uint32_t write_domain); static void i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj); static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj); static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj); @@ -1477,10 +1477,10 @@ i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) * MI_FLUSH * drm_agp_chipset_flush */ -static int -i915_gem_object_set_domain(struct drm_gem_object *obj, - uint32_t read_domains, - uint32_t write_domain) +static void +i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, + uint32_t read_domains, + uint32_t write_domain) { struct drm_device *dev = obj->dev; struct drm_i915_gem_object *obj_priv = obj->driver_private; @@ -1540,7 +1540,6 @@ i915_gem_object_set_domain(struct drm_gem_object *obj, obj->read_domains, obj->write_domain, dev->invalidate_domains, dev->flush_domains); #endif - return 0; } /** @@ -2043,24 +2042,10 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; - /* make sure all previous memory operations have passed */ - ret = i915_gem_object_set_domain(obj, - obj->pending_read_domains, - obj->pending_write_domain); - if (ret) { - /* As we've partially updated domains on our buffers, - * we have to emit the flush we've accumulated - * before exiting, or we'll have broken the - * active/flushing/inactive invariants. - * - * We'll potentially have some things marked as - * being in write domains that they actually aren't, - * but that should be merely a minor performance loss. - */ - flush_domains = i915_gem_dev_set_domain(dev); - (void)i915_add_request(dev, flush_domains); - goto err; - } + /* Compute new gpu domains and update invalidate/flushing */ + i915_gem_object_set_to_gpu_domain(obj, + obj->pending_read_domains, + obj->pending_write_domain); } i915_verify_inactive(dev, __FILE__, __LINE__); -- cgit v1.2.1 From 646f0f6e43bf6628b1f0f8ca6c0227ce72e8ef3c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 20 Nov 2008 23:23:03 -0800 Subject: drm/i915: Move the execbuffer domain computations together This eliminates the dev_set_domain function and just in-lines it where its used, with the goal of moving the manipulation and use of invalidate_domains and flush_domains closer together. This also avoids calling add_request unless some domain has been flushed. Signed-off-by: Keith Packard Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 65 +++++++++++++---------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 50c75327d567..c171a2d93bb6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1646,38 +1646,6 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj, return 0; } -/** - * Once all of the objects have been set in the proper domain, - * perform the necessary flush and invalidate operations. - * - * Returns the write domains flushed, for use in flush tracking. - */ -static uint32_t -i915_gem_dev_set_domain(struct drm_device *dev) -{ - uint32_t flush_domains = dev->flush_domains; - - /* - * Now that all the buffers are synced to the proper domains, - * flush and invalidate the collected domains - */ - if (dev->invalidate_domains | dev->flush_domains) { -#if WATCH_EXEC - DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n", - __func__, - dev->invalidate_domains, - dev->flush_domains); -#endif - i915_gem_flush(dev, - dev->invalidate_domains, - dev->flush_domains); - dev->invalidate_domains = 0; - dev->flush_domains = 0; - } - - return flush_domains; -} - /** * Pin an object to the GTT and evaluate the relocations landing in it. */ @@ -2002,13 +1970,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, return -EBUSY; } - /* Zero the gloabl flush/invalidate flags. These - * will be modified as each object is bound to the - * gtt - */ - dev->invalidate_domains = 0; - dev->flush_domains = 0; - /* Look up object handles and perform the relocations */ for (i = 0; i < args->buffer_count; i++) { object_list[i] = drm_gem_object_lookup(dev, file_priv, @@ -2039,10 +2000,17 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, i915_verify_inactive(dev, __FILE__, __LINE__); + /* Zero the global flush/invalidate flags. These + * will be modified as new domains are computed + * for each object + */ + dev->invalidate_domains = 0; + dev->flush_domains = 0; + for (i = 0; i < args->buffer_count; i++) { struct drm_gem_object *obj = object_list[i]; - /* Compute new gpu domains and update invalidate/flushing */ + /* Compute new gpu domains and update invalidate/flush */ i915_gem_object_set_to_gpu_domain(obj, obj->pending_read_domains, obj->pending_write_domain); @@ -2050,8 +2018,19 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, i915_verify_inactive(dev, __FILE__, __LINE__); - /* Flush/invalidate caches and chipset buffer */ - flush_domains = i915_gem_dev_set_domain(dev); + if (dev->invalidate_domains | dev->flush_domains) { +#if WATCH_EXEC + DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n", + __func__, + dev->invalidate_domains, + dev->flush_domains); +#endif + i915_gem_flush(dev, + dev->invalidate_domains, + dev->flush_domains); + if (dev->flush_domains) + (void)i915_add_request(dev, dev->flush_domains); + } i915_verify_inactive(dev, __FILE__, __LINE__); @@ -2071,8 +2050,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, ~0); #endif - (void)i915_add_request(dev, flush_domains); - /* Exec the batchbuffer */ ret = i915_dispatch_gem_execbuffer(dev, args, exec_offset); if (ret) { -- cgit v1.2.1 From ac94a962b24a88ea5d00f4697550d9982f300751 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 20 Nov 2008 23:30:27 -0800 Subject: drm/i915: Retry execbuffer pinning after clearing the GTT If we fail to pin all of the buffers in an execbuffer request, go through and clear the GTT and try again to see if its just a matter of fragmentation Signed-off-by: Keith Packard Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 57 +++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c171a2d93bb6..174c0c3ba0b0 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1087,6 +1087,19 @@ i915_gem_evict_something(struct drm_device *dev) return ret; } +static int +i915_gem_evict_everything(struct drm_device *dev) +{ + int ret; + + for (;;) { + ret = i915_gem_evict_something(dev); + if (ret != 0) + break; + } + return ret; +} + static int i915_gem_object_get_page_list(struct drm_gem_object *obj) { @@ -1173,7 +1186,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment) ret = i915_gem_evict_something(dev); if (ret != 0) { - DRM_ERROR("Failed to evict a buffer %d\n", ret); + if (ret != -ERESTARTSYS) + DRM_ERROR("Failed to evict a buffer %d\n", ret); return ret; } goto search_free; @@ -1922,6 +1936,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, int ret, i, pinned = 0; uint64_t exec_offset; uint32_t seqno, flush_domains; + int pin_tries; #if WATCH_EXEC DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n", @@ -1970,7 +1985,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, return -EBUSY; } - /* Look up object handles and perform the relocations */ + /* Look up object handles */ for (i = 0; i < args->buffer_count; i++) { object_list[i] = drm_gem_object_lookup(dev, file_priv, exec_list[i].handle); @@ -1980,17 +1995,39 @@ i915_gem_execbuffer(struct drm_device *dev, void *data, ret = -EBADF; goto err; } + } - object_list[i]->pending_read_domains = 0; - object_list[i]->pending_write_domain = 0; - ret = i915_gem_object_pin_and_relocate(object_list[i], - file_priv, - &exec_list[i]); - if (ret) { - DRM_ERROR("object bind and relocate failed %d\n", ret); + /* Pin and relocate */ + for (pin_tries = 0; ; pin_tries++) { + ret = 0; + for (i = 0; i < args->buffer_count; i++) { + object_list[i]->pending_read_domains = 0; + object_list[i]->pending_write_domain = 0; + ret = i915_gem_object_pin_and_relocate(object_list[i], + file_priv, + &exec_list[i]); + if (ret) + break; + pinned = i + 1; + } + /* success */ + if (ret == 0) + break; + + /* error other than GTT full, or we've already tried again */ + if (ret != -ENOMEM || pin_tries >= 1) { + DRM_ERROR("Failed to pin buffers %d\n", ret); goto err; } - pinned = i + 1; + + /* unpin all of our buffers */ + for (i = 0; i < pinned; i++) + i915_gem_object_unpin(object_list[i]); + + /* evict everyone we can from the aperture */ + ret = i915_gem_evict_everything(dev); + if (ret) + goto err; } /* Set the pending read domains for the batch buffer to COMMAND */ -- cgit v1.2.1 From 0235439232cb6f8a54f8976aa8330c1c98ebad0b Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 26 Nov 2008 13:58:13 -0800 Subject: drm/i915: Return error in i915_gem_set_to_gtt_domain if we're not in the GTT. It's only for flushing caches appropriately for GTT access, not for actually getting it there. Prevents potential smashing of cpu read/write domains on unbound objects. Signed-off-by: Eric Anholt Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 174c0c3ba0b0..3fde82be014f 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -435,6 +435,13 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, #endif if (read_domains & I915_GEM_DOMAIN_GTT) { ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0); + + /* Silently promote "you're not bound, there was nothing to do" + * to success, since the client was just asking us to + * make sure everything was done. + */ + if (ret == -EINVAL) + ret = 0; } else { ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0); } @@ -1304,6 +1311,10 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) struct drm_i915_gem_object *obj_priv = obj->driver_private; int ret; + /* Not valid to be called on unbound objects. */ + if (obj_priv->gtt_space == NULL) + return -EINVAL; + i915_gem_object_flush_gpu_write_domain(obj); /* Wait on any GPU rendering and flushing to occur. */ ret = i915_gem_object_wait_rendering(obj); -- cgit v1.2.1 From fae7043c65d65f3e5b34bac4431c6ccc49190c41 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 9 Dec 2008 15:30:50 +1000 Subject: drm/radeon: don't actually enable the IRQ regs until irq is enabled vblank can try and enable the IRQ registers before we've set the interrupt handler up. Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_drv.h | 1 - drivers/gpu/drm/radeon/radeon_irq.c | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index 7a183789be97..3bbb871b25d5 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.h +++ b/drivers/gpu/drm/radeon/radeon_drv.h @@ -299,7 +299,6 @@ typedef struct drm_radeon_private { atomic_t swi_emitted; int vblank_crtc; uint32_t irq_enable_reg; - int irq_enabled; uint32_t r500_disp_irq_reg; struct radeon_surface surfaces[RADEON_MAX_SURFACES]; diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c index 97c0599fdb1e..99be11418ac2 100644 --- a/drivers/gpu/drm/radeon/radeon_irq.c +++ b/drivers/gpu/drm/radeon/radeon_irq.c @@ -44,7 +44,8 @@ void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state) else dev_priv->irq_enable_reg &= ~mask; - RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); + if (!dev->irq_enabled) + 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) @@ -56,7 +57,8 @@ static void r500_vbl_irq_set_state(struct drm_device *dev, u32 mask, int state) else dev_priv->r500_disp_irq_reg &= ~mask; - RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg); + if (!dev->irq_enabled) + RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg); } int radeon_enable_vblank(struct drm_device *dev, int crtc) @@ -355,8 +357,6 @@ void radeon_driver_irq_uninstall(struct drm_device * dev) if (!dev_priv) return; - dev_priv->irq_enabled = 0; - if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) RADEON_WRITE(R500_DxMODE_INT_MASK, 0); /* Disable *all* interrupts */ -- cgit v1.2.1 From 15c35334c9b7a11f66ddf7fea9367884e1f5414f Mon Sep 17 00:00:00 2001 From: Owain Ainsworth Date: Sat, 6 Dec 2008 20:42:20 -0800 Subject: drm/i915: Don't return error in evict_everything when we get to the end. Returning -ENOMEM errored all the way out of execbuf, so the rendering never occurred. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 3fde82be014f..ad672d854828 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1104,6 +1104,8 @@ i915_gem_evict_everything(struct drm_device *dev) if (ret != 0) break; } + if (ret == -ENOMEM) + return 0; return ret; } -- cgit v1.2.1 From b60678a75d44fa9d5969f79781bd856ad5858609 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 8 Dec 2008 11:12:28 -0800 Subject: drm/i915: Disable the GM965 MSI errata workaround. Since applying the fix suggested by the errata (disabling MSI), we've had issues with interrupts being stuck on despite IIR being 0 on GM965 hardware. Most reporters of the issue have confirmed that turning MSI back on fixes things, and given the difficulties experienced in getting reliable MSI working on Linux, it's believable that the errata was about software issues and not actual hardware issues. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ba89b42f790a..553dd4bc3075 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -847,9 +847,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) * and the registers being closely associated. * * According to chipset errata, on the 965GM, MSI interrupts may - * be lost or delayed + * be lost or delayed, but we use them anyways to avoid + * stuck interrupts on some machines. */ - if (!IS_I945G(dev) && !IS_I945GM(dev) && !IS_I965GM(dev)) + if (!IS_I945G(dev) && !IS_I945GM(dev)) pci_enable_msi(dev->pdev); intel_opregion_init(dev); -- cgit v1.2.1 From c4de0a5d671e3af38dc3496538e391782aecc3a5 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Sun, 14 Dec 2008 19:05:04 -0800 Subject: drm/i915: Don't return busy for buffers left on the flushing list. These buffers don't have active rendering still occurring to them, they just need either a flush to be emitted or a retire_requests to occur so that we notice they're done. Return unbusy so that one of the two occurs. The two expected consumers of this interface (OpenGL and libdrm_intel BO cache) both want this behavior. Signed-off-by: Eric Anholt Acked-by: Keith Packard Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ad672d854828..24fe8c10b4b2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2309,7 +2309,14 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, } obj_priv = obj->driver_private; - args->busy = obj_priv->active; + /* Don't count being on the flushing list against the object being + * done. Otherwise, a buffer left on the flushing list but not getting + * flushed (because nobody's flushing that domain) won't ever return + * unbusy and get reused by libdrm's bo cache. The other expected + * consumer of this interface, OpenGL's occlusion queries, also specs + * that the objects get unbusy "eventually" without any interference. + */ + args->busy = obj_priv->active && obj_priv->last_rendering_seqno != 0; drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); -- cgit v1.2.1 From ac5c4e76180a74c7f922f6fa71ace0cef45fa433 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 19 Dec 2008 15:38:34 +1000 Subject: drm/i915: GEM on PAE has problems - disable it for now. On PAE systems, GEM allocates pages using shmem, and passes these pages to be bound into AGP, however the AGP interfaces + the x86 set_memory interfaces all take unsigned long not dma_addr_t. The initial fix for this was a mess, so we need to do this correctly for 2.6.29. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 10 +++++++++- drivers/gpu/drm/i915/i915_drv.h | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 553dd4bc3075..afa8a12cd009 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -717,7 +717,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = dev->pci_device; break; case I915_PARAM_HAS_GEM: - value = 1; + value = dev_priv->has_gem; break; default: DRM_ERROR("Unknown parameter %d\n", param->param); @@ -830,6 +830,14 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev_priv->regs = ioremap(base, size); +#ifdef CONFIG_HIGHMEM64G + /* don't enable GEM on PAE - needs agp + set_memory_* interface fixes */ + dev_priv->has_gem = 0; +#else + /* enable GEM by default */ + dev_priv->has_gem = 1; +#endif + i915_gem_load(dev); /* Init HWS */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index adc972cc6bfc..b3cc4731aa7c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -106,6 +106,8 @@ struct intel_opregion { typedef struct drm_i915_private { struct drm_device *dev; + int has_gem; + void __iomem *regs; drm_local_map_t *sarea; -- cgit v1.2.1 From 077ebed54fe66612f58b076628a72eca2be8df90 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 22 Dec 2008 17:11:02 +1000 Subject: drm/radeon: fix correctness of irq_enabled check for radeon. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This check was introduced with the logic the wrong way around. Fixes regression: http://bugzilla.kernel.org/show_bug.cgi?id=12216 Tested-by: François Valenduc Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c index 99be11418ac2..8289e16419a8 100644 --- a/drivers/gpu/drm/radeon/radeon_irq.c +++ b/drivers/gpu/drm/radeon/radeon_irq.c @@ -44,7 +44,7 @@ void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state) else dev_priv->irq_enable_reg &= ~mask; - if (!dev->irq_enabled) + if (dev->irq_enabled) RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); } @@ -57,7 +57,7 @@ static void r500_vbl_irq_set_state(struct drm_device *dev, u32 mask, int state) else dev_priv->r500_disp_irq_reg &= ~mask; - if (!dev->irq_enabled) + if (dev->irq_enabled) RADEON_WRITE(R500_DxMODE_INT_MASK, dev_priv->r500_disp_irq_reg); } -- cgit v1.2.1