From acd15b6cc20f85bcef9e08b6ed4f142c34791c32 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 30 Nov 2012 11:24:50 +0100 Subject: drm/i915: optimize ilk/snb irq handler We only need to read/write the south interrupt register if the corresponding bit is set in the north master interrupt register. Noticed while reading our interrupt handling code. Same optimization has already been applied on ivb in commit 0e43406bcc1868a316eea6012a0a09d992c53521 Author: Chris Wilson Date: Wed May 9 21:45:44 2012 +0100 drm/i915: Simplify interrupt processing for IvyBridge We can take advantage that the PCH_IIR is a subordinate register to reduce one of the required IIR reads, and that we only need to clear interrupts handled to reduce the writes. And by simply tidying the code we can reduce the line count and hopefully make it more readable. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6cd3dc94b472..26753ee6571d 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -758,7 +758,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = IRQ_NONE; - u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir; + u32 de_iir, gt_iir, de_ier, pm_iir; atomic_inc(&dev_priv->irq_received); @@ -769,11 +769,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); - pch_iir = I915_READ(SDEIIR); pm_iir = I915_READ(GEN6_PMIIR); - if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 && - (!IS_GEN6(dev) || pm_iir == 0)) + if (de_iir == 0 && gt_iir == 0 && (!IS_GEN6(dev) || pm_iir == 0)) goto done; ret = IRQ_HANDLED; @@ -804,10 +802,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) /* check event from PCH */ if (de_iir & DE_PCH_EVENT) { + u32 pch_iir = I915_READ(SDEIIR); + if (HAS_PCH_CPT(dev)) cpt_irq_handler(dev, pch_iir); else ibx_irq_handler(dev, pch_iir); + + /* should clear PCH hotplug event before clear CPU irq */ + I915_WRITE(SDEIIR, pch_iir); } if (IS_GEN5(dev) && de_iir & DE_PCU_EVENT) @@ -816,8 +819,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); - /* should clear PCH hotplug event before clear CPU irq */ - I915_WRITE(SDEIIR, pch_iir); I915_WRITE(GTIIR, gt_iir); I915_WRITE(DEIIR, de_iir); I915_WRITE(GEN6_PMIIR, pm_iir); -- cgit v1.2.3 From 960e3564bfa464f92549383d41659f2aaeee1420 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 15 Nov 2012 11:32:23 +0000 Subject: drm/i915: Support readback of stolen objects upon error Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 26753ee6571d..7a0ddee9751a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -929,6 +929,14 @@ i915_error_object_create(struct drm_i915_private *dev_priv, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); + } else if (src->stolen) { + unsigned long offset; + + offset = dev_priv->mm.stolen_base; + offset += src->stolen->start; + offset += i << PAGE_SHIFT; + + memcpy_fromio(d, (void *)offset, PAGE_SIZE); } else { struct page *page; void *s; -- cgit v1.2.3 From 1a240d4de2ccf40de5796a4d1dbb3a0236051fc9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 29 Nov 2012 22:18:51 +0100 Subject: drm/i915: fixup sparse warnings - __iomem where there is none (I love how we mix these things up). - Use gfp_t instead of an other plain type. - Unconfuse one place about enum pipe vs enum transcoder - for the pch transcoder we actually use the pipe enum. Fixup the other cases where we assign the pipe to the cpu transcoder with explicit casts. - Declare the mch_lock properly in a header. There is still a decent mess in intel_bios.c about __iomem, but heck, this is x86 and we're allowed to do that. Makes-sparse-happy: Chris Wilson [danvet: Use a space after the cast consistently and fix up the newly-added cast in i915_irq.c to properly use __iomem.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 ++-- drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 5 +---- drivers/gpu/drm/i915/intel_ddi.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 9 +++++---- 6 files changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 58e667687741..ce60506b574b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -546,11 +546,11 @@ static int i915_hws_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_ring_buffer *ring; - const volatile u32 __iomem *hws; + const u32 *hws; int i; ring = &dev_priv->ring[(uintptr_t)node->info_ent->data]; - hws = (volatile u32 __iomem *)ring->status_page.page_addr; + hws = ring->status_page.page_addr; if (hws == NULL) return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3473298a137f..bb0eb0f8f6b3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -577,6 +577,9 @@ struct intel_gen6_power_mgmt { struct mutex hw_lock; }; +/* defined intel_pm.c */ +extern spinlock_t mchdev_lock; + struct intel_ilk_power_mgmt { u8 cur_delay; u8 min_delay; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b68bc02745db..cb941448c2bc 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3705,7 +3705,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, { struct drm_i915_gem_object *obj; struct address_space *mapping; - u32 mask; + gfp_t mask; obj = i915_gem_object_alloc(dev); if (obj == NULL) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7a0ddee9751a..68e7cafa13de 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -300,9 +300,6 @@ static void i915_hotplug_work_func(struct work_struct *work) drm_helper_hpd_irq_event(dev); } -/* defined intel_pm.c */ -extern spinlock_t mchdev_lock; - static void ironlake_handle_rps_change(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -936,7 +933,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, offset += src->stolen->start; offset += i << PAGE_SHIFT; - memcpy_fromio(d, (void *)offset, PAGE_SIZE); + memcpy_fromio(d, (void __iomem *) offset, PAGE_SIZE); } else { struct page *page; void *s; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index ad936c68e4cb..f02b3feff504 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1044,7 +1044,7 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) if (port == PORT_A) cpu_transcoder = TRANSCODER_EDP; else - cpu_transcoder = pipe; + cpu_transcoder = (enum transcoder) pipe; tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder)); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d303f2a90e70..093a163d4b1c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1671,7 +1671,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv, BUG_ON(dev_priv->info->gen < 5); /* FDI must be feeding us bits for PCH ports */ - assert_fdi_tx_enabled(dev_priv, cpu_transcoder); + assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder); assert_fdi_rx_enabled(dev_priv, TRANSCODER_A); /* Workaround: set timing override bit. */ @@ -1759,7 +1759,7 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, { enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); - enum transcoder pch_transcoder; + enum pipe pch_transcoder; int reg; u32 val; @@ -1779,7 +1779,8 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, if (pch_port) { /* if driving the PCH, we need FDI enabled */ assert_fdi_rx_pll_enabled(dev_priv, pch_transcoder); - assert_fdi_tx_pll_enabled(dev_priv, cpu_transcoder); + assert_fdi_tx_pll_enabled(dev_priv, + (enum pipe) cpu_transcoder); } /* FIXME: assert CPU port conditions for SNB+ */ } @@ -3598,7 +3599,7 @@ static void haswell_crtc_off(struct drm_crtc *crtc) /* Stop saying we're using TRANSCODER_EDP because some other CRTC might * start using it. */ - intel_crtc->cpu_transcoder = intel_crtc->pipe; + intel_crtc->cpu_transcoder = (enum transcoder) intel_crtc->pipe; intel_ddi_put_crtc_pll(crtc); } -- cgit v1.2.3 From 4a06e201dae98ed893bae677b7b05bff29fcbab0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:40 +0100 Subject: drm/i915: haswell has the same irq handlers as ivb No need to have the exaxt same code twice. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 68e7cafa13de..2aa23e7fc2ab 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2711,7 +2711,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = valleyview_irq_uninstall; dev->driver->enable_vblank = valleyview_enable_vblank; dev->driver->disable_vblank = valleyview_disable_vblank; - } else if (IS_IVYBRIDGE(dev)) { + } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { /* Share pre & uninstall handlers with ILK/SNB */ dev->driver->irq_handler = ivybridge_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; @@ -2719,14 +2719,6 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ivybridge_enable_vblank; dev->driver->disable_vblank = ivybridge_disable_vblank; - } else if (IS_HASWELL(dev)) { - /* Share interrupts handling with IVB */ - dev->driver->irq_handler = ivybridge_irq_handler; - dev->driver->irq_preinstall = ironlake_irq_preinstall; - dev->driver->irq_postinstall = ivybridge_irq_postinstall; - dev->driver->irq_uninstall = ironlake_irq_uninstall; - dev->driver->enable_vblank = ivybridge_enable_vblank; - dev->driver->disable_vblank = ivybridge_disable_vblank; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; -- cgit v1.2.3 From d83779a9cb9374977c1c05364b4d7dfe9ec68ef3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:41 +0100 Subject: drm/i915: don't handle PIPE_LEGACY_BLC_EVENT_STATUS on vlv This is for legacy legacy stuff, and checking with the leftover pipe from the previous loop is propably not what we want. Since pipe == 2 after the loop ... Then we only assing a variable and do nothing with it. Cc: Jesse Barnes Reviewed-by: Jesse Barnes Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2aa23e7fc2ab..935b6b7bae73 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -530,7 +530,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) unsigned long irqflags; int pipe; u32 pipe_stats[I915_MAX_PIPES]; - bool blc_event; atomic_inc(&dev_priv->irq_received); @@ -587,9 +586,6 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) I915_READ(PORT_HOTPLUG_STAT); } - if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) - blc_event = true; - if (pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); -- cgit v1.2.3 From 61bac78e03d4385e225cb2837e33974feda489c2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 21:03:21 +0100 Subject: drm/i915: setup the hangcheck timer early ... together with all the other irq related resources in intel_irq_init. I've managed to oops in the notify_ring function on my ilk, presumably because of the powerctx setup call to i915_gpu_idle. Note that this is only a problem with the reorder irq setup sequence for irq-driver gmbus/dp aux. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 --- drivers/gpu/drm/i915/i915_irq.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2635ee6a34d4..901f8fc3ed88 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1611,9 +1611,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_opregion_init(dev); acpi_video_register(); - setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, - (unsigned long) dev); - if (IS_GEN5(dev)) intel_gpu_ips_init(dev_priv); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 935b6b7bae73..568820bf4587 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2687,6 +2687,9 @@ void intel_irq_init(struct drm_device *dev) INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work); INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); + setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, + (unsigned long) dev); + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { -- cgit v1.2.3 From 52d7ecedac3f96fb562cb482c139015372728638 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 21:03:22 +0100 Subject: drm/i915: reorder setup sequence to have irqs for output setup Otherwise the new&shiny irq-driven gmbus and dp aux code won't work that well. Noticed since the dp aux code doesn't have an automatic fallback with a timeout (since the hw provides for that already). v2: Simple move drm_irq_install before intel_modeset_gem_init, as suggested by Ben Widawsky. v3: Now that interrupts are enabled before all connectors are fully set up, we might fall over serving a HPD interrupt while things are still being set up. Instead of jumping through massive hoops and complicating the code with a separate hpd irq enable step, simply block out the hotplug work item from doing anything until things are in place. v4: Actually, we can enable hotplug processing only after the fbdev is fully set up, since we call down into the fbdev from the hotplug work functions. So stick the hpd enabling right next to the poll helper initialization. v5: We need to enable irqs before intel_modeset_init, since that function sets up the outputs. v6: Fixup cleanup sequence, too. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 23 ++++++++++++++--------- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_irq.c | 4 ++++ 3 files changed, 19 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 901f8fc3ed88..14d679ab52b7 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1294,19 +1294,21 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_vga_switcheroo; + ret = drm_irq_install(dev); + if (ret) + goto cleanup_gem_stolen; + + /* Important: The output setup functions called by modeset_init need + * working irqs for e.g. gmbus and dp aux transfers. */ intel_modeset_init(dev); ret = i915_gem_init(dev); if (ret) - goto cleanup_gem_stolen; - - intel_modeset_gem_init(dev); + goto cleanup_irq; INIT_WORK(&dev_priv->console_resume_work, intel_console_resume); - ret = drm_irq_install(dev); - if (ret) - goto cleanup_gem; + intel_modeset_gem_init(dev); /* Always safe in the mode setting case. */ /* FIXME: do pre/post-mode set stuff in core KMS code */ @@ -1314,7 +1316,10 @@ static int i915_load_modeset_init(struct drm_device *dev) ret = intel_fbdev_init(dev); if (ret) - goto cleanup_irq; + goto cleanup_gem; + + /* Only enable hotplug handling once the fbdev is fully set up. */ + dev_priv->enable_hotplug_processing = true; drm_kms_helper_poll_init(dev); @@ -1323,13 +1328,13 @@ static int i915_load_modeset_init(struct drm_device *dev) return 0; -cleanup_irq: - drm_irq_uninstall(dev); cleanup_gem: mutex_lock(&dev->struct_mutex); i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); +cleanup_irq: + drm_irq_uninstall(dev); cleanup_gem_stolen: i915_gem_cleanup_stolen(dev); cleanup_vga_switcheroo: diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bb0eb0f8f6b3..e7411f363b3d 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -673,6 +673,7 @@ typedef struct drm_i915_private { u32 hotplug_supported_mask; struct work_struct hotplug_work; + bool enable_hotplug_processing; int num_pipe; int num_pch_pll; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 568820bf4587..02a13ab148df 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -287,6 +287,10 @@ static void i915_hotplug_work_func(struct work_struct *work) struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder; + /* HPD irq before everything is fully set up. */ + if (!dev_priv->enable_hotplug_processing) + return; + mutex_lock(&mode_config->mutex); DRM_DEBUG_KMS("running encoder hotplug functions\n"); -- cgit v1.2.3 From 515ac2bb95f609bc4a0d2ad5f7011b3264b2bb21 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:44 +0100 Subject: drm/i915: wire up gmbus irq handler Only enables the interrupt and puts a irq handler into place, doesn't do anything yet. Unfortunately there's no gmbus interrupt support for gen2/3 (safe for pnv, but there the irq is marked as "Test mode"). v2: Wire up the irq handler for vlv and gen4 properly. v3: i915_enable_pipestat expects the mask bit, not the status bits ... and for added hilarity those are rather inconsistently named. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 02a13ab148df..956b95f1f113 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -525,6 +525,11 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, queue_work(dev_priv->wq, &dev_priv->rps.work); } +static void gmbus_irq_handler(struct drm_device *dev) +{ + DRM_DEBUG_DRIVER("GMBUS interrupt\n"); +} + static irqreturn_t valleyview_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -590,6 +595,9 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) I915_READ(PORT_HOTPLUG_STAT); } + if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) + gmbus_irq_handler(dev); + if (pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); @@ -616,7 +624,7 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) SDE_AUDIO_POWER_SHIFT); if (pch_iir & SDE_GMBUS) - DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n"); + gmbus_irq_handler(dev); if (pch_iir & SDE_AUDIO_HDCP_MASK) DRM_DEBUG_DRIVER("PCH HDCP audio interrupt\n"); @@ -662,7 +670,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) DRM_DEBUG_DRIVER("AUX channel interrupt\n"); if (pch_iir & SDE_GMBUS_CPT) - DRM_DEBUG_DRIVER("PCH GMBUS interrupt\n"); + gmbus_irq_handler(dev); if (pch_iir & SDE_AUDIO_CP_REQ_CPT) DRM_DEBUG_DRIVER("Audio CP request interrupt\n"); @@ -1880,12 +1888,14 @@ static int ironlake_irq_postinstall(struct drm_device *dev) hotplug_mask = (SDE_CRT_HOTPLUG_CPT | SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT); + SDE_PORTD_HOTPLUG_CPT | + SDE_GMBUS_CPT); } else { hotplug_mask = (SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG | SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG | + SDE_GMBUS | SDE_AUX_MASK); } @@ -1945,7 +1955,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) hotplug_mask = (SDE_CRT_HOTPLUG_CPT | SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT); + SDE_PORTD_HOTPLUG_CPT | + SDE_GMBUS_CPT); dev_priv->pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); @@ -1999,6 +2010,7 @@ static int valleyview_irq_postinstall(struct drm_device *dev) POSTING_READ(VLV_IER); i915_enable_pipestat(dev_priv, 0, pipestat_enable); + i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); i915_enable_pipestat(dev_priv, 1, pipestat_enable); I915_WRITE(VLV_IIR, 0xffffffff); @@ -2483,6 +2495,7 @@ static int i965_irq_postinstall(struct drm_device *dev) dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; + i915_enable_pipestat(dev_priv, 0, PIPE_GMBUS_EVENT_ENABLE); /* * Enable some error detection, note the instruction error mask @@ -2636,6 +2649,9 @@ static irqreturn_t i965_irq_handler(int irq, void *arg) if (blc_event || (iir & I915_ASLE_INTERRUPT)) intel_opregion_asle_intr(dev); + if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) + gmbus_irq_handler(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 -- cgit v1.2.3 From 28c70f162a315bdcfbe0bf940a740ef8bfb918d6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:45 +0100 Subject: drm/i915: use the gmbus irq for waits We need two special things to properly wire this up: - Add another argument to gmbus_wait_hw_status to pass in the correct interrupt bit in gmbus4. - Since we can only get an irq for one of the two events we want, hand-roll the wait_event_timeout code so that we wake up every jiffie and can check for NAKs. This way we also subsume gmbus support for platforms without interrupts (or where those are not yet enabled). The important bit really is to only enable one gmbus interrupt source at the same time - with that piece of lore figured out, this seems to work flawlessly. Ben Widawsky rightfully complained the lack of measurements for the claimed benefits (especially since the first version was actually broken and fell back to bit-banging). Previously reading the 256 byte hdmi EDID takes about 72 ms here. With this patch it's down to 33 ms. Given that transfering the 256 bytes over i2c at wire speed takes 20.5ms alone, the reduction in additional overhead is rather nice. v2: Chris Wilson wondered whether GMBUS4 might contain some set bits when booting up an hence result in some spurious interrupts. Since we clear GMBUS4 after every wait and we do gmbus transfer really early in the setup sequence to detect displays the window is small, but still be paranoid and clear it properly. v3: Clarify the comment that gmbus irq generation can only support one kind of event, why it bothers us and how we work around that limit. Cc: Daniel Kurtz Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 +++ drivers/gpu/drm/i915/i915_irq.c | 4 ++++ drivers/gpu/drm/i915/intel_i2c.c | 45 ++++++++++++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e7411f363b3d..3843383e13cc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -641,6 +641,7 @@ typedef struct drm_i915_private { struct intel_gmbus gmbus[GMBUS_NUM_PORTS]; + /** gmbus_mutex protects against concurrent usage of the single hw gmbus * controller on different i2c buses. */ struct mutex gmbus_mutex; @@ -650,6 +651,8 @@ typedef struct drm_i915_private { */ uint32_t gpio_mmio_base; + wait_queue_head_t gmbus_wait_queue; + struct pci_dev *bridge_dev; struct intel_ring_buffer ring[I915_NUM_RINGS]; uint32_t next_seqno; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 956b95f1f113..917a48de08fa 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -527,7 +527,11 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, static void gmbus_irq_handler(struct drm_device *dev) { + struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; + DRM_DEBUG_DRIVER("GMBUS interrupt\n"); + + wake_up_all(&dev_priv->gmbus_wait_queue); } static irqreturn_t valleyview_irq_handler(int irq, void *arg) diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index a16eecdf1ed1..8b7189253cb4 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -63,6 +63,7 @@ intel_i2c_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; I915_WRITE(dev_priv->gpio_mmio_base + GMBUS0, 0); + I915_WRITE(dev_priv->gpio_mmio_base + GMBUS4, 0); } static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable) @@ -204,20 +205,38 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin) static int gmbus_wait_hw_status(struct drm_i915_private *dev_priv, - u32 gmbus2_status) + u32 gmbus2_status, + u32 gmbus4_irq_en) { - int ret; + int i; int reg_offset = dev_priv->gpio_mmio_base; - u32 gmbus2; + u32 gmbus2 = 0; + DEFINE_WAIT(wait); + + /* Important: The hw handles only the first bit, so set only one! Since + * we also need to check for NAKs besides the hw ready/idle signal, we + * need to wake up periodically and check that ourselves. */ + I915_WRITE(GMBUS4 + reg_offset, gmbus4_irq_en); - ret = wait_for((gmbus2 = I915_READ(GMBUS2 + reg_offset)) & - (GMBUS_SATOER | gmbus2_status), - 50); + for (i = 0; i < msecs_to_jiffies(50) + 1; i++) { + prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + + gmbus2 = I915_READ(GMBUS2 + reg_offset); + if (gmbus2 & (GMBUS_SATOER | gmbus2_status)) + break; + + schedule_timeout(1); + } + finish_wait(&dev_priv->gmbus_wait_queue, &wait); + + I915_WRITE(GMBUS4 + reg_offset, 0); if (gmbus2 & GMBUS_SATOER) return -ENXIO; - - return ret; + if (gmbus2 & gmbus2_status) + return 0; + return -ETIMEDOUT; } static int @@ -238,7 +257,8 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, int ret; u32 val, loop = 0; - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, + GMBUS_HW_RDY_EN); if (ret) return ret; @@ -282,7 +302,8 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) I915_WRITE(GMBUS3 + reg_offset, val); - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY, + GMBUS_HW_RDY_EN); if (ret) return ret; } @@ -367,7 +388,8 @@ gmbus_xfer(struct i2c_adapter *adapter, if (ret == -ENXIO) goto clear_err; - ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE); + ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE, + GMBUS_HW_WAIT_EN); if (ret == -ENXIO) goto clear_err; if (ret) @@ -473,6 +495,7 @@ int intel_setup_gmbus(struct drm_device *dev) dev_priv->gpio_mmio_base = 0; mutex_init(&dev_priv->gmbus_mutex); + init_waitqueue_head(&dev_priv->gmbus_wait_queue); for (i = 0; i < GMBUS_NUM_PORTS; i++) { struct intel_gmbus *bus = &dev_priv->gmbus[i]; -- cgit v1.2.3 From ce99c2569dfcec9662993961db7f433e160c50f3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:47 +0100 Subject: drm/i915: wire up do aux channel done interrupt Doesn't do anything yet than call dp_aux_irq_handler. Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 917a48de08fa..1e245e501e5a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -534,6 +534,11 @@ static void gmbus_irq_handler(struct drm_device *dev) wake_up_all(&dev_priv->gmbus_wait_queue); } +static void dp_aux_irq_handler(struct drm_device *dev) +{ + DRM_DEBUG_DRIVER("AUX channel interrupt\n"); +} + static irqreturn_t valleyview_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -627,6 +632,9 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir) (pch_iir & SDE_AUDIO_POWER_MASK) >> SDE_AUDIO_POWER_SHIFT); + if (pch_iir & SDE_AUX_MASK) + dp_aux_irq_handler(dev); + if (pch_iir & SDE_GMBUS) gmbus_irq_handler(dev); @@ -671,7 +679,7 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir) SDE_AUDIO_POWER_SHIFT_CPT); if (pch_iir & SDE_AUX_MASK_CPT) - DRM_DEBUG_DRIVER("AUX channel interrupt\n"); + dp_aux_irq_handler(dev); if (pch_iir & SDE_GMBUS_CPT) gmbus_irq_handler(dev); @@ -712,6 +720,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) de_iir = I915_READ(DEIIR); if (de_iir) { + if (de_iir & DE_AUX_CHANNEL_A_IVB) + dp_aux_irq_handler(dev); + if (de_iir & DE_GSE_IVB) intel_opregion_gse_intr(dev); @@ -790,6 +801,9 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) else snb_gt_irq_handler(dev, dev_priv, gt_iir); + if (de_iir & DE_AUX_CHANNEL_A) + dp_aux_irq_handler(dev); + if (de_iir & DE_GSE) intel_opregion_gse_intr(dev); @@ -1858,7 +1872,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; /* enable kind of interrupts always enabled */ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | - DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE; + DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | + DE_AUX_CHANNEL_A; u32 render_irqs; u32 hotplug_mask; @@ -1893,7 +1908,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT); + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT); } else { hotplug_mask = (SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG | @@ -1930,7 +1946,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | DE_PCH_EVENT_IVB | DE_PLANEC_FLIP_DONE_IVB | DE_PLANEB_FLIP_DONE_IVB | - DE_PLANEA_FLIP_DONE_IVB; + DE_PLANEA_FLIP_DONE_IVB | + DE_AUX_CHANNEL_A_IVB; u32 render_irqs; u32 hotplug_mask; @@ -1960,7 +1977,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) SDE_PORTB_HOTPLUG_CPT | SDE_PORTC_HOTPLUG_CPT | SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT); + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT); dev_priv->pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); -- cgit v1.2.3 From 9ee32fea5fe810ec06af3a15e4c65478de56d4f5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 1 Dec 2012 13:53:48 +0100 Subject: drm/i915: irq-drive the dp aux communication At least on the platforms that have a dp aux irq and also have it enabled - vlvhsw should have one, too. But I don't have a machine to test this on. Judging from docs there's no dp aux interrupt for gm45. Also, I only have an ivb cpu edp machine, so the dp aux A code for snb/ilk is untested. For dpcd probing when nothing is connected it slashes about 5ms of cpu time (cpu time is now negligible), which agrees with 3 * 5 400 usec timeouts. A previous version of this patch increases the time required to go through the dp_detect cycle (which includes reading the edid) from around 33 ms to around 40 ms. Experiments indicated that this is purely due to the irq latency - the hw doesn't allow us to queue up dp aux transactions and hence irq latency directly affects throughput. gmbus is much better, there we have a 8 byte buffer, and we get the irq once another 4 bytes can be queued up. But by using the pm_qos interface to request the lowest possible cpu wake-up latency this slowdown completely disappeared. Since all our output detection logic is single-threaded with the mode_config mutex right now anyway, I've decide not ot play fancy and to just reuse the gmbus wait queue. But this would definitely prep the way to run dp detection on different ports in parallel v2: Add a timeout for dp aux transfers when using interrupts - the hw _does_ prevent this with the hw-based 400 usec timeout, but if the irq somehow doesn't arrive we're screwed. Lesson learned while developing this ;-) v3: While at it also convert the busy-loop to wait_for_atomic, so that we don't run the risk of an infinite loop any more. v4: Ensure we have the smallest possible irq latency by using the pm_qos interface. v5: Add a comment to the code to explain why we frob pm_qos. Suggested by Chris Wilson. v6: Disable dp irq for vlv, that's easier than trying to get at docs and hw. v7: Squash in a fix for Haswell that Paulo Zanoni tracked down - the dp aux registers aren't at a fixed offset any more, but can be on the PCH while the DP port is on the cpu die. Reviewed-by: Imre Deak (v6) Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 4 +++ drivers/gpu/drm/i915/i915_irq.c | 6 ++++ drivers/gpu/drm/i915/intel_dp.c | 79 ++++++++++++++++++++++++++++++++++------- 4 files changed, 77 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 14d679ab52b7..93630669c6dc 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1737,6 +1737,7 @@ int i915_driver_unload(struct drm_device *dev) intel_teardown_mchbar(dev); destroy_workqueue(dev_priv->wq); + pm_qos_remove_request(&dev_priv->pm_qos); if (dev_priv->slab) kmem_cache_destroy(dev_priv->slab); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3843383e13cc..a00ee3da632f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -40,6 +40,7 @@ #include #include #include +#include /* General customization: */ @@ -665,6 +666,9 @@ typedef struct drm_i915_private { /* protects the irq masks */ spinlock_t irq_lock; + /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */ + struct pm_qos_request pm_qos; + /* DPIO indirect register protection */ spinlock_t dpio_lock; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 1e245e501e5a..58bb11b58796 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -536,7 +536,11 @@ static void gmbus_irq_handler(struct drm_device *dev) static void dp_aux_irq_handler(struct drm_device *dev) { + struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; + DRM_DEBUG_DRIVER("AUX channel interrupt\n"); + + wake_up_all(&dev_priv->gmbus_wait_queue); } static irqreturn_t valleyview_irq_handler(int irq, void *arg) @@ -2732,6 +2736,8 @@ void intel_irq_init(struct drm_device *dev) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); + pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0); + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b51043e651b5..ada1b316195c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -322,6 +322,48 @@ intel_dp_check_edp(struct intel_dp *intel_dp) } } +static uint32_t +intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq) +{ + struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = intel_dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t ch_ctl = intel_dp->output_reg + 0x10; + uint32_t status; + bool done; + + if (IS_HASWELL(dev)) { + switch (intel_dig_port->port) { + case PORT_A: + ch_ctl = DPA_AUX_CH_CTL; + break; + case PORT_B: + ch_ctl = PCH_DPB_AUX_CH_CTL; + break; + case PORT_C: + ch_ctl = PCH_DPC_AUX_CH_CTL; + break; + case PORT_D: + ch_ctl = PCH_DPD_AUX_CH_CTL; + break; + default: + BUG(); + } + } + +#define C (((status = I915_READ(ch_ctl)) & DP_AUX_CH_CTL_SEND_BUSY) == 0) + if (has_aux_irq) + done = wait_event_timeout(dev_priv->gmbus_wait_queue, C, 10); + else + done = wait_for_atomic(C, 10) == 0; + if (!done) + DRM_ERROR("dp aux hw did not signal timeout (has irq: %i)!\n", + has_aux_irq); +#undef C + + return status; +} + static int intel_dp_aux_ch(struct intel_dp *intel_dp, uint8_t *send, int send_bytes, @@ -333,11 +375,17 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = dev->dev_private; uint32_t ch_ctl = output_reg + 0x10; uint32_t ch_data = ch_ctl + 4; - int i; - int recv_bytes; + int i, ret, recv_bytes; uint32_t status; uint32_t aux_clock_divider; int try, precharge; + bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev); + + /* dp aux is extremely sensitive to irq latency, hence request the + * lowest possible wakeup latency and so prevent the cpu from going into + * deep sleep states. + */ + pm_qos_update_request(&dev_priv->pm_qos, 0); if (IS_HASWELL(dev)) { switch (intel_dig_port->port) { @@ -400,7 +448,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, if (try == 3) { WARN(1, "dp_aux_ch not started status 0x%08x\n", I915_READ(ch_ctl)); - return -EBUSY; + ret = -EBUSY; + goto out; } /* Must try at least 3 times according to DP spec */ @@ -413,6 +462,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, /* Send the command and wait for it to complete */ I915_WRITE(ch_ctl, DP_AUX_CH_CTL_SEND_BUSY | + (has_aux_irq ? DP_AUX_CH_CTL_INTERRUPT : 0) | DP_AUX_CH_CTL_TIME_OUT_400us | (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | @@ -420,12 +470,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, DP_AUX_CH_CTL_DONE | DP_AUX_CH_CTL_TIME_OUT_ERROR | DP_AUX_CH_CTL_RECEIVE_ERROR); - for (;;) { - status = I915_READ(ch_ctl); - if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) - break; - udelay(100); - } + + status = intel_dp_aux_wait_done(intel_dp, has_aux_irq); /* Clear done status and any errors */ I915_WRITE(ch_ctl, @@ -443,7 +489,8 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, if ((status & DP_AUX_CH_CTL_DONE) == 0) { DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); - return -EBUSY; + ret = -EBUSY; + goto out; } /* Check for timeout or receive error. @@ -451,14 +498,16 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, */ if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); - return -EIO; + ret = -EIO; + goto out; } /* Timeouts occur when the device isn't connected, so they're * "normal" -- don't fill the kernel log with these */ if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } /* Unload any bytes sent back from the other side */ @@ -471,7 +520,11 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, unpack_aux(I915_READ(ch_data + i), recv + i, recv_bytes - i); - return recv_bytes; + ret = recv_bytes; +out: + pm_qos_update_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE); + + return ret; } /* Write data to the aux channel in native mode */ -- cgit v1.2.3 From 36dacf5b8b3c0bab7010943c34d2dbcc09a0d6f3 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Thu, 6 Dec 2012 09:44:52 -0200 Subject: drm/i915: be less verbose when handling gmbus/aux irqs Having 9500 lines repeated on dmesg does not help me at all. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 58bb11b58796..2e1d80de6ccc 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -529,8 +529,6 @@ static void gmbus_irq_handler(struct drm_device *dev) { struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; - DRM_DEBUG_DRIVER("GMBUS interrupt\n"); - wake_up_all(&dev_priv->gmbus_wait_queue); } @@ -538,8 +536,6 @@ static void dp_aux_irq_handler(struct drm_device *dev) { struct drm_i915_private *dev_priv = (drm_i915_private_t *) dev->dev_private; - DRM_DEBUG_DRIVER("AUX channel interrupt\n"); - wake_up_all(&dev_priv->gmbus_wait_queue); } -- cgit v1.2.3 From 97a19a247c23e286814a5ac7ec0825d0ff82a16c Mon Sep 17 00:00:00 2001 From: Tomas Janousek Date: Sat, 8 Dec 2012 13:48:13 +0100 Subject: drm/i915: don't prevent CPU idle states Commit 9ee32fea5f unconditionally prevents the CPU from entering idle states until intel_dp_aux_ch completes for the first time, which never happens on my DisplayPort-less intel gfx, causing the CPU to get rather hot. Signed-off-by: Tomas Janousek Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2e1d80de6ccc..914ecf4acfa6 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2732,7 +2732,7 @@ void intel_irq_init(struct drm_device *dev) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); - pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, 0); + pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ -- cgit v1.2.3 From 20afbda209d708be66944907966486d0c1331cb8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 11 Dec 2012 14:05:07 +0100 Subject: drm/i915: Fixup hpd irq register setup ordering For GMCH platforms we set up the hpd irq registers in the irq postinstall hook. But since we only enable the irq sources we actually need in PORT_HOTPLUG_EN/STATUS, taking dev_priv->hotplug_supported_mask into account, no hpd interrupt sources is enabled since commit 52d7ecedac3f96fb562cb482c139015372728638 Author: Daniel Vetter Date: Sat Dec 1 21:03:22 2012 +0100 drm/i915: reorder setup sequence to have irqs for output setup Wrongly set-up interrupts also lead to broken hw-based load-detection on at least GM45, resulting in ghost VGA/TV-out outputs. To fix this, delay the hotplug register setup until after all outputs are set up, by moving it into a new dev_priv->display.hpd_irq_callback. We might also move the PCH_SPLIT platforms to such a setup eventually. Another funny part is that we need to delay the fbdev initial config probing until after the hpd regs are setup, for otherwise it'll detect ghost outputs. But we can only enable the hpd interrupt handling itself (and the output polling) _after_ that initial scan, due to massive locking brain-damage in the fbdev setup code. Add a big comment to explain this cute little dragon lair. v2: Encapsulate all the fbdev handling by wrapping the move call into intel_fbdev_initial_config in intel_fb.c. Requested by Chris Wilson. v3: Applied bikeshed from Jesse Barnes. v4: Imre Deak noticed that we also need to call intel_hpd_init after the drm_irqinstall calls in the gpu reset and resume paths - otherwise hotplug will be broken. Also improve the comment a bit about why hpd_init needs to be called before we set up the initial fbdev config. Bugzilla: Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=54943 Reported-by: Chris Wilson Reviewed-by: Jesse Barnes (v3) Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 15 ++++++++++ drivers/gpu/drm/i915/i915_drv.c | 2 ++ drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/i915_irq.c | 63 +++++++++++++++++++++++++++++++--------- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_fb.c | 10 ++++++- 6 files changed, 79 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 93630669c6dc..0c2ab40ab2ed 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1318,6 +1318,21 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_gem; + /* Only enable hotplug handling once the fbdev is fully set up. */ + intel_hpd_init(dev); + + /* + * Some ports require correctly set-up hpd registers for detection to + * work properly (leading to ghost connected connector status), e.g. VGA + * on gm45. Hence we can only set up the initial fbdev config after hpd + * irqs are fully enabled. Now we should scan for the initial config + * only once hotplug handling is enabled, but due to screwed-up locking + * around kms/fbdev init we can't protect the fdbev initial config + * scanning against hotplug events. Hence do this first and ignore the + * tiny window where we will loose hotplug notifactions. + */ + intel_fbdev_initial_config(dev); + /* Only enable hotplug handling once the fbdev is fully set up. */ dev_priv->enable_hotplug_processing = true; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index a12921892446..fbd0b28b7200 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -566,6 +566,7 @@ static int __i915_drm_thaw(struct drm_device *dev) intel_modeset_init_hw(dev); intel_modeset_setup_hw_state(dev, false); drm_irq_install(dev); + intel_hpd_init(dev); } intel_opregion_init(dev); @@ -871,6 +872,7 @@ int i915_reset(struct drm_device *dev) drm_irq_uninstall(dev); drm_irq_install(dev); + intel_hpd_init(dev); } else { mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9da67824dde3..e55303e2f74e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -297,6 +297,7 @@ struct drm_i915_display_funcs { struct drm_i915_gem_object *obj); int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y); + void (*hpd_irq_setup)(struct drm_device *dev); /* clock updates for mode set */ /* cursor updates */ /* render clock increase/decrease */ @@ -1343,6 +1344,7 @@ void i915_hangcheck_elapsed(unsigned long data); void i915_handle_error(struct drm_device *dev, bool wedged); extern void intel_irq_init(struct drm_device *dev); +extern void intel_hpd_init(struct drm_device *dev); extern void intel_gt_init(struct drm_device *dev); extern void intel_gt_reset(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 914ecf4acfa6..551f370657b7 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1995,7 +1995,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask; - u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); u32 pipestat_enable = PLANE_FLIP_DONE_INT_EN_VLV; u32 render_irqs; u16 msid; @@ -2024,6 +2023,9 @@ static int valleyview_irq_postinstall(struct drm_device *dev) msid |= (1<<14); pci_write_config_word(dev_priv->dev->pdev, 0x98, msid); + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + I915_WRITE(VLV_IMR, dev_priv->irq_mask); I915_WRITE(VLV_IER, enable_mask); I915_WRITE(VLV_IIR, 0xffffffff); @@ -2053,6 +2055,15 @@ static int valleyview_irq_postinstall(struct drm_device *dev) #endif I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE); + + return 0; +} + +static void valleyview_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); + /* Note HDMI and DP share bits */ if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) hotplug_en |= HDMIB_HOTPLUG_INT_EN; @@ -2070,8 +2081,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) } I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - - return 0; } static void valleyview_irq_uninstall(struct drm_device *dev) @@ -2301,6 +2310,9 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_USER_INTERRUPT; if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + /* Enable in IER... */ enable_mask |= I915_DISPLAY_PORT_INTERRUPT; /* and unmask in IMR */ @@ -2311,8 +2323,18 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); + intel_opregion_enable_asle(dev); + + return 0; +} + +static void i915_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en; + if (I915_HAS_HOTPLUG(dev)) { - u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); + hotplug_en = I915_READ(PORT_HOTPLUG_EN); if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) hotplug_en |= HDMIB_HOTPLUG_INT_EN; @@ -2333,10 +2355,6 @@ static int i915_irq_postinstall(struct drm_device *dev) I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); } - - intel_opregion_enable_asle(dev); - - return 0; } static irqreturn_t i915_irq_handler(int irq, void *arg) @@ -2496,7 +2514,6 @@ static void i965_irq_preinstall(struct drm_device * dev) static int i965_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 hotplug_en; u32 enable_mask; u32 error_mask; @@ -2538,6 +2555,19 @@ static int i965_irq_postinstall(struct drm_device *dev) I915_WRITE(IER, enable_mask); POSTING_READ(IER); + I915_WRITE(PORT_HOTPLUG_EN, 0); + POSTING_READ(PORT_HOTPLUG_EN); + + intel_opregion_enable_asle(dev); + + return 0; +} + +static void i965_hpd_irq_setup(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug_en; + /* Note HDMI and DP share hotplug bits */ hotplug_en = 0; if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) @@ -2572,10 +2602,6 @@ static int i965_irq_postinstall(struct drm_device *dev) /* Ignore TV since it's buggy */ I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - - intel_opregion_enable_asle(dev); - - return 0; } static irqreturn_t i965_irq_handler(int irq, void *arg) @@ -2754,6 +2780,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = valleyview_irq_uninstall; dev->driver->enable_vblank = valleyview_enable_vblank; dev->driver->disable_vblank = valleyview_disable_vblank; + dev_priv->display.hpd_irq_setup = valleyview_hpd_irq_setup; } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { /* Share pre & uninstall handlers with ILK/SNB */ dev->driver->irq_handler = ivybridge_irq_handler; @@ -2780,13 +2807,23 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_postinstall = i915_irq_postinstall; dev->driver->irq_uninstall = i915_irq_uninstall; dev->driver->irq_handler = i915_irq_handler; + dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; } else { dev->driver->irq_preinstall = i965_irq_preinstall; dev->driver->irq_postinstall = i965_irq_postinstall; dev->driver->irq_uninstall = i965_irq_uninstall; dev->driver->irq_handler = i965_irq_handler; + dev_priv->display.hpd_irq_setup = i965_hpd_irq_setup; } dev->driver->enable_vblank = i915_enable_vblank; dev->driver->disable_vblank = i915_disable_vblank; } } + +void intel_hpd_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->display.hpd_irq_setup) + dev_priv->display.hpd_irq_setup(dev); +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 7ca7772eaccd..22728f2c1d83 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -587,6 +587,7 @@ extern int intel_framebuffer_init(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd, struct drm_i915_gem_object *obj); extern int intel_fbdev_init(struct drm_device *dev); +extern void intel_fbdev_initial_config(struct drm_device *dev); extern void intel_fbdev_fini(struct drm_device *dev); extern void intel_fbdev_set_suspend(struct drm_device *dev, int state); extern void intel_prepare_page_flip(struct drm_device *dev, int plane); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index b7773e548911..822896782cd0 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -243,10 +243,18 @@ int intel_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&ifbdev->helper); - drm_fb_helper_initial_config(&ifbdev->helper, 32); + return 0; } +void intel_fbdev_initial_config(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + /* Due to peculiar init order wrt to hpd handling this is separate. */ + drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); +} + void intel_fbdev_fini(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; -- cgit v1.2.3 From 7dbf9d6e0fd8d42398955de119ccba6c35813c88 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 18 Dec 2012 10:31:22 -0800 Subject: drm/i915: BUG() if fences are used on unsupported platform Signed-off-by: Ben Widawsky Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d15c86279d02..ad03dea210bd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2649,7 +2649,7 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg, case 4: i965_write_fence_reg(dev, reg, obj); break; case 3: i915_write_fence_reg(dev, reg, obj); break; case 2: i830_write_fence_reg(dev, reg, obj); break; - default: break; + default: BUG(); } } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 551f370657b7..6ba0573e7f16 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1106,6 +1106,8 @@ static void i915_gem_record_fences(struct drm_device *dev, error->fence[i] = I915_READ(FENCE_REG_830_0 + (i * 4)); break; + default: + BUG(); } } -- cgit v1.2.3 From af5163acd8319dbc901bb59a8d503d8bb774d88b Mon Sep 17 00:00:00 2001 From: Egbert Eich Date: Thu, 10 Jan 2013 10:02:39 -0500 Subject: drm/i915: Remove pch_rq_mask from struct drm_i915_private. This variable is only used locally in the irq postinstall functions for ivybridge and ironlake. Signed-off-by: Egbert Eich Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/i915_irq.c | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b1b1b7350ca4..910cc0b545a6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -697,7 +697,6 @@ typedef struct drm_i915_private { u32 pipestat[2]; u32 irq_mask; u32 gt_irq_mask; - u32 pch_irq_mask; u32 hotplug_supported_mask; struct work_struct hotplug_work; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6689a61b02a3..202813128441 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1892,6 +1892,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev) DE_AUX_CHANNEL_A; u32 render_irqs; u32 hotplug_mask; + u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -1935,10 +1936,10 @@ static int ironlake_irq_postinstall(struct drm_device *dev) SDE_AUX_MASK); } - dev_priv->pch_irq_mask = ~hotplug_mask; + pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, dev_priv->pch_irq_mask); + I915_WRITE(SDEIMR, pch_irq_mask); I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); @@ -1966,6 +1967,7 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_AUX_CHANNEL_A_IVB; u32 render_irqs; u32 hotplug_mask; + u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -1995,10 +1997,10 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) SDE_PORTD_HOTPLUG_CPT | SDE_GMBUS_CPT | SDE_AUX_MASK_CPT); - dev_priv->pch_irq_mask = ~hotplug_mask; + pch_irq_mask = ~hotplug_mask; I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, dev_priv->pch_irq_mask); + I915_WRITE(SDEIMR, pch_irq_mask); I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); -- cgit v1.2.3 From 5d4545aef561ad47f91bcf75814af20c104b5a9e Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 17 Jan 2013 12:45:15 -0800 Subject: drm/i915: Create a gtt structure The purpose of the gtt structure is to help isolate our gtt specific properties from the rest of the code (in doing so it help us finish the isolation from the AGP connection). The following members are pulled out (and renamed): gtt_start gtt_total gtt_mappable_end gtt_mappable gtt_base_addr gsm The gtt structure will serve as a nice place to put gen specific gtt routines in upcoming patches. As far as what else I feel belongs in this structure: it is meant to encapsulate the GTT's physical properties. This is why I've not added fields which track various drm_mm properties, or things like gtt_mtrr (which is itself a pretty transient field). Reviewed-by: Rodrigo Vivi [Ben modified commit messages] Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 4 ++-- drivers/gpu/drm/i915/i915_dma.c | 20 ++++++++++---------- drivers/gpu/drm/i915/i915_drv.h | 29 +++++++++++++++++++++-------- drivers/gpu/drm/i915/i915_gem.c | 14 +++++++------- drivers/gpu/drm/i915/i915_gem_evict.c | 2 +- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 2 +- drivers/gpu/drm/i915/i915_gem_gtt.c | 24 ++++++++++++------------ drivers/gpu/drm/i915/i915_gem_tiling.c | 2 +- drivers/gpu/drm/i915/i915_irq.c | 4 ++-- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_fb.c | 2 +- drivers/gpu/drm/i915/intel_overlay.c | 4 ++-- 12 files changed, 61 insertions(+), 48 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 35d326d70fab..773b23ecc83b 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -259,8 +259,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data) count, size); seq_printf(m, "%zu [%zu] gtt total\n", - dev_priv->mm.gtt_total, - dev_priv->mm.gtt_mappable_end - dev_priv->mm.gtt_start); + dev_priv->gtt.total, + dev_priv->gtt.mappable_end - dev_priv->gtt.start); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 442118293883..bb622135798a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1076,7 +1076,7 @@ static int i915_set_status_page(struct drm_device *dev, void *data, ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12); dev_priv->dri1.gfx_hws_cpu_addr = - ioremap_wc(dev_priv->mm.gtt_base_addr + hws->addr, 4096); + ioremap_wc(dev_priv->gtt.mappable_base + hws->addr, 4096); if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) { i915_dma_cleanup(dev); ring->status_page.gfx_addr = 0; @@ -1543,17 +1543,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) } aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - dev_priv->mm.gtt_base_addr = dev_priv->mm.gtt->gma_bus_addr; + dev_priv->gtt.mappable_base = dev_priv->mm.gtt->gma_bus_addr; - dev_priv->mm.gtt_mapping = - io_mapping_create_wc(dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable = + io_mapping_create_wc(dev_priv->gtt.mappable_base, aperture_size); - if (dev_priv->mm.gtt_mapping == NULL) { + if (dev_priv->gtt.mappable == NULL) { ret = -EIO; goto out_rmmap; } - i915_mtrr_setup(dev_priv, dev_priv->mm.gtt_base_addr, + i915_mtrr_setup(dev_priv, dev_priv->gtt.mappable_base, aperture_size); /* The i915 workqueue is primarily used for batched retirement of @@ -1658,11 +1658,11 @@ out_gem_unload: out_mtrrfree: if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable_base, aperture_size); dev_priv->mm.gtt_mtrr = -1; } - io_mapping_free(dev_priv->mm.gtt_mapping); + io_mapping_free(dev_priv->gtt.mappable); out_rmmap: pci_iounmap(dev->pdev, dev_priv->regs); put_gmch: @@ -1696,10 +1696,10 @@ int i915_driver_unload(struct drm_device *dev) /* Cancel the retire work handler, which should be idle now. */ cancel_delayed_work_sync(&dev_priv->mm.retire_work); - io_mapping_free(dev_priv->mm.gtt_mapping); + io_mapping_free(dev_priv->gtt.mappable); if (dev_priv->mm.gtt_mtrr >= 0) { mtrr_del(dev_priv->mm.gtt_mtrr, - dev_priv->mm.gtt_base_addr, + dev_priv->gtt.mappable_base, dev_priv->mm.gtt->gtt_mappable_entries * PAGE_SIZE); dev_priv->mm.gtt_mtrr = -1; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6e2c10b65c8d..f3f2e5e1393f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -364,6 +364,25 @@ struct intel_device_info { u8 has_llc:1; }; +/* The Graphics Translation Table is the way in which GEN hardware translates a + * Graphics Virtual Address into a Physical Address. In addition to the normal + * collateral associated with any va->pa translations GEN hardware also has a + * portion of the GTT which can be mapped by the CPU and remain both coherent + * and correct (in cases like swizzling). That region is referred to as GMADR in + * the spec. + */ +struct i915_gtt { + unsigned long start; /* Start offset of used GTT */ + size_t total; /* Total size GTT can map */ + + unsigned long mappable_end; /* End offset that we can CPU map */ + struct io_mapping *mappable; /* Mapping to our CPU mappable region */ + phys_addr_t mappable_base; /* PA of our GMADR */ + + /** "Graphics Stolen Memory" holds the global PTEs */ + void __iomem *gsm; +}; + #define I915_PPGTT_PD_ENTRIES 512 #define I915_PPGTT_PT_ENTRIES 1024 struct i915_hw_ppgtt { @@ -781,6 +800,8 @@ typedef struct drm_i915_private { /* Register state */ bool modeset_on_lid; + struct i915_gtt gtt; + struct { /** Bridge to intel-gtt-ko */ struct intel_gtt *gtt; @@ -799,15 +820,8 @@ typedef struct drm_i915_private { struct list_head unbound_list; /** Usable portion of the GTT for GEM */ - unsigned long gtt_start; - unsigned long gtt_mappable_end; unsigned long stolen_base; /* limited to low memory (32-bit) */ - /** "Graphics Stolen Memory" holds the global PTEs */ - void __iomem *gsm; - - struct io_mapping *gtt_mapping; - phys_addr_t gtt_base_addr; int gtt_mtrr; /** PPGTT used for aliasing the PPGTT with the GTT */ @@ -885,7 +899,6 @@ typedef struct drm_i915_private { struct drm_i915_gem_phys_object *phys_objs[I915_MAX_PHYS_OBJECT]; /* accounting, useful for userland debugging */ - size_t gtt_total; size_t object_memory; u32 object_count; } mm; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a2bb18914329..51fdf16181a7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -186,7 +186,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, pinned += obj->gtt_space->size; mutex_unlock(&dev->struct_mutex); - args->aper_size = dev_priv->mm.gtt_total; + args->aper_size = dev_priv->gtt.total; args->aper_available_size = args->aper_size - pinned; return 0; @@ -637,7 +637,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, * source page isn't available. Return the error and we'll * retry in the slow path. */ - if (fast_user_write(dev_priv->mm.gtt_mapping, page_base, + if (fast_user_write(dev_priv->gtt.mappable, page_base, page_offset, user_data, page_length)) { ret = -EFAULT; goto out_unpin; @@ -1362,7 +1362,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) obj->fault_mappable = true; - pfn = ((dev_priv->mm.gtt_base_addr + obj->gtt_offset) >> PAGE_SHIFT) + + pfn = ((dev_priv->gtt.mappable_base + obj->gtt_offset) >> PAGE_SHIFT) + page_offset; /* Finally, remap it using the new GTT offset */ @@ -1544,7 +1544,7 @@ i915_gem_mmap_gtt(struct drm_file *file, goto unlock; } - if (obj->base.size > dev_priv->mm.gtt_mappable_end) { + if (obj->base.size > dev_priv->gtt.mappable_end) { ret = -E2BIG; goto out; } @@ -2910,7 +2910,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, * before evicting everything in a vain attempt to find space. */ if (obj->base.size > - (map_and_fenceable ? dev_priv->mm.gtt_mappable_end : dev_priv->mm.gtt_total)) { + (map_and_fenceable ? dev_priv->gtt.mappable_end : dev_priv->gtt.total)) { DRM_ERROR("Attempting to bind an object larger than the aperture\n"); return -E2BIG; } @@ -2931,7 +2931,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, if (map_and_fenceable) ret = drm_mm_insert_node_in_range_generic(&dev_priv->mm.gtt_space, node, size, alignment, obj->cache_level, - 0, dev_priv->mm.gtt_mappable_end); + 0, dev_priv->gtt.mappable_end); else ret = drm_mm_insert_node_generic(&dev_priv->mm.gtt_space, node, size, alignment, obj->cache_level); @@ -2971,7 +2971,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, (node->start & (fence_alignment - 1)) == 0; mappable = - obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end; + obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end; obj->map_and_fenceable = mappable && fenceable; diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 776a3225184c..c86d5d9356fd 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -80,7 +80,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, if (mappable) drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space, min_size, alignment, cache_level, - 0, dev_priv->mm.gtt_mappable_end); + 0, dev_priv->gtt.mappable_end); else drm_mm_init_scan(&dev_priv->mm.gtt_space, min_size, alignment, cache_level); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index f5a11ecf5494..27269103b621 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -281,7 +281,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj, /* Map the page containing the relocation we're going to perform. */ reloc->offset += obj->gtt_offset; - reloc_page = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + reloc_page = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, reloc->offset & PAGE_MASK); reloc_entry = (uint32_t __iomem *) (reloc_page + (reloc->offset & ~PAGE_MASK)); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 8c42ddd467b6..61bfb12e1016 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -290,7 +290,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) return; - pd_addr = (gtt_pte_t __iomem*)dev_priv->mm.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); + pd_addr = (gtt_pte_t __iomem*)dev_priv->gtt.gsm + ppgtt->pd_offset/sizeof(gtt_pte_t); for (i = 0; i < ppgtt->num_pd_entries; i++) { dma_addr_t pt_addr; @@ -367,7 +367,7 @@ static void i915_ggtt_clear_range(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; gtt_pte_t scratch_pte; - gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->mm.gsm + first_entry; + gtt_pte_t __iomem *gtt_base = (gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry; const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; int i; @@ -393,8 +393,8 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) struct drm_i915_gem_object *obj; /* First fill our portion of the GTT with scratch pages */ - i915_ggtt_clear_range(dev, dev_priv->mm.gtt_start / PAGE_SIZE, - dev_priv->mm.gtt_total / PAGE_SIZE); + i915_ggtt_clear_range(dev, dev_priv->gtt.start / PAGE_SIZE, + dev_priv->gtt.total / PAGE_SIZE); list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { i915_gem_clflush_object(obj); @@ -433,7 +433,7 @@ static void gen6_ggtt_bind_object(struct drm_i915_gem_object *obj, const int first_entry = obj->gtt_space->start >> PAGE_SHIFT; const int max_entries = dev_priv->mm.gtt->gtt_total_entries - first_entry; gtt_pte_t __iomem *gtt_entries = - (gtt_pte_t __iomem *)dev_priv->mm.gsm + first_entry; + (gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry; int unused, i = 0; unsigned int len, m = 0; dma_addr_t addr; @@ -556,9 +556,9 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, obj->has_global_gtt_mapping = 1; } - dev_priv->mm.gtt_start = start; - dev_priv->mm.gtt_mappable_end = mappable_end; - dev_priv->mm.gtt_total = end - start; + dev_priv->gtt.start = start; + dev_priv->gtt.mappable_end = mappable_end; + dev_priv->gtt.total = end - start; /* Clear any non-preallocated blocks */ drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space, @@ -752,9 +752,9 @@ int i915_gem_gtt_init(struct drm_device *dev) goto err_out; } - dev_priv->mm.gsm = ioremap_wc(gtt_bus_addr, - dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); - if (!dev_priv->mm.gsm) { + dev_priv->gtt.gsm = ioremap_wc(gtt_bus_addr, + dev_priv->mm.gtt->gtt_total_entries * sizeof(gtt_pte_t)); + if (!dev_priv->gtt.gsm) { DRM_ERROR("Failed to map the gtt page table\n"); teardown_scratch_page(dev); ret = -ENOMEM; @@ -778,7 +778,7 @@ err_out: void i915_gem_gtt_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - iounmap(dev_priv->mm.gsm); + iounmap(dev_priv->gtt.gsm); teardown_scratch_page(dev); if (INTEL_INFO(dev)->gen < 6) intel_gmch_remove(); diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index e76f0d8470a1..abcba2f5a788 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -357,7 +357,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj->map_and_fenceable = obj->gtt_space == NULL || - (obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end && + (obj->gtt_offset + obj->base.size <= dev_priv->gtt.mappable_end && i915_gem_object_fence_ok(obj, args->tiling_mode)); /* Rebind if we need a change of alignment */ diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 202813128441..cc49a6ddc052 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -939,7 +939,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, goto unwind; local_irq_save(flags); - if (reloc_offset < dev_priv->mm.gtt_mappable_end && + if (reloc_offset < dev_priv->gtt.mappable_end && src->has_global_gtt_mapping) { void __iomem *s; @@ -948,7 +948,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv, * captures what the GPU read. */ - s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + s = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, reloc_offset); memcpy_fromio(d, s, PAGE_SIZE); io_mapping_unmap_atomic(s); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 40b6b5e9d6ef..e4c5067a54d3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8687,7 +8687,7 @@ void intel_modeset_init(struct drm_device *dev) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; } - dev->mode_config.fb_base = dev_priv->mm.gtt_base_addr; + dev->mode_config.fb_base = dev_priv->gtt.mappable_base; DRM_DEBUG_KMS("%d display pipe%s available.\n", dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : ""); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 71d55801c0d9..ce02af8ca96e 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -142,7 +142,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev, info->fix.smem_len = size; info->screen_base = - ioremap_wc(dev_priv->mm.gtt_base_addr + obj->gtt_offset, + ioremap_wc(dev_priv->gtt.mappable_base + obj->gtt_offset, size); if (!info->screen_base) { ret = -ENOSPC; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index fabe0acf808d..ba978bf93a2e 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -195,7 +195,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay) if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_obj->handle->vaddr; else - regs = io_mapping_map_wc(dev_priv->mm.gtt_mapping, + regs = io_mapping_map_wc(dev_priv->gtt.mappable, overlay->reg_bo->gtt_offset); return regs; @@ -1434,7 +1434,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay) regs = (struct overlay_registers __iomem *) overlay->reg_bo->phys_obj->handle->vaddr; else - regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, + regs = io_mapping_map_atomic_wc(dev_priv->gtt.mappable, overlay->reg_bo->gtt_offset); return regs; -- cgit v1.2.3 From 99584db33ba4f864777e2cfef5329ed1bf13f714 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:04 +0100 Subject: drm/i915: extract hangcheck/reset/error_state state into substruct This has been sprinkled all over the place in dev_priv. I think it'd be good to also move all the code into a separate file like i915_gem_error.c, but that's for another patch. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 10 +++--- drivers/gpu/drm/i915/i915_dma.c | 6 ++-- drivers/gpu/drm/i915/i915_drv.c | 8 ++--- drivers/gpu/drm/i915/i915_drv.h | 39 +++++++++++++--------- drivers/gpu/drm/i915/i915_gem.c | 10 +++--- drivers/gpu/drm/i915/i915_irq.c | 59 ++++++++++++++++++--------------- drivers/gpu/drm/i915/intel_ringbuffer.c | 2 +- 7 files changed, 73 insertions(+), 61 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 90a6fc506dd5..3b1bf4e70d94 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -814,11 +814,11 @@ static int i915_error_state_open(struct inode *inode, struct file *file) error_priv->dev = dev; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error_priv->error = dev_priv->first_error; + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error_priv->error = dev_priv->gpu_error.first_error; if (error_priv->error) kref_get(&error_priv->error->ref); - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); return single_open(file, i915_error_state, error_priv); } @@ -1727,7 +1727,7 @@ i915_ring_stop_read(struct file *filp, int len; len = snprintf(buf, sizeof(buf), - "0x%08x\n", dev_priv->stop_rings); + "0x%08x\n", dev_priv->gpu_error.stop_rings); if (len > sizeof(buf)) len = sizeof(buf); @@ -1763,7 +1763,7 @@ i915_ring_stop_write(struct file *filp, if (ret) return ret; - dev_priv->stop_rings = val; + dev_priv->gpu_error.stop_rings = val; mutex_unlock(&dev->struct_mutex); return cnt; diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3f70178c63ca..11c7aa80e29b 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1605,7 +1605,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) pci_enable_msi(dev->pdev); spin_lock_init(&dev_priv->irq_lock); - spin_lock_init(&dev_priv->error_lock); + spin_lock_init(&dev_priv->gpu_error.lock); spin_lock_init(&dev_priv->rps.lock); mutex_init(&dev_priv->dpio_lock); @@ -1725,8 +1725,8 @@ int i915_driver_unload(struct drm_device *dev) } /* Free error state after interrupts are fully disabled. */ - del_timer_sync(&dev_priv->hangcheck_timer); - cancel_work_sync(&dev_priv->error_work); + del_timer_sync(&dev_priv->gpu_error.hangcheck_timer); + cancel_work_sync(&dev_priv->gpu_error.work); i915_destroy_error_state(dev); if (dev->pdev->msi_enabled) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index c8cbc32fe8db..3ff8e73f4341 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -779,9 +779,9 @@ int intel_gpu_reset(struct drm_device *dev) } /* Also reset the gpu hangman. */ - if (dev_priv->stop_rings) { + if (dev_priv->gpu_error.stop_rings) { DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n"); - dev_priv->stop_rings = 0; + dev_priv->gpu_error.stop_rings = 0; if (ret == -ENODEV) { DRM_ERROR("Reset not implemented, but ignoring " "error for simulated gpu hangs\n"); @@ -820,12 +820,12 @@ int i915_reset(struct drm_device *dev) i915_gem_reset(dev); ret = -ENODEV; - if (get_seconds() - dev_priv->last_gpu_reset < 5) + if (get_seconds() - dev_priv->gpu_error.last_reset < 5) DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); else ret = intel_gpu_reset(dev); - dev_priv->last_gpu_reset = get_seconds(); + dev_priv->gpu_error.last_reset = get_seconds(); if (ret) { DRM_ERROR("Failed to reset chip.\n"); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ea3226852ea8..dfe0e747c4f7 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -766,6 +766,28 @@ struct i915_gem_mm { u32 object_count; }; +struct i915_gpu_error { + /* For hangcheck timer */ +#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ +#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD) + struct timer_list hangcheck_timer; + int hangcheck_count; + uint32_t last_acthd[I915_NUM_RINGS]; + uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; + + /* For reset and error_state handling. */ + spinlock_t lock; + /* Protected by the above dev->gpu_error.lock. */ + struct drm_i915_error_state *first_error; + struct work_struct work; + struct completion completion; + + unsigned long last_reset; + + /* For gpu hang simulation. */ + unsigned int stop_rings; +}; + typedef struct drm_i915_private { struct drm_device *dev; struct kmem_cache *slab; @@ -829,16 +851,6 @@ typedef struct drm_i915_private { int num_pipe; int num_pch_pll; - /* For hangcheck timer */ -#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ -#define DRM_I915_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD) - struct timer_list hangcheck_timer; - int hangcheck_count; - uint32_t last_acthd[I915_NUM_RINGS]; - uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; - - unsigned int stop_rings; - unsigned long cfb_size; unsigned int cfb_fb; enum plane cfb_plane; @@ -886,11 +898,6 @@ typedef struct drm_i915_private { unsigned int fsb_freq, mem_freq, is_ddr3; - spinlock_t error_lock; - /* Protected by dev->error_lock. */ - struct drm_i915_error_state *first_error; - struct work_struct error_work; - struct completion error_completion; struct workqueue_struct *wq; /* Display functions */ @@ -949,7 +956,7 @@ typedef struct drm_i915_private { struct drm_mm_node *compressed_fb; struct drm_mm_node *compressed_llb; - unsigned long last_gpu_reset; + struct i915_gpu_error gpu_error; /* list of fbdev register on this device */ struct intel_fbdev *fbdev; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e4132ffb7609..95e022e7a26e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -90,7 +90,7 @@ static int i915_gem_wait_for_error(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct completion *x = &dev_priv->error_completion; + struct completion *x = &dev_priv->gpu_error.completion; unsigned long flags; int ret; @@ -943,7 +943,7 @@ i915_gem_check_wedge(struct drm_i915_private *dev_priv, bool interruptible) { if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->error_completion; + struct completion *x = &dev_priv->gpu_error.completion; bool recovery_complete; unsigned long flags; @@ -2045,7 +2045,7 @@ i915_add_request(struct intel_ring_buffer *ring, if (!dev_priv->mm.suspended) { if (i915_enable_hangcheck) { - mod_timer(&dev_priv->hangcheck_timer, + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } if (was_empty) { @@ -3803,7 +3803,7 @@ i915_gem_idle(struct drm_device *dev) * And not confound mm.suspended! */ dev_priv->mm.suspended = 1; - del_timer_sync(&dev_priv->hangcheck_timer); + del_timer_sync(&dev_priv->gpu_error.hangcheck_timer); i915_kernel_lost_context(dev); i915_gem_cleanup_ringbuffer(dev); @@ -4064,7 +4064,7 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list); INIT_DELAYED_WORK(&dev_priv->mm.retire_work, i915_gem_retire_work_handler); - init_completion(&dev_priv->error_completion); + init_completion(&dev_priv->gpu_error.completion); /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ if (IS_GEN3(dev)) { diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index cc49a6ddc052..c768ebdf8a27 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -356,8 +356,8 @@ static void notify_ring(struct drm_device *dev, wake_up_all(&ring->irq_queue); if (i915_enable_hangcheck) { - dev_priv->hangcheck_count = 0; - mod_timer(&dev_priv->hangcheck_timer, + dev_priv->gpu_error.hangcheck_count = 0; + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } } @@ -863,7 +863,7 @@ done: static void i915_error_work_func(struct work_struct *work) { drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - error_work); + gpu_error.work); struct drm_device *dev = dev_priv->dev; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; @@ -878,7 +878,7 @@ static void i915_error_work_func(struct work_struct *work) atomic_set(&dev_priv->mm.wedged, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); } - complete_all(&dev_priv->error_completion); + complete_all(&dev_priv->gpu_error.completion); } } @@ -1255,9 +1255,9 @@ static void i915_capture_error_state(struct drm_device *dev) unsigned long flags; int i, pipe; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error = dev_priv->first_error; - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error = dev_priv->gpu_error.first_error; + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) return; @@ -1341,12 +1341,12 @@ static void i915_capture_error_state(struct drm_device *dev) error->overlay = intel_overlay_capture_error_state(dev); error->display = intel_display_capture_error_state(dev); - spin_lock_irqsave(&dev_priv->error_lock, flags); - if (dev_priv->first_error == NULL) { - dev_priv->first_error = error; + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + if (dev_priv->gpu_error.first_error == NULL) { + dev_priv->gpu_error.first_error = error; error = NULL; } - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) i915_error_state_free(&error->ref); @@ -1358,10 +1358,10 @@ void i915_destroy_error_state(struct drm_device *dev) struct drm_i915_error_state *error; unsigned long flags; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error = dev_priv->first_error; - dev_priv->first_error = NULL; - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + spin_lock_irqsave(&dev_priv->gpu_error.lock, flags); + error = dev_priv->gpu_error.first_error; + dev_priv->gpu_error.first_error = NULL; + spin_unlock_irqrestore(&dev_priv->gpu_error.lock, flags); if (error) kref_put(&error->ref, i915_error_state_free); @@ -1482,7 +1482,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - INIT_COMPLETION(dev_priv->error_completion); + INIT_COMPLETION(dev_priv->gpu_error.completion); atomic_set(&dev_priv->mm.wedged, 1); /* @@ -1492,7 +1492,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) wake_up_all(&ring->irq_queue); } - queue_work(dev_priv->wq, &dev_priv->error_work); + queue_work(dev_priv->wq, &dev_priv->gpu_error.work); } static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) @@ -1723,7 +1723,7 @@ static bool i915_hangcheck_hung(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - if (dev_priv->hangcheck_count++ > 1) { + if (dev_priv->gpu_error.hangcheck_count++ > 1) { bool hung = true; DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); @@ -1782,25 +1782,29 @@ void i915_hangcheck_elapsed(unsigned long data) goto repeat; } - dev_priv->hangcheck_count = 0; + dev_priv->gpu_error.hangcheck_count = 0; return; } i915_get_extra_instdone(dev, instdone); - if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 && - memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) { + if (memcmp(dev_priv->gpu_error.last_acthd, acthd, + sizeof(acthd)) == 0 && + memcmp(dev_priv->gpu_error.prev_instdone, instdone, + sizeof(instdone)) == 0) { if (i915_hangcheck_hung(dev)) return; } else { - dev_priv->hangcheck_count = 0; + dev_priv->gpu_error.hangcheck_count = 0; - memcpy(dev_priv->last_acthd, acthd, sizeof(acthd)); - memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone)); + memcpy(dev_priv->gpu_error.last_acthd, acthd, + sizeof(acthd)); + memcpy(dev_priv->gpu_error.prev_instdone, instdone, + sizeof(instdone)); } repeat: /* Reset timer case chip hangs without another request being added */ - mod_timer(&dev_priv->hangcheck_timer, + mod_timer(&dev_priv->gpu_error.hangcheck_timer, round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES)); } @@ -2769,11 +2773,12 @@ void intel_irq_init(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); + INIT_WORK(&dev_priv->gpu_error.work, i915_error_work_func); INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work); INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work); - setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, + setup_timer(&dev_priv->gpu_error.hangcheck_timer, + i915_hangcheck_elapsed, (unsigned long) dev); pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index ce1d07487402..d6b06aa4c05c 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1491,7 +1491,7 @@ void intel_ring_advance(struct intel_ring_buffer *ring) struct drm_i915_private *dev_priv = ring->dev->dev_private; ring->tail &= ring->size - 1; - if (dev_priv->stop_rings & intel_ring_flag(ring)) + if (dev_priv->gpu_error.stop_rings & intel_ring_flag(ring)) return; ring->write_tail(ring, ring->tail); } -- cgit v1.2.3 From 33196deddacc7790defb9a7e84659e0362d4da7a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 14 Nov 2012 17:14:05 +0100 Subject: drm/i915: move wedged to the other gpu error handling stuff And to make Ben Widawsky happier, use the gpu_error instead of the entire device as the argument in some functions. Drop the outdated comment on ->wedged for now, a follow-up patch will change the semantics and add a proper comment again. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 13 +++---------- drivers/gpu/drm/i915/i915_gem.c | 34 ++++++++++++++++----------------- drivers/gpu/drm/i915/i915_irq.c | 6 +++--- drivers/gpu/drm/i915/intel_display.c | 4 ++-- drivers/gpu/drm/i915/intel_ringbuffer.c | 6 ++++-- 6 files changed, 30 insertions(+), 35 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 3b1bf4e70d94..e1b7eaf60d24 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1672,7 +1672,7 @@ i915_wedged_read(struct file *filp, len = snprintf(buf, sizeof(buf), "wedged : %d\n", - atomic_read(&dev_priv->mm.wedged)); + atomic_read(&dev_priv->gpu_error.wedged)); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index dfe0e747c4f7..62da6c7a4884 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -744,15 +744,6 @@ struct i915_gem_mm { */ int suspended; - /** - * Flag if the hardware appears to be wedged. - * - * This is set when attempts to idle the device timeout. - * It prevents command submission from occurring and makes - * every pending request fail - */ - atomic_t wedged; - /** Bit 6 swizzling required for X tiling */ uint32_t bit_6_swizzle_x; /** Bit 6 swizzling required for Y tiling */ @@ -784,6 +775,8 @@ struct i915_gpu_error { unsigned long last_reset; + atomic_t wedged; + /* For gpu hang simulation. */ unsigned int stop_rings; }; @@ -1548,7 +1541,7 @@ i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj) void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring); -int __must_check i915_gem_check_wedge(struct drm_i915_private *dev_priv, +int __must_check i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible); void i915_gem_reset(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 95e022e7a26e..04b2f92eb456 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -87,14 +87,13 @@ static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv, } static int -i915_gem_wait_for_error(struct drm_device *dev) +i915_gem_wait_for_error(struct i915_gpu_error *error) { - struct drm_i915_private *dev_priv = dev->dev_private; - struct completion *x = &dev_priv->gpu_error.completion; + struct completion *x = &error->completion; unsigned long flags; int ret; - if (!atomic_read(&dev_priv->mm.wedged)) + if (!atomic_read(&error->wedged)) return 0; /* @@ -110,7 +109,7 @@ i915_gem_wait_for_error(struct drm_device *dev) return ret; } - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_read(&error->wedged)) { /* GPU is hung, bump the completion count to account for * the token we just consumed so that we never hit zero and * end up waiting upon a subsequent completion event that @@ -125,9 +124,10 @@ i915_gem_wait_for_error(struct drm_device *dev) int i915_mutex_lock_interruptible(struct drm_device *dev) { + struct drm_i915_private *dev_priv = dev->dev_private; int ret; - ret = i915_gem_wait_for_error(dev); + ret = i915_gem_wait_for_error(&dev_priv->gpu_error); if (ret) return ret; @@ -939,11 +939,11 @@ unlock: } int -i915_gem_check_wedge(struct drm_i915_private *dev_priv, +i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible) { - if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->gpu_error.completion; + if (atomic_read(&error->wedged)) { + struct completion *x = &error->completion; bool recovery_complete; unsigned long flags; @@ -1025,7 +1025,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - atomic_read(&dev_priv->mm.wedged)) + atomic_read(&dev_priv->gpu_error.wedged)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1035,7 +1035,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, end = wait_event_timeout(ring->irq_queue, EXIT_COND, timeout_jiffies); - ret = i915_gem_check_wedge(dev_priv, interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) end = ret; } while (end == 0 && wait_forever); @@ -1081,7 +1081,7 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) BUG_ON(!mutex_is_locked(&dev->struct_mutex)); BUG_ON(seqno == 0); - ret = i915_gem_check_wedge(dev_priv, interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) return ret; @@ -1146,7 +1146,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, if (seqno == 0) return 0; - ret = i915_gem_check_wedge(dev_priv, true); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, true); if (ret) return ret; @@ -1379,7 +1379,7 @@ out: /* If this -EIO is due to a gpu hang, give the reset code a * chance to clean up the mess. Otherwise return the proper * SIGBUS. */ - if (!atomic_read(&dev_priv->mm.wedged)) + if (!atomic_read(&dev_priv->gpu_error.wedged)) return VM_FAULT_SIGBUS; case -EAGAIN: /* Give the error handler a chance to run and move the @@ -3390,7 +3390,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) u32 seqno = 0; int ret; - if (atomic_read(&dev_priv->mm.wedged)) + if (atomic_read(&dev_priv->gpu_error.wedged)) return -EIO; spin_lock(&file_priv->mm.lock); @@ -3978,9 +3978,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_read(&dev_priv->gpu_error.wedged)) { DRM_ERROR("Reenabling wedged hardware, good luck\n"); - atomic_set(&dev_priv->mm.wedged, 0); + atomic_set(&dev_priv->gpu_error.wedged, 0); } mutex_lock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c768ebdf8a27..f2c0016fa533 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -871,11 +871,11 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (atomic_read(&dev_priv->mm.wedged)) { + if (atomic_read(&dev_priv->gpu_error.wedged)) { DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); if (!i915_reset(dev)) { - atomic_set(&dev_priv->mm.wedged, 0); + atomic_set(&dev_priv->gpu_error.wedged, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); } complete_all(&dev_priv->gpu_error.completion); @@ -1483,7 +1483,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) if (wedged) { INIT_COMPLETION(dev_priv->gpu_error.completion); - atomic_set(&dev_priv->mm.wedged, 1); + atomic_set(&dev_priv->gpu_error.wedged, 1); /* * Wakeup waiting processes so they don't hang diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b35902e5d925..160aa5f76118 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2223,7 +2223,7 @@ intel_finish_fb(struct drm_framebuffer *old_fb) WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); wait_event(dev_priv->pending_flip_queue, - atomic_read(&dev_priv->mm.wedged) || + atomic_read(&dev_priv->gpu_error.wedged) || atomic_read(&obj->pending_flip) == 0); /* Big Hammer, we also need to ensure that any pending @@ -2871,7 +2871,7 @@ static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) unsigned long flags; bool pending; - if (atomic_read(&dev_priv->mm.wedged)) + if (atomic_read(&dev_priv->gpu_error.wedged)) return false; spin_lock_irqsave(&dev->event_lock, flags); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index d6b06aa4c05c..9438bcd50678 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1371,7 +1371,8 @@ static int ring_wait_for_space(struct intel_ring_buffer *ring, int n) msleep(1); - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); if (ret) return ret; } while (!time_after(jiffies, end)); @@ -1460,7 +1461,8 @@ int intel_ring_begin(struct intel_ring_buffer *ring, drm_i915_private_t *dev_priv = ring->dev->dev_private; int ret; - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); + ret = i915_gem_check_wedge(&dev_priv->gpu_error, + dev_priv->mm.interruptible); if (ret) return ret; -- cgit v1.2.3 From 1f83fee08d625f8d0130f9fe5ef7b17c2e022f3c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Nov 2012 17:17:22 +0100 Subject: drm/i915: clear up wedged transitions We have two important transitions of the wedged state in the current code: - 0 -> 1: This means a hang has been detected, and signals to everyone that they please get of any locks, so that the reset work item can do its job. - 1 -> 0: The reset handler has completed. Now the last transition mixes up two states: "Reset completed and successful" and "Reset failed". To distinguish these two we do some tricks with the reset completion, but I simply could not convince myself that this doesn't race under odd circumstances. Hence split this up, and add a new terminal state indicating that the hw is gone for good. Also add explicit #defines for both states, update comments. v2: Split out the reset handling bugfix for the throttle ioctl. v3: s/tmp/wedged/ sugested by Chris Wilson. Also fixup up a rebase error which prevented this patch from actually compiling. v4: To unify the wedged state with the reset counter, keep the reset-in-progress state just as a flag. The terminally-wedged state is now denoted with a big number. v5: Add a comment to the reset_counter special values explaining that WEDGED & RESET_IN_PROGRESS needs to be true for the code to be correct. v6: Fixup logic errors introduced with the wedged+reset_counter unification. Since WEDGED implies reset-in-progress (in a way we're terminally stuck in the dead-but-reset-not-completed state), we need ensure that we check for this everywhere. The specific bug was in wait_for_error, which would simply have timed out. v7: Extract an inline i915_reset_in_progress helper to make the code more readable. Also annote the reset-in-progress case with an unlikely, to help the compiler optimize the fastpath. Do the same for the terminally wedged case with i915_terminally_wedged. Reviewed-by: Damien Lespiau Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 40 +++++++++++++++++++++++++++-- drivers/gpu/drm/i915/i915_gem.c | 49 +++++++++++++----------------------- drivers/gpu/drm/i915/i915_irq.c | 23 +++++++++++------ drivers/gpu/drm/i915/intel_display.c | 4 +-- 5 files changed, 74 insertions(+), 44 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index e1b7eaf60d24..384f19368a1d 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1672,7 +1672,7 @@ i915_wedged_read(struct file *filp, len = snprintf(buf, sizeof(buf), "wedged : %d\n", - atomic_read(&dev_priv->gpu_error.wedged)); + atomic_read(&dev_priv->gpu_error.reset_counter)); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 62da6c7a4884..c84743bb6937 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -771,11 +771,37 @@ struct i915_gpu_error { /* Protected by the above dev->gpu_error.lock. */ struct drm_i915_error_state *first_error; struct work_struct work; - struct completion completion; unsigned long last_reset; - atomic_t wedged; + /** + * State variable controlling the reset flow + * + * Upper bits are for the reset counter. + * + * Lowest bit controls the reset state machine: Set means a reset is in + * progress. This state will (presuming we don't have any bugs) decay + * into either unset (successful reset) or the special WEDGED value (hw + * terminally sour). All waiters on the reset_queue will be woken when + * that happens. + */ + atomic_t reset_counter; + + /** + * Special values/flags for reset_counter + * + * Note that the code relies on + * I915_WEDGED & I915_RESET_IN_PROGRESS_FLAG + * being true. + */ +#define I915_RESET_IN_PROGRESS_FLAG 1 +#define I915_WEDGED 0xffffffff + + /** + * Waitqueue to signal when the reset has completed. Used by clients + * that wait for dev_priv->mm.wedged to settle. + */ + wait_queue_head_t reset_queue; /* For gpu hang simulation. */ unsigned int stop_rings; @@ -1543,6 +1569,16 @@ void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring); int __must_check i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible); +static inline bool i915_reset_in_progress(struct i915_gpu_error *error) +{ + return unlikely(atomic_read(&error->reset_counter) + & I915_RESET_IN_PROGRESS_FLAG); +} + +static inline bool i915_terminally_wedged(struct i915_gpu_error *error) +{ + return atomic_read(&error->reset_counter) == I915_WEDGED; +} void i915_gem_reset(struct drm_device *dev); void i915_gem_clflush_object(struct drm_i915_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c96a501b8205..2ca901194824 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -89,36 +89,32 @@ static void i915_gem_info_remove_obj(struct drm_i915_private *dev_priv, static int i915_gem_wait_for_error(struct i915_gpu_error *error) { - struct completion *x = &error->completion; - unsigned long flags; int ret; - if (!atomic_read(&error->wedged)) +#define EXIT_COND (!i915_reset_in_progress(error)) + if (EXIT_COND) return 0; + /* GPU is already declared terminally dead, give up. */ + if (i915_terminally_wedged(error)) + return -EIO; + /* * Only wait 10 seconds for the gpu reset to complete to avoid hanging * userspace. If it takes that long something really bad is going on and * we should simply try to bail out and fail as gracefully as possible. */ - ret = wait_for_completion_interruptible_timeout(x, 10*HZ); + ret = wait_event_interruptible_timeout(error->reset_queue, + EXIT_COND, + 10*HZ); if (ret == 0) { DRM_ERROR("Timed out waiting for the gpu reset to complete\n"); return -EIO; } else if (ret < 0) { return ret; } +#undef EXIT_COND - if (atomic_read(&error->wedged)) { - /* GPU is hung, bump the completion count to account for - * the token we just consumed so that we never hit zero and - * end up waiting upon a subsequent completion event that - * will never happen. - */ - spin_lock_irqsave(&x->wait.lock, flags); - x->done++; - spin_unlock_irqrestore(&x->wait.lock, flags); - } return 0; } @@ -942,23 +938,14 @@ int i915_gem_check_wedge(struct i915_gpu_error *error, bool interruptible) { - if (atomic_read(&error->wedged)) { - struct completion *x = &error->completion; - bool recovery_complete; - unsigned long flags; - - /* Give the error handler a chance to run. */ - spin_lock_irqsave(&x->wait.lock, flags); - recovery_complete = x->done > 0; - spin_unlock_irqrestore(&x->wait.lock, flags); - + if (i915_reset_in_progress(error)) { /* Non-interruptible callers can't handle -EAGAIN, hence return * -EIO unconditionally for these. */ if (!interruptible) return -EIO; - /* Recovery complete, but still wedged means reset failure. */ - if (recovery_complete) + /* Recovery complete, but the reset failed ... */ + if (i915_terminally_wedged(error)) return -EIO; return -EAGAIN; @@ -1025,7 +1012,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - atomic_read(&dev_priv->gpu_error.wedged)) + i915_reset_in_progress(&dev_priv->gpu_error)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1379,7 +1366,7 @@ out: /* If this -EIO is due to a gpu hang, give the reset code a * chance to clean up the mess. Otherwise return the proper * SIGBUS. */ - if (!atomic_read(&dev_priv->gpu_error.wedged)) + if (i915_terminally_wedged(&dev_priv->gpu_error)) return VM_FAULT_SIGBUS; case -EAGAIN: /* Give the error handler a chance to run and move the @@ -3983,9 +3970,9 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; - if (atomic_read(&dev_priv->gpu_error.wedged)) { + if (i915_reset_in_progress(&dev_priv->gpu_error)) { DRM_ERROR("Reenabling wedged hardware, good luck\n"); - atomic_set(&dev_priv->gpu_error.wedged, 0); + atomic_set(&dev_priv->gpu_error.reset_counter, 0); } mutex_lock(&dev->struct_mutex); @@ -4069,7 +4056,7 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list); INIT_DELAYED_WORK(&dev_priv->mm.retire_work, i915_gem_retire_work_handler); - init_completion(&dev_priv->gpu_error.completion); + init_waitqueue_head(&dev_priv->gpu_error.reset_queue); /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ if (IS_GEN3(dev)) { diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index f2c0016fa533..4562c5406ef8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -862,8 +862,10 @@ done: */ static void i915_error_work_func(struct work_struct *work) { - drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - gpu_error.work); + struct i915_gpu_error *error = container_of(work, struct i915_gpu_error, + work); + drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t, + gpu_error); struct drm_device *dev = dev_priv->dev; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; @@ -871,14 +873,18 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (atomic_read(&dev_priv->gpu_error.wedged)) { + if (i915_reset_in_progress(error)) { DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); + if (!i915_reset(dev)) { - atomic_set(&dev_priv->gpu_error.wedged, 0); + atomic_set(&error->reset_counter, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); + } else { + atomic_set(&error->reset_counter, I915_WEDGED); } - complete_all(&dev_priv->gpu_error.completion); + + wake_up_all(&dev_priv->gpu_error.reset_queue); } } @@ -1482,11 +1488,12 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - INIT_COMPLETION(dev_priv->gpu_error.completion); - atomic_set(&dev_priv->gpu_error.wedged, 1); + atomic_set(&dev_priv->gpu_error.reset_counter, + I915_RESET_IN_PROGRESS_FLAG); /* - * Wakeup waiting processes so they don't hang + * Wakeup waiting processes so that the reset work item + * doesn't deadlock trying to grab various locks. */ for_each_ring(ring, dev_priv, i) wake_up_all(&ring->irq_queue); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 160aa5f76118..77254460a5cb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2223,7 +2223,7 @@ intel_finish_fb(struct drm_framebuffer *old_fb) WARN_ON(waitqueue_active(&dev_priv->pending_flip_queue)); wait_event(dev_priv->pending_flip_queue, - atomic_read(&dev_priv->gpu_error.wedged) || + i915_reset_in_progress(&dev_priv->gpu_error) || atomic_read(&obj->pending_flip) == 0); /* Big Hammer, we also need to ensure that any pending @@ -2871,7 +2871,7 @@ static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc) unsigned long flags; bool pending; - if (atomic_read(&dev_priv->gpu_error.wedged)) + if (i915_reset_in_progress(&dev_priv->gpu_error)) return false; spin_lock_irqsave(&dev->event_lock, flags); -- cgit v1.2.3 From f69061bedd6ea63f271fe97914364def2f33fc6b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Dec 2012 09:01:42 +0100 Subject: drm/i915: create a race-free reset detection With the previous patch the state transition handling of the reset code itself is now (hopefully) race free and solid. But that still leaves out everyone else - with the various lock-free wait paths we have there's the possibility that the reset happens between the point where we read the seqno we should wait on and the actual wait. And if __wait_seqno then never sees the RESET_IN_PROGRESS state, we'll happily wait for a seqno which will in all likelyhood never signal. In practice this is not a big problem since the X server gets constantly interrupted, and can then submit more work (hopefully) to unblock everyone else: As soon as a new seqno write lands, all waiters will unblock. But running the i-g-t reset testcase ZZ_hangman can expose this race, especially on slower hw with fewer cpu cores. Now looking forward to ARB_robustness and friends that's not the best possible behaviour, hence this patch adds a reset_counter to be able to detect any reset, even if a given thread never observed the in-progress state. The important part is to correctly order things: - The write side needs to increment the counter after any seqno gets reset. Hence we need to do that at the end of the reset work, and again wake everyone up. We also need to place a barrier in between any possible seqno changes and the counter increment, since any unlock operations only guarantee that nothing leaks out, but not that at later load operation gets moved ahead. - On the read side we need to ensure that no reset can sneak in and invalidate the seqno. In all cases we can use the one-sided barrier that unlock operations guarantee (of the lock protecting the respective seqno/ring pair) to ensure correct ordering. Hence it is sufficient to place the atomic read before the mutex/spin_unlock and no additional barriers are required. The end-result of all this is that we need to wake up everyone twice in a reset operation: - First, before the reset starts, to get any lockholders of the locks, so that the reset can proceed. - Second, after the reset is completed, to allow waiters to properly and reliably detect the reset condition and bail out. I admit that this entire reset_counter thing smells a bit like overkill, but I think it's justified since it makes it really explicit what the bail-out condition is. And we need a reset counter anyway to implement ARB_robustness, and imo with finer-grained locking on the horizont this is the most resilient scheme I could think of. v2: Drop spurious change in the wait_for_error EXIT_COND - we only need to wait until we leave the reset-in-progress wedged state. v3: Don't play tricks with barriers in the throttle ioctl, the spin_unlock is barrier enough. I've also considered using a little helper to grab the current reset_counter, but then decided that hiding the atomic_read isn't a great idea, since having it explicitly show up in the code is a nice remainder to reviews to check the memory barriers. v4: Add a comment to explain why we need to fall through in __wait_seqno in the end variable assignments. v5: Review from Damien: - s/smb/smp/ in a comment - don't increment the reset counter after we've set it to WEDGED. Now we (again) properly wedge the gpu when the reset fails. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 11 +++++++++-- drivers/gpu/drm/i915/i915_gem.c | 36 +++++++++++++++++++++++++++++++----- drivers/gpu/drm/i915/i915_irq.c | 30 +++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c84743bb6937..56ece50910a8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -775,9 +775,16 @@ struct i915_gpu_error { unsigned long last_reset; /** - * State variable controlling the reset flow + * State variable and reset counter controlling the reset flow * - * Upper bits are for the reset counter. + * Upper bits are for the reset counter. This counter is used by the + * wait_seqno code to race-free noticed that a reset event happened and + * that it needs to restart the entire ioctl (since most likely the + * seqno it waited for won't ever signal anytime soon). + * + * This is important for lock-free wait paths, where no contended lock + * naturally enforces the correct ordering between the bail-out of the + * waiter and the gpu reset work code. * * Lowest bit controls the reset state machine: Set means a reset is in * progress. This state will (presuming we don't have any bugs) decay diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5bb370fdc99c..5226ebcac33a 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -976,13 +976,22 @@ i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) * __wait_seqno - wait until execution of seqno has finished * @ring: the ring expected to report seqno * @seqno: duh! + * @reset_counter: reset sequence associated with the given seqno * @interruptible: do an interruptible wait (normally yes) * @timeout: in - how long to wait (NULL forever); out - how much time remaining * + * Note: It is of utmost importance that the passed in seqno and reset_counter + * values have been read by the caller in an smp safe manner. Where read-side + * locks are involved, it is sufficient to read the reset_counter before + * unlocking the lock that protects the seqno. For lockless tricks, the + * reset_counter _must_ be read before, and an appropriate smp_rmb must be + * inserted. + * * Returns 0 if the seqno was found within the alloted time. Else returns the * errno with remaining time filled in timeout argument. */ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, + unsigned reset_counter, bool interruptible, struct timespec *timeout) { drm_i915_private_t *dev_priv = ring->dev->dev_private; @@ -1012,7 +1021,8 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, #define EXIT_COND \ (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - i915_reset_in_progress(&dev_priv->gpu_error)) + i915_reset_in_progress(&dev_priv->gpu_error) || \ + reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) do { if (interruptible) end = wait_event_interruptible_timeout(ring->irq_queue, @@ -1022,6 +1032,13 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, end = wait_event_timeout(ring->irq_queue, EXIT_COND, timeout_jiffies); + /* We need to check whether any gpu reset happened in between + * the caller grabbing the seqno and now ... */ + if (reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter)) + end = -EAGAIN; + + /* ... but upgrade the -EGAIN to an -EIO if the gpu is truely + * gone. */ ret = i915_gem_check_wedge(&dev_priv->gpu_error, interruptible); if (ret) end = ret; @@ -1076,7 +1093,9 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) if (ret) return ret; - return __wait_seqno(ring, seqno, interruptible, NULL); + return __wait_seqno(ring, seqno, + atomic_read(&dev_priv->gpu_error.reset_counter), + interruptible, NULL); } /** @@ -1123,6 +1142,7 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring = obj->ring; + unsigned reset_counter; u32 seqno; int ret; @@ -1141,8 +1161,9 @@ i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, if (ret) return ret; + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, NULL); + ret = __wait_seqno(ring, seqno, reset_counter, true, NULL); mutex_lock(&dev->struct_mutex); i915_gem_retire_requests_ring(ring); @@ -2297,10 +2318,12 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj) int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_wait *args = data; struct drm_i915_gem_object *obj; struct intel_ring_buffer *ring = NULL; struct timespec timeout_stack, *timeout = NULL; + unsigned reset_counter; u32 seqno = 0; int ret = 0; @@ -2341,9 +2364,10 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) } drm_gem_object_unreference(&obj->base); + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); mutex_unlock(&dev->struct_mutex); - ret = __wait_seqno(ring, seqno, true, timeout); + ret = __wait_seqno(ring, seqno, reset_counter, true, timeout); if (timeout) { WARN_ON(!timespec_valid(timeout)); args->timeout_ns = timespec_to_ns(timeout); @@ -3394,6 +3418,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) unsigned long recent_enough = jiffies - msecs_to_jiffies(20); struct drm_i915_gem_request *request; struct intel_ring_buffer *ring = NULL; + unsigned reset_counter; u32 seqno = 0; int ret; @@ -3413,12 +3438,13 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) ring = request->ring; seqno = request->seqno; } + reset_counter = atomic_read(&dev_priv->gpu_error.reset_counter); spin_unlock(&file_priv->mm.lock); if (seqno == 0) return 0; - ret = __wait_seqno(ring, seqno, true, NULL); + ret = __wait_seqno(ring, seqno, reset_counter, true, NULL); if (ret == 0) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 4562c5406ef8..f833f2c155f8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -867,9 +867,11 @@ static void i915_error_work_func(struct work_struct *work) drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t, gpu_error); struct drm_device *dev = dev_priv->dev; + struct intel_ring_buffer *ring; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; char *reset_done_event[] = { "ERROR=0", NULL }; + int i, ret; kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); @@ -877,13 +879,31 @@ static void i915_error_work_func(struct work_struct *work) DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); - if (!i915_reset(dev)) { - atomic_set(&error->reset_counter, 0); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); + ret = i915_reset(dev); + + if (ret == 0) { + /* + * After all the gem state is reset, increment the reset + * counter and wake up everyone waiting for the reset to + * complete. + * + * Since unlock operations are a one-sided barrier only, + * we need to insert a barrier here to order any seqno + * updates before + * the counter increment. + */ + smp_mb__before_atomic_inc(); + atomic_inc(&dev_priv->gpu_error.reset_counter); + + kobject_uevent_env(&dev->primary->kdev.kobj, + KOBJ_CHANGE, reset_done_event); } else { atomic_set(&error->reset_counter, I915_WEDGED); } + for_each_ring(ring, dev_priv, i) + wake_up_all(&ring->irq_queue); + wake_up_all(&dev_priv->gpu_error.reset_queue); } } @@ -1488,8 +1508,8 @@ void i915_handle_error(struct drm_device *dev, bool wedged) i915_report_and_clear_eir(dev); if (wedged) { - atomic_set(&dev_priv->gpu_error.reset_counter, - I915_RESET_IN_PROGRESS_FLAG); + atomic_set_mask(I915_RESET_IN_PROGRESS_FLAG, + &dev_priv->gpu_error.reset_counter); /* * Wakeup waiting processes so that the reset work item -- cgit v1.2.3 From 7db0ba242b3a7ddcfc1450bc50eb3102c68f0244 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 6 Dec 2012 16:23:37 +0100 Subject: drm/i915: clarify concurrent hang detect/gpu reset consistency Damien Lespiau wondered how race the gpu reset/hang detection code is against concurrent gpu resets/hang detections or combinations thereof. Luckily the single work item is guranteed to never run concurrently, so reset handling is already single-threaded. Hence we only have to worry about concurrent hang detections, or a hang detection firing off while we're still processing an older gpu reset request. Due to the new mechanism of setting the reset in progress flag and the ordering guaranteed by the schedule_work function there's nothing to do but add a comment explaining why we're safe. The only thing I've noticed is that we still try to reset the gpu now, even when it is declared terminally wedged. Add a check for that to avoid continous warnings about failed resets, in case the hangcheck timer ever gets stuck. Reviewed-by: Damien Lespiau Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index f833f2c155f8..943db1001cfd 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -875,9 +875,20 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); - if (i915_reset_in_progress(error)) { + /* + * Note that there's only one work item which does gpu resets, so we + * need not worry about concurrent gpu resets potentially incrementing + * error->reset_counter twice. We only need to take care of another + * racing irq/hangcheck declaring the gpu dead for a second time. A + * quick check for that is good enough: schedule_work ensures the + * correct ordering between hang detection and this work item, and since + * the reset in-progress bit is only ever set by code outside of this + * work we don't need to worry about any other races. + */ + if (i915_reset_in_progress(error) && !i915_terminally_wedged(error)) { DRM_DEBUG_DRIVER("resetting chip\n"); - kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); + kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, + reset_event); ret = i915_reset(dev); -- cgit v1.2.3 From 2f86f1916504525a6fdd6b412374b4ebf1102cbe Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 28 Jan 2013 15:32:15 -0800 Subject: drm/i915: Error state should print /sys/kernel/debug /sys/kernel/debug has more or less been the standard location of debugfs for several years now. Other parts of DRM already use this location, so we should as well. Signed-off-by: Ben Widawsky Reviewed-by: Carl Worth Reviewed-by: Damien Lespiau [danvet: split up long line.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 943db1001cfd..5648d846cdbf 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1305,7 +1305,8 @@ static void i915_capture_error_state(struct drm_device *dev) return; } - DRM_INFO("capturing error event; look for more information in /debug/dri/%d/i915_error_state\n", + DRM_INFO("capturing error event; look for more information in" + "/sys/kernel/debug/dri/%d/i915_error_state\n", dev->primary->index); kref_init(&error->ref); -- cgit v1.2.3 From 26739f12cf210cb8df35969258a1f064e8e12b63 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 7 Feb 2013 12:42:32 +0100 Subject: drm/i915: unify HDMI/DP hpd definitions They're physically the same pins and also the same bits, duplicating only confuses the reader. This also makes it a bit obvious that we have quite some code duplication going on here. Squashing that is for a larger rework in our hpd handling though. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 36 ++++++++++++++++++------------------ drivers/gpu/drm/i915/i915_reg.h | 28 +++++++++------------------- drivers/gpu/drm/i915/intel_dp.c | 12 ++++++------ drivers/gpu/drm/i915/intel_hdmi.c | 10 +++++----- 4 files changed, 38 insertions(+), 48 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 13bb8d3f2a77..b27ffdbc98a7 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2137,12 +2137,12 @@ static void valleyview_hpd_irq_setup(struct drm_device *dev) u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN); /* Note HDMI and DP share bits */ - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) hotplug_en |= SDVOC_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) @@ -2408,12 +2408,12 @@ static void i915_hpd_irq_setup(struct drm_device *dev) if (I915_HAS_HOTPLUG(dev)) { hotplug_en = I915_READ(PORT_HOTPLUG_EN); - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_I915) hotplug_en |= SDVOC_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS_I915) @@ -2642,12 +2642,12 @@ static void i965_hpd_irq_setup(struct drm_device *dev) /* Note HDMI and DP share hotplug bits */ hotplug_en = 0; - if (dev_priv->hotplug_supported_mask & HDMIB_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMIC_HOTPLUG_INT_STATUS) - hotplug_en |= HDMIC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & HDMID_HOTPLUG_INT_STATUS) - hotplug_en |= HDMID_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTB_HOTPLUG_INT_STATUS) + hotplug_en |= PORTB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTC_HOTPLUG_INT_STATUS) + hotplug_en |= PORTC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & PORTD_HOTPLUG_INT_STATUS) + hotplug_en |= PORTD_HOTPLUG_INT_EN; if (IS_G4X(dev)) { if (dev_priv->hotplug_supported_mask & SDVOC_HOTPLUG_INT_STATUS_G4X) hotplug_en |= SDVOC_HOTPLUG_INT_EN; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 8754f9160aef..b9fdc9963817 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1625,12 +1625,9 @@ /* Hotplug control (945+ only) */ #define PORT_HOTPLUG_EN (dev_priv->info->display_mmio_offset + 0x61110) -#define HDMIB_HOTPLUG_INT_EN (1 << 29) -#define DPB_HOTPLUG_INT_EN (1 << 29) -#define HDMIC_HOTPLUG_INT_EN (1 << 28) -#define DPC_HOTPLUG_INT_EN (1 << 28) -#define HDMID_HOTPLUG_INT_EN (1 << 27) -#define DPD_HOTPLUG_INT_EN (1 << 27) +#define PORTB_HOTPLUG_INT_EN (1 << 29) +#define PORTC_HOTPLUG_INT_EN (1 << 28) +#define PORTD_HOTPLUG_INT_EN (1 << 27) #define SDVOB_HOTPLUG_INT_EN (1 << 26) #define SDVOC_HOTPLUG_INT_EN (1 << 25) #define TV_HOTPLUG_INT_EN (1 << 18) @@ -1653,19 +1650,12 @@ #define PORT_HOTPLUG_STAT (dev_priv->info->display_mmio_offset + 0x61114) /* HDMI/DP bits are gen4+ */ -#define DPB_HOTPLUG_LIVE_STATUS (1 << 29) -#define DPC_HOTPLUG_LIVE_STATUS (1 << 28) -#define DPD_HOTPLUG_LIVE_STATUS (1 << 27) -#define DPD_HOTPLUG_INT_STATUS (3 << 21) -#define DPC_HOTPLUG_INT_STATUS (3 << 19) -#define DPB_HOTPLUG_INT_STATUS (3 << 17) -/* HDMI bits are shared with the DP bits */ -#define HDMIB_HOTPLUG_LIVE_STATUS (1 << 29) -#define HDMIC_HOTPLUG_LIVE_STATUS (1 << 28) -#define HDMID_HOTPLUG_LIVE_STATUS (1 << 27) -#define HDMID_HOTPLUG_INT_STATUS (3 << 21) -#define HDMIC_HOTPLUG_INT_STATUS (3 << 19) -#define HDMIB_HOTPLUG_INT_STATUS (3 << 17) +#define PORTB_HOTPLUG_LIVE_STATUS (1 << 29) +#define PORTC_HOTPLUG_LIVE_STATUS (1 << 28) +#define PORTD_HOTPLUG_LIVE_STATUS (1 << 27) +#define PORTD_HOTPLUG_INT_STATUS (3 << 21) +#define PORTC_HOTPLUG_INT_STATUS (3 << 19) +#define PORTB_HOTPLUG_INT_STATUS (3 << 17) /* CRT/TV common between gen3+ */ #define CRT_HOTPLUG_INT_STATUS (1 << 11) #define TV_HOTPLUG_INT_STATUS (1 << 10) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 56e408b2e3c8..7b8bfe8982e6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2302,13 +2302,13 @@ g4x_dp_detect(struct intel_dp *intel_dp) switch (intel_dig_port->port) { case PORT_B: - bit = DPB_HOTPLUG_LIVE_STATUS; + bit = PORTB_HOTPLUG_LIVE_STATUS; break; case PORT_C: - bit = DPC_HOTPLUG_LIVE_STATUS; + bit = PORTC_HOTPLUG_LIVE_STATUS; break; case PORT_D: - bit = DPD_HOTPLUG_LIVE_STATUS; + bit = PORTD_HOTPLUG_LIVE_STATUS; break; default: return connector_status_unknown; @@ -2838,15 +2838,15 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, name = "DPDDC-A"; break; case PORT_B: - dev_priv->hotplug_supported_mask |= DPB_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; name = "DPDDC-B"; break; case PORT_C: - dev_priv->hotplug_supported_mask |= DPC_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; name = "DPDDC-C"; break; case PORT_D: - dev_priv->hotplug_supported_mask |= DPD_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; name = "DPDDC-D"; break; default: diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 5b4efd64c2f9..5a6138c62fe9 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -802,10 +802,10 @@ static bool g4x_hdmi_connected(struct intel_hdmi *intel_hdmi) switch (intel_dig_port->port) { case PORT_B: - bit = HDMIB_HOTPLUG_LIVE_STATUS; + bit = PORTB_HOTPLUG_LIVE_STATUS; break; case PORT_C: - bit = HDMIC_HOTPLUG_LIVE_STATUS; + bit = PORTC_HOTPLUG_LIVE_STATUS; break; default: bit = 0; @@ -1022,15 +1022,15 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port, switch (port) { case PORT_B: intel_hdmi->ddc_bus = GMBUS_PORT_DPB; - dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTB_HOTPLUG_INT_STATUS; break; case PORT_C: intel_hdmi->ddc_bus = GMBUS_PORT_DPC; - dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTC_HOTPLUG_INT_STATUS; break; case PORT_D: intel_hdmi->ddc_bus = GMBUS_PORT_DPD; - dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; + dev_priv->hotplug_supported_mask |= PORTD_HOTPLUG_INT_STATUS; break; case PORT_A: /* Internal port only for eDP. */ -- cgit v1.2.3 From d46da4377689bd938795e53c4e2fb54dbcaeea44 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 8 Feb 2013 17:35:15 -0200 Subject: drm/i915: add ibx_irq_postinstall So we can remove duplicated code. Note that this function is used not only on IBX, but also CPT and LPT. Signed-off-by: Paulo Zanoni Reviewed-by: Ben Widawsky [danvet: Also bikeshed s/ironlake_enable_pch_hotplug/ibx_enable_hotplug to keep consistent with our ibx for pch naming scheme.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 68 +++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 43 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b27ffdbc98a7..2cd97d1cc920 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1924,7 +1924,7 @@ static void valleyview_irq_preinstall(struct drm_device *dev) * This register is the same on all known PCH chips. */ -static void ironlake_enable_pch_hotplug(struct drm_device *dev) +static void ibx_enable_hotplug(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 hotplug; @@ -1937,6 +1937,28 @@ static void ironlake_enable_pch_hotplug(struct drm_device *dev) I915_WRITE(PCH_PORT_HOTPLUG, hotplug); } +static void ibx_irq_postinstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 mask; + + if (HAS_PCH_IBX(dev)) + mask = SDE_HOTPLUG_MASK | + SDE_GMBUS | + SDE_AUX_MASK; + else + mask = SDE_HOTPLUG_MASK_CPT | + SDE_GMBUS_CPT | + SDE_AUX_MASK_CPT; + + I915_WRITE(SDEIIR, I915_READ(SDEIIR)); + I915_WRITE(SDEIMR, ~mask); + I915_WRITE(SDEIER, mask); + POSTING_READ(SDEIER); + + ibx_enable_hotplug(dev); +} + static int ironlake_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -1945,8 +1967,6 @@ static int ironlake_irq_postinstall(struct drm_device *dev) DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE | DE_AUX_CHANNEL_A; u32 render_irqs; - u32 hotplug_mask; - u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -1974,30 +1994,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev) I915_WRITE(GTIER, render_irqs); POSTING_READ(GTIER); - if (HAS_PCH_CPT(dev)) { - hotplug_mask = (SDE_CRT_HOTPLUG_CPT | - SDE_PORTB_HOTPLUG_CPT | - SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT | - SDE_AUX_MASK_CPT); - } else { - hotplug_mask = (SDE_CRT_HOTPLUG | - SDE_PORTB_HOTPLUG | - SDE_PORTC_HOTPLUG | - SDE_PORTD_HOTPLUG | - SDE_GMBUS | - SDE_AUX_MASK); - } - - pch_irq_mask = ~hotplug_mask; - - I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, pch_irq_mask); - I915_WRITE(SDEIER, hotplug_mask); - POSTING_READ(SDEIER); - - ironlake_enable_pch_hotplug(dev); + ibx_irq_postinstall(dev); if (IS_IRONLAKE_M(dev)) { /* Clear & enable PCU event interrupts */ @@ -2020,8 +2017,6 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB; u32 render_irqs; - u32 hotplug_mask; - u32 pch_irq_mask; dev_priv->irq_mask = ~display_mask; @@ -2045,20 +2040,7 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) I915_WRITE(GTIER, render_irqs); POSTING_READ(GTIER); - hotplug_mask = (SDE_CRT_HOTPLUG_CPT | - SDE_PORTB_HOTPLUG_CPT | - SDE_PORTC_HOTPLUG_CPT | - SDE_PORTD_HOTPLUG_CPT | - SDE_GMBUS_CPT | - SDE_AUX_MASK_CPT); - pch_irq_mask = ~hotplug_mask; - - I915_WRITE(SDEIIR, I915_READ(SDEIIR)); - I915_WRITE(SDEIMR, pch_irq_mask); - I915_WRITE(SDEIER, hotplug_mask); - POSTING_READ(SDEIER); - - ironlake_enable_pch_hotplug(dev); + ibx_irq_postinstall(dev); return 0; } -- cgit v1.2.3 From 44498aea293b37af1d463acd9658cdce1ecdf427 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 22 Feb 2013 17:05:28 -0300 Subject: drm/i915: also disable south interrupts when handling them From the docs: "IIR can queue up to two interrupt events. When the IIR is cleared, it will set itself again after one clock if a second event was stored." "Only the rising edge of the PCH Display interrupt will cause the North Display IIR (DEIIR) PCH Display Interrupt even bit to be set, so all PCH Display Interrupts, including back to back interrupts, must be cleared before a new PCH Display interrupt can cause DEIIR to be set". The current code works fine because we don't get many interrupts, but if we enable the PCH FIFO underrun interrupts we'll start getting so many interrupts that at some point new PCH interrupts won't cause DEIIR to be set. The initial implementation I tried was to turn the code that checks SDEIIR into a loop, but we can still get interrupts even after the loop is done (and before the irq handler finishes), so we have to either disable the interrupts or mask them. In the end I concluded that just disabling the PCH interrupts is enough, you don't even need the loop, so this is what this patch implements. I've tested it and it passes the 2 "PCH FIFO underrun interrupt storms" I can reproduce: the "ironlake_crtc_disable" case and the "wrong watermarks" case. In other words, here's how to reproduce the problem fixed by this patch: 1 - Enable PCH FIFO underrun interrupts (SERR_INT on SNB+) 2 - Boot the machine 3 - While booting we'll get tons of PCH FIFO underrun interrupts 4 - Plug a new monitor 5 - Run xrandr, notice it won't detect the new monitor 6 - Read SDEIIR and notice it's not 0 while DEIIR is 0 Q: Can't we just clear DEIIR before SDEIIR? A: It doesn't work. SDEIIR has to be completely cleared (including the interrupts stored on its back queue) before it can flip DEIIR's bit to 1 again, and even while you're clearing it you'll be getting more and more interrupts. Q: Why does it work by just disabling+enabling the south interrupts? A: Because when we re-enable them, if there's something on the SDEIIR register (maybe an interrupt stored on the queue), the re-enabling will make DEIIR's bit flip to 1, and since we'll already have interrupts enabled we'll get another interrupt, then run our irq handler again to process the "back" interrupts. v2: Even bigger commit message, added code comments. Note that this fixes missed dp aux irqs which have been reported for 3.9-rc1. This regression has been introduced by switching to irq-driven dp aux transactions with commit 9ee32fea5fe810ec06af3a15e4c65478de56d4f5 Author: Daniel Vetter Date: Sat Dec 1 13:53:48 2012 +0100 drm/i915: irq-drive the dp aux communication References: http://www.mail-archive.com/intel-gfx@lists.freedesktop.org/msg18588.html References: https://lkml.org/lkml/2013/2/26/769 Tested-by: Imre Deak Reported-by: Sedat Dilek Reported-by: Linus Torvalds Signed-off-by: Paulo Zanoni [danvet: Pimp commit message with references for the dp aux irq timeout regression this fixes.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/i915_irq.c') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2cd97d1cc920..3c7bb0410b51 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -701,7 +701,7 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 de_iir, gt_iir, de_ier, pm_iir; + u32 de_iir, gt_iir, de_ier, pm_iir, sde_ier; irqreturn_t ret = IRQ_NONE; int i; @@ -711,6 +711,15 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) de_ier = I915_READ(DEIER); I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); + /* Disable south interrupts. We'll only write to SDEIIR once, so further + * interrupts will will be stored on its back queue, and then we'll be + * able to process them after we restore SDEIER (as soon as we restore + * it, we'll get an interrupt if SDEIIR still has something to process + * due to its back queue). */ + sde_ier = I915_READ(SDEIER); + I915_WRITE(SDEIER, 0); + POSTING_READ(SDEIER); + gt_iir = I915_READ(GTIIR); if (gt_iir) { snb_gt_irq_handler(dev, dev_priv, gt_iir); @@ -759,6 +768,8 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) I915_WRITE(DEIER, de_ier); POSTING_READ(DEIER); + I915_WRITE(SDEIER, sde_ier); + POSTING_READ(SDEIER); return ret; } @@ -778,7 +789,7 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = IRQ_NONE; - u32 de_iir, gt_iir, de_ier, pm_iir; + u32 de_iir, gt_iir, de_ier, pm_iir, sde_ier; atomic_inc(&dev_priv->irq_received); @@ -787,6 +798,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); POSTING_READ(DEIER); + /* Disable south interrupts. We'll only write to SDEIIR once, so further + * interrupts will will be stored on its back queue, and then we'll be + * able to process them after we restore SDEIER (as soon as we restore + * it, we'll get an interrupt if SDEIIR still has something to process + * due to its back queue). */ + sde_ier = I915_READ(SDEIER); + I915_WRITE(SDEIER, 0); + POSTING_READ(SDEIER); + de_iir = I915_READ(DEIIR); gt_iir = I915_READ(GTIIR); pm_iir = I915_READ(GEN6_PMIIR); @@ -849,6 +869,8 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) done: I915_WRITE(DEIER, de_ier); POSTING_READ(DEIER); + I915_WRITE(SDEIER, sde_ier); + POSTING_READ(SDEIER); return ret; } -- cgit v1.2.3