diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2012-04-24 17:59:50 -0400 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-05-03 05:18:19 -0400 |
commit | 38bde18045afe854f389961846d64c4b3f86105f (patch) | |
tree | a3071211e984632f02976730fbd8ff6aad0785c9 /drivers/gpu/drm/i915/i915_irq.c | |
parent | 55b39755ea3a767da85e6725a783da90c574a274 (diff) |
drm/i915: Handle PendingFlip on gen3 robustly
We appear to allow too many pending pageflips as evidenced by an
apparent pin-leak. So borrow the pageflip completion logic from i8xx for
handling PendingFlip in a robust manner.
v2: Address Jesse's reminders about the nuances of gen3 IRQ handling.
References: https://bugzilla.kernel.org/show_bug.cgi?id=41882
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 74 |
1 files changed, 43 insertions, 31 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 24d23a21c538..51f872084efa 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -2350,16 +2350,31 @@ static void i915_irq_preinstall(struct drm_device * dev) | |||
2350 | static int i915_irq_postinstall(struct drm_device *dev) | 2350 | static int i915_irq_postinstall(struct drm_device *dev) |
2351 | { | 2351 | { |
2352 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 2352 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
2353 | u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; | 2353 | u32 enable_mask; |
2354 | 2354 | ||
2355 | dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; | 2355 | dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; |
2356 | 2356 | ||
2357 | /* Unmask the interrupts that we always want on. */ | ||
2358 | dev_priv->irq_mask = ~I915_INTERRUPT_ENABLE_FIX; | ||
2359 | |||
2360 | dev_priv->pipestat[0] = 0; | 2357 | dev_priv->pipestat[0] = 0; |
2361 | dev_priv->pipestat[1] = 0; | 2358 | dev_priv->pipestat[1] = 0; |
2362 | 2359 | ||
2360 | I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); | ||
2361 | |||
2362 | /* Unmask the interrupts that we always want on. */ | ||
2363 | dev_priv->irq_mask = | ||
2364 | ~(I915_ASLE_INTERRUPT | | ||
2365 | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | | ||
2366 | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | | ||
2367 | I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | | ||
2368 | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | | ||
2369 | I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); | ||
2370 | |||
2371 | enable_mask = | ||
2372 | I915_ASLE_INTERRUPT | | ||
2373 | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | | ||
2374 | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | | ||
2375 | I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT | | ||
2376 | I915_USER_INTERRUPT; | ||
2377 | |||
2363 | if (I915_HAS_HOTPLUG(dev)) { | 2378 | if (I915_HAS_HOTPLUG(dev)) { |
2364 | /* Enable in IER... */ | 2379 | /* Enable in IER... */ |
2365 | enable_mask |= I915_DISPLAY_PORT_INTERRUPT; | 2380 | enable_mask |= I915_DISPLAY_PORT_INTERRUPT; |
@@ -2367,12 +2382,6 @@ static int i915_irq_postinstall(struct drm_device *dev) | |||
2367 | dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT; | 2382 | dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT; |
2368 | } | 2383 | } |
2369 | 2384 | ||
2370 | /* | ||
2371 | * Enable some error detection, note the instruction error mask | ||
2372 | * bit is reserved, so we leave it masked. | ||
2373 | */ | ||
2374 | I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); | ||
2375 | |||
2376 | I915_WRITE(IMR, dev_priv->irq_mask); | 2385 | I915_WRITE(IMR, dev_priv->irq_mask); |
2377 | I915_WRITE(IER, enable_mask); | 2386 | I915_WRITE(IER, enable_mask); |
2378 | POSTING_READ(IER); | 2387 | POSTING_READ(IER); |
@@ -2412,15 +2421,21 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) | |||
2412 | struct drm_i915_master_private *master_priv; | 2421 | struct drm_i915_master_private *master_priv; |
2413 | u32 iir, new_iir, pipe_stats[I915_MAX_PIPES]; | 2422 | u32 iir, new_iir, pipe_stats[I915_MAX_PIPES]; |
2414 | unsigned long irqflags; | 2423 | unsigned long irqflags; |
2415 | int ret = IRQ_NONE, pipe; | 2424 | u32 flip_mask = |
2425 | I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | | ||
2426 | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; | ||
2427 | u32 flip[2] = { | ||
2428 | I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT, | ||
2429 | I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | ||
2430 | }; | ||
2431 | int pipe, ret = IRQ_NONE; | ||
2416 | 2432 | ||
2417 | atomic_inc(&dev_priv->irq_received); | 2433 | atomic_inc(&dev_priv->irq_received); |
2418 | 2434 | ||
2419 | iir = I915_READ(IIR); | 2435 | iir = I915_READ(IIR); |
2420 | 2436 | do { | |
2421 | for (;;) { | 2437 | bool irq_received = (iir & ~flip_mask) != 0; |
2422 | bool blc_event = false; | 2438 | bool blc_event = false; |
2423 | int irq_received = iir != 0; | ||
2424 | 2439 | ||
2425 | /* Can't rely on pipestat interrupt bit in iir as it might | 2440 | /* Can't rely on pipestat interrupt bit in iir as it might |
2426 | * have been cleared after the pipestat interrupt was received. | 2441 | * have been cleared after the pipestat interrupt was received. |
@@ -2435,15 +2450,13 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) | |||
2435 | int reg = PIPESTAT(pipe); | 2450 | int reg = PIPESTAT(pipe); |
2436 | pipe_stats[pipe] = I915_READ(reg); | 2451 | pipe_stats[pipe] = I915_READ(reg); |
2437 | 2452 | ||
2438 | /* | 2453 | /* Clear the PIPE*STAT regs before the IIR */ |
2439 | * Clear the PIPE*STAT regs before the IIR | ||
2440 | */ | ||
2441 | if (pipe_stats[pipe] & 0x8000ffff) { | 2454 | if (pipe_stats[pipe] & 0x8000ffff) { |
2442 | if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) | 2455 | if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) |
2443 | DRM_DEBUG_DRIVER("pipe %c underrun\n", | 2456 | DRM_DEBUG_DRIVER("pipe %c underrun\n", |
2444 | pipe_name(pipe)); | 2457 | pipe_name(pipe)); |
2445 | I915_WRITE(reg, pipe_stats[pipe]); | 2458 | I915_WRITE(reg, pipe_stats[pipe]); |
2446 | irq_received = 1; | 2459 | irq_received = true; |
2447 | } | 2460 | } |
2448 | } | 2461 | } |
2449 | spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); | 2462 | spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); |
@@ -2451,8 +2464,6 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) | |||
2451 | if (!irq_received) | 2464 | if (!irq_received) |
2452 | break; | 2465 | break; |
2453 | 2466 | ||
2454 | ret = IRQ_HANDLED; | ||
2455 | |||
2456 | /* Consume port. Then clear IIR or we'll miss events */ | 2467 | /* Consume port. Then clear IIR or we'll miss events */ |
2457 | if ((I915_HAS_HOTPLUG(dev)) && | 2468 | if ((I915_HAS_HOTPLUG(dev)) && |
2458 | (iir & I915_DISPLAY_PORT_INTERRUPT)) { | 2469 | (iir & I915_DISPLAY_PORT_INTERRUPT)) { |
@@ -2465,26 +2476,26 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) | |||
2465 | &dev_priv->hotplug_work); | 2476 | &dev_priv->hotplug_work); |
2466 | 2477 | ||
2467 | I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); | 2478 | I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); |
2468 | I915_READ(PORT_HOTPLUG_STAT); | 2479 | POSTING_READ(PORT_HOTPLUG_STAT); |
2469 | } | 2480 | } |
2470 | 2481 | ||
2471 | I915_WRITE(IIR, iir); | 2482 | I915_WRITE(IIR, iir & ~flip_mask); |
2472 | new_iir = I915_READ(IIR); /* Flush posted writes */ | 2483 | new_iir = I915_READ(IIR); /* Flush posted writes */ |
2473 | 2484 | ||
2474 | if (iir & I915_USER_INTERRUPT) | 2485 | if (iir & I915_USER_INTERRUPT) |
2475 | notify_ring(dev, &dev_priv->ring[RCS]); | 2486 | notify_ring(dev, &dev_priv->ring[RCS]); |
2476 | 2487 | ||
2477 | if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) | ||
2478 | intel_prepare_page_flip(dev, 0); | ||
2479 | |||
2480 | if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) | ||
2481 | intel_prepare_page_flip(dev, 1); | ||
2482 | |||
2483 | for_each_pipe(pipe) { | 2488 | for_each_pipe(pipe) { |
2489 | int plane = pipe; | ||
2490 | if (IS_MOBILE(dev)) | ||
2491 | plane = !plane; | ||
2484 | if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS && | 2492 | if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS && |
2485 | drm_handle_vblank(dev, pipe)) { | 2493 | drm_handle_vblank(dev, pipe)) { |
2486 | i915_pageflip_stall_check(dev, pipe); | 2494 | if (iir & flip[plane]) { |
2487 | intel_finish_page_flip(dev, pipe); | 2495 | intel_prepare_page_flip(dev, plane); |
2496 | intel_finish_page_flip(dev, pipe); | ||
2497 | flip_mask &= ~flip[plane]; | ||
2498 | } | ||
2488 | } | 2499 | } |
2489 | 2500 | ||
2490 | if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) | 2501 | if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) |
@@ -2509,8 +2520,9 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) | |||
2509 | * trigger the 99% of 100,000 interrupts test for disabling | 2520 | * trigger the 99% of 100,000 interrupts test for disabling |
2510 | * stray interrupts. | 2521 | * stray interrupts. |
2511 | */ | 2522 | */ |
2523 | ret = IRQ_HANDLED; | ||
2512 | iir = new_iir; | 2524 | iir = new_iir; |
2513 | } | 2525 | } while (iir & ~flip_mask); |
2514 | 2526 | ||
2515 | if (dev->primary->master) { | 2527 | if (dev->primary->master) { |
2516 | master_priv = dev->primary->master->driver_priv; | 2528 | master_priv = dev->primary->master->driver_priv; |