diff options
author | Dave Airlie <airlied@redhat.com> | 2012-09-03 12:05:01 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2012-09-03 12:05:01 +1000 |
commit | 65983bd605a12edd16a4f39f513aad65f1cad062 (patch) | |
tree | bf8c84598565c26e251efb9cb591977446e7e76b | |
parent | 93bb70e0c00f1be4cc857e4d8375c44058cce71e (diff) | |
parent | d7c3b937bdf45f0b844400b7bf6fd3ed50bac604 (diff) | |
download | lwn-65983bd605a12edd16a4f39f513aad65f1cad062.tar.gz lwn-65983bd605a12edd16a4f39f513aad65f1cad062.zip |
Merge branch 'for-airlied' of git://people.freedesktop.org/~danvet/drm-intel into drm-next
Daniel writes:
"New stuff for -next. Highlights:
- prep patches for the modeset rework. Note that one of those patches
touches the fb helper in the common drm code.
- hasw hdmi audio support (Wang Xingchao)
- improved instdone dumping for gen7 (Ben)
- unbound tracking and a few follow-up patches from Chris
- dma_buf->begin/end_cpu_access plus fix for drm/udl (Dave)
- improve mmio error reporting for hsw
- prep patch for WQ_NON_REENTRANT removal (Tejun Heo)
"
* 'for-airlied' of git://people.freedesktop.org/~danvet/drm-intel: (41 commits)
drm/i915: Remove __GFP_NO_KSWAPD
drm/i915: disable rc6 on ilk when vt-d is enabled
drm/i915: Avoid unbinding due to an interrupted pin_and_fence during execbuffer
drm/i915: Use new INSTDONE registers (Gen7+)
drm/i915: Add new INSTDONE registers
drm/i915: Extract reading INSTDONE
drm/i915: Use a non-blocking wait for set-to-domain ioctl
drm/i915: Juggle code order to ease flow of the next patch
drm/i915: Use cpu relocations if the object is in the GTT but not mappable
drm/i915: Extract general object init routine
drm/i915: Protect private gem objects from truncate (such as imported dmabuf)
drm/i915: Only pwrite through the GTT if there is space in the aperture
i915: use alloc_ordered_workqueue() instead of explicit UNBOUND w/ max_active = 1
drm/i915: Find unclaimed MMIO writes.
drm/i915: Add ERR_INT to gen7 error state
drm/i915: Cantiga+ cannot handle a hsync front porch of 0
drm/i915: fix reassignment of variable "intel_dp->DP"
drm/i915: Try harder to allocate an mmap_offset
drm/i915: Show pin count in debugfs
drm/i915: Show (count, size) of purgeable objects in i915_gem_objects
...
34 files changed, 1052 insertions, 807 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 061d26dd0751..eb79515797d9 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1230,7 +1230,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_fb_helper_crtc **crtcs; struct drm_display_mode **modes; - struct drm_encoder *encoder; struct drm_mode_set *modeset; bool *enabled; int width, height; @@ -1241,11 +1240,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) width = dev->mode_config.max_width; height = dev->mode_config.max_height; - /* clean out all the encoder/crtc combos */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - encoder->crtc = NULL; - } - crtcs = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); modes = kcalloc(dev->mode_config.num_connector, diff --git a/drivers/gpu/drm/i915/dvo.h b/drivers/gpu/drm/i915/dvo.h index 0c8ac4d92deb..0fa839e439b3 100644 --- a/drivers/gpu/drm/i915/dvo.h +++ b/drivers/gpu/drm/i915/dvo.h @@ -58,13 +58,12 @@ struct intel_dvo_dev_ops { void (*create_resources)(struct intel_dvo_device *dvo); /* - * Turn on/off output or set intermediate power levels if available. + * Turn on/off output. * - * Unsupported intermediate modes drop to the lower power setting. - * If the mode is DPMSModeOff, the output must be disabled, - * as the DPLL may be disabled afterwards. + * Because none of our dvo drivers support an intermediate power levels, + * we don't expose this in the interfac. */ - void (*dpms)(struct intel_dvo_device *dvo, int mode); + void (*dpms)(struct intel_dvo_device *dvo, bool enable); /* * Callback for testing a video mode for a given output. diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c index 1ca799a1e1fc..71e7650a2994 100644 --- a/drivers/gpu/drm/i915/dvo_ch7017.c +++ b/drivers/gpu/drm/i915/dvo_ch7017.c @@ -163,7 +163,7 @@ struct ch7017_priv { }; static void ch7017_dump_regs(struct intel_dvo_device *dvo); -static void ch7017_dpms(struct intel_dvo_device *dvo, int mode); +static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable); static bool ch7017_read(struct intel_dvo_device *dvo, u8 addr, u8 *val) { @@ -309,7 +309,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo, lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED | (mode->hdisplay & 0x0700) >> 8; - ch7017_dpms(dvo, DRM_MODE_DPMS_OFF); + ch7017_dpms(dvo, false); ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, horizontal_active_pixel_input); ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT, @@ -331,7 +331,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo, } /* set the CH7017 power state */ -static void ch7017_dpms(struct intel_dvo_device *dvo, int mode) +static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable) { uint8_t val; @@ -345,7 +345,7 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode) CH7017_DAC3_POWER_DOWN | CH7017_TV_POWER_DOWN_EN); - if (mode == DRM_MODE_DPMS_ON) { + if (enable) { /* Turn on the LVDS */ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, val & ~CH7017_LVDS_POWER_DOWN_EN); diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c index 4a036600e806..c1dea5b11f91 100644 --- a/drivers/gpu/drm/i915/dvo_ch7xxx.c +++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c @@ -289,9 +289,9 @@ static void ch7xxx_mode_set(struct intel_dvo_device *dvo, } /* set the CH7xxx power state */ -static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode) +static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable) { - if (mode == DRM_MODE_DPMS_ON) + if (enable) ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP); else ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD); diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c index 04f2893d5e3c..fa8ff6b050fa 100644 --- a/drivers/gpu/drm/i915/dvo_ivch.c +++ b/drivers/gpu/drm/i915/dvo_ivch.c @@ -288,7 +288,7 @@ static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo, } /** Sets the power state of the panel connected to the ivch */ -static void ivch_dpms(struct intel_dvo_device *dvo, int mode) +static void ivch_dpms(struct intel_dvo_device *dvo, bool enable) { int i; uint16_t vr01, vr30, backlight; @@ -297,13 +297,13 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode) if (!ivch_read(dvo, VR01, &vr01)) return; - if (mode == DRM_MODE_DPMS_ON) + if (enable) backlight = 1; else backlight = 0; ivch_write(dvo, VR80, backlight); - if (mode == DRM_MODE_DPMS_ON) + if (enable) vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE; else vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE); @@ -315,7 +315,7 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode) if (!ivch_read(dvo, VR30, &vr30)) break; - if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON)) + if (((vr30 & VR30_PANEL_ON) != 0) == enable) break; udelay(1000); } diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c index 1a0bad9a5fab..c4d9f2f395e6 100644 --- a/drivers/gpu/drm/i915/dvo_ns2501.c +++ b/drivers/gpu/drm/i915/dvo_ns2501.c @@ -75,11 +75,6 @@ struct ns2501_priv { #define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr)) /* - * Include the PLL launcher prototype - */ -extern void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe); - -/* * For reasons unclear to me, the ns2501 at least on the Fujitsu/Siemens * laptops does not react on the i2c bus unless * both the PLL is running and the display is configured in its native @@ -113,8 +108,6 @@ static void enable_dvo(struct intel_dvo_device *dvo) I915_WRITE(DVOC_SRCDIM, 0x400300); // 1024x768 I915_WRITE(FW_BLC, 0x1080304); - intel_enable_pll(dev_priv, 0); - I915_WRITE(DVOC, 0x90004084); } @@ -500,19 +493,19 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo, } /* set the NS2501 power state */ -static void ns2501_dpms(struct intel_dvo_device *dvo, int mode) +static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable) { bool ok; bool restore = false; struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); unsigned char ch; - DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %d\n", - __FUNCTION__, mode); + DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %i\n", + __FUNCTION__, enable); ch = ns->reg_8_shadow; - if (mode == DRM_MODE_DPMS_ON) + if (enable) ch |= NS2501_8_PD; else ch &= ~NS2501_8_PD; @@ -526,12 +519,10 @@ static void ns2501_dpms(struct intel_dvo_device *dvo, int mode) ok &= ns2501_writeb(dvo, NS2501_REG8, ch); ok &= ns2501_writeb(dvo, 0x34, - (mode == - DRM_MODE_DPMS_ON) ? (0x03) : (0x00)); + enable ? 0x03 : 0x00); ok &= ns2501_writeb(dvo, 0x35, - (mode == - DRM_MODE_DPMS_ON) ? (0xff) : (0x00)); + enable ? 0xff : 0x00); if (!ok) { if (restore) restore_dvo(dvo); diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c index a0b13a6f619d..cc24c1cabecd 100644 --- a/drivers/gpu/drm/i915/dvo_sil164.c +++ b/drivers/gpu/drm/i915/dvo_sil164.c @@ -208,7 +208,7 @@ static void sil164_mode_set(struct intel_dvo_device *dvo, } /* set the SIL164 power state */ -static void sil164_dpms(struct intel_dvo_device *dvo, int mode) +static void sil164_dpms(struct intel_dvo_device *dvo, bool enable) { int ret; unsigned char ch; @@ -217,7 +217,7 @@ static void sil164_dpms(struct intel_dvo_device *dvo, int mode) if (ret == false) return; - if (mode == DRM_MODE_DPMS_ON) + if (enable) ch |= SIL164_8_PD; else ch &= ~SIL164_8_PD; diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c index aa2cd3ec54aa..097b3e82b00f 100644 --- a/drivers/gpu/drm/i915/dvo_tfp410.c +++ b/drivers/gpu/drm/i915/dvo_tfp410.c @@ -234,14 +234,14 @@ static void tfp410_mode_set(struct intel_dvo_device *dvo, } /* set the tfp410 power state */ -static void tfp410_dpms(struct intel_dvo_device *dvo, int mode) +static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable) { uint8_t ctl1; if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1)) return; - if (mode == DRM_MODE_DPMS_ON) + if (enable) ctl1 |= TFP410_CTL_1_PD; else ctl1 &= ~TFP410_CTL_1_PD; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a18e93687b8b..3d886af2aae6 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -118,6 +118,8 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); if (obj->base.name) seq_printf(m, " (name: %d)", obj->base.name); + if (obj->pin_count) + seq_printf(m, " (pinned x %d)", obj->pin_count); if (obj->fence_reg != I915_FENCE_REG_NONE) seq_printf(m, " (fence: %d)", obj->fence_reg); if (obj->gtt_space != NULL) @@ -197,8 +199,8 @@ static int i915_gem_object_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; struct drm_i915_private *dev_priv = dev->dev_private; - u32 count, mappable_count; - size_t size, mappable_size; + u32 count, mappable_count, purgeable_count; + size_t size, mappable_size, purgeable_size; struct drm_i915_gem_object *obj; int ret; @@ -211,7 +213,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data) dev_priv->mm.object_memory); size = count = mappable_size = mappable_count = 0; - count_objects(&dev_priv->mm.gtt_list, gtt_list); + count_objects(&dev_priv->mm.bound_list, gtt_list); seq_printf(m, "%u [%u] objects, %zu [%zu] bytes in gtt\n", count, mappable_count, size, mappable_size); @@ -225,8 +227,16 @@ 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 = purgeable_size = purgeable_count = 0; + list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) { + size += obj->base.size, ++count; + if (obj->madv == I915_MADV_DONTNEED) + purgeable_size += obj->base.size, ++purgeable_count; + } + seq_printf(m, "%u unbound objects, %zu bytes\n", count, size); + size = count = mappable_size = mappable_count = 0; - list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { if (obj->fault_mappable) { size += obj->gtt_space->size; ++count; @@ -235,7 +245,13 @@ static int i915_gem_object_info(struct seq_file *m, void* data) mappable_size += obj->gtt_space->size; ++mappable_count; } + if (obj->madv == I915_MADV_DONTNEED) { + purgeable_size += obj->base.size; + ++purgeable_count; + } } + seq_printf(m, "%u purgeable objects, %zu bytes\n", + purgeable_count, purgeable_size); seq_printf(m, "%u pinned mappable objects, %zu bytes\n", mappable_count, mappable_size); seq_printf(m, "%u fault mappable objects, %zu bytes\n", @@ -264,7 +280,7 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data) return ret; total_obj_size = total_gtt_size = count = 0; - list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { if (list == PINNED_LIST && obj->pin_count == 0) continue; @@ -526,7 +542,8 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data) for (i = 0; i < dev_priv->num_fence_regs; i++) { struct drm_i915_gem_object *obj = dev_priv->fence_regs[i].obj; - seq_printf(m, "Fenced object[%2d] = ", i); + seq_printf(m, "Fence %d, pin count = %d, object = ", + i, dev_priv->fence_regs[i].pin_count); if (obj == NULL) seq_printf(m, "unused"); else @@ -645,10 +662,9 @@ static void i915_ring_error_state(struct seq_file *m, seq_printf(m, " IPEIR: 0x%08x\n", error->ipeir[ring]); seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr[ring]); seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone[ring]); - if (ring == RCS && INTEL_INFO(dev)->gen >= 4) { - seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1); + if (ring == RCS && INTEL_INFO(dev)->gen >= 4) seq_printf(m, " BBADDR: 0x%08llx\n", error->bbaddr); - } + if (INTEL_INFO(dev)->gen >= 4) seq_printf(m, " INSTPS: 0x%08x\n", error->instps[ring]); seq_printf(m, " INSTPM: 0x%08x\n", error->instpm[ring]); @@ -697,11 +713,17 @@ static int i915_error_state(struct seq_file *m, void *unused) for (i = 0; i < dev_priv->num_fence_regs; i++) seq_printf(m, " fence[%d] = %08llx\n", i, error->fence[i]); + for (i = 0; i < ARRAY_SIZE(error->extra_instdone); i++) + seq_printf(m, " INSTDONE_%d: 0x%08x\n", i, error->extra_instdone[i]); + if (INTEL_INFO(dev)->gen >= 6) { seq_printf(m, "ERROR: 0x%08x\n", error->error); seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg); } + if (INTEL_INFO(dev)->gen == 7) + seq_printf(m, "ERR_INT: 0x%08x\n", error->err_int); + for_each_ring(ring, dev_priv, i) i915_ring_error_state(m, dev, error, i); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0a1b64f8d442..2c09900e3267 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -235,10 +235,10 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) } } - dev_priv->cpp = init->cpp; - dev_priv->back_offset = init->back_offset; - dev_priv->front_offset = init->front_offset; - dev_priv->current_page = 0; + dev_priv->dri1.cpp = init->cpp; + dev_priv->dri1.back_offset = init->back_offset; + dev_priv->dri1.front_offset = init->front_offset; + dev_priv->dri1.current_page = 0; if (master_priv->sarea_priv) master_priv->sarea_priv->pf_current_page = 0; @@ -575,7 +575,7 @@ static int i915_dispatch_flip(struct drm_device * dev) DRM_DEBUG_DRIVER("%s: page=%d pfCurrentPage=%d\n", __func__, - dev_priv->current_page, + dev_priv->dri1.current_page, master_priv->sarea_priv->pf_current_page); i915_kernel_lost_context(dev); @@ -589,12 +589,12 @@ static int i915_dispatch_flip(struct drm_device * dev) OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP); OUT_RING(0); - if (dev_priv->current_page == 0) { - OUT_RING(dev_priv->back_offset); - dev_priv->current_page = 1; + if (dev_priv->dri1.current_page == 0) { + OUT_RING(dev_priv->dri1.back_offset); + dev_priv->dri1.current_page = 1; } else { - OUT_RING(dev_priv->front_offset); - dev_priv->current_page = 0; + OUT_RING(dev_priv->dri1.front_offset); + dev_priv->dri1.current_page = 0; } OUT_RING(0); @@ -613,7 +613,7 @@ static int i915_dispatch_flip(struct drm_device * dev) ADVANCE_LP_RING(); } - master_priv->sarea_priv->pf_current_page = dev_priv->current_page; + master_priv->sarea_priv->pf_current_page = dev_priv->dri1.current_page; return 0; } @@ -1012,6 +1012,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_SEMAPHORES: value = i915_semaphore_is_enabled(dev); break; + case I915_PARAM_HAS_PRIME_VMAP_FLUSH: + value = 1; + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); @@ -1555,11 +1558,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) * * All tasks on the workqueue are expected to acquire the dev mutex * so there is no point in running more than one instance of the - * workqueue at any time: max_active = 1 and NON_REENTRANT. + * workqueue at any time. Use an ordered one. */ - dev_priv->wq = alloc_workqueue("i915", - WQ_UNBOUND | WQ_NON_REENTRANT, - 1); + dev_priv->wq = alloc_ordered_workqueue("i915", 0); if (dev_priv->wq == NULL) { DRM_ERROR("Failed to create our workqueue.\n"); ret = -ENOMEM; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 7ebb13b65133..7f3863fb138c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1174,6 +1174,10 @@ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ if (unlikely(__fifo_ret)) { \ gen6_gt_check_fifodbg(dev_priv); \ } \ + if (IS_HASWELL(dev_priv->dev) && (I915_READ_NOTRACE(GEN7_ERR_INT) & ERR_INT_MMIO_UNCLAIMED)) { \ + DRM_ERROR("Unclaimed write to %x\n", reg); \ + writel(ERR_INT_MMIO_UNCLAIMED, dev_priv->regs + GEN7_ERR_INT); \ + } \ } __i915_write(8, b) __i915_write(16, w) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 261fe2175afb..58b43db0a134 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -196,9 +196,10 @@ struct drm_i915_error_state { u32 cpu_ring_head[I915_NUM_RINGS]; u32 cpu_ring_tail[I915_NUM_RINGS]; u32 error; /* gen6+ */ + u32 err_int; /* gen7 */ u32 instpm[I915_NUM_RINGS]; u32 instps[I915_NUM_RINGS]; - u32 instdone1; + u32 extra_instdone[I915_NUM_INSTDONE_REG]; u32 seqno[I915_NUM_RINGS]; u64 bbaddr; u32 fault_reg[I915_NUM_RINGS]; @@ -428,12 +429,6 @@ typedef struct drm_i915_private { struct resource mch_res; - unsigned int cpp; - int back_offset; - int front_offset; - int current_page; - int page_flipping; - atomic_t irq_received; /* protects the irq masks */ @@ -451,7 +446,6 @@ typedef struct drm_i915_private { u32 hotplug_supported_mask; struct work_struct hotplug_work; - unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int num_pipe; int num_pch_pll; @@ -460,8 +454,7 @@ typedef struct drm_i915_private { struct timer_list hangcheck_timer; int hangcheck_count; uint32_t last_acthd[I915_NUM_RINGS]; - uint32_t last_instdone; - uint32_t last_instdone1; + uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; unsigned int stop_rings; @@ -692,7 +685,13 @@ typedef struct drm_i915_private { struct drm_mm gtt_space; /** List of all objects in gtt_space. Used to restore gtt * mappings on resume */ - struct list_head gtt_list; + struct list_head bound_list; + /** + * List of objects which are not bound to the GTT (thus + * are idle and not used by the GPU) but still have + * (presumably uncached) pages still attached. + */ + struct list_head unbound_list; /** Usable portion of the GTT for GEM */ unsigned long gtt_start; @@ -790,6 +789,12 @@ typedef struct drm_i915_private { struct { unsigned allow_batchbuffer : 1; u32 __iomem *gfx_hws_cpu_addr; + + unsigned int cpp; + int back_offset; + int front_offset; + int current_page; + int page_flipping; } dri1; /* Kernel Modesetting */ @@ -1296,19 +1301,20 @@ int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); int i915_gem_init_object(struct drm_gem_object *obj); +void i915_gem_object_init(struct drm_i915_gem_object *obj); struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size); void i915_gem_free_object(struct drm_gem_object *obj); int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, - bool map_and_fenceable); + bool map_and_fenceable, + bool nonblocking); void i915_gem_object_unpin(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj); void i915_gem_release_mmap(struct drm_i915_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); -int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, - gfp_t gfpmask); +int __must_check i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj); int __must_check i915_mutex_lock_interruptible(struct drm_device *dev); int i915_gem_object_sync(struct drm_i915_gem_object *obj, struct intel_ring_buffer *to); @@ -1449,8 +1455,9 @@ void i915_gem_init_global_gtt(struct drm_device *dev, int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, unsigned cache_level, - bool mappable); -int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only); + bool mappable, + bool nonblock); +int i915_gem_evict_everything(struct drm_device *dev); /* i915_gem_stolen.c */ int i915_gem_init_stolen(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 31054fa44c47..87a64e5f28fb 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -41,7 +41,8 @@ static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *o static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj); static __must_check int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, unsigned alignment, - bool map_and_fenceable); + bool map_and_fenceable, + bool nonblocking); static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_i915_gem_object *obj, struct drm_i915_gem_pwrite *args, @@ -55,6 +56,8 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, static int i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc); +static long i915_gem_purge(struct drm_i915_private *dev_priv, long target); +static void i915_gem_shrink_all(struct drm_i915_private *dev_priv); static void i915_gem_object_truncate(struct drm_i915_gem_object *obj); static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj) @@ -140,7 +143,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; + return obj->gtt_space && !obj->active; } int @@ -179,7 +182,7 @@ 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.gtt_list, gtt_list) + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) if (obj->pin_count) pinned += obj->gtt_space->size; mutex_unlock(&dev->struct_mutex); @@ -423,9 +426,11 @@ i915_gem_shmem_pread(struct drm_device *dev, * anyway again before the next pread happens. */ if (obj->cache_level == I915_CACHE_NONE) needs_clflush = 1; - ret = i915_gem_object_set_to_gtt_domain(obj, false); - if (ret) - return ret; + if (obj->gtt_space) { + ret = i915_gem_object_set_to_gtt_domain(obj, false); + if (ret) + return ret; + } } offset = args->offset; @@ -605,7 +610,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, char __user *user_data; int page_offset, page_length, ret; - ret = i915_gem_object_pin(obj, 0, true); + ret = i915_gem_object_pin(obj, 0, true, true); if (ret) goto out; @@ -751,9 +756,11 @@ i915_gem_shmem_pwrite(struct drm_device *dev, * right away and we therefore have to clflush anyway. */ if (obj->cache_level == I915_CACHE_NONE) needs_clflush_after = 1; - ret = i915_gem_object_set_to_gtt_domain(obj, true); - if (ret) - return ret; + if (obj->gtt_space) { + ret = i915_gem_object_set_to_gtt_domain(obj, true); + if (ret) + return ret; + } } /* Same trick applies for invalidate partially written cachelines before * writing. */ @@ -919,10 +926,8 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, goto out; } - if (obj->gtt_space && - obj->cache_level == I915_CACHE_NONE && + if (obj->cache_level == I915_CACHE_NONE && obj->tiling_mode == I915_TILING_NONE && - obj->map_and_fenceable && obj->base.write_domain != I915_GEM_DOMAIN_CPU) { ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file); /* Note that the gtt paths might fail with non-page-backed user @@ -930,7 +935,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, * textures). Fallback to the shmem path in that case. */ } - if (ret == -EFAULT) + if (ret == -EFAULT || ret == -ENOSPC) ret = i915_gem_shmem_pwrite(dev, obj, args, file); out: @@ -940,6 +945,240 @@ unlock: return ret; } +int +i915_gem_check_wedge(struct drm_i915_private *dev_priv, + bool interruptible) +{ + if (atomic_read(&dev_priv->mm.wedged)) { + struct completion *x = &dev_priv->error_completion; + 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); + + /* Non-interruptible callers can't handle -EAGAIN, hence return + * -EIO unconditionally for these. */ + if (!interruptible) + return -EIO; + + /* Recovery complete, but still wedged means reset failure. */ + if (recovery_complete) + return -EIO; + + return -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; + + BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex)); + + ret = 0; + if (seqno == ring->outstanding_lazy_request) + ret = i915_add_request(ring, NULL, NULL); + + return ret; +} + +/** + * __wait_seqno - wait until execution of seqno has finished + * @ring: the ring expected to report seqno + * @seqno: duh! + * @interruptible: do an interruptible wait (normally yes) + * @timeout: in - how long to wait (NULL forever); out - how much time remaining + * + * Returns 0 if the seqno was found within the alloted time. Else returns the + * errno with remaining time filled in timeout argument. + */ +static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, + bool interruptible, struct timespec *timeout) +{ + drm_i915_private_t *dev_priv = ring->dev->dev_private; + struct timespec before, now, wait_time={1,0}; + unsigned long timeout_jiffies; + long end; + bool wait_forever = true; + int ret; + + if (i915_seqno_passed(ring->get_seqno(ring, true), seqno)) + return 0; + + trace_i915_gem_request_wait_begin(ring, seqno); + + if (timeout != NULL) { + wait_time = *timeout; + wait_forever = false; + } + + timeout_jiffies = timespec_to_jiffies(&wait_time); + + if (WARN_ON(!ring->irq_get(ring))) + return -ENODEV; + + /* Record current time in case interrupted by signal, or wedged * */ + getrawmonotonic(&before); + +#define EXIT_COND \ + (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ + atomic_read(&dev_priv->mm.wedged)) + do { + if (interruptible) + end = wait_event_interruptible_timeout(ring->irq_queue, + EXIT_COND, + timeout_jiffies); + else + end = wait_event_timeout(ring->irq_queue, EXIT_COND, + timeout_jiffies); + + ret = i915_gem_check_wedge(dev_priv, interruptible); + if (ret) + end = ret; + } while (end == 0 && wait_forever); + + getrawmonotonic(&now); + + ring->irq_put(ring); + trace_i915_gem_request_wait_end(ring, seqno); +#undef EXIT_COND + + if (timeout) { + struct timespec sleep_time = timespec_sub(now, before); + *timeout = timespec_sub(*timeout, sleep_time); + } + + switch (end) { + case -EIO: + case -EAGAIN: /* Wedged */ + case -ERESTARTSYS: /* Signal */ + return (int)end; + case 0: /* Timeout */ + if (timeout) + set_normalized_timespec(timeout, 0, 0); + return -ETIME; + default: /* Completed */ + WARN_ON(end < 0); /* We're not aware of other errors */ + return 0; + } +} + +/** + * Waits for a sequence number to be signaled, and cleans up the + * request and object lists appropriately for that event. + */ +int +i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + bool interruptible = dev_priv->mm.interruptible; + int ret; + + BUG_ON(!mutex_is_locked(&dev->struct_mutex)); + BUG_ON(seqno == 0); + + ret = i915_gem_check_wedge(dev_priv, interruptible); + if (ret) + return ret; + + ret = i915_gem_check_olr(ring, seqno); + if (ret) + return ret; + + return __wait_seqno(ring, seqno, interruptible, NULL); +} + +/** + * Ensures that all rendering to the object has completed and the object is + * safe to unbind from the GTT or access from the CPU. + */ +static __must_check int +i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, + bool readonly) +{ + struct intel_ring_buffer *ring = obj->ring; + u32 seqno; + int ret; + + seqno = readonly ? obj->last_write_seqno : obj->last_read_seqno; + if (seqno == 0) + return 0; + + ret = i915_wait_seqno(ring, seqno); + if (ret) + return ret; + + i915_gem_retire_requests_ring(ring); + + /* Manually manage the write flush as we may have not yet + * retired the buffer. + */ + if (obj->last_write_seqno && + i915_seqno_passed(seqno, obj->last_write_seqno)) { + obj->last_write_seqno = 0; + obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; + } + + return 0; +} + +/* A nonblocking variant of the above wait. This is a highly dangerous routine + * as the object state may change during this call. + */ +static __must_check int +i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, + bool readonly) +{ + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = obj->ring; + u32 seqno; + int ret; + + BUG_ON(!mutex_is_locked(&dev->struct_mutex)); + BUG_ON(!dev_priv->mm.interruptible); + + seqno = readonly ? obj->last_write_seqno : obj->last_read_seqno; + if (seqno == 0) + return 0; + + ret = i915_gem_check_wedge(dev_priv, true); + if (ret) + return ret; + + ret = i915_gem_check_olr(ring, seqno); + if (ret) + return ret; + + mutex_unlock(&dev->struct_mutex); + ret = __wait_seqno(ring, seqno, true, NULL); + mutex_lock(&dev->struct_mutex); + + i915_gem_retire_requests_ring(ring); + + /* Manually manage the write flush as we may have not yet + * retired the buffer. + */ + if (obj->last_write_seqno && + i915_seqno_passed(seqno, obj->last_write_seqno)) { + obj->last_write_seqno = 0; + obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; + } + + return ret; +} + /** * Called when user space prepares to use an object with the CPU, either * through the mmap ioctl's mapping or a GTT mapping. @@ -977,6 +1216,14 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, goto unlock; } + /* Try to flush the object off the GPU without holding the lock. + * We will repeat the flush holding the lock in the normal manner + * to catch cases where we are gazumped. + */ + ret = i915_gem_object_wait_rendering__nonblocking(obj, !write_domain); + if (ret) + goto unref; + if (read_domains & I915_GEM_DOMAIN_GTT) { ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0); @@ -990,6 +1237,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0); } +unref: drm_gem_object_unreference(&obj->base); unlock: mutex_unlock(&dev->struct_mutex); @@ -1109,7 +1357,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) goto unlock; } if (!obj->gtt_space) { - ret = i915_gem_object_bind_to_gtt(obj, 0, true); + ret = i915_gem_object_bind_to_gtt(obj, 0, true, false); if (ret) goto unlock; @@ -1270,6 +1518,42 @@ i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, return i915_gem_get_gtt_size(dev, size, tiling_mode); } +static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) +{ + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + int ret; + + if (obj->base.map_list.map) + return 0; + + ret = drm_gem_create_mmap_offset(&obj->base); + if (ret != -ENOSPC) + return ret; + + /* Badly fragmented mmap space? The only way we can recover + * space is by destroying unwanted objects. We can't randomly release + * mmap_offsets as userspace expects them to be persistent for the + * lifetime of the objects. The closest we can is to release the + * offsets on purgeable objects by truncating it and marking it purged, + * which prevents userspace from ever using that object again. + */ + i915_gem_purge(dev_priv, obj->base.size >> PAGE_SHIFT); + ret = drm_gem_create_mmap_offset(&obj->base); + if (ret != -ENOSPC) + return ret; + + i915_gem_shrink_all(dev_priv); + return drm_gem_create_mmap_offset(&obj->base); +} + +static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj) +{ + if (!obj->base.map_list.map) + return; + + drm_gem_free_mmap_offset(&obj->base); +} + int i915_gem_mmap_gtt(struct drm_file *file, struct drm_device *dev, @@ -1301,11 +1585,9 @@ i915_gem_mmap_gtt(struct drm_file *file, goto out; } - if (!obj->base.map_list.map) { - ret = drm_gem_create_mmap_offset(&obj->base); - if (ret) - goto out; - } + ret = i915_gem_object_create_mmap_offset(obj); + if (ret) + goto out; *offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT; @@ -1340,64 +1622,58 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset); } -int -i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, - gfp_t gfpmask) +/* Immediately discard the backing storage */ +static void +i915_gem_object_truncate(struct drm_i915_gem_object *obj) { - int page_count, i; - struct address_space *mapping; struct inode *inode; - struct page *page; - if (obj->pages || obj->sg_table) - return 0; + i915_gem_object_free_mmap_offset(obj); - /* Get the list of pages out of our struct file. They'll be pinned - * at this point until we release them. - */ - page_count = obj->base.size / PAGE_SIZE; - BUG_ON(obj->pages != NULL); - obj->pages = drm_malloc_ab(page_count, sizeof(struct page *)); - if (obj->pages == NULL) - return -ENOMEM; + if (obj->base.filp == NULL) + return; + /* Our goal here is to return as much of the memory as + * is possible back to the system as we are called from OOM. + * To do this we must instruct the shmfs to drop all of its + * backing pages, *now*. + */ inode = obj->base.filp->f_path.dentry->d_inode; - mapping = inode->i_mapping; - gfpmask |= mapping_gfp_mask(mapping); - - for (i = 0; i < page_count; i++) { - page = shmem_read_mapping_page_gfp(mapping, i, gfpmask); - if (IS_ERR(page)) - goto err_pages; - - obj->pages[i] = page; - } - - if (i915_gem_object_needs_bit17_swizzle(obj)) - i915_gem_object_do_bit_17_swizzle(obj); - - return 0; + shmem_truncate_range(inode, 0, (loff_t)-1); -err_pages: - while (i--) - page_cache_release(obj->pages[i]); + obj->madv = __I915_MADV_PURGED; +} - drm_free_large(obj->pages); - obj->pages = NULL; - return PTR_ERR(page); +static inline int +i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj) +{ + return obj->madv == I915_MADV_DONTNEED; } -static void +static int i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) { int page_count = obj->base.size / PAGE_SIZE; - int i; + int ret, i; - if (!obj->pages) - return; + BUG_ON(obj->gtt_space); + + if (obj->pages == NULL) + return 0; + BUG_ON(obj->gtt_space); BUG_ON(obj->madv == __I915_MADV_PURGED); + ret = i915_gem_object_set_to_cpu_domain(obj, true); + if (ret) { + /* In the event of a disaster, abandon all caches and + * hope for the best. + */ + WARN_ON(ret != -EIO); + i915_gem_clflush_object(obj); + obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU; + } + if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_save_bit_17_swizzle(obj); @@ -1417,6 +1693,129 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) drm_free_large(obj->pages); obj->pages = NULL; + + list_del(&obj->gtt_list); + + if (i915_gem_object_is_purgeable(obj)) + i915_gem_object_truncate(obj); + + return 0; +} + +static long +i915_gem_purge(struct drm_i915_private *dev_priv, long target) +{ + struct drm_i915_gem_object *obj, *next; + long count = 0; + + list_for_each_entry_safe(obj, next, + &dev_priv->mm.unbound_list, + gtt_list) { + if (i915_gem_object_is_purgeable(obj) && + i915_gem_object_put_pages_gtt(obj) == 0) { + count += obj->base.size >> PAGE_SHIFT; + if (count >= target) + return count; + } + } + + list_for_each_entry_safe(obj, next, + &dev_priv->mm.inactive_list, + mm_list) { + if (i915_gem_object_is_purgeable(obj) && + i915_gem_object_unbind(obj) == 0 && + i915_gem_object_put_pages_gtt(obj) == 0) { + count += obj->base.size >> PAGE_SHIFT; + if (count >= target) + return count; + } + } + + return count; +} + +static void +i915_gem_shrink_all(struct drm_i915_private *dev_priv) +{ + struct drm_i915_gem_object *obj, *next; + + i915_gem_evict_everything(dev_priv->dev); + + list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list) + i915_gem_object_put_pages_gtt(obj); +} + +int +i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) +{ + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + int page_count, i; + struct address_space *mapping; + struct page *page; + gfp_t gfp; + + if (obj->pages || obj->sg_table) + return 0; + + /* Assert that the object is not currently in any GPU domain. As it + * wasn't in the GTT, there shouldn't be any way it could have been in + * a GPU cache + */ + BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS); + BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS); + + /* Get the list of pages out of our struct file. They'll be pinned + * at this point until we release them. + */ + page_count = obj->base.size / PAGE_SIZE; + obj->pages = drm_malloc_ab(page_count, sizeof(struct page *)); + if (obj->pages == NULL) + return -ENOMEM; + + /* Fail silently without starting the shrinker */ + mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping; + gfp = mapping_gfp_mask(mapping); + gfp |= __GFP_NORETRY | __GFP_NOWARN; + gfp &= ~(__GFP_IO | __GFP_WAIT); + for (i = 0; i < page_count; i++) { + page = shmem_read_mapping_page_gfp(mapping, i, gfp); + if (IS_ERR(page)) { + i915_gem_purge(dev_priv, page_count); + page = shmem_read_mapping_page_gfp(mapping, i, gfp); + } + if (IS_ERR(page)) { + /* We've tried hard to allocate the memory by reaping + * our own buffer, now let the real VM do its job and + * go down in flames if truly OOM. + */ + gfp &= ~(__GFP_NORETRY | __GFP_NOWARN); + gfp |= __GFP_IO | __GFP_WAIT; + + i915_gem_shrink_all(dev_priv); + page = shmem_read_mapping_page_gfp(mapping, i, gfp); + if (IS_ERR(page)) + goto err_pages; + + gfp |= __GFP_NORETRY | __GFP_NOWARN; + gfp &= ~(__GFP_IO | __GFP_WAIT); + } + + obj->pages[i] = page; + } + + if (i915_gem_object_needs_bit17_swizzle(obj)) + i915_gem_object_do_bit_17_swizzle(obj); + + list_add_tail(&obj->gtt_list, &dev_priv->mm.unbound_list); + return 0; + +err_pages: + while (i--) + page_cache_release(obj->pages[i]); + + drm_free_large(obj->pages); + obj->pages = NULL; + return PTR_ERR(page); } void @@ -1486,32 +1885,6 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) WARN_ON(i915_verify_lists(dev)); } -/* Immediately discard the backing storage */ -static void -i915_gem_object_truncate(struct drm_i915_gem_object *obj) -{ - struct inode *inode; - - /* Our goal here is to return as much of the memory as - * is possible back to the system as we are called from OOM. - * To do this we must instruct the shmfs to drop all of its - * backing pages, *now*. - */ - inode = obj->base.filp->f_path.dentry->d_inode; - shmem_truncate_range(inode, 0, (loff_t)-1); - - if (obj->base.map_list.map) - drm_gem_free_mmap_offset(&obj->base); - - obj->madv = __I915_MADV_PURGED; -} - -static inline int -i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj) -{ - return obj->madv == I915_MADV_DONTNEED; -} - static u32 i915_gem_get_seqno(struct drm_device *dev) { @@ -1698,6 +2071,7 @@ void i915_gem_reset(struct drm_device *dev) obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS; } + /* The fence registers are invalidated so clear them out */ i915_gem_reset_fences(dev); } @@ -1821,197 +2195,6 @@ i915_gem_retire_work_handler(struct work_struct *work) mutex_unlock(&dev->struct_mutex); } -int -i915_gem_check_wedge(struct drm_i915_private *dev_priv, - bool interruptible) -{ - if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->error_completion; - 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); - - /* Non-interruptible callers can't handle -EAGAIN, hence return - * -EIO unconditionally for these. */ - if (!interruptible) - return -EIO; - - /* Recovery complete, but still wedged means reset failure. */ - if (recovery_complete) - return -EIO; - - return -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; - - BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex)); - - ret = 0; - if (seqno == ring->outstanding_lazy_request) - ret = i915_add_request(ring, NULL, NULL); - - return ret; -} - -/** - * __wait_seqno - wait until execution of seqno has finished - * @ring: the ring expected to report seqno - * @seqno: duh! - * @interruptible: do an interruptible wait (normally yes) - * @timeout: in - how long to wait (NULL forever); out - how much time remaining - * - * Returns 0 if the seqno was found within the alloted time. Else returns the - * errno with remaining time filled in timeout argument. - */ -static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, - bool interruptible, struct timespec *timeout) -{ - drm_i915_private_t *dev_priv = ring->dev->dev_private; - struct timespec before, now, wait_time={1,0}; - unsigned long timeout_jiffies; - long end; - bool wait_forever = true; - int ret; - - if (i915_seqno_passed(ring->get_seqno(ring, true), seqno)) - return 0; - - trace_i915_gem_request_wait_begin(ring, seqno); - - if (timeout != NULL) { - wait_time = *timeout; - wait_forever = false; - } - - timeout_jiffies = timespec_to_jiffies(&wait_time); - - if (WARN_ON(!ring->irq_get(ring))) - return -ENODEV; - - /* Record current time in case interrupted by signal, or wedged * */ - getrawmonotonic(&before); - -#define EXIT_COND \ - (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - atomic_read(&dev_priv->mm.wedged)) - do { - if (interruptible) - end = wait_event_interruptible_timeout(ring->irq_queue, - EXIT_COND, - timeout_jiffies); - else - end = wait_event_timeout(ring->irq_queue, EXIT_COND, - timeout_jiffies); - - ret = i915_gem_check_wedge(dev_priv, interruptible); - if (ret) - end = ret; - } while (end == 0 && wait_forever); - - getrawmonotonic(&now); - - ring->irq_put(ring); - trace_i915_gem_request_wait_end(ring, seqno); -#undef EXIT_COND - - if (timeout) { - struct timespec sleep_time = timespec_sub(now, before); - *timeout = timespec_sub(*timeout, sleep_time); - } - - switch (end) { - case -EIO: - case -EAGAIN: /* Wedged */ - case -ERESTARTSYS: /* Signal */ - return (int)end; - case 0: /* Timeout */ - if (timeout) - set_normalized_timespec(timeout, 0, 0); - return -ETIME; - default: /* Completed */ - WARN_ON(end < 0); /* We're not aware of other errors */ - return 0; - } -} - -/** - * Waits for a sequence number to be signaled, and cleans up the - * request and object lists appropriately for that event. - */ -int -i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) -{ - drm_i915_private_t *dev_priv = ring->dev->dev_private; - int ret = 0; - - BUG_ON(seqno == 0); - - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); - if (ret) - return ret; - - ret = i915_gem_check_olr(ring, seqno); - if (ret) - return ret; - - ret = __wait_seqno(ring, seqno, dev_priv->mm.interruptible, NULL); - - return ret; -} - -/** - * Ensures that all rendering to the object has completed and the object is - * safe to unbind from the GTT or access from the CPU. - */ -static __must_check int -i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, - bool readonly) -{ - u32 seqno; - int ret; - - /* If there is rendering queued on the buffer being evicted, wait for - * it. - */ - if (readonly) - seqno = obj->last_write_seqno; - else - seqno = obj->last_read_seqno; - if (seqno == 0) - return 0; - - ret = i915_wait_seqno(obj->ring, seqno); - if (ret) - return ret; - - /* Manually manage the write flush as we may have not yet retired - * the buffer. - */ - if (obj->last_write_seqno && - i915_seqno_passed(seqno, obj->last_write_seqno)) { - obj->last_write_seqno = 0; - obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; - } - - i915_gem_retire_requests_ring(obj->ring); - return 0; -} - /** * Ensures that an object will eventually get non-busy by flushing any required * write domains, emitting any outstanding lazy request and retiring and @@ -2199,6 +2382,8 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) if (obj->pin_count) return -EBUSY; + BUG_ON(obj->pages == NULL); + ret = i915_gem_object_finish_gpu(obj); if (ret) return ret; @@ -2209,22 +2394,6 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) i915_gem_object_finish_gtt(obj); - /* Move the object to the CPU domain to ensure that - * any possible CPU writes while it's not in the GTT - * are flushed when we go to remap it. - */ - if (ret == 0) - ret = i915_gem_object_set_to_cpu_domain(obj, 1); - if (ret == -ERESTARTSYS) - return ret; - if (ret) { - /* In the event of a disaster, abandon all caches and - * hope for the best. - */ - i915_gem_clflush_object(obj); - obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU; - } - /* release the fence reg _after_ flushing */ ret = i915_gem_object_put_fence(obj); if (ret) @@ -2240,10 +2409,8 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) } i915_gem_gtt_finish_object(obj); - i915_gem_object_put_pages_gtt(obj); - - list_del_init(&obj->gtt_list); - list_del_init(&obj->mm_list); + list_del(&obj->mm_list); + list_move_tail(&obj->gtt_list, &dev_priv->mm.unbound_list); /* Avoid an unnecessary call to unbind on rebind. */ obj->map_and_fenceable = true; @@ -2251,10 +2418,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) obj->gtt_space = NULL; obj->gtt_offset = 0; - if (i915_gem_object_is_purgeable(obj)) - i915_gem_object_truncate(obj); - - return ret; + return 0; } static int i915_ring_idle(struct intel_ring_buffer *ring) @@ -2662,12 +2826,12 @@ static void i915_gem_verify_gtt(struct drm_device *dev) static int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, unsigned alignment, - bool map_and_fenceable) + bool map_and_fenceable, + bool nonblocking) { struct drm_device *dev = obj->base.dev; drm_i915_private_t *dev_priv = dev->dev_private; struct drm_mm_node *free_space; - gfp_t gfpmask = __GFP_NORETRY | __GFP_NOWARN; u32 size, fence_size, fence_alignment, unfenced_alignment; bool mappable, fenceable; int ret; @@ -2707,6 +2871,10 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, return -E2BIG; } + ret = i915_gem_object_get_pages_gtt(obj); + if (ret) + return ret; + search_free: if (map_and_fenceable) free_space = @@ -2733,12 +2901,10 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, false); } if (obj->gtt_space == NULL) { - /* If the gtt is empty and we're still having trouble - * fitting our object in, we're out of memory. - */ ret = i915_gem_evict_something(dev, size, alignment, obj->cache_level, - map_and_fenceable); + map_and_fenceable, + nonblocking); if (ret) return ret; @@ -2752,55 +2918,20 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, return -EINVAL; } - ret = i915_gem_object_get_pages_gtt(obj, gfpmask); - if (ret) { - drm_mm_put_block(obj->gtt_space); - obj->gtt_space = NULL; - - if (ret == -ENOMEM) { - /* first try to reclaim some memory by clearing the GTT */ - ret = i915_gem_evict_everything(dev, false); - if (ret) { - /* now try to shrink everyone else */ - if (gfpmask) { - gfpmask = 0; - goto search_free; - } - - return -ENOMEM; - } - - goto search_free; - } - - return ret; - } ret = i915_gem_gtt_prepare_object(obj); if (ret) { - i915_gem_object_put_pages_gtt(obj); drm_mm_put_block(obj->gtt_space); obj->gtt_space = NULL; - - if (i915_gem_evict_everything(dev, false)) - return ret; - - goto search_free; + return ret; } if (!dev_priv->mm.aliasing_ppgtt) i915_gem_gtt_bind_object(obj, obj->cache_level); - list_add_tail(&obj->gtt_list, &dev_priv->mm.gtt_list); + list_move_tail(&obj->gtt_list, &dev_priv->mm.bound_list); list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list); - /* Assert that the object is not currently in any GPU domain. As it - * wasn't in the GTT, there shouldn't be any way it could have been in - * a GPU cache - */ - BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS); - BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS); - obj->gtt_offset = obj->gtt_space->start; fenceable = @@ -3113,7 +3244,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, * (e.g. libkms for the bootup splash), we have to ensure that we * always use map_and_fenceable for all scanout buffers. */ - ret = i915_gem_object_pin(obj, alignment, true); + ret = i915_gem_object_pin(obj, alignment, true, false); if (ret) return ret; @@ -3250,7 +3381,8 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) int i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, - bool map_and_fenceable) + bool map_and_fenceable, + bool nonblocking) { int ret; @@ -3274,7 +3406,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, if (obj->gtt_space == NULL) { ret = i915_gem_object_bind_to_gtt(obj, alignment, - map_and_fenceable); + map_and_fenceable, + nonblocking); if (ret) return ret; } @@ -3332,7 +3465,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, obj->user_pin_count++; obj->pin_filp = file; if (obj->user_pin_count == 1) { - ret = i915_gem_object_pin(obj, args->alignment, true); + ret = i915_gem_object_pin(obj, args->alignment, true, false); if (ret) goto out; } @@ -3464,9 +3597,8 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data, if (obj->madv != __I915_MADV_PURGED) obj->madv = args->madv; - /* if the object is no longer bound, discard its backing storage */ - if (i915_gem_object_is_purgeable(obj) && - obj->gtt_space == NULL) + /* if the object is no longer attached, discard its backing storage */ + if (i915_gem_object_is_purgeable(obj) && obj->pages == NULL) i915_gem_object_truncate(obj); args->retained = obj->madv != __I915_MADV_PURGED; @@ -3478,10 +3610,26 @@ unlock: return ret; } +void i915_gem_object_init(struct drm_i915_gem_object *obj) +{ + obj->base.driver_private = NULL; + + INIT_LIST_HEAD(&obj->mm_list); + INIT_LIST_HEAD(&obj->gtt_list); + INIT_LIST_HEAD(&obj->ring_list); + INIT_LIST_HEAD(&obj->exec_list); + + obj->fence_reg = I915_FENCE_REG_NONE; + obj->madv = I915_MADV_WILLNEED; + /* Avoid an unnecessary call to unbind on the first bind. */ + obj->map_and_fenceable = true; + + i915_gem_info_add_obj(obj->base.dev->dev_private, obj->base.size); +} + struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size) { - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; struct address_space *mapping; u32 mask; @@ -3505,7 +3653,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping; mapping_set_gfp_mask(mapping, mask); - i915_gem_info_add_obj(dev_priv, size); + i915_gem_object_init(obj); obj->base.write_domain = I915_GEM_DOMAIN_CPU; obj->base.read_domains = I915_GEM_DOMAIN_CPU; @@ -3527,16 +3675,6 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, } else obj->cache_level = I915_CACHE_NONE; - obj->base.driver_private = NULL; - obj->fence_reg = I915_FENCE_REG_NONE; - INIT_LIST_HEAD(&obj->mm_list); - INIT_LIST_HEAD(&obj->gtt_list); - INIT_LIST_HEAD(&obj->ring_list); - INIT_LIST_HEAD(&obj->exec_list); - obj->madv = I915_MADV_WILLNEED; - /* Avoid an unnecessary call to unbind on the first bind. */ - obj->map_and_fenceable = true; - return obj; } @@ -3573,8 +3711,8 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) dev_priv->mm.interruptible = was_interruptible; } - if (obj->base.map_list.map) - drm_gem_free_mmap_offset(&obj->base); + i915_gem_object_put_pages_gtt(obj); + i915_gem_object_free_mmap_offset(obj); drm_gem_object_release(&obj->base); i915_gem_info_remove_obj(dev_priv, obj->base.size); @@ -3605,7 +3743,7 @@ i915_gem_idle(struct drm_device *dev) /* Under UMS, be paranoid and evict. */ if (!drm_core_check_feature(dev, DRIVER_MODESET)) - i915_gem_evict_everything(dev, false); + i915_gem_evict_everything(dev); i915_gem_reset_fences(dev); @@ -3963,8 +4101,9 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->mm.active_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); + INIT_LIST_HEAD(&dev_priv->mm.unbound_list); + INIT_LIST_HEAD(&dev_priv->mm.bound_list); INIT_LIST_HEAD(&dev_priv->mm.fence_list); - INIT_LIST_HEAD(&dev_priv->mm.gtt_list); for (i = 0; i < I915_NUM_RINGS; i++) init_ring_lists(&dev_priv->ring[i]); for (i = 0; i < I915_MAX_NUM_FENCES; i++) @@ -4209,13 +4348,6 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file) } static int -i915_gpu_is_active(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - return !list_empty(&dev_priv->mm.active_list); -} - -static int i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc) { struct drm_i915_private *dev_priv = @@ -4223,60 +4355,26 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc) struct drm_i915_private, mm.inactive_shrinker); struct drm_device *dev = dev_priv->dev; - struct drm_i915_gem_object *obj, *next; + struct drm_i915_gem_object *obj; int nr_to_scan = sc->nr_to_scan; int cnt; if (!mutex_trylock(&dev->struct_mutex)) return 0; - /* "fast-path" to count number of available objects */ - if (nr_to_scan == 0) { - cnt = 0; - list_for_each_entry(obj, - &dev_priv->mm.inactive_list, - mm_list) - cnt++; - mutex_unlock(&dev->struct_mutex); - return cnt / 100 * sysctl_vfs_cache_pressure; + if (nr_to_scan) { + nr_to_scan -= i915_gem_purge(dev_priv, nr_to_scan); + if (nr_to_scan > 0) + i915_gem_shrink_all(dev_priv); } -rescan: - /* first scan for clean buffers */ - i915_gem_retire_requests(dev); - - list_for_each_entry_safe(obj, next, - &dev_priv->mm.inactive_list, - mm_list) { - if (i915_gem_object_is_purgeable(obj)) { - if (i915_gem_object_unbind(obj) == 0 && - --nr_to_scan == 0) - break; - } - } - - /* second pass, evict/count anything still on the inactive list */ cnt = 0; - list_for_each_entry_safe(obj, next, - &dev_priv->mm.inactive_list, - mm_list) { - if (nr_to_scan && - i915_gem_object_unbind(obj) == 0) - nr_to_scan--; - else - cnt++; - } + list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) + cnt += obj->base.size >> PAGE_SHIFT; + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) + if (obj->pin_count == 0) + cnt += obj->base.size >> PAGE_SHIFT; - if (nr_to_scan && i915_gpu_is_active(dev)) { - /* - * We are desperate for pages, so as a last resort, wait - * for the GPU to finish and discard whatever we can. - * This has a dramatic impact to reduce the number of - * OOM-killer events whilst running the GPU aggressively. - */ - if (i915_gpu_idle(dev) == 0) - goto rescan; - } mutex_unlock(&dev->struct_mutex); - return cnt / 100 * sysctl_vfs_cache_pressure; + return cnt; } diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 5c2d354cebbd..4aa7ecf77ede 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -221,7 +221,7 @@ static int create_default_context(struct drm_i915_private *dev_priv) * default context. */ dev_priv->ring[RCS].default_context = ctx; - ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false); + ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false, false); if (ret) goto err_destroy; @@ -374,7 +374,7 @@ static int do_switch(struct i915_hw_context *to) if (from_obj == to->obj) return 0; - ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false); + ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index aa308e1337db..43c95307f99e 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -33,7 +33,7 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme struct drm_i915_gem_object *obj = attachment->dmabuf->priv; struct drm_device *dev = obj->base.dev; int npages = obj->base.size / PAGE_SIZE; - struct sg_table *sg = NULL; + struct sg_table *sg; int ret; int nents; @@ -41,10 +41,10 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme if (ret) return ERR_PTR(ret); - if (!obj->pages) { - ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN); - if (ret) - goto out; + ret = i915_gem_object_get_pages_gtt(obj); + if (ret) { + sg = ERR_PTR(ret); + goto out; } /* link the pages into an SG then map the sg */ @@ -89,12 +89,10 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf) goto out_unlock; } - if (!obj->pages) { - ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ERR_PTR(ret); - } + ret = i915_gem_object_get_pages_gtt(obj); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return ERR_PTR(ret); } obj->dma_buf_vmapping = vmap(obj->pages, obj->base.size / PAGE_SIZE, 0, PAGE_KERNEL); @@ -151,6 +149,22 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct * return -EINVAL; } +static int i915_gem_begin_cpu_access(struct dma_buf *dma_buf, size_t start, size_t length, enum dma_data_direction direction) +{ + struct drm_i915_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->base.dev; + int ret; + bool write = (direction == DMA_BIDIRECTIONAL || direction == DMA_TO_DEVICE); + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ret = i915_gem_object_set_to_cpu_domain(obj, write); + mutex_unlock(&dev->struct_mutex); + return ret; +} + static const struct dma_buf_ops i915_dmabuf_ops = { .map_dma_buf = i915_gem_map_dma_buf, .unmap_dma_buf = i915_gem_unmap_dma_buf, @@ -162,6 +176,7 @@ static const struct dma_buf_ops i915_dmabuf_ops = { .mmap = i915_gem_dmabuf_mmap, .vmap = i915_gem_dmabuf_vmap, .vunmap = i915_gem_dmabuf_vunmap, + .begin_cpu_access = i915_gem_begin_cpu_access, }; struct dma_buf *i915_gem_prime_export(struct drm_device *dev, diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 7279c31d4a9a..a2d8acde8550 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -45,7 +45,7 @@ mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind) int i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, unsigned cache_level, - bool mappable) + bool mappable, bool nonblocking) { drm_i915_private_t *dev_priv = dev->dev_private; struct list_head eviction_list, unwind_list; @@ -92,12 +92,16 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, goto found; } + if (nonblocking) + goto none; + /* Now merge in the soon-to-be-expired objects... */ list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { if (mark_free(obj, &unwind_list)) goto found; } +none: /* Nothing found, clean up and bail out! */ while (!list_empty(&unwind_list)) { obj = list_first_entry(&unwind_list, @@ -148,7 +152,7 @@ found: } int -i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) +i915_gem_evict_everything(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj, *next; @@ -160,7 +164,7 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) if (lists_empty) return -ENOSPC; - trace_i915_gem_evict_everything(dev, purgeable_only); + trace_i915_gem_evict_everything(dev); /* The gpu_idle will flush everything in the write domain to the * active list. Then we must move everything off the active list @@ -174,12 +178,9 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) /* 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) { - if (obj->pin_count == 0) - WARN_ON(i915_gem_object_unbind(obj)); - } - } + &dev_priv->mm.inactive_list, mm_list) + if (obj->pin_count == 0) + WARN_ON(i915_gem_object_unbind(obj)); return 0; } diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index afb312ee050c..e6b2205ecf6d 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -95,6 +95,7 @@ eb_destroy(struct eb_objects *eb) static inline int use_cpu_reloc(struct drm_i915_gem_object *obj) { return (obj->base.write_domain == I915_GEM_DOMAIN_CPU || + !obj->map_and_fenceable || obj->cache_level != I915_CACHE_NONE); } @@ -330,7 +331,8 @@ i915_gem_execbuffer_relocate(struct drm_device *dev, return ret; } -#define __EXEC_OBJECT_HAS_FENCE (1<<31) +#define __EXEC_OBJECT_HAS_PIN (1<<31) +#define __EXEC_OBJECT_HAS_FENCE (1<<30) static int need_reloc_mappable(struct drm_i915_gem_object *obj) @@ -340,9 +342,10 @@ need_reloc_mappable(struct drm_i915_gem_object *obj) } static int -pin_and_fence_object(struct drm_i915_gem_object *obj, - struct intel_ring_buffer *ring) +i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj, + struct intel_ring_buffer *ring) { + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; bool need_fence, need_mappable; @@ -354,15 +357,17 @@ pin_and_fence_object(struct drm_i915_gem_object *obj, obj->tiling_mode != I915_TILING_NONE; need_mappable = need_fence || need_reloc_mappable(obj); - ret = i915_gem_object_pin(obj, entry->alignment, need_mappable); + ret = i915_gem_object_pin(obj, entry->alignment, need_mappable, false); if (ret) return ret; + entry->flags |= __EXEC_OBJECT_HAS_PIN; + if (has_fenced_gpu_access) { if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) { ret = i915_gem_object_get_fence(obj); if (ret) - goto err_unpin; + return ret; if (i915_gem_object_pin_fence(obj)) entry->flags |= __EXEC_OBJECT_HAS_FENCE; @@ -371,12 +376,35 @@ pin_and_fence_object(struct drm_i915_gem_object *obj, } } + /* Ensure ppgtt mapping exists if needed */ + if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) { + i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt, + obj, obj->cache_level); + + obj->has_aliasing_ppgtt_mapping = 1; + } + entry->offset = obj->gtt_offset; return 0; +} -err_unpin: - i915_gem_object_unpin(obj); - return ret; +static void +i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj) +{ + struct drm_i915_gem_exec_object2 *entry; + + if (!obj->gtt_space) + return; + + entry = obj->exec_entry; + + if (entry->flags & __EXEC_OBJECT_HAS_FENCE) + i915_gem_object_unpin_fence(obj); + + if (entry->flags & __EXEC_OBJECT_HAS_PIN) + i915_gem_object_unpin(obj); + + entry->flags &= ~(__EXEC_OBJECT_HAS_FENCE | __EXEC_OBJECT_HAS_PIN); } static int @@ -384,11 +412,10 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, struct drm_file *file, struct list_head *objects) { - drm_i915_private_t *dev_priv = ring->dev->dev_private; struct drm_i915_gem_object *obj; - int ret, retry; - bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; struct list_head ordered_objects; + bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; + int retry; INIT_LIST_HEAD(&ordered_objects); while (!list_empty(objects)) { @@ -426,12 +453,12 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, * 2. Bind new objects. * 3. Decrement pin count. * - * This avoid unnecessary unbinding of later objects in order to makr + * This avoid unnecessary unbinding of later objects in order to make * room for the earlier objects *unless* we need to defragment. */ retry = 0; do { - ret = 0; + int ret = 0; /* Unbind any ill-fitting objects or pin. */ list_for_each_entry(obj, objects, exec_list) { @@ -451,7 +478,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, (need_mappable && !obj->map_and_fenceable)) ret = i915_gem_object_unbind(obj); else - ret = pin_and_fence_object(obj, ring); + ret = i915_gem_execbuffer_reserve_object(obj, ring); if (ret) goto err; } @@ -461,77 +488,22 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, if (obj->gtt_space) continue; - ret = pin_and_fence_object(obj, ring); - if (ret) { - int ret_ignore; - - /* This can potentially raise a harmless - * -EINVAL if we failed to bind in the above - * call. It cannot raise -EINTR since we know - * that the bo is freshly bound and so will - * not need to be flushed or waited upon. - */ - ret_ignore = i915_gem_object_unbind(obj); - (void)ret_ignore; - WARN_ON(obj->gtt_space); - break; - } + ret = i915_gem_execbuffer_reserve_object(obj, ring); + if (ret) + goto err; } - /* Decrement pin count for bound objects */ - list_for_each_entry(obj, objects, exec_list) { - struct drm_i915_gem_exec_object2 *entry; +err: /* Decrement pin count for bound objects */ + list_for_each_entry(obj, objects, exec_list) + i915_gem_execbuffer_unreserve_object(obj); - if (!obj->gtt_space) - continue; - - entry = obj->exec_entry; - if (entry->flags & __EXEC_OBJECT_HAS_FENCE) { - i915_gem_object_unpin_fence(obj); - entry->flags &= ~__EXEC_OBJECT_HAS_FENCE; - } - - i915_gem_object_unpin(obj); - - /* ... and ensure ppgtt mapping exist if needed. */ - if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) { - i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt, - obj, obj->cache_level); - - obj->has_aliasing_ppgtt_mapping = 1; - } - } - - if (ret != -ENOSPC || retry > 1) + if (ret != -ENOSPC || retry++) return ret; - /* First attempt, just clear anything that is purgeable. - * Second attempt, clear the entire GTT. - */ - ret = i915_gem_evict_everything(ring->dev, retry == 0); + ret = i915_gem_evict_everything(ring->dev); if (ret) return ret; - - retry++; } while (1); - -err: - list_for_each_entry_continue_reverse(obj, objects, exec_list) { - struct drm_i915_gem_exec_object2 *entry; - - if (!obj->gtt_space) - continue; - - entry = obj->exec_entry; - if (entry->flags & __EXEC_OBJECT_HAS_FENCE) { - i915_gem_object_unpin_fence(obj); - entry->flags &= ~__EXEC_OBJECT_HAS_FENCE; - } - - i915_gem_object_unpin(obj); - } - - return ret; } static int diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 804d65345e2c..18477314d85d 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -351,7 +351,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) intel_gtt_clear_range(dev_priv->mm.gtt_start / PAGE_SIZE, (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE); - list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { i915_gem_clflush_object(obj); i915_gem_gtt_bind_object(obj, obj->cache_level); } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index a61b41a8c607..34dc7158f03c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1070,6 +1070,36 @@ i915_error_first_batchbuffer(struct drm_i915_private *dev_priv, return NULL; } +/* NB: please notice the memset */ +static void i915_get_extra_instdone(struct drm_device *dev, + uint32_t *instdone) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + memset(instdone, 0, sizeof(*instdone) * I915_NUM_INSTDONE_REG); + + switch(INTEL_INFO(dev)->gen) { + case 2: + case 3: + instdone[0] = I915_READ(INSTDONE); + break; + case 4: + case 5: + case 6: + instdone[0] = I915_READ(INSTDONE_I965); + instdone[1] = I915_READ(INSTDONE1); + break; + default: + WARN_ONCE(1, "Unsupported platform\n"); + case 7: + instdone[0] = I915_READ(GEN7_INSTDONE_1); + instdone[1] = I915_READ(GEN7_SC_INSTDONE); + instdone[2] = I915_READ(GEN7_SAMPLER_INSTDONE); + instdone[3] = I915_READ(GEN7_ROW_INSTDONE); + break; + } +} + + static void i915_record_ring_state(struct drm_device *dev, struct drm_i915_error_state *error, struct intel_ring_buffer *ring) @@ -1091,10 +1121,8 @@ static void i915_record_ring_state(struct drm_device *dev, error->ipehr[ring->id] = I915_READ(RING_IPEHR(ring->mmio_base)); error->instdone[ring->id] = I915_READ(RING_INSTDONE(ring->mmio_base)); error->instps[ring->id] = I915_READ(RING_INSTPS(ring->mmio_base)); - if (ring->id == RCS) { - error->instdone1 = I915_READ(INSTDONE1); + if (ring->id == RCS) error->bbaddr = I915_READ64(BB_ADDR); - } } else { error->faddr[ring->id] = I915_READ(DMA_FADD_I8XX); error->ipeir[ring->id] = I915_READ(IPEIR); @@ -1210,6 +1238,11 @@ static void i915_capture_error_state(struct drm_device *dev) error->done_reg = I915_READ(DONE_REG); } + if (INTEL_INFO(dev)->gen == 7) + error->err_int = I915_READ(GEN7_ERR_INT); + + i915_get_extra_instdone(dev, error->extra_instdone); + i915_gem_record_fences(dev, error); i915_gem_record_rings(dev, error); @@ -1221,7 +1254,7 @@ 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.gtt_list, gtt_list) + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) if (obj->pin_count) i++; error->pinned_bo_count = i - error->active_bo_count; @@ -1246,7 +1279,7 @@ static void i915_capture_error_state(struct drm_device *dev) error->pinned_bo_count = capture_pinned_bo(error->pinned_bo, error->pinned_bo_count, - &dev_priv->mm.gtt_list); + &dev_priv->mm.bound_list); do_gettimeofday(&error->time); @@ -1285,24 +1318,26 @@ void i915_destroy_error_state(struct drm_device *dev) static void i915_report_and_clear_eir(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t instdone[I915_NUM_INSTDONE_REG]; u32 eir = I915_READ(EIR); - int pipe; + int pipe, i; if (!eir) return; pr_err("render error detected, EIR: 0x%08x\n", eir); + i915_get_extra_instdone(dev, instdone); + if (IS_G4X(dev)) { if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) { u32 ipeir = I915_READ(IPEIR_I965); pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965)); pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965)); - pr_err(" INSTDONE: 0x%08x\n", - I915_READ(INSTDONE_I965)); + for (i = 0; i < ARRAY_SIZE(instdone); i++) + pr_err(" INSTDONE_%d: 0x%08x\n", i, instdone[i]); pr_err(" INSTPS: 0x%08x\n", I915_READ(INSTPS)); - pr_err(" INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1)); pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965)); I915_WRITE(IPEIR_I965, ipeir); POSTING_READ(IPEIR_I965); @@ -1336,12 +1371,13 @@ static void i915_report_and_clear_eir(struct drm_device *dev) if (eir & I915_ERROR_INSTRUCTION) { pr_err("instruction error\n"); pr_err(" INSTPM: 0x%08x\n", I915_READ(INSTPM)); + for (i = 0; i < ARRAY_SIZE(instdone); i++) + pr_err(" INSTDONE_%d: 0x%08x\n", i, instdone[i]); if (INTEL_INFO(dev)->gen < 4) { u32 ipeir = I915_READ(IPEIR); pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR)); pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR)); - pr_err(" INSTDONE: 0x%08x\n", I915_READ(INSTDONE)); pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD)); I915_WRITE(IPEIR, ipeir); POSTING_READ(IPEIR); @@ -1350,10 +1386,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev) pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965)); pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965)); - pr_err(" INSTDONE: 0x%08x\n", - I915_READ(INSTDONE_I965)); pr_err(" INSTPS: 0x%08x\n", I915_READ(INSTPS)); - pr_err(" INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1)); pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965)); I915_WRITE(IPEIR_I965, ipeir); POSTING_READ(IPEIR_I965); @@ -1668,7 +1701,7 @@ void i915_hangcheck_elapsed(unsigned long data) { struct drm_device *dev = (struct drm_device *)data; drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t acthd[I915_NUM_RINGS], instdone, instdone1; + uint32_t acthd[I915_NUM_RINGS], instdone[I915_NUM_INSTDONE_REG]; struct intel_ring_buffer *ring; bool err = false, idle; int i; @@ -1696,25 +1729,16 @@ void i915_hangcheck_elapsed(unsigned long data) return; } - if (INTEL_INFO(dev)->gen < 4) { - instdone = I915_READ(INSTDONE); - instdone1 = 0; - } else { - instdone = I915_READ(INSTDONE_I965); - instdone1 = I915_READ(INSTDONE1); - } - + i915_get_extra_instdone(dev, instdone); if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 && - dev_priv->last_instdone == instdone && - dev_priv->last_instdone1 == instdone1) { + memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) { if (i915_hangcheck_hung(dev)) return; } else { dev_priv->hangcheck_count = 0; memcpy(dev_priv->last_acthd, acthd, sizeof(acthd)); - dev_priv->last_instdone = instdone; - dev_priv->last_instdone1 = instdone1; + memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone)); } repeat: diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ab8cffe193cd..75dcfa4ec5ce 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -479,6 +479,11 @@ #define IPEIR_I965 0x02064 #define IPEHR_I965 0x02068 #define INSTDONE_I965 0x0206c +#define GEN7_INSTDONE_1 0x0206c +#define GEN7_SC_INSTDONE 0x07100 +#define GEN7_SAMPLER_INSTDONE 0x0e160 +#define GEN7_ROW_INSTDONE 0x0e164 +#define I915_NUM_INSTDONE_REG 4 #define RING_IPEIR(base) ((base)+0x64) #define RING_IPEHR(base) ((base)+0x68) #define RING_INSTDONE(base) ((base)+0x6c) @@ -501,6 +506,8 @@ #define DMA_FADD_I8XX 0x020d0 #define ERROR_GEN6 0x040a0 +#define GEN7_ERR_INT 0x44040 +#define ERR_INT_MMIO_UNCLAIMED (1<<13) /* GM45+ chicken bits -- debug workaround bits that may be required * for various sorts of correct behavior. The top 16 bits of each are @@ -4248,7 +4255,15 @@ #define G4X_HDMIW_HDMIEDID 0x6210C #define IBX_HDMIW_HDMIEDID_A 0xE2050 +#define IBX_HDMIW_HDMIEDID_B 0xE2150 +#define IBX_HDMIW_HDMIEDID(pipe) _PIPE(pipe, \ + IBX_HDMIW_HDMIEDID_A, \ + IBX_HDMIW_HDMIEDID_B) #define IBX_AUD_CNTL_ST_A 0xE20B4 +#define IBX_AUD_CNTL_ST_B 0xE21B4 +#define IBX_AUD_CNTL_ST(pipe) _PIPE(pipe, \ + IBX_AUD_CNTL_ST_A, \ + IBX_AUD_CNTL_ST_B) #define IBX_ELD_BUFFER_SIZE (0x1f << 10) #define IBX_ELD_ADDRESS (0x1f << 5) #define IBX_ELD_ACK (1 << 4) @@ -4257,7 +4272,15 @@ #define IBX_CP_READYB (1 << 1) #define CPT_HDMIW_HDMIEDID_A 0xE5050 +#define CPT_HDMIW_HDMIEDID_B 0xE5150 +#define CPT_HDMIW_HDMIEDID(pipe) _PIPE(pipe, \ + CPT_HDMIW_HDMIEDID_A, \ + CPT_HDMIW_HDMIEDID_B) #define CPT_AUD_CNTL_ST_A 0xE50B4 +#define CPT_AUD_CNTL_ST_B 0xE51B4 +#define CPT_AUD_CNTL_ST(pipe) _PIPE(pipe, \ + CPT_AUD_CNTL_ST_A, \ + CPT_AUD_CNTL_ST_B) #define CPT_AUD_CNTRL_ST2 0xE50C0 /* These are the 4 32-bit write offset registers for each stream @@ -4267,7 +4290,15 @@ #define GEN7_SO_WRITE_OFFSET(n) (0x5280 + (n) * 4) #define IBX_AUD_CONFIG_A 0xe2000 +#define IBX_AUD_CONFIG_B 0xe2100 +#define IBX_AUD_CFG(pipe) _PIPE(pipe, \ + IBX_AUD_CONFIG_A, \ + IBX_AUD_CONFIG_B) #define CPT_AUD_CONFIG_A 0xe5000 +#define CPT_AUD_CONFIG_B 0xe5100 +#define CPT_AUD_CFG(pipe) _PIPE(pipe, \ + CPT_AUD_CONFIG_A, \ + CPT_AUD_CONFIG_B) #define AUD_CONFIG_N_VALUE_INDEX (1 << 29) #define AUD_CONFIG_N_PROG_ENABLE (1 << 28) #define AUD_CONFIG_UPPER_N_SHIFT 20 @@ -4278,6 +4309,54 @@ #define AUD_CONFIG_PIXEL_CLOCK_HDMI (0xf << 16) #define AUD_CONFIG_DISABLE_NCTS (1 << 3) +/* HSW Audio */ +#define HSW_AUD_CONFIG_A 0x65000 /* Audio Configuration Transcoder A */ +#define HSW_AUD_CONFIG_B 0x65100 /* Audio Configuration Transcoder B */ +#define HSW_AUD_CFG(pipe) _PIPE(pipe, \ + HSW_AUD_CONFIG_A, \ + HSW_AUD_CONFIG_B) + +#define HSW_AUD_MISC_CTRL_A 0x65010 /* Audio Misc Control Convert 1 */ +#define HSW_AUD_MISC_CTRL_B 0x65110 /* Audio Misc Control Convert 2 */ +#define HSW_AUD_MISC_CTRL(pipe) _PIPE(pipe, \ + HSW_AUD_MISC_CTRL_A, \ + HSW_AUD_MISC_CTRL_B) + +#define HSW_AUD_DIP_ELD_CTRL_ST_A 0x650b4 /* Audio DIP and ELD Control State Transcoder A */ +#define HSW_AUD_DIP_ELD_CTRL_ST_B 0x651b4 /* Audio DIP and ELD Control State Transcoder B */ +#define HSW_AUD_DIP_ELD_CTRL(pipe) _PIPE(pipe, \ + HSW_AUD_DIP_ELD_CTRL_ST_A, \ + HSW_AUD_DIP_ELD_CTRL_ST_B) + +/* Audio Digital Converter */ +#define HSW_AUD_DIG_CNVT_1 0x65080 /* Audio Converter 1 */ +#define HSW_AUD_DIG_CNVT_2 0x65180 /* Audio Converter 1 */ +#define AUD_DIG_CNVT(pipe) _PIPE(pipe, \ + HSW_AUD_DIG_CNVT_1, \ + HSW_AUD_DIG_CNVT_2) +#define DIP_PORT_SEL_MASK 0x3 + +#define HSW_AUD_EDID_DATA_A 0x65050 +#define HSW_AUD_EDID_DATA_B 0x65150 +#define HSW_AUD_EDID_DATA(pipe) _PIPE(pipe, \ + HSW_AUD_EDID_DATA_A, \ + HSW_AUD_EDID_DATA_B) + +#define HSW_AUD_PIPE_CONV_CFG 0x6507c /* Audio pipe and converter configs */ +#define HSW_AUD_PIN_ELD_CP_VLD 0x650c0 /* Audio ELD and CP Ready Status */ +#define AUDIO_INACTIVE_C (1<<11) +#define AUDIO_INACTIVE_B (1<<7) +#define AUDIO_INACTIVE_A (1<<3) +#define AUDIO_OUTPUT_ENABLE_A (1<<2) +#define AUDIO_OUTPUT_ENABLE_B (1<<6) +#define AUDIO_OUTPUT_ENABLE_C (1<<10) +#define AUDIO_ELD_VALID_A (1<<0) +#define AUDIO_ELD_VALID_B (1<<4) +#define AUDIO_ELD_VALID_C (1<<8) +#define AUDIO_CP_READY_A (1<<1) +#define AUDIO_CP_READY_B (1<<5) +#define AUDIO_CP_READY_C (1<<9) + /* HSW Power Wells */ #define HSW_PWR_WELL_CTL1 0x45400 /* BIOS */ #define HSW_PWR_WELL_CTL2 0x45404 /* Driver */ diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index fe90b3a84a6d..3c4093d91f60 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -214,22 +214,18 @@ TRACE_EVENT(i915_gem_evict, ); TRACE_EVENT(i915_gem_evict_everything, - TP_PROTO(struct drm_device *dev, bool purgeable), - TP_ARGS(dev, purgeable), + TP_PROTO(struct drm_device *dev), + TP_ARGS(dev), TP_STRUCT__entry( __field(u32, dev) - __field(bool, purgeable) ), TP_fast_assign( __entry->dev = dev->primary->index; - __entry->purgeable = purgeable; ), - TP_printk("dev=%d%s", - __entry->dev, - __entry->purgeable ? ", purgeable only" : "") + TP_printk("dev=%d", __entry->dev) ); TRACE_EVENT(i915_gem_ring_dispatch, diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 80bf3112dc1f..236191377b0f 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -547,14 +547,12 @@ intel_crt_detect(struct drm_connector *connector, bool force) return connector->status; /* for pre-945g platforms use load detect */ - if (intel_get_load_detect_pipe(&crt->base, connector, NULL, - &tmp)) { + if (intel_get_load_detect_pipe(connector, NULL, &tmp)) { if (intel_crt_detect_ddc(connector)) status = connector_status_connected; else status = intel_crt_load_detect(crt); - intel_release_load_detect_pipe(&crt->base, connector, - &tmp); + intel_release_load_detect_pipe(connector, &tmp); } else status = connector_status_unknown; @@ -694,7 +692,7 @@ void intel_crt_init(struct drm_device *dev) if (IS_HASWELL(dev)) crt->base.crtc_mask = (1 << 0); else - crt->base.crtc_mask = (1 << 0) | (1 << 1); + crt->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); if (IS_GEN2(dev)) connector->interlace_allowed = 0; diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 958422606bc7..170e3861aa4e 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -713,8 +713,12 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, /* Proper support for digital audio needs a new logic and a new set * of registers, so we leave it for future patch bombing. */ - DRM_DEBUG_DRIVER("HDMI audio on pipe %c not yet supported on DDI\n", + DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n", pipe_name(intel_crtc->pipe)); + + /* write eld */ + DRM_DEBUG_DRIVER("HDMI audio: write eld information\n"); + intel_write_eld(encoder, adjusted_mode); } /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in HDMI mode */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a9ab1aff2c77..778cbb88bddc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1384,7 +1384,7 @@ static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv, enum pipe pipe, int reg) { u32 val = I915_READ(reg); - WARN(hdmi_pipe_enabled(dev_priv, val, pipe), + WARN(hdmi_pipe_enabled(dev_priv, pipe, val), "PCH HDMI (0x%08x) enabled on transcoder %c, should be disabled\n", reg, pipe_name(pipe)); @@ -1404,13 +1404,13 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, reg = PCH_ADPA; val = I915_READ(reg); - WARN(adpa_pipe_enabled(dev_priv, val, pipe), + WARN(adpa_pipe_enabled(dev_priv, pipe, val), "PCH VGA enabled on transcoder %c, should be disabled\n", pipe_name(pipe)); reg = PCH_LVDS; val = I915_READ(reg); - WARN(lvds_pipe_enabled(dev_priv, val, pipe), + WARN(lvds_pipe_enabled(dev_priv, pipe, val), "PCH LVDS enabled on transcoder %c, should be disabled\n", pipe_name(pipe)); @@ -1432,7 +1432,7 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, * * Unfortunately needed by dvo_ns2501 since the dvo depends on it running. */ -void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) +static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) { int reg; u32 val; @@ -1874,7 +1874,7 @@ static void disable_pch_hdmi(struct drm_i915_private *dev_priv, enum pipe pipe, int reg) { u32 val = I915_READ(reg); - if (hdmi_pipe_enabled(dev_priv, val, pipe)) { + if (hdmi_pipe_enabled(dev_priv, pipe, val)) { DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n", reg, pipe); I915_WRITE(reg, val & ~PORT_ENABLE); @@ -1896,12 +1896,12 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv, reg = PCH_ADPA; val = I915_READ(reg); - if (adpa_pipe_enabled(dev_priv, val, pipe)) + if (adpa_pipe_enabled(dev_priv, pipe, val)) I915_WRITE(reg, val & ~ADPA_DAC_ENABLE); reg = PCH_LVDS; val = I915_READ(reg); - if (lvds_pipe_enabled(dev_priv, val, pipe)) { + if (lvds_pipe_enabled(dev_priv, pipe, val)) { DRM_DEBUG_KMS("disable lvds on pipe %d val 0x%08x\n", pipe, val); I915_WRITE(reg, val & ~LVDS_PORT_EN); POSTING_READ(reg); @@ -2709,11 +2709,10 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) DRM_DEBUG_KMS("FDI train done.\n"); } -static void ironlake_fdi_pll_enable(struct drm_crtc *crtc) +static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) { - struct drm_device *dev = crtc->dev; + struct drm_device *dev = intel_crtc->base.dev; 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; @@ -2754,6 +2753,35 @@ static void ironlake_fdi_pll_enable(struct drm_crtc *crtc) } } +static void ironlake_fdi_pll_disable(struct intel_crtc *intel_crtc) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = intel_crtc->pipe; + u32 reg, temp; + + /* Switch from PCDclk to Rawclk */ + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_PCDCLK); + + /* Disable CPU FDI TX PLL */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE); + + POSTING_READ(reg); + udelay(100); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE); + + /* Wait for the clocks to turn off. */ + POSTING_READ(reg); + udelay(100); +} + static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3201,7 +3229,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) is_pch_port = intel_crtc_driving_pch(crtc); if (is_pch_port) - ironlake_fdi_pll_enable(crtc); + ironlake_fdi_pll_enable(intel_crtc); else ironlake_fdi_disable(crtc); @@ -3304,26 +3332,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) /* disable PCH DPLL */ intel_disable_pch_pll(intel_crtc); - /* Switch from PCDclk to Rawclk */ - reg = FDI_RX_CTL(pipe); - temp = I915_READ(reg); - I915_WRITE(reg, temp & ~FDI_PCDCLK); - - /* Disable CPU FDI TX PLL */ - reg = FDI_TX_CTL(pipe); - temp = I915_READ(reg); - I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE); - - POSTING_READ(reg); - udelay(100); - - reg = FDI_RX_CTL(pipe); - temp = I915_READ(reg); - I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE); - - /* Wait for the clocks to turn off. */ - POSTING_READ(reg); - udelay(100); + ironlake_fdi_pll_disable(intel_crtc); intel_crtc->active = false; intel_update_watermarks(dev); @@ -3593,6 +3602,13 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, if (!(adjusted_mode->private_flags & INTEL_MODE_CRTC_TIMINGS_SET)) drm_mode_set_crtcinfo(adjusted_mode, 0); + /* WaPruneModeWithIncorrectHsyncOffset: Cantiga+ cannot handle modes + * with a hsync front porch of 0. + */ + if ((INTEL_INFO(dev)->gen > 4 || IS_G4X(dev)) && + adjusted_mode->hsync_start == adjusted_mode->hdisplay) + return false; + return true; } @@ -5057,6 +5073,91 @@ static void g4x_write_eld(struct drm_connector *connector, I915_WRITE(G4X_AUD_CNTL_ST, i); } +static void haswell_write_eld(struct drm_connector *connector, + struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = connector->dev->dev_private; + uint8_t *eld = connector->eld; + struct drm_device *dev = crtc->dev; + uint32_t eldv; + uint32_t i; + int len; + int pipe = to_intel_crtc(crtc)->pipe; + int tmp; + + int hdmiw_hdmiedid = HSW_AUD_EDID_DATA(pipe); + int aud_cntl_st = HSW_AUD_DIP_ELD_CTRL(pipe); + int aud_config = HSW_AUD_CFG(pipe); + int aud_cntrl_st2 = HSW_AUD_PIN_ELD_CP_VLD; + + + DRM_DEBUG_DRIVER("HDMI: Haswell Audio initialize....\n"); + + /* Audio output enable */ + DRM_DEBUG_DRIVER("HDMI audio: enable codec\n"); + tmp = I915_READ(aud_cntrl_st2); + tmp |= (AUDIO_OUTPUT_ENABLE_A << (pipe * 4)); + I915_WRITE(aud_cntrl_st2, tmp); + + /* Wait for 1 vertical blank */ + intel_wait_for_vblank(dev, pipe); + + /* Set ELD valid state */ + tmp = I915_READ(aud_cntrl_st2); + DRM_DEBUG_DRIVER("HDMI audio: pin eld vld status=0x%8x\n", tmp); + tmp |= (AUDIO_ELD_VALID_A << (pipe * 4)); + I915_WRITE(aud_cntrl_st2, tmp); + tmp = I915_READ(aud_cntrl_st2); + DRM_DEBUG_DRIVER("HDMI audio: eld vld status=0x%8x\n", tmp); + + /* Enable HDMI mode */ + tmp = I915_READ(aud_config); + DRM_DEBUG_DRIVER("HDMI audio: audio conf: 0x%8x\n", tmp); + /* clear N_programing_enable and N_value_index */ + tmp &= ~(AUD_CONFIG_N_VALUE_INDEX | AUD_CONFIG_N_PROG_ENABLE); + I915_WRITE(aud_config, tmp); + + DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe)); + + eldv = AUDIO_ELD_VALID_A << (pipe * 4); + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n"); + eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */ + I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */ + } else + I915_WRITE(aud_config, 0); + + if (intel_eld_uptodate(connector, + aud_cntrl_st2, eldv, + aud_cntl_st, IBX_ELD_ADDRESS, + hdmiw_hdmiedid)) + return; + + i = I915_READ(aud_cntrl_st2); + i &= ~eldv; + I915_WRITE(aud_cntrl_st2, i); + + if (!eld[0]) + return; + + i = I915_READ(aud_cntl_st); + i &= ~IBX_ELD_ADDRESS; + I915_WRITE(aud_cntl_st, i); + i = (i >> 29) & DIP_PORT_SEL_MASK; /* DIP_Port_Select, 0x1 = PortB */ + DRM_DEBUG_DRIVER("port num:%d\n", i); + + len = min_t(uint8_t, eld[2], 21); /* 84 bytes of hw ELD buffer */ + DRM_DEBUG_DRIVER("ELD size %d\n", len); + for (i = 0; i < len; i++) + I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i)); + + i = I915_READ(aud_cntrl_st2); + i |= eldv; + I915_WRITE(aud_cntrl_st2, i); + +} + static void ironlake_write_eld(struct drm_connector *connector, struct drm_crtc *crtc) { @@ -5069,28 +5170,24 @@ static void ironlake_write_eld(struct drm_connector *connector, int aud_config; int aud_cntl_st; int aud_cntrl_st2; + int pipe = to_intel_crtc(crtc)->pipe; if (HAS_PCH_IBX(connector->dev)) { - hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID_A; - aud_config = IBX_AUD_CONFIG_A; - aud_cntl_st = IBX_AUD_CNTL_ST_A; + hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID(pipe); + aud_config = IBX_AUD_CFG(pipe); + aud_cntl_st = IBX_AUD_CNTL_ST(pipe); aud_cntrl_st2 = IBX_AUD_CNTL_ST2; } else { - hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID_A; - aud_config = CPT_AUD_CONFIG_A; - aud_cntl_st = CPT_AUD_CNTL_ST_A; + hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID(pipe); + aud_config = CPT_AUD_CFG(pipe); + aud_cntl_st = CPT_AUD_CNTL_ST(pipe); aud_cntrl_st2 = CPT_AUD_CNTRL_ST2; } - i = to_intel_crtc(crtc)->pipe; - hdmiw_hdmiedid += i * 0x100; - aud_cntl_st += i * 0x100; - aud_config += i * 0x100; - - DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i)); + DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe)); i = I915_READ(aud_cntl_st); - i = (i >> 29) & 0x3; /* DIP_Port_Select, 0x1 = PortB */ + i = (i >> 29) & DIP_PORT_SEL_MASK; /* DIP_Port_Select, 0x1 = PortB */ if (!i) { DRM_DEBUG_DRIVER("Audio directed to unknown port\n"); /* operate blindly on all ports */ @@ -5584,12 +5681,13 @@ mode_fits_in_fbdev(struct drm_device *dev, return fb; } -bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, - struct drm_connector *connector, +bool intel_get_load_detect_pipe(struct drm_connector *connector, struct drm_display_mode *mode, struct intel_load_detect_pipe *old) { struct intel_crtc *intel_crtc; + struct intel_encoder *intel_encoder = + intel_attached_encoder(connector); struct drm_crtc *possible_crtc; struct drm_encoder *encoder = &intel_encoder->base; struct drm_crtc *crtc = NULL; @@ -5615,21 +5713,12 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, if (encoder->crtc) { crtc = encoder->crtc; - intel_crtc = to_intel_crtc(crtc); - old->dpms_mode = intel_crtc->dpms_mode; + old->dpms_mode = connector->dpms; old->load_detect_temp = false; /* Make sure the crtc and connector are running */ - if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) { - struct drm_encoder_helper_funcs *encoder_funcs; - struct drm_crtc_helper_funcs *crtc_funcs; - - crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); - - encoder_funcs = encoder->helper_private; - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); - } + if (connector->dpms != DRM_MODE_DPMS_ON) + connector->funcs->dpms(connector, DRM_MODE_DPMS_ON); return true; } @@ -5657,7 +5746,7 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, connector->encoder = encoder; intel_crtc = to_intel_crtc(crtc); - old->dpms_mode = intel_crtc->dpms_mode; + old->dpms_mode = connector->dpms; old->load_detect_temp = true; old->release_fb = NULL; @@ -5682,33 +5771,34 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n"); if (IS_ERR(crtc->fb)) { DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n"); - crtc->fb = old_fb; - return false; + goto fail; } if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_fb)) { DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); - crtc->fb = old_fb; - return false; + goto fail; } /* let the connector get through one full cycle before testing */ intel_wait_for_vblank(dev, intel_crtc->pipe); return true; +fail: + connector->encoder = NULL; + encoder->crtc = NULL; + crtc->fb = old_fb; + return false; } -void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, - struct drm_connector *connector, +void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_load_detect_pipe *old) { + struct intel_encoder *intel_encoder = + intel_attached_encoder(connector); struct drm_encoder *encoder = &intel_encoder->base; struct drm_device *dev = encoder->dev; - struct drm_crtc *crtc = encoder->crtc; - struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", connector->base.id, drm_get_connector_name(connector), @@ -5716,6 +5806,7 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, if (old->load_detect_temp) { connector->encoder = NULL; + encoder->crtc = NULL; drm_helper_disable_unused_functions(dev); if (old->release_fb) @@ -5725,10 +5816,8 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, } /* Switch crtc and encoder back off if necessary */ - if (old->dpms_mode != DRM_MODE_DPMS_ON) { - encoder_funcs->dpms(encoder, old->dpms_mode); - crtc_funcs->dpms(crtc, old->dpms_mode); - } + if (old->dpms_mode != DRM_MODE_DPMS_ON) + connector->funcs->dpms(connector, old->dpms_mode); } /* Returns the clock of the currently programmed mode of the given pipe. */ @@ -6939,7 +7028,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.write_eld = ironlake_write_eld; } else if (IS_HASWELL(dev)) { dev_priv->display.fdi_link_train = hsw_fdi_link_train; - dev_priv->display.write_eld = ironlake_write_eld; + dev_priv->display.write_eld = haswell_write_eld; } else dev_priv->display.update_wm = NULL; } else if (IS_G4X(dev)) { @@ -7017,21 +7106,16 @@ static struct intel_quirk intel_quirks[] = { /* HP Mini needs pipe A force quirk (LP: #322104) */ { 0x27ae, 0x103c, 0x361a, quirk_pipea_force }, - /* Thinkpad R31 needs pipe A force quirk */ - { 0x3577, 0x1014, 0x0505, quirk_pipea_force }, /* Toshiba Protege R-205, S-209 needs pipe A force quirk */ { 0x2592, 0x1179, 0x0001, quirk_pipea_force }, - /* ThinkPad X30 needs pipe A force quirk (LP: #304614) */ - { 0x3577, 0x1014, 0x0513, quirk_pipea_force }, - /* ThinkPad X40 needs pipe A force quirk */ - /* ThinkPad T60 needs pipe A force quirk (bug #16494) */ { 0x2782, 0x17aa, 0x201a, quirk_pipea_force }, /* 855 & before need to leave pipe A & dpll A up */ { 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, { 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, + { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, /* Lenovo U160 cannot use SSC on LVDS */ { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable }, @@ -7242,7 +7326,7 @@ struct intel_display_error_state { u32 position; u32 base; u32 size; - } cursor[2]; + } cursor[I915_MAX_PIPES]; struct intel_pipe_error_state { u32 conf; @@ -7254,7 +7338,7 @@ struct intel_display_error_state { u32 vtotal; u32 vblank; u32 vsync; - } pipe[2]; + } pipe[I915_MAX_PIPES]; struct intel_plane_error_state { u32 control; @@ -7264,7 +7348,7 @@ struct intel_display_error_state { u32 addr; u32 surface; u32 tile_offset; - } plane[2]; + } plane[I915_MAX_PIPES]; }; struct intel_display_error_state * @@ -7278,7 +7362,7 @@ intel_display_capture_error_state(struct drm_device *dev) if (error == NULL) return NULL; - for (i = 0; i < 2; i++) { + for_each_pipe(i) { error->cursor[i].control = I915_READ(CURCNTR(i)); error->cursor[i].position = I915_READ(CURPOS(i)); error->cursor[i].base = I915_READ(CURBASE(i)); @@ -7311,9 +7395,11 @@ intel_display_print_error_state(struct seq_file *m, struct drm_device *dev, struct intel_display_error_state *error) { + drm_i915_private_t *dev_priv = dev->dev_private; int i; - for (i = 0; i < 2; i++) { + seq_printf(m, "Num Pipes: %d\n", dev_priv->num_pipe); + for_each_pipe(i) { seq_printf(m, "Pipe [%d]:\n", i); seq_printf(m, " CONF: %08x\n", error->pipe[i].conf); seq_printf(m, " SRC: %08x\n", error->pipe[i].source); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index d14b1e39244c..143d19c26752 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -850,10 +850,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, * supposed to be read-only. */ intel_dp->DP = I915_READ(intel_dp->output_reg) & DP_DETECTED; - intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; /* Handle DP bits in common between all three register formats */ - intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; switch (intel_dp->lane_count) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e86b3a20d70b..ee0beb4833fb 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -435,12 +435,10 @@ struct intel_load_detect_pipe { bool load_detect_temp; int dpms_mode; }; -extern bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, - struct drm_connector *connector, +extern bool intel_get_load_detect_pipe(struct drm_connector *connector, struct drm_display_mode *mode, struct intel_load_detect_pipe *old); -extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, - struct drm_connector *connector, +extern void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_load_detect_pipe *old); extern void intelfb_restore(void); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 03dfdff8e003..227551f12d25 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -115,9 +115,9 @@ static void intel_dvo_dpms(struct drm_encoder *encoder, int mode) if (mode == DRM_MODE_DPMS_ON) { I915_WRITE(dvo_reg, temp | DVO_ENABLE); I915_READ(dvo_reg); - intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode); + intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); } else { - intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode); + intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false); I915_WRITE(dvo_reg, temp & ~DVO_ENABLE); I915_READ(dvo_reg); } diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 830d0dd610e1..afd0f30ab882 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -235,54 +235,6 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay, return 0; } -/* Workaround for i830 bug where pipe a must be enable to change control regs */ -static int -i830_activate_pipe_a(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct intel_crtc *crtc; - struct drm_crtc_helper_funcs *crtc_funcs; - struct drm_display_mode vesa_640x480 = { - DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, - 752, 800, 0, 480, 489, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) - }, *mode; - - crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[0]); - if (crtc->dpms_mode == DRM_MODE_DPMS_ON) - return 0; - - /* most i8xx have pipe a forced on, so don't trust dpms mode */ - if (I915_READ(_PIPEACONF) & PIPECONF_ENABLE) - return 0; - - crtc_funcs = crtc->base.helper_private; - if (crtc_funcs->dpms == NULL) - return 0; - - DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n"); - - mode = drm_mode_duplicate(dev, &vesa_640x480); - - if (!drm_crtc_helper_set_mode(&crtc->base, mode, - crtc->base.x, crtc->base.y, - crtc->base.fb)) - return 0; - - crtc_funcs->dpms(&crtc->base, DRM_MODE_DPMS_ON); - return 1; -} - -static void -i830_deactivate_pipe_a(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[0]; - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); -} - /* overlay needs to be disable in OCMD reg */ static int intel_overlay_on(struct intel_overlay *overlay) { @@ -290,17 +242,12 @@ static int intel_overlay_on(struct intel_overlay *overlay) 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; BUG_ON(overlay->active); overlay->active = 1; - if (IS_I830(dev)) { - pipe_a_quirk = i830_activate_pipe_a(dev); - if (pipe_a_quirk < 0) - return pipe_a_quirk; - } + WARN_ON(IS_I830(dev) && !(dev_priv->quirks & QUIRK_PIPEA_FORCE)); request = kzalloc(sizeof(*request), GFP_KERNEL); if (request == NULL) { @@ -322,9 +269,6 @@ static int intel_overlay_on(struct intel_overlay *overlay) ret = intel_overlay_do_wait_request(overlay, request, NULL); out: - if (pipe_a_quirk) - i830_deactivate_pipe_a(dev); - return ret; } @@ -1439,7 +1383,7 @@ void intel_setup_overlay(struct drm_device *dev) } overlay->flip_addr = reg_bo->phys_obj->handle->busaddr; } else { - ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true); + ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true, false); if (ret) { DRM_ERROR("failed to pin overlay register bo\n"); goto out_free_bo; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 9b05f7832dc2..a465debdfcf7 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2138,7 +2138,7 @@ intel_alloc_context_page(struct drm_device *dev) return NULL; } - ret = i915_gem_object_pin(ctx, 4096, true); + ret = i915_gem_object_pin(ctx, 4096, true, false); if (ret) { DRM_ERROR("failed to pin power context: %d\n", ret); goto err_unref; @@ -2372,6 +2372,11 @@ int intel_enable_rc6(const struct drm_device *dev) return i915_enable_rc6; if (INTEL_INFO(dev)->gen == 5) { +#ifdef CONFIG_INTEL_IOMMU + /* Disable rc6 on ilk if VT-d is on. */ + if (intel_iommu_gfx_mapped) + return false; +#endif DRM_DEBUG_DRIVER("Ironlake: only RC6 available\n"); return INTEL_RC6_ENABLE; } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index c828169c73ae..ac93643731aa 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -391,7 +391,7 @@ init_pipe_control(struct intel_ring_buffer *ring) i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); - ret = i915_gem_object_pin(obj, 4096, true); + ret = i915_gem_object_pin(obj, 4096, true, false); if (ret) goto err_unref; @@ -979,7 +979,7 @@ static int init_status_page(struct intel_ring_buffer *ring) i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); - ret = i915_gem_object_pin(obj, 4096, true); + ret = i915_gem_object_pin(obj, 4096, true, false); if (ret != 0) { goto err_unref; } @@ -1036,7 +1036,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, ring->obj = obj; - ret = i915_gem_object_pin(obj, PAGE_SIZE, true); + ret = i915_gem_object_pin(obj, PAGE_SIZE, true, false); if (ret) goto err_unref; diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 1a0bab07699e..95653a508987 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1303,12 +1303,9 @@ intel_tv_detect(struct drm_connector *connector, bool force) if (force) { struct intel_load_detect_pipe tmp; - if (intel_get_load_detect_pipe(&intel_tv->base, connector, - &mode, &tmp)) { + if (intel_get_load_detect_pipe(connector, &mode, &tmp)) { type = intel_tv_detect_type(intel_tv, connector); - intel_release_load_detect_pipe(&intel_tv->base, - connector, - &tmp); + intel_release_load_detect_pipe(connector, &tmp); } else return connector_status_unknown; } else diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index ce9a61179925..b8c00ed33051 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/fb.h> +#include <linux/dma-buf.h> #include "drmP.h" #include "drm.h" @@ -377,16 +378,33 @@ static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb, { struct udl_framebuffer *ufb = to_udl_fb(fb); int i; + int ret = 0; if (!ufb->active_16) return 0; + if (ufb->obj->base.import_attach) { + ret = dma_buf_begin_cpu_access(ufb->obj->base.import_attach->dmabuf, + 0, ufb->obj->base.size, + DMA_FROM_DEVICE); + if (ret) + return ret; + } + for (i = 0; i < num_clips; i++) { - udl_handle_damage(ufb, clips[i].x1, clips[i].y1, + ret = udl_handle_damage(ufb, clips[i].x1, clips[i].y1, clips[i].x2 - clips[i].x1, clips[i].y2 - clips[i].y1); + if (ret) + break; } - return 0; + + if (ufb->obj->base.import_attach) { + dma_buf_end_cpu_access(ufb->obj->base.import_attach->dmabuf, + 0, ufb->obj->base.size, + DMA_FROM_DEVICE); + } + return ret; } static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb) diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index 291ecc145585..47b256375831 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -181,11 +181,6 @@ int udl_gem_vmap(struct udl_gem_object *obj) int ret; if (obj->base.import_attach) { - ret = dma_buf_begin_cpu_access(obj->base.import_attach->dmabuf, - 0, obj->base.size, DMA_BIDIRECTIONAL); - if (ret) - return -EINVAL; - obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf); if (!obj->vmapping) return -ENOMEM; @@ -206,8 +201,6 @@ void udl_gem_vunmap(struct udl_gem_object *obj) { if (obj->base.import_attach) { dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping); - dma_buf_end_cpu_access(obj->base.import_attach->dmabuf, 0, - obj->base.size, DMA_BIDIRECTIONAL); return; } diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index d8a79bf59ae7..05e24d31f329 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -312,6 +312,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_ALIASING_PPGTT 18 #define I915_PARAM_HAS_WAIT_TIMEOUT 19 #define I915_PARAM_HAS_SEMAPHORES 20 +#define I915_PARAM_HAS_PRIME_VMAP_FLUSH 21 typedef struct drm_i915_getparam { int param; |