From 083f9560cdff268e2ca82dc90ba9c509742359b8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 20 Apr 2012 20:23:49 +0200 Subject: [PATCH 01/88] drm/i915: print computed bpp in dp link configuration Pretty useful to debug our DP bandwidth woes. v2: Also print out the required and available link bandwidth, suggested by Chris Wilson. v3: Also print out the input parameters so that diagnosing failures to find a valid dp link configuration is possible. v4: s/Display port/DP/ to shorten the output. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_dp.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 44cf32c8bcbf..22c66b7c7e4a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -688,7 +688,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, int lane_count, clock; int max_lane_count = intel_dp_max_lane_count(intel_dp); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; - int bpp; + int bpp, mode_rate; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { @@ -702,24 +702,30 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, mode->clock = intel_dp->panel_fixed_mode->clock; } + DRM_DEBUG_KMS("DP link computation with max lane count %i " + "max bw %02x pixel clock %iKHz\n", + max_lane_count, bws[max_clock], mode->clock); + if (!intel_dp_adjust_dithering(intel_dp, mode, adjusted_mode)) return false; bpp = adjusted_mode->private_flags & INTEL_MODE_DP_FORCE_6BPC ? 18 : 24; + mode_rate = intel_dp_link_required(mode->clock, bpp); for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { for (clock = 0; clock <= max_clock; clock++) { int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); - if (intel_dp_link_required(mode->clock, bpp) - <= link_avail) { + if (mode_rate <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw); - DRM_DEBUG_KMS("Display port link bw %02x lane " - "count %d clock %d\n", + DRM_DEBUG_KMS("DP link bw %02x lane " + "count %d clock %d bpp %d\n", intel_dp->link_bw, intel_dp->lane_count, - adjusted_mode->clock); + adjusted_mode->clock, bpp); + DRM_DEBUG_KMS("DP link bw required %i available %i\n", + mode_rate, link_avail); return true; } } From 828ed3e1703a2323250f4acdac34f79fd27364d9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 18 Apr 2012 17:12:26 +0100 Subject: [PATCH 02/88] drm/i915/sprite: Avoid incurring extra vblank stall when updating plane on IVB IvyBridge requires an extra frame between disabling the low power watermarks and enabling scaling on the sprite plane. If the scaling is already enabled, then we have already disabled the low power watermarks and need not incur an extra wait. Similarly, as we disable the scaling when turning off the sprite plane, we can update the scaling enabled flag and restore the low power watermarks. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sprite.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index fbf03b996587..44315674f472 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -110,14 +110,18 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, * when scaling is disabled. */ if (crtc_w != src_w || crtc_h != src_h) { - dev_priv->sprite_scaling_enabled = true; - intel_update_watermarks(dev); - intel_wait_for_vblank(dev, pipe); + if (!dev_priv->sprite_scaling_enabled) { + dev_priv->sprite_scaling_enabled = true; + intel_update_watermarks(dev); + intel_wait_for_vblank(dev, pipe); + } sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; } else { - dev_priv->sprite_scaling_enabled = false; - /* potentially re-enable LP watermarks */ - intel_update_watermarks(dev); + if (dev_priv->sprite_scaling_enabled) { + dev_priv->sprite_scaling_enabled = false; + /* potentially re-enable LP watermarks */ + intel_update_watermarks(dev); + } } I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); @@ -151,6 +155,9 @@ ivb_disable_plane(struct drm_plane *plane) /* Activate double buffered register update */ I915_MODIFY_DISPBASE(SPRSURF(pipe), 0); POSTING_READ(SPRSURF(pipe)); + + dev_priv->sprite_scaling_enabled = false; + intel_update_watermarks(dev); } static int From 92d68ed7e57e5534c8c82ab28da194b73acb2710 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 19 Apr 2012 17:55:23 +0100 Subject: [PATCH 03/88] drm/i915: Remove i915_gem_ringbuffer_(data|info) from debugfs This was originally used as an attempt to diagnose GPU hangs, but was never very reliable and superseded by the i915_error_state capture on hangcheck. It now lies languishing unused and unwanted. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 69 ----------------------------- 1 file changed, 69 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 35462df7cefd..8c899a2237ff 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -602,69 +602,6 @@ static int i915_hws_info(struct seq_file *m, void *data) return 0; } -static int i915_ringbuffer_data(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - drm_i915_private_t *dev_priv = dev->dev_private; - struct intel_ring_buffer *ring; - int ret; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - - ring = &dev_priv->ring[(uintptr_t)node->info_ent->data]; - if (!ring->obj) { - seq_printf(m, "No ringbuffer setup\n"); - } else { - const u8 __iomem *virt = ring->virtual_start; - uint32_t off; - - for (off = 0; off < ring->size; off += 4) { - uint32_t *ptr = (uint32_t *)(virt + off); - seq_printf(m, "%08x : %08x\n", off, *ptr); - } - } - mutex_unlock(&dev->struct_mutex); - - return 0; -} - -static int i915_ringbuffer_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - drm_i915_private_t *dev_priv = dev->dev_private; - struct intel_ring_buffer *ring; - int ret; - - ring = &dev_priv->ring[(uintptr_t)node->info_ent->data]; - if (ring->size == 0) - return 0; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - - seq_printf(m, "Ring %s:\n", ring->name); - seq_printf(m, " Head : %08x\n", I915_READ_HEAD(ring) & HEAD_ADDR); - seq_printf(m, " Tail : %08x\n", I915_READ_TAIL(ring) & TAIL_ADDR); - seq_printf(m, " Size : %08x\n", ring->size); - seq_printf(m, " Active : %08x\n", intel_ring_get_active_head(ring)); - seq_printf(m, " NOPID : %08x\n", I915_READ_NOPID(ring)); - if (IS_GEN6(dev) || IS_GEN7(dev)) { - seq_printf(m, " Sync 0 : %08x\n", I915_READ_SYNC_0(ring)); - seq_printf(m, " Sync 1 : %08x\n", I915_READ_SYNC_1(ring)); - } - seq_printf(m, " Control : %08x\n", I915_READ_CTL(ring)); - seq_printf(m, " Start : %08x\n", I915_READ_START(ring)); - - mutex_unlock(&dev->struct_mutex); - - return 0; -} - static const char *ring_str(int ring) { switch (ring) { @@ -1910,12 +1847,6 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws", i915_hws_info, 0, (void *)RCS}, {"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS}, {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS}, - {"i915_ringbuffer_data", i915_ringbuffer_data, 0, (void *)RCS}, - {"i915_ringbuffer_info", i915_ringbuffer_info, 0, (void *)RCS}, - {"i915_bsd_ringbuffer_data", i915_ringbuffer_data, 0, (void *)VCS}, - {"i915_bsd_ringbuffer_info", i915_ringbuffer_info, 0, (void *)VCS}, - {"i915_blt_ringbuffer_data", i915_ringbuffer_data, 0, (void *)BCS}, - {"i915_blt_ringbuffer_info", i915_ringbuffer_info, 0, (void *)BCS}, {"i915_error_state", i915_error_state, 0}, {"i915_rstdby_delays", i915_rstdby_delays, 0}, {"i915_cur_delayinfo", i915_cur_delayinfo, 0}, From 5bc4418b557d3f56918ff21b0bd12467eb2e94aa Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 16 Apr 2012 14:07:42 -0700 Subject: [PATCH 04/88] drm/i915: [sparse] __iomem fixes for opregion Almost all of the errors related __iomem problems. Most of the changes here are trivial, however there is plenty of chance for yank/paste errors. Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 10 ++-- drivers/gpu/drm/i915/intel_opregion.c | 67 +++++++++++++++------------ 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 69e153956182..4cbc45f0424f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -122,11 +122,11 @@ struct opregion_asle; struct drm_i915_private; struct intel_opregion { - struct opregion_header *header; - struct opregion_acpi *acpi; - struct opregion_swsci *swsci; - struct opregion_asle *asle; - void *vbt; + struct opregion_header __iomem *header; + struct opregion_acpi __iomem *acpi; + struct opregion_swsci __iomem *swsci; + struct opregion_asle __iomem *asle; + void __iomem *vbt; u32 __iomem *lid_state; }; #define OPREGION_SIZE (8*1024) diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 34929aeca66b..18bd0af855dc 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -151,7 +151,7 @@ struct opregion_asle { static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) { struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle *asle = dev_priv->opregion.asle; + struct opregion_asle __iomem *asle = dev_priv->opregion.asle; u32 max; if (!(bclp & ASLE_BCLP_VALID)) @@ -163,7 +163,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) max = intel_panel_get_max_backlight(dev); intel_panel_set_backlight(dev, bclp * max / 255); - asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; + iowrite32((bclp*0x64)/0xff | ASLE_CBLV_VALID, &asle->cblv); return 0; } @@ -200,14 +200,14 @@ static u32 asle_set_pfit(struct drm_device *dev, u32 pfit) void intel_opregion_asle_intr(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle *asle = dev_priv->opregion.asle; + struct opregion_asle __iomem *asle = dev_priv->opregion.asle; u32 asle_stat = 0; u32 asle_req; if (!asle) return; - asle_req = asle->aslc & ASLE_REQ_MSK; + asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK; if (!asle_req) { DRM_DEBUG_DRIVER("non asle set request??\n"); @@ -215,31 +215,31 @@ void intel_opregion_asle_intr(struct drm_device *dev) } if (asle_req & ASLE_SET_ALS_ILLUM) - asle_stat |= asle_set_als_illum(dev, asle->alsi); + asle_stat |= asle_set_als_illum(dev, ioread32(&asle->alsi)); if (asle_req & ASLE_SET_BACKLIGHT) - asle_stat |= asle_set_backlight(dev, asle->bclp); + asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp)); if (asle_req & ASLE_SET_PFIT) - asle_stat |= asle_set_pfit(dev, asle->pfit); + asle_stat |= asle_set_pfit(dev, ioread32(&asle->pfit)); if (asle_req & ASLE_SET_PWM_FREQ) - asle_stat |= asle_set_pwm_freq(dev, asle->pfmb); + asle_stat |= asle_set_pwm_freq(dev, ioread32(&asle->pfmb)); - asle->aslc = asle_stat; + iowrite32(asle_stat, &asle->aslc); } void intel_opregion_gse_intr(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle *asle = dev_priv->opregion.asle; + struct opregion_asle __iomem *asle = dev_priv->opregion.asle; u32 asle_stat = 0; u32 asle_req; if (!asle) return; - asle_req = asle->aslc & ASLE_REQ_MSK; + asle_req = ioread32(&asle->aslc) & ASLE_REQ_MSK; if (!asle_req) { DRM_DEBUG_DRIVER("non asle set request??\n"); @@ -252,7 +252,7 @@ void intel_opregion_gse_intr(struct drm_device *dev) } if (asle_req & ASLE_SET_BACKLIGHT) - asle_stat |= asle_set_backlight(dev, asle->bclp); + asle_stat |= asle_set_backlight(dev, ioread32(&asle->bclp)); if (asle_req & ASLE_SET_PFIT) { DRM_DEBUG_DRIVER("Pfit is not supported\n"); @@ -264,7 +264,7 @@ void intel_opregion_gse_intr(struct drm_device *dev) asle_stat |= ASLE_PWM_FREQ_FAILED; } - asle->aslc = asle_stat; + iowrite32(asle_stat, &asle->aslc); } #define ASLE_ALS_EN (1<<0) #define ASLE_BLC_EN (1<<1) @@ -274,15 +274,16 @@ void intel_opregion_gse_intr(struct drm_device *dev) void intel_opregion_enable_asle(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - struct opregion_asle *asle = dev_priv->opregion.asle; + struct opregion_asle __iomem *asle = dev_priv->opregion.asle; if (asle) { if (IS_MOBILE(dev)) intel_enable_asle(dev); - asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | - ASLE_PFMB_EN; - asle->ardy = 1; + iowrite32(ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | + ASLE_PFMB_EN, + &asle->tche); + iowrite32(1, &asle->ardy); } } @@ -300,7 +301,7 @@ static int intel_opregion_video_event(struct notifier_block *nb, Linux, these are handled by the dock, button and video drivers. */ - struct opregion_acpi *acpi; + struct opregion_acpi __iomem *acpi; struct acpi_bus_event *event = data; int ret = NOTIFY_OK; @@ -312,10 +313,11 @@ static int intel_opregion_video_event(struct notifier_block *nb, acpi = system_opregion->acpi; - if (event->type == 0x80 && !(acpi->cevt & 0x1)) + if (event->type == 0x80 && + (ioread32(&acpi->cevt) & 1) == 0) ret = NOTIFY_BAD; - acpi->csts = 0; + iowrite32(0, &acpi->csts); return ret; } @@ -339,6 +341,7 @@ static void intel_didl_outputs(struct drm_device *dev) struct acpi_device *acpi_dev, *acpi_cdev, *acpi_video_bus = NULL; unsigned long long device_id; acpi_status status; + u32 temp; int i = 0; handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev); @@ -373,7 +376,8 @@ static void intel_didl_outputs(struct drm_device *dev) if (ACPI_SUCCESS(status)) { if (!device_id) goto blind_set; - opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f); + iowrite32((u32)(device_id & 0x0f0f), + &opregion->acpi->didl[i]); i++; } } @@ -381,7 +385,7 @@ static void intel_didl_outputs(struct drm_device *dev) end: /* If fewer than 8 outputs, the list must be null terminated */ if (i < 8) - opregion->acpi->didl[i] = 0; + iowrite32(0, &opregion->acpi->didl[i]); return; blind_set: @@ -415,7 +419,9 @@ static void intel_didl_outputs(struct drm_device *dev) output_type = ACPI_LVDS_OUTPUT; break; } - opregion->acpi->didl[i] |= (1<<31) | output_type | i; + temp = ioread32(&opregion->acpi->didl[i]); + iowrite32(temp | (1<<31) | output_type | i, + &opregion->acpi->didl[i]); i++; } goto end; @@ -436,8 +442,8 @@ void intel_opregion_init(struct drm_device *dev) /* Notify BIOS we are ready to handle ACPI video ext notifs. * Right now, all the events are handled by the ACPI video module. * We don't actually need to do anything with them. */ - opregion->acpi->csts = 0; - opregion->acpi->drdy = 1; + iowrite32(0, &opregion->acpi->csts); + iowrite32(1, &opregion->acpi->drdy); system_opregion = opregion; register_acpi_notifier(&intel_opregion_notifier); @@ -456,7 +462,7 @@ void intel_opregion_fini(struct drm_device *dev) return; if (opregion->acpi) { - opregion->acpi->drdy = 0; + iowrite32(0, &opregion->acpi->drdy); system_opregion = NULL; unregister_acpi_notifier(&intel_opregion_notifier); @@ -476,8 +482,9 @@ int intel_opregion_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; - void *base; + void __iomem *base; u32 asls, mboxes; + char buf[sizeof(OPREGION_SIGNATURE)]; int err = 0; pci_read_config_dword(dev->pdev, PCI_ASLS, &asls); @@ -491,7 +498,9 @@ int intel_opregion_setup(struct drm_device *dev) if (!base) return -ENOMEM; - if (memcmp(base, OPREGION_SIGNATURE, 16)) { + memcpy_fromio(buf, base, sizeof(buf)); + + if (memcmp(buf, OPREGION_SIGNATURE, 16)) { DRM_DEBUG_DRIVER("opregion signature mismatch\n"); err = -EINVAL; goto err_out; @@ -501,7 +510,7 @@ int intel_opregion_setup(struct drm_device *dev) opregion->lid_state = base + ACPI_CLID; - mboxes = opregion->header->mboxes; + mboxes = ioread32(&opregion->header->mboxes); if (mboxes & MBOX_ACPI) { DRM_DEBUG_DRIVER("Public ACPI methods supported\n"); opregion->acpi = base + OPREGION_ACPI_OFFSET; From 75020bc11c2fa4c060d45b8d0e3f6a37109725bc Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 16 Apr 2012 14:07:43 -0700 Subject: [PATCH 05/88] drm/i915: [sparse] __iomem fixes for overlay With the exception of a forced cast for phys_obj stuff (a problem in other patches as well) all of these are fairly simple __iomem compliance fixes. As with other patches, yank/paste errors may exist. Signed-off-by: Ben Widawsky [danvet: Added comment to explain the __iomem cast.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_overlay.c | 133 +++++++++++++++------------ 1 file changed, 75 insertions(+), 58 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 80b331c322fb..0f0fe31fc466 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -187,14 +187,14 @@ struct intel_overlay { void (*flip_tail)(struct intel_overlay *); }; -static struct overlay_registers * +static struct overlay_registers __iomem * intel_overlay_map_regs(struct intel_overlay *overlay) { drm_i915_private_t *dev_priv = overlay->dev->dev_private; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) - regs = overlay->reg_bo->phys_obj->handle->vaddr; + regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_obj->handle->vaddr; else regs = io_mapping_map_wc(dev_priv->mm.gtt_mapping, overlay->reg_bo->gtt_offset); @@ -203,7 +203,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay) } static void intel_overlay_unmap_regs(struct intel_overlay *overlay, - struct overlay_registers *regs) + struct overlay_registers __iomem *regs) { if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev)) io_mapping_unmap(regs); @@ -619,14 +619,15 @@ static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = { 0x3000, 0x0800, 0x3000 }; -static void update_polyphase_filter(struct overlay_registers *regs) +static void update_polyphase_filter(struct overlay_registers __iomem *regs) { - memcpy(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs)); - memcpy(regs->UV_HCOEFS, uv_static_hcoeffs, sizeof(uv_static_hcoeffs)); + memcpy_toio(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs)); + memcpy_toio(regs->UV_HCOEFS, uv_static_hcoeffs, + sizeof(uv_static_hcoeffs)); } static bool update_scaling_factors(struct intel_overlay *overlay, - struct overlay_registers *regs, + struct overlay_registers __iomem *regs, struct put_image_params *params) { /* fixed point with a 12 bit shift */ @@ -665,16 +666,19 @@ static bool update_scaling_factors(struct intel_overlay *overlay, overlay->old_xscale = xscale; overlay->old_yscale = yscale; - regs->YRGBSCALE = (((yscale & FRACT_MASK) << 20) | - ((xscale >> FP_SHIFT) << 16) | - ((xscale & FRACT_MASK) << 3)); + iowrite32(((yscale & FRACT_MASK) << 20) | + ((xscale >> FP_SHIFT) << 16) | + ((xscale & FRACT_MASK) << 3), + ®s->YRGBSCALE); - regs->UVSCALE = (((yscale_UV & FRACT_MASK) << 20) | - ((xscale_UV >> FP_SHIFT) << 16) | - ((xscale_UV & FRACT_MASK) << 3)); + iowrite32(((yscale_UV & FRACT_MASK) << 20) | + ((xscale_UV >> FP_SHIFT) << 16) | + ((xscale_UV & FRACT_MASK) << 3), + ®s->UVSCALE); - regs->UVSCALEV = ((((yscale >> FP_SHIFT) << 16) | - ((yscale_UV >> FP_SHIFT) << 0))); + iowrite32((((yscale >> FP_SHIFT) << 16) | + ((yscale_UV >> FP_SHIFT) << 0)), + ®s->UVSCALEV); if (scale_changed) update_polyphase_filter(regs); @@ -683,30 +687,32 @@ static bool update_scaling_factors(struct intel_overlay *overlay, } static void update_colorkey(struct intel_overlay *overlay, - struct overlay_registers *regs) + struct overlay_registers __iomem *regs) { u32 key = overlay->color_key; switch (overlay->crtc->base.fb->bits_per_pixel) { case 8: - regs->DCLRKV = 0; - regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE; + iowrite32(0, ®s->DCLRKV); + iowrite32(CLK_RGB8I_MASK | DST_KEY_ENABLE, ®s->DCLRKM); break; case 16: if (overlay->crtc->base.fb->depth == 15) { - regs->DCLRKV = RGB15_TO_COLORKEY(key); - regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE; + iowrite32(RGB15_TO_COLORKEY(key), ®s->DCLRKV); + iowrite32(CLK_RGB15_MASK | DST_KEY_ENABLE, + ®s->DCLRKM); } else { - regs->DCLRKV = RGB16_TO_COLORKEY(key); - regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE; + iowrite32(RGB16_TO_COLORKEY(key), ®s->DCLRKV); + iowrite32(CLK_RGB16_MASK | DST_KEY_ENABLE, + ®s->DCLRKM); } break; case 24: case 32: - regs->DCLRKV = key; - regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE; + iowrite32(key, ®s->DCLRKV); + iowrite32(CLK_RGB24_MASK | DST_KEY_ENABLE, ®s->DCLRKM); break; } } @@ -761,9 +767,10 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, struct put_image_params *params) { int ret, tmp_width; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; bool scale_changed = false; struct drm_device *dev = overlay->dev; + u32 swidth, swidthsw, sheight, ostride; BUG_ON(!mutex_is_locked(&dev->struct_mutex)); BUG_ON(!mutex_is_locked(&dev->mode_config.mutex)); @@ -782,16 +789,18 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, goto out_unpin; if (!overlay->active) { + u32 oconfig; regs = intel_overlay_map_regs(overlay); if (!regs) { ret = -ENOMEM; goto out_unpin; } - regs->OCONFIG = OCONF_CC_OUT_8BIT; + oconfig = OCONF_CC_OUT_8BIT; if (IS_GEN4(overlay->dev)) - regs->OCONFIG |= OCONF_CSC_MODE_BT709; - regs->OCONFIG |= overlay->crtc->pipe == 0 ? + oconfig |= OCONF_CSC_MODE_BT709; + oconfig |= overlay->crtc->pipe == 0 ? OCONF_PIPE_A : OCONF_PIPE_B; + iowrite32(oconfig, ®s->OCONFIG); intel_overlay_unmap_regs(overlay, regs); ret = intel_overlay_on(overlay); @@ -805,42 +814,46 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, goto out_unpin; } - regs->DWINPOS = (params->dst_y << 16) | params->dst_x; - regs->DWINSZ = (params->dst_h << 16) | params->dst_w; + iowrite32((params->dst_y << 16) | params->dst_x, ®s->DWINPOS); + iowrite32((params->dst_h << 16) | params->dst_w, ®s->DWINSZ); if (params->format & I915_OVERLAY_YUV_PACKED) tmp_width = packed_width_bytes(params->format, params->src_w); else tmp_width = params->src_w; - regs->SWIDTH = params->src_w; - regs->SWIDTHSW = calc_swidthsw(overlay->dev, - params->offset_Y, tmp_width); - regs->SHEIGHT = params->src_h; - regs->OBUF_0Y = new_bo->gtt_offset + params->offset_Y; - regs->OSTRIDE = params->stride_Y; + swidth = params->src_w; + swidthsw = calc_swidthsw(overlay->dev, params->offset_Y, tmp_width); + sheight = params->src_h; + iowrite32(new_bo->gtt_offset + params->offset_Y, ®s->OBUF_0Y); + ostride = params->stride_Y; if (params->format & I915_OVERLAY_YUV_PLANAR) { int uv_hscale = uv_hsubsampling(params->format); int uv_vscale = uv_vsubsampling(params->format); u32 tmp_U, tmp_V; - regs->SWIDTH |= (params->src_w/uv_hscale) << 16; + swidth |= (params->src_w/uv_hscale) << 16; tmp_U = calc_swidthsw(overlay->dev, params->offset_U, params->src_w/uv_hscale); tmp_V = calc_swidthsw(overlay->dev, params->offset_V, params->src_w/uv_hscale); - regs->SWIDTHSW |= max_t(u32, tmp_U, tmp_V) << 16; - regs->SHEIGHT |= (params->src_h/uv_vscale) << 16; - regs->OBUF_0U = new_bo->gtt_offset + params->offset_U; - regs->OBUF_0V = new_bo->gtt_offset + params->offset_V; - regs->OSTRIDE |= params->stride_UV << 16; + swidthsw |= max_t(u32, tmp_U, tmp_V) << 16; + sheight |= (params->src_h/uv_vscale) << 16; + iowrite32(new_bo->gtt_offset + params->offset_U, ®s->OBUF_0U); + iowrite32(new_bo->gtt_offset + params->offset_V, ®s->OBUF_0V); + ostride |= params->stride_UV << 16; } + iowrite32(swidth, ®s->SWIDTH); + iowrite32(swidthsw, ®s->SWIDTHSW); + iowrite32(sheight, ®s->SHEIGHT); + iowrite32(ostride, ®s->OSTRIDE); + scale_changed = update_scaling_factors(overlay, regs, params); update_colorkey(overlay, regs); - regs->OCMD = overlay_cmd_reg(params); + iowrite32(overlay_cmd_reg(params), ®s->OCMD); intel_overlay_unmap_regs(overlay, regs); @@ -860,7 +873,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, int intel_overlay_switch_off(struct intel_overlay *overlay) { - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; struct drm_device *dev = overlay->dev; int ret; @@ -879,7 +892,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay) return ret; regs = intel_overlay_map_regs(overlay); - regs->OCMD = 0; + iowrite32(0, ®s->OCMD); intel_overlay_unmap_regs(overlay, regs); ret = intel_overlay_off(overlay); @@ -1250,10 +1263,11 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, } static void update_reg_attrs(struct intel_overlay *overlay, - struct overlay_registers *regs) + struct overlay_registers __iomem *regs) { - regs->OCLRC0 = (overlay->contrast << 18) | (overlay->brightness & 0xff); - regs->OCLRC1 = overlay->saturation; + iowrite32((overlay->contrast << 18) | (overlay->brightness & 0xff), + ®s->OCLRC0); + iowrite32(overlay->saturation, ®s->OCLRC1); } static bool check_gamma_bounds(u32 gamma1, u32 gamma2) @@ -1306,7 +1320,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, struct drm_intel_overlay_attrs *attrs = data; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_overlay *overlay; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; int ret; if (!dev_priv) { @@ -1396,7 +1410,7 @@ void intel_setup_overlay(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; struct intel_overlay *overlay; struct drm_i915_gem_object *reg_bo; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; int ret; if (!HAS_OVERLAY(dev)) @@ -1451,7 +1465,7 @@ void intel_setup_overlay(struct drm_device *dev) if (!regs) goto out_unpin_bo; - memset(regs, 0, sizeof(struct overlay_registers)); + memset_io(regs, 0, sizeof(struct overlay_registers)); update_polyphase_filter(regs); update_reg_attrs(overlay, regs); @@ -1499,14 +1513,17 @@ struct intel_overlay_error_state { u32 isr; }; -static struct overlay_registers * +static struct overlay_registers __iomem * intel_overlay_map_regs_atomic(struct intel_overlay *overlay) { drm_i915_private_t *dev_priv = overlay->dev->dev_private; - struct overlay_registers *regs; + struct overlay_registers __iomem *regs; if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) - regs = overlay->reg_bo->phys_obj->handle->vaddr; + /* Cast to make sparse happy, but it's wc memory anyway, so + * equivalent to the wc io mapping on X86. */ + regs = (struct overlay_registers __iomem *) + overlay->reg_bo->phys_obj->handle->vaddr; else regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping, overlay->reg_bo->gtt_offset); @@ -1515,7 +1532,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay) } static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay, - struct overlay_registers *regs) + struct overlay_registers __iomem *regs) { if (!OVERLAY_NEEDS_PHYSICAL(overlay->dev)) io_mapping_unmap_atomic(regs); @@ -1540,9 +1557,9 @@ intel_overlay_capture_error_state(struct drm_device *dev) error->dovsta = I915_READ(DOVSTA); error->isr = I915_READ(ISR); if (OVERLAY_NEEDS_PHYSICAL(overlay->dev)) - error->base = (long) overlay->reg_bo->phys_obj->handle->vaddr; + error->base = (__force long)overlay->reg_bo->phys_obj->handle->vaddr; else - error->base = (long) overlay->reg_bo->gtt_offset; + error->base = overlay->reg_bo->gtt_offset; regs = intel_overlay_map_regs_atomic(overlay); if (!regs) From 0d38f00904bd74d791d60105dd4323c60f1767cb Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 21 Apr 2012 22:49:10 +0200 Subject: [PATCH 06/88] drm/i915: [sparse] __iomem fixes for debugfs These were mostly straight forward. No forced casting needed. Signed-off-by: Ben Widawsky [danvet: fix conflict with ringbuffer_data removal and drop the hunk about the status page - that needs more care to fix up.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 8c899a2237ff..54a10667fe82 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1290,17 +1290,25 @@ static int i915_opregion(struct seq_file *m, void *unused) struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; + void *data = kmalloc(OPREGION_SIZE, GFP_KERNEL); int ret; + if (data == NULL) + return -ENOMEM; + ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) - return ret; + goto out; - if (opregion->header) - seq_write(m, opregion->header, OPREGION_SIZE); + if (opregion->header) { + memcpy_fromio(data, opregion->header, OPREGION_SIZE); + seq_write(m, data, OPREGION_SIZE); + } mutex_unlock(&dev->struct_mutex); +out: + kfree(data); return 0; } From 4f0c7cfbb4d25aad44137b6531365d29d3080349 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 16 Apr 2012 14:07:47 -0700 Subject: [PATCH 07/88] drm/i915: [sparse] __iomem fixes for gem As with one of the earlier patches in the series, we're forced to cast for copy_[to|from]_user. Again because of the nature of the GEN x86 exclusivity, this should be safe. Signed-off-by: Ben Widawsky [danvet: Added some bikeshed.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 7bc4a40132ad..9e9ea759e496 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -282,8 +282,8 @@ __copy_to_user_swizzled(char __user *cpu_vaddr, } static inline int -__copy_from_user_swizzled(char __user *gpu_vaddr, int gpu_offset, - const char *cpu_vaddr, +__copy_from_user_swizzled(char *gpu_vaddr, int gpu_offset, + const char __user *cpu_vaddr, int length) { int ret, cpu_offset = 0; @@ -558,11 +558,14 @@ fast_user_write(struct io_mapping *mapping, char __user *user_data, int length) { - char *vaddr_atomic; + void __iomem *vaddr_atomic; + void *vaddr; unsigned long unwritten; vaddr_atomic = io_mapping_map_atomic_wc(mapping, page_base); - unwritten = __copy_from_user_inatomic_nocache(vaddr_atomic + page_offset, + /* We can use the cpu mem copy function because this is X86. */ + vaddr = (void __force*)vaddr_atomic + page_offset; + unwritten = __copy_from_user_inatomic_nocache(vaddr, user_data, length); io_mapping_unmap_atomic(vaddr_atomic); return unwritten; From 63c62275ec484a0f6da6a600d9b44ee0279c0900 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 21 Apr 2012 23:17:55 +0200 Subject: [PATCH 08/88] drm/i915: re-add static qualifier to get_cxsr_latency This got lost in the intel_pm.c move. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 36940a390ef2..a2d2ce474d0e 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -562,7 +562,7 @@ static const struct cxsr_latency cxsr_latency_table[] = { {0, 1, 400, 800, 6042, 36042, 6584, 36584}, /* DDR3-800 SC */ }; -const struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, +static const struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, int is_ddr3, int fsb, int mem) From 38de45c5fc61a4a6b2f0b4b7c9ae9f32f058f420 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 20 Apr 2012 21:25:04 +0200 Subject: [PATCH 09/88] drm/i915: fixup tv load-detect on enabled but not active crtc When fixing up the crt load detect code I've failed to notice the same problem in the tv load detect code. Again, unconditionally use the load detect pipe infrastructure, it gets things right. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_tv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 67f444d632fb..2e626b861cd8 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1251,9 +1251,7 @@ intel_tv_detect(struct drm_connector *connector, bool force) mode = reported_modes[0]; drm_mode_set_crtcinfo(&mode, 0); - if (intel_tv->base.base.crtc && intel_tv->base.base.crtc->enabled) { - type = intel_tv_detect_type(intel_tv, connector); - } else if (force) { + if (force) { struct intel_load_detect_pipe tmp; if (intel_get_load_detect_pipe(&intel_tv->base, connector, From 5d82e3e6427d407e5e4713a2f73210b4e7801ad3 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 21 Apr 2012 16:23:23 +0100 Subject: [PATCH 10/88] drm/i915: Clarify the semantics of tiling_changed Rename obj->tiling_changed to obj->fence_dirty so that it is clear that it flags when the parameters for an active fence (including the no-fence) register are changed. Also, do not set this flag when the object does not have a fence register allocated currently and the gpu does not depend upon the unfence. This case works exactly like when a tiled object lost its fence and hence does not need additional handling for the tiling change in the code. v2: Use fence_dirty to better express what the flag tracks and add a few more details to the comments to serve as a reminder of how the GPU also uses the unfenced register slot. Signed-off-by: Chris Wilson [danvet: Add some bikeshed to the commit message about the stricter use of fence_dirty.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 9 ++++++++- drivers/gpu/drm/i915/i915_gem.c | 8 ++++---- drivers/gpu/drm/i915/i915_gem_tiling.c | 10 +++++++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4cbc45f0424f..faea1b656821 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -869,7 +869,14 @@ struct drm_i915_gem_object { * Current tiling mode for the object. */ unsigned int tiling_mode:2; - unsigned int tiling_changed:1; + /** + * Whether the tiling parameters for the currently associated fence + * register have changed. Note that for the purposes of tracking + * tiling changes we also treat the unfenced register, the register + * slot that the object occupies whilst it executes a fenced + * command (such as BLT on gen2/3), as a "fence". + */ + unsigned int fence_dirty:1; /** How many users have pinned this object in GTT space. The following * users can each hold at most one reference: pwrite/pread, pin_ioctl diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 9e9ea759e496..38490cdf2d9f 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -66,7 +66,7 @@ static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj) /* As we do not have an associated fence register, we will force * a tiling change if we ever need to acquire one. */ - obj->tiling_changed = false; + obj->fence_dirty = false; obj->fence_reg = I915_FENCE_REG_NONE; } @@ -2459,7 +2459,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) /* Have we updated the tiling parameters upon the object and so * will need to serialise the write to the associated fence register? */ - if (obj->tiling_changed) { + if (obj->fence_dirty) { ret = i915_gem_object_flush_fence(obj); if (ret) return ret; @@ -2468,7 +2468,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) /* Just update our place in the LRU if our fence is getting reused. */ if (obj->fence_reg != I915_FENCE_REG_NONE) { reg = &dev_priv->fence_regs[obj->fence_reg]; - if (!obj->tiling_changed) { + if (!obj->fence_dirty) { list_move_tail(®->lru_list, &dev_priv->mm.fence_list); return 0; @@ -2491,7 +2491,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) return 0; i915_gem_object_update_fence(obj, reg, enable); - obj->tiling_changed = false; + obj->fence_dirty = false; return 0; } diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 1a9306665987..c9786cd5dfe3 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -355,6 +355,11 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, * no longer meets the alignment restrictions for its new * tiling mode. Otherwise we can just leave it alone, but * need to ensure that any fence register is cleared. + * + * After updating the tiling parameters, we then flag whether + * we need to update an associated fence register. Note this + * has to also include the unfenced register the GPU uses + * whilst executing a fenced command for an untiled object. */ i915_gem_release_mmap(obj); @@ -374,7 +379,10 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, } if (ret == 0) { - obj->tiling_changed = true; + obj->fence_dirty = + obj->fenced_gpu_access || + obj->fence_reg != I915_FENCE_REG_NONE; + obj->tiling_mode = args->tiling_mode; obj->stride = args->stride; } From 1869b620d27a0e353bd6558015713fad2d0cc09b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 21 Apr 2012 16:23:24 +0100 Subject: [PATCH 11/88] drm/i915: Only the zap the VMA after updating the tiling parameters If we fail to unbind and so abort the change in tiling, we will have removed the VMA for the object for no reason. The likelihood of unbind failing is slim (other than ERESTARTSYS which will cause userspace to try again), so the change is mostly for the principle. Also improve the slightly stale comment. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_tiling.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index c9786cd5dfe3..b964df51cec7 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -354,14 +354,15 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, /* We need to rebind the object if its current allocation * no longer meets the alignment restrictions for its new * tiling mode. Otherwise we can just leave it alone, but - * need to ensure that any fence register is cleared. + * need to ensure that any fence register is updated before + * the next fenced (either through the GTT or by the BLT unit + * on older GPUs) access. * * After updating the tiling parameters, we then flag whether * we need to update an associated fence register. Note this * has to also include the unfenced register the GPU uses * whilst executing a fenced command for an untiled object. */ - i915_gem_release_mmap(obj); obj->map_and_fenceable = obj->gtt_space == NULL || @@ -385,6 +386,9 @@ i915_gem_set_tiling(struct drm_device *dev, void *data, obj->tiling_mode = args->tiling_mode; obj->stride = args->stride; + + /* Force the fence to be reacquired for GTT access */ + i915_gem_release_mmap(obj); } } /* we have to maintain this existing ABI... */ From c2798b19bac2538393fc932bfbe59807a4734b3e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 22 Apr 2012 21:13:57 +0100 Subject: [PATCH 12/88] drm/i915: i8xx interrupt handler gen2 hardware has some significant differences from the other interrupt routines that were glossed over and then forgotten about in the transition to KMS. Such as - 16bit IIR - PendingFlip status bit This patch reintroduces a handler specifically for gen2 for the purpose of handling pageflips correctly, simplifying code in the process. v2: Also fixup ring get/put irq to only access 16bit registers (Daniel) Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=24202 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=41793 Signed-off-by: Chris Wilson [danvet: use posting_read16 in intel_ringbuffer.c and kill _driver from the function names.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 161 +++++++++++++++++++++++- drivers/gpu/drm/i915/intel_ringbuffer.c | 53 +++++++- 2 files changed, 206 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ab023ca73b45..d45b43a35f15 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2446,6 +2446,152 @@ static void i915_driver_irq_uninstall(struct drm_device * dev) I915_WRITE(IIR, I915_READ(IIR)); } +static void i8xx_irq_preinstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + atomic_set(&dev_priv->irq_received, 0); + + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE16(IMR, 0xffff); + I915_WRITE16(IER, 0x0); + POSTING_READ16(IER); +} + +static int i8xx_irq_postinstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + + dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; + + dev_priv->pipestat[0] = 0; + dev_priv->pipestat[1] = 0; + + I915_WRITE16(EMR, + ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); + + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask = + ~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); + I915_WRITE16(IMR, dev_priv->irq_mask); + + I915_WRITE16(IER, + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT | + I915_USER_INTERRUPT); + POSTING_READ16(IER); + + return 0; +} + +static irqreturn_t i8xx_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + struct drm_i915_master_private *master_priv; + u16 iir, new_iir; + u32 pipe_stats[2]; + unsigned long irqflags; + int irq_received; + int pipe; + u16 flip_mask = + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + + atomic_inc(&dev_priv->irq_received); + + iir = I915_READ16(IIR); + if (iir == 0) + return IRQ_NONE; + + while (iir & ~flip_mask) { + /* Can't rely on pipestat interrupt bit in iir as it might + * have been cleared after the pipestat interrupt was received. + * It doesn't set the bit in iir again, but it still produces + * interrupts (for non-MSI). + */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + i915_handle_error(dev, false); + + for_each_pipe(pipe) { + int reg = PIPESTAT(pipe); + pipe_stats[pipe] = I915_READ(reg); + + /* + * Clear the PIPE*STAT regs before the IIR + */ + if (pipe_stats[pipe] & 0x8000ffff) { + if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) + DRM_DEBUG_DRIVER("pipe %c underrun\n", + pipe_name(pipe)); + I915_WRITE(reg, pipe_stats[pipe]); + irq_received = 1; + } + } + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + + I915_WRITE16(IIR, iir & ~flip_mask); + new_iir = I915_READ16(IIR); /* Flush posted writes */ + + if (dev->primary->master) { + master_priv = dev->primary->master->driver_priv; + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_dispatch = + READ_BREADCRUMB(dev_priv); + } + + if (iir & I915_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[RCS]); + + if (pipe_stats[0] & PIPE_VBLANK_INTERRUPT_STATUS && + drm_handle_vblank(dev, 0)) { + if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { + intel_prepare_page_flip(dev, 0); + intel_finish_page_flip(dev, 0); + flip_mask &= ~I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT; + } + } + + if (pipe_stats[1] & PIPE_VBLANK_INTERRUPT_STATUS && + drm_handle_vblank(dev, 1)) { + if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { + intel_prepare_page_flip(dev, 1); + intel_finish_page_flip(dev, 1); + flip_mask &= ~I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + } + } + + iir = new_iir; + } + + return IRQ_HANDLED; +} + +static void i8xx_irq_uninstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + dev_priv->vblank_pipe = 0; + + for_each_pipe(pipe) { + /* Clear enable bits; then clear status bits */ + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); + } + I915_WRITE16(IMR, 0xffff); + I915_WRITE16(IER, 0x0); + I915_WRITE16(IIR, I915_READ16(IIR)); +} + void intel_irq_init(struct drm_device *dev) { dev->driver->get_vblank_counter = i915_get_vblank_counter; @@ -2485,10 +2631,17 @@ void intel_irq_init(struct drm_device *dev) dev->driver->enable_vblank = ironlake_enable_vblank; dev->driver->disable_vblank = ironlake_disable_vblank; } else { - dev->driver->irq_preinstall = i915_driver_irq_preinstall; - dev->driver->irq_postinstall = i915_driver_irq_postinstall; - dev->driver->irq_uninstall = i915_driver_irq_uninstall; - dev->driver->irq_handler = i915_driver_irq_handler; + if (INTEL_INFO(dev)->gen == 2) { + dev->driver->irq_preinstall = i8xx_irq_preinstall; + dev->driver->irq_postinstall = i8xx_irq_postinstall; + dev->driver->irq_handler = i8xx_irq_handler; + dev->driver->irq_uninstall = i8xx_irq_uninstall; + } else { + dev->driver->irq_preinstall = i915_driver_irq_preinstall; + dev->driver->irq_postinstall = i915_driver_irq_postinstall; + dev->driver->irq_uninstall = i915_driver_irq_uninstall; + dev->driver->irq_handler = i915_driver_irq_handler; + } dev->driver->enable_vblank = i915_enable_vblank; dev->driver->disable_vblank = i915_disable_vblank; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 12d9bc789dfb..6249a7fa9acc 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -678,6 +678,41 @@ i9xx_ring_put_irq(struct intel_ring_buffer *ring) spin_unlock(&ring->irq_lock); } +static bool +i8xx_ring_get_irq(struct intel_ring_buffer *ring) +{ + struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + + if (!dev->irq_enabled) + return false; + + spin_lock(&ring->irq_lock); + if (ring->irq_refcount++ == 0) { + dev_priv->irq_mask &= ~ring->irq_enable_mask; + I915_WRITE16(IMR, dev_priv->irq_mask); + POSTING_READ16(IMR); + } + spin_unlock(&ring->irq_lock); + + return true; +} + +static void +i8xx_ring_put_irq(struct intel_ring_buffer *ring) +{ + struct drm_device *dev = ring->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + + spin_lock(&ring->irq_lock); + if (--ring->irq_refcount == 0) { + dev_priv->irq_mask |= ring->irq_enable_mask; + I915_WRITE16(IMR, dev_priv->irq_mask); + POSTING_READ16(IMR); + } + spin_unlock(&ring->irq_lock); +} + void intel_ring_setup_status_page(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; @@ -1310,8 +1345,13 @@ int intel_init_render_ring_buffer(struct drm_device *dev) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; - ring->irq_get = i9xx_ring_get_irq; - ring->irq_put = i9xx_ring_put_irq; + if (IS_GEN2(dev)) { + ring->irq_get = i8xx_ring_get_irq; + ring->irq_put = i8xx_ring_put_irq; + } else { + ring->irq_get = i9xx_ring_get_irq; + ring->irq_put = i9xx_ring_put_irq; + } ring->irq_enable_mask = I915_USER_INTERRUPT; } ring->write_tail = ring_write_tail; @@ -1358,8 +1398,13 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size) else ring->flush = gen4_render_ring_flush; ring->get_seqno = ring_get_seqno; - ring->irq_get = i9xx_ring_get_irq; - ring->irq_put = i9xx_ring_put_irq; + if (IS_GEN2(dev)) { + ring->irq_get = i8xx_ring_get_irq; + ring->irq_put = i8xx_ring_put_irq; + } else { + ring->irq_get = i9xx_ring_get_irq; + ring->irq_put = i9xx_ring_put_irq; + } ring->irq_enable_mask = I915_USER_INTERRUPT; ring->write_tail = ring_write_tail; if (INTEL_INFO(dev)->gen >= 4) From ee7b9f93fd96a72e5d09e2b44024c11880873c6b Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 20 Apr 2012 17:11:53 +0100 Subject: [PATCH 13/88] drm/i915: manage PCH PLLs separately from pipes PCH PLLs aren't required for outputs on the CPU, so we shouldn't just treat them as part of the pipe. So split the code out and manage PCH PLLs separately, allocating them when needed or trying to re-use existing PCH PLL setups when the timings match. v2: add num_pch_pll field to dev_priv (Daniel) don't NULL the pch_pll pointer in disable or DPMS will fail (Jesse) put register offsets in pll struct (Chris) v3: Decouple enable/disable of PLLs from get/put. v4: Track temporary PLL disabling during modeset v5: Tidy PLL initialisation by only checking for num_pch_pll == 0 (Eugeni) v6: Avoid mishandling allocation failure by embedding the small array of PLLs into the device struct Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=44309 Signed-off-by: Jesse Barnes (up to v2) Signed-off-by: Chris Wilson (v3+) Reviewed-by: Eugeni Dodonov Tested-by: Jesse Barnes Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 5 + drivers/gpu/drm/i915/i915_drv.h | 14 ++ drivers/gpu/drm/i915/i915_reg.h | 6 +- drivers/gpu/drm/i915/i915_suspend.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 275 +++++++++++++++++++-------- drivers/gpu/drm/i915/intel_dp.c | 1 + drivers/gpu/drm/i915/intel_drv.h | 4 +- 7 files changed, 224 insertions(+), 83 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 3effcf71e1b1..95ccdffb5deb 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -377,18 +377,23 @@ void intel_detect_pch(struct drm_device *dev) if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_IBX; + dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found Ibex Peak PCH\n"); } else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_CPT; + dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found CougarPoint PCH\n"); } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) { /* PantherPoint is CPT compatible */ dev_priv->pch_type = PCH_CPT; + dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found PatherPoint PCH\n"); } else if (id == INTEL_PCH_LPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; + dev_priv->num_pch_pll = 0; DRM_DEBUG_KMS("Found LynxPoint PCH\n"); } + BUG_ON(dev_priv->num_pch_pll > I915_NUM_PLLS); } pci_dev_put(pch); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index faea1b656821..c1440137002a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -78,6 +78,16 @@ enum port { #define for_each_pipe(p) for ((p) = 0; (p) < dev_priv->num_pipe; (p)++) +struct intel_pch_pll { + int refcount; /* count of number of CRTCs sharing this PLL */ + int active; /* count of number of active CRTCs (i.e. DPMS on) */ + bool on; /* is the PLL actually active? Disabled during modeset */ + int pll_reg; + int fp0_reg; + int fp1_reg; +}; +#define I915_NUM_PLLS 2 + /* Interface history: * * 1.1: Original. @@ -233,6 +243,7 @@ struct drm_i915_display_funcs { struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb); + void (*off)(struct drm_crtc *crtc); void (*write_eld)(struct drm_connector *connector, struct drm_crtc *crtc); void (*fdi_link_train)(struct drm_crtc *crtc); @@ -391,6 +402,7 @@ typedef struct drm_i915_private { unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int vblank_pipe; int num_pipe; + int num_pch_pll; /* For hangcheck timer */ #define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ @@ -753,6 +765,8 @@ typedef struct drm_i915_private { wait_queue_head_t pending_flip_queue; bool flip_pending_is_done; + struct intel_pch_pll pch_plls[I915_NUM_PLLS]; + /* Reclocking support */ bool render_reclock_avail; bool lvds_downclock_avail; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5ac9837e49a5..91f1d1cd0070 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3402,15 +3402,15 @@ #define _PCH_DPLL_A 0xc6014 #define _PCH_DPLL_B 0xc6018 -#define PCH_DPLL(pipe) (pipe == 0 ? _PCH_DPLL_A : _PCH_DPLL_B) +#define _PCH_DPLL(pll) (pll == 0 ? _PCH_DPLL_A : _PCH_DPLL_B) #define _PCH_FPA0 0xc6040 #define FP_CB_TUNE (0x3<<22) #define _PCH_FPA1 0xc6044 #define _PCH_FPB0 0xc6048 #define _PCH_FPB1 0xc604c -#define PCH_FP0(pipe) (pipe == 0 ? _PCH_FPA0 : _PCH_FPB0) -#define PCH_FP1(pipe) (pipe == 0 ? _PCH_FPA1 : _PCH_FPB1) +#define _PCH_FP0(pll) (pll == 0 ? _PCH_FPA0 : _PCH_FPB0) +#define _PCH_FP1(pll) (pll == 0 ? _PCH_FPA1 : _PCH_FPB1) #define PCH_DPLL_TEST 0xc606c diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 0c3e3bf67c28..73a5c3c12fe0 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -40,7 +40,7 @@ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe) return false; if (HAS_PCH_SPLIT(dev)) - dpll_reg = PCH_DPLL(pipe); + dpll_reg = _PCH_DPLL(pipe); else dpll_reg = (pipe == PIPE_A) ? _DPLL_A : _DPLL_B; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4c844c68ec80..c5f071daa851 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -911,26 +911,28 @@ static void assert_pll(struct drm_i915_private *dev_priv, /* For ILK+ */ static void assert_pch_pll(struct drm_i915_private *dev_priv, - enum pipe pipe, bool state) + struct intel_crtc *intel_crtc, bool state) { int reg; u32 val; bool cur_state; + if (!intel_crtc->pch_pll) { + WARN(1, "asserting PCH PLL enabled with no PLL\n"); + return; + } + if (HAS_PCH_CPT(dev_priv->dev)) { u32 pch_dpll; pch_dpll = I915_READ(PCH_DPLL_SEL); /* Make sure the selected PLL is enabled to the transcoder */ - WARN(!((pch_dpll >> (4 * pipe)) & 8), - "transcoder %d PLL not enabled\n", pipe); - - /* Convert the transcoder pipe number to a pll pipe number */ - pipe = (pch_dpll >> (4 * pipe)) & 1; + WARN(!((pch_dpll >> (4 * intel_crtc->pipe)) & 8), + "transcoder %d PLL not enabled\n", intel_crtc->pipe); } - reg = PCH_DPLL(pipe); + reg = intel_crtc->pch_pll->pll_reg; val = I915_READ(reg); cur_state = !!(val & DPLL_VCO_ENABLE); WARN(cur_state != state, @@ -1306,60 +1308,79 @@ static void intel_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) * The PCH PLL needs to be enabled before the PCH transcoder, since it * drives the transcoder clock. */ -static void intel_enable_pch_pll(struct drm_i915_private *dev_priv, - enum pipe pipe) +static void intel_enable_pch_pll(struct intel_crtc *intel_crtc) { + struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; + struct intel_pch_pll *pll = intel_crtc->pch_pll; int reg; u32 val; - if (pipe > 1) - return; - /* PCH only available on ILK+ */ BUG_ON(dev_priv->info->gen < 5); + BUG_ON(pll == NULL); + BUG_ON(pll->refcount == 0); + + DRM_DEBUG_KMS("enable PCH PLL %x (active %d, on? %d)for crtc %d\n", + pll->pll_reg, pll->active, pll->on, + intel_crtc->base.base.id); /* PCH refclock must be enabled first */ assert_pch_refclk_enabled(dev_priv); - reg = PCH_DPLL(pipe); + if (pll->active++ && pll->on) { + assert_pch_pll_enabled(dev_priv, intel_crtc); + return; + } + + DRM_DEBUG_KMS("enabling PCH PLL %x\n", pll->pll_reg); + + reg = pll->pll_reg; val = I915_READ(reg); val |= DPLL_VCO_ENABLE; I915_WRITE(reg, val); POSTING_READ(reg); udelay(200); + + pll->on = true; } -static void intel_disable_pch_pll(struct drm_i915_private *dev_priv, - enum pipe pipe) +static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) { + struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; + struct intel_pch_pll *pll = intel_crtc->pch_pll; int reg; - u32 val, pll_mask = TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL, - pll_sel = TRANSC_DPLL_ENABLE; - - if (pipe > 1) - return; + u32 val; /* PCH only available on ILK+ */ BUG_ON(dev_priv->info->gen < 5); + if (pll == NULL) + return; + + BUG_ON(pll->refcount == 0); + + DRM_DEBUG_KMS("disable PCH PLL %x (active %d, on? %d) for crtc %d\n", + pll->pll_reg, pll->active, pll->on, + intel_crtc->base.base.id); + + BUG_ON(pll->active == 0); + if (--pll->active) { + assert_pch_pll_enabled(dev_priv, intel_crtc); + return; + } + + DRM_DEBUG_KMS("disabling PCH PLL %x\n", pll->pll_reg); /* Make sure transcoder isn't still depending on us */ - assert_transcoder_disabled(dev_priv, pipe); + assert_transcoder_disabled(dev_priv, intel_crtc->pipe); - if (pipe == 0) - pll_sel |= TRANSC_DPLLA_SEL; - else if (pipe == 1) - pll_sel |= TRANSC_DPLLB_SEL; - - - if ((I915_READ(PCH_DPLL_SEL) & pll_mask) == pll_sel) - return; - - reg = PCH_DPLL(pipe); + reg = pll->pll_reg; val = I915_READ(reg); val &= ~DPLL_VCO_ENABLE; I915_WRITE(reg, val); POSTING_READ(reg); udelay(200); + + pll->on = false; } static void intel_enable_transcoder(struct drm_i915_private *dev_priv, @@ -1373,7 +1394,7 @@ static void intel_enable_transcoder(struct drm_i915_private *dev_priv, BUG_ON(dev_priv->info->gen < 5); /* Make sure PCH DPLL is enabled */ - assert_pch_pll_enabled(dev_priv, pipe); + assert_pch_pll_enabled(dev_priv, to_intel_crtc(crtc)); /* FDI must be feeding us bits for PCH ports */ assert_fdi_tx_enabled(dev_priv, pipe); @@ -2578,29 +2599,36 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - u32 reg, temp, transc_sel; + u32 reg, temp; /* For PCH output, training FDI link */ dev_priv->display.fdi_link_train(crtc); - intel_enable_pch_pll(dev_priv, pipe); + intel_enable_pch_pll(intel_crtc); if (HAS_PCH_CPT(dev)) { - transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL : - TRANSC_DPLLB_SEL; + u32 sel; - /* Be sure PCH DPLL SEL is set */ temp = I915_READ(PCH_DPLL_SEL); - if (pipe == 0) { - temp &= ~(TRANSA_DPLLB_SEL); - temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); - } else if (pipe == 1) { - temp &= ~(TRANSB_DPLLB_SEL); - temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); - } else if (pipe == 2) { - temp &= ~(TRANSC_DPLLB_SEL); - temp |= (TRANSC_DPLL_ENABLE | transc_sel); + switch (pipe) { + default: + case 0: + temp |= TRANSA_DPLL_ENABLE; + sel = TRANSA_DPLLB_SEL; + break; + case 1: + temp |= TRANSB_DPLL_ENABLE; + sel = TRANSB_DPLLB_SEL; + break; + case 2: + temp |= TRANSC_DPLL_ENABLE; + sel = TRANSC_DPLLB_SEL; + break; } + if (intel_crtc->pch_pll->pll_reg == _PCH_DPLL_B) + temp |= sel; + else + temp &= ~sel; I915_WRITE(PCH_DPLL_SEL, temp); } @@ -2658,6 +2686,79 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) intel_enable_transcoder(dev_priv, pipe); } +static void intel_put_pch_pll(struct intel_crtc *intel_crtc) +{ + struct intel_pch_pll *pll = intel_crtc->pch_pll; + + if (pll == NULL) + return; + + if (pll->refcount == 0) { + WARN(1, "bad PCH PLL refcount\n"); + return; + } + + --pll->refcount; + intel_crtc->pch_pll = NULL; +} + +static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u32 dpll, u32 fp) +{ + struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; + struct intel_pch_pll *pll; + int i; + + pll = intel_crtc->pch_pll; + if (pll) { + DRM_DEBUG_KMS("CRTC:%d reusing existing PCH PLL %x\n", + intel_crtc->base.base.id, pll->pll_reg); + goto prepare; + } + + for (i = 0; i < dev_priv->num_pch_pll; i++) { + pll = &dev_priv->pch_plls[i]; + + /* Only want to check enabled timings first */ + if (pll->refcount == 0) + continue; + + if (dpll == (I915_READ(pll->pll_reg) & 0x7fffffff) && + fp == I915_READ(pll->fp0_reg)) { + DRM_DEBUG_KMS("CRTC:%d sharing existing PCH PLL %x (refcount %d, ative %d)\n", + intel_crtc->base.base.id, + pll->pll_reg, pll->refcount, pll->active); + + goto found; + } + } + + /* Ok no matching timings, maybe there's a free one? */ + for (i = 0; i < dev_priv->num_pch_pll; i++) { + pll = &dev_priv->pch_plls[i]; + if (pll->refcount == 0) { + DRM_DEBUG_KMS("CRTC:%d allocated PCH PLL %x\n", + intel_crtc->base.base.id, pll->pll_reg); + goto found; + } + } + + return NULL; + +found: + intel_crtc->pch_pll = pll; + pll->refcount++; + DRM_DEBUG_DRIVER("using pll %d for pipe %d\n", i, intel_crtc->pipe); +prepare: /* separate function? */ + DRM_DEBUG_DRIVER("switching PLL %x off\n", pll->pll_reg); + I915_WRITE(pll->fp0_reg, fp); + I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); + + POSTING_READ(pll->pll_reg); + udelay(150); + pll->on = false; + return pll; +} + void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2802,8 +2903,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) } /* disable PCH DPLL */ - if (!intel_crtc->no_pll) - intel_disable_pch_pll(dev_priv, pipe); + intel_disable_pch_pll(intel_crtc); /* Switch from PCDclk to Rawclk */ reg = FDI_RX_CTL(pipe); @@ -2859,6 +2959,12 @@ static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode) } } +static void ironlake_crtc_off(struct drm_crtc *crtc) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + intel_put_pch_pll(intel_crtc); +} + static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) { if (!enable && intel_crtc->overlay) { @@ -2950,6 +3056,10 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) } } +static void i9xx_crtc_off(struct drm_crtc *crtc) +{ +} + /** * Sets the power management mode of the pipe and plane. */ @@ -2997,8 +3107,11 @@ static void intel_crtc_disable(struct drm_crtc *crtc) { struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + dev_priv->display.off(crtc); + assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane); assert_pipe_disabled(dev->dev_private, to_intel_crtc(crtc)->pipe); @@ -4238,29 +4351,18 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); - /* PCH eDP needs FDI, but CPU eDP does not */ - if (!intel_crtc->no_pll) { - if (!is_cpu_edp) { - I915_WRITE(PCH_FP0(pipe), fp); - I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); + /* CPU eDP is the only output that doesn't need a PCH PLL of its own */ + if (!is_cpu_edp) { + struct intel_pch_pll *pll; - POSTING_READ(PCH_DPLL(pipe)); - udelay(150); - } - } else { - if (dpll == (I915_READ(PCH_DPLL(0)) & 0x7fffffff) && - fp == I915_READ(PCH_FP0(0))) { - intel_crtc->use_pll_a = true; - DRM_DEBUG_KMS("using pipe a dpll\n"); - } else if (dpll == (I915_READ(PCH_DPLL(1)) & 0x7fffffff) && - fp == I915_READ(PCH_FP0(1))) { - intel_crtc->use_pll_a = false; - DRM_DEBUG_KMS("using pipe b dpll\n"); - } else { - DRM_DEBUG_KMS("no matching PLL configuration for pipe 2\n"); + pll = intel_get_pch_pll(intel_crtc, dpll, fp); + if (pll == NULL) { + DRM_DEBUG_DRIVER("failed to find PLL for pipe %d\n", + pipe); return -EINVAL; } - } + } else + intel_put_pch_pll(intel_crtc); /* The LVDS pin pair needs to be on before the DPLLs are enabled. * This is an exception to the general rule that mode_set doesn't turn @@ -4317,11 +4419,11 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(TRANSDPLINK_N1(pipe), 0); } - if (!intel_crtc->no_pll && (!edp_encoder || is_pch_edp)) { - I915_WRITE(PCH_DPLL(pipe), dpll); + if (intel_crtc->pch_pll) { + I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); /* Wait for the clocks to stabilize. */ - POSTING_READ(PCH_DPLL(pipe)); + POSTING_READ(intel_crtc->pch_pll->pll_reg); udelay(150); /* The pixel multiplier can only be updated once the @@ -4329,20 +4431,20 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, * * So write it again. */ - I915_WRITE(PCH_DPLL(pipe), dpll); + I915_WRITE(intel_crtc->pch_pll->pll_reg, dpll); } intel_crtc->lowfreq_avail = false; - if (!intel_crtc->no_pll) { + if (intel_crtc->pch_pll) { if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(PCH_FP1(pipe), fp2); + I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp2); intel_crtc->lowfreq_avail = true; if (HAS_PIPE_CXSR(dev)) { DRM_DEBUG_KMS("enabling CxSR downclocking\n"); pipeconf |= PIPECONF_CXSR_DOWNCLOCK; } } else { - I915_WRITE(PCH_FP1(pipe), fp); + I915_WRITE(intel_crtc->pch_pll->fp1_reg, fp); if (HAS_PIPE_CXSR(dev)) { DRM_DEBUG_KMS("disabling CxSR downclocking\n"); pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; @@ -6016,6 +6118,23 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .page_flip = intel_crtc_page_flip, }; +static void intel_pch_pll_init(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int i; + + if (dev_priv->num_pch_pll == 0) { + DRM_DEBUG_KMS("No PCH PLLs on this hardware, skipping initialisation\n"); + return; + } + + for (i = 0; i < dev_priv->num_pch_pll; i++) { + dev_priv->pch_plls[i].pll_reg = _PCH_DPLL(i); + dev_priv->pch_plls[i].fp0_reg = _PCH_FP0(i); + dev_priv->pch_plls[i].fp1_reg = _PCH_FP1(i); + } +} + static void intel_crtc_init(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -6053,8 +6172,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->bpp = 24; /* default for pre-Ironlake */ if (HAS_PCH_SPLIT(dev)) { - if (pipe == 2 && IS_IVYBRIDGE(dev)) - intel_crtc->no_pll = true; intel_helper_funcs.prepare = ironlake_crtc_prepare; intel_helper_funcs.commit = ironlake_crtc_commit; } else { @@ -6337,10 +6454,12 @@ static void intel_init_display(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { dev_priv->display.dpms = ironlake_crtc_dpms; dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; + dev_priv->display.off = ironlake_crtc_off; dev_priv->display.update_plane = ironlake_update_plane; } else { dev_priv->display.dpms = i9xx_crtc_dpms; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; + dev_priv->display.off = i9xx_crtc_off; dev_priv->display.update_plane = i9xx_update_plane; } @@ -6603,6 +6722,8 @@ void intel_modeset_init(struct drm_device *dev) DRM_DEBUG_KMS("plane %d init failed: %d\n", i, ret); } + intel_pch_pll_init(dev); + /* Just disable it once at startup */ i915_disable_vga(dev); intel_setup_outputs(dev); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 22c66b7c7e4a..a1a5ce71558a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2445,6 +2445,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) } intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); + connector->interlace_allowed = true; connector->doublescan_allowed = 0; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index c5bf8bebf0b0..4b7ec449d3cc 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -179,8 +179,8 @@ struct intel_crtc { bool cursor_visible; unsigned int bpp; - bool no_pll; /* tertiary pipe for IVB */ - bool use_pll_a; + /* We can share PLLs across outputs if the timings match */ + struct intel_pch_pll *pch_pll; }; struct intel_plane { From 6b26c86d615747f67fede6ca9609b0f8b2037d11 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 24 Apr 2012 14:04:12 +0200 Subject: [PATCH 14/88] drm/i915: create macros to handle masked bits ... and put them to so good use. Note that there's functional change in vlv clock gating code, we now no longer spuriously read back the current value of the bit. According to Bspec the high bits should always read zero, so ORing this in should have no effect. Reviewed-by: Jesse Barnes Reviewed-by: Chris Wilson Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 4 ++-- drivers/gpu/drm/i915/i915_gem.c | 8 ++++---- drivers/gpu/drm/i915/i915_irq.c | 5 ++--- drivers/gpu/drm/i915/i915_reg.h | 8 +++----- drivers/gpu/drm/i915/intel_pm.c | 5 ++--- drivers/gpu/drm/i915/intel_ringbuffer.c | 13 +++++-------- 6 files changed, 18 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 95ccdffb5deb..8a98f9a16418 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -438,7 +438,7 @@ void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1)) udelay(10); - I915_WRITE_NOTRACE(FORCEWAKE_MT, (1<<16) | 1); + I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1)); POSTING_READ(FORCEWAKE_MT); count = 0; @@ -480,7 +480,7 @@ void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) { - I915_WRITE_NOTRACE(FORCEWAKE_MT, (1<<16) | 0); + I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(1)); /* The below doubles as a POSTING_READ */ gen6_gt_check_fifodbg(dev_priv); } diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 38490cdf2d9f..cfbcf7ef567e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3494,9 +3494,9 @@ void i915_gem_init_swizzling(struct drm_device *dev) I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_SWZCTL); if (IS_GEN6(dev)) - I915_WRITE(ARB_MODE, ARB_MODE_ENABLE(ARB_MODE_SWIZZLE_SNB)); + I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_SNB)); else - I915_WRITE(ARB_MODE, ARB_MODE_ENABLE(ARB_MODE_SWIZZLE_IVB)); + I915_WRITE(ARB_MODE, _MASKED_BIT_ENABLE(ARB_MODE_SWIZZLE_IVB)); } void i915_gem_init_ppgtt(struct drm_device *dev) @@ -3545,7 +3545,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) ecochk = I915_READ(GAM_ECOCHK); I915_WRITE(GAM_ECOCHK, ecochk | ECOCHK_SNB_BIT | ECOCHK_PPGTT_CACHE64B); - I915_WRITE(GFX_MODE, GFX_MODE_ENABLE(GFX_PPGTT_ENABLE)); + I915_WRITE(GFX_MODE, _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); } else if (INTEL_INFO(dev)->gen >= 7) { I915_WRITE(GAM_ECOCHK, ECOCHK_PPGTT_CACHE64B); /* GFX_MODE is per-ring on gen7+ */ @@ -3556,7 +3556,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) if (INTEL_INFO(dev)->gen >= 7) I915_WRITE(RING_MODE_GEN7(ring), - GFX_MODE_ENABLE(GFX_PPGTT_ENABLE)); + _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G); I915_WRITE(RING_PP_DIR_BASE(ring), pd_offset); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d45b43a35f15..26172eef9787 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1648,7 +1648,7 @@ static int i915_enable_vblank(struct drm_device *dev, int pipe) /* maintain vblank delivery even in deep C-states */ if (dev_priv->info->gen == 3) - I915_WRITE(INSTPM, INSTPM_AGPBUSY_DIS << 16); + I915_WRITE(INSTPM, _MASKED_BIT_DISABLE(INSTPM_AGPBUSY_DIS)); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); return 0; @@ -1722,8 +1722,7 @@ static void i915_disable_vblank(struct drm_device *dev, int pipe) spin_lock_irqsave(&dev_priv->irq_lock, irqflags); if (dev_priv->info->gen == 3) - I915_WRITE(INSTPM, - INSTPM_AGPBUSY_DIS << 16 | INSTPM_AGPBUSY_DIS); + I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_AGPBUSY_DIS)); i915_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE | diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 91f1d1cd0070..f1f4d8f1df6a 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -29,6 +29,9 @@ #define _PORT(port, a, b) ((a) + (port)*((b)-(a))) +#define _MASKED_BIT_ENABLE(a) (((a) << 16) | (a)) +#define _MASKED_BIT_DISABLE(a) ((a) << 16) + /* * The Bridge device's PCI config space has information about the * fb aperture size and the amount of pre-reserved memory. @@ -425,8 +428,6 @@ #define ARB_MODE 0x04030 #define ARB_MODE_SWIZZLE_SNB (1<<4) #define ARB_MODE_SWIZZLE_IVB (1<<5) -#define ARB_MODE_ENABLE(x) GFX_MODE_ENABLE(x) -#define ARB_MODE_DISABLE(x) GFX_MODE_DISABLE(x) #define RENDER_HWS_PGA_GEN7 (0x04080) #define RING_FAULT_REG(ring) (0x4094 + 0x100*(ring)->id) #define DONE_REG 0x40b0 @@ -514,9 +515,6 @@ #define GFX_PSMI_GRANULARITY (1<<10) #define GFX_PPGTT_ENABLE (1<<9) -#define GFX_MODE_ENABLE(bit) (((bit) << 16) | (bit)) -#define GFX_MODE_DISABLE(bit) (((bit) << 16) | (0)) - #define SCPD0 0x0209c /* 915+ only */ #define IER 0x020a0 #define IIR 0x020a4 diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index a2d2ce474d0e..a26bf49c4649 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2821,9 +2821,8 @@ static void valleyview_init_clock_gating(struct drm_device *dev) intel_flush_display_plane(dev_priv, pipe); } - I915_WRITE(CACHE_MODE_1, I915_READ(CACHE_MODE_1) | - (PIXEL_SUBSPAN_COLLECT_OPT_DISABLE << 16) | - PIXEL_SUBSPAN_COLLECT_OPT_DISABLE); + I915_WRITE(CACHE_MODE_1, + _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); } static void g4x_init_clock_gating(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 6249a7fa9acc..f797613e6c4a 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -401,12 +401,11 @@ static int init_render_ring(struct intel_ring_buffer *ring) int ret = init_ring_common(ring); if (INTEL_INFO(dev)->gen > 3) { - int mode = VS_TIMER_DISPATCH << 16 | VS_TIMER_DISPATCH; - I915_WRITE(MI_MODE, mode); + I915_WRITE(MI_MODE, _MASKED_BIT_ENABLE(VS_TIMER_DISPATCH)); if (IS_GEN7(dev)) I915_WRITE(GFX_MODE_GEN7, - GFX_MODE_DISABLE(GFX_TLB_INVALIDATE_ALWAYS) | - GFX_MODE_ENABLE(GFX_REPLAY_MODE)); + _MASKED_BIT_DISABLE(GFX_TLB_INVALIDATE_ALWAYS) | + _MASKED_BIT_ENABLE(GFX_REPLAY_MODE)); } if (INTEL_INFO(dev)->gen >= 5) { @@ -415,10 +414,8 @@ static int init_render_ring(struct intel_ring_buffer *ring) return ret; } - if (INTEL_INFO(dev)->gen >= 6) { - I915_WRITE(INSTPM, - INSTPM_FORCE_ORDERING << 16 | INSTPM_FORCE_ORDERING); - } + if (INTEL_INFO(dev)->gen >= 6) + I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING)); return ret; } From 97e1930f09e9b656e823cd841cf29bfbbe326832 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 24 Apr 2012 16:00:21 +0200 Subject: [PATCH 15/88] drm/i915: implement Disable4x2SubspanOptimization w/a for ivb, too Copy&pasted from the vlv setup code. According to docs, we need that on ivb, too. v2: Use new masked bit handling macros. Reviewed-by: Ben Widawsky Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index a26bf49c4649..93d4ce3fc122 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2776,6 +2776,10 @@ static void ivybridge_init_clock_gating(struct drm_device *dev) } gen7_setup_fixed_func_scheduler(dev_priv); + + /* WaDisable4x2SubspanOptimization */ + I915_WRITE(CACHE_MODE_1, + _MASKED_BIT_ENABLE(PIXEL_SUBSPAN_COLLECT_OPT_DISABLE)); } static void valleyview_init_clock_gating(struct drm_device *dev) From 7338aefa5cad08eb6794aa8e9a5853dedb990aa0 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 21:48:47 +0100 Subject: [PATCH 16/88] drm/i915: Use a global lock for modifying global irq flags We were attempting to use a per-ring spinlock whilst modifying global IRQ flags. A recipe for rare missed interrupts. Signed-off-by: Chris Wilson Acked-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ringbuffer.c | 41 +++++++++++++++---------- drivers/gpu/drm/i915/intel_ringbuffer.h | 3 +- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index f797613e6c4a..b7f446ee28da 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -610,17 +610,18 @@ gen5_ring_get_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; if (!dev->irq_enabled) return false; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) { dev_priv->gt_irq_mask &= ~ring->irq_enable_mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask); POSTING_READ(GTIMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); return true; } @@ -630,14 +631,15 @@ gen5_ring_put_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) { dev_priv->gt_irq_mask |= ring->irq_enable_mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask); POSTING_READ(GTIMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); } static bool @@ -645,17 +647,18 @@ i9xx_ring_get_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; if (!dev->irq_enabled) return false; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) { dev_priv->irq_mask &= ~ring->irq_enable_mask; I915_WRITE(IMR, dev_priv->irq_mask); POSTING_READ(IMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); return true; } @@ -665,14 +668,15 @@ i9xx_ring_put_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) { dev_priv->irq_mask |= ring->irq_enable_mask; I915_WRITE(IMR, dev_priv->irq_mask); POSTING_READ(IMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); } static bool @@ -680,17 +684,18 @@ i8xx_ring_get_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; if (!dev->irq_enabled) return false; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) { dev_priv->irq_mask &= ~ring->irq_enable_mask; I915_WRITE16(IMR, dev_priv->irq_mask); POSTING_READ16(IMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); return true; } @@ -700,14 +705,15 @@ i8xx_ring_put_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) { dev_priv->irq_mask |= ring->irq_enable_mask; I915_WRITE16(IMR, dev_priv->irq_mask); POSTING_READ16(IMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); } void intel_ring_setup_status_page(struct intel_ring_buffer *ring) @@ -786,6 +792,7 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; if (!dev->irq_enabled) return false; @@ -795,14 +802,14 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring) * blt/bsd rings on ivb. */ gen6_gt_force_wake_get(dev_priv); - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) { I915_WRITE_IMR(ring, ~ring->irq_enable_mask); dev_priv->gt_irq_mask &= ~ring->irq_enable_mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask); POSTING_READ(GTIMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); return true; } @@ -812,15 +819,16 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; drm_i915_private_t *dev_priv = dev->dev_private; + unsigned long flags; - spin_lock(&ring->irq_lock); + spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) { I915_WRITE_IMR(ring, ~0); dev_priv->gt_irq_mask |= ring->irq_enable_mask; I915_WRITE(GTIMR, dev_priv->gt_irq_mask); POSTING_READ(GTIMR); } - spin_unlock(&ring->irq_lock); + spin_unlock_irqrestore(&dev_priv->irq_lock, flags); gen6_gt_force_wake_put(dev_priv); } @@ -954,7 +962,6 @@ static int intel_init_ring_buffer(struct drm_device *dev, ring->size = 32 * PAGE_SIZE; init_waitqueue_head(&ring->irq_queue); - spin_lock_init(&ring->irq_lock); if (I915_NEED_GFX_HWS(dev)) { ret = init_status_page(ring); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 06a66adf69c2..e0b25bb2fb5d 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -56,8 +56,7 @@ struct intel_ring_buffer { */ u32 last_retired_head; - spinlock_t irq_lock; - u32 irq_refcount; + u32 irq_refcount; /* protected by dev_priv->irq_lock */ u32 irq_enable_mask; /* bitmask to enable ring interrupt */ u32 irq_seqno; /* last seq seem at irq time */ u32 trace_irq_seqno; From c7bd4c25650704d4d065eb4ce2a122d2a80ce804 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 16:36:50 +0100 Subject: [PATCH 17/88] drm/i915: Remove too early plane enable on pre-PCH hardware Enabling the plane before we have assigned valid address means that it will access random PTE (often with conflicting memory types) and cause GPU lockups. However, enabling the plane too early appears to workaround a number of bugs in our modesetting code. Cc: Franz Melchior References: https://bugs.freedesktop.org/show_bug.cgi?id=39947 References: https://bugs.freedesktop.org/show_bug.cgi?id=41091 References: https://bugs.freedesktop.org/show_bug.cgi?id=49041 Signed-off-by: Chris Wilson Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c5f071daa851..cf9dfcd7a94f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3932,7 +3932,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DSPCNTR(plane), dspcntr); POSTING_READ(DSPCNTR(plane)); - intel_enable_plane(dev_priv, plane, pipe); ret = intel_pipe_set_base(crtc, x, y, old_fb); From 8325a09dd0b138484ccff66b2987f49fa3813ec8 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 15:52:35 +0100 Subject: [PATCH 18/88] drm/i915: Bump the inactive LRU on set-to-GTT-domain Currently, we only bump the inactive LRU of an object when we bind into the GTT for a page-fault. As the object may be used many times before its mapping is zapped, we do not mark it as active as frequently as we should. Userspace should be calling set-to-GTT-domain before each pointer deference (for synchronous access) and so is a good place to mark the buffer as active. Marking the buffer as recently used places it at the end of the inactive eviction queue, though still before anything with outstanding rendering. This reduces the likelihood of evicting a buffer that is going to be used again by the CPU in the near future. This way we can hopefully avoid to kick out upload buffers right before we use them on the gpu. Note that we need to check that the object is not active or pinned, for otherwise we create havoc on the active/pinned lists, which also use obj->mm_list. The active lists are sorted by and evicted in last GPU rendering order, access by the CPU to a still active buffer therefore does not affect its eviction ordering. Pinned objects are currently excluded from eviction, therefore the only list that we need to bump for GTT access by the CPU is the inactive list. Signed-off-by: Chris Wilson [danvet: Added further explanations to the commit message as discussed on irc.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index cfbcf7ef567e..ad2a6924d9ce 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -132,7 +132,7 @@ int i915_mutex_lock_interruptible(struct drm_device *dev) static inline bool i915_gem_object_is_inactive(struct drm_i915_gem_object *obj) { - return obj->gtt_space && !obj->active && obj->pin_count == 0; + return !obj->active && obj->pin_count == 0; } int @@ -2737,6 +2737,7 @@ i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj) int i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) { + drm_i915_private_t *dev_priv = obj->base.dev->dev_private; uint32_t old_write_domain, old_read_domains; int ret; @@ -2777,6 +2778,10 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) old_read_domains, old_write_domain); + /* And bump the LRU for this access */ + if (i915_gem_object_is_inactive(obj)) + list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); + return 0; } From a39d7efc6200d05b9ca3cfeec5dd82f6dd03f4e8 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 18:22:52 +0100 Subject: [PATCH 19/88] drm/i915: Remove i915_gem_evict_inactive() This was only used by one external caller who would just be as happy with evict-everything, so perform the replacement and make the function private. In the process we note that unbinding the inactive list should not fail, and make it a warning instead. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 5 +---- drivers/gpu/drm/i915/i915_gem.c | 9 ++------- drivers/gpu/drm/i915/i915_gem_evict.c | 21 ++++++--------------- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c1440137002a..57f60fa719f4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1370,10 +1370,7 @@ void i915_gem_init_global_gtt(struct drm_device *dev, /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, bool mappable); -int __must_check i915_gem_evict_everything(struct drm_device *dev, - bool purgeable_only); -int __must_check i915_gem_evict_inactive(struct drm_device *dev, - bool purgeable_only); +int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only); /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index ad2a6924d9ce..2fc7c55e0e05 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3455,13 +3455,8 @@ i915_gem_idle(struct drm_device *dev) } /* Under UMS, be paranoid and evict. */ - if (!drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = i915_gem_evict_inactive(dev, false); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } - } + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + i915_gem_evict_everything(dev, false); i915_gem_reset_fences(dev); diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 21a82710f4b2..399a3a8946b3 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -166,8 +166,9 @@ int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) { drm_i915_private_t *dev_priv = dev->dev_private; - int ret; + struct drm_i915_gem_object *obj, *next; bool lists_empty; + int ret; lists_empty = (list_empty(&dev_priv->mm.inactive_list) && list_empty(&dev_priv->mm.flushing_list) && @@ -184,24 +185,14 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); - return i915_gem_evict_inactive(dev, purgeable_only); -} - -/** Unbinds all inactive objects. */ -int -i915_gem_evict_inactive(struct drm_device *dev, bool purgeable_only) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_gem_object *obj, *next; - + /* Having flushed everything, unbind() should never raise an error */ list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list) { if (!purgeable_only || obj->madv != I915_MADV_WILLNEED) { - int ret = i915_gem_object_unbind(obj); - if (ret) - return ret; + if (obj->pin_count == 0) + WARN_ON(i915_gem_object_unbind(obj)); } } - return 0; + return ret; } From 1b50247a8ddde4af5aaa0e6bc125615372ce6c16 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 15:47:30 +0100 Subject: [PATCH 20/88] drm/i915: Remove the list of pinned inactive objects Simplify object tracking by removing the inactive but pinned list. The only place where this was used is for counting the available memory, which is just as easy performed by checking all objects on the rare occasions it is required (application startup). For ease of debugging, we keep the reporting of pinned objects through the error-state and debugfs. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 16 ++---- drivers/gpu/drm/i915/i915_drv.h | 6 -- drivers/gpu/drm/i915/i915_gem.c | 38 +++---------- drivers/gpu/drm/i915/i915_gem_debug.c | 16 ------ drivers/gpu/drm/i915/i915_gem_evict.c | 10 ++-- drivers/gpu/drm/i915/i915_irq.c | 80 +++++++++++++++++---------- 6 files changed, 68 insertions(+), 98 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 54a10667fe82..ecf746837b23 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -178,10 +178,6 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) seq_printf(m, "Inactive:\n"); head = &dev_priv->mm.inactive_list; break; - case PINNED_LIST: - seq_printf(m, "Pinned:\n"); - head = &dev_priv->mm.pinned_list; - break; case FLUSHING_LIST: seq_printf(m, "Flushing:\n"); head = &dev_priv->mm.flushing_list; @@ -251,11 +247,6 @@ static int i915_gem_object_info(struct seq_file *m, void* data) seq_printf(m, " %u [%u] active objects, %zu [%zu] bytes\n", count, mappable_count, size, mappable_size); - size = count = mappable_size = mappable_count = 0; - count_objects(&dev_priv->mm.pinned_list, mm_list); - seq_printf(m, " %u [%u] pinned objects, %zu [%zu] bytes\n", - count, mappable_count, size, mappable_size); - size = count = mappable_size = mappable_count = 0; count_objects(&dev_priv->mm.inactive_list, mm_list); seq_printf(m, " %u [%u] inactive objects, %zu [%zu] bytes\n", @@ -294,6 +285,7 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data) { struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; + uintptr_t list = (uintptr_t) node->info_ent->data; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; size_t total_obj_size, total_gtt_size; @@ -305,6 +297,9 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data) total_obj_size = total_gtt_size = count = 0; list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + if (list == PINNED_LIST && obj->pin_count == 0) + continue; + seq_printf(m, " "); describe_obj(m, obj); seq_printf(m, "\n"); @@ -321,7 +316,6 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data) return 0; } - static int i915_gem_pageflip_info(struct seq_file *m, void *data) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -1842,10 +1836,10 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_capabilities", i915_capabilities, 0}, {"i915_gem_objects", i915_gem_object_info, 0}, {"i915_gem_gtt", i915_gem_gtt_info, 0}, + {"i915_gem_pinned", i915_gem_gtt_info, 0, (void *) PINNED_LIST}, {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST}, {"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST}, {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST}, - {"i915_gem_pinned", i915_gem_object_list_info, 0, (void *) PINNED_LIST}, {"i915_gem_deferred_free", i915_gem_object_list_info, 0, (void *) DEFERRED_FREE_LIST}, {"i915_gem_pageflip", i915_gem_pageflip_info, 0}, {"i915_gem_request", i915_gem_request_info, 0}, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 57f60fa719f4..560ce7f44a3b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -689,12 +689,6 @@ typedef struct drm_i915_private { */ struct list_head inactive_list; - /** - * LRU list of objects which are not in the ringbuffer but - * are still pinned in the GTT. - */ - struct list_head pinned_list; - /** LRU list of objects with fence regs on them. */ struct list_head fence_list; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 2fc7c55e0e05..bf4683c9aed8 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -132,7 +132,7 @@ int i915_mutex_lock_interruptible(struct drm_device *dev) static inline bool i915_gem_object_is_inactive(struct drm_i915_gem_object *obj) { - return !obj->active && obj->pin_count == 0; + return !obj->active; } int @@ -171,8 +171,9 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, pinned = 0; mutex_lock(&dev->struct_mutex); - list_for_each_entry(obj, &dev_priv->mm.pinned_list, mm_list) - pinned += obj->gtt_space->size; + list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) + if (obj->pin_count) + pinned += obj->gtt_space->size; mutex_unlock(&dev->struct_mutex); args->aper_size = dev_priv->mm.gtt_total; @@ -1455,10 +1456,7 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - if (obj->pin_count != 0) - list_move_tail(&obj->mm_list, &dev_priv->mm.pinned_list); - else - list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); + list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); BUG_ON(!list_empty(&obj->gpu_write_list)); BUG_ON(!obj->active); @@ -3063,12 +3061,9 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, bool map_and_fenceable) { - struct drm_device *dev = obj->base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; int ret; BUG_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT); - WARN_ON(i915_verify_lists(dev)); if (obj->gtt_space != NULL) { if ((alignment && obj->gtt_offset & (alignment - 1)) || @@ -3096,34 +3091,20 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, if (!obj->has_global_gtt_mapping && map_and_fenceable) i915_gem_gtt_bind_object(obj, obj->cache_level); - if (obj->pin_count++ == 0) { - if (!obj->active) - list_move_tail(&obj->mm_list, - &dev_priv->mm.pinned_list); - } + obj->pin_count++; obj->pin_mappable |= map_and_fenceable; - WARN_ON(i915_verify_lists(dev)); return 0; } void i915_gem_object_unpin(struct drm_i915_gem_object *obj) { - struct drm_device *dev = obj->base.dev; - drm_i915_private_t *dev_priv = dev->dev_private; - - WARN_ON(i915_verify_lists(dev)); BUG_ON(obj->pin_count == 0); BUG_ON(obj->gtt_space == NULL); - if (--obj->pin_count == 0) { - if (!obj->active) - list_move_tail(&obj->mm_list, - &dev_priv->mm.inactive_list); + if (--obj->pin_count == 0) obj->pin_mappable = false; - } - WARN_ON(i915_verify_lists(dev)); } int @@ -3426,12 +3407,10 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); struct drm_device *dev = obj->base.dev; - while (obj->pin_count > 0) - i915_gem_object_unpin(obj); - if (obj->phys_obj) i915_gem_detach_phys_object(dev, obj); + obj->pin_count = 0; i915_gem_free_object_tail(obj); } @@ -3699,7 +3678,6 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->mm.active_list); INIT_LIST_HEAD(&dev_priv->mm.flushing_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); - INIT_LIST_HEAD(&dev_priv->mm.pinned_list); INIT_LIST_HEAD(&dev_priv->mm.fence_list); INIT_LIST_HEAD(&dev_priv->mm.deferred_free_list); INIT_LIST_HEAD(&dev_priv->mm.gtt_list); diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c index cc93cac242d6..a4f6aaabca99 100644 --- a/drivers/gpu/drm/i915/i915_gem_debug.c +++ b/drivers/gpu/drm/i915/i915_gem_debug.c @@ -114,22 +114,6 @@ i915_verify_lists(struct drm_device *dev) } } - list_for_each_entry(obj, &dev_priv->mm.pinned_list, list) { - if (obj->base.dev != dev || - !atomic_read(&obj->base.refcount.refcount)) { - DRM_ERROR("freed pinned %p\n", obj); - err++; - break; - } else if (!obj->pin_count || obj->active || - (obj->base.write_domain & I915_GEM_GPU_DOMAINS)) { - DRM_ERROR("invalid pinned %p (p %d a %d w %x)\n", - obj, - obj->pin_count, obj->active, - obj->base.write_domain); - err++; - } - } - return warned = err; } #endif /* WATCH_INACTIVE */ diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 399a3a8946b3..91ebb94d7c8b 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -35,6 +35,9 @@ static bool mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind) { + if (obj->pin_count) + return false; + list_add(&obj->exec_list, unwind); return drm_mm_scan_add_block(obj->gtt_space); } @@ -90,7 +93,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, /* Now merge in the soon-to-be-expired objects... */ list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { /* Does the object require an outstanding flush? */ - if (obj->base.write_domain || obj->pin_count) + if (obj->base.write_domain) continue; if (mark_free(obj, &unwind_list)) @@ -99,14 +102,11 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, /* Finally add anything with a pending flush (in order of retirement) */ list_for_each_entry(obj, &dev_priv->mm.flushing_list, mm_list) { - if (obj->pin_count) - continue; - if (mark_free(obj, &unwind_list)) goto found; } list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { - if (!obj->base.write_domain || obj->pin_count) + if (!obj->base.write_domain) continue; if (mark_free(obj, &unwind_list)) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 26172eef9787..24e1dd252575 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -918,37 +918,56 @@ i915_error_state_free(struct drm_device *dev, kfree(error->overlay); kfree(error); } +static void capture_bo(struct drm_i915_error_buffer *err, + struct drm_i915_gem_object *obj) +{ + err->size = obj->base.size; + err->name = obj->base.name; + err->seqno = obj->last_rendering_seqno; + err->gtt_offset = obj->gtt_offset; + err->read_domains = obj->base.read_domains; + err->write_domain = obj->base.write_domain; + err->fence_reg = obj->fence_reg; + err->pinned = 0; + if (obj->pin_count > 0) + err->pinned = 1; + if (obj->user_pin_count > 0) + err->pinned = -1; + err->tiling = obj->tiling_mode; + err->dirty = obj->dirty; + err->purgeable = obj->madv != I915_MADV_WILLNEED; + err->ring = obj->ring ? obj->ring->id : -1; + err->cache_level = obj->cache_level; +} -static u32 capture_bo_list(struct drm_i915_error_buffer *err, - int count, - struct list_head *head) +static u32 capture_active_bo(struct drm_i915_error_buffer *err, + int count, struct list_head *head) { struct drm_i915_gem_object *obj; int i = 0; list_for_each_entry(obj, head, mm_list) { - err->size = obj->base.size; - err->name = obj->base.name; - err->seqno = obj->last_rendering_seqno; - err->gtt_offset = obj->gtt_offset; - err->read_domains = obj->base.read_domains; - err->write_domain = obj->base.write_domain; - err->fence_reg = obj->fence_reg; - err->pinned = 0; - if (obj->pin_count > 0) - err->pinned = 1; - if (obj->user_pin_count > 0) - err->pinned = -1; - err->tiling = obj->tiling_mode; - err->dirty = obj->dirty; - err->purgeable = obj->madv != I915_MADV_WILLNEED; - err->ring = obj->ring ? obj->ring->id : -1; - err->cache_level = obj->cache_level; - + capture_bo(err++, obj); if (++i == count) break; + } - err++; + return i; +} + +static u32 capture_pinned_bo(struct drm_i915_error_buffer *err, + int count, struct list_head *head) +{ + struct drm_i915_gem_object *obj; + int i = 0; + + list_for_each_entry(obj, head, gtt_list) { + if (obj->pin_count == 0) + continue; + + capture_bo(err++, obj); + if (++i == count) + break; } return i; @@ -1155,8 +1174,9 @@ static void i915_capture_error_state(struct drm_device *dev) list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) i++; error->active_bo_count = i; - list_for_each_entry(obj, &dev_priv->mm.pinned_list, mm_list) - i++; + list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) + if (obj->pin_count) + i++; error->pinned_bo_count = i - error->active_bo_count; error->active_bo = NULL; @@ -1171,15 +1191,15 @@ static void i915_capture_error_state(struct drm_device *dev) if (error->active_bo) error->active_bo_count = - capture_bo_list(error->active_bo, - error->active_bo_count, - &dev_priv->mm.active_list); + capture_active_bo(error->active_bo, + error->active_bo_count, + &dev_priv->mm.active_list); if (error->pinned_bo) error->pinned_bo_count = - capture_bo_list(error->pinned_bo, - error->pinned_bo_count, - &dev_priv->mm.pinned_list); + capture_pinned_bo(error->pinned_bo, + error->pinned_bo_count, + &dev_priv->mm.gtt_list); do_gettimeofday(&error->time); From 1488fc08c1706288616c602416654fd38c773deb Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 15:47:31 +0100 Subject: [PATCH 21/88] drm/i915: Remove the deferred-free list The use of the mm_list by deferred-free breaks the following patches to extend the range of objects tracked. We can simplify things if we just make the unbind during free uninterrutible. Note that unbinding should never fail, because we hold an additional reference on every active object. Only the ilk vt-d workaround breaks this, but already takes care of not failing by waiting for the gpu to quiescent non-interruptible. But the existence of the deferred free list casted some doubts on this theory, hence WARN if the unbind fails and only then retry non-interruptible. We can kill this additional code after a release in case the theory is indeed right and no one has hit that WARN. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 11 ------ drivers/gpu/drm/i915/i915_drv.h | 8 ---- drivers/gpu/drm/i915/i915_gem.c | 58 ++++++++++------------------- 3 files changed, 19 insertions(+), 58 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index ecf746837b23..120db4687a2f 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -47,7 +47,6 @@ enum { FLUSHING_LIST, INACTIVE_LIST, PINNED_LIST, - DEFERRED_FREE_LIST, }; static const char *yesno(int v) @@ -182,10 +181,6 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) seq_printf(m, "Flushing:\n"); head = &dev_priv->mm.flushing_list; break; - case DEFERRED_FREE_LIST: - seq_printf(m, "Deferred free:\n"); - head = &dev_priv->mm.deferred_free_list; - break; default: mutex_unlock(&dev->struct_mutex); return -EINVAL; @@ -252,11 +247,6 @@ static int i915_gem_object_info(struct seq_file *m, void* data) seq_printf(m, " %u [%u] inactive objects, %zu [%zu] bytes\n", count, mappable_count, size, mappable_size); - size = count = mappable_size = mappable_count = 0; - count_objects(&dev_priv->mm.deferred_free_list, mm_list); - seq_printf(m, " %u [%u] freed objects, %zu [%zu] bytes\n", - count, mappable_count, size, mappable_size); - size = count = mappable_size = mappable_count = 0; list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { if (obj->fault_mappable) { @@ -1840,7 +1830,6 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST}, {"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST}, {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST}, - {"i915_gem_deferred_free", i915_gem_object_list_info, 0, (void *) DEFERRED_FREE_LIST}, {"i915_gem_pageflip", i915_gem_pageflip_info, 0}, {"i915_gem_request", i915_gem_request_info, 0}, {"i915_gem_seqno", i915_gem_seqno_info, 0}, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 560ce7f44a3b..21127aff3900 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -692,14 +692,6 @@ typedef struct drm_i915_private { /** LRU list of objects with fence regs on them. */ struct list_head fence_list; - /** - * List of objects currently pending being freed. - * - * These objects are no longer in use, but due to a signal - * we were prevented from freeing them at the appointed time. - */ - struct list_head deferred_free_list; - /** * We leave the user IRQ off as much as possible, * but this means that requests will finish and never diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index bf4683c9aed8..9bf11c8fe920 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -46,7 +46,6 @@ static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_i915_gem_object *obj, struct drm_i915_gem_pwrite *args, struct drm_file *file); -static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj); static void i915_gem_write_fence(struct drm_device *dev, int reg, struct drm_i915_gem_object *obj); @@ -1782,20 +1781,6 @@ i915_gem_retire_requests(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; int i; - if (!list_empty(&dev_priv->mm.deferred_free_list)) { - struct drm_i915_gem_object *obj, *next; - - /* We must be careful that during unbind() we do not - * accidentally infinitely recurse into retire requests. - * Currently: - * retire -> free -> unbind -> wait -> retire_ring - */ - list_for_each_entry_safe(obj, next, - &dev_priv->mm.deferred_free_list, - mm_list) - i915_gem_free_object_tail(obj); - } - for (i = 0; i < I915_NUM_RINGS; i++) i915_gem_retire_requests_ring(&dev_priv->ring[i]); } @@ -2067,7 +2052,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) } ret = i915_gem_object_finish_gpu(obj); - if (ret == -ERESTARTSYS) + if (ret) return ret; /* Continue on if we fail due to EIO, the GPU is hung so we * should be safe and we need to cleanup or else we might @@ -2094,7 +2079,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) /* release the fence reg _after_ flushing */ ret = i915_gem_object_put_fence(obj); - if (ret == -ERESTARTSYS) + if (ret) return ret; trace_i915_gem_object_unbind(obj); @@ -3377,21 +3362,29 @@ int i915_gem_init_object(struct drm_gem_object *obj) return 0; } -static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj) +void i915_gem_free_object(struct drm_gem_object *gem_obj) { + struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); struct drm_device *dev = obj->base.dev; drm_i915_private_t *dev_priv = dev->dev_private; - int ret; - - ret = i915_gem_object_unbind(obj); - if (ret == -ERESTARTSYS) { - list_move(&obj->mm_list, - &dev_priv->mm.deferred_free_list); - return; - } trace_i915_gem_object_destroy(obj); + if (obj->phys_obj) + i915_gem_detach_phys_object(dev, obj); + + obj->pin_count = 0; + if (WARN_ON(i915_gem_object_unbind(obj) == -ERESTARTSYS)) { + bool was_interruptible; + + was_interruptible = dev_priv->mm.interruptible; + dev_priv->mm.interruptible = false; + + WARN_ON(i915_gem_object_unbind(obj)); + + dev_priv->mm.interruptible = was_interruptible; + } + if (obj->base.map_list.map) drm_gem_free_mmap_offset(&obj->base); @@ -3402,18 +3395,6 @@ static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj) kfree(obj); } -void i915_gem_free_object(struct drm_gem_object *gem_obj) -{ - struct drm_i915_gem_object *obj = to_intel_bo(gem_obj); - struct drm_device *dev = obj->base.dev; - - if (obj->phys_obj) - i915_gem_detach_phys_object(dev, obj); - - obj->pin_count = 0; - i915_gem_free_object_tail(obj); -} - int i915_gem_idle(struct drm_device *dev) { @@ -3679,7 +3660,6 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->mm.flushing_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); INIT_LIST_HEAD(&dev_priv->mm.fence_list); - INIT_LIST_HEAD(&dev_priv->mm.deferred_free_list); INIT_LIST_HEAD(&dev_priv->mm.gtt_list); for (i = 0; i < I915_NUM_RINGS; i++) init_ring_lists(&dev_priv->ring[i]); From 9797fbfbcfe251b5297b82803002c713e61aa410 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 15:47:39 +0100 Subject: [PATCH 22/88] drm/i915: Split the stolen handling for GEM out of i915_dma.c We slightly modify the initialisation sequence to move the initialisation of the memory managers earlier and in particular before probing outputs and detecting any existing output configuration. This is essential if we wish to track preallocated objects and preserve them whilst initialising GEM. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/i915_dma.c | 161 ++------------------ drivers/gpu/drm/i915/i915_drv.h | 4 + drivers/gpu/drm/i915/i915_gem_stolen.c | 202 +++++++++++++++++++++++++ 4 files changed, 219 insertions(+), 149 deletions(-) create mode 100644 drivers/gpu/drm/i915/i915_gem_stolen.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index b65c06f1a021..8b8bbc70f86b 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -11,6 +11,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ i915_gem_evict.o \ i915_gem_execbuffer.o \ i915_gem_gtt.o \ + i915_gem_stolen.o \ i915_gem_tiling.o \ i915_sysfs.o \ i915_trace_points.o \ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index a813f652fa1f..e3f1488b9933 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1019,133 +1019,6 @@ intel_teardown_mchbar(struct drm_device *dev) release_resource(&dev_priv->mch_res); } -#define PTE_ADDRESS_MASK 0xfffff000 -#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ -#define PTE_MAPPING_TYPE_UNCACHED (0 << 1) -#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ -#define PTE_MAPPING_TYPE_CACHED (3 << 1) -#define PTE_MAPPING_TYPE_MASK (3 << 1) -#define PTE_VALID (1 << 0) - -/** - * i915_stolen_to_phys - take an offset into stolen memory and turn it into - * a physical one - * @dev: drm device - * @offset: address to translate - * - * Some chip functions require allocations from stolen space and need the - * physical address of the memory in question. - */ -static unsigned long i915_stolen_to_phys(struct drm_device *dev, u32 offset) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct pci_dev *pdev = dev_priv->bridge_dev; - u32 base; - -#if 0 - /* On the machines I have tested the Graphics Base of Stolen Memory - * is unreliable, so compute the base by subtracting the stolen memory - * from the Top of Low Usable DRAM which is where the BIOS places - * the graphics stolen memory. - */ - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - /* top 32bits are reserved = 0 */ - pci_read_config_dword(pdev, 0xA4, &base); - } else { - /* XXX presume 8xx is the same as i915 */ - pci_bus_read_config_dword(pdev->bus, 2, 0x5C, &base); - } -#else - if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { - u16 val; - pci_read_config_word(pdev, 0xb0, &val); - base = val >> 4 << 20; - } else { - u8 val; - pci_read_config_byte(pdev, 0x9c, &val); - base = val >> 3 << 27; - } - base -= dev_priv->mm.gtt->stolen_size; -#endif - - return base + offset; -} - -static void i915_warn_stolen(struct drm_device *dev) -{ - DRM_ERROR("not enough stolen space for compressed buffer, disabling\n"); - DRM_ERROR("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); -} - -static void i915_setup_compression(struct drm_device *dev, int size) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb); - unsigned long cfb_base; - unsigned long ll_base = 0; - - /* Just in case the BIOS is doing something questionable. */ - intel_disable_fbc(dev); - - compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); - if (compressed_fb) - compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); - if (!compressed_fb) - goto err; - - cfb_base = i915_stolen_to_phys(dev, compressed_fb->start); - if (!cfb_base) - goto err_fb; - - if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) { - compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen, - 4096, 4096, 0); - if (compressed_llb) - compressed_llb = drm_mm_get_block(compressed_llb, - 4096, 4096); - if (!compressed_llb) - goto err_fb; - - ll_base = i915_stolen_to_phys(dev, compressed_llb->start); - if (!ll_base) - goto err_llb; - } - - dev_priv->cfb_size = size; - - dev_priv->compressed_fb = compressed_fb; - if (HAS_PCH_SPLIT(dev)) - I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); - else if (IS_GM45(dev)) { - I915_WRITE(DPFC_CB_BASE, compressed_fb->start); - } else { - I915_WRITE(FBC_CFB_BASE, cfb_base); - I915_WRITE(FBC_LL_BASE, ll_base); - dev_priv->compressed_llb = compressed_llb; - } - - DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", - cfb_base, ll_base, size >> 20); - return; - -err_llb: - drm_mm_put_block(compressed_llb); -err_fb: - drm_mm_put_block(compressed_fb); -err: - dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; - i915_warn_stolen(dev); -} - -static void i915_cleanup_compression(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - drm_mm_put_block(dev_priv->compressed_fb); - if (dev_priv->compressed_llb) - drm_mm_put_block(dev_priv->compressed_llb); -} - /* true = enable decode, false = disable decoder */ static unsigned int i915_vga_set_decode(void *cookie, bool state) { @@ -1207,16 +1080,12 @@ intel_enable_ppgtt(struct drm_device *dev) static int i915_load_gem_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long prealloc_size, gtt_size, mappable_size; + unsigned long gtt_size, mappable_size; int ret; - prealloc_size = dev_priv->mm.gtt->stolen_size; gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - /* Basic memrange allocator for stolen space */ - drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); - mutex_lock(&dev->struct_mutex); if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { /* PPGTT pdes are stolen from global gtt ptes, so shrink the @@ -1252,20 +1121,6 @@ static int i915_load_gem_init(struct drm_device *dev) return ret; } - /* Try to set up FBC with a reasonable compressed buffer size */ - if (I915_HAS_FBC(dev) && i915_powersave) { - int cfb_size; - - /* Leave 1M for line length buffer & misc. */ - - /* Try to get a 32M buffer... */ - if (prealloc_size > (36*1024*1024)) - cfb_size = 32*1024*1024; - else /* fall back to 7/8 of the stolen space */ - cfb_size = prealloc_size * 7 / 8; - i915_setup_compression(dev, cfb_size); - } - /* Allow hardware batchbuffers unless told otherwise. */ dev_priv->allow_batchbuffer = 1; return 0; @@ -1304,11 +1159,18 @@ static int i915_load_modeset_init(struct drm_device *dev) if (IS_GEN3(dev) && (I915_READ(ECOSKPD) & ECO_FLIP_DONE)) dev_priv->flip_pending_is_done = true; + /* Initialise stolen first so that we may reserve preallocated + * objects for the BIOS to KMS transition. + */ + ret = i915_gem_init_stolen(dev); + if (ret) + goto cleanup_vga_switcheroo; + intel_modeset_init(dev); ret = i915_load_gem_init(dev); if (ret) - goto cleanup_vga_switcheroo; + goto cleanup_gem_stolen; intel_modeset_gem_init(dev); @@ -1338,6 +1200,8 @@ static int i915_load_modeset_init(struct drm_device *dev) i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); +cleanup_gem_stolen: + i915_gem_cleanup_stolen(dev); cleanup_vga_switcheroo: vga_switcheroo_unregister_client(dev->pdev); cleanup_vga_client: @@ -2252,8 +2116,7 @@ int i915_driver_unload(struct drm_device *dev) i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); i915_gem_cleanup_aliasing_ppgtt(dev); - if (I915_HAS_FBC(dev) && i915_powersave) - i915_cleanup_compression(dev); + i915_gem_cleanup_stolen(dev); drm_mm_takedown(&dev_priv->mm.stolen); intel_cleanup_overlay(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 21127aff3900..b0b761f4c23a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1358,6 +1358,10 @@ int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, bool mappable); int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only); +/* i915_gem_stolen.c */ +int i915_gem_init_stolen(struct drm_device *dev); +void i915_gem_cleanup_stolen(struct drm_device *dev); + /* i915_gem_tiling.c */ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c new file mode 100644 index 000000000000..ada2e90a2a60 --- /dev/null +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -0,0 +1,202 @@ +/* + * Copyright © 2008-2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Eric Anholt + * Chris Wilson + * + */ + +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/* + * The BIOS typically reserves some of the system's memory for the exclusive + * use of the integrated graphics. This memory is no longer available for + * use by the OS and so the user finds that his system has less memory + * available than he put in. We refer to this memory as stolen. + * + * The BIOS will allocate its framebuffer from the stolen memory. Our + * goal is try to reuse that object for our own fbcon which must always + * be available for panics. Anything else we can reuse the stolen memory + * for is a boon. + */ + +#define PTE_ADDRESS_MASK 0xfffff000 +#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */ +#define PTE_MAPPING_TYPE_UNCACHED (0 << 1) +#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */ +#define PTE_MAPPING_TYPE_CACHED (3 << 1) +#define PTE_MAPPING_TYPE_MASK (3 << 1) +#define PTE_VALID (1 << 0) + +/** + * i915_stolen_to_phys - take an offset into stolen memory and turn it into + * a physical one + * @dev: drm device + * @offset: address to translate + * + * Some chip functions require allocations from stolen space and need the + * physical address of the memory in question. + */ +static unsigned long i915_stolen_to_phys(struct drm_device *dev, u32 offset) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct pci_dev *pdev = dev_priv->bridge_dev; + u32 base; + +#if 0 + /* On the machines I have tested the Graphics Base of Stolen Memory + * is unreliable, so compute the base by subtracting the stolen memory + * from the Top of Low Usable DRAM which is where the BIOS places + * the graphics stolen memory. + */ + if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { + /* top 32bits are reserved = 0 */ + pci_read_config_dword(pdev, 0xA4, &base); + } else { + /* XXX presume 8xx is the same as i915 */ + pci_bus_read_config_dword(pdev->bus, 2, 0x5C, &base); + } +#else + if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { + u16 val; + pci_read_config_word(pdev, 0xb0, &val); + base = val >> 4 << 20; + } else { + u8 val; + pci_read_config_byte(pdev, 0x9c, &val); + base = val >> 3 << 27; + } + base -= dev_priv->mm.gtt->stolen_size; +#endif + + return base + offset; +} + +static void i915_warn_stolen(struct drm_device *dev) +{ + DRM_INFO("not enough stolen space for compressed buffer, disabling\n"); + DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); +} + +static void i915_setup_compression(struct drm_device *dev, int size) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb); + unsigned long cfb_base; + unsigned long ll_base = 0; + + /* Just in case the BIOS is doing something questionable. */ + intel_disable_fbc(dev); + + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); + if (compressed_fb) + compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); + if (!compressed_fb) + goto err; + + cfb_base = i915_stolen_to_phys(dev, compressed_fb->start); + if (!cfb_base) + goto err_fb; + + if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) { + compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen, + 4096, 4096, 0); + if (compressed_llb) + compressed_llb = drm_mm_get_block(compressed_llb, + 4096, 4096); + if (!compressed_llb) + goto err_fb; + + ll_base = i915_stolen_to_phys(dev, compressed_llb->start); + if (!ll_base) + goto err_llb; + } + + dev_priv->cfb_size = size; + + dev_priv->compressed_fb = compressed_fb; + if (HAS_PCH_SPLIT(dev)) + I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); + else if (IS_GM45(dev)) { + I915_WRITE(DPFC_CB_BASE, compressed_fb->start); + } else { + I915_WRITE(FBC_CFB_BASE, cfb_base); + I915_WRITE(FBC_LL_BASE, ll_base); + dev_priv->compressed_llb = compressed_llb; + } + + DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", + cfb_base, ll_base, size >> 20); + return; + +err_llb: + drm_mm_put_block(compressed_llb); +err_fb: + drm_mm_put_block(compressed_fb); +err: + dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; + i915_warn_stolen(dev); +} + +static void i915_cleanup_compression(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + drm_mm_put_block(dev_priv->compressed_fb); + if (dev_priv->compressed_llb) + drm_mm_put_block(dev_priv->compressed_llb); +} + +void i915_gem_cleanup_stolen(struct drm_device *dev) +{ + if (I915_HAS_FBC(dev) && i915_powersave) + i915_cleanup_compression(dev); +} + +int i915_gem_init_stolen(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size; + + /* Basic memrange allocator for stolen space */ + drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); + + /* Try to set up FBC with a reasonable compressed buffer size */ + if (I915_HAS_FBC(dev) && i915_powersave) { + int cfb_size; + + /* Leave 1M for line length buffer & misc. */ + + /* Try to get a 32M buffer... */ + if (prealloc_size > (36*1024*1024)) + cfb_size = 32*1024*1024; + else /* fall back to 7/8 of the stolen space */ + cfb_size = prealloc_size * 7 / 8; + i915_setup_compression(dev, cfb_size); + } + + return 0; +} From 1070a42b6bc5cc10a33d2fe22f0b295a0194a582 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 15:47:41 +0100 Subject: [PATCH 23/88] drm/i915: Move GEM initialisation from i915_dma.c to i915_gem.c Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 66 +-------------------------------- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 64 ++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index e3f1488b9933..9e80d5afd270 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1062,70 +1062,6 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev) return can_switch; } -static bool -intel_enable_ppgtt(struct drm_device *dev) -{ - if (i915_enable_ppgtt >= 0) - return i915_enable_ppgtt; - -#ifdef CONFIG_INTEL_IOMMU - /* Disable ppgtt on SNB if VT-d is on. */ - if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) - return false; -#endif - - return true; -} - -static int i915_load_gem_init(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long gtt_size, mappable_size; - int ret; - - gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; - mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; - - mutex_lock(&dev->struct_mutex); - if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { - /* PPGTT pdes are stolen from global gtt ptes, so shrink the - * aperture accordingly when using aliasing ppgtt. */ - gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; - - i915_gem_init_global_gtt(dev, 0, mappable_size, gtt_size); - - ret = i915_gem_init_aliasing_ppgtt(dev); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ret; - } - } else { - /* Let GEM Manage all of the aperture. - * - * However, leave one page at the end still bound to the scratch - * page. There are a number of places where the hardware - * apparently prefetches past the end of the object, and we've - * seen multiple hangs with the GPU head pointer stuck in a - * batchbuffer bound at the last page of the aperture. One page - * should be enough to keep any prefetching inside of the - * aperture. - */ - i915_gem_init_global_gtt(dev, 0, mappable_size, - gtt_size); - } - - ret = i915_gem_init_hw(dev); - mutex_unlock(&dev->struct_mutex); - if (ret) { - i915_gem_cleanup_aliasing_ppgtt(dev); - return ret; - } - - /* Allow hardware batchbuffers unless told otherwise. */ - dev_priv->allow_batchbuffer = 1; - return 0; -} - static int i915_load_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1168,7 +1104,7 @@ static int i915_load_modeset_init(struct drm_device *dev) intel_modeset_init(dev); - ret = i915_load_gem_init(dev); + ret = i915_gem_init(dev); if (ret) goto cleanup_gem_stolen; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b0b761f4c23a..2166da1168c1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1294,6 +1294,7 @@ int __must_check i915_gem_object_set_domain(struct drm_i915_gem_object *obj, uint32_t read_domains, uint32_t write_domain); int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj); +int __must_check i915_gem_init(struct drm_device *dev); int __must_check i915_gem_init_hw(struct drm_device *dev); void i915_gem_init_swizzling(struct drm_device *dev); void i915_gem_init_ppgtt(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 9bf11c8fe920..b402d70dc78b 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3560,6 +3560,70 @@ i915_gem_init_hw(struct drm_device *dev) return ret; } +static bool +intel_enable_ppgtt(struct drm_device *dev) +{ + if (i915_enable_ppgtt >= 0) + return i915_enable_ppgtt; + +#ifdef CONFIG_INTEL_IOMMU + /* Disable ppgtt on SNB if VT-d is on. */ + if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) + return false; +#endif + + return true; +} + +int i915_gem_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long gtt_size, mappable_size; + int ret; + + gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT; + mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + + mutex_lock(&dev->struct_mutex); + if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) { + /* PPGTT pdes are stolen from global gtt ptes, so shrink the + * aperture accordingly when using aliasing ppgtt. */ + gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE; + + i915_gem_init_global_gtt(dev, 0, mappable_size, gtt_size); + + ret = i915_gem_init_aliasing_ppgtt(dev); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return ret; + } + } else { + /* Let GEM Manage all of the aperture. + * + * However, leave one page at the end still bound to the scratch + * page. There are a number of places where the hardware + * apparently prefetches past the end of the object, and we've + * seen multiple hangs with the GPU head pointer stuck in a + * batchbuffer bound at the last page of the aperture. One page + * should be enough to keep any prefetching inside of the + * aperture. + */ + i915_gem_init_global_gtt(dev, 0, mappable_size, + gtt_size); + } + + ret = i915_gem_init_hw(dev); + mutex_unlock(&dev->struct_mutex); + if (ret) { + i915_gem_cleanup_aliasing_ppgtt(dev); + return ret; + } + + /* Allow hardware batchbuffers unless told otherwise. */ + dev_priv->allow_batchbuffer = 1; + return 0; +} + void i915_gem_cleanup_ringbuffer(struct drm_device *dev) { From 13a86b85aca24f825c7843cdcd81eaff19fa4ea9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 14:51:43 +0100 Subject: [PATCH 24/88] drm/i915: CR clock gating is recommend to be set on PineView The specs recommend that this bit be set on PineView. No reason is given, but it sounds like a powersaving bit that we should expect the BIOS to be setting... v2: Rebase on top of _MASKED_ENABLE_BIT Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 93d4ce3fc122..0552058a202f 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2878,6 +2878,9 @@ static void gen3_init_clock_gating(struct drm_device *dev) dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING | DSTATE_DOT_CLOCK_GATING; I915_WRITE(D_STATE, dstate); + + if (IS_PINEVIEW(dev)) + I915_WRITE(ECOSKPD, _MASKED_BIT_ENABLE(ECO_GATING_CX_ONLY)); } static void i85x_init_clock_gating(struct drm_device *dev) From 1cff8f6b4c668a060c7e3f82d3f1fb5aaa37edc4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 24 Apr 2012 09:55:08 +0200 Subject: [PATCH 25/88] drm/i915: properly check for MODESET for kms driver ioctls Also ditch the cargo-culted dev_priv checks - either we have a giant hole in our setup code or this is useless. Plainly bogus to check for it in either case. v2: Chris Wilson noticed that I've missed one bogus dev_priv check. v3: The check in the overlay code is redundant (Chris) Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 7 ++----- drivers/gpu/drm/i915/intel_overlay.c | 12 ++---------- drivers/gpu/drm/i915/intel_sprite.c | 10 ++++------ 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index cf9dfcd7a94f..e1716be47656 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6189,15 +6189,12 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, struct drm_file *file) { - drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data; struct drm_mode_object *drmmode_obj; struct intel_crtc *crtc; - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id, DRM_MODE_OBJECT_CRTC); diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 0f0fe31fc466..a0b5053c5a32 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1122,11 +1122,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, struct put_image_params *params; int ret; - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - + /* No need to check for DRIVER_MODESET - we don't set it up then. */ overlay = dev_priv->overlay; if (!overlay) { DRM_DEBUG("userspace bug: no overlay\n"); @@ -1323,11 +1319,7 @@ int intel_overlay_attrs(struct drm_device *dev, void *data, struct overlay_registers __iomem *regs; int ret; - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - + /* No need to check for DRIVER_MODESET - we don't set it up then. */ overlay = dev_priv->overlay; if (!overlay) { DRM_DEBUG("userspace bug: no overlay\n"); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 44315674f472..2a20fb0781d7 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -558,14 +558,13 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_intel_sprite_colorkey *set = data; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mode_object *obj; struct drm_plane *plane; struct intel_plane *intel_plane; int ret = 0; - if (!dev_priv) - return -EINVAL; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; /* Make sure we don't try to enable both src & dest simultaneously */ if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) @@ -592,14 +591,13 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_intel_sprite_colorkey *get = data; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mode_object *obj; struct drm_plane *plane; struct intel_plane *intel_plane; int ret = 0; - if (!dev_priv) - return -EINVAL; + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; mutex_lock(&dev->mode_config.mutex); From 7bb6fb8dd958ae773ac205282e3c0b56c22e01ed Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 24 Apr 2012 08:22:52 +0200 Subject: [PATCH 26/88] drm/i915: disallow gem ums init ioctl for kms This ioctl used in a kms driver is only useful to create massive havoc. v2: Bail out with -ENODEV as suggested by Chris Wilson. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b402d70dc78b..1b808d041949 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -140,6 +140,9 @@ i915_gem_init_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_init *args = data; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + if (args->gtt_start >= args->gtt_end || (args->gtt_end | args->gtt_start) & (PAGE_SIZE - 1)) return -EINVAL; From 63ed2cb2d15019edf8af324479028996046c8631 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 23 Apr 2012 16:50:50 +0200 Subject: [PATCH 27/88] drm/i915: rip out GEM drm feature checks We always set it so there's no point in checking. We could instead add a bit that tells us whether gem is actually initialized (i.e. either kms or gem_init_ioctl called), but that's imho not worth it. So just rip it out. There's a little change in the wait_ring timeout, but we've never run with anything else than the 60 second timeout, even on dri1 userspace. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 19 +------------------ drivers/gpu/drm/i915/intel_ringbuffer.c | 15 ++++++--------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1b808d041949..0d53eacd293f 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -168,9 +168,6 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_object *obj; size_t pinned; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - pinned = 0; mutex_lock(&dev->struct_mutex); list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) @@ -246,6 +243,7 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_i915_gem_create *args = data; + return i915_gem_create(file, dev, args->size, &args->handle); } @@ -931,9 +929,6 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, uint32_t write_domain = args->write_domain; int ret; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - /* Only handle setting domains to types used by the CPU. */ if (write_domain & I915_GEM_GPU_DOMAINS) return -EINVAL; @@ -987,9 +982,6 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data, struct drm_i915_gem_object *obj; int ret = 0; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; @@ -1025,9 +1017,6 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_gem_object *obj; unsigned long addr; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - obj = drm_gem_object_lookup(dev, file, args->handle); if (obj == NULL) return -ENOENT; @@ -1255,9 +1244,6 @@ i915_gem_mmap_gtt(struct drm_file *file, struct drm_i915_gem_object *obj; int ret; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; @@ -1315,9 +1301,6 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, { struct drm_i915_gem_mmap_gtt *args = data; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index b7f446ee28da..427b7c55ffe0 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1164,15 +1164,12 @@ int intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n) return ret; trace_i915_ring_wait_begin(ring); - if (drm_core_check_feature(dev, DRIVER_GEM)) - /* With GEM the hangcheck timer should kick us out of the loop, - * leaving it early runs the risk of corrupting GEM state (due - * to running on almost untested codepaths). But on resume - * timers don't work yet, so prevent a complete hang in that - * case by choosing an insanely large timeout. */ - end = jiffies + 60 * HZ; - else - end = jiffies + 3 * HZ; + /* With GEM the hangcheck timer should kick us out of the loop, + * leaving it early runs the risk of corrupting GEM state (due + * to running on almost untested codepaths). But on resume + * timers don't work yet, so prevent a complete hang in that + * case by choosing an insanely large timeout. */ + end = jiffies + 60 * HZ; do { ring->head = I915_READ_HEAD(ring); From 2e895b17d7df7af20f7fbdceff249795d2146d01 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 23 Apr 2012 16:50:51 +0200 Subject: [PATCH 28/88] drm/i915: rip out dev_priv->has_gem Always true these days. It has been added originally to work around some issues with the agp layer in 2.6.29: commit ac5c4e76180a74c7f922f6fa71ace0cef45fa433 Author: Dave Airlie Date: Fri Dec 19 15:38:34 2008 +1000 drm/i915: GEM on PAE has problems - disable it for now. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 7 ++----- drivers/gpu/drm/i915/i915_drv.h | 1 - 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 9e80d5afd270..d49bd82e24e9 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -751,7 +751,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = dev->pci_device; break; case I915_PARAM_HAS_GEM: - value = dev_priv->has_gem; + value = 1; break; case I915_PARAM_NUM_FENCES_AVAIL: value = dev_priv->num_fence_regs - dev_priv->fence_reg_start; @@ -764,7 +764,7 @@ static int i915_getparam(struct drm_device *dev, void *data, break; case I915_PARAM_HAS_EXECBUF2: /* depends on GEM */ - value = dev_priv->has_gem; + value = 1; break; case I915_PARAM_HAS_BSD: value = HAS_BSD(dev); @@ -1869,9 +1869,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto out_mtrrfree; } - /* enable GEM by default */ - dev_priv->has_gem = 1; - intel_irq_init(dev); /* Try to make sure MCHBAR is enabled before poking at it */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2166da1168c1..cf2c8966d240 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -339,7 +339,6 @@ typedef struct drm_i915_private { const struct intel_device_info *info; - int has_gem; int relative_constants_mode; void __iomem *regs; From cd9d4e9f9fa2809dedc637406fa15016bfcb2ad0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 24 Apr 2012 08:29:42 +0200 Subject: [PATCH 29/88] drm/i915: check for kms in dri1 ioctls Calling these when gem assumes full control of the hw won't end in anything else than tears. So be a bit more paranoid here. Just serves as documentation. v2: Bail out with ENODEV as suggested by Chris Wilson. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 18 ++++++++++++++++++ drivers/gpu/drm/i915/i915_irq.c | 12 ++++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index d49bd82e24e9..e2983a9a1255 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -239,6 +239,9 @@ static int i915_dma_init(struct drm_device *dev, void *data, drm_i915_init_t *init = data; int retcode = 0; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + switch (init->func) { case I915_INIT_DMA: retcode = i915_initialize(dev, init); @@ -581,6 +584,9 @@ static int i915_flush_ioctl(struct drm_device *dev, void *data, { int ret; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + RING_LOCK_TEST_WITH_RETURN(dev, file_priv); mutex_lock(&dev->struct_mutex); @@ -601,6 +607,9 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, int ret; struct drm_clip_rect *cliprects = NULL; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + if (!dev_priv->allow_batchbuffer) { DRM_ERROR("Batchbuffer ioctl disabled\n"); return -EINVAL; @@ -658,6 +667,9 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, DRM_DEBUG_DRIVER("i915 cmdbuffer, buf %p sz %d cliprects %d\n", cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + RING_LOCK_TEST_WITH_RETURN(dev, file_priv); if (cmdbuf->num_cliprects < 0) @@ -714,6 +726,9 @@ static int i915_flip_bufs(struct drm_device *dev, void *data, { int ret; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + DRM_DEBUG_DRIVER("%s\n", __func__); RING_LOCK_TEST_WITH_RETURN(dev, file_priv); @@ -850,6 +865,9 @@ static int i915_set_status_page(struct drm_device *dev, void *data, drm_i915_hws_addr_t *hws = data; struct intel_ring_buffer *ring = LP_RING(dev_priv); + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + if (!I915_NEED_GFX_HWS(dev)) return -EINVAL; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 24e1dd252575..0211263aad7d 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1612,6 +1612,9 @@ int i915_irq_emit(struct drm_device *dev, void *data, drm_i915_irq_emit_t *emit = data; int result; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + if (!dev_priv || !LP_RING(dev_priv)->virtual_start) { DRM_ERROR("called with no initialization\n"); return -EINVAL; @@ -1639,6 +1642,9 @@ int i915_irq_wait(struct drm_device *dev, void *data, drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_irq_wait_t *irqwait = data; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; @@ -1801,6 +1807,9 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data, { drm_i915_private_t *dev_priv = dev->dev_private; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; @@ -1815,6 +1824,9 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_vblank_pipe_t *pipe = data; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; From 8b2e326dc7c5aa6952c88656d04d0d81fd85a6f8 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:41 +0100 Subject: [PATCH 30/88] drm/i915: Unconditionally initialise the interrupt workers Rather than duplicate similar code across the IRQ installers, perform the initialisation of the workers upfront. This will lead to simpler teardown and quiescent code as we can assume that the workers have been initialised. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0211263aad7d..2dc023d657b0 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2004,10 +2004,6 @@ static void ironlake_irq_preinstall(struct drm_device *dev) atomic_set(&dev_priv->irq_received, 0); - INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); - if (IS_GEN6(dev) || IS_IVYBRIDGE(dev)) - INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work); I915_WRITE(HWSTAM, 0xeffe); @@ -2035,9 +2031,6 @@ static void valleyview_irq_preinstall(struct drm_device *dev) atomic_set(&dev_priv->irq_received, 0); - INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); - /* VLV magic */ I915_WRITE(VLV_IMR, 0); I915_WRITE(RING_IMR(RENDER_RING_BASE), 0); @@ -2306,9 +2299,6 @@ static void i915_driver_irq_preinstall(struct drm_device * dev) atomic_set(&dev_priv->irq_received, 0); - INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); - INIT_WORK(&dev_priv->error_work, i915_error_work_func); - if (I915_HAS_HOTPLUG(dev)) { I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); @@ -2625,6 +2615,12 @@ static void i8xx_irq_uninstall(struct drm_device * dev) 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->rps_work, gen6_pm_rps_work); + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev) || IS_IVYBRIDGE(dev) || From fa883c62af00c577418a06ffb938d98bd6aba1bc Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:42 +0100 Subject: [PATCH 31/88] drm/i915: Remove redundant initialisation of per-ring IRQ waitqueues The waitqueues are already initialised during ring initialisation so kill the redundant and duplicated code to do so in each generations IRQ installer. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2dc023d657b0..82bd8b6d3740 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2085,12 +2085,6 @@ static int ironlake_irq_postinstall(struct drm_device *dev) u32 render_irqs; u32 hotplug_mask; - DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue); - if (HAS_BSD(dev)) - DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue); - if (HAS_BLT(dev)) - DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue); - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; dev_priv->irq_mask = ~display_mask; @@ -2160,12 +2154,6 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) u32 render_irqs; u32 hotplug_mask; - DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue); - if (HAS_BSD(dev)) - DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue); - if (HAS_BLT(dev)) - DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue); - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; dev_priv->irq_mask = ~display_mask; @@ -2216,11 +2204,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) dev_priv->irq_mask = ~enable_mask; - - DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue); - DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue); - DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue); - dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; From e0f608d7fd9b0f22c4fc4873139f9e702e778349 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:43 +0100 Subject: [PATCH 32/88] drm/i915: pending_flip_is_done is gen3, name it so And remove the cargo-culted copy from the valleyview irq handler. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 4 ---- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/i915_irq.c | 18 +++++++++--------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index e2983a9a1255..7a55abbddc99 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1109,10 +1109,6 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_vga_client; - /* IIR "flip pending" bit means done if this bit is set */ - if (IS_GEN3(dev) && (I915_READ(ECOSKPD) & ECO_FLIP_DONE)) - dev_priv->flip_pending_is_done = true; - /* Initialise stolen first so that we may reserve preallocated * objects for the BIOS to KMS transition. */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index cf2c8966d240..552f2504ff23 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -748,7 +748,7 @@ typedef struct drm_i915_private { struct drm_crtc *plane_to_crtc_mapping[3]; struct drm_crtc *pipe_to_crtc_mapping[3]; wait_queue_head_t pending_flip_queue; - bool flip_pending_is_done; + bool gen3_flip_pending_is_done; struct intel_pch_pll pch_plls[I915_NUM_PLLS]; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 82bd8b6d3740..c2a4c12548a8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -541,17 +541,13 @@ static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS) if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) { drm_handle_vblank(dev, 0); vblank++; - if (!dev_priv->flip_pending_is_done) { - intel_finish_page_flip(dev, 0); - } + intel_finish_page_flip(dev, 0); } if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) { drm_handle_vblank(dev, 1); vblank++; - if (!dev_priv->flip_pending_is_done) { - intel_finish_page_flip(dev, 0); - } + intel_finish_page_flip(dev, 0); } if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) @@ -1494,13 +1490,13 @@ static irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { intel_prepare_page_flip(dev, 0); - if (dev_priv->flip_pending_is_done) + if (dev_priv->gen3_flip_pending_is_done) intel_finish_page_flip_plane(dev, 0); } if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { intel_prepare_page_flip(dev, 1); - if (dev_priv->flip_pending_is_done) + if (dev_priv->gen3_flip_pending_is_done) intel_finish_page_flip_plane(dev, 1); } @@ -1508,7 +1504,7 @@ static irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) if (pipe_stats[pipe] & vblank_status && drm_handle_vblank(dev, pipe)) { vblank++; - if (!dev_priv->flip_pending_is_done) { + if (!dev_priv->gen3_flip_pending_is_done) { i915_pageflip_stall_check(dev, pipe); intel_finish_page_flip(dev, pipe); } @@ -2604,6 +2600,10 @@ void intel_irq_init(struct drm_device *dev) INIT_WORK(&dev_priv->error_work, i915_error_work_func); INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work); + /* IIR "flip pending" bit means done if this bit is set */ + if (IS_GEN3(dev) && (I915_READ(ECOSKPD) & ECO_FLIP_DONE)) + dev_priv->gen3_flip_pending_is_done = true; + dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev) || IS_IVYBRIDGE(dev) || From a266c7d548adc85d325e40316f1645258354ca35 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:44 +0100 Subject: [PATCH 33/88] drm/i915: Duplicate and split the gen3/4 irq handler In preparation for rewriting the gen3 irq handler. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 800 +++++++++++++++++++++----------- 1 file changed, 531 insertions(+), 269 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c2a4c12548a8..3b3943395a6a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1401,144 +1401,6 @@ static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) } } -static irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) -{ - struct drm_device *dev = (struct drm_device *) arg; - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct drm_i915_master_private *master_priv; - u32 iir, new_iir; - u32 pipe_stats[I915_MAX_PIPES]; - u32 vblank_status; - int vblank = 0; - unsigned long irqflags; - int irq_received; - int ret = IRQ_NONE, pipe; - bool blc_event = false; - - atomic_inc(&dev_priv->irq_received); - - iir = I915_READ(IIR); - - if (INTEL_INFO(dev)->gen >= 4) - vblank_status = PIPE_START_VBLANK_INTERRUPT_STATUS; - else - vblank_status = PIPE_VBLANK_INTERRUPT_STATUS; - - for (;;) { - irq_received = iir != 0; - - /* Can't rely on pipestat interrupt bit in iir as it might - * have been cleared after the pipestat interrupt was received. - * It doesn't set the bit in iir again, but it still produces - * interrupts (for non-MSI). - */ - spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) - i915_handle_error(dev, false); - - for_each_pipe(pipe) { - int reg = PIPESTAT(pipe); - pipe_stats[pipe] = I915_READ(reg); - - /* - * Clear the PIPE*STAT regs before the IIR - */ - if (pipe_stats[pipe] & 0x8000ffff) { - if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) - DRM_DEBUG_DRIVER("pipe %c underrun\n", - pipe_name(pipe)); - I915_WRITE(reg, pipe_stats[pipe]); - irq_received = 1; - } - } - spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); - - if (!irq_received) - break; - - ret = IRQ_HANDLED; - - /* Consume port. Then clear IIR or we'll miss events */ - if ((I915_HAS_HOTPLUG(dev)) && - (iir & I915_DISPLAY_PORT_INTERRUPT)) { - u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); - - DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", - hotplug_status); - if (hotplug_status & dev_priv->hotplug_supported_mask) - queue_work(dev_priv->wq, - &dev_priv->hotplug_work); - - I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); - I915_READ(PORT_HOTPLUG_STAT); - } - - I915_WRITE(IIR, iir); - new_iir = I915_READ(IIR); /* Flush posted writes */ - - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } - - if (iir & I915_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[RCS]); - if (iir & I915_BSD_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[VCS]); - - if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { - intel_prepare_page_flip(dev, 0); - if (dev_priv->gen3_flip_pending_is_done) - intel_finish_page_flip_plane(dev, 0); - } - - if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { - intel_prepare_page_flip(dev, 1); - if (dev_priv->gen3_flip_pending_is_done) - intel_finish_page_flip_plane(dev, 1); - } - - for_each_pipe(pipe) { - if (pipe_stats[pipe] & vblank_status && - drm_handle_vblank(dev, pipe)) { - vblank++; - if (!dev_priv->gen3_flip_pending_is_done) { - i915_pageflip_stall_check(dev, pipe); - intel_finish_page_flip(dev, pipe); - } - } - - if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) - blc_event = true; - } - - - if (blc_event || (iir & I915_ASLE_INTERRUPT)) - intel_opregion_asle_intr(dev); - - /* With MSI, interrupts are only generated when iir - * transitions from zero to nonzero. If another bit got - * set while we were handling the existing iir bits, then - * we would never get another interrupt. - * - * This is fine on non-MSI as well, as if we hit this path - * we avoid exiting the interrupt handler only to generate - * another one. - * - * Note that for MSI this could cause a stray interrupt report - * if an interrupt landed in the time between writing IIR and - * the posting read. This should be rare enough to never - * trigger the 99% of 100,000 interrupts test for disabling - * stray interrupts. - */ - iir = new_iir; - } - - return ret; -} - static int i915_emit_irq(struct drm_device * dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -2271,106 +2133,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) return 0; } -static void i915_driver_irq_preinstall(struct drm_device * dev) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - int pipe; - - atomic_set(&dev_priv->irq_received, 0); - - if (I915_HAS_HOTPLUG(dev)) { - I915_WRITE(PORT_HOTPLUG_EN, 0); - I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - } - - I915_WRITE(HWSTAM, 0xeffe); - for_each_pipe(pipe) - I915_WRITE(PIPESTAT(pipe), 0); - I915_WRITE(IMR, 0xffffffff); - I915_WRITE(IER, 0x0); - POSTING_READ(IER); -} - -/* - * Must be called after intel_modeset_init or hotplug interrupts won't be - * enabled correctly. - */ -static int i915_driver_irq_postinstall(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; - u32 error_mask; - - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - - /* Unmask the interrupts that we always want on. */ - dev_priv->irq_mask = ~I915_INTERRUPT_ENABLE_FIX; - - dev_priv->pipestat[0] = 0; - dev_priv->pipestat[1] = 0; - - if (I915_HAS_HOTPLUG(dev)) { - /* Enable in IER... */ - enable_mask |= I915_DISPLAY_PORT_INTERRUPT; - /* and unmask in IMR */ - dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT; - } - - /* - * Enable some error detection, note the instruction error mask - * bit is reserved, so we leave it masked. - */ - if (IS_G4X(dev)) { - error_mask = ~(GM45_ERROR_PAGE_TABLE | - GM45_ERROR_MEM_PRIV | - GM45_ERROR_CP_PRIV | - I915_ERROR_MEMORY_REFRESH); - } else { - error_mask = ~(I915_ERROR_PAGE_TABLE | - I915_ERROR_MEMORY_REFRESH); - } - I915_WRITE(EMR, error_mask); - - I915_WRITE(IMR, dev_priv->irq_mask); - I915_WRITE(IER, enable_mask); - POSTING_READ(IER); - - if (I915_HAS_HOTPLUG(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 & SDVOC_HOTPLUG_INT_STATUS) - hotplug_en |= SDVOC_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS) - hotplug_en |= SDVOB_HOTPLUG_INT_EN; - if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) { - hotplug_en |= CRT_HOTPLUG_INT_EN; - - /* Programming the CRT detection parameters tends - to generate a spurious hotplug event about three - seconds later. So just do it once. - */ - if (IS_G4X(dev)) - hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; - hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; - } - - /* Ignore TV since it's buggy */ - - I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); - } - - intel_opregion_enable_asle(dev); - - return 0; -} - static void valleyview_irq_uninstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -2419,33 +2181,6 @@ static void ironlake_irq_uninstall(struct drm_device *dev) I915_WRITE(SDEIIR, I915_READ(SDEIIR)); } -static void i915_driver_irq_uninstall(struct drm_device * dev) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - int pipe; - - if (!dev_priv) - return; - - dev_priv->vblank_pipe = 0; - - if (I915_HAS_HOTPLUG(dev)) { - I915_WRITE(PORT_HOTPLUG_EN, 0); - I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); - } - - I915_WRITE(HWSTAM, 0xffffffff); - for_each_pipe(pipe) - I915_WRITE(PIPESTAT(pipe), 0); - I915_WRITE(IMR, 0xffffffff); - I915_WRITE(IER, 0x0); - - for_each_pipe(pipe) - I915_WRITE(PIPESTAT(pipe), - I915_READ(PIPESTAT(pipe)) & 0x8000ffff); - I915_WRITE(IIR, I915_READ(IIR)); -} - static void i8xx_irq_preinstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -2592,6 +2327,528 @@ static void i8xx_irq_uninstall(struct drm_device * dev) I915_WRITE16(IIR, I915_READ16(IIR)); } +static void i915_irq_preinstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + atomic_set(&dev_priv->irq_received, 0); + + if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + } + + I915_WRITE(HWSTAM, 0xeffe); + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IER, 0x0); + POSTING_READ(IER); +} + +static int i915_irq_postinstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; + u32 error_mask; + + dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; + + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask = ~I915_INTERRUPT_ENABLE_FIX; + + dev_priv->pipestat[0] = 0; + dev_priv->pipestat[1] = 0; + + if (I915_HAS_HOTPLUG(dev)) { + /* Enable in IER... */ + enable_mask |= I915_DISPLAY_PORT_INTERRUPT; + /* and unmask in IMR */ + dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT; + } + + /* + * Enable some error detection, note the instruction error mask + * bit is reserved, so we leave it masked. + */ + if (IS_G4X(dev)) { + error_mask = ~(GM45_ERROR_PAGE_TABLE | + GM45_ERROR_MEM_PRIV | + GM45_ERROR_CP_PRIV | + I915_ERROR_MEMORY_REFRESH); + } else { + error_mask = ~(I915_ERROR_PAGE_TABLE | + I915_ERROR_MEMORY_REFRESH); + } + I915_WRITE(EMR, error_mask); + + I915_WRITE(IMR, dev_priv->irq_mask); + I915_WRITE(IER, enable_mask); + POSTING_READ(IER); + + if (I915_HAS_HOTPLUG(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 & SDVOC_HOTPLUG_INT_STATUS) + hotplug_en |= SDVOC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS) + hotplug_en |= SDVOB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) { + hotplug_en |= CRT_HOTPLUG_INT_EN; + + /* Programming the CRT detection parameters tends + to generate a spurious hotplug event about three + seconds later. So just do it once. + */ + if (IS_G4X(dev)) + hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; + hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; + } + + /* Ignore TV since it's buggy */ + + I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); + } + + intel_opregion_enable_asle(dev); + + return 0; +} + +static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + struct drm_i915_master_private *master_priv; + u32 iir, new_iir; + u32 pipe_stats[I915_MAX_PIPES]; + u32 vblank_status; + int vblank = 0; + unsigned long irqflags; + int irq_received; + int ret = IRQ_NONE, pipe; + bool blc_event = false; + + atomic_inc(&dev_priv->irq_received); + + iir = I915_READ(IIR); + + if (INTEL_INFO(dev)->gen >= 4) + vblank_status = PIPE_START_VBLANK_INTERRUPT_STATUS; + else + vblank_status = PIPE_VBLANK_INTERRUPT_STATUS; + + for (;;) { + irq_received = iir != 0; + + /* Can't rely on pipestat interrupt bit in iir as it might + * have been cleared after the pipestat interrupt was received. + * It doesn't set the bit in iir again, but it still produces + * interrupts (for non-MSI). + */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + i915_handle_error(dev, false); + + for_each_pipe(pipe) { + int reg = PIPESTAT(pipe); + pipe_stats[pipe] = I915_READ(reg); + + /* + * Clear the PIPE*STAT regs before the IIR + */ + if (pipe_stats[pipe] & 0x8000ffff) { + if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) + DRM_DEBUG_DRIVER("pipe %c underrun\n", + pipe_name(pipe)); + I915_WRITE(reg, pipe_stats[pipe]); + irq_received = 1; + } + } + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + + if (!irq_received) + break; + + ret = IRQ_HANDLED; + + /* Consume port. Then clear IIR or we'll miss events */ + if ((I915_HAS_HOTPLUG(dev)) && + (iir & I915_DISPLAY_PORT_INTERRUPT)) { + u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); + + DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", + hotplug_status); + if (hotplug_status & dev_priv->hotplug_supported_mask) + queue_work(dev_priv->wq, + &dev_priv->hotplug_work); + + I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); + I915_READ(PORT_HOTPLUG_STAT); + } + + I915_WRITE(IIR, iir); + new_iir = I915_READ(IIR); /* Flush posted writes */ + + if (dev->primary->master) { + master_priv = dev->primary->master->driver_priv; + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_dispatch = + READ_BREADCRUMB(dev_priv); + } + + if (iir & I915_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[RCS]); + if (iir & I915_BSD_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[VCS]); + + if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { + intel_prepare_page_flip(dev, 0); + if (dev_priv->gen3_flip_pending_is_done) + intel_finish_page_flip_plane(dev, 0); + } + + if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { + intel_prepare_page_flip(dev, 1); + if (dev_priv->gen3_flip_pending_is_done) + intel_finish_page_flip_plane(dev, 1); + } + + for_each_pipe(pipe) { + if (pipe_stats[pipe] & vblank_status && + drm_handle_vblank(dev, pipe)) { + vblank++; + if (!dev_priv->gen3_flip_pending_is_done) { + i915_pageflip_stall_check(dev, pipe); + intel_finish_page_flip(dev, pipe); + } + } + + if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) + blc_event = true; + } + + + if (blc_event || (iir & I915_ASLE_INTERRUPT)) + intel_opregion_asle_intr(dev); + + /* With MSI, interrupts are only generated when iir + * transitions from zero to nonzero. If another bit got + * set while we were handling the existing iir bits, then + * we would never get another interrupt. + * + * This is fine on non-MSI as well, as if we hit this path + * we avoid exiting the interrupt handler only to generate + * another one. + * + * Note that for MSI this could cause a stray interrupt report + * if an interrupt landed in the time between writing IIR and + * the posting read. This should be rare enough to never + * trigger the 99% of 100,000 interrupts test for disabling + * stray interrupts. + */ + iir = new_iir; + } + + return ret; +} + +static void i915_irq_uninstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + if (!dev_priv) + return; + + dev_priv->vblank_pipe = 0; + + if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + } + + I915_WRITE(HWSTAM, 0xffffffff); + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IER, 0x0); + + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), + I915_READ(PIPESTAT(pipe)) & 0x8000ffff); + I915_WRITE(IIR, I915_READ(IIR)); +} + +static void i965_irq_preinstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + atomic_set(&dev_priv->irq_received, 0); + + if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + } + + I915_WRITE(HWSTAM, 0xeffe); + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IER, 0x0); + POSTING_READ(IER); +} + +static int i965_irq_postinstall(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; + u32 error_mask; + + dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; + + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask = ~I915_INTERRUPT_ENABLE_FIX; + + dev_priv->pipestat[0] = 0; + dev_priv->pipestat[1] = 0; + + if (I915_HAS_HOTPLUG(dev)) { + /* Enable in IER... */ + enable_mask |= I915_DISPLAY_PORT_INTERRUPT; + /* and unmask in IMR */ + dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT; + } + + /* + * Enable some error detection, note the instruction error mask + * bit is reserved, so we leave it masked. + */ + if (IS_G4X(dev)) { + error_mask = ~(GM45_ERROR_PAGE_TABLE | + GM45_ERROR_MEM_PRIV | + GM45_ERROR_CP_PRIV | + I915_ERROR_MEMORY_REFRESH); + } else { + error_mask = ~(I915_ERROR_PAGE_TABLE | + I915_ERROR_MEMORY_REFRESH); + } + I915_WRITE(EMR, error_mask); + + I915_WRITE(IMR, dev_priv->irq_mask); + I915_WRITE(IER, enable_mask); + POSTING_READ(IER); + + if (I915_HAS_HOTPLUG(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 & SDVOC_HOTPLUG_INT_STATUS) + hotplug_en |= SDVOC_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & SDVOB_HOTPLUG_INT_STATUS) + hotplug_en |= SDVOB_HOTPLUG_INT_EN; + if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) { + hotplug_en |= CRT_HOTPLUG_INT_EN; + + /* Programming the CRT detection parameters tends + to generate a spurious hotplug event about three + seconds later. So just do it once. + */ + if (IS_G4X(dev)) + hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; + hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; + } + + /* 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(DRM_IRQ_ARGS) +{ + struct drm_device *dev = (struct drm_device *) arg; + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + struct drm_i915_master_private *master_priv; + u32 iir, new_iir; + u32 pipe_stats[I915_MAX_PIPES]; + u32 vblank_status; + int vblank = 0; + unsigned long irqflags; + int irq_received; + int ret = IRQ_NONE, pipe; + bool blc_event = false; + + atomic_inc(&dev_priv->irq_received); + + iir = I915_READ(IIR); + + if (INTEL_INFO(dev)->gen >= 4) + vblank_status = PIPE_START_VBLANK_INTERRUPT_STATUS; + else + vblank_status = PIPE_VBLANK_INTERRUPT_STATUS; + + for (;;) { + irq_received = iir != 0; + + /* Can't rely on pipestat interrupt bit in iir as it might + * have been cleared after the pipestat interrupt was received. + * It doesn't set the bit in iir again, but it still produces + * interrupts (for non-MSI). + */ + spin_lock_irqsave(&dev_priv->irq_lock, irqflags); + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + i915_handle_error(dev, false); + + for_each_pipe(pipe) { + int reg = PIPESTAT(pipe); + pipe_stats[pipe] = I915_READ(reg); + + /* + * Clear the PIPE*STAT regs before the IIR + */ + if (pipe_stats[pipe] & 0x8000ffff) { + if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) + DRM_DEBUG_DRIVER("pipe %c underrun\n", + pipe_name(pipe)); + I915_WRITE(reg, pipe_stats[pipe]); + irq_received = 1; + } + } + spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); + + if (!irq_received) + break; + + ret = IRQ_HANDLED; + + /* Consume port. Then clear IIR or we'll miss events */ + if ((I915_HAS_HOTPLUG(dev)) && + (iir & I915_DISPLAY_PORT_INTERRUPT)) { + u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT); + + DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n", + hotplug_status); + if (hotplug_status & dev_priv->hotplug_supported_mask) + queue_work(dev_priv->wq, + &dev_priv->hotplug_work); + + I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); + I915_READ(PORT_HOTPLUG_STAT); + } + + I915_WRITE(IIR, iir); + new_iir = I915_READ(IIR); /* Flush posted writes */ + + if (dev->primary->master) { + master_priv = dev->primary->master->driver_priv; + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_dispatch = + READ_BREADCRUMB(dev_priv); + } + + if (iir & I915_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[RCS]); + if (iir & I915_BSD_USER_INTERRUPT) + notify_ring(dev, &dev_priv->ring[VCS]); + + if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { + intel_prepare_page_flip(dev, 0); + if (dev_priv->gen3_flip_pending_is_done) + intel_finish_page_flip_plane(dev, 0); + } + + if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { + intel_prepare_page_flip(dev, 1); + if (dev_priv->gen3_flip_pending_is_done) + intel_finish_page_flip_plane(dev, 1); + } + + for_each_pipe(pipe) { + if (pipe_stats[pipe] & vblank_status && + drm_handle_vblank(dev, pipe)) { + vblank++; + if (!dev_priv->gen3_flip_pending_is_done) { + i915_pageflip_stall_check(dev, pipe); + intel_finish_page_flip(dev, pipe); + } + } + + if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) + blc_event = true; + } + + + if (blc_event || (iir & I915_ASLE_INTERRUPT)) + intel_opregion_asle_intr(dev); + + /* With MSI, interrupts are only generated when iir + * transitions from zero to nonzero. If another bit got + * set while we were handling the existing iir bits, then + * we would never get another interrupt. + * + * This is fine on non-MSI as well, as if we hit this path + * we avoid exiting the interrupt handler only to generate + * another one. + * + * Note that for MSI this could cause a stray interrupt report + * if an interrupt landed in the time between writing IIR and + * the posting read. This should be rare enough to never + * trigger the 99% of 100,000 interrupts test for disabling + * stray interrupts. + */ + iir = new_iir; + } + + return ret; +} + +static void i965_irq_uninstall(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe; + + if (!dev_priv) + return; + + dev_priv->vblank_pipe = 0; + + if (I915_HAS_HOTPLUG(dev)) { + I915_WRITE(PORT_HOTPLUG_EN, 0); + I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); + } + + I915_WRITE(HWSTAM, 0xffffffff); + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE(IMR, 0xffffffff); + I915_WRITE(IER, 0x0); + + for_each_pipe(pipe) + I915_WRITE(PIPESTAT(pipe), + I915_READ(PIPESTAT(pipe)) & 0x8000ffff); + I915_WRITE(IIR, I915_READ(IIR)); +} + void intel_irq_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2646,11 +2903,16 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_postinstall = i8xx_irq_postinstall; dev->driver->irq_handler = i8xx_irq_handler; dev->driver->irq_uninstall = i8xx_irq_uninstall; + } else if (INTEL_INFO(dev)->gen == 3) { + dev->driver->irq_preinstall = i915_irq_preinstall; + dev->driver->irq_postinstall = i915_irq_postinstall; + dev->driver->irq_uninstall = i915_irq_uninstall; + dev->driver->irq_handler = i915_irq_handler; } else { - dev->driver->irq_preinstall = i915_driver_irq_preinstall; - dev->driver->irq_postinstall = i915_driver_irq_postinstall; - dev->driver->irq_uninstall = i915_driver_irq_uninstall; - dev->driver->irq_handler = i915_driver_irq_handler; + 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->driver->enable_vblank = i915_enable_vblank; dev->driver->disable_vblank = i915_disable_vblank; From 4f7d1e79b1967d2f1a718d4b9afbb23053858c0a Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:45 +0100 Subject: [PATCH 34/88] drm/i915: Clear FlipDone semantics change for pageflipping on gen3 On later gen3, you are able to select the meaning of the FlipPending status bit in IIR and change it to FlipDone. This was sometimes done by the BIOS leading to confusion on just how pageflipping worked on gen3. Simplify the implementation by using the legacy meaning for all gen3 machines. Note: this makes all gen3 machines equally broken... Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/i915_irq.c | 39 ++++++++++----------------------- 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 552f2504ff23..0095c8d12569 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -748,7 +748,6 @@ typedef struct drm_i915_private { struct drm_crtc *plane_to_crtc_mapping[3]; struct drm_crtc *pipe_to_crtc_mapping[3]; wait_queue_head_t pending_flip_queue; - bool gen3_flip_pending_is_done; struct intel_pch_pll pch_plls[I915_NUM_PLLS]; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3b3943395a6a..7ea95b64891f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2510,26 +2510,18 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) if (iir & I915_BSD_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[VCS]); - if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { + if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) intel_prepare_page_flip(dev, 0); - if (dev_priv->gen3_flip_pending_is_done) - intel_finish_page_flip_plane(dev, 0); - } - if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { + if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) intel_prepare_page_flip(dev, 1); - if (dev_priv->gen3_flip_pending_is_done) - intel_finish_page_flip_plane(dev, 1); - } for_each_pipe(pipe) { if (pipe_stats[pipe] & vblank_status && drm_handle_vblank(dev, pipe)) { vblank++; - if (!dev_priv->gen3_flip_pending_is_done) { - i915_pageflip_stall_check(dev, pipe); - intel_finish_page_flip(dev, pipe); - } + i915_pageflip_stall_check(dev, pipe); + intel_finish_page_flip(dev, pipe); } if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) @@ -2771,26 +2763,18 @@ static irqreturn_t i965_irq_handler(DRM_IRQ_ARGS) if (iir & I915_BSD_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[VCS]); - if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) { + if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) intel_prepare_page_flip(dev, 0); - if (dev_priv->gen3_flip_pending_is_done) - intel_finish_page_flip_plane(dev, 0); - } - if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) { + if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) intel_prepare_page_flip(dev, 1); - if (dev_priv->gen3_flip_pending_is_done) - intel_finish_page_flip_plane(dev, 1); - } for_each_pipe(pipe) { if (pipe_stats[pipe] & vblank_status && drm_handle_vblank(dev, pipe)) { vblank++; - if (!dev_priv->gen3_flip_pending_is_done) { - i915_pageflip_stall_check(dev, pipe); - intel_finish_page_flip(dev, pipe); - } + i915_pageflip_stall_check(dev, pipe); + intel_finish_page_flip(dev, pipe); } if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) @@ -2857,10 +2841,6 @@ void intel_irq_init(struct drm_device *dev) INIT_WORK(&dev_priv->error_work, i915_error_work_func); INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work); - /* IIR "flip pending" bit means done if this bit is set */ - if (IS_GEN3(dev) && (I915_READ(ECOSKPD) & ECO_FLIP_DONE)) - dev_priv->gen3_flip_pending_is_done = true; - dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev) || IS_IVYBRIDGE(dev) || @@ -2904,6 +2884,9 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_handler = i8xx_irq_handler; dev->driver->irq_uninstall = i8xx_irq_uninstall; } else if (INTEL_INFO(dev)->gen == 3) { + /* IIR "flip pending" means done if this bit is set */ + I915_WRITE(ECOSKPD, _MASKED_BIT_DISABLE(ECO_FLIP_DONE)); + dev->driver->irq_preinstall = i915_irq_preinstall; dev->driver->irq_postinstall = i915_irq_postinstall; dev->driver->irq_uninstall = i915_irq_uninstall; From 2c8ba29f15b57b0a587fae2a83ce906c82031d18 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:46 +0100 Subject: [PATCH 35/88] drm/i915: Remove gen3 irq code from gen4 irq routine And a couple of miscellaneous cleanups to the main body of the IRQ loop; move per-loop condition variables within the scope of the loop and move the old DRI1 breadcrumb to the tail of the function and so only execute it once. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 7ea95b64891f..874d62b78fbe 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2683,23 +2683,17 @@ static irqreturn_t i965_irq_handler(DRM_IRQ_ARGS) struct drm_i915_master_private *master_priv; u32 iir, new_iir; u32 pipe_stats[I915_MAX_PIPES]; - u32 vblank_status; - int vblank = 0; unsigned long irqflags; int irq_received; int ret = IRQ_NONE, pipe; - bool blc_event = false; atomic_inc(&dev_priv->irq_received); iir = I915_READ(IIR); - if (INTEL_INFO(dev)->gen >= 4) - vblank_status = PIPE_START_VBLANK_INTERRUPT_STATUS; - else - vblank_status = PIPE_VBLANK_INTERRUPT_STATUS; - for (;;) { + bool blc_event = false; + irq_received = iir != 0; /* Can't rely on pipestat interrupt bit in iir as it might @@ -2751,13 +2745,6 @@ static irqreturn_t i965_irq_handler(DRM_IRQ_ARGS) I915_WRITE(IIR, iir); new_iir = I915_READ(IIR); /* Flush posted writes */ - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } - if (iir & I915_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[RCS]); if (iir & I915_BSD_USER_INTERRUPT) @@ -2770,9 +2757,8 @@ static irqreturn_t i965_irq_handler(DRM_IRQ_ARGS) intel_prepare_page_flip(dev, 1); for_each_pipe(pipe) { - if (pipe_stats[pipe] & vblank_status && + if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS && drm_handle_vblank(dev, pipe)) { - vblank++; i915_pageflip_stall_check(dev, pipe); intel_finish_page_flip(dev, pipe); } @@ -2803,6 +2789,13 @@ static irqreturn_t i965_irq_handler(DRM_IRQ_ARGS) iir = new_iir; } + if (dev->primary->master) { + master_priv = dev->primary->master->driver_priv; + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_dispatch = + READ_BREADCRUMB(dev_priv); + } + return ret; } From 8291ee90cd5a9b043074dec313f52b89a71271a9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:47 +0100 Subject: [PATCH 36/88] drm/i915: Remove gen4 irq code from gen3 irq routine A couple of miscellaneous cleanups as well to move per-loop condition variables within the scope of the loop and the update of the DRI1 breadcrumb to the tail of the function. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 55 +++++++-------------------------- 1 file changed, 12 insertions(+), 43 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 874d62b78fbe..e8e4b9c41c6f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2351,7 +2351,6 @@ static int i915_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; - u32 error_mask; dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; @@ -2372,16 +2371,7 @@ static int i915_irq_postinstall(struct drm_device *dev) * Enable some error detection, note the instruction error mask * bit is reserved, so we leave it masked. */ - if (IS_G4X(dev)) { - error_mask = ~(GM45_ERROR_PAGE_TABLE | - GM45_ERROR_MEM_PRIV | - GM45_ERROR_CP_PRIV | - I915_ERROR_MEMORY_REFRESH); - } else { - error_mask = ~(I915_ERROR_PAGE_TABLE | - I915_ERROR_MEMORY_REFRESH); - } - I915_WRITE(EMR, error_mask); + I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); I915_WRITE(IMR, dev_priv->irq_mask); I915_WRITE(IER, enable_mask); @@ -2390,7 +2380,6 @@ static int i915_irq_postinstall(struct drm_device *dev) if (I915_HAS_HOTPLUG(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) @@ -2403,13 +2392,6 @@ static int i915_irq_postinstall(struct drm_device *dev) hotplug_en |= SDVOB_HOTPLUG_INT_EN; if (dev_priv->hotplug_supported_mask & CRT_HOTPLUG_INT_STATUS) { hotplug_en |= CRT_HOTPLUG_INT_EN; - - /* Programming the CRT detection parameters tends - to generate a spurious hotplug event about three - seconds later. So just do it once. - */ - if (IS_G4X(dev)) - hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; } @@ -2428,26 +2410,17 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; struct drm_i915_master_private *master_priv; - u32 iir, new_iir; - u32 pipe_stats[I915_MAX_PIPES]; - u32 vblank_status; - int vblank = 0; + u32 iir, new_iir, pipe_stats[I915_MAX_PIPES]; unsigned long irqflags; - int irq_received; int ret = IRQ_NONE, pipe; - bool blc_event = false; atomic_inc(&dev_priv->irq_received); iir = I915_READ(IIR); - if (INTEL_INFO(dev)->gen >= 4) - vblank_status = PIPE_START_VBLANK_INTERRUPT_STATUS; - else - vblank_status = PIPE_VBLANK_INTERRUPT_STATUS; - for (;;) { - irq_received = iir != 0; + bool blc_event = false; + int irq_received = iir != 0; /* Can't rely on pipestat interrupt bit in iir as it might * have been cleared after the pipestat interrupt was received. @@ -2498,17 +2471,8 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) I915_WRITE(IIR, iir); new_iir = I915_READ(IIR); /* Flush posted writes */ - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } - if (iir & I915_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[RCS]); - if (iir & I915_BSD_USER_INTERRUPT) - notify_ring(dev, &dev_priv->ring[VCS]); if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) intel_prepare_page_flip(dev, 0); @@ -2517,9 +2481,8 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) intel_prepare_page_flip(dev, 1); for_each_pipe(pipe) { - if (pipe_stats[pipe] & vblank_status && + if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS && drm_handle_vblank(dev, pipe)) { - vblank++; i915_pageflip_stall_check(dev, pipe); intel_finish_page_flip(dev, pipe); } @@ -2528,7 +2491,6 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) blc_event = true; } - if (blc_event || (iir & I915_ASLE_INTERRUPT)) intel_opregion_asle_intr(dev); @@ -2550,6 +2512,13 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) iir = new_iir; } + if (dev->primary->master) { + master_priv = dev->primary->master->driver_priv; + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_dispatch = + READ_BREADCRUMB(dev_priv); + } + return ret; } From 00d98ebd948b75919f542f53c31e9eb83310fbcf Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:48 +0100 Subject: [PATCH 37/88] drm/i915: HWSTAM is only 16-bit on gen3 Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index e8e4b9c41c6f..3c3e33a6c249 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2339,7 +2339,7 @@ static void i915_irq_preinstall(struct drm_device * dev) I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); } - I915_WRITE(HWSTAM, 0xeffe); + I915_WRITE16(HWSTAM, 0xeffe); for_each_pipe(pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(IMR, 0xffffffff); @@ -2537,7 +2537,7 @@ static void i915_irq_uninstall(struct drm_device * dev) I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); } - I915_WRITE(HWSTAM, 0xffffffff); + I915_WRITE16(HWSTAM, 0xffff); for_each_pipe(pipe) I915_WRITE(PIPESTAT(pipe), 0); I915_WRITE(IMR, 0xffffffff); From 55b39755ea3a767da85e6725a783da90c574a274 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:49 +0100 Subject: [PATCH 38/88] drm/i915: Cleanup gen3 irq uninstall Bring the for-each-pipe loops together so that the code is easier on the eyes. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3c3e33a6c249..24d23a21c538 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2527,9 +2527,6 @@ static void i915_irq_uninstall(struct drm_device * dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe; - if (!dev_priv) - return; - dev_priv->vblank_pipe = 0; if (I915_HAS_HOTPLUG(dev)) { @@ -2538,14 +2535,14 @@ static void i915_irq_uninstall(struct drm_device * dev) } I915_WRITE16(HWSTAM, 0xffff); - for_each_pipe(pipe) + for_each_pipe(pipe) { + /* Clear enable bits; then clear status bits */ I915_WRITE(PIPESTAT(pipe), 0); + I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe))); + } I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); - for_each_pipe(pipe) - I915_WRITE(PIPESTAT(pipe), - I915_READ(PIPESTAT(pipe)) & 0x8000ffff); I915_WRITE(IIR, I915_READ(IIR)); } From 38bde18045afe854f389961846d64c4b3f86105f Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:50 +0100 Subject: [PATCH 39/88] drm/i915: Handle PendingFlip on gen3 robustly We appear to allow too many pending pageflips as evidenced by an apparent pin-leak. So borrow the pageflip completion logic from i8xx for handling PendingFlip in a robust manner. v2: Address Jesse's reminders about the nuances of gen3 IRQ handling. References: https://bugzilla.kernel.org/show_bug.cgi?id=41882 Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 74 +++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 24d23a21c538..51f872084efa 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2350,16 +2350,31 @@ static void i915_irq_preinstall(struct drm_device * dev) static int i915_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; + u32 enable_mask; dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - /* Unmask the interrupts that we always want on. */ - dev_priv->irq_mask = ~I915_INTERRUPT_ENABLE_FIX; - dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; + I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); + + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask = + ~(I915_ASLE_INTERRUPT | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); + + enable_mask = + I915_ASLE_INTERRUPT | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT | + I915_USER_INTERRUPT; + if (I915_HAS_HOTPLUG(dev)) { /* Enable in IER... */ enable_mask |= I915_DISPLAY_PORT_INTERRUPT; @@ -2367,12 +2382,6 @@ static int i915_irq_postinstall(struct drm_device *dev) dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT; } - /* - * Enable some error detection, note the instruction error mask - * bit is reserved, so we leave it masked. - */ - I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH)); - I915_WRITE(IMR, dev_priv->irq_mask); I915_WRITE(IER, enable_mask); POSTING_READ(IER); @@ -2412,15 +2421,21 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) struct drm_i915_master_private *master_priv; u32 iir, new_iir, pipe_stats[I915_MAX_PIPES]; unsigned long irqflags; - int ret = IRQ_NONE, pipe; + u32 flip_mask = + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT; + u32 flip[2] = { + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT, + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT + }; + int pipe, ret = IRQ_NONE; atomic_inc(&dev_priv->irq_received); iir = I915_READ(IIR); - - for (;;) { + do { + bool irq_received = (iir & ~flip_mask) != 0; bool blc_event = false; - int irq_received = iir != 0; /* Can't rely on pipestat interrupt bit in iir as it might * have been cleared after the pipestat interrupt was received. @@ -2435,15 +2450,13 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) int reg = PIPESTAT(pipe); pipe_stats[pipe] = I915_READ(reg); - /* - * Clear the PIPE*STAT regs before the IIR - */ + /* Clear the PIPE*STAT regs before the IIR */ if (pipe_stats[pipe] & 0x8000ffff) { if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) DRM_DEBUG_DRIVER("pipe %c underrun\n", pipe_name(pipe)); I915_WRITE(reg, pipe_stats[pipe]); - irq_received = 1; + irq_received = true; } } spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); @@ -2451,8 +2464,6 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) if (!irq_received) break; - ret = IRQ_HANDLED; - /* Consume port. Then clear IIR or we'll miss events */ if ((I915_HAS_HOTPLUG(dev)) && (iir & I915_DISPLAY_PORT_INTERRUPT)) { @@ -2465,26 +2476,26 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) &dev_priv->hotplug_work); I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); - I915_READ(PORT_HOTPLUG_STAT); + POSTING_READ(PORT_HOTPLUG_STAT); } - I915_WRITE(IIR, iir); + I915_WRITE(IIR, iir & ~flip_mask); new_iir = I915_READ(IIR); /* Flush posted writes */ if (iir & I915_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[RCS]); - if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT) - intel_prepare_page_flip(dev, 0); - - if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT) - intel_prepare_page_flip(dev, 1); - for_each_pipe(pipe) { + int plane = pipe; + if (IS_MOBILE(dev)) + plane = !plane; if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS && drm_handle_vblank(dev, pipe)) { - i915_pageflip_stall_check(dev, pipe); - intel_finish_page_flip(dev, pipe); + if (iir & flip[plane]) { + intel_prepare_page_flip(dev, plane); + intel_finish_page_flip(dev, pipe); + flip_mask &= ~flip[plane]; + } } if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) @@ -2509,8 +2520,9 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) * trigger the 99% of 100,000 interrupts test for disabling * stray interrupts. */ + ret = IRQ_HANDLED; iir = new_iir; - } + } while (iir & ~flip_mask); if (dev->primary->master) { master_priv = dev->primary->master->driver_priv; From bbba0a9712be1c8e970fc66fc3becad0ce809e4f Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:51 +0100 Subject: [PATCH 40/88] drm/i915: Inline I915_INTERRUPT_ENABLE_FIX Since there is only one remaining user of I915_INTERRUPT_ENABLE_FIX, expand it at the callsite. Quoting Jesse Barnes: "I'd really like to get rid of these defines at the top of i915_irq.c. Some are unused and the others just make you check for the right bits everytime your read the code." Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes [danvet: Add bikeshed suggested by Jesse.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 51f872084efa..0d5e3f657431 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -39,24 +39,6 @@ #define MAX_NOPID ((u32)~0) -/** - * Interrupts that are always left unmasked. - * - * Since pipe events are edge-triggered from the PIPESTAT register to IIR, - * we leave them always unmasked in IMR and then control enabling them through - * PIPESTAT alone. - */ -#define I915_INTERRUPT_ENABLE_FIX \ - (I915_ASLE_INTERRUPT | \ - I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \ - I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | \ - I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | \ - I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) - -/** Interrupts that we mask and unmask at runtime. */ -#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT | I915_BSD_USER_INTERRUPT) - #define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\ PIPE_VBLANK_INTERRUPT_STATUS) @@ -2581,13 +2563,24 @@ 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 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; + u32 enable_mask; u32 error_mask; dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; /* Unmask the interrupts that we always want on. */ - dev_priv->irq_mask = ~I915_INTERRUPT_ENABLE_FIX; + dev_priv->irq_mask = ~(I915_ASLE_INTERRUPT | + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | + I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | + I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); + + enable_mask = ~dev_priv->irq_mask; + enable_mask |= I915_USER_INTERRUPT; + + if (IS_G4X(dev)) + enable_mask |= I915_BSD_USER_INTERRUPT; dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; From 624f8698c496f088d20be8ca8883811eb945b445 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 24 Apr 2012 22:59:52 +0100 Subject: [PATCH 41/88] drm/i915: Remove unused and unloved vblank macros Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0d5e3f657431..5c360eeb371b 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -37,17 +37,6 @@ #include "i915_trace.h" #include "intel_drv.h" -#define MAX_NOPID ((u32)~0) - -#define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\ - PIPE_VBLANK_INTERRUPT_STATUS) - -#define I915_PIPE_VBLANK_ENABLE (PIPE_START_VBLANK_INTERRUPT_ENABLE |\ - PIPE_VBLANK_INTERRUPT_ENABLE) - -#define DRM_I915_VBLANK_PIPE_ALL (DRM_I915_VBLANK_PIPE_A | \ - DRM_I915_VBLANK_PIPE_B) - /* For display hotplug interrupt */ static void ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask) From 507432986c15f18c5102b18027e4716fc9e9009e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 22:02:54 +0200 Subject: [PATCH 42/88] drm/i915: use the new masked bit macro some more I've missed this one. v2: Chris Wilson noticed another register. v3: Color choice improvements. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 8 ++------ drivers/gpu/drm/i915/i915_reg.h | 2 -- drivers/gpu/drm/i915/intel_pm.c | 3 +-- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 0d53eacd293f..b46a3fd17746 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3721,12 +3721,8 @@ i915_gem_load(struct drm_device *dev) /* On GEN3 we really need to make sure the ARB C3 LP bit is set */ if (IS_GEN3(dev)) { - u32 tmp = I915_READ(MI_ARB_STATE); - if (!(tmp & MI_ARB_C3_LP_WRITE_ENABLE)) { - /* arb state is a masked write, so set bit + bit in mask */ - tmp = MI_ARB_C3_LP_WRITE_ENABLE | (MI_ARB_C3_LP_WRITE_ENABLE << MI_ARB_MASK_SHIFT); - I915_WRITE(MI_ARB_STATE, tmp); - } + I915_WRITE(MI_ARB_STATE, + _MASKED_BIT_ENABLE(MI_ARB_C3_LP_WRITE_ENABLE)); } dev_priv->relative_constants_mode = I915_EXEC_CONSTANTS_REL_GENERAL; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f1f4d8f1df6a..7bc407a87c0c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -570,7 +570,6 @@ #define LM_BURST_LENGTH 0x00000700 #define LM_FIFO_WATERMARK 0x0000001F #define MI_ARB_STATE 0x020e4 /* 915+ only */ -#define MI_ARB_MASK_SHIFT 16 /* shift for enable bits */ /* Make render/texture TLB fetches lower priorty than associated data * fetches. This is not turned on by default @@ -635,7 +634,6 @@ #define MI_ARB_DISPLAY_PRIORITY_B_A (1 << 0) /* display B > display A */ #define CACHE_MODE_0 0x02120 /* 915+ only */ -#define CM0_MASK_SHIFT 16 #define CM0_IZ_OPT_DISABLE (1<<6) #define CM0_ZR_OPT_DISABLE (1<<5) #define CM0_STC_EVICT_DISABLE_LRA_SNB (1<<5) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 0552058a202f..e66330cc0934 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2663,9 +2663,8 @@ static void gen6_init_clock_gating(struct drm_device *dev) I915_WRITE(WM2_LP_ILK, 0); I915_WRITE(WM1_LP_ILK, 0); - /* clear masked bit */ I915_WRITE(CACHE_MODE_0, - CM0_STC_EVICT_DISABLE_LRA_SNB << CM0_MASK_SHIFT); + _MASKED_BIT_DISABLE(CM0_STC_EVICT_DISABLE_LRA_SNB)); I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) | From b2da9fe5d5994a104bbae154590070d698279919 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 26 Apr 2012 16:02:58 -0700 Subject: [PATCH 43/88] drm/i915: remove do_retire from i915_wait_request This originates from a hack by me to quickly fix a bug in an earlier patch where we needed control over whether or not waiting on a seqno actually did any retire list processing. Since the two operations aren't clearly related, we should pull the parameter out of the wait function, and make the caller responsible for retiring if the action is desired. The only function call site which did not get an explicit retire_request call (on purpose) is i915_gem_inactive_shrink(). That code was already calling retire_request a second time. v2: don't modify any behavior excepit i915_gem_inactive_shrink(Daniel) Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +- drivers/gpu/drm/i915/i915_drv.h | 5 ++-- drivers/gpu/drm/i915/i915_gem.c | 33 ++++++++-------------- drivers/gpu/drm/i915/i915_gem_evict.c | 15 ++++++++-- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 3 +- drivers/gpu/drm/i915/i915_gem_gtt.c | 2 +- drivers/gpu/drm/i915/intel_overlay.c | 8 +++--- drivers/gpu/drm/i915/intel_ringbuffer.c | 4 ++- 8 files changed, 37 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 7a55abbddc99..17081dad3faa 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -2009,9 +2009,10 @@ int i915_driver_unload(struct drm_device *dev) unregister_shrinker(&dev_priv->mm.inactive_shrinker); mutex_lock(&dev->struct_mutex); - ret = i915_gpu_idle(dev, true); + ret = i915_gpu_idle(dev); if (ret) DRM_ERROR("failed to idle hardware: %d\n", ret); + i915_gem_retire_requests(dev); mutex_unlock(&dev->struct_mutex); /* Cancel the retire work handler, which should be idle now. */ diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0095c8d12569..2113a1ab9625 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1297,14 +1297,13 @@ int __must_check i915_gem_init_hw(struct drm_device *dev); void i915_gem_init_swizzling(struct drm_device *dev); void i915_gem_init_ppgtt(struct drm_device *dev); void i915_gem_cleanup_ringbuffer(struct drm_device *dev); -int __must_check i915_gpu_idle(struct drm_device *dev, bool do_retire); +int __must_check i915_gpu_idle(struct drm_device *dev); int __must_check i915_gem_idle(struct drm_device *dev); int __must_check i915_add_request(struct intel_ring_buffer *ring, struct drm_file *file, struct drm_i915_gem_request *request); int __must_check i915_wait_request(struct intel_ring_buffer *ring, - uint32_t seqno, - bool do_retire); + uint32_t seqno); int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int __must_check i915_gem_object_set_to_gtt_domain(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 b46a3fd17746..e378204970fd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1825,8 +1825,7 @@ i915_gem_retire_work_handler(struct work_struct *work) */ int i915_wait_request(struct intel_ring_buffer *ring, - uint32_t seqno, - bool do_retire) + uint32_t seqno) { drm_i915_private_t *dev_priv = ring->dev->dev_private; u32 ier; @@ -1902,14 +1901,6 @@ i915_wait_request(struct intel_ring_buffer *ring, if (atomic_read(&dev_priv->mm.wedged)) ret = -EAGAIN; - /* Directly dispatch request retiring. While we have the work queue - * to handle this, the waiter on a request often wants an associated - * buffer to have made it to the inactive list, and we would need - * a separate wait queue to handle that. - */ - if (ret == 0 && do_retire) - i915_gem_retire_requests_ring(ring); - return ret; } @@ -1931,10 +1922,10 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj) * it. */ if (obj->active) { - ret = i915_wait_request(obj->ring, obj->last_rendering_seqno, - true); + ret = i915_wait_request(obj->ring, obj->last_rendering_seqno); if (ret) return ret; + i915_gem_retire_requests_ring(obj->ring); } return 0; @@ -2117,7 +2108,7 @@ i915_gem_flush_ring(struct intel_ring_buffer *ring, return 0; } -static int i915_ring_idle(struct intel_ring_buffer *ring, bool do_retire) +static int i915_ring_idle(struct intel_ring_buffer *ring) { int ret; @@ -2131,18 +2122,17 @@ static int i915_ring_idle(struct intel_ring_buffer *ring, bool do_retire) return ret; } - return i915_wait_request(ring, i915_gem_next_request_seqno(ring), - do_retire); + return i915_wait_request(ring, i915_gem_next_request_seqno(ring)); } -int i915_gpu_idle(struct drm_device *dev, bool do_retire) +int i915_gpu_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; int ret, i; /* Flush everything onto the inactive list. */ for (i = 0; i < I915_NUM_RINGS; i++) { - ret = i915_ring_idle(&dev_priv->ring[i], do_retire); + ret = i915_ring_idle(&dev_priv->ring[i]); if (ret) return ret; } @@ -2331,9 +2321,7 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) } if (obj->last_fenced_seqno) { - ret = i915_wait_request(obj->ring, - obj->last_fenced_seqno, - false); + ret = i915_wait_request(obj->ring, obj->last_fenced_seqno); if (ret) return ret; @@ -3394,11 +3382,12 @@ i915_gem_idle(struct drm_device *dev) return 0; } - ret = i915_gpu_idle(dev, true); + ret = i915_gpu_idle(dev); if (ret) { mutex_unlock(&dev->struct_mutex); return ret; } + i915_gem_retire_requests(dev); /* Under UMS, be paranoid and evict. */ if (!drm_core_check_feature(dev, DRIVER_MODESET)) @@ -4025,7 +4014,7 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc) * This has a dramatic impact to reduce the number of * OOM-killer events whilst running the GPU aggressively. */ - if (i915_gpu_idle(dev, true) == 0) + if (i915_gpu_idle(dev) == 0) goto rescan; } mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 91ebb94d7c8b..3bcf0451d07c 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -168,7 +168,7 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj, *next; bool lists_empty; - int ret; + int ret,i; lists_empty = (list_empty(&dev_priv->mm.inactive_list) && list_empty(&dev_priv->mm.flushing_list) && @@ -178,11 +178,20 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) trace_i915_gem_evict_everything(dev, purgeable_only); - /* Flush everything (on to the inactive lists) and evict */ - ret = i915_gpu_idle(dev, true); + ret = i915_gpu_idle(dev); if (ret) return ret; + /* The gpu_idle will flush everything in the write domain to the + * active list. Then we must move everything off the active list + * with retire requests. + */ + for (i = 0; i < I915_NUM_RINGS; i++) + if (WARN_ON(!list_empty(&dev_priv->ring[i].gpu_write_list))) + return -EBUSY; + + i915_gem_retire_requests(dev); + BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); /* Having flushed everything, unbind() should never raise an error */ diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 68ec0130a626..cbba0aa6104b 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1220,9 +1220,10 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, * so every billion or so execbuffers, we need to stall * the GPU in order to reset the counters. */ - ret = i915_gpu_idle(dev, true); + ret = i915_gpu_idle(dev); if (ret) goto err; + i915_gem_retire_requests(dev); BUG_ON(ring->sync_seqno[i]); } diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 25c8bf9d1d4e..29d573c27b35 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -317,7 +317,7 @@ static bool do_idling(struct drm_i915_private *dev_priv) if (unlikely(dev_priv->mm.gtt->do_idle_maps)) { dev_priv->mm.interruptible = false; - if (i915_gpu_idle(dev_priv->dev, false)) { + if (i915_gpu_idle(dev_priv->dev)) { DRM_ERROR("Couldn't idle GPU\n"); /* Wait a bit, in hopes it avoids the hang */ udelay(10); diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index a0b5053c5a32..e06e46a30757 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -225,10 +225,10 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay, } overlay->last_flip_req = request->seqno; overlay->flip_tail = tail; - ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req, - true); + ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req); if (ret) return ret; + i915_gem_retire_requests(dev); overlay->last_flip_req = 0; return 0; @@ -447,10 +447,10 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay) if (overlay->last_flip_req == 0) return 0; - ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req, - true); + ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req); if (ret) return ret; + i915_gem_retire_requests(dev); if (overlay->flip_tail) overlay->flip_tail(overlay); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 427b7c55ffe0..a7d97d17b285 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1088,9 +1088,11 @@ static int intel_ring_wait_seqno(struct intel_ring_buffer *ring, u32 seqno) was_interruptible = dev_priv->mm.interruptible; dev_priv->mm.interruptible = false; - ret = i915_wait_request(ring, seqno, true); + ret = i915_wait_request(ring, seqno); dev_priv->mm.interruptible = was_interruptible; + if (!ret) + i915_gem_retire_requests_ring(ring); return ret; } From be998e2e3945d97ffad3c800ef52fd487868b3e9 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 26 Apr 2012 16:03:00 -0700 Subject: [PATCH 44/88] drm/i915: move vbetool invoked ier stuff This extra bit of interrupt enabling code doesn't belong in the wait seqno function. If anything we should pull it out to a helper so the throttle code can also use it. The history is a bit vague, but I am going to attempt to just dump it, unless someone can argue otherwise. Removing this allows for a shared lock free wait seqno function. To keep tabs on this issue though, the IER value is stored on error capture (recommended by Chris Wilson) v2: fixed typo EIR->IER (Ben) Fix some white space (Ben) Move IER capture to globally instead of per ring (Ben) Signed-off-by: Ben Widawsky [danvet: ier is a 16 bit reg on gen2!] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 14 -------------- drivers/gpu/drm/i915/i915_irq.c | 10 ++++++++++ 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 120db4687a2f..8ed034233e38 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -712,6 +712,7 @@ static int i915_error_state(struct seq_file *m, void *unused) error->time.tv_usec); seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device); seq_printf(m, "EIR: 0x%08x\n", error->eir); + seq_printf(m, "IER: 0x%08x\n", error->ier); seq_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er); for (i = 0; i < dev_priv->num_fence_regs; i++) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2113a1ab9625..e719522cb40b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -173,6 +173,7 @@ struct intel_display_error_state; struct drm_i915_error_state { u32 eir; u32 pgtbl_er; + u32 ier; u32 pipestat[I915_MAX_PIPES]; u32 tail[I915_NUM_RINGS]; u32 head[I915_NUM_RINGS]; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e378204970fd..f194603140c0 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1828,7 +1828,6 @@ i915_wait_request(struct intel_ring_buffer *ring, uint32_t seqno) { drm_i915_private_t *dev_priv = ring->dev->dev_private; - u32 ier; int ret = 0; BUG_ON(seqno == 0); @@ -1863,19 +1862,6 @@ i915_wait_request(struct intel_ring_buffer *ring, } if (!i915_seqno_passed(ring->get_seqno(ring), seqno)) { - if (HAS_PCH_SPLIT(ring->dev)) - ier = I915_READ(DEIER) | I915_READ(GTIER); - else if (IS_VALLEYVIEW(ring->dev)) - ier = I915_READ(GTIER) | I915_READ(VLV_IER); - else - ier = I915_READ(IER); - if (!ier) { - DRM_ERROR("something (likely vbetool) disabled " - "interrupts, re-enabling\n"); - ring->dev->driver->irq_preinstall(ring->dev); - ring->dev->driver->irq_postinstall(ring->dev); - } - trace_i915_gem_request_wait_begin(ring, seqno); ring->waiting_seqno = seqno; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 5c360eeb371b..0acadc5e02d2 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1122,6 +1122,16 @@ static void i915_capture_error_state(struct drm_device *dev) error->eir = I915_READ(EIR); error->pgtbl_er = I915_READ(PGTBL_ER); + + if (HAS_PCH_SPLIT(dev)) + error->ier = I915_READ(DEIER) | I915_READ(GTIER); + else if (IS_VALLEYVIEW(dev)) + error->ier = I915_READ(GTIER) | I915_READ(VLV_IER); + else if (IS_GEN2(dev)) + error->ier = I915_READ16(IER); + else + error->ier = I915_READ(IER); + for_each_pipe(pipe) error->pipestat[pipe] = I915_READ(PIPESTAT(pipe)); From 9574b3fe29ba9064c03e8db0e399f5fe356ee03b Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 26 Apr 2012 16:03:01 -0700 Subject: [PATCH 45/88] drm/i915: kill waiting_seqno The waiting_seqno is not terribly useful, and as such we can remove it so that we'll be able to extract lockless code. v2: Keep the information for error_state (Chris) Check if ring is initialized in hangcheck (Chris) Capture the waiting ring (Chris) Signed-off-by: Ben Widawsky [danvet: add some bikeshed to clarify a comment.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 3 +-- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 2 -- drivers/gpu/drm/i915/i915_irq.c | 14 +++++++++----- drivers/gpu/drm/i915/intel_ringbuffer.h | 1 - 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 8ed034233e38..9d4bf6bfc46a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -414,8 +414,6 @@ static void i915_ring_seqno_info(struct seq_file *m, if (ring->get_seqno) { seq_printf(m, "Current sequence (%s): %d\n", ring->name, ring->get_seqno(ring)); - seq_printf(m, "Waiter sequence (%s): %d\n", - ring->name, ring->waiting_seqno); seq_printf(m, "IRQ sequence (%s): %d\n", ring->name, ring->irq_seqno); } @@ -687,6 +685,7 @@ static void i915_ring_error_state(struct seq_file *m, error->semaphore_mboxes[ring][1]); } seq_printf(m, " seqno: 0x%08x\n", error->seqno[ring]); + seq_printf(m, " waiting: %s\n", yesno(error->waiting[ring])); seq_printf(m, " ring->head: 0x%08x\n", error->cpu_ring_head[ring]); seq_printf(m, " ring->tail: 0x%08x\n", error->cpu_ring_tail[ring]); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e719522cb40b..ca13098c7573 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -174,6 +174,7 @@ struct drm_i915_error_state { u32 eir; u32 pgtbl_er; u32 ier; + bool waiting[I915_NUM_RINGS]; u32 pipestat[I915_MAX_PIPES]; u32 tail[I915_NUM_RINGS]; u32 head[I915_NUM_RINGS]; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f194603140c0..292b4a9e7bab 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1864,7 +1864,6 @@ i915_wait_request(struct intel_ring_buffer *ring, if (!i915_seqno_passed(ring->get_seqno(ring), seqno)) { trace_i915_gem_request_wait_begin(ring, seqno); - ring->waiting_seqno = seqno; if (ring->irq_get(ring)) { if (dev_priv->mm.interruptible) ret = wait_event_interruptible(ring->irq_queue, @@ -1880,7 +1879,6 @@ i915_wait_request(struct intel_ring_buffer *ring, seqno) || atomic_read(&dev_priv->mm.wedged), 3000)) ret = -EBUSY; - ring->waiting_seqno = 0; trace_i915_gem_request_wait_end(ring, seqno); } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 0acadc5e02d2..a0129cfc3b31 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1031,6 +1031,7 @@ static void i915_record_ring_state(struct drm_device *dev, error->instdone[ring->id] = I915_READ(INSTDONE); } + error->waiting[ring->id] = waitqueue_active(&ring->irq_queue); error->instpm[ring->id] = I915_READ(RING_INSTPM(ring->mmio_base)); error->seqno[ring->id] = ring->get_seqno(ring); error->acthd[ring->id] = intel_ring_get_active_head(ring); @@ -1708,14 +1709,17 @@ ring_last_seqno(struct intel_ring_buffer *ring) static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err) { + /* We don't check whether the ring even exists before calling this + * function. Hence check whether it's initialized. */ + if (ring->obj == NULL) + return true; + if (list_empty(&ring->request_list) || i915_seqno_passed(ring->get_seqno(ring), ring_last_seqno(ring))) { /* Issue a wake-up to catch stuck h/w. */ - if (ring->waiting_seqno && waitqueue_active(&ring->irq_queue)) { - DRM_ERROR("Hangcheck timer elapsed... %s idle [waiting on %d, at %d], missed IRQ?\n", - ring->name, - ring->waiting_seqno, - ring->get_seqno(ring)); + if (waitqueue_active(&ring->irq_queue)) { + DRM_ERROR("Hangcheck timer elapsed... %s idle\n", + ring->name); wake_up_all(&ring->irq_queue); *err = true; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index e0b25bb2fb5d..d3bf2595589f 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -60,7 +60,6 @@ struct intel_ring_buffer { u32 irq_enable_mask; /* bitmask to enable ring interrupt */ u32 irq_seqno; /* last seq seem at irq time */ u32 trace_irq_seqno; - u32 waiting_seqno; u32 sync_seqno[I915_NUM_RINGS-1]; bool __must_check (*irq_get)(struct intel_ring_buffer *ring); void (*irq_put)(struct intel_ring_buffer *ring); From c58cf4f1082d81c42be89e9c6eeca6466954a70c Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 26 Apr 2012 16:03:02 -0700 Subject: [PATCH 46/88] drm/i915: drop polled waits from i915_wait_request The only time irq_get should fail is during unload or suspend. Both of these points should try to quiesce the GPU before disabling interrupts and so the atomic polling should never occur. This was recommended by Chris Wilson as a way of reducing added complexity to the polled wait which I introduced in an RFC patch. 09:57 < ickle_> it's only there as a fudge for waiting after irqs after uninstalled during s&r, we aren't actually meant to hit it 09:57 < ickle_> so maybe we should just kill the code there and fix the breakage v2: return -ENODEV instead of -EBUSY when irq_get fails Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 292b4a9e7bab..148e04baceba 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1864,22 +1864,19 @@ i915_wait_request(struct intel_ring_buffer *ring, if (!i915_seqno_passed(ring->get_seqno(ring), seqno)) { trace_i915_gem_request_wait_begin(ring, seqno); - if (ring->irq_get(ring)) { - if (dev_priv->mm.interruptible) - ret = wait_event_interruptible(ring->irq_queue, - i915_seqno_passed(ring->get_seqno(ring), seqno) - || atomic_read(&dev_priv->mm.wedged)); - else - wait_event(ring->irq_queue, - i915_seqno_passed(ring->get_seqno(ring), seqno) - || atomic_read(&dev_priv->mm.wedged)); + if (WARN_ON(!ring->irq_get(ring))) + return -ENODEV; - ring->irq_put(ring); - } else if (wait_for_atomic(i915_seqno_passed(ring->get_seqno(ring), - seqno) || - atomic_read(&dev_priv->mm.wedged), 3000)) - ret = -EBUSY; + if (dev_priv->mm.interruptible) + ret = wait_event_interruptible(ring->irq_queue, + i915_seqno_passed(ring->get_seqno(ring), seqno) + || atomic_read(&dev_priv->mm.wedged)); + else + wait_event(ring->irq_queue, + i915_seqno_passed(ring->get_seqno(ring), seqno) + || atomic_read(&dev_priv->mm.wedged)); + ring->irq_put(ring); trace_i915_gem_request_wait_end(ring, seqno); } if (atomic_read(&dev_priv->mm.wedged)) From 604dd3ec75b2488dc81cd25dd85fcea23df25a9e Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 26 Apr 2012 16:03:03 -0700 Subject: [PATCH 47/88] drm/i915: extract __wait_seqno from i915_wait_request i915_wait_request is actually a fairly large function encapsulating quite a few different operations. Because being able to wait on seqnos in various conditions is useful, extracting that bit of code to a helper function seems useful v2: pull the irq_get/put as well (Ben) Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 49 +++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 148e04baceba..b7a99bfa423d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1819,6 +1819,36 @@ i915_gem_retire_work_handler(struct work_struct *work) mutex_unlock(&dev->struct_mutex); } +static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, + bool interruptible) +{ + drm_i915_private_t *dev_priv = ring->dev->dev_private; + int ret = 0; + + if (i915_seqno_passed(ring->get_seqno(ring), seqno)) + return 0; + + trace_i915_gem_request_wait_begin(ring, seqno); + if (WARN_ON(!ring->irq_get(ring))) + return -ENODEV; + +#define EXIT_COND \ + (i915_seqno_passed(ring->get_seqno(ring), seqno) || \ + atomic_read(&dev_priv->mm.wedged)) + + if (interruptible) + ret = wait_event_interruptible(ring->irq_queue, + EXIT_COND); + else + wait_event(ring->irq_queue, EXIT_COND); + + ring->irq_put(ring); + trace_i915_gem_request_wait_end(ring, seqno); +#undef EXIT_COND + + return ret; +} + /** * Waits for a sequence number to be signaled, and cleans up the * request and object lists appropriately for that event. @@ -1861,24 +1891,7 @@ i915_wait_request(struct intel_ring_buffer *ring, seqno = request->seqno; } - if (!i915_seqno_passed(ring->get_seqno(ring), seqno)) { - trace_i915_gem_request_wait_begin(ring, seqno); - - if (WARN_ON(!ring->irq_get(ring))) - return -ENODEV; - - if (dev_priv->mm.interruptible) - ret = wait_event_interruptible(ring->irq_queue, - i915_seqno_passed(ring->get_seqno(ring), seqno) - || atomic_read(&dev_priv->mm.wedged)); - else - wait_event(ring->irq_queue, - i915_seqno_passed(ring->get_seqno(ring), seqno) - || atomic_read(&dev_priv->mm.wedged)); - - ring->irq_put(ring); - trace_i915_gem_request_wait_end(ring, seqno); - } + ret = __wait_seqno(ring, seqno, dev_priv->mm.interruptible); if (atomic_read(&dev_priv->mm.wedged)) ret = -EAGAIN; From 4146b08d767a702682bd28e2a21b062983fd937c Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 26 Apr 2012 16:03:04 -0700 Subject: [PATCH 48/88] drm/i915: remove polled wait from throttle It's about to go away anyway. Just here to help bisection. Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b7a99bfa423d..c19a6214daf7 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2996,11 +2996,8 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) if (ret == 0 && atomic_read(&dev_priv->mm.wedged)) ret = -EIO; - } else if (wait_for_atomic(i915_seqno_passed(ring->get_seqno(ring), - seqno) || - atomic_read(&dev_priv->mm.wedged), 3000)) { + } else ret = -EBUSY; - } } if (ret == 0) From 3b88cc0dd72e82df65c634f19417158787d86bfe Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 26 Apr 2012 16:03:05 -0700 Subject: [PATCH 49/88] drm/i915: use __wait_seqno for ring throttle It turns out throttle had an almost identical bit of code to do the wait. Now we can call the new helper directly. This is just a bonus, and not needed for the overall series. v2: remove irq_get/put which is now in __wait_seqno (Ben) Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c19a6214daf7..293f573803ab 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2981,25 +2981,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) if (seqno == 0) return 0; - ret = 0; - if (!i915_seqno_passed(ring->get_seqno(ring), seqno)) { - /* And wait for the seqno passing without holding any locks and - * causing extra latency for others. This is safe as the irq - * generation is designed to be run atomically and so is - * lockless. - */ - if (ring->irq_get(ring)) { - ret = wait_event_interruptible(ring->irq_queue, - i915_seqno_passed(ring->get_seqno(ring), seqno) - || atomic_read(&dev_priv->mm.wedged)); - ring->irq_put(ring); - - if (ret == 0 && atomic_read(&dev_priv->mm.wedged)) - ret = -EIO; - } else - ret = -EBUSY; - } - + ret = __wait_seqno(ring, seqno, true); if (ret == 0) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, 0); From 6d171cb4c2b74a34283333af93fdd7bebdf57e6d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 28 Apr 2012 09:00:03 +0100 Subject: [PATCH 50/88] drm/i915: Remove unused ring->irq_seqno Signed-off-by: Chris Wilson Reviewed-by: Ben Widawsky Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 2 -- drivers/gpu/drm/i915/i915_irq.c | 5 +---- drivers/gpu/drm/i915/intel_ringbuffer.h | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 9d4bf6bfc46a..c83791e1e16f 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -414,8 +414,6 @@ static void i915_ring_seqno_info(struct seq_file *m, if (ring->get_seqno) { seq_printf(m, "Current sequence (%s): %d\n", ring->name, ring->get_seqno(ring)); - seq_printf(m, "IRQ sequence (%s): %d\n", - ring->name, ring->irq_seqno); } } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index a0129cfc3b31..2e93ac0232f6 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -331,15 +331,12 @@ static void notify_ring(struct drm_device *dev, struct intel_ring_buffer *ring) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 seqno; if (ring->obj == NULL) return; - seqno = ring->get_seqno(ring); - trace_i915_gem_request_complete(ring, seqno); + trace_i915_gem_request_complete(ring, ring->get_seqno(ring)); - ring->irq_seqno = seqno; wake_up_all(&ring->irq_queue); if (i915_enable_hangcheck) { dev_priv->hangcheck_count = 0; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index d3bf2595589f..4d1c6c4235d6 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -58,7 +58,6 @@ struct intel_ring_buffer { u32 irq_refcount; /* protected by dev_priv->irq_lock */ u32 irq_enable_mask; /* bitmask to enable ring interrupt */ - u32 irq_seqno; /* last seq seem at irq time */ u32 trace_irq_seqno; u32 sync_seqno[I915_NUM_RINGS-1]; bool __must_check (*irq_get)(struct intel_ring_buffer *ring); From 582be6b415b85e754be5342894e85f3883e92362 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 30 Apr 2012 19:35:02 +0100 Subject: [PATCH 51/88] drm/i915: Only enable IPS polling for gen5 On SandyBridge IPS was entirely implemented in hardware and not reliant on the driver monitoring power consumption and feeding back desired run states, so the hardware is able to adapt quicker and more flexibly. Which is a huge relief for us as we no longer have to carry empirically derived magic algorithms. Yet despite the advance in technology, the driver was still doing its IPS polling on all machines. Restrict it to the only supported hardware, Clarkdale/Arrandale. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 3 +++ drivers/gpu/drm/i915/i915_dma.c | 15 ++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index c83791e1e16f..ae68ac1c488e 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1193,6 +1193,9 @@ static int i915_emon_status(struct seq_file *m, void *unused) unsigned long temp, chipset, gfx; int ret; + if (!IS_GEN5(dev)) + return -ENODEV; + ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 17081dad3faa..0d99a16ff47b 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1520,6 +1520,9 @@ void i915_update_gfx_val(struct drm_i915_private *dev_priv) unsigned long diffms; u32 count; + if (dev_priv->info->gen != 5) + return; + getrawmonotonic(&now); diff1 = timespec_sub(now, dev_priv->last_time2); @@ -1959,12 +1962,14 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); - spin_lock(&mchdev_lock); - i915_mch_dev = dev_priv; - dev_priv->mchdev_lock = &mchdev_lock; - spin_unlock(&mchdev_lock); + if (IS_GEN5(dev)) { + spin_lock(&mchdev_lock); + i915_mch_dev = dev_priv; + dev_priv->mchdev_lock = &mchdev_lock; + spin_unlock(&mchdev_lock); - ips_ping_for_i915_load(); + ips_ping_for_i915_load(); + } return 0; From 7001f22f7c96bfcaa7d043dbb75b12c27880607d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 30 Apr 2012 19:17:35 +0100 Subject: [PATCH 52/88] drm/i915: Remove unused dev_priv->vblank_pipe vblank_pipe was intended to be used for tracking DRI1 state. However, the vblank_pipe reported to DRI1 is fixed to umask both pipes, and the dev_priv->vblank_pipe unused and superfluous. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 - drivers/gpu/drm/i915/i915_irq.c | 20 -------------------- 2 files changed, 21 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ca13098c7573..b7b58bcf8242 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -401,7 +401,6 @@ typedef struct drm_i915_private { int tex_lru_log_granularity; int allow_batchbuffer; unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; - int vblank_pipe; 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 2e93ac0232f6..39c4e686455d 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1925,7 +1925,6 @@ static int ironlake_irq_postinstall(struct drm_device *dev) u32 render_irqs; u32 hotplug_mask; - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; dev_priv->irq_mask = ~display_mask; /* should always can generate irq */ @@ -1994,7 +1993,6 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) u32 render_irqs; u32 hotplug_mask; - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; dev_priv->irq_mask = ~display_mask; /* should always can generate irq */ @@ -2047,8 +2045,6 @@ static int valleyview_irq_postinstall(struct drm_device *dev) dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - /* Hack for broken MSIs on VLV */ pci_write_config_dword(dev_priv->dev->pdev, 0x94, 0xfee00000); pci_read_config_word(dev->pdev, 0x98, &msid); @@ -2123,8 +2119,6 @@ static void valleyview_irq_uninstall(struct drm_device *dev) if (!dev_priv) return; - dev_priv->vblank_pipe = 0; - for_each_pipe(pipe) I915_WRITE(PIPESTAT(pipe), 0xffff); @@ -2146,8 +2140,6 @@ static void ironlake_irq_uninstall(struct drm_device *dev) if (!dev_priv) return; - dev_priv->vblank_pipe = 0; - I915_WRITE(HWSTAM, 0xffffffff); I915_WRITE(DEIMR, 0xffffffff); @@ -2181,8 +2173,6 @@ static int i8xx_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; @@ -2297,8 +2287,6 @@ static void i8xx_irq_uninstall(struct drm_device * dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe; - dev_priv->vblank_pipe = 0; - for_each_pipe(pipe) { /* Clear enable bits; then clear status bits */ I915_WRITE(PIPESTAT(pipe), 0); @@ -2334,8 +2322,6 @@ static int i915_irq_postinstall(struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; u32 enable_mask; - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - dev_priv->pipestat[0] = 0; dev_priv->pipestat[1] = 0; @@ -2521,8 +2507,6 @@ static void i915_irq_uninstall(struct drm_device * dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe; - dev_priv->vblank_pipe = 0; - if (I915_HAS_HOTPLUG(dev)) { I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); @@ -2566,8 +2550,6 @@ static int i965_irq_postinstall(struct drm_device *dev) u32 enable_mask; u32 error_mask; - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - /* Unmask the interrupts that we always want on. */ dev_priv->irq_mask = ~(I915_ASLE_INTERRUPT | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | @@ -2778,8 +2760,6 @@ static void i965_irq_uninstall(struct drm_device * dev) if (!dev_priv) return; - dev_priv->vblank_pipe = 0; - if (I915_HAS_HOTPLUG(dev)) { I915_WRITE(PORT_HOTPLUG_EN, 0); I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); From d1c1edbc741924b8983ebad1329f4b761664e48b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:01 +0200 Subject: [PATCH 53/88] drm/i915: move dri1 vblank stubs to i915_dma.c i915_dma.c contains most of the old dri1 horror-show, so move the remaining bits there, too. The code has been removed and the only thing left are some stubs to ensure that userspace doesn't try to use this stuff. vblank_pipe_set only returns 0 without any side-effects, so we can even stub it out with the canonical drm_noop. v2: Rebase against ENODEV changes. Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 44 +++++++++++++++++++++++- drivers/gpu/drm/i915/i915_drv.h | 7 ---- drivers/gpu/drm/i915/i915_irq.c | 61 --------------------------------- 3 files changed, 43 insertions(+), 69 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0d99a16ff47b..5fde7ed86b17 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -721,6 +721,48 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, return ret; } +static int i915_vblank_pipe_get(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_vblank_pipe_t *pipe = data; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; + + return 0; +} + +/** + * Schedule buffer swap at given vertical blank. + */ +static int i915_vblank_swap(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + /* The delayed swap mechanism was fundamentally racy, and has been + * removed. The model was that the client requested a delayed flip/swap + * from the kernel, then waited for vblank before continuing to perform + * rendering. The problem was that the kernel might wake the client + * up before it dispatched the vblank swap (since the lock has to be + * held while touching the ringbuffer), in which case the client would + * clear and start the next frame before the swap occurred, and + * flicker would occur in addition to likely missing the vblank. + * + * In the absence of this ioctl, userland falls back to a correct path + * of waiting for a vblank, then dispatching the swap on its own. + * Context switching to userland and back is plenty fast enough for + * meeting the requirements of vblank swapping. + */ + return -EINVAL; +} + static int i915_flip_bufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -2162,7 +2204,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_INIT_HEAP, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(I915_CMDBUFFER, i915_cmdbuffer, DRM_AUTH), DRM_IOCTL_DEF_DRV(I915_DESTROY_HEAP, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF_DRV(I915_SET_VBLANK_PIPE, i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF_DRV(I915_SET_VBLANK_PIPE, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(I915_GET_VBLANK_PIPE, i915_vblank_pipe_get, DRM_AUTH), DRM_IOCTL_DEF_DRV(I915_VBLANK_SWAP, i915_vblank_swap, DRM_AUTH), DRM_IOCTL_DEF_DRV(I915_HWS_ADDR, i915_set_status_page, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b7b58bcf8242..2de9947b7e6a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1156,13 +1156,6 @@ extern int i915_irq_wait(struct drm_device *dev, void *data, extern void intel_irq_init(struct drm_device *dev); -extern int i915_vblank_pipe_set(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int i915_vblank_pipe_get(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int i915_vblank_swap(struct drm_device *dev, void *data, - struct drm_file *file_priv); - void i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 39c4e686455d..96c14fe0e7f8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1636,67 +1636,6 @@ static void valleyview_disable_vblank(struct drm_device *dev, int pipe) spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } - -/* Set the vblank monitor pipe - */ -int i915_vblank_pipe_set(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -ENODEV; - - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - return 0; -} - -int i915_vblank_pipe_get(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_vblank_pipe_t *pipe = data; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -ENODEV; - - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - - return 0; -} - -/** - * Schedule buffer swap at given vertical blank. - */ -int i915_vblank_swap(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - /* The delayed swap mechanism was fundamentally racy, and has been - * removed. The model was that the client requested a delayed flip/swap - * from the kernel, then waited for vblank before continuing to perform - * rendering. The problem was that the kernel might wake the client - * up before it dispatched the vblank swap (since the lock has to be - * held while touching the ringbuffer), in which case the client would - * clear and start the next frame before the swap occurred, and - * flicker would occur in addition to likely missing the vblank. - * - * In the absence of this ioctl, userland falls back to a correct path - * of waiting for a vblank, then dispatching the swap on its own. - * Context switching to userland and back is plenty fast enough for - * meeting the requirements of vblank swapping. - */ - return -EINVAL; -} - static u32 ring_last_seqno(struct intel_ring_buffer *ring) { From 8781342df72fcc37f2aebd43d3a293b0a343806d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 2 May 2012 11:49:32 +0200 Subject: [PATCH 54/88] drm/i915: create dev_priv->dri1 dragon dungeon^W^W sub-struct ... and shove allow_batchbuffer in there. More dragons will follow suit. There's the curious case that we allow this for KMS ... Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 8 ++++---- drivers/gpu/drm/i915/i915_drv.h | 10 +++++++++- drivers/gpu/drm/i915/i915_gem.c | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 5fde7ed86b17..3b068a3917a9 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -198,7 +198,7 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) /* Allow hardware batchbuffers unless told otherwise. */ - dev_priv->allow_batchbuffer = 1; + dev_priv->dri1.allow_batchbuffer = 1; return 0; } @@ -610,7 +610,7 @@ static int i915_batchbuffer(struct drm_device *dev, void *data, if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; - if (!dev_priv->allow_batchbuffer) { + if (!dev_priv->dri1.allow_batchbuffer) { DRM_ERROR("Batchbuffer ioctl disabled\n"); return -EINVAL; } @@ -799,7 +799,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = dev->pdev->irq ? 1 : 0; break; case I915_PARAM_ALLOW_BATCHBUFFER: - value = dev_priv->allow_batchbuffer ? 1 : 0; + value = dev_priv->dri1.allow_batchbuffer ? 1 : 0; break; case I915_PARAM_LAST_DISPATCH: value = READ_BREADCRUMB(dev_priv); @@ -882,7 +882,7 @@ static int i915_setparam(struct drm_device *dev, void *data, dev_priv->tex_lru_log_granularity = param->value; break; case I915_SETPARAM_ALLOW_BATCHBUFFER: - dev_priv->allow_batchbuffer = param->value; + dev_priv->dri1.allow_batchbuffer = param->value ? 1 : 0; break; case I915_SETPARAM_NUM_USED_FENCES: if (param->value > dev_priv->num_fence_regs || diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2de9947b7e6a..65af417b823a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -399,7 +399,6 @@ typedef struct drm_i915_private { struct work_struct hotplug_work; int tex_lru_log_granularity; - int allow_batchbuffer; unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int num_pipe; int num_pch_pll; @@ -740,6 +739,15 @@ typedef struct drm_i915_private { size_t object_memory; u32 object_count; } mm; + + /* Old dri1 support infrastructure, beware the dragons ya fools entering + * here! */ + struct { + unsigned allow_batchbuffer : 1; + } dri1; + + /* Kernel Modesetting */ + struct sdvo_device_mapping sdvo_mappings[2]; /* indicate whether the LVDS_BORDER should be enabled or not */ unsigned int lvds_border_bits; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 293f573803ab..5e34775aa3a2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3568,7 +3568,7 @@ int i915_gem_init(struct drm_device *dev) } /* Allow hardware batchbuffers unless told otherwise. */ - dev_priv->allow_batchbuffer = 1; + dev_priv->dri1.allow_batchbuffer = 1; return 0; } From 53ca26cab8cd7e637ec0f5741fa2064dbc9d392e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:03 +0200 Subject: [PATCH 55/88] drm/i915 disallow physical batchbuffers for KMS Even the horrible gen3 XvMC code has learned to do this right by the time xf86-video-intel releases learned to do kernel modesetting. So we can just disallow this. Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5e34775aa3a2..131eadb84adb 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3567,8 +3567,9 @@ int i915_gem_init(struct drm_device *dev) return ret; } - /* Allow hardware batchbuffers unless told otherwise. */ - dev_priv->dri1.allow_batchbuffer = 1; + /* Allow hardware batchbuffers unless told otherwise, but not for KMS. */ + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + dev_priv->dri1.allow_batchbuffer = 1; return 0; } From 64c43c332156261d72e50e929203de116b1129a7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:04 +0200 Subject: [PATCH 56/88] drm/i915: rip out dev_priv->tex_lru_log_granularity Assigned in setparam, used never. I didn't bother to dig through the archives to figure out what this was supposed to do. Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 1 - drivers/gpu/drm/i915/i915_drv.h | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3b068a3917a9..4f1a9e864e03 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -879,7 +879,6 @@ static int i915_setparam(struct drm_device *dev, void *data, case I915_SETPARAM_USE_MI_BATCHBUFFER_START: break; case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY: - dev_priv->tex_lru_log_granularity = param->value; break; case I915_SETPARAM_ALLOW_BATCHBUFFER: dev_priv->dri1.allow_batchbuffer = param->value ? 1 : 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 65af417b823a..e7f428923733 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -398,7 +398,6 @@ typedef struct drm_i915_private { u32 hotplug_supported_mask; struct work_struct hotplug_work; - int tex_lru_log_granularity; unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int num_pipe; int num_pch_pll; From 6d90c952cdd20158ec41a5c016c6fad73c9a8749 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:05 +0200 Subject: [PATCH 57/88] drm/i915: remove LP_RING&friends from modeset code The LP refers to 'low priority' as opposed to the high priority ring on gen2/3. So lets constrain its use to the code of that era. Unfortunately we can't yet completely remove the associated macros from common headers and shove them into i915_dma.c to the other dri1 legacy support code, a few cleanups are still missing for that. Reviewed-by: Chris Wilson Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 76 +++++++++++++++------------- drivers/gpu/drm/i915/intel_overlay.c | 58 +++++++++++---------- drivers/gpu/drm/i915/intel_pm.c | 27 +++++----- 3 files changed, 86 insertions(+), 75 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e1716be47656..278c0f071585 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5749,16 +5749,17 @@ static int intel_gen2_queue_flip(struct drm_device *dev, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); unsigned long offset; u32 flip_mask; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; - ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); + ret = intel_pin_and_fence_fb_obj(dev, obj, ring); if (ret) goto err; /* Offset into the new buffer for cases of shared fbs between CRTCs */ offset = crtc->y * fb->pitches[0] + crtc->x * fb->bits_per_pixel/8; - ret = BEGIN_LP_RING(6); + ret = intel_ring_begin(ring, 6); if (ret) goto err_unpin; @@ -5769,14 +5770,14 @@ static int intel_gen2_queue_flip(struct drm_device *dev, flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; else flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - OUT_RING(MI_WAIT_FOR_EVENT | flip_mask); - OUT_RING(MI_NOOP); - OUT_RING(MI_DISPLAY_FLIP | - MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - OUT_RING(fb->pitches[0]); - OUT_RING(obj->gtt_offset + offset); - OUT_RING(0); /* aux display base address, unused */ - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_DISPLAY_FLIP | + MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); + intel_ring_emit(ring, fb->pitches[0]); + intel_ring_emit(ring, obj->gtt_offset + offset); + intel_ring_emit(ring, 0); /* aux display base address, unused */ + intel_ring_advance(ring); return 0; err_unpin: @@ -5794,16 +5795,17 @@ static int intel_gen3_queue_flip(struct drm_device *dev, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); unsigned long offset; u32 flip_mask; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; - ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); + ret = intel_pin_and_fence_fb_obj(dev, obj, ring); if (ret) goto err; /* Offset into the new buffer for cases of shared fbs between CRTCs */ offset = crtc->y * fb->pitches[0] + crtc->x * fb->bits_per_pixel/8; - ret = BEGIN_LP_RING(6); + ret = intel_ring_begin(ring, 6); if (ret) goto err_unpin; @@ -5811,15 +5813,15 @@ static int intel_gen3_queue_flip(struct drm_device *dev, flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; else flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; - OUT_RING(MI_WAIT_FOR_EVENT | flip_mask); - OUT_RING(MI_NOOP); - OUT_RING(MI_DISPLAY_FLIP_I915 | - MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - OUT_RING(fb->pitches[0]); - OUT_RING(obj->gtt_offset + offset); - OUT_RING(MI_NOOP); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | + MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); + intel_ring_emit(ring, fb->pitches[0]); + intel_ring_emit(ring, obj->gtt_offset + offset); + intel_ring_emit(ring, MI_NOOP); - ADVANCE_LP_RING(); + intel_ring_advance(ring); return 0; err_unpin: @@ -5836,13 +5838,14 @@ static int intel_gen4_queue_flip(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); uint32_t pf, pipesrc; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; - ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); + ret = intel_pin_and_fence_fb_obj(dev, obj, ring); if (ret) goto err; - ret = BEGIN_LP_RING(4); + ret = intel_ring_begin(ring, 4); if (ret) goto err_unpin; @@ -5850,10 +5853,10 @@ static int intel_gen4_queue_flip(struct drm_device *dev, * Display Registers (which do not change across a page-flip) * so we need only reprogram the base address. */ - OUT_RING(MI_DISPLAY_FLIP | - MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - OUT_RING(fb->pitches[0]); - OUT_RING(obj->gtt_offset | obj->tiling_mode); + intel_ring_emit(ring, MI_DISPLAY_FLIP | + MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); + intel_ring_emit(ring, fb->pitches[0]); + intel_ring_emit(ring, obj->gtt_offset | obj->tiling_mode); /* XXX Enabling the panel-fitter across page-flip is so far * untested on non-native modes, so ignore it for now. @@ -5861,8 +5864,8 @@ static int intel_gen4_queue_flip(struct drm_device *dev, */ pf = 0; pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; - OUT_RING(pf | pipesrc); - ADVANCE_LP_RING(); + intel_ring_emit(ring, pf | pipesrc); + intel_ring_advance(ring); return 0; err_unpin: @@ -5878,26 +5881,27 @@ static int intel_gen6_queue_flip(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; uint32_t pf, pipesrc; int ret; - ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv)); + ret = intel_pin_and_fence_fb_obj(dev, obj, ring); if (ret) goto err; - ret = BEGIN_LP_RING(4); + ret = intel_ring_begin(ring, 4); if (ret) goto err_unpin; - OUT_RING(MI_DISPLAY_FLIP | - MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); - OUT_RING(fb->pitches[0] | obj->tiling_mode); - OUT_RING(obj->gtt_offset); + intel_ring_emit(ring, MI_DISPLAY_FLIP | + MI_DISPLAY_FLIP_PLANE(intel_crtc->plane)); + intel_ring_emit(ring, fb->pitches[0] | obj->tiling_mode); + intel_ring_emit(ring, obj->gtt_offset); pf = I915_READ(PF_CTL(intel_crtc->pipe)) & PF_ENABLE; pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff; - OUT_RING(pf | pipesrc); - ADVANCE_LP_RING(); + intel_ring_emit(ring, pf | pipesrc); + intel_ring_advance(ring); return 0; err_unpin: diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index e06e46a30757..0bfab0bf60f4 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -215,17 +215,18 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay, { struct drm_device *dev = overlay->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; BUG_ON(overlay->last_flip_req); - ret = i915_add_request(LP_RING(dev_priv), NULL, request); + ret = i915_add_request(ring, NULL, request); if (ret) { kfree(request); return ret; } overlay->last_flip_req = request->seqno; overlay->flip_tail = tail; - ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req); + ret = i915_wait_request(ring, overlay->last_flip_req); if (ret) return ret; i915_gem_retire_requests(dev); @@ -287,6 +288,7 @@ static int intel_overlay_on(struct intel_overlay *overlay) { struct drm_device *dev = overlay->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; struct drm_i915_gem_request *request; int pipe_a_quirk = 0; int ret; @@ -306,17 +308,17 @@ static int intel_overlay_on(struct intel_overlay *overlay) goto out; } - ret = BEGIN_LP_RING(4); + ret = intel_ring_begin(ring, 4); if (ret) { kfree(request); goto out; } - OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON); - OUT_RING(overlay->flip_addr | OFC_UPDATE); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - OUT_RING(MI_NOOP); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_ON); + intel_ring_emit(ring, overlay->flip_addr | OFC_UPDATE); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); ret = intel_overlay_do_wait_request(overlay, request, NULL); out: @@ -332,6 +334,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay, { struct drm_device *dev = overlay->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; struct drm_i915_gem_request *request; u32 flip_addr = overlay->flip_addr; u32 tmp; @@ -351,16 +354,16 @@ static int intel_overlay_continue(struct intel_overlay *overlay, if (tmp & (1 << 17)) DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp); - ret = BEGIN_LP_RING(2); + ret = intel_ring_begin(ring, 2); if (ret) { kfree(request); return ret; } - OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); - OUT_RING(flip_addr); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); + intel_ring_emit(ring, flip_addr); + intel_ring_advance(ring); - ret = i915_add_request(LP_RING(dev_priv), NULL, request); + ret = i915_add_request(ring, NULL, request); if (ret) { kfree(request); return ret; @@ -401,6 +404,7 @@ static int intel_overlay_off(struct intel_overlay *overlay) { struct drm_device *dev = overlay->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; u32 flip_addr = overlay->flip_addr; struct drm_i915_gem_request *request; int ret; @@ -417,20 +421,20 @@ static int intel_overlay_off(struct intel_overlay *overlay) * of the hw. Do it in both cases */ flip_addr |= OFC_UPDATE; - ret = BEGIN_LP_RING(6); + ret = intel_ring_begin(ring, 6); if (ret) { kfree(request); return ret; } /* wait for overlay to go idle */ - OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); - OUT_RING(flip_addr); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE); + intel_ring_emit(ring, flip_addr); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); /* turn overlay off */ - OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF); - OUT_RING(flip_addr); - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_OFF); + intel_ring_emit(ring, flip_addr); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_advance(ring); return intel_overlay_do_wait_request(overlay, request, intel_overlay_off_tail); @@ -442,12 +446,13 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay) { struct drm_device *dev = overlay->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; if (overlay->last_flip_req == 0) return 0; - ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req); + ret = i915_wait_request(ring, overlay->last_flip_req); if (ret) return ret; i915_gem_retire_requests(dev); @@ -467,6 +472,7 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay) { struct drm_device *dev = overlay->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; /* Only wait if there is actually an old frame to release to @@ -483,15 +489,15 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay) if (request == NULL) return -ENOMEM; - ret = BEGIN_LP_RING(2); + ret = intel_ring_begin(ring, 2); if (ret) { kfree(request); return ret; } - OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); - OUT_RING(MI_NOOP); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); ret = intel_overlay_do_wait_request(overlay, request, intel_overlay_release_old_vid_tail); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e66330cc0934..0a3699908fdf 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2436,6 +2436,7 @@ static int ironlake_setup_rc6(struct drm_device *dev) void ironlake_enable_rc6(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; int ret; /* rc6 disabled by default due to repeated reports of hanging during @@ -2455,31 +2456,31 @@ void ironlake_enable_rc6(struct drm_device *dev) * GPU can automatically power down the render unit if given a page * to save state. */ - ret = BEGIN_LP_RING(6); + ret = intel_ring_begin(ring, 6); if (ret) { ironlake_teardown_rc6(dev); mutex_unlock(&dev->struct_mutex); return; } - OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); - OUT_RING(MI_SET_CONTEXT); - OUT_RING(dev_priv->renderctx->gtt_offset | - MI_MM_SPACE_GTT | - MI_SAVE_EXT_STATE_EN | - MI_RESTORE_EXT_STATE_EN | - MI_RESTORE_INHIBIT); - OUT_RING(MI_SUSPEND_FLUSH); - OUT_RING(MI_NOOP); - OUT_RING(MI_FLUSH); - ADVANCE_LP_RING(); + intel_ring_emit(ring, MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN); + intel_ring_emit(ring, MI_SET_CONTEXT); + intel_ring_emit(ring, dev_priv->renderctx->gtt_offset | + MI_MM_SPACE_GTT | + MI_SAVE_EXT_STATE_EN | + MI_RESTORE_EXT_STATE_EN | + MI_RESTORE_INHIBIT); + intel_ring_emit(ring, MI_SUSPEND_FLUSH); + intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, MI_FLUSH); + intel_ring_advance(ring); /* * Wait for the command parser to advance past MI_SET_CONTEXT. The HW * does an implicit flush, combined with MI_FLUSH above, it should be * safe to assume that renderctx is valid */ - ret = intel_wait_ring_idle(LP_RING(dev_priv)); + ret = intel_wait_ring_idle(ring); if (ret) { DRM_ERROR("failed to enable ironlake power power savings\n"); ironlake_teardown_rc6(dev); From d56b21361ca52afa5d7cb47c9792d0982666d9a5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:06 +0200 Subject: [PATCH 58/88] drm/i915: kill intel_clear_scanline_wait This is a pretty racy way to close these races, and we have much better means to cope with these races meanwhile: For non-broken userspace we correctly wait for any outstanding rendering, for broken userspace the hangcheck will save the day. Reviewed-by: Chris Wilson Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 278c0f071585..2617ab580dff 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2528,26 +2528,6 @@ static void ironlake_fdi_disable(struct drm_crtc *crtc) udelay(100); } -/* - * When we disable a pipe, we need to clear any pending scanline wait events - * to avoid hanging the ring, which we assume we are waiting on. - */ -static void intel_clear_scanline_wait(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_ring_buffer *ring; - u32 tmp; - - if (IS_GEN2(dev)) - /* Can't break the hang on i8xx */ - return; - - ring = LP_RING(dev_priv); - tmp = I915_READ_CTL(ring); - if (tmp & RING_WAIT) - I915_WRITE_CTL(ring, tmp); -} - static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -2931,7 +2911,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) mutex_lock(&dev->struct_mutex); intel_update_fbc(dev); - intel_clear_scanline_wait(dev); mutex_unlock(&dev->struct_mutex); } @@ -3036,7 +3015,6 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) intel_crtc->active = false; intel_update_fbc(dev); intel_update_watermarks(dev); - intel_clear_scanline_wait(dev); } static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) From 647a3fb2f31298ad9d062cbb8019b5dc26e4ca69 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:07 +0200 Subject: [PATCH 59/88] drm/i915: rip out dri1 breadcrumb updates from gen5+ irq handlers We never supported dri1 on gen5+. VLV never had that code, so no need to remove it. Reviewed-by: Chris Wilson Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 96c14fe0e7f8..ef2c23d7f48f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -582,7 +582,6 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) 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; - struct drm_i915_master_private *master_priv; atomic_inc(&dev_priv->irq_received); @@ -601,13 +600,6 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) ret = IRQ_HANDLED; - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } - snb_gt_irq_handler(dev, dev_priv, gt_iir); if (de_iir & DE_GSE_IVB) @@ -669,7 +661,6 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) int ret = IRQ_NONE; u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir; u32 hotplug_mask; - struct drm_i915_master_private *master_priv; atomic_inc(&dev_priv->irq_received); @@ -694,13 +685,6 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) ret = IRQ_HANDLED; - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } - if (IS_GEN5(dev)) ilk_gt_irq_handler(dev, dev_priv, gt_iir); else From 9488867a67fda2310448e44a906f1a54faa72fb4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:08 +0200 Subject: [PATCH 60/88] drm/i915: move dri1 irq ioctl code to i915_dma.c Let's just get this out of the way. v2: Rebase against ENODEV changes. Reviewed-by: Chris Wilson Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 110 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 4 -- drivers/gpu/drm/i915/i915_irq.c | 110 -------------------------------- 3 files changed, 110 insertions(+), 114 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 4f1a9e864e03..6f6c4bda689c 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -721,6 +721,116 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data, return ret; } +static int i915_emit_irq(struct drm_device * dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; + + i915_kernel_lost_context(dev); + + DRM_DEBUG_DRIVER("\n"); + + dev_priv->counter++; + if (dev_priv->counter > 0x7FFFFFFFUL) + dev_priv->counter = 1; + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_enqueue = dev_priv->counter; + + if (BEGIN_LP_RING(4) == 0) { + OUT_RING(MI_STORE_DWORD_INDEX); + OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(dev_priv->counter); + OUT_RING(MI_USER_INTERRUPT); + ADVANCE_LP_RING(); + } + + return dev_priv->counter; +} + +static int i915_wait_irq(struct drm_device * dev, int irq_nr) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; + int ret = 0; + struct intel_ring_buffer *ring = LP_RING(dev_priv); + + DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr, + READ_BREADCRUMB(dev_priv)); + + if (READ_BREADCRUMB(dev_priv) >= irq_nr) { + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); + return 0; + } + + if (master_priv->sarea_priv) + master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; + + if (ring->irq_get(ring)) { + DRM_WAIT_ON(ret, ring->irq_queue, 3 * DRM_HZ, + READ_BREADCRUMB(dev_priv) >= irq_nr); + ring->irq_put(ring); + } else if (wait_for(READ_BREADCRUMB(dev_priv) >= irq_nr, 3000)) + ret = -EBUSY; + + if (ret == -EBUSY) { + DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", + READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); + } + + return ret; +} + +/* Needs the lock as it touches the ring. + */ +static int i915_irq_emit(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_irq_emit_t *emit = data; + int result; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + + if (!dev_priv || !LP_RING(dev_priv)->virtual_start) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + RING_LOCK_TEST_WITH_RETURN(dev, file_priv); + + mutex_lock(&dev->struct_mutex); + result = i915_emit_irq(dev); + mutex_unlock(&dev->struct_mutex); + + if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) { + DRM_ERROR("copy_to_user\n"); + return -EFAULT; + } + + return 0; +} + +/* Doesn't need the hardware lock. + */ +static int i915_irq_wait(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + drm_i915_irq_wait_t *irqwait = data; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -ENODEV; + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + return i915_wait_irq(dev, irqwait->irq_seq); +} + static int i915_vblank_pipe_get(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e7f428923733..b297812b8984 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1156,10 +1156,6 @@ extern void i915_update_gfx_val(struct drm_i915_private *dev_priv); /* i915_irq.c */ void i915_hangcheck_elapsed(unsigned long data); void i915_handle_error(struct drm_device *dev, bool wedged); -extern int i915_irq_emit(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int i915_irq_wait(struct drm_device *dev, void *data, - struct drm_file *file_priv); extern void intel_irq_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ef2c23d7f48f..8675e433cd55 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1364,116 +1364,6 @@ static void i915_pageflip_stall_check(struct drm_device *dev, int pipe) } } -static int i915_emit_irq(struct drm_device * dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; - - i915_kernel_lost_context(dev); - - DRM_DEBUG_DRIVER("\n"); - - dev_priv->counter++; - if (dev_priv->counter > 0x7FFFFFFFUL) - dev_priv->counter = 1; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_enqueue = dev_priv->counter; - - if (BEGIN_LP_RING(4) == 0) { - OUT_RING(MI_STORE_DWORD_INDEX); - OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); - OUT_RING(dev_priv->counter); - OUT_RING(MI_USER_INTERRUPT); - ADVANCE_LP_RING(); - } - - return dev_priv->counter; -} - -static int i915_wait_irq(struct drm_device * dev, int irq_nr) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; - int ret = 0; - struct intel_ring_buffer *ring = LP_RING(dev_priv); - - DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr, - READ_BREADCRUMB(dev_priv)); - - if (READ_BREADCRUMB(dev_priv) >= irq_nr) { - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); - return 0; - } - - if (master_priv->sarea_priv) - master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; - - if (ring->irq_get(ring)) { - DRM_WAIT_ON(ret, ring->irq_queue, 3 * DRM_HZ, - READ_BREADCRUMB(dev_priv) >= irq_nr); - ring->irq_put(ring); - } else if (wait_for(READ_BREADCRUMB(dev_priv) >= irq_nr, 3000)) - ret = -EBUSY; - - if (ret == -EBUSY) { - DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", - READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); - } - - return ret; -} - -/* Needs the lock as it touches the ring. - */ -int i915_irq_emit(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_irq_emit_t *emit = data; - int result; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -ENODEV; - - if (!dev_priv || !LP_RING(dev_priv)->virtual_start) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - RING_LOCK_TEST_WITH_RETURN(dev, file_priv); - - mutex_lock(&dev->struct_mutex); - result = i915_emit_irq(dev); - mutex_unlock(&dev->struct_mutex); - - if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) { - DRM_ERROR("copy_to_user\n"); - return -EFAULT; - } - - return 0; -} - -/* Doesn't need the hardware lock. - */ -int i915_irq_wait(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_irq_wait_t *irqwait = data; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return -ENODEV; - - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - return i915_wait_irq(dev, irqwait->irq_seq); -} - /* Called from drm generic code, passed 'crtc' which * we use as a pipe index */ From d05c617ea4643f19232a38a5190cfe95ee88f1dd Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:09 +0200 Subject: [PATCH 61/88] drm/i915: extract dri1 breadcrumb update from irq handler ... and hide it in i915_dma.c. This way all the legacy stuff dealing with READ_BREADCRUMB and LP_RING and friends is in i915_dma.c. v2: Rebase on top of Chris Wilson's rework irq handling code. Reviewed-by: Chris Wilson Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 13 +++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_irq.c | 24 +++--------------------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 6f6c4bda689c..30cb4461a250 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -47,6 +47,19 @@ #include #include +void i915_update_dri1_breadcrumb(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_master_private *master_priv; + + if (dev->primary->master) { + master_priv = dev->primary->master->driver_priv; + if (master_priv->sarea_priv) + master_priv->sarea_priv->last_dispatch = + READ_BREADCRUMB(dev_priv); + } +} + static void i915_write_hws_pga(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b297812b8984..f16f080043f6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1129,6 +1129,7 @@ extern int i915_master_create(struct drm_device *dev, struct drm_master *master) extern void i915_master_destroy(struct drm_device *dev, struct drm_master *master); /* i915_dma.c */ +void i915_update_dri1_breadcrumb(struct drm_device *dev); extern void i915_kernel_lost_context(struct drm_device * dev); extern int i915_driver_load(struct drm_device *, unsigned long flags); extern int i915_driver_unload(struct drm_device *); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 8675e433cd55..49e5d3ec5559 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2015,7 +2015,6 @@ static irqreturn_t i8xx_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct drm_i915_master_private *master_priv; u16 iir, new_iir; u32 pipe_stats[2]; unsigned long irqflags; @@ -2061,12 +2060,7 @@ static irqreturn_t i8xx_irq_handler(DRM_IRQ_ARGS) I915_WRITE16(IIR, iir & ~flip_mask); new_iir = I915_READ16(IIR); /* Flush posted writes */ - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } + i915_update_dri1_breadcrumb(dev); if (iir & I915_USER_INTERRUPT) notify_ring(dev, &dev_priv->ring[RCS]); @@ -2199,7 +2193,6 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct drm_i915_master_private *master_priv; u32 iir, new_iir, pipe_stats[I915_MAX_PIPES]; unsigned long irqflags; u32 flip_mask = @@ -2305,12 +2298,7 @@ static irqreturn_t i915_irq_handler(DRM_IRQ_ARGS) iir = new_iir; } while (iir & ~flip_mask); - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } + i915_update_dri1_breadcrumb(dev); return ret; } @@ -2446,7 +2434,6 @@ static irqreturn_t i965_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct drm_i915_master_private *master_priv; u32 iir, new_iir; u32 pipe_stats[I915_MAX_PIPES]; unsigned long irqflags; @@ -2555,12 +2542,7 @@ static irqreturn_t i965_irq_handler(DRM_IRQ_ARGS) iir = new_iir; } - if (dev->primary->master) { - master_priv = dev->primary->master->driver_priv; - if (master_priv->sarea_priv) - master_priv->sarea_priv->last_dispatch = - READ_BREADCRUMB(dev_priv); - } + i915_update_dri1_breadcrumb(dev); return ret; } From 09422b2e7290c2899df78ebcc6b1bdcc4f2328dd Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:10 +0200 Subject: [PATCH 62/88] drm/i915: move LP_RING&friends to i915_dma.c Wohoo! Now we only need to move all the gem/kms stuff that accidentally landed in i915_dma.c out of it, and this will be our legacy dri1 grave-yard. Reviewed-by: Chris Wilson Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 26 +++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.h | 22 --------------------- drivers/gpu/drm/i915/intel_ringbuffer.h | 3 --- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 30cb4461a250..833ac8a0cf43 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -47,6 +47,32 @@ #include #include +#define LP_RING(d) (&((struct drm_i915_private *)(d))->ring[RCS]) + +#define BEGIN_LP_RING(n) \ + intel_ring_begin(LP_RING(dev_priv), (n)) + +#define OUT_RING(x) \ + intel_ring_emit(LP_RING(dev_priv), x) + +#define ADVANCE_LP_RING() \ + intel_ring_advance(LP_RING(dev_priv)) + +/** + * Lock test for when it's just for synchronization of ring access. + * + * In that case, we don't need to do it when GEM is initialized as nobody else + * has access to the ring. + */ +#define RING_LOCK_TEST_WITH_RETURN(dev, file) do { \ + if (LP_RING(dev->dev_private)->obj == NULL) \ + LOCK_TEST_WITH_RETURN(dev, file); \ +} while (0) + +#define READ_HWSP(dev_priv, reg) intel_read_status_page(LP_RING(dev_priv), reg) +#define READ_BREADCRUMB(dev_priv) READ_HWSP(dev_priv, I915_BREADCRUMB_INDEX) +#define I915_BREADCRUMB_INDEX 0x21 + void i915_update_dri1_breadcrumb(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f16f080043f6..7d48ae3d7678 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1471,28 +1471,6 @@ extern void intel_display_print_error_state(struct seq_file *m, struct intel_display_error_state *error); #endif -#define LP_RING(d) (&((struct drm_i915_private *)(d))->ring[RCS]) - -#define BEGIN_LP_RING(n) \ - intel_ring_begin(LP_RING(dev_priv), (n)) - -#define OUT_RING(x) \ - intel_ring_emit(LP_RING(dev_priv), x) - -#define ADVANCE_LP_RING() \ - intel_ring_advance(LP_RING(dev_priv)) - -/** - * Lock test for when it's just for synchronization of ring access. - * - * In that case, we don't need to do it when GEM is initialized as nobody else - * has access to the ring. - */ -#define RING_LOCK_TEST_WITH_RETURN(dev, file) do { \ - if (LP_RING(dev->dev_private)->obj == NULL) \ - LOCK_TEST_WITH_RETURN(dev, file); \ -} while (0) - /* On SNB platform, before reading ring registers forcewake bit * must be set to prevent GT core from power down and stale values being * returned. diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 4d1c6c4235d6..7b879926969e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -167,10 +167,7 @@ intel_read_status_page(struct intel_ring_buffer *ring, * * The area from dword 0x20 to 0x3ff is available for driver usage. */ -#define READ_HWSP(dev_priv, reg) intel_read_status_page(LP_RING(dev_priv), reg) -#define READ_BREADCRUMB(dev_priv) READ_HWSP(dev_priv, I915_BREADCRUMB_INDEX) #define I915_GEM_HWS_INDEX 0x20 -#define I915_BREADCRUMB_INDEX 0x21 void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring); From 6ebebc9206fca1d20d90edec4873e819cc4051d0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:11 +0200 Subject: [PATCH 63/88] drm/i915: disallow clip rects on gen5+ Unfortunately there has been dri1 userspace that used gem to manage the gtt and hence also needed cliprects in the execbuf ioctl. So we can't ever remove that code without breaking the ioctl abi. But at least we can disable it on gen5+, because these horrible versions of mesa have not supported these chips. Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index cbba0aa6104b..a46ed26464f4 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1116,6 +1116,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, return -EINVAL; } + if (INTEL_INFO(dev)->gen >= 5) { + DRM_DEBUG("clip rectangles are only valid on pre-gen5\n"); + return -EINVAL; + } + cliprects = kmalloc(args->num_cliprects * sizeof(*cliprects), GFP_KERNEL); if (cliprects == NULL) { From eb48eb005098cc6f845d281dd079baea7573f54c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:12 +0200 Subject: [PATCH 64/88] drm/i915: move the ips code to intel_pm.c We now have a nice home for power management code, so let's use it! v2: Resolve conflict agains "Only enable IPS polling for gen5" Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 478 +----------------------------- drivers/gpu/drm/i915/intel_drv.h | 3 + drivers/gpu/drm/i915/intel_pm.c | 481 +++++++++++++++++++++++++++++++ 3 files changed, 487 insertions(+), 475 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 833ac8a0cf43..1b1f8e70df8d 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -36,14 +36,12 @@ #include "i915_drm.h" #include "i915_drv.h" #include "i915_trace.h" -#include "../../../platform/x86/intel_ips.h" #include #include #include #include #include #include -#include #include #include @@ -1481,468 +1479,6 @@ static void i915_ironlake_get_mem_freq(struct drm_device *dev) } } -static const struct cparams { - u16 i; - u16 t; - u16 m; - u16 c; -} cparams[] = { - { 1, 1333, 301, 28664 }, - { 1, 1066, 294, 24460 }, - { 1, 800, 294, 25192 }, - { 0, 1333, 276, 27605 }, - { 0, 1066, 276, 27605 }, - { 0, 800, 231, 23784 }, -}; - -unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) -{ - u64 total_count, diff, ret; - u32 count1, count2, count3, m = 0, c = 0; - unsigned long now = jiffies_to_msecs(jiffies), diff1; - int i; - - diff1 = now - dev_priv->last_time1; - - /* Prevent division-by-zero if we are asking too fast. - * Also, we don't get interesting results if we are polling - * faster than once in 10ms, so just return the saved value - * in such cases. - */ - if (diff1 <= 10) - return dev_priv->chipset_power; - - count1 = I915_READ(DMIEC); - count2 = I915_READ(DDREC); - count3 = I915_READ(CSIEC); - - total_count = count1 + count2 + count3; - - /* FIXME: handle per-counter overflow */ - if (total_count < dev_priv->last_count1) { - diff = ~0UL - dev_priv->last_count1; - diff += total_count; - } else { - diff = total_count - dev_priv->last_count1; - } - - for (i = 0; i < ARRAY_SIZE(cparams); i++) { - if (cparams[i].i == dev_priv->c_m && - cparams[i].t == dev_priv->r_t) { - m = cparams[i].m; - c = cparams[i].c; - break; - } - } - - diff = div_u64(diff, diff1); - ret = ((m * diff) + c); - ret = div_u64(ret, 10); - - dev_priv->last_count1 = total_count; - dev_priv->last_time1 = now; - - dev_priv->chipset_power = ret; - - return ret; -} - -unsigned long i915_mch_val(struct drm_i915_private *dev_priv) -{ - unsigned long m, x, b; - u32 tsfs; - - tsfs = I915_READ(TSFS); - - m = ((tsfs & TSFS_SLOPE_MASK) >> TSFS_SLOPE_SHIFT); - x = I915_READ8(TR1); - - b = tsfs & TSFS_INTR_MASK; - - return ((m * x) / 127) - b; -} - -static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid) -{ - static const struct v_table { - u16 vd; /* in .1 mil */ - u16 vm; /* in .1 mil */ - } v_table[] = { - { 0, 0, }, - { 375, 0, }, - { 500, 0, }, - { 625, 0, }, - { 750, 0, }, - { 875, 0, }, - { 1000, 0, }, - { 1125, 0, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4125, 3000, }, - { 4250, 3125, }, - { 4375, 3250, }, - { 4500, 3375, }, - { 4625, 3500, }, - { 4750, 3625, }, - { 4875, 3750, }, - { 5000, 3875, }, - { 5125, 4000, }, - { 5250, 4125, }, - { 5375, 4250, }, - { 5500, 4375, }, - { 5625, 4500, }, - { 5750, 4625, }, - { 5875, 4750, }, - { 6000, 4875, }, - { 6125, 5000, }, - { 6250, 5125, }, - { 6375, 5250, }, - { 6500, 5375, }, - { 6625, 5500, }, - { 6750, 5625, }, - { 6875, 5750, }, - { 7000, 5875, }, - { 7125, 6000, }, - { 7250, 6125, }, - { 7375, 6250, }, - { 7500, 6375, }, - { 7625, 6500, }, - { 7750, 6625, }, - { 7875, 6750, }, - { 8000, 6875, }, - { 8125, 7000, }, - { 8250, 7125, }, - { 8375, 7250, }, - { 8500, 7375, }, - { 8625, 7500, }, - { 8750, 7625, }, - { 8875, 7750, }, - { 9000, 7875, }, - { 9125, 8000, }, - { 9250, 8125, }, - { 9375, 8250, }, - { 9500, 8375, }, - { 9625, 8500, }, - { 9750, 8625, }, - { 9875, 8750, }, - { 10000, 8875, }, - { 10125, 9000, }, - { 10250, 9125, }, - { 10375, 9250, }, - { 10500, 9375, }, - { 10625, 9500, }, - { 10750, 9625, }, - { 10875, 9750, }, - { 11000, 9875, }, - { 11125, 10000, }, - { 11250, 10125, }, - { 11375, 10250, }, - { 11500, 10375, }, - { 11625, 10500, }, - { 11750, 10625, }, - { 11875, 10750, }, - { 12000, 10875, }, - { 12125, 11000, }, - { 12250, 11125, }, - { 12375, 11250, }, - { 12500, 11375, }, - { 12625, 11500, }, - { 12750, 11625, }, - { 12875, 11750, }, - { 13000, 11875, }, - { 13125, 12000, }, - { 13250, 12125, }, - { 13375, 12250, }, - { 13500, 12375, }, - { 13625, 12500, }, - { 13750, 12625, }, - { 13875, 12750, }, - { 14000, 12875, }, - { 14125, 13000, }, - { 14250, 13125, }, - { 14375, 13250, }, - { 14500, 13375, }, - { 14625, 13500, }, - { 14750, 13625, }, - { 14875, 13750, }, - { 15000, 13875, }, - { 15125, 14000, }, - { 15250, 14125, }, - { 15375, 14250, }, - { 15500, 14375, }, - { 15625, 14500, }, - { 15750, 14625, }, - { 15875, 14750, }, - { 16000, 14875, }, - { 16125, 15000, }, - }; - if (dev_priv->info->is_mobile) - return v_table[pxvid].vm; - else - return v_table[pxvid].vd; -} - -void i915_update_gfx_val(struct drm_i915_private *dev_priv) -{ - struct timespec now, diff1; - u64 diff; - unsigned long diffms; - u32 count; - - if (dev_priv->info->gen != 5) - return; - - getrawmonotonic(&now); - diff1 = timespec_sub(now, dev_priv->last_time2); - - /* Don't divide by 0 */ - diffms = diff1.tv_sec * 1000 + diff1.tv_nsec / 1000000; - if (!diffms) - return; - - count = I915_READ(GFXEC); - - if (count < dev_priv->last_count2) { - diff = ~0UL - dev_priv->last_count2; - diff += count; - } else { - diff = count - dev_priv->last_count2; - } - - dev_priv->last_count2 = count; - dev_priv->last_time2 = now; - - /* More magic constants... */ - diff = diff * 1181; - diff = div_u64(diff, diffms * 10); - dev_priv->gfx_power = diff; -} - -unsigned long i915_gfx_val(struct drm_i915_private *dev_priv) -{ - unsigned long t, corr, state1, corr2, state2; - u32 pxvid, ext_v; - - pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4)); - pxvid = (pxvid >> 24) & 0x7f; - ext_v = pvid_to_extvid(dev_priv, pxvid); - - state1 = ext_v; - - t = i915_mch_val(dev_priv); - - /* Revel in the empirically derived constants */ - - /* Correction factor in 1/100000 units */ - if (t > 80) - corr = ((t * 2349) + 135940); - else if (t >= 50) - corr = ((t * 964) + 29317); - else /* < 50 */ - corr = ((t * 301) + 1004); - - corr = corr * ((150142 * state1) / 10000 - 78642); - corr /= 100000; - corr2 = (corr * dev_priv->corr); - - state2 = (corr2 * state1) / 10000; - state2 /= 100; /* convert to mW */ - - i915_update_gfx_val(dev_priv); - - return dev_priv->gfx_power + state2; -} - -/* Global for IPS driver to get at the current i915 device */ -static struct drm_i915_private *i915_mch_dev; -/* - * Lock protecting IPS related data structures - * - i915_mch_dev - * - dev_priv->max_delay - * - dev_priv->min_delay - * - dev_priv->fmax - * - dev_priv->gpu_busy - */ -static DEFINE_SPINLOCK(mchdev_lock); - -/** - * i915_read_mch_val - return value for IPS use - * - * Calculate and return a value for the IPS driver to use when deciding whether - * we have thermal and power headroom to increase CPU or GPU power budget. - */ -unsigned long i915_read_mch_val(void) -{ - struct drm_i915_private *dev_priv; - unsigned long chipset_val, graphics_val, ret = 0; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) - goto out_unlock; - dev_priv = i915_mch_dev; - - chipset_val = i915_chipset_val(dev_priv); - graphics_val = i915_gfx_val(dev_priv); - - ret = chipset_val + graphics_val; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_read_mch_val); - -/** - * i915_gpu_raise - raise GPU frequency limit - * - * Raise the limit; IPS indicates we have thermal headroom. - */ -bool i915_gpu_raise(void) -{ - struct drm_i915_private *dev_priv; - bool ret = true; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) { - ret = false; - goto out_unlock; - } - dev_priv = i915_mch_dev; - - if (dev_priv->max_delay > dev_priv->fmax) - dev_priv->max_delay--; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_gpu_raise); - -/** - * i915_gpu_lower - lower GPU frequency limit - * - * IPS indicates we're close to a thermal limit, so throttle back the GPU - * frequency maximum. - */ -bool i915_gpu_lower(void) -{ - struct drm_i915_private *dev_priv; - bool ret = true; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) { - ret = false; - goto out_unlock; - } - dev_priv = i915_mch_dev; - - if (dev_priv->max_delay < dev_priv->min_delay) - dev_priv->max_delay++; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_gpu_lower); - -/** - * i915_gpu_busy - indicate GPU business to IPS - * - * Tell the IPS driver whether or not the GPU is busy. - */ -bool i915_gpu_busy(void) -{ - struct drm_i915_private *dev_priv; - bool ret = false; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) - goto out_unlock; - dev_priv = i915_mch_dev; - - ret = dev_priv->busy; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_gpu_busy); - -/** - * i915_gpu_turbo_disable - disable graphics turbo - * - * Disable graphics turbo by resetting the max frequency and setting the - * current frequency to the default. - */ -bool i915_gpu_turbo_disable(void) -{ - struct drm_i915_private *dev_priv; - bool ret = true; - - spin_lock(&mchdev_lock); - if (!i915_mch_dev) { - ret = false; - goto out_unlock; - } - dev_priv = i915_mch_dev; - - dev_priv->max_delay = dev_priv->fstart; - - if (!ironlake_set_drps(dev_priv->dev, dev_priv->fstart)) - ret = false; - -out_unlock: - spin_unlock(&mchdev_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable); - -/** - * Tells the intel_ips driver that the i915 driver is now loaded, if - * IPS got loaded first. - * - * This awkward dance is so that neither module has to depend on the - * other in order for IPS to do the appropriate communication of - * GPU turbo limits to i915. - */ -static void -ips_ping_for_i915_load(void) -{ - void (*link)(void); - - link = symbol_get(ips_link_to_i915_driver); - if (link) { - link(); - symbol_put(ips_link_to_i915_driver); - } -} - static void i915_mtrr_setup(struct drm_i915_private *dev_priv, unsigned long base, unsigned long size) @@ -2152,14 +1688,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed, (unsigned long) dev); - if (IS_GEN5(dev)) { - spin_lock(&mchdev_lock); - i915_mch_dev = dev_priv; - dev_priv->mchdev_lock = &mchdev_lock; - spin_unlock(&mchdev_lock); - - ips_ping_for_i915_load(); - } + if (IS_GEN5(dev)) + intel_gpu_ips_init(dev_priv); return 0; @@ -2194,9 +1724,7 @@ int i915_driver_unload(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int ret; - spin_lock(&mchdev_lock); - i915_mch_dev = NULL; - spin_unlock(&mchdev_lock); + intel_gpu_ips_teardown(); i915_teardown_sysfs(dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4b7ec449d3cc..f4f1e8bba535 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -462,5 +462,8 @@ extern void intel_init_pm(struct drm_device *dev); extern bool intel_fbc_enabled(struct drm_device *dev); extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval); extern void intel_update_fbc(struct drm_device *dev); +/* IPS */ +extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv); +extern void intel_gpu_ips_teardown(void); #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 0a3699908fdf..e0f016c24dce 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -28,6 +28,8 @@ #include #include "i915_drv.h" #include "intel_drv.h" +#include "../../../platform/x86/intel_ips.h" +#include /* FBC, or Frame Buffer Compression, is a technique employed to compress the * framebuffer contents in-memory, aiming at reducing the required bandwidth @@ -2508,6 +2510,485 @@ static unsigned long intel_pxfreq(u32 vidfreq) return freq; } +static const struct cparams { + u16 i; + u16 t; + u16 m; + u16 c; +} cparams[] = { + { 1, 1333, 301, 28664 }, + { 1, 1066, 294, 24460 }, + { 1, 800, 294, 25192 }, + { 0, 1333, 276, 27605 }, + { 0, 1066, 276, 27605 }, + { 0, 800, 231, 23784 }, +}; + +unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) +{ + u64 total_count, diff, ret; + u32 count1, count2, count3, m = 0, c = 0; + unsigned long now = jiffies_to_msecs(jiffies), diff1; + int i; + + diff1 = now - dev_priv->last_time1; + + /* Prevent division-by-zero if we are asking too fast. + * Also, we don't get interesting results if we are polling + * faster than once in 10ms, so just return the saved value + * in such cases. + */ + if (diff1 <= 10) + return dev_priv->chipset_power; + + count1 = I915_READ(DMIEC); + count2 = I915_READ(DDREC); + count3 = I915_READ(CSIEC); + + total_count = count1 + count2 + count3; + + /* FIXME: handle per-counter overflow */ + if (total_count < dev_priv->last_count1) { + diff = ~0UL - dev_priv->last_count1; + diff += total_count; + } else { + diff = total_count - dev_priv->last_count1; + } + + for (i = 0; i < ARRAY_SIZE(cparams); i++) { + if (cparams[i].i == dev_priv->c_m && + cparams[i].t == dev_priv->r_t) { + m = cparams[i].m; + c = cparams[i].c; + break; + } + } + + diff = div_u64(diff, diff1); + ret = ((m * diff) + c); + ret = div_u64(ret, 10); + + dev_priv->last_count1 = total_count; + dev_priv->last_time1 = now; + + dev_priv->chipset_power = ret; + + return ret; +} + +unsigned long i915_mch_val(struct drm_i915_private *dev_priv) +{ + unsigned long m, x, b; + u32 tsfs; + + tsfs = I915_READ(TSFS); + + m = ((tsfs & TSFS_SLOPE_MASK) >> TSFS_SLOPE_SHIFT); + x = I915_READ8(TR1); + + b = tsfs & TSFS_INTR_MASK; + + return ((m * x) / 127) - b; +} + +static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid) +{ + static const struct v_table { + u16 vd; /* in .1 mil */ + u16 vm; /* in .1 mil */ + } v_table[] = { + { 0, 0, }, + { 375, 0, }, + { 500, 0, }, + { 625, 0, }, + { 750, 0, }, + { 875, 0, }, + { 1000, 0, }, + { 1125, 0, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4125, 3000, }, + { 4250, 3125, }, + { 4375, 3250, }, + { 4500, 3375, }, + { 4625, 3500, }, + { 4750, 3625, }, + { 4875, 3750, }, + { 5000, 3875, }, + { 5125, 4000, }, + { 5250, 4125, }, + { 5375, 4250, }, + { 5500, 4375, }, + { 5625, 4500, }, + { 5750, 4625, }, + { 5875, 4750, }, + { 6000, 4875, }, + { 6125, 5000, }, + { 6250, 5125, }, + { 6375, 5250, }, + { 6500, 5375, }, + { 6625, 5500, }, + { 6750, 5625, }, + { 6875, 5750, }, + { 7000, 5875, }, + { 7125, 6000, }, + { 7250, 6125, }, + { 7375, 6250, }, + { 7500, 6375, }, + { 7625, 6500, }, + { 7750, 6625, }, + { 7875, 6750, }, + { 8000, 6875, }, + { 8125, 7000, }, + { 8250, 7125, }, + { 8375, 7250, }, + { 8500, 7375, }, + { 8625, 7500, }, + { 8750, 7625, }, + { 8875, 7750, }, + { 9000, 7875, }, + { 9125, 8000, }, + { 9250, 8125, }, + { 9375, 8250, }, + { 9500, 8375, }, + { 9625, 8500, }, + { 9750, 8625, }, + { 9875, 8750, }, + { 10000, 8875, }, + { 10125, 9000, }, + { 10250, 9125, }, + { 10375, 9250, }, + { 10500, 9375, }, + { 10625, 9500, }, + { 10750, 9625, }, + { 10875, 9750, }, + { 11000, 9875, }, + { 11125, 10000, }, + { 11250, 10125, }, + { 11375, 10250, }, + { 11500, 10375, }, + { 11625, 10500, }, + { 11750, 10625, }, + { 11875, 10750, }, + { 12000, 10875, }, + { 12125, 11000, }, + { 12250, 11125, }, + { 12375, 11250, }, + { 12500, 11375, }, + { 12625, 11500, }, + { 12750, 11625, }, + { 12875, 11750, }, + { 13000, 11875, }, + { 13125, 12000, }, + { 13250, 12125, }, + { 13375, 12250, }, + { 13500, 12375, }, + { 13625, 12500, }, + { 13750, 12625, }, + { 13875, 12750, }, + { 14000, 12875, }, + { 14125, 13000, }, + { 14250, 13125, }, + { 14375, 13250, }, + { 14500, 13375, }, + { 14625, 13500, }, + { 14750, 13625, }, + { 14875, 13750, }, + { 15000, 13875, }, + { 15125, 14000, }, + { 15250, 14125, }, + { 15375, 14250, }, + { 15500, 14375, }, + { 15625, 14500, }, + { 15750, 14625, }, + { 15875, 14750, }, + { 16000, 14875, }, + { 16125, 15000, }, + }; + if (dev_priv->info->is_mobile) + return v_table[pxvid].vm; + else + return v_table[pxvid].vd; +} + +void i915_update_gfx_val(struct drm_i915_private *dev_priv) +{ + struct timespec now, diff1; + u64 diff; + unsigned long diffms; + u32 count; + + if (dev_priv->info->gen != 5) + return; + + getrawmonotonic(&now); + diff1 = timespec_sub(now, dev_priv->last_time2); + + /* Don't divide by 0 */ + diffms = diff1.tv_sec * 1000 + diff1.tv_nsec / 1000000; + if (!diffms) + return; + + count = I915_READ(GFXEC); + + if (count < dev_priv->last_count2) { + diff = ~0UL - dev_priv->last_count2; + diff += count; + } else { + diff = count - dev_priv->last_count2; + } + + dev_priv->last_count2 = count; + dev_priv->last_time2 = now; + + /* More magic constants... */ + diff = diff * 1181; + diff = div_u64(diff, diffms * 10); + dev_priv->gfx_power = diff; +} + +unsigned long i915_gfx_val(struct drm_i915_private *dev_priv) +{ + unsigned long t, corr, state1, corr2, state2; + u32 pxvid, ext_v; + + pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4)); + pxvid = (pxvid >> 24) & 0x7f; + ext_v = pvid_to_extvid(dev_priv, pxvid); + + state1 = ext_v; + + t = i915_mch_val(dev_priv); + + /* Revel in the empirically derived constants */ + + /* Correction factor in 1/100000 units */ + if (t > 80) + corr = ((t * 2349) + 135940); + else if (t >= 50) + corr = ((t * 964) + 29317); + else /* < 50 */ + corr = ((t * 301) + 1004); + + corr = corr * ((150142 * state1) / 10000 - 78642); + corr /= 100000; + corr2 = (corr * dev_priv->corr); + + state2 = (corr2 * state1) / 10000; + state2 /= 100; /* convert to mW */ + + i915_update_gfx_val(dev_priv); + + return dev_priv->gfx_power + state2; +} + +/* Global for IPS driver to get at the current i915 device */ +static struct drm_i915_private *i915_mch_dev; +/* + * Lock protecting IPS related data structures + * - i915_mch_dev + * - dev_priv->max_delay + * - dev_priv->min_delay + * - dev_priv->fmax + * - dev_priv->gpu_busy + */ +static DEFINE_SPINLOCK(mchdev_lock); + +/** + * i915_read_mch_val - return value for IPS use + * + * Calculate and return a value for the IPS driver to use when deciding whether + * we have thermal and power headroom to increase CPU or GPU power budget. + */ +unsigned long i915_read_mch_val(void) +{ + struct drm_i915_private *dev_priv; + unsigned long chipset_val, graphics_val, ret = 0; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) + goto out_unlock; + dev_priv = i915_mch_dev; + + chipset_val = i915_chipset_val(dev_priv); + graphics_val = i915_gfx_val(dev_priv); + + ret = chipset_val + graphics_val; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_read_mch_val); + +/** + * i915_gpu_raise - raise GPU frequency limit + * + * Raise the limit; IPS indicates we have thermal headroom. + */ +bool i915_gpu_raise(void) +{ + struct drm_i915_private *dev_priv; + bool ret = true; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) { + ret = false; + goto out_unlock; + } + dev_priv = i915_mch_dev; + + if (dev_priv->max_delay > dev_priv->fmax) + dev_priv->max_delay--; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_gpu_raise); + +/** + * i915_gpu_lower - lower GPU frequency limit + * + * IPS indicates we're close to a thermal limit, so throttle back the GPU + * frequency maximum. + */ +bool i915_gpu_lower(void) +{ + struct drm_i915_private *dev_priv; + bool ret = true; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) { + ret = false; + goto out_unlock; + } + dev_priv = i915_mch_dev; + + if (dev_priv->max_delay < dev_priv->min_delay) + dev_priv->max_delay++; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_gpu_lower); + +/** + * i915_gpu_busy - indicate GPU business to IPS + * + * Tell the IPS driver whether or not the GPU is busy. + */ +bool i915_gpu_busy(void) +{ + struct drm_i915_private *dev_priv; + bool ret = false; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) + goto out_unlock; + dev_priv = i915_mch_dev; + + ret = dev_priv->busy; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_gpu_busy); + +/** + * i915_gpu_turbo_disable - disable graphics turbo + * + * Disable graphics turbo by resetting the max frequency and setting the + * current frequency to the default. + */ +bool i915_gpu_turbo_disable(void) +{ + struct drm_i915_private *dev_priv; + bool ret = true; + + spin_lock(&mchdev_lock); + if (!i915_mch_dev) { + ret = false; + goto out_unlock; + } + dev_priv = i915_mch_dev; + + dev_priv->max_delay = dev_priv->fstart; + + if (!ironlake_set_drps(dev_priv->dev, dev_priv->fstart)) + ret = false; + +out_unlock: + spin_unlock(&mchdev_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable); + +/** + * Tells the intel_ips driver that the i915 driver is now loaded, if + * IPS got loaded first. + * + * This awkward dance is so that neither module has to depend on the + * other in order for IPS to do the appropriate communication of + * GPU turbo limits to i915. + */ +static void +ips_ping_for_i915_load(void) +{ + void (*link)(void); + + link = symbol_get(ips_link_to_i915_driver); + if (link) { + link(); + symbol_put(ips_link_to_i915_driver); + } +} + +void intel_gpu_ips_init(struct drm_i915_private *dev_priv) +{ + spin_lock(&mchdev_lock); + i915_mch_dev = dev_priv; + dev_priv->mchdev_lock = &mchdev_lock; + spin_unlock(&mchdev_lock); + + ips_ping_for_i915_load(); +} + +void intel_gpu_ips_teardown(void) +{ + spin_lock(&mchdev_lock); + i915_mch_dev = NULL; + spin_unlock(&mchdev_lock); +} + void intel_init_emon(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; From b3daeaef559d87b974c13a096582c5c70dc11061 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:13 +0200 Subject: [PATCH 65/88] drm/i915: move rps/emon function declarations They're now in intel_pm.c, so group them a bit better. Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index f4f1e8bba535..7c323c5843d2 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -401,10 +401,6 @@ extern void intel_enable_clock_gating(struct drm_device *dev); extern void ironlake_disable_rc6(struct drm_device *dev); extern void ironlake_enable_drps(struct drm_device *dev); extern void ironlake_disable_drps(struct drm_device *dev); -extern void gen6_enable_rps(struct drm_i915_private *dev_priv); -extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv); -extern void gen6_disable_rps(struct drm_device *dev); -extern void intel_init_emon(struct drm_device *dev); extern int intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_i915_gem_object *obj, @@ -466,4 +462,9 @@ extern void intel_update_fbc(struct drm_device *dev); extern void intel_gpu_ips_init(struct drm_i915_private *dev_priv); extern void intel_gpu_ips_teardown(void); +extern void gen6_enable_rps(struct drm_i915_private *dev_priv); +extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv); +extern void gen6_disable_rps(struct drm_device *dev); +extern void intel_init_emon(struct drm_device *dev); + #endif /* __INTEL_DRV_H__ */ From 627965ad3e2f8aa9311f8b238060ddb648268f6b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:14 +0200 Subject: [PATCH 66/88] drm/i915: kill pointless clearing of dev_priv->hws_map We kzalloc dev_priv, and we never use hws_map in intel_ringbuffer.c. Reviewed-by: Chris Wilson Acked-by: Jesse Barnes Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ringbuffer.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index a7d97d17b285..df3a770d60fa 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -890,7 +890,6 @@ i915_dispatch_execbuffer(struct intel_ring_buffer *ring, static void cleanup_status_page(struct intel_ring_buffer *ring) { - drm_i915_private_t *dev_priv = ring->dev->dev_private; struct drm_i915_gem_object *obj; obj = ring->status_page.obj; @@ -901,14 +900,11 @@ static void cleanup_status_page(struct intel_ring_buffer *ring) i915_gem_object_unpin(obj); drm_gem_object_unreference(&obj->base); ring->status_page.obj = NULL; - - memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); } static int init_status_page(struct intel_ring_buffer *ring) { struct drm_device *dev = ring->dev; - drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; int ret; @@ -929,7 +925,6 @@ static int init_status_page(struct intel_ring_buffer *ring) ring->status_page.gfx_addr = obj->gtt_offset; ring->status_page.page_addr = kmap(obj->pages[0]); if (ring->status_page.page_addr == NULL) { - memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); goto err_unpin; } ring->status_page.obj = obj; From 316d388450be37fedcf4b37cf211b2bdc7826bb8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:15 +0200 Subject: [PATCH 67/88] drm/i915: rework legacy GFX HWS handling To get the fun stuff out of the way, the legacy hws is allocated by userspace when the gpu needs a gfx hws. And there's no reference-counting going on, so userspace can simply screw everyone over. At least it's not as horrible as i810, where the ringbuffer is allocated by userspace ... We can't fix this disaster, but we can at least tidy up the code a bit to make things clearer: - Drop the drm ioremap indirection. - Add a new new read_legacy_status_page to paper over the differences between the legacy gfx hws and the physical hws shared with the new ringbuffer code. - Add a pointer in dev_priv->dri1 for the cpu addresses - that one is an iomem remapping as opposed to all other hw status pages. This is just prep work to make sparse happy. Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 29 ++++++++++++++++------------- drivers/gpu/drm/i915/i915_drv.h | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 1b1f8e70df8d..f0c0a7ed90e5 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -67,7 +67,16 @@ LOCK_TEST_WITH_RETURN(dev, file); \ } while (0) -#define READ_HWSP(dev_priv, reg) intel_read_status_page(LP_RING(dev_priv), reg) +static inline u32 +intel_read_legacy_status_page(struct drm_i915_private *dev_priv, int reg) +{ + if (I915_NEED_GFX_HWS(dev_priv->dev)) + return ioread32(dev_priv->dri1.gfx_hws_cpu_addr + reg); + else + return intel_read_status_page(LP_RING(dev_priv), reg); +} + +#define READ_HWSP(dev_priv, reg) intel_read_legacy_status_page(dev_priv, reg) #define READ_BREADCRUMB(dev_priv) READ_HWSP(dev_priv, I915_BREADCRUMB_INDEX) #define I915_BREADCRUMB_INDEX 0x21 @@ -137,7 +146,7 @@ static void i915_free_hws(struct drm_device *dev) if (ring->status_page.gfx_addr) { ring->status_page.gfx_addr = 0; - drm_core_ioremapfree(&dev_priv->hws_map, dev); + iounmap(dev_priv->dri1.gfx_hws_cpu_addr); } /* Need to rewrite hardware status page */ @@ -1073,23 +1082,17 @@ static int i915_set_status_page(struct drm_device *dev, void *data, ring->status_page.gfx_addr = hws->addr & (0x1ffff<<12); - dev_priv->hws_map.offset = dev->agp->base + hws->addr; - dev_priv->hws_map.size = 4*1024; - dev_priv->hws_map.type = 0; - dev_priv->hws_map.flags = 0; - dev_priv->hws_map.mtrr = 0; - - drm_core_ioremap_wc(&dev_priv->hws_map, dev); - if (dev_priv->hws_map.handle == NULL) { + dev_priv->dri1.gfx_hws_cpu_addr = ioremap_wc(dev->agp->base + hws->addr, + 4096); + if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) { i915_dma_cleanup(dev); ring->status_page.gfx_addr = 0; DRM_ERROR("can not ioremap virtual address for" " G33 hw status page\n"); return -ENOMEM; } - ring->status_page.page_addr = - (void __force __iomem *)dev_priv->hws_map.handle; - memset_io(ring->status_page.page_addr, 0, PAGE_SIZE); + + memset_io(dev_priv->dri1.gfx_hws_cpu_addr, 0, PAGE_SIZE); I915_WRITE(HWS_PGA, ring->status_page.gfx_addr); DRM_DEBUG_DRIVER("load hws HWS_PGA with gfx mem 0x%x\n", diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7d48ae3d7678..1f2510931019 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -369,7 +369,6 @@ typedef struct drm_i915_private { drm_dma_handle_t *status_page_dmah; uint32_t counter; - drm_local_map_t hws_map; struct drm_i915_gem_object *pwrctx; struct drm_i915_gem_object *renderctx; @@ -743,6 +742,7 @@ typedef struct drm_i915_private { * here! */ struct { unsigned allow_batchbuffer : 1; + u32 __iomem *gfx_hws_cpu_addr; } dri1; /* Kernel Modesetting */ From 4225d0f219d22440e33a5686bf806356cb25bcf5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:16 +0200 Subject: [PATCH 68/88] drm/i915: fixup __iomem mixups in ringbuffer.c Two things: - ring->virtual start is an __iomem pointer, treat it accordingly. - dev_priv->status_page.page_addr is now always a cpu addr, no pointer casting needed for that. Take the opportunity to remove the unnecessary drm indirection when setting up the ringbuffer iomapping. v2: Add a compiler barrier before reading the hw status page. Acked-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 2 +- drivers/gpu/drm/i915/intel_ringbuffer.c | 39 ++++++++----------------- drivers/gpu/drm/i915/intel_ringbuffer.h | 7 +++-- 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index f0c0a7ed90e5..b5a1a72d3325 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -256,7 +256,7 @@ static int i915_dma_resume(struct drm_device * dev) DRM_DEBUG_DRIVER("%s\n", __func__); - if (ring->map.handle == NULL) { + if (ring->virtual_start == NULL) { DRM_ERROR("can not ioremap virtual address for" " ring buffer\n"); return -ENOMEM; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index df3a770d60fa..38096080a3de 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -977,20 +977,14 @@ static int intel_init_ring_buffer(struct drm_device *dev, if (ret) goto err_unref; - ring->map.size = ring->size; - ring->map.offset = dev->agp->base + obj->gtt_offset; - ring->map.type = 0; - ring->map.flags = 0; - ring->map.mtrr = 0; - - drm_core_ioremap_wc(&ring->map, dev); - if (ring->map.handle == NULL) { + ring->virtual_start = ioremap_wc(dev->agp->base + obj->gtt_offset, + ring->size); + if (ring->virtual_start == NULL) { DRM_ERROR("Failed to map ringbuffer.\n"); ret = -EINVAL; goto err_unpin; } - ring->virtual_start = ring->map.handle; ret = ring->init(ring); if (ret) goto err_unmap; @@ -1006,7 +1000,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, return 0; err_unmap: - drm_core_ioremapfree(&ring->map, dev); + iounmap(ring->virtual_start); err_unpin: i915_gem_object_unpin(obj); err_unref: @@ -1034,7 +1028,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring) I915_WRITE_CTL(ring, 0); - drm_core_ioremapfree(&ring->map, ring->dev); + iounmap(ring->virtual_start); i915_gem_object_unpin(ring->obj); drm_gem_object_unreference(&ring->obj->base); @@ -1048,7 +1042,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring) static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring) { - unsigned int *virt; + uint32_t __iomem *virt; int rem = ring->size - ring->tail; if (ring->space < rem) { @@ -1057,12 +1051,10 @@ static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring) return ret; } - virt = (unsigned int *)(ring->virtual_start + ring->tail); - rem /= 8; - while (rem--) { - *virt++ = MI_NOOP; - *virt++ = MI_NOOP; - } + virt = ring->virtual_start + ring->tail; + rem /= 4; + while (rem--) + iowrite32(MI_NOOP, virt++); ring->tail = 0; ring->space = ring_space(ring); @@ -1427,20 +1419,13 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size) if (IS_I830(ring->dev)) ring->effective_size -= 128; - ring->map.offset = start; - ring->map.size = size; - ring->map.type = 0; - ring->map.flags = 0; - ring->map.mtrr = 0; - - drm_core_ioremap_wc(&ring->map, dev); - if (ring->map.handle == NULL) { + ring->virtual_start = ioremap_wc(start, size); + if (ring->virtual_start == NULL) { DRM_ERROR("can not ioremap virtual address for" " ring buffer\n"); return -ENOMEM; } - ring->virtual_start = (void __force __iomem *)ring->map.handle; return 0; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 7b879926969e..baba75714578 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -2,7 +2,7 @@ #define _INTEL_RINGBUFFER_H_ struct intel_hw_status_page { - u32 __iomem *page_addr; + u32 *page_addr; unsigned int gfx_addr; struct drm_i915_gem_object *obj; }; @@ -115,7 +115,6 @@ struct intel_ring_buffer { u32 outstanding_lazy_request; wait_queue_head_t irq_queue; - drm_local_map_t map; void *private; }; @@ -149,7 +148,9 @@ static inline u32 intel_read_status_page(struct intel_ring_buffer *ring, int reg) { - return ioread32(ring->status_page.page_addr + reg); + /* Ensure that the compiler doesn't optimize away the load. */ + barrier(); + return ring->status_page.page_addr[reg]; } /** From c921aba84ae5bf74b5386b4d2e01a5706ae4b878 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Apr 2012 23:28:17 +0200 Subject: [PATCH 69/88] drm/i915: move pnv|ilk_gem_mem_freq to intel_pm.c Because this is the place where we actually use the results of them. Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 112 ------------------------------- drivers/gpu/drm/i915/intel_pm.c | 113 ++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 112 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index b5a1a72d3325..006ea473b57d 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1375,113 +1375,6 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master) master->driver_priv = NULL; } -static void i915_pineview_get_mem_freq(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - u32 tmp; - - tmp = I915_READ(CLKCFG); - - switch (tmp & CLKCFG_FSB_MASK) { - case CLKCFG_FSB_533: - dev_priv->fsb_freq = 533; /* 133*4 */ - break; - case CLKCFG_FSB_800: - dev_priv->fsb_freq = 800; /* 200*4 */ - break; - case CLKCFG_FSB_667: - dev_priv->fsb_freq = 667; /* 167*4 */ - break; - case CLKCFG_FSB_400: - dev_priv->fsb_freq = 400; /* 100*4 */ - break; - } - - switch (tmp & CLKCFG_MEM_MASK) { - case CLKCFG_MEM_533: - dev_priv->mem_freq = 533; - break; - case CLKCFG_MEM_667: - dev_priv->mem_freq = 667; - break; - case CLKCFG_MEM_800: - dev_priv->mem_freq = 800; - break; - } - - /* detect pineview DDR3 setting */ - tmp = I915_READ(CSHRDDR3CTL); - dev_priv->is_ddr3 = (tmp & CSHRDDR3CTL_DDR3) ? 1 : 0; -} - -static void i915_ironlake_get_mem_freq(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - u16 ddrpll, csipll; - - ddrpll = I915_READ16(DDRMPLL1); - csipll = I915_READ16(CSIPLL0); - - switch (ddrpll & 0xff) { - case 0xc: - dev_priv->mem_freq = 800; - break; - case 0x10: - dev_priv->mem_freq = 1066; - break; - case 0x14: - dev_priv->mem_freq = 1333; - break; - case 0x18: - dev_priv->mem_freq = 1600; - break; - default: - DRM_DEBUG_DRIVER("unknown memory frequency 0x%02x\n", - ddrpll & 0xff); - dev_priv->mem_freq = 0; - break; - } - - dev_priv->r_t = dev_priv->mem_freq; - - switch (csipll & 0x3ff) { - case 0x00c: - dev_priv->fsb_freq = 3200; - break; - case 0x00e: - dev_priv->fsb_freq = 3733; - break; - case 0x010: - dev_priv->fsb_freq = 4266; - break; - case 0x012: - dev_priv->fsb_freq = 4800; - break; - case 0x014: - dev_priv->fsb_freq = 5333; - break; - case 0x016: - dev_priv->fsb_freq = 5866; - break; - case 0x018: - dev_priv->fsb_freq = 6400; - break; - default: - DRM_DEBUG_DRIVER("unknown fsb frequency 0x%04x\n", - csipll & 0x3ff); - dev_priv->fsb_freq = 0; - break; - } - - if (dev_priv->fsb_freq == 3200) { - dev_priv->c_m = 0; - } else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) { - dev_priv->c_m = 1; - } else { - dev_priv->c_m = 2; - } -} - static void i915_mtrr_setup(struct drm_i915_private *dev_priv, unsigned long base, unsigned long size) @@ -1634,11 +1527,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto out_gem_unload; } - if (IS_PINEVIEW(dev)) - i915_pineview_get_mem_freq(dev); - else if (IS_GEN5(dev)) - i915_ironlake_get_mem_freq(dev); - /* On the 945G/GM, the chipset reports the MSI capability on the * integrated graphics even though the support isn't actually there * according to the published specs. It doesn't appear to function diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e0f016c24dce..6ddf80774335 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -526,6 +526,113 @@ void intel_update_fbc(struct drm_device *dev) } } +static void i915_pineview_get_mem_freq(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u32 tmp; + + tmp = I915_READ(CLKCFG); + + switch (tmp & CLKCFG_FSB_MASK) { + case CLKCFG_FSB_533: + dev_priv->fsb_freq = 533; /* 133*4 */ + break; + case CLKCFG_FSB_800: + dev_priv->fsb_freq = 800; /* 200*4 */ + break; + case CLKCFG_FSB_667: + dev_priv->fsb_freq = 667; /* 167*4 */ + break; + case CLKCFG_FSB_400: + dev_priv->fsb_freq = 400; /* 100*4 */ + break; + } + + switch (tmp & CLKCFG_MEM_MASK) { + case CLKCFG_MEM_533: + dev_priv->mem_freq = 533; + break; + case CLKCFG_MEM_667: + dev_priv->mem_freq = 667; + break; + case CLKCFG_MEM_800: + dev_priv->mem_freq = 800; + break; + } + + /* detect pineview DDR3 setting */ + tmp = I915_READ(CSHRDDR3CTL); + dev_priv->is_ddr3 = (tmp & CSHRDDR3CTL_DDR3) ? 1 : 0; +} + +static void i915_ironlake_get_mem_freq(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + u16 ddrpll, csipll; + + ddrpll = I915_READ16(DDRMPLL1); + csipll = I915_READ16(CSIPLL0); + + switch (ddrpll & 0xff) { + case 0xc: + dev_priv->mem_freq = 800; + break; + case 0x10: + dev_priv->mem_freq = 1066; + break; + case 0x14: + dev_priv->mem_freq = 1333; + break; + case 0x18: + dev_priv->mem_freq = 1600; + break; + default: + DRM_DEBUG_DRIVER("unknown memory frequency 0x%02x\n", + ddrpll & 0xff); + dev_priv->mem_freq = 0; + break; + } + + dev_priv->r_t = dev_priv->mem_freq; + + switch (csipll & 0x3ff) { + case 0x00c: + dev_priv->fsb_freq = 3200; + break; + case 0x00e: + dev_priv->fsb_freq = 3733; + break; + case 0x010: + dev_priv->fsb_freq = 4266; + break; + case 0x012: + dev_priv->fsb_freq = 4800; + break; + case 0x014: + dev_priv->fsb_freq = 5333; + break; + case 0x016: + dev_priv->fsb_freq = 5866; + break; + case 0x018: + dev_priv->fsb_freq = 6400; + break; + default: + DRM_DEBUG_DRIVER("unknown fsb frequency 0x%04x\n", + csipll & 0x3ff); + dev_priv->fsb_freq = 0; + break; + } + + if (dev_priv->fsb_freq == 3200) { + dev_priv->c_m = 0; + } else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) { + dev_priv->c_m = 1; + } else { + dev_priv->c_m = 2; + } +} + static const struct cxsr_latency cxsr_latency_table[] = { {1, 0, 800, 400, 3382, 33382, 3983, 33983}, /* DDR2-400 SC */ {1, 0, 800, 667, 3354, 33354, 3807, 33807}, /* DDR2-667 SC */ @@ -3440,6 +3547,12 @@ void intel_init_pm(struct drm_device *dev) /* 855GM needs testing */ } + /* For cxsr */ + if (IS_PINEVIEW(dev)) + i915_pineview_get_mem_freq(dev); + else if (IS_GEN5(dev)) + i915_ironlake_get_mem_freq(dev); + /* For FIFO watermark updates */ if (HAS_PCH_SPLIT(dev)) { dev_priv->display.force_wake_get = __gen6_gt_force_wake_get; From b4aca0106c466b5a0329318203f65bac2d91b682 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Wed, 25 Apr 2012 20:50:12 -0700 Subject: [PATCH 70/88] drm/i915: extract some common olr+wedge code The new wait_rendering ioctl also needs to check for an oustanding lazy request, and we already duplicate that logic at three places. So extract it. While at it, also extract the code to check the gpu wedging state to improve code flow. v2: Don't use seqno as an outparam (Chris) v3 by danvet: Kill stale comment and pimp commit message Signed-off-by: Ben Widawsky Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 120 +++++++++++++++++--------------- 1 file changed, 63 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 131eadb84adb..4ab57fd752dc 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1819,6 +1819,57 @@ i915_gem_retire_work_handler(struct work_struct *work) mutex_unlock(&dev->struct_mutex); } +static int +i915_gem_check_wedge(struct drm_i915_private *dev_priv) +{ + BUG_ON(!mutex_is_locked(&dev_priv->dev->struct_mutex)); + + if (atomic_read(&dev_priv->mm.wedged)) { + struct completion *x = &dev_priv->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); + + return recovery_complete ? -EIO : -EAGAIN; + } + + return 0; +} + +/* + * Compare seqno against outstanding lazy request. Emit a request if they are + * equal. + */ +static int +i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) +{ + int ret = 0; + + BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex)); + + if (seqno == ring->outstanding_lazy_request) { + struct drm_i915_gem_request *request; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) + return -ENOMEM; + + ret = i915_add_request(ring, NULL, request); + if (ret) { + kfree(request); + return ret; + } + + BUG_ON(seqno != request->seqno); + } + + return ret; +} + static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, bool interruptible) { @@ -1862,34 +1913,13 @@ i915_wait_request(struct intel_ring_buffer *ring, BUG_ON(seqno == 0); - if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->error_completion; - bool recovery_complete; - unsigned long flags; + ret = i915_gem_check_wedge(dev_priv); + if (ret) + return ret; - /* 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); - - return recovery_complete ? -EIO : -EAGAIN; - } - - if (seqno == ring->outstanding_lazy_request) { - struct drm_i915_gem_request *request; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request == NULL) - return -ENOMEM; - - ret = i915_add_request(ring, NULL, request); - if (ret) { - kfree(request); - return ret; - } - - seqno = request->seqno; - } + ret = i915_gem_check_olr(ring, seqno); + if (ret) + return ret; ret = __wait_seqno(ring, seqno, dev_priv->mm.interruptible); if (atomic_read(&dev_priv->mm.wedged)) @@ -1957,22 +1987,9 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj, if (seqno <= from->sync_seqno[idx]) return 0; - if (seqno == from->outstanding_lazy_request) { - struct drm_i915_gem_request *request; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request == NULL) - return -ENOMEM; - - ret = i915_add_request(from, NULL, request); - if (ret) { - kfree(request); - return ret; - } - - seqno = request->seqno; - } - + ret = i915_gem_check_olr(obj->ring, seqno); + if (ret) + return ret; ret = to->sync_to(to, from, seqno); if (!ret) @@ -3160,20 +3177,9 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { ret = i915_gem_flush_ring(obj->ring, 0, obj->base.write_domain); - } else if (obj->ring->outstanding_lazy_request == - obj->last_rendering_seqno) { - struct drm_i915_gem_request *request; - - /* This ring is not being cleared by active usage, - * so emit a request to do so. - */ - request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request) { - ret = i915_add_request(obj->ring, NULL, request); - if (ret) - kfree(request); - } else - ret = -ENOMEM; + } else { + ret = i915_gem_check_olr(obj->ring, + obj->last_rendering_seqno); } /* Update the active list for the hardware's current position. From 555304f47d5694b6a8f671778143acf4a0bf5c5c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 2 May 2012 11:06:23 +0200 Subject: [PATCH 71/88] drm/i915: rip out intel_dpio_write again Unfortunately it looks like further vlv patches are still stalled due to fried hw, and too many people are a bit annoyed about the unused function warning. So let's just rip it out, we can easily put it back in again. Acked-by: Jesse Barnes Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2617ab580dff..059e4684efd1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -384,28 +384,6 @@ u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) return val; } -static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, - u32 val) -{ - unsigned long flags; - - spin_lock_irqsave(&dev_priv->dpio_lock, flags); - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { - DRM_ERROR("DPIO idle wait timed out\n"); - goto out_unlock; - } - - I915_WRITE(DPIO_DATA, val); - I915_WRITE(DPIO_REG, reg); - I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_WRITE | DPIO_PORTID | - DPIO_BYTE); - if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) - DRM_ERROR("DPIO write wait timed out\n"); - -out_unlock: - spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); -} - static void vlv_init_dpio(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; From e04c735029bc133466b89265a0745a226d0eac23 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 2 May 2012 20:43:56 +0100 Subject: [PATCH 72/88] drm/i915: Wait for the clocks to stabilise before updating PLLs When initialising the PLL registers we may have to clear existing state from the BIOS - that is the PLL may already be enabled. So we need to disable it, wait for the clocks to settle and then rewrite it. The issue came to light when Ben tested commit 88ca4bb7974277793e602d88739d4e8f56b89e64 Author: Jesse Barnes Date: Fri Apr 20 17:11:53 2012 +0100 drm/i915: manage PCH PLLs separately from pipes and found that booting into a VGA monitor was no longer working. Closer inspection suggests that it was a pre-existing bug now being hit by the rearranged code. Perhaps Ben was not even the first person to stumble upon this bug, https://bugs.freedesktop.org/show_bug.cgi?id=37029. Signed-off-by: Chris Wilson Reported-and-Tested-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 059e4684efd1..6ab195c0097f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2708,11 +2708,14 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3 DRM_DEBUG_DRIVER("using pll %d for pipe %d\n", i, intel_crtc->pipe); prepare: /* separate function? */ DRM_DEBUG_DRIVER("switching PLL %x off\n", pll->pll_reg); - I915_WRITE(pll->fp0_reg, fp); - I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); + /* Wait for the clocks to stabilize before rewriting the regs */ + I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); POSTING_READ(pll->pll_reg); udelay(150); + + I915_WRITE(pll->fp0_reg, fp); + I915_WRITE(pll->pll_reg, dpll & ~DPLL_VCO_ENABLE); pll->on = false; return pll; } From 0aa534df05b9f44795e2cac7383d5c68fdd11440 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 13 Apr 2012 16:31:40 -0300 Subject: [PATCH 73/88] drm/i915: rename AVI InfoFrame field 'PR' to 'YQ_CN_PR' To keep the consistency with the other fields. Signed-off-by: Paulo Zanoni Reviewed-by: Adam Jackson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 7c323c5843d2..3205a774ec4b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -267,8 +267,8 @@ struct dip_infoframe { uint8_t ITC_EC_Q_SC; /* PB4 - VIC 6:0 */ uint8_t VIC; - /* PB5 - PR 3:0 */ - uint8_t PR; + /* PB5 - YQ 7:6, CN 5:4, PR 3:0 */ + uint8_t YQ_CN_PR; /* PB6 to PB13 */ uint16_t top_bar_end; uint16_t bottom_bar_start; From c846b6194dbe02d9f4fc8c0471e799babc1c3f47 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 13 Apr 2012 16:31:41 -0300 Subject: [PATCH 74/88] drm/i915: make DBLCLK modes work They require an AVI InfoFrame with a proper Pixel Repetition field. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=45729 Signed-off-by: Paulo Zanoni Reviewed-by: Adam Jackson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 2 ++ drivers/gpu/drm/i915/intel_hdmi.c | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 3205a774ec4b..d9ffe381c723 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -234,6 +234,8 @@ struct cxsr_latency { #define DIP_TYPE_AVI 0x82 #define DIP_VERSION_AVI 0x2 #define DIP_LEN_AVI 13 +#define DIP_AVI_PR_1 0 +#define DIP_AVI_PR_2 1 #define DIP_TYPE_SPD 0x83 #define DIP_VERSION_SPD 0x1 diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 7de2d3b85b32..8d2501704182 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -220,7 +220,8 @@ static void intel_set_infoframe(struct drm_encoder *encoder, intel_hdmi->write_infoframe(encoder, frame); } -static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) +static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode) { struct dip_infoframe avi_if = { .type = DIP_TYPE_AVI, @@ -228,6 +229,9 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) .len = DIP_LEN_AVI, }; + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK) + avi_if.body.avi.YQ_CN_PR |= DIP_AVI_PR_2; + intel_set_infoframe(encoder, &avi_if); } @@ -290,7 +294,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, I915_WRITE(intel_hdmi->sdvox_reg, sdvox); POSTING_READ(intel_hdmi->sdvox_reg); - intel_hdmi_set_avi_infoframe(encoder); + intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); intel_hdmi_set_spd_infoframe(encoder); } From 9104183dad6314c55344d65738cd719b909a3e0a Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 26 Apr 2012 11:28:42 +0100 Subject: [PATCH 75/88] drm/i915: Periodically sanity check power management Every time we use the device after a period of idleness, check that the power management setup is still sane. This is to workaround a bug whereby it seems that we begin suppressing power management interrupts, preventing SandyBridge+ from going into turbo mode. This patch does have a side-effect. It removes the mark-busy for just moving the cursor - we don't want to increase the render clock just for the sprite, though we may want to bump the display frequency. I'd argue that we do not, and certainly don't want to take the struct_mutex here due to the large latencies that introduces. References: https://bugs.freedesktop.org/show_bug.cgi?id=44006 Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_display.c | 8 +++--- drivers/gpu/drm/i915/intel_drv.h | 2 ++ drivers/gpu/drm/i915/intel_pm.c | 37 ++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1f2510931019..00a20e985d28 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -240,6 +240,7 @@ struct drm_i915_display_funcs { void (*update_wm)(struct drm_device *dev); void (*update_sprite_wm)(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size); + void (*sanitize_pm)(struct drm_device *dev); int (*crtc_mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6ab195c0097f..ba04447926ee 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4834,9 +4834,6 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, else i9xx_update_cursor(crtc, base); } - - if (visible) - intel_mark_busy(dev, to_intel_framebuffer(crtc->fb)->obj); } static int intel_crtc_cursor_set(struct drm_crtc *crtc, @@ -5530,9 +5527,10 @@ void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj) if (!drm_core_check_feature(dev, DRIVER_MODESET)) return; - if (!dev_priv->busy) + if (!dev_priv->busy) { + intel_sanitize_pm(dev); dev_priv->busy = true; - else + } else mod_timer(&dev_priv->idle_timer, jiffies + msecs_to_jiffies(GPU_IDLE_TIMEOUT)); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d9ffe381c723..cfec4842e0c4 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -344,6 +344,8 @@ extern int intel_plane_init(struct drm_device *dev, enum pipe pipe); extern void intel_flush_display_plane(struct drm_i915_private *dev_priv, enum plane plane); +void intel_sanitize_pm(struct drm_device *dev); + /* intel_panel.c */ extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 6ddf80774335..43892341079a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3525,6 +3525,41 @@ void intel_init_clock_gating(struct drm_device *dev) dev_priv->display.init_pch_clock_gating(dev); } +static void gen6_sanitize_pm(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 limits, delay, old; + + gen6_gt_force_wake_get(dev_priv); + + old = limits = I915_READ(GEN6_RP_INTERRUPT_LIMITS); + /* Make sure we continue to get interrupts + * until we hit the minimum or maximum frequencies. + */ + limits &= ~(0x3f << 16 | 0x3f << 24); + delay = dev_priv->cur_delay; + if (delay < dev_priv->max_delay) + limits |= (dev_priv->max_delay & 0x3f) << 24; + if (delay > dev_priv->min_delay) + limits |= (dev_priv->min_delay & 0x3f) << 16; + + if (old != limits) { + DRM_ERROR("Power management discrepancy: GEN6_RP_INTERRUPT_LIMITS expected %08x, was %08x\n", + limits, old); + I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); + } + + gen6_gt_force_wake_put(dev_priv); +} + +void intel_sanitize_pm(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->display.sanitize_pm) + dev_priv->display.sanitize_pm(dev); +} + /* Set up chip specific power management-related functions */ void intel_init_pm(struct drm_device *dev) { @@ -3607,6 +3642,7 @@ void intel_init_pm(struct drm_device *dev) dev_priv->display.update_wm = NULL; } dev_priv->display.init_clock_gating = gen6_init_clock_gating; + dev_priv->display.sanitize_pm = gen6_sanitize_pm; } else if (IS_IVYBRIDGE(dev)) { /* FIXME: detect B0+ stepping and use auto training */ if (SNB_READ_WM0_LATENCY()) { @@ -3618,6 +3654,7 @@ void intel_init_pm(struct drm_device *dev) dev_priv->display.update_wm = NULL; } dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; + dev_priv->display.sanitize_pm = gen6_sanitize_pm; } else dev_priv->display.update_wm = NULL; } else if (IS_VALLEYVIEW(dev)) { From f7bacf195e100a028eaa26c4b5f5e07665d51cee Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 3 May 2012 15:51:58 +0200 Subject: [PATCH 76/88] drm/i915: rip out unnecessary calls to drm_mode_set_crtcinfo Our handling of the crtc timing computation has been nicely cargo-culted with calls to drm_mode_set_crtcinfo sprinkled all over the place. But with commit f9bef081c3c3f77bec54454872e98d3ec635756f Author: Daniel Vetter Date: Sun Apr 15 19:53:19 2012 +0200 drm/i915: don't clobber the special upscaling lvds timings and commit ca9bfa7eed20ea34e862804e62aae10eb159edbb Author: Daniel Vetter Date: Sat Jan 28 14:49:20 2012 +0100 drm/i915: fixup interlaced vertical timings confusion, part 1 we now only set the crtc timing fields in the encoder->mode_fixup (lvds only) and in crtc->mode_fixup (for everyone else). And since commit 75c13993db592343bda1fd62f2555fea037d56bd Author: Daniel Vetter Date: Sat Jan 28 23:48:46 2012 +0100 drm/i915: fixup overlay checks for interlaced modes the only places we actually need the crtc timings is in the mode_set function. I guess the idea of the drm core is that every time it creates a drm mode, it also sets the timings. But afaics it never uses them, safe for the precise vblank timestamp code (but that can only run on active modes, i.e. after our mode_fixup functions have been called). The problem is that drm core always sets CRTC_INTERLACE_HALVE_V, so the timings are pretty much bogus for us anyway (at least with interlaced support). So I guess it's the drivers job that every active modes needs to have crtc timings that suits it, and with these patches we should have that. drm core doesn't seem to care about modes that just get passed around. Hence we can now safely rip out all the remaining calls to set_crtcinfo left in the driver and clean up this confusion. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 1 - drivers/gpu/drm/i915/intel_overlay.c | 2 +- drivers/gpu/drm/i915/intel_sdvo.c | 3 --- drivers/gpu/drm/i915/intel_tv.c | 1 - 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ba04447926ee..e20f8042fddd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5357,7 +5357,6 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; drm_mode_set_name(mode); - drm_mode_set_crtcinfo(mode, 0); return mode; } diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 0bfab0bf60f4..458743da3774 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -263,7 +263,7 @@ i830_activate_pipe_a(struct drm_device *dev) DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n"); mode = drm_mode_duplicate(dev, &vesa_640x480); - drm_mode_set_crtcinfo(mode, 0); + if (!drm_crtc_helper_set_mode(&crtc->base, mode, crtc->base.x, crtc->base.y, crtc->base.fb)) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index c330efd59a0e..a3ccdccef0f9 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1576,9 +1576,6 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector) intel_sdvo->sdvo_lvds_fixed_mode = drm_mode_duplicate(connector->dev, newmode); - drm_mode_set_crtcinfo(intel_sdvo->sdvo_lvds_fixed_mode, - 0); - intel_sdvo->is_lvds = true; break; } diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 2e626b861cd8..3346612d2953 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1249,7 +1249,6 @@ intel_tv_detect(struct drm_connector *connector, bool force) int type; mode = reported_modes[0]; - drm_mode_set_crtcinfo(&mode, 0); if (force) { struct intel_load_detect_pipe tmp; From c6ebd4c015a80991fe149a6a003ae4c83386e00e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 24 Apr 2012 18:27:57 +0200 Subject: [PATCH 77/88] drm/i915: use mode values consistently when converting to sdvo dtd The drm_mode->dtd conversion used the crtc timings, whereas the dtd->drm_mod did not set these. Use the standard mode information, not the crtc timings, in both cases to make these two functions proper inverses of each another. Note that this also kills the risk that we handle interlaced timings inconsistently because the drm core uses half-frames for crtc timings, whereas we need full frames. But interlaced support is pretty decently broken anyway for sdvo encoders, so no big deal. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_sdvo.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index a3ccdccef0f9..9b3a5f999ad7 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -746,18 +746,18 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd, uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len; uint16_t h_sync_offset, v_sync_offset; - width = mode->crtc_hdisplay; - height = mode->crtc_vdisplay; + width = mode->hdisplay; + height = mode->vdisplay; /* do some mode translations */ - h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start; - h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; + h_blank_len = mode->htotal - mode->hdisplay; + h_sync_len = mode->hsync_end - mode->hsync_start; - v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start; - v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start; + v_blank_len = mode->vtotal - mode->vdisplay; + v_sync_len = mode->vsync_end - mode->vsync_start; - h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; - v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; + h_sync_offset = mode->hsync_start - mode->hdisplay; + v_sync_offset = mode->vsync_start - mode->vdisplay; dtd->part1.clock = mode->clock / 10; dtd->part1.h_active = width & 0xff; From e5eb3d63c6182d3f21fbfc836ded748d49d521f9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 3 May 2012 14:48:16 +0200 Subject: [PATCH 78/88] drm/i915: add interface to simulate gpu hangs gpu reset is a very important piece of our infrastructure. Unfortunately we only really it test by actually hanging the gpu, which often has bad side-effects for the entire system. And the gpu hang handling code is one of the rather complicated pieces of code we have, consisting of - hang detection - error capture - actual gpu reset - reset of all the gem bookkeeping - reinitialition of the entire gpu This patch adds a debugfs to selectively stopping rings by ceasing to update the hw tail pointer, which will result in the gpu no longer updating it's head pointer and eventually to the hangcheck firing. This way we can exercise the gpu hang code under controlled conditions without a dying gpu taking down the entire systems. Patch motivated by me forgetting to properly reinitialize ppgtt after a gpu reset. Usage: echo $((1 << $ringnum)) > i915_ring_stop # stops one ring echo 0xffffffff > i915_ring_stop # stops all, future-proof version then run whatever testload is desired. i915_ring_stop automatically resets after a gpu hang is detected to avoid hanging the gpu to fast and declaring it wedged. v2: Incorporate feedback from Chris Wilson. v3: Add the missing cleanup. v4: Fix up inconsistent size of ring_stop_read vs _write, noticed by Eugeni Dodonov. Reviewed-by: Chris Wilson Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 65 +++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_drv.c | 2 + drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/intel_ringbuffer.c | 4 ++ 4 files changed, 73 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index ae68ac1c488e..192b27e9046c 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1585,6 +1585,64 @@ static const struct file_operations i915_wedged_fops = { .llseek = default_llseek, }; +static ssize_t +i915_ring_stop_read(struct file *filp, + char __user *ubuf, + size_t max, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + drm_i915_private_t *dev_priv = dev->dev_private; + char buf[20]; + int len; + + len = snprintf(buf, sizeof(buf), + "0x%08x\n", dev_priv->stop_rings); + + if (len > sizeof(buf)) + len = sizeof(buf); + + return simple_read_from_buffer(ubuf, max, ppos, buf, len); +} + +static ssize_t +i915_ring_stop_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct drm_device *dev = filp->private_data; + struct drm_i915_private *dev_priv = dev->dev_private; + char buf[20]; + int val = 0; + + if (cnt > 0) { + if (cnt > sizeof(buf) - 1) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + buf[cnt] = 0; + + val = simple_strtoul(buf, NULL, 0); + } + + DRM_DEBUG_DRIVER("Stopping rings 0x%08x\n", val); + + mutex_lock(&dev->struct_mutex); + dev_priv->stop_rings = val; + mutex_unlock(&dev->struct_mutex); + + return cnt; +} + +static const struct file_operations i915_ring_stop_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = i915_ring_stop_read, + .write = i915_ring_stop_write, + .llseek = default_llseek, +}; static ssize_t i915_max_freq_read(struct file *filp, char __user *ubuf, @@ -1885,6 +1943,11 @@ int i915_debugfs_init(struct drm_minor *minor) &i915_cache_sharing_fops); if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_ring_stop", + &i915_ring_stop_fops); + if (ret) + return ret; return drm_debugfs_create_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, @@ -1903,6 +1966,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor) 1, minor); drm_debugfs_remove_files((struct drm_info_list *) &i915_cache_sharing_fops, 1, minor); + drm_debugfs_remove_files((struct drm_info_list *) &i915_ring_stop_fops, + 1, minor); } #endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 8a98f9a16418..90a84f9de8e9 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -800,6 +800,8 @@ int i915_reset(struct drm_device *dev, u8 flags) if (!mutex_trylock(&dev->struct_mutex)) return -EBUSY; + dev_priv->stop_rings = 0; + i915_gem_reset(dev); ret = -ENODEV; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 00a20e985d28..090ec20293f2 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -412,6 +412,8 @@ typedef struct drm_i915_private { uint32_t last_instdone; uint32_t last_instdone1; + unsigned int stop_rings; + unsigned long cfb_size; unsigned int cfb_fb; enum plane cfb_plane; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 38096080a3de..3aabe8dfe5c5 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1210,7 +1210,11 @@ int intel_ring_begin(struct intel_ring_buffer *ring, 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)) + return; ring->write_tail(ring, ring->tail); } From 742cbee8049f434d8ae7908ee56fe4cad3db2658 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 27 Apr 2012 15:17:39 +0200 Subject: [PATCH 79/88] drm/i915: rework dev->first_error locking - reduce the irq disabled section, even for a debugfs file this was way too long. - always disable irqs when taking the lock. v2: Thou shalt not mistake locking for reference counting, so: - reference count the error_state to protect from concurent freeeing. This will be only really used in the next patch. Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 13 ++++++++----- drivers/gpu/drm/i915/i915_drv.h | 5 +++++ drivers/gpu/drm/i915/i915_irq.c | 12 +++++++----- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 192b27e9046c..72c73de35065 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -698,12 +698,16 @@ static int i915_error_state(struct seq_file *m, void *unused) int i, j, page, offset, elt; spin_lock_irqsave(&dev_priv->error_lock, flags); - if (!dev_priv->first_error) { + error = dev_priv->first_error; + if (error) + kref_get(&error->ref); + spin_unlock_irqrestore(&dev_priv->error_lock, flags); + + if (!error) { seq_printf(m, "no error state collected\n"); - goto out; + return 0; } - error = dev_priv->first_error; seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec, error->time.tv_usec); @@ -786,8 +790,7 @@ static int i915_error_state(struct seq_file *m, void *unused) if (error->display) intel_display_print_error_state(m, dev, error->display); -out: - spin_unlock_irqrestore(&dev_priv->error_lock, flags); + kref_put(&error->ref, i915_error_state_free); return 0; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 090ec20293f2..c070a2b3f049 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -39,6 +39,7 @@ #include #include #include +#include /* General customization: */ @@ -171,6 +172,7 @@ struct sdvo_device_mapping { struct intel_display_error_state; struct drm_i915_error_state { + struct kref ref; u32 eir; u32 pgtbl_er; u32 ier; @@ -465,6 +467,7 @@ 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; @@ -1163,6 +1166,8 @@ void i915_handle_error(struct drm_device *dev, bool wedged); extern void intel_irq_init(struct drm_device *dev); +void i915_error_state_free(struct kref *error_ref); + void i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 49e5d3ec5559..8334550fccf4 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -850,10 +850,11 @@ i915_error_object_free(struct drm_i915_error_object *obj) kfree(obj); } -static void -i915_error_state_free(struct drm_device *dev, - struct drm_i915_error_state *error) +void +i915_error_state_free(struct kref *error_ref) { + struct drm_i915_error_state *error = container_of(error_ref, + typeof(*error), ref); int i; for (i = 0; i < ARRAY_SIZE(error->ring); i++) { @@ -1102,6 +1103,7 @@ static void i915_capture_error_state(struct drm_device *dev) DRM_INFO("capturing error event; look for more information in /debug/dri/%d/i915_error_state\n", dev->primary->index); + kref_init(&error->ref); error->eir = I915_READ(EIR); error->pgtbl_er = I915_READ(PGTBL_ER); @@ -1173,7 +1175,7 @@ static void i915_capture_error_state(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->error_lock, flags); if (error) - i915_error_state_free(dev, error); + i915_error_state_free(&error->ref); } void i915_destroy_error_state(struct drm_device *dev) @@ -1188,7 +1190,7 @@ void i915_destroy_error_state(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->error_lock, flags); if (error) - i915_error_state_free(dev, error); + kref_put(&error->ref, i915_error_state_free); } #else #define i915_capture_error_state(x) From d54423037f141518950f324af88a551a82449496 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 27 Apr 2012 15:17:40 +0200 Subject: [PATCH 80/88] drm/i915: allow the existing error_state to be destroyed ... by writing (anything) to i915_error_state. This way we can simulate a bunch of gpu hangs and run the error_state capture code every time (without the need to reload the module). To make that happen we need to abandon the simple seq_file wrappers provided by the drm core. While at it put the new error_state refcounting to some good use and associated the error_state to the debugfs when opening the file. Otherwise the error_state could change while someone is reading it. This should help greatly when we finally get around to split up the giant single seq_file block that the error_state file currently is into smaller parts. v2: Actually squash all the fixes into the patch ... Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 90 ++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 72c73de35065..950f72a0d729 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -688,21 +688,19 @@ static void i915_ring_error_state(struct seq_file *m, seq_printf(m, " ring->tail: 0x%08x\n", error->cpu_ring_tail[ring]); } +struct i915_error_state_file_priv { + struct drm_device *dev; + struct drm_i915_error_state *error; +}; + static int i915_error_state(struct seq_file *m, void *unused) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; + struct i915_error_state_file_priv *error_priv = m->private; + struct drm_device *dev = error_priv->dev; drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_i915_error_state *error; - unsigned long flags; + struct drm_i915_error_state *error = error_priv->error; int i, j, page, offset, elt; - spin_lock_irqsave(&dev_priv->error_lock, flags); - error = dev_priv->first_error; - if (error) - kref_get(&error->ref); - spin_unlock_irqrestore(&dev_priv->error_lock, flags); - if (!error) { seq_printf(m, "no error state collected\n"); return 0; @@ -790,11 +788,71 @@ static int i915_error_state(struct seq_file *m, void *unused) if (error->display) intel_display_print_error_state(m, dev, error->display); - kref_put(&error->ref, i915_error_state_free); - return 0; } +static ssize_t +i915_error_state_write(struct file *filp, + const char __user *ubuf, + size_t cnt, + loff_t *ppos) +{ + struct seq_file *m = filp->private_data; + struct i915_error_state_file_priv *error_priv = m->private; + struct drm_device *dev = error_priv->dev; + + DRM_DEBUG_DRIVER("Resetting error state\n"); + + mutex_lock(&dev->struct_mutex); + i915_destroy_error_state(dev); + mutex_unlock(&dev->struct_mutex); + + return cnt; +} + +static int i915_error_state_open(struct inode *inode, struct file *file) +{ + struct drm_device *dev = inode->i_private; + drm_i915_private_t *dev_priv = dev->dev_private; + struct i915_error_state_file_priv *error_priv; + unsigned long flags; + + error_priv = kzalloc(sizeof(*error_priv), GFP_KERNEL); + if (!error_priv) + return -ENOMEM; + + error_priv->dev = dev; + + spin_lock_irqsave(&dev_priv->error_lock, flags); + error_priv->error = dev_priv->first_error; + if (error_priv->error) + kref_get(&error_priv->error->ref); + spin_unlock_irqrestore(&dev_priv->error_lock, flags); + + return single_open(file, i915_error_state, error_priv); +} + +static int i915_error_state_release(struct inode *inode, struct file *file) +{ + struct seq_file *m = file->private_data; + struct i915_error_state_file_priv *error_priv = m->private; + + if (error_priv->error) + kref_put(&error_priv->error->ref, i915_error_state_free); + kfree(error_priv); + + return single_release(inode, file); +} + +static const struct file_operations i915_error_state_fops = { + .owner = THIS_MODULE, + .open = i915_error_state_open, + .read = seq_read, + .write = i915_error_state_write, + .llseek = default_llseek, + .release = i915_error_state_release, +}; + static int i915_rstdby_delays(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -1646,6 +1704,7 @@ static const struct file_operations i915_ring_stop_fops = { .write = i915_ring_stop_write, .llseek = default_llseek, }; + static ssize_t i915_max_freq_read(struct file *filp, char __user *ubuf, @@ -1900,7 +1959,6 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_gem_hws", i915_hws_info, 0, (void *)RCS}, {"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS}, {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS}, - {"i915_error_state", i915_error_state, 0}, {"i915_rstdby_delays", i915_rstdby_delays, 0}, {"i915_cur_delayinfo", i915_cur_delayinfo, 0}, {"i915_delayfreq_table", i915_delayfreq_table, 0}, @@ -1952,6 +2010,12 @@ int i915_debugfs_init(struct drm_minor *minor) if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, + "i915_error_state", + &i915_error_state_fops); + if (ret) + return ret; + return drm_debugfs_create_files(i915_debugfs_list, I915_DEBUGFS_ENTRIES, minor->debugfs_root, minor); From bcbc324a2136849a6264e775a465921856286e06 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 27 Apr 2012 15:17:41 +0200 Subject: [PATCH 81/88] drm/i915: simplify i915_reset a bit - need_display is always true, scrap it. - don't reacquire the mutex to do nothing after having restored the gem state. Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 90a84f9de8e9..3ffa9e7c0a8c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -787,11 +787,6 @@ static int gen6_do_reset(struct drm_device *dev, u8 flags) int i915_reset(struct drm_device *dev, u8 flags) { drm_i915_private_t *dev_priv = dev->dev_private; - /* - * We really should only reset the display subsystem if we actually - * need to - */ - bool need_display = true; int ret; if (!i915_try_reset) @@ -865,22 +860,18 @@ int i915_reset(struct drm_device *dev, u8 flags) drm_irq_uninstall(dev); drm_mode_config_reset(dev); drm_irq_install(dev); - - mutex_lock(&dev->struct_mutex); + } else { + mutex_unlock(&dev->struct_mutex); } - mutex_unlock(&dev->struct_mutex); - /* * Perform a full modeset as on later generations, e.g. Ironlake, we may * need to retrain the display link and cannot just restore the register * values. */ - if (need_display) { - mutex_lock(&dev->mode_config.mutex); - drm_helper_resume_force_mode(dev); - mutex_unlock(&dev->mode_config.mutex); - } + mutex_lock(&dev->mode_config.mutex); + drm_helper_resume_force_mode(dev); + mutex_unlock(&dev->mode_config.mutex); return 0; } From 350d2706209cabb187a86508ecd7763237f938c8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 27 Apr 2012 15:17:42 +0200 Subject: [PATCH 82/88] drm/i915: extract intel_gpu_reset Slightly cleans up the code and could be useful for e.g. Ben Widawsky's hw context patches. v2: New colours! Cc: Ben Widawsky Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 43 +++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 3ffa9e7c0a8c..27630842bdcc 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -768,6 +768,29 @@ static int gen6_do_reset(struct drm_device *dev, u8 flags) return ret; } +static int intel_gpu_reset(struct drm_device *dev, u8 flags) +{ + int ret = -ENODEV; + + switch (INTEL_INFO(dev)->gen) { + case 7: + case 6: + ret = gen6_do_reset(dev, flags); + break; + case 5: + ret = ironlake_do_reset(dev, flags); + break; + case 4: + ret = i965_do_reset(dev, flags); + break; + case 2: + ret = i8xx_do_reset(dev, flags); + break; + } + + return ret; +} + /** * i915_reset - reset chip after a hang * @dev: drm device to reset @@ -800,23 +823,11 @@ int i915_reset(struct drm_device *dev, u8 flags) i915_gem_reset(dev); ret = -ENODEV; - if (get_seconds() - dev_priv->last_gpu_reset < 5) { + if (get_seconds() - dev_priv->last_gpu_reset < 5) DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); - } else switch (INTEL_INFO(dev)->gen) { - case 7: - case 6: - ret = gen6_do_reset(dev, flags); - break; - case 5: - ret = ironlake_do_reset(dev, flags); - break; - case 4: - ret = i965_do_reset(dev, flags); - break; - case 2: - ret = i8xx_do_reset(dev, flags); - break; - } + else + ret = intel_gpu_reset(dev, flags); + dev_priv->last_gpu_reset = get_seconds(); if (ret) { DRM_ERROR("Failed to reset chip.\n"); From 2b9dc9a27b5869f082b08306da14f7c232693954 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 27 Apr 2012 15:17:43 +0200 Subject: [PATCH 83/88] drm/i915: make gpu hangman more resilient - reset the stop_rings infrastructure while resetting the hw to avoid angering the hangcheck right away (and potentially declaring the gpu permanently wedged). - ignore reset failures when hanging due to the hangman - we don't have reset code for all generations. v2: Ensure that we only ignore reset failures when the hw reset is not implemented and not when it failed. Reviewed-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 27630842bdcc..dbe0f88ad3d0 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -770,6 +770,7 @@ static int gen6_do_reset(struct drm_device *dev, u8 flags) static int intel_gpu_reset(struct drm_device *dev, u8 flags) { + struct drm_i915_private *dev_priv = dev->dev_private; int ret = -ENODEV; switch (INTEL_INFO(dev)->gen) { @@ -788,6 +789,17 @@ static int intel_gpu_reset(struct drm_device *dev, u8 flags) break; } + /* Also reset the gpu hangman. */ + if (dev_priv->stop_rings) { + DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n"); + dev_priv->stop_rings = 0; + if (ret == -ENODEV) { + DRM_ERROR("Reset not implemented, but ignoring " + "error for simulated gpu hangs\n"); + ret = 0; + } + } + return ret; } From d4b8bb2ac1254b98631909251f299f7789b5bed5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 27 Apr 2012 15:17:44 +0200 Subject: [PATCH 84/88] drm/i915: kill flags parameter for reset functions Only half of them even cared, and it's always the same one. Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 29 +++++++++++++++-------------- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/i915_irq.c | 2 +- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index dbe0f88ad3d0..2ecfcc2ef408 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -673,7 +673,7 @@ int i915_resume(struct drm_device *dev) return 0; } -static int i8xx_do_reset(struct drm_device *dev, u8 flags) +static int i8xx_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -710,7 +710,7 @@ static int i965_reset_complete(struct drm_device *dev) return gdrst & 0x1; } -static int i965_do_reset(struct drm_device *dev, u8 flags) +static int i965_do_reset(struct drm_device *dev) { u8 gdrst; @@ -720,20 +720,22 @@ static int i965_do_reset(struct drm_device *dev, u8 flags) * triggers the reset; when done, the hardware will clear it. */ pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst); - pci_write_config_byte(dev->pdev, I965_GDRST, gdrst | flags | 0x1); + pci_write_config_byte(dev->pdev, I965_GDRST, + gdrst | GRDOM_RENDER | 0x1); return wait_for(i965_reset_complete(dev), 500); } -static int ironlake_do_reset(struct drm_device *dev, u8 flags) +static int ironlake_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); - I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | flags | 0x1); + I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, + gdrst | GRDOM_RENDER | 0x1); return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); } -static int gen6_do_reset(struct drm_device *dev, u8 flags) +static int gen6_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; @@ -768,7 +770,7 @@ static int gen6_do_reset(struct drm_device *dev, u8 flags) return ret; } -static int intel_gpu_reset(struct drm_device *dev, u8 flags) +static int intel_gpu_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret = -ENODEV; @@ -776,16 +778,16 @@ static int intel_gpu_reset(struct drm_device *dev, u8 flags) switch (INTEL_INFO(dev)->gen) { case 7: case 6: - ret = gen6_do_reset(dev, flags); + ret = gen6_do_reset(dev); break; case 5: - ret = ironlake_do_reset(dev, flags); + ret = ironlake_do_reset(dev); break; case 4: - ret = i965_do_reset(dev, flags); + ret = i965_do_reset(dev); break; case 2: - ret = i8xx_do_reset(dev, flags); + ret = i8xx_do_reset(dev); break; } @@ -806,7 +808,6 @@ static int intel_gpu_reset(struct drm_device *dev, u8 flags) /** * i915_reset - reset chip after a hang * @dev: drm device to reset - * @flags: reset domains * * Reset the chip. Useful if a hang is detected. Returns zero on successful * reset or otherwise an error code. @@ -819,7 +820,7 @@ static int intel_gpu_reset(struct drm_device *dev, u8 flags) * - re-init interrupt state * - re-init display */ -int i915_reset(struct drm_device *dev, u8 flags) +int i915_reset(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; int ret; @@ -838,7 +839,7 @@ int i915_reset(struct drm_device *dev, u8 flags) if (get_seconds() - dev_priv->last_gpu_reset < 5) DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); else - ret = intel_gpu_reset(dev, flags); + ret = intel_gpu_reset(dev); dev_priv->last_gpu_reset = get_seconds(); if (ret) { diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c070a2b3f049..e03a4f80c5c9 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1153,7 +1153,7 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, extern int i915_emit_box(struct drm_device *dev, struct drm_clip_rect *box, int DR1, int DR4); -extern int i915_reset(struct drm_device *dev, u8 flags); +extern int i915_reset(struct drm_device *dev); extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv); extern unsigned long i915_mch_val(struct drm_i915_private *dev_priv); extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 8334550fccf4..722cdfc59a9a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -758,7 +758,7 @@ static void i915_error_work_func(struct work_struct *work) if (atomic_read(&dev_priv->mm.wedged)) { DRM_DEBUG_DRIVER("resetting chip\n"); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); - if (!i915_reset(dev, GRDOM_RENDER)) { + if (!i915_reset(dev)) { atomic_set(&dev_priv->mm.wedged, 0); kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); } From 5ccce180fe6d484454650d8b2a71fde22d311013 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 27 Apr 2012 15:17:45 +0200 Subject: [PATCH 85/88] drm/i915: also reset the media engine on gen4/5 ... we actually use it. Unfortunately we can't reset both at the same time without also resetting the display unit, so do render and media separately. Also replace magic constants with proper #defines. Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 28 +++++++++++++++++++++++++--- drivers/gpu/drm/i915/i915_reg.h | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 2ecfcc2ef408..45c9430cf629 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -712,6 +712,7 @@ static int i965_reset_complete(struct drm_device *dev) static int i965_do_reset(struct drm_device *dev) { + int ret; u8 gdrst; /* @@ -721,7 +722,17 @@ static int i965_do_reset(struct drm_device *dev) */ pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst); pci_write_config_byte(dev->pdev, I965_GDRST, - gdrst | GRDOM_RENDER | 0x1); + gdrst | GRDOM_RENDER | + GRDOM_RESET_ENABLE); + ret = wait_for(i965_reset_complete(dev), 500); + if (ret) + return ret; + + /* We can't reset render&media without also resetting display ... */ + pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst); + pci_write_config_byte(dev->pdev, I965_GDRST, + gdrst | GRDOM_MEDIA | + GRDOM_RESET_ENABLE); return wait_for(i965_reset_complete(dev), 500); } @@ -729,9 +740,20 @@ static int i965_do_reset(struct drm_device *dev) static int ironlake_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); + u32 gdrst; + int ret; + + gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, - gdrst | GRDOM_RENDER | 0x1); + gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE); + ret = wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); + if (ret) + return ret; + + /* We can't reset render&media without also resetting display ... */ + gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); + I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, + gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE); return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 7bc407a87c0c..3850b7b95167 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -82,6 +82,7 @@ #define GRDOM_FULL (0<<2) #define GRDOM_RENDER (1<<2) #define GRDOM_MEDIA (3<<2) +#define GRDOM_RESET_ENABLE (1<<0) #define GEN6_MBCUNIT_SNPCR 0x900c /* for LLC config */ #define GEN6_MBC_SNPCR_SHIFT 21 From 523bcb28c3fb92baecda6daa60560f67eb4a6177 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 27 Apr 2012 15:17:46 +0200 Subject: [PATCH 86/88] drm/i915: remove modeset reset from i915_reset On gen4+ we don't reset the display unit, so resetting the complete modeset state should not be necessary. We can't do reset on gen3 anyway, which leaves us with gen2 reset: According to Chris Wilson, that doesn't work so great, so he suggested we just ignore that. If the need ever arrises, we can re-add it later on. Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 45c9430cf629..133f1014ed90 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -904,21 +904,11 @@ int i915_reset(struct drm_device *dev) intel_modeset_init_hw(dev); drm_irq_uninstall(dev); - drm_mode_config_reset(dev); drm_irq_install(dev); } else { mutex_unlock(&dev->struct_mutex); } - /* - * Perform a full modeset as on later generations, e.g. Ironlake, we may - * need to retrain the display link and cannot just restore the register - * values. - */ - mutex_lock(&dev->mode_config.mutex); - drm_helper_resume_force_mode(dev); - mutex_unlock(&dev->mode_config.mutex); - return 0; } From 5fe9fe8c98eeed86650e9e5e9eaa156cf0ce1282 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 2 May 2012 21:33:52 +0200 Subject: [PATCH 87/88] drm/i915: fix gen4 gpu reset While trying to fix up gen4 gpu reset in commit f49f0586191fe16140410db0a46d43bdc690d6af Author: Kenneth Graunke Date: Sat Sep 11 01:19:14 2010 -0700 drm/i915: Actually set the reset bit in i965_reset a little confusion about when wait_for times out has been introduced - wait for loops _until_ the condition is true. This fixes gpu reset on my gm45, testing with my hangman code shows that it's now fairly reliable - it only died after well over 100 reset cycles. Cc: Eric Anholt Reviewed-by: Kenneth Graunke Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 133f1014ed90..77b7a50e2014 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -707,7 +707,7 @@ static int i965_reset_complete(struct drm_device *dev) { u8 gdrst; pci_read_config_byte(dev->pdev, I965_GDRST, &gdrst); - return gdrst & 0x1; + return (gdrst & GRDOM_RESET_ENABLE) == 0; } static int i965_do_reset(struct drm_device *dev) From b615b57a124a4af7b68196bc2fb8acc236041fa2 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 2 May 2012 09:52:12 +0100 Subject: [PATCH 88/88] drm/i915: Support pageflipping interrupts for all 3-pipes on IVB Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 31 ++++++++++++++++++++++--------- drivers/gpu/drm/i915/i915_reg.h | 7 +++++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 722cdfc59a9a..b4999b5288e8 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -615,12 +615,20 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) intel_finish_page_flip_plane(dev, 1); } + if (de_iir & DE_PLANEC_FLIP_DONE_IVB) { + intel_prepare_page_flip(dev, 2); + intel_finish_page_flip_plane(dev, 2); + } + if (de_iir & DE_PIPEA_VBLANK_IVB) drm_handle_vblank(dev, 0); if (de_iir & DE_PIPEB_VBLANK_IVB) drm_handle_vblank(dev, 1); + if (de_iir & DE_PIPEC_VBLANK_IVB) + drm_handle_vblank(dev, 2); + /* check event from PCH */ if (de_iir & DE_PCH_EVENT_IVB) { if (pch_iir & SDE_HOTPLUG_MASK_CPT) @@ -1418,8 +1426,8 @@ static int ivybridge_enable_vblank(struct drm_device *dev, int pipe) return -EINVAL; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - ironlake_enable_display_irq(dev_priv, (pipe == 0) ? - DE_PIPEA_VBLANK_IVB : DE_PIPEB_VBLANK_IVB); + ironlake_enable_display_irq(dev_priv, + DE_PIPEA_VBLANK_IVB << (5 * pipe)); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); return 0; @@ -1486,8 +1494,8 @@ static void ivybridge_disable_vblank(struct drm_device *dev, int pipe) unsigned long irqflags; spin_lock_irqsave(&dev_priv->irq_lock, irqflags); - ironlake_disable_display_irq(dev_priv, (pipe == 0) ? - DE_PIPEA_VBLANK_IVB : DE_PIPEB_VBLANK_IVB); + ironlake_disable_display_irq(dev_priv, + DE_PIPEA_VBLANK_IVB << (pipe * 5)); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); } @@ -1802,9 +1810,11 @@ static int ivybridge_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_IVB | - DE_PCH_EVENT_IVB | DE_PLANEA_FLIP_DONE_IVB | - DE_PLANEB_FLIP_DONE_IVB; + u32 display_mask = + 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; u32 render_irqs; u32 hotplug_mask; @@ -1813,8 +1823,11 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) /* should always can generate irq */ I915_WRITE(DEIIR, I915_READ(DEIIR)); I915_WRITE(DEIMR, dev_priv->irq_mask); - I915_WRITE(DEIER, display_mask | DE_PIPEA_VBLANK_IVB | - DE_PIPEB_VBLANK_IVB); + I915_WRITE(DEIER, + display_mask | + DE_PIPEC_VBLANK_IVB | + DE_PIPEB_VBLANK_IVB | + DE_PIPEA_VBLANK_IVB); POSTING_READ(DEIER); dev_priv->gt_irq_mask = ~0; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 3850b7b95167..10e71a9f8bd9 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3221,11 +3221,14 @@ #define DE_PCH_EVENT_IVB (1<<28) #define DE_DP_A_HOTPLUG_IVB (1<<27) #define DE_AUX_CHANNEL_A_IVB (1<<26) +#define DE_SPRITEC_FLIP_DONE_IVB (1<<14) +#define DE_PLANEC_FLIP_DONE_IVB (1<<13) +#define DE_PIPEC_VBLANK_IVB (1<<10) #define DE_SPRITEB_FLIP_DONE_IVB (1<<9) -#define DE_SPRITEA_FLIP_DONE_IVB (1<<4) #define DE_PLANEB_FLIP_DONE_IVB (1<<8) -#define DE_PLANEA_FLIP_DONE_IVB (1<<3) #define DE_PIPEB_VBLANK_IVB (1<<5) +#define DE_SPRITEA_FLIP_DONE_IVB (1<<4) +#define DE_PLANEA_FLIP_DONE_IVB (1<<3) #define DE_PIPEA_VBLANK_IVB (1<<0) #define VLV_MASTER_IER 0x4400c /* Gunit master IER */