From 284d952968d60cca156ef0c5efa62592b72264cb Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 6 Jun 2011 17:12:49 -0700 Subject: drm/i915: Call intel_enable_plane from i9xx_crtc_mode_set (again) This change got placed in the ironlake path instead of the 9xx path during a recent code shuffle. Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 81a9059b6a94..aa43e7be6053 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4687,6 +4687,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DSPCNTR(plane), dspcntr); POSTING_READ(DSPCNTR(plane)); + intel_enable_plane(dev_priv, plane, pipe); ret = intel_pipe_set_base(crtc, x, y, old_fb); @@ -5217,8 +5218,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DSPCNTR(plane), dspcntr); POSTING_READ(DSPCNTR(plane)); - if (!HAS_PCH_SPLIT(dev)) - intel_enable_plane(dev_priv, plane, pipe); ret = intel_pipe_set_base(crtc, x, y, old_fb); -- cgit v1.2.3 From a8198eea156df47e0e843ac5c7d4c8774e121c42 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 13 Apr 2011 22:04:09 +0100 Subject: drm/i915: Introduce i915_gem_object_finish_gpu() ... reincarnated from i915_gem_object_flush_gpu(). The semantic difference is that after calling finish_gpu() the object no longer resides in any GPU domain, and so will cause the GPU caches to be invalidated if it is ever used again. Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/i915_gem.c | 29 +++++++++++++++++++---------- drivers/gpu/drm/i915/intel_display.c | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f63ee162f124..4d1a8ae70a33 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1190,7 +1190,7 @@ void i915_gem_clflush_object(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_set_domain(struct drm_i915_gem_object *obj, uint32_t read_domains, uint32_t write_domain); -int __must_check i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj); +int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj); int __must_check i915_gem_init_ringbuffer(struct drm_device *dev); void i915_gem_cleanup_ringbuffer(struct drm_device *dev); void i915_gem_do_init(struct drm_device *dev, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 12d32579b951..6291dcdf5d40 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2165,23 +2165,29 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) return -EINVAL; } + ret = i915_gem_object_finish_gpu(obj); + if (ret == -ERESTARTSYS) + return ret; + /* Continue on if we fail due to EIO, the GPU is hung so we + * should be safe and we need to cleanup or else we might + * cause memory corruption through use-after-free. + */ + /* blow away mappings if mapped through GTT */ i915_gem_release_mmap(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. This will - * also ensure that all pending GPU writes are finished - * before we unbind. + * are flushed when we go to remap it. */ - ret = i915_gem_object_set_to_cpu_domain(obj, 1); + if (ret == 0) + ret = i915_gem_object_set_to_cpu_domain(obj, 1); if (ret == -ERESTARTSYS) return ret; - /* Continue on if we fail due to EIO, the GPU is hung so we - * should be safe and we need to cleanup or else we might - * cause memory corruption through use-after-free. - */ 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; } @@ -3045,11 +3051,11 @@ i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj, } int -i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj) +i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj) { int ret; - if (!obj->active) + if ((obj->base.read_domains & I915_GEM_GPU_DOMAINS) == 0) return 0; if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { @@ -3058,6 +3064,9 @@ i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj) return ret; } + /* Ensure that we invalidate the GPU's caches and TLBs. */ + obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS; + return i915_gem_object_wait_rendering(obj); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index aa43e7be6053..e32fb89b3165 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1971,7 +1971,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, * This should only fail upon a hung GPU, in which case we * can safely continue. */ - ret = i915_gem_object_flush_gpu(obj); + ret = i915_gem_object_finish_gpu(obj); (void) ret; } -- cgit v1.2.3 From b5ffc9bc38a4766d586c3aca6830ed2bd6952e5b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 13 Apr 2011 22:06:03 +0100 Subject: drm/i915: Introduce i915_gem_object_finish_gtt() Like its siblings finish_gpu(), this function clears the object from the GTT domain forcing it to be trigger a domain invalidation should we ever need to use via the GTT again. Note that the most important side-effect of finishing the GTT domain (aside from clearing the tracking read/write domains) is that it imposes an memory barrier so that all accesses are complete before it returns, which is important if you intend to be modifying translation tables shortly afterwards. The second most important side-effect is that it tears down the GTT mappings forcing a page-fault and invalidation on next user access to the object. Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6291dcdf5d40..e78a7ef634d9 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2149,6 +2149,30 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj) return 0; } +static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj) +{ + u32 old_write_domain, old_read_domains; + + if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) + return; + + /* Act a barrier for all accesses through the GTT */ + mb(); + + /* Force a pagefault for domain tracking on next user access */ + i915_gem_release_mmap(obj); + + old_read_domains = obj->base.read_domains; + old_write_domain = obj->base.write_domain; + + obj->base.read_domains &= ~I915_GEM_DOMAIN_GTT; + obj->base.write_domain &= ~I915_GEM_DOMAIN_GTT; + + trace_i915_gem_object_change_domain(obj, + old_read_domains, + old_write_domain); +} + /** * Unbinds an object from the GTT aperture. */ @@ -2173,8 +2197,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) * cause memory corruption through use-after-free. */ - /* blow away mappings if mapped through GTT */ - i915_gem_release_mmap(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 -- cgit v1.2.3 From d5bd144959e639443f387c34989cec7c9efff091 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 14 Apr 2011 06:48:26 +0100 Subject: drm/i915/gtt: Split out i915_gem_gtt_rebind_object() ... in preparation for changing the cache level (and thus the flags upon the PTEs) dynamically. Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_gtt.c | 42 ++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index e46b645773cf..837033cf2ce5 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -29,6 +29,9 @@ #include "i915_trace.h" #include "intel_drv.h" +static void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level); + /* XXX kill agp_type! */ static unsigned int cache_level_to_agp_type(struct drm_device *dev, enum i915_cache_level cache_level) @@ -59,24 +62,8 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE); list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { - unsigned int agp_type = - cache_level_to_agp_type(dev, obj->cache_level); - i915_gem_clflush_object(obj); - - if (dev_priv->mm.gtt->needs_dmar) { - BUG_ON(!obj->sg_list); - - intel_gtt_insert_sg_entries(obj->sg_list, - obj->num_sg, - obj->gtt_space->start >> PAGE_SHIFT, - agp_type); - } else - intel_gtt_insert_pages(obj->gtt_space->start - >> PAGE_SHIFT, - obj->base.size >> PAGE_SHIFT, - obj->pages, - agp_type); + i915_gem_gtt_rebind_object(obj, obj->cache_level); } intel_gtt_chipset_flush(); @@ -110,6 +97,27 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj) return 0; } +static void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level) +{ + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned int agp_type = cache_level_to_agp_type(dev, cache_level); + + if (dev_priv->mm.gtt->needs_dmar) { + BUG_ON(!obj->sg_list); + + intel_gtt_insert_sg_entries(obj->sg_list, + obj->num_sg, + obj->gtt_space->start >> PAGE_SHIFT, + agp_type); + } else + intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT, + obj->base.size >> PAGE_SHIFT, + obj->pages, + agp_type); +} + void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) { intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT, -- cgit v1.2.3 From e4ffd173a1c2f96b43127c2537dd99d89e759bba Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 4 Apr 2011 09:44:39 +0100 Subject: drm/i915: Add an interface to dynamically change the cache level [anholt v2: Don't forget that when going from cached to uncached, we haven't been tracking the write domain from the CPU perspective, since we haven't needed it for GPU coherency.] [ickle v3: We also need to make sure we relinquish any fences on older chipsets and clear the GTT for sane domain tracking.] Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt Reviewed-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 5 +++ drivers/gpu/drm/i915/i915_gem.c | 60 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_gem_gtt.c | 7 ++-- drivers/gpu/drm/i915/intel_ringbuffer.c | 6 ++-- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4d1a8ae70a33..e552aa6bc859 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1223,9 +1223,14 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file); uint32_t i915_gem_get_unfenced_gtt_alignment(struct drm_i915_gem_object *obj); +int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level); + /* i915_gem_gtt.c */ void i915_gem_restore_gtt_mappings(struct drm_device *dev); int __must_check i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj); +void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level); void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj); /* i915_gem_evict.c */ diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e78a7ef634d9..e6915072ba72 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3034,6 +3034,66 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) return 0; } +int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level) +{ + int ret; + + if (obj->cache_level == cache_level) + return 0; + + if (obj->pin_count) { + DRM_DEBUG("can not change the cache level of pinned objects\n"); + return -EBUSY; + } + + if (obj->gtt_space) { + ret = i915_gem_object_finish_gpu(obj); + if (ret) + return ret; + + i915_gem_object_finish_gtt(obj); + + /* Before SandyBridge, you could not use tiling or fence + * registers with snooped memory, so relinquish any fences + * currently pointing to our region in the aperture. + */ + if (INTEL_INFO(obj->base.dev)->gen < 6) { + ret = i915_gem_object_put_fence(obj); + if (ret) + return ret; + } + + i915_gem_gtt_rebind_object(obj, cache_level); + } + + if (cache_level == I915_CACHE_NONE) { + u32 old_read_domains, old_write_domain; + + /* If we're coming from LLC cached, then we haven't + * actually been tracking whether the data is in the + * CPU cache or not, since we only allow one bit set + * in obj->write_domain and have been skipping the clflushes. + * Just set it to the CPU cache for now. + */ + WARN_ON(obj->base.write_domain & ~I915_GEM_DOMAIN_CPU); + WARN_ON(obj->base.read_domains & ~I915_GEM_DOMAIN_CPU); + + old_read_domains = obj->base.read_domains; + old_write_domain = obj->base.write_domain; + + obj->base.read_domains = I915_GEM_DOMAIN_CPU; + obj->base.write_domain = I915_GEM_DOMAIN_CPU; + + trace_i915_gem_object_change_domain(obj, + old_read_domains, + old_write_domain); + } + + obj->cache_level = cache_level; + return 0; +} + /* * Prepare buffer for display plane. Use uninterruptible for possible flush * wait, as in modesetting process we're not supposed to be interrupted. diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 837033cf2ce5..7a709cd8d543 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -29,9 +29,6 @@ #include "i915_trace.h" #include "intel_drv.h" -static void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, - enum i915_cache_level cache_level); - /* XXX kill agp_type! */ static unsigned int cache_level_to_agp_type(struct drm_device *dev, enum i915_cache_level cache_level) @@ -97,8 +94,8 @@ int i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj) return 0; } -static void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, - enum i915_cache_level cache_level) +void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, + enum i915_cache_level cache_level) { struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 95c4b1429935..e9615685a39c 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -236,7 +236,8 @@ init_pipe_control(struct intel_ring_buffer *ring) ret = -ENOMEM; goto err; } - obj->cache_level = I915_CACHE_LLC; + + i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); ret = i915_gem_object_pin(obj, 4096, true); if (ret) @@ -776,7 +777,8 @@ static int init_status_page(struct intel_ring_buffer *ring) ret = -ENOMEM; goto err; } - obj->cache_level = I915_CACHE_LLC; + + i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); ret = i915_gem_object_pin(obj, 4096, true); if (ret != 0) { -- cgit v1.2.3 From c411964209508e32cf36f6512ed339996751f55f Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 29 Mar 2011 16:59:51 -0700 Subject: drm/i915: Mark the cursor and the overlay as being part of the display planes Signed-off-by: Chris Wilson Signed-off-by: Eric Anholt Reviewed-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_overlay.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e32fb89b3165..f79863a9a693 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5440,7 +5440,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, goto fail_locked; } - ret = i915_gem_object_set_to_gtt_domain(obj, 0); + ret = i915_gem_object_set_to_display_plane(obj, NULL); if (ret) { DRM_ERROR("failed to move cursor bo into the GTT\n"); goto fail_unpin; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index a670c006982e..e0903c5f0ca2 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -777,7 +777,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, if (ret != 0) return ret; - ret = i915_gem_object_set_to_gtt_domain(new_bo, 0); + ret = i915_gem_object_set_to_display_plane(new_bo, NULL); if (ret != 0) goto out_unpin; -- cgit v1.2.3 From 2da3b9b940e2a18147422c54ed8b29d01e1ade88 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 14 Apr 2011 09:41:17 +0100 Subject: drm/i915: Combine pinning with setting to the display plane We need to perform a few operations in order to move the object into the display plane (where it can be accessed coherently by the display engine) that are important for future safety to forbid whilst pinned. As a result, we want to need to perform some of the operations before pinning, but some are required once we have been bound into the GTT. So combine the pinning performed by all the callers with set_to_display_plane(), so this complication is contained within the single function. Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 3 ++- drivers/gpu/drm/i915/i915_gem.c | 37 +++++++++++++++++++++++++----------- drivers/gpu/drm/i915/intel_display.c | 18 ++++-------------- drivers/gpu/drm/i915/intel_overlay.c | 6 +----- 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e552aa6bc859..8a9fd9177860 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1209,7 +1209,8 @@ int __must_check i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write); int __must_check -i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj, +i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, + u32 alignment, struct intel_ring_buffer *pipelined); int i915_gem_attach_phys_object(struct drm_device *dev, struct drm_i915_gem_object *obj, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e6915072ba72..8a439f0c21f5 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3095,40 +3095,55 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, } /* - * Prepare buffer for display plane. Use uninterruptible for possible flush - * wait, as in modesetting process we're not supposed to be interrupted. + * Prepare buffer for display plane (scanout, cursors, etc). + * Can be called from an uninterruptible phase (modesetting) and allows + * any flushes to be pipelined (for pageflips). + * + * For the display plane, we want to be in the GTT but out of any write + * domains. So in many ways this looks like set_to_gtt_domain() apart from the + * ability to pipeline the waits, pinning and any additional subtleties + * that may differentiate the display plane from ordinary buffers. */ int -i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj, +i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, + u32 alignment, struct intel_ring_buffer *pipelined) { - uint32_t old_read_domains; + u32 old_read_domains, old_write_domain; int ret; - /* Not valid to be called on unbound objects. */ - if (obj->gtt_space == NULL) - return -EINVAL; - ret = i915_gem_object_flush_gpu_write_domain(obj); if (ret) return ret; - - /* Currently, we are always called from an non-interruptible context. */ if (pipelined != obj->ring) { ret = i915_gem_object_wait_rendering(obj); if (ret) return ret; } + /* As the user may map the buffer once pinned in the display plane + * (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); + if (ret) + return ret; + i915_gem_object_flush_cpu_write_domain(obj); + old_write_domain = obj->base.write_domain; old_read_domains = obj->base.read_domains; + + /* It should now be out of any other write domains, and we can update + * the domain values for our changes. + */ + BUG_ON((obj->base.write_domain & ~I915_GEM_DOMAIN_GTT) != 0); obj->base.read_domains |= I915_GEM_DOMAIN_GTT; trace_i915_gem_object_change_domain(obj, old_read_domains, - obj->base.write_domain); + old_write_domain); return 0; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f79863a9a693..86a3ec1469ba 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1812,14 +1812,10 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev, } dev_priv->mm.interruptible = false; - ret = i915_gem_object_pin(obj, alignment, true); + ret = i915_gem_object_pin_to_display_plane(obj, alignment, pipelined); if (ret) goto err_interruptible; - ret = i915_gem_object_set_to_display_plane(obj, pipelined); - if (ret) - goto err_unpin; - /* Install a fence for tiled scan-out. Pre-i965 always needs a * fence, whereas 965+ only requires a fence if using * framebuffer compression. For simplicity, we always install @@ -5434,21 +5430,15 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, goto fail_locked; } - ret = i915_gem_object_pin(obj, PAGE_SIZE, true); - if (ret) { - DRM_ERROR("failed to pin cursor bo\n"); - goto fail_locked; - } - - ret = i915_gem_object_set_to_display_plane(obj, NULL); + ret = i915_gem_object_pin_to_display_plane(obj, 0, NULL); if (ret) { DRM_ERROR("failed to move cursor bo into the GTT\n"); - goto fail_unpin; + goto fail_locked; } ret = i915_gem_object_put_fence(obj); if (ret) { - DRM_ERROR("failed to move cursor bo into the GTT\n"); + DRM_ERROR("failed to release fence for cursor"); goto fail_unpin; } diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index e0903c5f0ca2..fcf6fcb0b482 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -773,14 +773,10 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay, if (ret != 0) return ret; - ret = i915_gem_object_pin(new_bo, PAGE_SIZE, true); + ret = i915_gem_object_pin_to_display_plane(new_bo, 0, NULL); if (ret != 0) return ret; - ret = i915_gem_object_set_to_display_plane(new_bo, NULL); - if (ret != 0) - goto out_unpin; - ret = i915_gem_object_put_fence(new_bo); if (ret) goto out_unpin; -- cgit v1.2.3 From a7ef0640d984e265393a76aa08a09febd3e7ce34 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 29 Mar 2011 16:59:54 -0700 Subject: drm/i915: Use the uncached domain for the display planes The simplest and common method for ensuring scanout coherency on all chipsets is to mark the scanout buffers as uncached (and for userspace to remember to flush the render cache every so often). We can improve upon this for later generations by marking scanout objects as GFDT and only flush those cachelines when required. However, we start simple. [v2: Move the set to uncached above the clflush. Otherwise, we'd skip the clflush and try to scan out data that was still sitting in the cache.] Signed-off-by: Eric Anholt Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 8a439f0c21f5..fb0538358c34 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3122,6 +3122,19 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, return ret; } + /* The display engine is not coherent with the LLC cache on gen6. As + * a result, we make sure that the pinning that is about to occur is + * done with uncached PTEs. This is lowest common denominator for all + * chipsets. + * + * However for gen6+, we could do better by using the GFDT bit instead + * of uncaching, which would allow us to flush all the LLC-cached data + * with that bit in the PTE to main memory with just one PIPE_CONTROL. + */ + ret = i915_gem_object_set_cache_level(obj, I915_CACHE_NONE); + if (ret) + return ret; + /* As the user may map the buffer once pinned in the display plane * (e.g. libkms for the bootup splash), we have to ensure that we * always use map_and_fenceable for all scanout buffers. -- cgit v1.2.3 From a18711120764dd96ed2ee6a4d436c448542bad77 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 29 Mar 2011 16:59:55 -0700 Subject: drm/i915: Use the LLC mode on gen6 for everything but display. Improves full-screen openarena on my laptop 20.3% +/- 4.0% (n=3) Improves 800x600 nexuiz on my laptop 12.3% +/- 0.1% (n=3) We have more room to improve with doing LLC caching for display using GFDT, and in doing LLC+MLC caching, but this was an easy performance win and incremental improvement toward those two. Signed-off-by: Eric Anholt Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index fb0538358c34..cb1f61dba5eb 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3700,7 +3700,23 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, obj->base.write_domain = I915_GEM_DOMAIN_CPU; obj->base.read_domains = I915_GEM_DOMAIN_CPU; - obj->cache_level = I915_CACHE_NONE; + if (IS_GEN6(dev)) { + /* On Gen6, we can have the GPU use the LLC (the CPU + * cache) for about a 10% performance improvement + * compared to uncached. Graphics requests other than + * display scanout are coherent with the CPU in + * accessing this cache. This means in this mode we + * don't need to clflush on the CPU side, and on the + * GPU side we only need to flush internal caches to + * get data visible to the CPU. + * + * However, we maintain the display planes as UC, and so + * need to rebind when first used as such. + */ + obj->cache_level = I915_CACHE_LLC; + } 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); -- cgit v1.2.3 From b97c3d9c1655522be3adc5ae1aa153a18467e924 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 24 Jun 2011 21:02:59 -0700 Subject: drm/i915: i915_gem_object_finish_gtt must always release gtt mmap Even if the object is no longer in the GTT domain, there may still be a user space mapping which needs to be released. Without this fix, render-based text (mostly in firefox) would occasionally get corrupted when the system was under load. Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_gem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b29e0f2b780a..6026817372da 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2155,15 +2155,15 @@ static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj) { u32 old_write_domain, old_read_domains; - if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) - return; - /* Act a barrier for all accesses through the GTT */ mb(); /* Force a pagefault for domain tracking on next user access */ i915_gem_release_mmap(obj); + if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0) + return; + old_read_domains = obj->base.read_domains; old_write_domain = obj->base.write_domain; -- cgit v1.2.3 From 1e5216e43846b7758b2a04b3612c475608a4b708 Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Mon, 27 Jun 2011 16:18:20 -0700 Subject: drm/i915: more struct_mutex locking When auditing the locking in i915_gem.c (for a prospective change which I then abandoned), I noticed two places where struct_mutex is not held across GEM object manipulations that would usually require it. Since one is in initial setup and the other in driver unload, I'm guessing the mutex is not required for either; but post a patch in case it is. Signed-off-by: Hugh Dickins Cc: Chris Wilson Cc: Keith Packard Signed-off-by: Andrew Morton Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_dma.c | 3 +-- drivers/gpu/drm/i915/intel_overlay.c | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0239e9974bf2..2b79588541e7 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -2182,9 +2182,8 @@ int i915_driver_unload(struct drm_device *dev) /* Flush any outstanding unpin_work. */ flush_workqueue(dev_priv->wq); - i915_gem_free_all_phys_object(dev); - mutex_lock(&dev->struct_mutex); + i915_gem_free_all_phys_object(dev); i915_gem_cleanup_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); if (I915_HAS_FBC(dev) && i915_powersave) diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index fcf6fcb0b482..cffd3edd9bb4 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1412,6 +1412,8 @@ void intel_setup_overlay(struct drm_device *dev) goto out_free; overlay->reg_bo = reg_bo; + mutex_lock(&dev->struct_mutex); + if (OVERLAY_NEEDS_PHYSICAL(dev)) { ret = i915_gem_attach_phys_object(dev, reg_bo, I915_GEM_PHYS_OVERLAY_REGS, @@ -1436,6 +1438,8 @@ void intel_setup_overlay(struct drm_device *dev) } } + mutex_unlock(&dev->struct_mutex); + /* init all values */ overlay->color_key = 0x0101fe; overlay->brightness = -19; @@ -1460,6 +1464,7 @@ out_unpin_bo: i915_gem_object_unpin(reg_bo); out_free_bo: drm_gem_object_unreference(®_bo->base); + mutex_unlock(&dev->struct_mutex); out_free: kfree(overlay); return; -- cgit v1.2.3 From 3d73710880afa3d61cf57b5d4eb192e812eb7e4f Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 28 Jun 2011 10:59:12 -0700 Subject: cpufreq: expose a cpufreq_quick_get_max routine This allows drivers and other code to get the max reported CPU frequency. Initial use is for scaling ring frequency with GPU frequency in the i915 driver. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/cpufreq/cpufreq.c | 20 ++++++++++++++++++++ include/linux/cpufreq.h | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 0a5bea9e3585..987a165ede26 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1199,6 +1199,26 @@ unsigned int cpufreq_quick_get(unsigned int cpu) } EXPORT_SYMBOL(cpufreq_quick_get); +/** + * cpufreq_quick_get_max - get the max reported CPU frequency for this CPU + * @cpu: CPU number + * + * Just return the max possible frequency for a given CPU. + */ +unsigned int cpufreq_quick_get_max(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + unsigned int ret_freq = 0; + + if (policy) { + ret_freq = policy->max; + cpufreq_cpu_put(policy); + } + + return ret_freq; +} +EXPORT_SYMBOL(cpufreq_quick_get_max); + static unsigned int __cpufreq_get(unsigned int cpu) { diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 11be48e0d168..6216115c7789 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -324,11 +324,16 @@ static inline unsigned int cpufreq_get(unsigned int cpu) /* query the last known CPU freq (in kHz). If zero, cpufreq couldn't detect it */ #ifdef CONFIG_CPU_FREQ unsigned int cpufreq_quick_get(unsigned int cpu); +unsigned int cpufreq_quick_get_max(unsigned int cpu); #else static inline unsigned int cpufreq_quick_get(unsigned int cpu) { return 0; } +static inline unsigned int cpufreq_quick_get_max(unsigned int cpu) +{ + return 0; +} #endif -- cgit v1.2.3 From 23b2f8bb92feb83127679c53633def32d3108e70 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 28 Jun 2011 13:04:16 -0700 Subject: drm/i915: load a ring frequency scaling table v3 The ring frequency scaling table tells the PCU to treat certain GPU frequencies as if they were a given CPU frequency for purposes of scaling the ring frequency. Normally the PCU will scale the ring frequency based on the CPU P-state, but with the table present, it will also take the GPU frequency into account. The main downside of keeping the ring frequency high while the CPU is at a low frequency (or asleep altogether) is increased power consumption. But then if you're keeping your GPU busy, you probably want the extra performance. v2: - add units to debug table header (from Eric) - use tsc_khz as a fallback if the cpufreq driver doesn't give us a freq (from Chris) v3: - fix comments & debug output - remove unneeded force wake get/put Reviewed-by: Ben Widawsky Tested-by: Eric Anholt Reviewed-by: Eric Anholt Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_debugfs.c | 39 ++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_reg.h | 4 ++- drivers/gpu/drm/i915/i915_suspend.c | 4 ++- drivers/gpu/drm/i915/intel_display.c | 58 +++++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 1 + 5 files changed, 103 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 4d46441cbe2d..8a5a032ec696 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1123,6 +1123,44 @@ static int i915_emon_status(struct seq_file *m, void *unused) return 0; } +static int i915_ring_freq_table(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + int gpu_freq, ia_freq; + + if (!IS_GEN6(dev)) { + seq_printf(m, "unsupported on this chipset\n"); + return 0; + } + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + + seq_printf(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\n"); + + for (gpu_freq = dev_priv->min_delay; gpu_freq <= dev_priv->max_delay; + gpu_freq++) { + I915_WRITE(GEN6_PCODE_DATA, gpu_freq); + I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | + GEN6_PCODE_READ_MIN_FREQ_TABLE); + if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & + GEN6_PCODE_READY) == 0, 10)) { + DRM_ERROR("pcode read of freq table timed out\n"); + continue; + } + ia_freq = I915_READ(GEN6_PCODE_DATA); + seq_printf(m, "%d\t\t%d\n", gpu_freq * 50, ia_freq * 100); + } + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + static int i915_gfxec(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *) m->private; @@ -1426,6 +1464,7 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_inttoext_table", i915_inttoext_table, 0}, {"i915_drpc_info", i915_drpc_info, 0}, {"i915_emon_status", i915_emon_status, 0}, + {"i915_ring_freq_table", i915_ring_freq_table, 0}, {"i915_gfxec", i915_gfxec, 0}, {"i915_fbc_status", i915_fbc_status, 0}, {"i915_sr_status", i915_sr_status, 0}, diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5d5def756c9e..4a446b116e6a 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3434,7 +3434,9 @@ #define GEN6_PCODE_MAILBOX 0x138124 #define GEN6_PCODE_READY (1<<31) #define GEN6_READ_OC_PARAMS 0xc -#define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x9 +#define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x8 +#define GEN6_PCODE_READ_MIN_FREQ_TABLE 0x9 #define GEN6_PCODE_DATA 0x138128 +#define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 #endif /* _I915_REG_H_ */ diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index e8152d23d5b6..6fbd997f5a6c 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -875,8 +875,10 @@ int i915_restore_state(struct drm_device *dev) intel_init_emon(dev); } - if (IS_GEN6(dev)) + if (IS_GEN6(dev)) { gen6_enable_rps(dev_priv); + gen6_update_ring_freq(dev_priv); + } /* Cache mode state */ I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e58627f580c6..804ac4d6cb48 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -24,6 +24,7 @@ * Eric Anholt */ +#include #include #include #include @@ -7273,6 +7274,59 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) mutex_unlock(&dev_priv->dev->struct_mutex); } +void gen6_update_ring_freq(struct drm_i915_private *dev_priv) +{ + int min_freq = 15; + int gpu_freq, ia_freq, max_ia_freq; + int scaling_factor = 180; + + max_ia_freq = cpufreq_quick_get_max(0); + /* + * Default to measured freq if none found, PCU will ensure we don't go + * over + */ + if (!max_ia_freq) + max_ia_freq = tsc_khz; + + /* Convert from kHz to MHz */ + max_ia_freq /= 1000; + + mutex_lock(&dev_priv->dev->struct_mutex); + + /* + * For each potential GPU frequency, load a ring frequency we'd like + * to use for memory access. We do this by specifying the IA frequency + * the PCU should use as a reference to determine the ring frequency. + */ + for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay; + gpu_freq--) { + int diff = dev_priv->max_delay - gpu_freq; + + /* + * For GPU frequencies less than 750MHz, just use the lowest + * ring freq. + */ + if (gpu_freq < min_freq) + ia_freq = 800; + else + ia_freq = max_ia_freq - ((diff * scaling_factor) / 2); + ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100); + + I915_WRITE(GEN6_PCODE_DATA, + (ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) | + gpu_freq); + I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | + GEN6_PCODE_WRITE_MIN_FREQ_TABLE); + if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & + GEN6_PCODE_READY) == 0, 10)) { + DRM_ERROR("pcode write of freq table timed out\n"); + continue; + } + } + + mutex_unlock(&dev_priv->dev->struct_mutex); +} + static void ironlake_init_clock_gating(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -7916,8 +7970,10 @@ void intel_modeset_init(struct drm_device *dev) intel_init_emon(dev); } - if (IS_GEN6(dev)) + if (IS_GEN6(dev)) { gen6_enable_rps(dev_priv); + gen6_update_ring_freq(dev_priv); + } INIT_WORK(&dev_priv->idle_work, intel_idle_update); setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9ffa61eb4d7e..8ac3bd8b6faa 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -317,6 +317,7 @@ extern void intel_enable_clock_gating(struct drm_device *dev); extern void ironlake_enable_drps(struct drm_device *dev); extern void ironlake_disable_drps(struct drm_device *dev); extern void gen6_enable_rps(struct drm_i915_private *dev_priv); +extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv); extern void gen6_disable_rps(struct drm_device *dev); extern void intel_init_emon(struct drm_device *dev); -- cgit v1.2.3 From 3e0dc6b01f5301d63046f6deddde2c7f5c57d67a Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Wed, 29 Jun 2011 10:26:42 -0700 Subject: drm/i915: hangcheck disable parameter Provide a parameter to disable hanghcheck. This is useful mostly for developers trying to debug known problems, and probably should not be touched by normal users. Reviewed-by: Chris Wilson Signed-off-by: Ben Widawsky Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.c | 3 +++ drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 7 +++++-- drivers/gpu/drm/i915/i915_irq.c | 13 +++++++++---- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 609358faaa90..b54f7d9b173a 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -70,6 +70,9 @@ module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600); static bool i915_try_reset = true; module_param_named(reset, i915_try_reset, bool, 0600); +bool i915_enable_hangcheck = true; +module_param_named(enable_hangcheck, i915_enable_hangcheck, bool, 0644); + static struct drm_driver driver; extern int intel_agp_enabled; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 01affb63be29..e0f9ca3e5ff8 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -994,6 +994,7 @@ extern unsigned int i915_panel_use_ssc; extern int i915_vbt_sdvo_panel_type; extern unsigned int i915_enable_rc6; extern unsigned int i915_enable_fbc; +extern bool i915_enable_hangcheck; extern int i915_suspend(struct drm_device *dev, pm_message_t state); extern int i915_resume(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 71cc8a353a78..8b670e7ee404 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1779,8 +1779,11 @@ i915_add_request(struct intel_ring_buffer *ring, ring->outstanding_lazy_request = false; if (!dev_priv->mm.suspended) { - mod_timer(&dev_priv->hangcheck_timer, - jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); + if (i915_enable_hangcheck) { + mod_timer(&dev_priv->hangcheck_timer, + jiffies + + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); + } if (was_empty) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index ae2b49969b99..0b0de5239ad5 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -361,10 +361,12 @@ static void notify_ring(struct drm_device *dev, ring->irq_seqno = seqno; wake_up_all(&ring->irq_queue); - - dev_priv->hangcheck_count = 0; - mod_timer(&dev_priv->hangcheck_timer, - jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); + if (i915_enable_hangcheck) { + dev_priv->hangcheck_count = 0; + mod_timer(&dev_priv->hangcheck_timer, + jiffies + + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); + } } static void gen6_pm_rps_work(struct work_struct *work) @@ -1664,6 +1666,9 @@ void i915_hangcheck_elapsed(unsigned long data) uint32_t acthd, instdone, instdone1; bool err = false; + if (!i915_enable_hangcheck) + return; + /* If all work is done then ACTHD clearly hasn't advanced. */ if (i915_hangcheck_ring_idle(&dev_priv->ring[RCS], &err) && i915_hangcheck_ring_idle(&dev_priv->ring[VCS], &err) && -- cgit v1.2.3 From 1c70c0cebd1295a42fec75045b8a6b4419cedef3 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 29 Jun 2011 13:34:36 -0700 Subject: drm/i915: enable ring freq scaling, RC6 and graphics turbo on Ivy Bridge v3 They use the same register interfaces, so we can simply enable the existing code on IVB. v2: - resolve conflict with ring freq scaling, we can enable it too v3: - resolve conflict again, this time on drm-intel-next Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_debugfs.c | 4 ++-- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 8a5a032ec696..ed627307ab51 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -865,7 +865,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused) MEMSTAT_VID_SHIFT); seq_printf(m, "Current P-state: %d\n", (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT); - } else if (IS_GEN6(dev)) { + } else if (IS_GEN6(dev) || IS_GEN7(dev)) { u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS); u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); @@ -1131,7 +1131,7 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused) int ret; int gpu_freq, ia_freq; - if (!IS_GEN6(dev)) { + if (!(IS_GEN6(dev) || IS_GEN7(dev))) { seq_printf(m, "unsupported on this chipset\n"); return 0; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 804ac4d6cb48..823b8d99d9e6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7970,7 +7970,7 @@ void intel_modeset_init(struct drm_device *dev) intel_init_emon(dev); } - if (IS_GEN6(dev)) { + if (IS_GEN6(dev) || IS_GEN7(dev)) { gen6_enable_rps(dev_priv); gen6_update_ring_freq(dev_priv); } @@ -8014,7 +8014,7 @@ void intel_modeset_cleanup(struct drm_device *dev) if (IS_IRONLAKE_M(dev)) ironlake_disable_drps(dev); - if (IS_GEN6(dev)) + if (IS_GEN6(dev) || IS_GEN7(dev)) gen6_disable_rps(dev); if (IS_IRONLAKE_M(dev)) -- cgit v1.2.3 From 5d4fac9716988dc7a26dddcd994f4dc7ee651e3c Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:19 -0700 Subject: drm/i915: don't set SDVO color range on ILK+ These bits are reserved on ILK+ (ILK+ provides this feature in the transcoder and pipe configuration instead, which we already set). Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_hdmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index aa0a8e83142e..1b72aa42c2f9 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -124,7 +124,8 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, u32 sdvox; sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE; - sdvox |= intel_hdmi->color_range; + if (!HAS_PCH_SPLIT(dev)) + sdvox |= intel_hdmi->color_range; if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) sdvox |= SDVO_VSYNC_ACTIVE_HIGH; if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) -- cgit v1.2.3 From e9bcff5c0328f6edd3cbdd91783b23b5756f0880 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:20 -0700 Subject: drm/i915: don't set transcoder bpc on CougarPoint This prevents us from setting reserved or incorrect bits on CougarPoint. Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 823b8d99d9e6..c675f9f27d9c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1158,12 +1158,15 @@ static void intel_enable_transcoder(struct drm_i915_private *dev_priv, reg = TRANSCONF(pipe); val = I915_READ(reg); - /* - * make the BPC in transcoder be consistent with - * that in pipeconf reg. - */ - val &= ~PIPE_BPC_MASK; - val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK; + + if (HAS_PCH_IBX(dev_priv->dev)) { + /* + * make the BPC in transcoder be consistent with + * that in pipeconf reg. + */ + val &= ~PIPE_BPC_MASK; + val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK; + } I915_WRITE(reg, val | TRANS_ENABLE); if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100)) DRM_ERROR("failed to enable transcoder %d\n", pipe); -- cgit v1.2.3 From 9325c9f088c42e3e07475d4f733ee6539ebf9c0b Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:21 -0700 Subject: drm/i915: set bpc for DP transcoder This may not be the default value, so pull the bpc out of the pipe reg and write it to the DP transcoder so proper dithering and signaling occurs. Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c675f9f27d9c..ce3666d53f57 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2622,6 +2622,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) /* For PCH DP, enable TRANS_DP_CTL */ if (HAS_PCH_CPT(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) >> 5; reg = TRANS_DP_CTL(pipe); temp = I915_READ(reg); temp &= ~(TRANS_DP_PORT_SEL_MASK | @@ -2629,7 +2630,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) TRANS_DP_BPC_MASK); temp |= (TRANS_DP_OUTPUT_ENABLE | TRANS_DP_ENH_FRAMING); - temp |= TRANS_DP_8BPC; + temp |= bpc << 9; /* same format but at 11:9 */ if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC) temp |= TRANS_DP_HSYNC_ACTIVE_HIGH; -- cgit v1.2.3 From 5a3542041bf85a65633ed203c3782492116ebb94 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:22 -0700 Subject: drm/i915: split out Ironlake pipe bpp picking code Figuring out which pipe bpp to use is a bit painful. It depends on both the encoder and display configuration attached to a pipe. For instance, to drive a 24bpp framebuffer out to an 18bpp panel, we need to use 6bpc on the pipe but also enable dithering. But driving that same framebuffer to a DisplayPort output on another pipe means using 8bpc and no dithering. So split out and enhance the code to handle the various cases, returning an appropriate pipe bpp as well as whether dithering should be enabled. Save the resulting pipe bpp in the intel_crtc struct for use by encoders in calculating bandwidth requirements (defaults to 24bpp on pre-ILK). Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 194 +++++++++++++++++++++++++++-------- drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 153 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ce3666d53f57..7c5d28fd5f36 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4309,6 +4309,133 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) return dev_priv->lvds_use_ssc && i915_panel_use_ssc; } +/** + * intel_choose_pipe_bpp_dither - figure out what color depth the pipe should send + * @crtc: CRTC structure + * + * A pipe may be connected to one or more outputs. Based on the depth of the + * attached framebuffer, choose a good color depth to use on the pipe. + * + * If possible, match the pipe depth to the fb depth. In some cases, this + * isn't ideal, because the connected output supports a lesser or restricted + * set of depths. Resolve that here: + * LVDS typically supports only 6bpc, so clamp down in that case + * HDMI supports only 8bpc or 12bpc, so clamp to 8bpc with dither for 10bpc + * Displays may support a restricted set as well, check EDID and clamp as + * appropriate. + * + * RETURNS: + * Dithering requirement (i.e. false if display bpc and pipe bpc match, + * true if they don't match). + */ +static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, + unsigned int *pipe_bpp) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_encoder *encoder; + struct drm_connector *connector; + unsigned int display_bpc = UINT_MAX, bpc; + + /* Walk the encoders & connectors on this crtc, get min bpc */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + + if (encoder->crtc != crtc) + continue; + + if (intel_encoder->type == INTEL_OUTPUT_LVDS) { + unsigned int lvds_bpc; + + if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == + LVDS_A3_POWER_UP) + lvds_bpc = 8; + else + lvds_bpc = 6; + + if (lvds_bpc < display_bpc) { + DRM_DEBUG_DRIVER("clamping display bpc (was %d) to LVDS (%d)\n", display_bpc, lvds_bpc); + display_bpc = lvds_bpc; + } + continue; + } + + if (intel_encoder->type == INTEL_OUTPUT_EDP) { + /* Use VBT settings if we have an eDP panel */ + unsigned int edp_bpc = dev_priv->edp.bpp / 3; + + if (edp_bpc < display_bpc) { + DRM_DEBUG_DRIVER("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc); + display_bpc = edp_bpc; + } + continue; + } + + /* Not one of the known troublemakers, check the EDID */ + list_for_each_entry(connector, &dev->mode_config.connector_list, + head) { + if (connector->encoder != encoder) + continue; + + if (connector->display_info.bpc < display_bpc) { + DRM_DEBUG_DRIVER("clamping display bpc (was %d) to EDID reported max of %d\n", display_bpc, connector->display_info.bpc); + display_bpc = connector->display_info.bpc; + } + } + + /* + * HDMI is either 12 or 8, so if the display lets 10bpc sneak + * through, clamp it down. (Note: >12bpc will be caught below.) + */ + if (intel_encoder->type == INTEL_OUTPUT_HDMI) { + if (display_bpc > 8 && display_bpc < 12) { + DRM_DEBUG_DRIVER("forcing bpc to 12 for HDMI\n"); + display_bpc = 12; + } else { + DRM_DEBUG_DRIVER("forcing bpc to 8 for HDMI\n"); + display_bpc = 8; + } + } + } + + /* + * We could just drive the pipe at the highest bpc all the time and + * enable dithering as needed, but that costs bandwidth. So choose + * the minimum value that expresses the full color range of the fb but + * also stays within the max display bpc discovered above. + */ + + switch (crtc->fb->depth) { + case 8: + bpc = 8; /* since we go through a colormap */ + break; + case 15: + case 16: + bpc = 6; /* min is 18bpp */ + break; + case 24: + bpc = min((unsigned int)8, display_bpc); + break; + case 30: + bpc = min((unsigned int)10, display_bpc); + break; + case 48: + bpc = min((unsigned int)12, display_bpc); + break; + default: + DRM_DEBUG("unsupported depth, assuming 24 bits\n"); + bpc = min((unsigned int)8, display_bpc); + break; + } + + DRM_DEBUG_DRIVER("setting pipe bpc to %d (max display bpc %d)\n", + bpc, display_bpc); + + *pipe_bpp = bpc * 3; + + return display_bpc != bpc; +} + static int i9xx_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -4721,7 +4848,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, struct fdi_m_n m_n = {0}; u32 temp; u32 lvds_sync = 0; - int target_clock, pixel_multiplier, lane, link_bw, bpp, factor; + int target_clock, pixel_multiplier, lane, link_bw, factor; + unsigned int pipe_bpp; + bool dither; list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { if (encoder->base.crtc != crtc) @@ -4848,56 +4977,37 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* determine panel color depth */ temp = I915_READ(PIPECONF(pipe)); temp &= ~PIPE_BPC_MASK; - if (is_lvds) { - /* the BPC will be 6 if it is 18-bit LVDS panel */ - if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP) - temp |= PIPE_8BPC; - else - temp |= PIPE_6BPC; - } else if (has_edp_encoder) { - switch (dev_priv->edp.bpp/3) { - case 8: - temp |= PIPE_8BPC; - break; - case 10: - temp |= PIPE_10BPC; - break; - case 6: - temp |= PIPE_6BPC; - break; - case 12: - temp |= PIPE_12BPC; - break; - } - } else - temp |= PIPE_8BPC; - I915_WRITE(PIPECONF(pipe), temp); - - switch (temp & PIPE_BPC_MASK) { - case PIPE_8BPC: - bpp = 24; + dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp); + switch (pipe_bpp) { + case 18: + temp |= PIPE_6BPC; break; - case PIPE_10BPC: - bpp = 30; + case 24: + temp |= PIPE_8BPC; break; - case PIPE_6BPC: - bpp = 18; + case 30: + temp |= PIPE_10BPC; break; - case PIPE_12BPC: - bpp = 36; + case 36: + temp |= PIPE_12BPC; break; default: - DRM_ERROR("unknown pipe bpc value\n"); - bpp = 24; + WARN(1, "intel_choose_pipe_bpp returned invalid value\n"); + temp |= PIPE_8BPC; + pipe_bpp = 24; + break; } + intel_crtc->bpp = pipe_bpp; + I915_WRITE(PIPECONF(pipe), temp); + if (!lane) { /* * Account for spread spectrum to avoid * oversubscribing the link. Max center spread * is 2.5%; use 5% for safety's sake. */ - u32 bps = target_clock * bpp * 21 / 20; + u32 bps = target_clock * intel_crtc->bpp * 21 / 20; lane = bps / (link_bw * 8) + 1; } @@ -4905,7 +5015,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (pixel_multiplier > 1) link_bw *= pixel_multiplier; - ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n); + ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, + &m_n); /* Ironlake: try to setup display ref clock before DPLL * enabling. This is only under driver's control after @@ -5108,14 +5219,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(PCH_LVDS, temp); } - /* set the dithering flag and clear for anything other than a panel. */ pipeconf &= ~PIPECONF_DITHER_EN; pipeconf &= ~PIPECONF_DITHER_TYPE_MASK; - if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) { + if ((is_lvds && dev_priv->lvds_dither) || dither) { pipeconf |= PIPECONF_DITHER_EN; pipeconf |= PIPECONF_DITHER_TYPE_ST1; } - if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) { intel_dp_set_m_n(crtc, mode, adjusted_mode); } else { @@ -6638,6 +6747,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc_reset(&intel_crtc->base); intel_crtc->active = true; /* force the pipe off on setup_init_config */ + intel_crtc->bpp = 24; /* default for pre-Ironlake */ if (HAS_PCH_SPLIT(dev)) { intel_helper_funcs.prepare = ironlake_crtc_prepare; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8ac3bd8b6faa..7ec48d864577 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -170,6 +170,7 @@ struct intel_crtc { int16_t cursor_x, cursor_y; int16_t cursor_width, cursor_height; bool cursor_visible; + unsigned int bpp; }; #define to_intel_crtc(x) container_of(x, struct intel_crtc, base) -- cgit v1.2.3 From 17638cd68d5cbcd75dfad25966c0c56a5c2bac9f Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:23 -0700 Subject: drm/i915: split out plane update code Updating the planes is device specific, so create a new display callback and use it in pipe_set_base. (In fact we could go even further, valid display plane bits have changed with each generation, as has tiled buffer handling.) Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/intel_display.c | 107 ++++++++++++++++++++++++++++++++--- 2 files changed, 100 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4958ce068b53..a15f2c04f6a5 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -214,6 +214,8 @@ struct drm_i915_display_funcs { int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_i915_gem_object *obj); + int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y); /* clock updates for mode set */ /* cursor updates */ /* render clock increase/decrease */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7c5d28fd5f36..c166a88e426b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1841,10 +1841,8 @@ err_interruptible: return ret; } -/* Assume fb object is pinned & idle & fenced and just update base pointers */ -static int -intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, - int x, int y, enum mode_set_atomic state) +static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -1887,7 +1885,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, dspcntr |= DISPPLANE_32BPP_NO_ALPHA; break; default: - DRM_ERROR("Unknown color depth\n"); + DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel); return -EINVAL; } if (INTEL_INFO(dev)->gen >= 4) { @@ -1897,10 +1895,6 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, dspcntr &= ~DISPPLANE_TILED; } - if (HAS_PCH_SPLIT(dev)) - /* must disable */ - dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - I915_WRITE(reg, dspcntr); Start = obj->gtt_offset; @@ -1917,6 +1911,99 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, I915_WRITE(DSPADDR(plane), Start + Offset); POSTING_READ(reg); + return 0; +} + +static int ironlake_update_plane(struct drm_crtc *crtc, + struct drm_framebuffer *fb, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_framebuffer *intel_fb; + struct drm_i915_gem_object *obj; + int plane = intel_crtc->plane; + unsigned long Start, Offset; + u32 dspcntr; + u32 reg; + + switch (plane) { + case 0: + case 1: + break; + default: + DRM_ERROR("Can't update plane %d in SAREA\n", plane); + return -EINVAL; + } + + intel_fb = to_intel_framebuffer(fb); + obj = intel_fb->obj; + + reg = DSPCNTR(plane); + dspcntr = I915_READ(reg); + /* Mask out pixel format bits in case we change it */ + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + switch (fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (fb->depth != 16) + return -EINVAL; + + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + if (fb->depth == 24) + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + else if (fb->depth == 30) + dspcntr |= DISPPLANE_32BPP_30BIT_NO_ALPHA; + else + return -EINVAL; + break; + default: + DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel); + return -EINVAL; + } + + if (obj->tiling_mode != I915_TILING_NONE) + dspcntr |= DISPPLANE_TILED; + else + dspcntr &= ~DISPPLANE_TILED; + + /* must disable */ + dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; + + I915_WRITE(reg, dspcntr); + + Start = obj->gtt_offset; + Offset = y * fb->pitch + x * (fb->bits_per_pixel / 8); + + DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n", + Start, Offset, x, y, fb->pitch); + I915_WRITE(DSPSTRIDE(plane), fb->pitch); + I915_WRITE(DSPSURF(plane), Start); + I915_WRITE(DSPTILEOFF(plane), (y << 16) | x); + I915_WRITE(DSPADDR(plane), Offset); + POSTING_READ(reg); + + return 0; +} + +/* Assume fb object is pinned & idle & fenced and just update base pointers */ +static int +intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y, enum mode_set_atomic state) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + ret = dev_priv->display.update_plane(crtc, fb, x, y); + if (ret) + return ret; + intel_update_fbc(dev); intel_increase_pllclock(crtc); @@ -7797,9 +7884,11 @@ static void intel_init_display(struct drm_device *dev) if (HAS_PCH_SPLIT(dev)) { dev_priv->display.dpms = ironlake_crtc_dpms; dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; + dev_priv->display.update_plane = ironlake_update_plane; } else { dev_priv->display.dpms = i9xx_crtc_dpms; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; + dev_priv->display.update_plane = i9xx_update_plane; } if (I915_HAS_FBC(dev)) { -- cgit v1.2.3 From 858fa03527ded333dc5701f546bd5d1b5d7515ad Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:24 -0700 Subject: drm/i915: use pipe bpp in DP link bandwidth calculations The pipe may be driving various bpp values depending on the display configuration, so take that into account when calculating link bandwidth requirements. Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 391b55f1cc74..26ce8c0af9b8 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -682,7 +682,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_encoder *encoder; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int lane_count = 4, bpp = 24; + int lane_count = 4; struct intel_dp_m_n m_n; int pipe = intel_crtc->pipe; @@ -701,7 +701,6 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, break; } else if (is_edp(intel_dp)) { lane_count = dev_priv->edp.lanes; - bpp = dev_priv->edp.bpp; break; } } @@ -711,7 +710,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, * the number of bytes_per_pixel post-LUT, which we always * set up for 8-bits of R/G/B, or 3 bytes total. */ - intel_dp_compute_m_n(bpp, lane_count, + intel_dp_compute_m_n(intel_crtc->bpp, lane_count, mode->clock, adjusted_mode->clock, &m_n); if (HAS_PCH_SPLIT(dev)) { -- cgit v1.2.3 From 020f6704b5fbf687534ce53aeedc0364a995ae8a Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:25 -0700 Subject: drm/i915: use pipe bpp when setting HDMI bpc The Intel HDMI encoder can support 8bpc or 12bpc. Set the appropriate value based on the pipe bpp when configuring the output. Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_hdmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 1b72aa42c2f9..1ed8e6903915 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -131,6 +131,11 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) sdvox |= SDVO_HSYNC_ACTIVE_HIGH; + if (intel_crtc->bpp > 24) + sdvox |= COLOR_FORMAT_12bpc; + else + sdvox |= COLOR_FORMAT_8bpc; + /* Required on CPT */ if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev)) sdvox |= HDMI_MODE_SELECT; -- cgit v1.2.3 From 46e484566fe4dc8a34e0ccc154e2f8b939b9ec96 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:26 -0700 Subject: drm: bpp and depth changes require full mode sets To properly drive a framebuffer with a new depth or bpp, dither settings and link bandwidth calculations may change, so make sure we go through a full mode set in that case. Reported-by: Chris Wilson Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/drm_crtc_helper.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 92369655dca3..f88a9b2c977b 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -560,6 +560,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) mode_changed = true; } else if (set->fb == NULL) { mode_changed = true; + } else if (set->fb->depth != set->crtc->fb->depth) { + mode_changed = true; + } else if (set->fb->bits_per_pixel != + set->crtc->fb->bits_per_pixel) { + mode_changed = true; } else fb_changed = true; } -- cgit v1.2.3 From b5626747eca6d02124544d1d69049220f1c01fb1 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:27 -0700 Subject: drm/i915: check for supported depth at fb init time This will catch bad fb configs earlier. Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c166a88e426b..af3e5813366c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7061,6 +7061,11 @@ int intel_framebuffer_init(struct drm_device *dev, switch (mode_cmd->bpp) { case 8: case 16: + /* Only pre-ILK can handle 5:5:5 */ + if (mode_cmd->depth == 15 && !HAS_PCH_SPLIT(dev)) + return -EINVAL; + break; + case 24: case 32: break; -- cgit v1.2.3 From 89c6143263ef8e14e42e17324a234418d8030b10 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Jun 2011 12:19:28 -0700 Subject: drm/i915: use pipe bpp in DP link bandwidth calculation Now that we track bpp on a per-pipe basis, we can use the actual value rather than assuming 24bpp. Signed-off-by: Jesse Barnes Reviewed-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 26ce8c0af9b8..e17de2b3d415 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -179,12 +179,14 @@ intel_dp_link_clock(uint8_t link_bw) static int intel_dp_link_required(struct drm_device *dev, struct intel_dp *intel_dp, int pixel_clock) { - struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = intel_dp->base.base.crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int bpp = 24; - if (is_edp(intel_dp)) - return (pixel_clock * dev_priv->edp.bpp + 7) / 8; - else - return pixel_clock * 3; + if (intel_crtc) + bpp = intel_crtc->bpp; + + return (pixel_clock * bpp + 7) / 8; } static int -- cgit v1.2.3 From 61da5fab5a9b129cf05b1fe4666c3e45b3103fd4 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 7 Jul 2011 11:10:57 -0700 Subject: drm/i915/dp: retry link status read 3 times on failure Especially after a hotplug or power status change, the sink may not reply immediately to a link status query. So retry 3 times per the spec to really make sure nothing is there. See section 9.1 of the 1.1a DisplayPort spec. Signed-off-by: Jesse Barnes Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 391b55f1cc74..86912bd737ad 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1019,14 +1019,20 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) static bool intel_dp_get_link_status(struct intel_dp *intel_dp) { - int ret; + int ret, i; + + /* Must try AUX reads for this at least 3 times */ + for (i = 0; i < 3; i++) { + ret = intel_dp_aux_native_read(intel_dp, + DP_LANE0_1_STATUS, + intel_dp->link_status, + DP_LINK_STATUS_SIZE); + if (ret == DP_LINK_STATUS_SIZE) + return true; + msleep(1); + } - ret = intel_dp_aux_native_read(intel_dp, - DP_LANE0_1_STATUS, - intel_dp->link_status, DP_LINK_STATUS_SIZE); - if (ret != DP_LINK_STATUS_SIZE) - return false; - return true; + return false; } static uint8_t -- cgit v1.2.3 From 7183dc2912510cf005fcc59239f8d153ef51d3f0 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 7 Jul 2011 11:10:58 -0700 Subject: drm/i915/dp: use DP DPCD defines when looking at DPCD values Makes it easier to search for DP related constants. Reviewed-by: Keith Packard Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 86912bd737ad..fc51730b89d6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -138,8 +138,8 @@ intel_dp_max_lane_count(struct intel_dp *intel_dp) { int max_lane_count = 4; - if (intel_dp->dpcd[0] >= 0x11) { - max_lane_count = intel_dp->dpcd[2] & 0x1f; + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) { + max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f; switch (max_lane_count) { case 1: case 2: case 4: break; @@ -153,7 +153,7 @@ intel_dp_max_lane_count(struct intel_dp *intel_dp) static int intel_dp_max_link_bw(struct intel_dp *intel_dp) { - int max_link_bw = intel_dp->dpcd[1]; + int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; switch (max_link_bw) { case DP_LINK_BW_1_62: @@ -774,7 +774,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, /* * Check for DPCD version > 1.1 and enhanced framing support */ - if (intel_dp->dpcd[0] >= 0x11 && (intel_dp->dpcd[2] & DP_ENHANCED_FRAME_CAP)) { + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && + (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) { intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; intel_dp->DP |= DP_ENHANCED_FRAMING; } @@ -1553,7 +1554,7 @@ ironlake_dp_detect(struct intel_dp *intel_dp) 0x000, intel_dp->dpcd, sizeof (intel_dp->dpcd)) == sizeof(intel_dp->dpcd)) { - if (intel_dp->dpcd[0] != 0) + if (intel_dp->dpcd[DP_DPCD_REV] != 0) status = connector_status_connected; } DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0], @@ -1592,7 +1593,7 @@ g4x_dp_detect(struct intel_dp *intel_dp) if (intel_dp_aux_native_read(intel_dp, 0x000, intel_dp->dpcd, sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd)) { - if (intel_dp->dpcd[0] != 0) + if (intel_dp->dpcd[DP_DPCD_REV] != 0) status = connector_status_connected; } @@ -1960,8 +1961,9 @@ intel_dp_init(struct drm_device *dev, int output_reg) sizeof(intel_dp->dpcd)); ironlake_edp_panel_vdd_off(intel_dp); if (ret == sizeof(intel_dp->dpcd)) { - if (intel_dp->dpcd[0] >= 0x11) - dev_priv->no_aux_handshake = intel_dp->dpcd[3] & + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) + dev_priv->no_aux_handshake = + intel_dp->dpcd[DP_MAX_DOWNSPREAD] & DP_NO_AUX_HANDSHAKE_LINK_TRAINING; } else { /* if this fails, presume the device is a ghost */ -- cgit v1.2.3 From 59cd09e1aea3ac6eb15b45e5d2261a63ecb1799c Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 7 Jul 2011 11:10:59 -0700 Subject: drm/i915/dp: read more receiver capability bits on hotplug When a hotplug event is received, we need to check the receiver cap bits in case they've changed (as they might with a hub or chain config). Signed-off-by: Jesse Barnes Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index fc51730b89d6..a9a5051419df 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1522,6 +1522,8 @@ intel_dp_link_down(struct intel_dp *intel_dp) static void intel_dp_check_link_status(struct intel_dp *intel_dp) { + int ret; + if (!intel_dp->base.base.crtc) return; @@ -1530,6 +1532,15 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) return; } + /* Try to read receiver status if the link appears to be up */ + ret = intel_dp_aux_native_read(intel_dp, + 0x000, intel_dp->dpcd, + sizeof (intel_dp->dpcd)); + if (ret != sizeof(intel_dp->dpcd)) { + intel_dp_link_down(intel_dp); + return; + } + if (!intel_channel_eq_ok(intel_dp)) { intel_dp_start_link_train(intel_dp); intel_dp_complete_link_train(intel_dp); -- cgit v1.2.3 From 899526d9a73fda47516cf11ccb3467ad6702f568 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 7 Jul 2011 11:11:00 -0700 Subject: drm/i915/dp: try to read receiver capabilities 3 times when detecting If ->detect is called too soon after a hot plug event, the sink may not be ready yet. So try up to 3 times with 1ms sleeps in between tries to get the data (spec dictates that receivers must be ready to respond within 1ms and that sources should try 3 times). See section 9.1 of the 1.1a DisplayPort spec. Signed-off-by: Jesse Barnes Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index a9a5051419df..70075d9900da 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1551,6 +1551,7 @@ static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { enum drm_connector_status status; + int ret, i; /* Can't disconnect eDP, but you can close the lid... */ if (is_edp(intel_dp)) { @@ -1561,12 +1562,16 @@ ironlake_dp_detect(struct intel_dp *intel_dp) } status = connector_status_disconnected; - if (intel_dp_aux_native_read(intel_dp, - 0x000, intel_dp->dpcd, - sizeof (intel_dp->dpcd)) - == sizeof(intel_dp->dpcd)) { - if (intel_dp->dpcd[DP_DPCD_REV] != 0) + for (i = 0; i < 3; i++) { + ret = intel_dp_aux_native_read(intel_dp, + 0x000, intel_dp->dpcd, + sizeof (intel_dp->dpcd)); + if (ret == sizeof(intel_dp->dpcd) && + intel_dp->dpcd[DP_DPCD_REV] != 0) { status = connector_status_connected; + break; + } + msleep(1); } DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]); -- cgit v1.2.3 From 885a50147f00a8a80108904bf58a18af357717f3 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 7 Jul 2011 11:11:01 -0700 Subject: drm/i915/dp: remove DPMS mode tracking from DP We currently use this when a hot plug event is received, only checking the link status and re-training if we had previously configured a link. However if we want to preserve the DP configuration across both hot plug and DPMS events (which we do for userspace apps that don't respond to hot plug uevents), we need to unconditionally check the link and try to bring it up on hot plug. Signed-off-by: Jesse Barnes Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 70075d9900da..5f97c1751ff0 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -50,7 +50,6 @@ struct intel_dp { bool has_audio; int force_audio; uint32_t color_range; - int dpms_mode; uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[4]; @@ -1010,7 +1009,6 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) if (is_edp(intel_dp)) ironlake_edp_backlight_on(dev); } - intel_dp->dpms_mode = mode; } /* @@ -1813,8 +1811,7 @@ intel_dp_hot_plug(struct intel_encoder *intel_encoder) { struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base); - if (intel_dp->dpms_mode == DRM_MODE_DPMS_ON) - intel_dp_check_link_status(intel_dp); + intel_dp_check_link_status(intel_dp); } /* Return which DP Port should be selected for Transcoder DP control */ @@ -1882,7 +1879,6 @@ intel_dp_init(struct drm_device *dev, int output_reg) return; intel_dp->output_reg = output_reg; - intel_dp->dpms_mode = -1; intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL); if (!intel_connector) { -- cgit v1.2.3 From df0c237d124fb8d10b98f7b43d63d962eeed9355 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 7 Jul 2011 11:11:02 -0700 Subject: drm/i915/dp: consolidate AUX retry code When checking link status during a hot plug event or detecting sink presence, we need to retry 3 times per the spec (section 9.1 of the 1.1a DisplayPort spec). Consolidate the retry code into a native_aux_read_retry function for use by get_link_status and _detect. Signed-off-by: Jesse Barnes Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 51 ++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5f97c1751ff0..bca48b05520b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1012,21 +1012,23 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) } /* - * Fetch AUX CH registers 0x202 - 0x207 which contain - * link status information + * Native read with retry for link status and receiver capability reads for + * cases where the sink may still be asleep. */ static bool -intel_dp_get_link_status(struct intel_dp *intel_dp) +intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address, + uint8_t *recv, int recv_bytes) { int ret, i; - /* Must try AUX reads for this at least 3 times */ + /* + * Sinks are *supposed* to come up within 1ms from an off state, + * but we're also supposed to retry 3 times per the spec. + */ for (i = 0; i < 3; i++) { - ret = intel_dp_aux_native_read(intel_dp, - DP_LANE0_1_STATUS, - intel_dp->link_status, - DP_LINK_STATUS_SIZE); - if (ret == DP_LINK_STATUS_SIZE) + ret = intel_dp_aux_native_read(intel_dp, address, recv, + recv_bytes); + if (ret == recv_bytes) return true; msleep(1); } @@ -1034,6 +1036,19 @@ intel_dp_get_link_status(struct intel_dp *intel_dp) return false; } +/* + * Fetch AUX CH registers 0x202 - 0x207 which contain + * link status information + */ +static bool +intel_dp_get_link_status(struct intel_dp *intel_dp) +{ + return intel_dp_aux_native_read_retry(intel_dp, + DP_LANE0_1_STATUS, + intel_dp->link_status, + DP_LINK_STATUS_SIZE); +} + static uint8_t intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int r) @@ -1549,7 +1564,7 @@ static enum drm_connector_status ironlake_dp_detect(struct intel_dp *intel_dp) { enum drm_connector_status status; - int ret, i; + bool ret; /* Can't disconnect eDP, but you can close the lid... */ if (is_edp(intel_dp)) { @@ -1560,17 +1575,11 @@ ironlake_dp_detect(struct intel_dp *intel_dp) } status = connector_status_disconnected; - for (i = 0; i < 3; i++) { - ret = intel_dp_aux_native_read(intel_dp, - 0x000, intel_dp->dpcd, - sizeof (intel_dp->dpcd)); - if (ret == sizeof(intel_dp->dpcd) && - intel_dp->dpcd[DP_DPCD_REV] != 0) { - status = connector_status_connected; - break; - } - msleep(1); - } + ret = intel_dp_aux_native_read_retry(intel_dp, + 0x000, intel_dp->dpcd, + sizeof (intel_dp->dpcd)); + if (ret && intel_dp->dpcd[DP_DPCD_REV] != 0) + status = connector_status_connected; DRM_DEBUG_KMS("DPCD: %hx%hx%hx%hx\n", intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2], intel_dp->dpcd[3]); return status; -- cgit v1.2.3 From c7ad381078ee1b5ce2ab5274bd5f12fee6e1e59a Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 7 Jul 2011 11:11:03 -0700 Subject: drm/i915/dp: manage sink power state if possible On sinks with a DPCD rev of 1.1 or greater, we can send sink power management commands to address 0x600 per section 5.1.5 of the DisplayPort 1.1a spec. Signed-off-by: Jesse Barnes Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_dp.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index bca48b05520b..e2aced6eec4c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -942,11 +942,44 @@ static void ironlake_edp_pll_off(struct drm_encoder *encoder) udelay(200); } +/* If the sink supports it, try to set the power state appropriately */ +static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) +{ + int ret, i; + + /* Should have a valid DPCD by this point */ + if (intel_dp->dpcd[DP_DPCD_REV] < 0x11) + return; + + if (mode != DRM_MODE_DPMS_ON) { + ret = intel_dp_aux_native_write_1(intel_dp, DP_SET_POWER, + DP_SET_POWER_D3); + if (ret != 1) + DRM_DEBUG_DRIVER("failed to write sink power state\n"); + } else { + /* + * When turning on, we need to retry for 1ms to give the sink + * time to wake up. + */ + for (i = 0; i < 3; i++) { + ret = intel_dp_aux_native_write_1(intel_dp, + DP_SET_POWER, + DP_SET_POWER_D0); + if (ret == 1) + break; + msleep(1); + } + } +} + static void intel_dp_prepare(struct drm_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct drm_device *dev = encoder->dev; + /* Wake up the sink first */ + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); + if (is_edp(intel_dp)) { ironlake_edp_backlight_off(dev); ironlake_edp_panel_off(dev); @@ -990,6 +1023,7 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) if (mode != DRM_MODE_DPMS_ON) { if (is_edp(intel_dp)) ironlake_edp_backlight_off(dev); + intel_dp_sink_dpms(intel_dp, mode); intel_dp_link_down(intel_dp); if (is_edp(intel_dp)) ironlake_edp_panel_off(dev); @@ -998,6 +1032,7 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) } else { if (is_edp(intel_dp)) ironlake_edp_panel_vdd_on(intel_dp); + intel_dp_sink_dpms(intel_dp, mode); if (!(dp_reg & DP_PORT_EN)) { intel_dp_start_link_train(intel_dp); if (is_edp(intel_dp)) { -- cgit v1.2.3 From 1083694ab052e5ba38a8f9a057c4745448977837 Mon Sep 17 00:00:00 2001 From: Kenneth Graunke Date: Thu, 7 Jul 2011 15:33:26 -0700 Subject: drm/i915: Enable GPU reset on Ivybridge. According to the hardware documentation, GDRST is exactly the same as on Sandybridge. So simply enable the existing code. Signed-off-by: Kenneth Graunke Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 013d304455b9..58222e86e291 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -577,6 +577,7 @@ int i915_reset(struct drm_device *dev, u8 flags) if (get_seconds() - dev_priv->last_gpu_reset < 5) { DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); } else switch (INTEL_INFO(dev)->gen) { + case 7: case 6: ret = gen6_do_reset(dev, flags); /* If reset with a user forcewake, try to restore */ -- cgit v1.2.3 From 43a9539fa9e780f16c0d1e4bc91a2701f1ce178f Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 8 Jul 2011 12:22:36 +0100 Subject: drm/i915: Only export the generic intel_disable_fbc() interface As the enable/disable routines will be gain additional complexity in future patches, it is necessary that all callers do not bypass the generic interface by calling into the chipset routines directly. to do this we make the chipset routines static, so there is no choice. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_dma.c | 4 ++- drivers/gpu/drm/i915/i915_drv.h | 6 +---- drivers/gpu/drm/i915/i915_suspend.c | 4 +-- drivers/gpu/drm/i915/intel_display.c | 50 ++++++++++++++++++------------------ 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index e1787022d6c8..1315a882275c 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1073,6 +1073,9 @@ static void i915_setup_compression(struct drm_device *dev, int size) unsigned long cfb_base; unsigned long ll_base = 0; + /* Just in case the BIOS is doing something questionable. */ + intel_disable_fbc(dev); + compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); if (compressed_fb) compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); @@ -1099,7 +1102,6 @@ static void i915_setup_compression(struct drm_device *dev, int size) dev_priv->cfb_size = size; - intel_disable_fbc(dev); dev_priv->compressed_fb = compressed_fb; if (HAS_PCH_SPLIT(dev)) I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a15f2c04f6a5..56cb1c4fe73e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1289,12 +1289,8 @@ extern void intel_modeset_init(struct drm_device *dev); extern void intel_modeset_gem_init(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev); extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); -extern void i8xx_disable_fbc(struct drm_device *dev); -extern void g4x_disable_fbc(struct drm_device *dev); -extern void ironlake_disable_fbc(struct drm_device *dev); -extern void intel_disable_fbc(struct drm_device *dev); -extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval); extern bool intel_fbc_enabled(struct drm_device *dev); +extern void intel_disable_fbc(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); extern void ironlake_enable_rc6(struct drm_device *dev); extern void gen6_set_rps(struct drm_device *dev, u8 val); diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 8cd1c8b00660..285758603ac8 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -760,15 +760,13 @@ static void i915_restore_display(struct drm_device *dev) /* FIXME: restore TV & SDVO state */ /* only restore FBC info on the platform that supports FBC*/ + intel_disable_fbc(dev); if (I915_HAS_FBC(dev)) { if (HAS_PCH_SPLIT(dev)) { - ironlake_disable_fbc(dev); I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE); } else if (IS_GM45(dev)) { - g4x_disable_fbc(dev); I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE); } else { - i8xx_disable_fbc(dev); I915_WRITE(FBC_CFB_BASE, dev_priv->saveFBC_CFB_BASE); I915_WRITE(FBC_LL_BASE, dev_priv->saveFBC_LL_BASE); I915_WRITE(FBC_CONTROL2, dev_priv->saveFBC_CONTROL2); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index af3e5813366c..092361153957 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1384,6 +1384,28 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv, disable_pch_hdmi(dev_priv, pipe, HDMID); } +static void i8xx_disable_fbc(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 fbc_ctl; + + /* Disable compression */ + fbc_ctl = I915_READ(FBC_CONTROL); + if ((fbc_ctl & FBC_CTL_EN) == 0) + return; + + fbc_ctl &= ~FBC_CTL_EN; + I915_WRITE(FBC_CONTROL, fbc_ctl); + + /* Wait for compressing bit to clear */ + if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) { + DRM_DEBUG_KMS("FBC idle timed out\n"); + return; + } + + DRM_DEBUG_KMS("disabled FBC\n"); +} + static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) { struct drm_device *dev = crtc->dev; @@ -1439,28 +1461,6 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane); } -void i8xx_disable_fbc(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 fbc_ctl; - - /* Disable compression */ - fbc_ctl = I915_READ(FBC_CONTROL); - if ((fbc_ctl & FBC_CTL_EN) == 0) - return; - - fbc_ctl &= ~FBC_CTL_EN; - I915_WRITE(FBC_CONTROL, fbc_ctl); - - /* Wait for compressing bit to clear */ - if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) { - DRM_DEBUG_KMS("FBC idle timed out\n"); - return; - } - - DRM_DEBUG_KMS("disabled FBC\n"); -} - static bool i8xx_fbc_enabled(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -1516,7 +1516,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); } -void g4x_disable_fbc(struct drm_device *dev) +static void g4x_disable_fbc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 dpfc_ctl; @@ -1616,7 +1616,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane); } -void ironlake_disable_fbc(struct drm_device *dev) +static void ironlake_disable_fbc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 dpfc_ctl; @@ -1648,7 +1648,7 @@ bool intel_fbc_enabled(struct drm_device *dev) return dev_priv->display.fbc_enabled(dev); } -void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) +static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; -- cgit v1.2.3 From 973d04f990ac241881c4793cdfbe367b3caf3dba Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 8 Jul 2011 12:22:37 +0100 Subject: drm/i915: Replace direct calls to vfunc.disable_fbc with intel_disable_fbc() ...to ensure that any pending FBC enable tasklet is cancelled. Signed-off-by: Chris Wilson Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 092361153957..ab78dfd4dfb6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2820,9 +2820,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) intel_disable_plane(dev_priv, plane, pipe); - if (dev_priv->cfb_plane == plane && - dev_priv->display.disable_fbc) - dev_priv->display.disable_fbc(dev); + if (dev_priv->cfb_plane == plane) + intel_disable_fbc(dev); intel_disable_pipe(dev_priv, pipe); @@ -2986,9 +2985,8 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) intel_crtc_dpms_overlay(intel_crtc, false); intel_crtc_update_cursor(crtc, false); - if (dev_priv->cfb_plane == plane && - dev_priv->display.disable_fbc) - dev_priv->display.disable_fbc(dev); + if (dev_priv->cfb_plane == plane) + intel_disable_fbc(dev); intel_disable_plane(dev_priv, plane, pipe); intel_disable_pipe(dev_priv, pipe); @@ -8217,8 +8215,7 @@ void intel_modeset_cleanup(struct drm_device *dev) intel_increase_pllclock(crtc); } - if (dev_priv->display.disable_fbc) - dev_priv->display.disable_fbc(dev); + intel_disable_fbc(dev); if (IS_IRONLAKE_M(dev)) ironlake_disable_drps(dev); -- cgit v1.2.3 From f19a079a800dfd365fa8ed422acf29ca7a036ea3 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 8 Jul 2011 12:22:38 +0100 Subject: drm/i915: Remove vestigial pitch from post-gen2 FBC control routines The cfb_pitch was only used for 8xx_enable_fbc(), every later routine was just overwriting the value with itself thanks to a copy'n'paste error. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ab78dfd4dfb6..5c359e59d325 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1482,8 +1482,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) dpfc_ctl = I915_READ(DPFC_CONTROL); if (dpfc_ctl & DPFC_CTL_EN) { - if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 && - dev_priv->cfb_fence == obj->fence_reg && + if (dev_priv->cfb_fence == obj->fence_reg && dev_priv->cfb_plane == intel_crtc->plane && dev_priv->cfb_y == crtc->y) return; @@ -1492,7 +1491,6 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) intel_wait_for_vblank(dev, intel_crtc->pipe); } - dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; dev_priv->cfb_fence = obj->fence_reg; dev_priv->cfb_plane = intel_crtc->plane; dev_priv->cfb_y = crtc->y; @@ -1572,8 +1570,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); if (dpfc_ctl & DPFC_CTL_EN) { - if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 && - dev_priv->cfb_fence == obj->fence_reg && + if (dev_priv->cfb_fence == obj->fence_reg && dev_priv->cfb_plane == intel_crtc->plane && dev_priv->cfb_offset == obj->gtt_offset && dev_priv->cfb_y == crtc->y) @@ -1583,7 +1580,6 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) intel_wait_for_vblank(dev, intel_crtc->pipe); } - dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; dev_priv->cfb_fence = obj->fence_reg; dev_priv->cfb_plane = intel_crtc->plane; dev_priv->cfb_offset = obj->gtt_offset; -- cgit v1.2.3 From de568510cd410d82d370d3000808aca63ef28a22 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 8 Jul 2011 12:22:39 +0100 Subject: drm/i915: Use of a CPU fence is mandatory to update FBC regions upon CPU writes ...and this requirement is enforced by intel_update_fbc() so we can remove the later check from g4x_enable_fbc() and ironlake_enable_fbc(). Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5c359e59d325..31c75266a456 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1441,9 +1441,8 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) I915_WRITE(FBC_TAG + (i * 4), 0); /* Set it up... */ - fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane; - if (obj->tiling_mode != I915_TILING_NONE) - fbc_ctl2 |= FBC_CTL_CPU_FENCE; + fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE; + fbc_ctl2 |= plane; I915_WRITE(FBC_CONTROL2, fbc_ctl2); I915_WRITE(FBC_FENCE_OFF, crtc->y); @@ -1453,8 +1452,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */ fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT; - if (obj->tiling_mode != I915_TILING_NONE) - fbc_ctl |= dev_priv->cfb_fence; + fbc_ctl |= dev_priv->cfb_fence; I915_WRITE(FBC_CONTROL, fbc_ctl); DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ", @@ -1496,12 +1494,8 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) dev_priv->cfb_y = crtc->y; dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X; - if (obj->tiling_mode != I915_TILING_NONE) { - dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence; - I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY); - } else { - I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY); - } + dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence; + I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY); I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | @@ -1587,12 +1581,8 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) dpfc_ctl &= DPFC_RESERVED; dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X); - if (obj->tiling_mode != I915_TILING_NONE) { - dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence); - I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY); - } else { - I915_WRITE(ILK_DPFC_CHICKEN, ~DPFC_HT_MODIFY); - } + dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence); + I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY); I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) | @@ -1760,8 +1750,13 @@ static void intel_update_fbc(struct drm_device *dev) dev_priv->no_fbc_reason = FBC_BAD_PLANE; goto out_disable; } - if (obj->tiling_mode != I915_TILING_X) { - DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n"); + + /* The use of a CPU fence is mandatory in order to detect writes + * by the CPU to the scanout and trigger updates to the FBC. + */ + if (obj->tiling_mode != I915_TILING_X || + obj->fence_reg == I915_FENCE_REG_NONE) { + DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n"); dev_priv->no_fbc_reason = FBC_NOT_TILED; goto out_disable; } -- cgit v1.2.3 From 9ce9d0695d15da23ffe817516ba5d0b58caf8d05 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 8 Jul 2011 12:22:40 +0100 Subject: drm/i915: Set persistent-mode for ILK/SNB framebuffer compression Persistent mode is intended for use with front-buffer rendering, such as X, where it is necessary to detect writes to the scanout either by the GPU or through the CPU's fence, and recompress the dirty regions on the fly. (By comparison to the back-buffer rendering, the scanout is always recompressed after a page-flip.) References: https://bugs.freedesktop.org/show_bug.cgi?id=33487 References: https://bugs.freedesktop.org/show_bug.cgi?id=31742 Signed-off-by: Ben Widawsky Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_display.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 4a446b116e6a..96fb0fa47c40 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -579,6 +579,7 @@ #define DPFC_CTL_PLANEA (0<<30) #define DPFC_CTL_PLANEB (1<<30) #define DPFC_CTL_FENCE_EN (1<<29) +#define DPFC_CTL_PERSISTENT_MODE (1<<25) #define DPFC_SR_EN (1<<10) #define DPFC_CTL_LIMIT_1X (0<<6) #define DPFC_CTL_LIMIT_2X (1<<6) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 31c75266a456..9df96bdc002e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1581,6 +1581,8 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) dpfc_ctl &= DPFC_RESERVED; dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X); + /* Set persistent mode for front-buffer rendering, ala X. */ + dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE; dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence); I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY); -- cgit v1.2.3 From 7782de3bd671657674e7baba854f0fd43e9f86bc Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 8 Jul 2011 12:22:41 +0100 Subject: drm/i915: Disable FBC across page-flipping Page-flipping updates the scanout address, nukes the FBC compressed image and so forces an FBC update so that the displayed image remains consistent. However, page-flipping does not update the FBC registers themselves, which remain pointing to both the old address and the old CPU fence. Future updates to the new front-buffer (scanout) are then undetected! This first approach to demonstrate the issue and highlight the fix, simply disables FBC upon page-flip (a recompression will be forced on every flip so FBC becomes immaterial) and then re-enables FBC in the page-flip finish work function, so that the FBC registers are now pointing to the new framebuffer and front-buffer rendering works once more. Ideally, we want to only re-enable FBC after page-flipping is complete, as otherwise we are just wasting cycles and power (with needless recompression) whilst the page-flipping application is still running. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=33487 Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_display.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9df96bdc002e..f0788a97801e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6333,6 +6333,7 @@ static void intel_unpin_work_fn(struct work_struct *__work) drm_gem_object_unreference(&work->pending_flip_obj->base); drm_gem_object_unreference(&work->old_fb_obj->base); + intel_update_fbc(work->dev); mutex_unlock(&work->dev->struct_mutex); kfree(work); } @@ -6697,6 +6698,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (ret) goto cleanup_pending; + intel_disable_fbc(dev); mutex_unlock(&dev->struct_mutex); trace_i915_flip_request(intel_crtc->plane, obj); -- cgit v1.2.3 From 1630fe754c83b3e57efa51c85f1a21e612a63a0e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 8 Jul 2011 12:22:42 +0100 Subject: drm/i915: Perform intel_enable_fbc() from a delayed task In order to accommodate the requirements of re-enabling FBC after page-flipping, but to avoid doing so and incurring the cost of a wait for vblank in the middle of a page-flip sequence, we defer the actual enablement by 50ms. If any request to disable FBC arrive within that interval, the enablement is cancelled and we are saved from blocking on the wait. Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/intel_display.c | 83 +++++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 7 +++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 56cb1c4fe73e..3154b3dfd804 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -266,6 +266,7 @@ enum intel_pch { #define QUIRK_PIPEA_FORCE (1<<0) struct intel_fbdev; +struct intel_fbc_work; typedef struct drm_i915_private { struct drm_device *dev; @@ -335,6 +336,7 @@ typedef struct drm_i915_private { int cfb_fence; int cfb_plane; int cfb_y; + struct intel_fbc_work *fbc_work; struct intel_opregion opregion; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f0788a97801e..df2839c59b74 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1636,20 +1636,96 @@ bool intel_fbc_enabled(struct drm_device *dev) return dev_priv->display.fbc_enabled(dev); } +static void intel_fbc_work_fn(struct work_struct *__work) +{ + struct intel_fbc_work *work = + container_of(to_delayed_work(__work), + struct intel_fbc_work, work); + struct drm_device *dev = work->crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + mutex_lock(&dev->struct_mutex); + if (work == dev_priv->fbc_work) { + /* Double check that we haven't switched fb without cancelling + * the prior work. + */ + if (work->crtc->fb == work->fb) + dev_priv->display.enable_fbc(work->crtc, + work->interval); + + dev_priv->fbc_work = NULL; + } + mutex_unlock(&dev->struct_mutex); + + kfree(work); +} + +static void intel_cancel_fbc_work(struct drm_i915_private *dev_priv) +{ + if (dev_priv->fbc_work == NULL) + return; + + DRM_DEBUG_KMS("cancelling pending FBC enable\n"); + + /* Synchronisation is provided by struct_mutex and checking of + * dev_priv->fbc_work, so we can perform the cancellation + * entirely asynchronously. + */ + if (cancel_delayed_work(&dev_priv->fbc_work->work)) + /* tasklet was killed before being run, clean up */ + kfree(dev_priv->fbc_work); + + /* Mark the work as no longer wanted so that if it does + * wake-up (because the work was already running and waiting + * for our mutex), it will discover that is no longer + * necessary to run. + */ + dev_priv->fbc_work = NULL; +} + static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) { - struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_fbc_work *work; + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; if (!dev_priv->display.enable_fbc) return; - dev_priv->display.enable_fbc(crtc, interval); + intel_cancel_fbc_work(dev_priv); + + work = kzalloc(sizeof *work, GFP_KERNEL); + if (work == NULL) { + dev_priv->display.enable_fbc(crtc, interval); + return; + } + + work->crtc = crtc; + work->fb = crtc->fb; + work->interval = interval; + INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn); + + dev_priv->fbc_work = work; + + DRM_DEBUG_KMS("scheduling delayed FBC enable\n"); + + /* Delay the actual enabling to let pageflipping cease and the + * display to settle before starting the compression. + * + * A more complicated solution would involve tracking vblanks + * following the termination of the page-flipping sequence + * and indeed performing the enable as a co-routine and not + * waiting synchronously upon the vblank. + */ + schedule_delayed_work(&work->work, msecs_to_jiffies(50)); } void intel_disable_fbc(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + intel_cancel_fbc_work(dev_priv); + if (!dev_priv->display.disable_fbc) return; @@ -8227,6 +8303,9 @@ void intel_modeset_cleanup(struct drm_device *dev) drm_irq_uninstall(dev); cancel_work_sync(&dev_priv->hotplug_work); + /* flush any delayed tasks or pending work */ + flush_scheduled_work(); + /* Shut off idle work before the crtcs get freed. */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { intel_crtc = to_intel_crtc(crtc); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 7ec48d864577..6e990f9760ef 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -234,6 +234,13 @@ struct intel_unpin_work { bool enable_stall_check; }; +struct intel_fbc_work { + struct delayed_work work; + struct drm_crtc *crtc; + struct drm_framebuffer *fb; + int interval; +}; + int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter); extern bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus); -- cgit v1.2.3 From 016b9b61ed692498a5d46dff974fe41b20e7e60b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 8 Jul 2011 12:22:43 +0100 Subject: drm/i915: Share the common work of disabling active FBC before updating Upon review, all path share the same dependencies for updating the registers and so we can benefit from sharing the code and checking early. This removes the unsightly intel_wait_for_vblank() from the lowlevel functions and upon further analysis the only path that will require a wait is if we are performing an instantaneous transition between two valid FBC configurations. The page-flip path itself will have disabled FBC registers and will have waited for at least one vblank before finishing the flip and attempting to re-enable FBC. This wait can be accomplished simply by delaying the enable until after we are sure that a vblank will have passed, which we are already doing to make sure that the display is settled before enabling FBC. Signed-off-by: Chris Wilson Reviewed-by: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.h | 6 +- drivers/gpu/drm/i915/intel_display.c | 118 ++++++++++++++++++----------------- 2 files changed, 64 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 3154b3dfd804..00dc59a51ccd 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -331,10 +331,8 @@ typedef struct drm_i915_private { uint32_t last_instdone1; unsigned long cfb_size; - unsigned long cfb_pitch; - unsigned long cfb_offset; - int cfb_fence; - int cfb_plane; + unsigned int cfb_fb; + enum plane cfb_plane; int cfb_y; struct intel_fbc_work *fbc_work; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index df2839c59b74..b5b15bda71d9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1414,27 +1414,17 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); struct drm_i915_gem_object *obj = intel_fb->obj; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int cfb_pitch; int plane, i; u32 fbc_ctl, fbc_ctl2; - if (fb->pitch == dev_priv->cfb_pitch && - obj->fence_reg == dev_priv->cfb_fence && - intel_crtc->plane == dev_priv->cfb_plane && - I915_READ(FBC_CONTROL) & FBC_CTL_EN) - return; - - i8xx_disable_fbc(dev); - - dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE; - - if (fb->pitch < dev_priv->cfb_pitch) - dev_priv->cfb_pitch = fb->pitch; + cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE; + if (fb->pitch < cfb_pitch) + cfb_pitch = fb->pitch; /* FBC_CTL wants 64B units */ - dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1; - dev_priv->cfb_fence = obj->fence_reg; - dev_priv->cfb_plane = intel_crtc->plane; - plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB; + cfb_pitch = (cfb_pitch / 64) - 1; + plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB; /* Clear old tags */ for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++) @@ -1450,13 +1440,13 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval) fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC; if (IS_I945GM(dev)) fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */ - fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; + fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT; - fbc_ctl |= dev_priv->cfb_fence; + fbc_ctl |= obj->fence_reg; I915_WRITE(FBC_CONTROL, fbc_ctl); - DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ", - dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane); + DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ", + cfb_pitch, crtc->y, intel_crtc->plane); } static bool i8xx_fbc_enabled(struct drm_device *dev) @@ -1478,23 +1468,8 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval) unsigned long stall_watermark = 200; u32 dpfc_ctl; - dpfc_ctl = I915_READ(DPFC_CONTROL); - if (dpfc_ctl & DPFC_CTL_EN) { - if (dev_priv->cfb_fence == obj->fence_reg && - dev_priv->cfb_plane == intel_crtc->plane && - dev_priv->cfb_y == crtc->y) - return; - - I915_WRITE(DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN); - intel_wait_for_vblank(dev, intel_crtc->pipe); - } - - dev_priv->cfb_fence = obj->fence_reg; - dev_priv->cfb_plane = intel_crtc->plane; - dev_priv->cfb_y = crtc->y; - dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X; - dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence; + dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg; I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY); I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | @@ -1563,27 +1538,11 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) u32 dpfc_ctl; dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); - if (dpfc_ctl & DPFC_CTL_EN) { - if (dev_priv->cfb_fence == obj->fence_reg && - dev_priv->cfb_plane == intel_crtc->plane && - dev_priv->cfb_offset == obj->gtt_offset && - dev_priv->cfb_y == crtc->y) - return; - - I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN); - intel_wait_for_vblank(dev, intel_crtc->pipe); - } - - dev_priv->cfb_fence = obj->fence_reg; - dev_priv->cfb_plane = intel_crtc->plane; - dev_priv->cfb_offset = obj->gtt_offset; - dev_priv->cfb_y = crtc->y; - dpfc_ctl &= DPFC_RESERVED; dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X); /* Set persistent mode for front-buffer rendering, ala X. */ dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE; - dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence); + dpfc_ctl |= (DPFC_CTL_FENCE_EN | obj->fence_reg); I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY); I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN | @@ -1596,7 +1555,7 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval) if (IS_GEN6(dev)) { I915_WRITE(SNB_DPFC_CTL_SA, - SNB_CPU_FENCE_ENABLE | dev_priv->cfb_fence); + SNB_CPU_FENCE_ENABLE | obj->fence_reg); I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y); sandybridge_blit_fbc_update(dev); } @@ -1649,10 +1608,15 @@ static void intel_fbc_work_fn(struct work_struct *__work) /* Double check that we haven't switched fb without cancelling * the prior work. */ - if (work->crtc->fb == work->fb) + if (work->crtc->fb == work->fb) { dev_priv->display.enable_fbc(work->crtc, work->interval); + dev_priv->cfb_plane = to_intel_crtc(work->crtc)->plane; + dev_priv->cfb_fb = work->crtc->fb->base.id; + dev_priv->cfb_y = work->crtc->y; + } + dev_priv->fbc_work = NULL; } mutex_unlock(&dev->struct_mutex); @@ -1710,7 +1674,10 @@ static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval) DRM_DEBUG_KMS("scheduling delayed FBC enable\n"); /* Delay the actual enabling to let pageflipping cease and the - * display to settle before starting the compression. + * display to settle before starting the compression. Note that + * this delay also serves a second purpose: it allows for a + * vblank to pass after disabling the FBC before we attempt + * to modify the control registers. * * A more complicated solution would involve tracking vblanks * following the termination of the page-flipping sequence @@ -1730,6 +1697,7 @@ void intel_disable_fbc(struct drm_device *dev) return; dev_priv->display.disable_fbc(dev); + dev_priv->cfb_plane = -1; } /** @@ -1843,6 +1811,44 @@ static void intel_update_fbc(struct drm_device *dev) if (in_dbg_master()) goto out_disable; + /* If the scanout has not changed, don't modify the FBC settings. + * Note that we make the fundamental assumption that the fb->obj + * cannot be unpinned (and have its GTT offset and fence revoked) + * without first being decoupled from the scanout and FBC disabled. + */ + if (dev_priv->cfb_plane == intel_crtc->plane && + dev_priv->cfb_fb == fb->base.id && + dev_priv->cfb_y == crtc->y) + return; + + if (intel_fbc_enabled(dev)) { + /* We update FBC along two paths, after changing fb/crtc + * configuration (modeswitching) and after page-flipping + * finishes. For the latter, we know that not only did + * we disable the FBC at the start of the page-flip + * sequence, but also more than one vblank has passed. + * + * For the former case of modeswitching, it is possible + * to switch between two FBC valid configurations + * instantaneously so we do need to disable the FBC + * before we can modify its control registers. We also + * have to wait for the next vblank for that to take + * effect. However, since we delay enabling FBC we can + * assume that a vblank has passed since disabling and + * that we can safely alter the registers in the deferred + * callback. + * + * In the scenario that we go from a valid to invalid + * and then back to valid FBC configuration we have + * no strict enforcement that a vblank occurred since + * disabling the FBC. However, along all current pipe + * disabling paths we do need to wait for a vblank at + * some point. And we wait before enabling FBC anyway. + */ + DRM_DEBUG_KMS("disabling active FBC for update\n"); + intel_disable_fbc(dev); + } + intel_enable_fbc(crtc, 500); return; -- cgit v1.2.3 From c7c369472dad852f6fe06a8be94dea72de784934 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 8 Jul 2011 10:29:42 -0700 Subject: drm/i915: Enable i915 frame buffer compression by default We'll try again with the new fixes. Prepare to see this reverted when we get regression reports... Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index ec4308d765c5..629d24ca73e2 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -55,7 +55,7 @@ module_param_named(semaphores, i915_semaphores, int, 0600); unsigned int i915_enable_rc6 = 1; module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600); -unsigned int i915_enable_fbc = 0; +unsigned int i915_enable_fbc = 1; module_param_named(i915_enable_fbc, i915_enable_fbc, int, 0600); unsigned int i915_lvds_downclock = 0; -- cgit v1.2.3 From a7b85d2aa63ed09cd5a4a640772b3272f5ac7caa Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 10 Jul 2011 13:12:17 -0700 Subject: drm/i915: Clean up i915_driver_load failure path i915_driver_load adds a write-combining MTRR region for the GTT aperture to improve memory speeds through the aperture. If i915_driver_load fails after this, it would not have cleaned up the MTRR. This shouldn't cause any problems, except for consuming an MTRR register. Still, it's best to clean up completely in the failure path, which is easily done by calling mtrr_del if the mtrr was successfully allocated. i915_driver_load calls i915_gem_load which register i915_gem_inactive_shrink. If i915_driver_load fails after calling i915_gem_load, the shrinker will be left registered. When called, it will access freed memory and crash. The fix is to unregister the shrinker in the failure path using code duplicated from i915_driver_unload. i915_driver_load also has some incorrect gotos in the error cleanup paths: * After failing to initialize the GTT (which cannot happen, btw, intel_gtt_get returns a fixed (non-NULL) value), it tries to free the uninitialized WC IO mapping. Fixed this by changing the target from out_iomapfree to out_rmmap Signed-off-by: Keith Packard Tested-by: Lin Ming --- drivers/gpu/drm/i915/i915_dma.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index e1787022d6c8..296fbd66f0e1 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1943,7 +1943,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (!dev_priv->mm.gtt) { DRM_ERROR("Failed to initialize GTT\n"); ret = -ENODEV; - goto out_iomapfree; + goto out_rmmap; } agp_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; @@ -1987,7 +1987,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (dev_priv->wq == NULL) { DRM_ERROR("Failed to create our workqueue.\n"); ret = -ENOMEM; - goto out_iomapfree; + goto out_mtrrfree; } /* enable GEM by default */ @@ -2074,13 +2074,21 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) return 0; out_gem_unload: + if (dev_priv->mm.inactive_shrinker.shrink) + unregister_shrinker(&dev_priv->mm.inactive_shrinker); + if (dev->pdev->msi_enabled) pci_disable_msi(dev->pdev); intel_teardown_gmbus(dev); intel_teardown_mchbar(dev); destroy_workqueue(dev_priv->wq); -out_iomapfree: +out_mtrrfree: + if (dev_priv->mm.gtt_mtrr >= 0) { + mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base, + dev->agp->agp_info.aper_size * 1024 * 1024); + dev_priv->mm.gtt_mtrr = -1; + } io_mapping_free(dev_priv->mm.gtt_mapping); out_rmmap: pci_iounmap(dev->pdev, dev_priv->regs); -- cgit v1.2.3 From 05bd42688dbc066d4e2689b6f73c0470601f788b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 12 Jul 2011 08:49:31 -0700 Subject: Revert "drm/i915: enable rc6 by default" This reverts commit a51f7a66fb5e4af5ec4286baef940d06594b59d2. We still have a few Ironlake and Sandybridge machines which fail when RC6 is enabled. Better luck next release? Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 58222e86e291..eb91e2dd7914 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -52,7 +52,7 @@ module_param_named(powersave, i915_powersave, int, 0600); unsigned int i915_semaphores = 0; module_param_named(semaphores, i915_semaphores, int, 0600); -unsigned int i915_enable_rc6 = 1; +unsigned int i915_enable_rc6 = 0; module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600); unsigned int i915_enable_fbc = 0; -- cgit v1.2.3 From a94919eaddaa3fede1df8563ce4d761a75374645 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 12 Jul 2011 18:03:29 +0100 Subject: drm/i915/ringbuffer: Idling requires waiting for the ring to be empty ...which is measured by the size and not the amount of space remaining. Waiting upon size-8, did one of two things. In the common case with more than 8 bytes available to write into the ring, it would return immediately. Otherwise, it would timeout given the impossible condition of waiting for more space than is available in the ring, leading to warnings such as: [drm:intel_cleanup_ring_buffer] *ERROR* failed to quiesce render ring whilst cleaning up: -16 Signed-off-by: Chris Wilson Reviewed-by: Eric Anholt Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_ringbuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index c0e0ee63fbf4..39ac2b634ae5 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -165,7 +165,7 @@ void intel_cleanup_ring_buffer(struct intel_ring_buffer *ring); int __must_check intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n); static inline int intel_wait_ring_idle(struct intel_ring_buffer *ring) { - return intel_wait_ring_buffer(ring, ring->space - 8); + return intel_wait_ring_buffer(ring, ring->size - 8); } int __must_check intel_ring_begin(struct intel_ring_buffer *ring, int n); -- cgit v1.2.3 From 835bff7ef220f9d7df94970557a6a2296f74106f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 12 May 2011 17:10:57 -0700 Subject: drm/i915: Select correct pipe during TV detect Signed-off-by: Keith Packard Tested-by: Yi Sun --- drivers/gpu/drm/i915/intel_tv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 113e4e7264cd..950ae1477738 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1236,6 +1236,8 @@ intel_tv_detect_type (struct intel_tv *intel_tv, struct drm_connector *connector) { struct drm_encoder *encoder = &intel_tv->base.base; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; unsigned long irqflags; @@ -1258,6 +1260,10 @@ intel_tv_detect_type (struct intel_tv *intel_tv, /* Poll for TV detection */ tv_ctl &= ~(TV_ENC_ENABLE | TV_TEST_MODE_MASK); tv_ctl |= TV_TEST_MODE_MONITOR_DETECT; + if (intel_crtc->pipe == 1) + tv_ctl |= TV_ENC_PIPEB_SELECT; + else + tv_ctl &= ~TV_ENC_PIPEB_SELECT; tv_dac &= ~(TVDAC_SENSE_MASK | DAC_A_MASK | DAC_B_MASK | DAC_C_MASK); tv_dac |= (TVDAC_STATE_CHG_EN | -- cgit v1.2.3 From 2bf71160f94270319eb965935045f825ec446e8a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 12 May 2011 17:10:58 -0700 Subject: drm/i915: TVDAC_STATE_CHG does not indicate successful load-detect Do not use this bit to indicate that load detection has completed, instead just wait for vblank, at which point the load registers will have been updated. Signed-off-by: Keith Packard Tested-by: Yi Sun --- drivers/gpu/drm/i915/intel_tv.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 950ae1477738..210d570fd516 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1283,26 +1283,26 @@ intel_tv_detect_type (struct intel_tv *intel_tv, to_intel_crtc(intel_tv->base.base.crtc)->pipe); type = -1; - if (wait_for((tv_dac = I915_READ(TV_DAC)) & TVDAC_STATE_CHG, 20) == 0) { - DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac); - /* - * A B C - * 0 1 1 Composite - * 1 0 X svideo - * 0 0 0 Component - */ - if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { - DRM_DEBUG_KMS("Detected Composite TV connection\n"); - type = DRM_MODE_CONNECTOR_Composite; - } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { - DRM_DEBUG_KMS("Detected S-Video TV connection\n"); - type = DRM_MODE_CONNECTOR_SVIDEO; - } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { - DRM_DEBUG_KMS("Detected Component TV connection\n"); - type = DRM_MODE_CONNECTOR_Component; - } else { - DRM_DEBUG_KMS("Unrecognised TV connection\n"); - } + tv_dac = I915_READ(TV_DAC); + DRM_DEBUG_KMS("TV detected: %x, %x\n", tv_ctl, tv_dac); + /* + * A B C + * 0 1 1 Composite + * 1 0 X svideo + * 0 0 0 Component + */ + if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) { + DRM_DEBUG_KMS("Detected Composite TV connection\n"); + type = DRM_MODE_CONNECTOR_Composite; + } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) { + DRM_DEBUG_KMS("Detected S-Video TV connection\n"); + type = DRM_MODE_CONNECTOR_SVIDEO; + } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) { + DRM_DEBUG_KMS("Detected Component TV connection\n"); + type = DRM_MODE_CONNECTOR_Component; + } else { + DRM_DEBUG_KMS("Unrecognised TV connection\n"); + type = -1; } I915_WRITE(TV_DAC, save_tv_dac & ~TVDAC_STATE_CHG_EN); -- cgit v1.2.3 From f5a3d0c4086d1854cbda545092c462b84cba20ce Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 12 Jul 2011 18:30:52 -0400 Subject: i915: Fix opregion notifications opregion-based platforms will send ACPI video event 0x80 for a range of notification types for legacy compatibility. This is interpreted as a display switch event, which may not be appropriate in the circumstances. When we receive such an event we should make sure that the platform is genuinely requesting a display switch before passing that event through to userspace. Signed-off-by: Matthew Garrett Tested-by: Adam Jackson Signed-off-by: Keith Packard --- drivers/acpi/video.c | 7 ++++--- drivers/gpu/drm/i915/intel_opregion.c | 15 +++++++++++---- include/acpi/video.h | 2 ++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index db39e9e607d8..ada4b4d9bdc8 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -46,7 +46,6 @@ #define PREFIX "ACPI: " -#define ACPI_VIDEO_CLASS "video" #define ACPI_VIDEO_BUS_NAME "Video Bus" #define ACPI_VIDEO_DEVICE_NAME "Video Device" #define ACPI_VIDEO_NOTIFY_SWITCH 0x80 @@ -1445,7 +1444,8 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event) case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch, * most likely via hotkey. */ acpi_bus_generate_proc_event(device, event, 0); - keycode = KEY_SWITCHVIDEOMODE; + if (!acpi_notifier_call_chain(device, event, 0)) + keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video @@ -1475,7 +1475,8 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event) break; } - acpi_notifier_call_chain(device, event, 0); + if (event != ACPI_VIDEO_NOTIFY_SWITCH) + acpi_notifier_call_chain(device, event, 0); if (keycode) { input_report_key(input, keycode, 1); diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index d2c710422908..b7c5ddb564d1 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -297,19 +297,26 @@ static int intel_opregion_video_event(struct notifier_block *nb, /* The only video events relevant to opregion are 0x80. These indicate either a docking event, lid switch or display switch request. In Linux, these are handled by the dock, button and video drivers. - We might want to fix the video driver to be opregion-aware in - future, but right now we just indicate to the firmware that the - request has been handled */ + */ struct opregion_acpi *acpi; + struct acpi_bus_event *event = data; + int ret = NOTIFY_OK; + + if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0) + return NOTIFY_DONE; if (!system_opregion) return NOTIFY_DONE; acpi = system_opregion->acpi; + + if (event->type == 0x80 && !(acpi->cevt & 0x1)) + ret = NOTIFY_BAD; + acpi->csts = 0; - return NOTIFY_OK; + return ret; } static struct notifier_block intel_opregion_notifier = { diff --git a/include/acpi/video.h b/include/acpi/video.h index 0e98e679d3a7..61109f2609fc 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -5,6 +5,8 @@ struct acpi_device; +#define ACPI_VIDEO_CLASS "video" + #define ACPI_VIDEO_DISPLAY_CRT 1 #define ACPI_VIDEO_DISPLAY_TV 2 #define ACPI_VIDEO_DISPLAY_DVI 3 -- cgit v1.2.3 From 95736720fc866eadb2ce1789631b907c0f38cb7c Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 12 May 2011 22:17:09 +0100 Subject: drm/i915: Cache GT fifo count for SandyBridge The read back of the available FIFO entries is vital for system stability, but extremely costly. However, we only need a guide so as to avoid eating into the reserved entries and since we are the only consumer we can cache the read of the count from the last write. Signed-off-by: Chris Wilson Reviewed-by: Ben Widawsky Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.c | 15 ++++++++++----- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_reg.h | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 6ed73ae2133a..d988cc350f66 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -348,12 +348,17 @@ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) { - int loop = 500; - u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); - while (fifo < 20 && loop--) { - udelay(10); - fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); + if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES ) { + int loop = 500; + u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); + while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) { + udelay(10); + fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); + } + WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES); + dev_priv->gt_fifo_count = fifo; } + dev_priv->gt_fifo_count--; } static int i915_drm_freeze(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 00dc59a51ccd..b37146c4526f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -277,6 +277,7 @@ typedef struct drm_i915_private { int relative_constants_mode; void __iomem *regs; + u32 gt_fifo_count; struct intel_gmbus { struct i2c_adapter adapter; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 96fb0fa47c40..02db299f621a 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3361,6 +3361,7 @@ #define FORCEWAKE_ACK 0x130090 #define GT_FIFO_FREE_ENTRIES 0x120008 +#define GT_FIFO_NUM_RESERVED_ENTRIES 20 #define GEN6_RPNSWREQ 0xA008 #define GEN6_TURBO_DISABLE (1<<31) -- cgit v1.2.3 From 99834ea446d5c0da3f6cfa355fe4670840d45f79 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 13 Jul 2011 21:01:46 +0100 Subject: drm/i915/bios: Avoid temporary allocation whilst searching for downclock Alan Cox reported a missing check on the kmalloc return value for the allocation of a temporary mode used for searching for the LVDS downlock frequency. This allocation is roughly 200 bytes, a little too large to friviously place on the stack. However, we can simply use the few bytes we need stored within the original DVO timing data, skip the translation and do the compare directly between the timing data rather than on a mode, thus avoiding the need for any temporary allocations. Signed-off-by: Chris Wilson Cc: Alan Cox Cc: Keith Packard Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/intel_bios.c | 142 ++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 59 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 927442a11925..61abef8a8119 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -74,7 +74,7 @@ get_blocksize(void *p) static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, - struct lvds_dvo_timing *dvo_timing) + const struct lvds_dvo_timing *dvo_timing) { panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | dvo_timing->hactive_lo; @@ -115,20 +115,75 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, drm_mode_set_name(panel_fixed_mode); } +static bool +lvds_dvo_timing_equal_size(const struct lvds_dvo_timing *a, + const struct lvds_dvo_timing *b) +{ + if (a->hactive_hi != b->hactive_hi || + a->hactive_lo != b->hactive_lo) + return false; + + if (a->hsync_off_hi != b->hsync_off_hi || + a->hsync_off_lo != b->hsync_off_lo) + return false; + + if (a->hsync_pulse_width != b->hsync_pulse_width) + return false; + + if (a->hblank_hi != b->hblank_hi || + a->hblank_lo != b->hblank_lo) + return false; + + if (a->vactive_hi != b->vactive_hi || + a->vactive_lo != b->vactive_lo) + return false; + + if (a->vsync_off != b->vsync_off) + return false; + + if (a->vsync_pulse_width != b->vsync_pulse_width) + return false; + + if (a->vblank_hi != b->vblank_hi || + a->vblank_lo != b->vblank_lo) + return false; + + return true; +} + +static const struct lvds_dvo_timing * +get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data, + const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs, + int index) +{ + /* + * the size of fp_timing varies on the different platform. + * So calculate the DVO timing relative offset in LVDS data + * entry to get the DVO timing entry + */ + + int lfp_data_size = + lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset - + lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset; + int dvo_timing_offset = + lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset - + lvds_lfp_data_ptrs->ptr[0].fp_timing_offset; + char *entry = (char *)lvds_lfp_data->data + lfp_data_size * index; + + return (struct lvds_dvo_timing *)(entry + dvo_timing_offset); +} + /* Try to find integrated panel data */ static void parse_lfp_panel_data(struct drm_i915_private *dev_priv, struct bdb_header *bdb) { - struct bdb_lvds_options *lvds_options; - struct bdb_lvds_lfp_data *lvds_lfp_data; - struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; - struct bdb_lvds_lfp_data_entry *entry; - struct lvds_dvo_timing *dvo_timing; + const struct bdb_lvds_options *lvds_options; + const struct bdb_lvds_lfp_data *lvds_lfp_data; + const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; + const struct lvds_dvo_timing *panel_dvo_timing; struct drm_display_mode *panel_fixed_mode; - int lfp_data_size, dvo_timing_offset; - int i, temp_downclock; - struct drm_display_mode *temp_mode; + int i, downclock; lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); if (!lvds_options) @@ -150,75 +205,44 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, dev_priv->lvds_vbt = 1; - lfp_data_size = lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset - - lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset; - entry = (struct bdb_lvds_lfp_data_entry *) - ((uint8_t *)lvds_lfp_data->data + (lfp_data_size * - lvds_options->panel_type)); - dvo_timing_offset = lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset - - lvds_lfp_data_ptrs->ptr[0].fp_timing_offset; - - /* - * the size of fp_timing varies on the different platform. - * So calculate the DVO timing relative offset in LVDS data - * entry to get the DVO timing entry - */ - dvo_timing = (struct lvds_dvo_timing *) - ((unsigned char *)entry + dvo_timing_offset); + panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data, + lvds_lfp_data_ptrs, + lvds_options->panel_type); panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); if (!panel_fixed_mode) return; - fill_detail_timing_data(panel_fixed_mode, dvo_timing); + fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing); dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n"); drm_mode_debug_printmodeline(panel_fixed_mode); - temp_mode = kzalloc(sizeof(*temp_mode), GFP_KERNEL); - temp_downclock = panel_fixed_mode->clock; /* - * enumerate the LVDS panel timing info entry in VBT to check whether - * the LVDS downclock is found. + * Iterate over the LVDS panel timing info to find the lowest clock + * for the native resolution. */ + downclock = panel_dvo_timing->clock; for (i = 0; i < 16; i++) { - entry = (struct bdb_lvds_lfp_data_entry *) - ((uint8_t *)lvds_lfp_data->data + (lfp_data_size * i)); - dvo_timing = (struct lvds_dvo_timing *) - ((unsigned char *)entry + dvo_timing_offset); - - fill_detail_timing_data(temp_mode, dvo_timing); - - if (temp_mode->hdisplay == panel_fixed_mode->hdisplay && - temp_mode->hsync_start == panel_fixed_mode->hsync_start && - temp_mode->hsync_end == panel_fixed_mode->hsync_end && - temp_mode->htotal == panel_fixed_mode->htotal && - temp_mode->vdisplay == panel_fixed_mode->vdisplay && - temp_mode->vsync_start == panel_fixed_mode->vsync_start && - temp_mode->vsync_end == panel_fixed_mode->vsync_end && - temp_mode->vtotal == panel_fixed_mode->vtotal && - temp_mode->clock < temp_downclock) { - /* - * downclock is already found. But we expect - * to find the lower downclock. - */ - temp_downclock = temp_mode->clock; - } - /* clear it to zero */ - memset(temp_mode, 0, sizeof(*temp_mode)); + const struct lvds_dvo_timing *dvo_timing; + + dvo_timing = get_lvds_dvo_timing(lvds_lfp_data, + lvds_lfp_data_ptrs, + i); + if (lvds_dvo_timing_equal_size(dvo_timing, panel_dvo_timing) && + dvo_timing->clock < downclock) + downclock = dvo_timing->clock; } - kfree(temp_mode); - if (temp_downclock < panel_fixed_mode->clock && - i915_lvds_downclock) { + + if (downclock < panel_dvo_timing->clock && i915_lvds_downclock) { dev_priv->lvds_downclock_avail = 1; - dev_priv->lvds_downclock = temp_downclock; + dev_priv->lvds_downclock = downclock * 10; DRM_DEBUG_KMS("LVDS downclock is found in VBT. " "Normal Clock %dKHz, downclock %dKHz\n", - temp_downclock, panel_fixed_mode->clock); + panel_fixed_mode->clock, 10*downclock); } - return; } /* Try to find sdvo panel data */ -- cgit v1.2.3 From a35d9d3cf75604e9ef17faedd333bf2a66a513d8 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Wed, 13 Jul 2011 14:38:17 -0700 Subject: drm/i915: add module parameter compiler hints Signed-off-by: Ben Widawsky Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.c | 24 ++++++++++++------------ drivers/gpu/drm/i915/i915_drv.h | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index d988cc350f66..ef5cf036364d 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -37,40 +37,40 @@ #include #include "drm_crtc_helper.h" -static int i915_modeset = -1; +static int i915_modeset __read_mostly = -1; module_param_named(modeset, i915_modeset, int, 0400); -unsigned int i915_fbpercrtc = 0; +unsigned int i915_fbpercrtc __always_unused = 0; module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400); -int i915_panel_ignore_lid = 0; +int i915_panel_ignore_lid __read_mostly = 0; module_param_named(panel_ignore_lid, i915_panel_ignore_lid, int, 0600); -unsigned int i915_powersave = 1; +unsigned int i915_powersave __read_mostly = 1; module_param_named(powersave, i915_powersave, int, 0600); -unsigned int i915_semaphores = 0; +unsigned int i915_semaphores __read_mostly = 0; module_param_named(semaphores, i915_semaphores, int, 0600); -unsigned int i915_enable_rc6 = 0; +unsigned int i915_enable_rc6 __read_mostly = 0; module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600); -unsigned int i915_enable_fbc = 1; +unsigned int i915_enable_fbc __read_mostly = 1; module_param_named(i915_enable_fbc, i915_enable_fbc, int, 0600); -unsigned int i915_lvds_downclock = 0; +unsigned int i915_lvds_downclock __read_mostly = 0; module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400); -unsigned int i915_panel_use_ssc = 1; +unsigned int i915_panel_use_ssc __read_mostly = 1; module_param_named(lvds_use_ssc, i915_panel_use_ssc, int, 0600); -int i915_vbt_sdvo_panel_type = -1; +int i915_vbt_sdvo_panel_type __read_mostly = -1; module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600); -static bool i915_try_reset = true; +static bool i915_try_reset __read_mostly = true; module_param_named(reset, i915_try_reset, bool, 0600); -bool i915_enable_hangcheck = true; +bool i915_enable_hangcheck __read_mostly = true; module_param_named(enable_hangcheck, i915_enable_hangcheck, bool, 0644); static struct drm_driver driver; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index b37146c4526f..78cdd158287a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -988,16 +988,16 @@ struct drm_i915_file_private { extern struct drm_ioctl_desc i915_ioctls[]; extern int i915_max_ioctl; -extern unsigned int i915_fbpercrtc; -extern int i915_panel_ignore_lid; -extern unsigned int i915_powersave; -extern unsigned int i915_semaphores; -extern unsigned int i915_lvds_downclock; -extern unsigned int i915_panel_use_ssc; -extern int i915_vbt_sdvo_panel_type; -extern unsigned int i915_enable_rc6; -extern unsigned int i915_enable_fbc; -extern bool i915_enable_hangcheck; +extern unsigned int i915_fbpercrtc __always_unused; +extern int i915_panel_ignore_lid __read_mostly; +extern unsigned int i915_powersave __read_mostly; +extern unsigned int i915_semaphores __read_mostly; +extern unsigned int i915_lvds_downclock __read_mostly; +extern unsigned int i915_panel_use_ssc __read_mostly; +extern int i915_vbt_sdvo_panel_type __read_mostly; +extern unsigned int i915_enable_rc6 __read_mostly; +extern unsigned int i915_enable_fbc __read_mostly; +extern bool i915_enable_hangcheck __read_mostly; extern int i915_suspend(struct drm_device *dev, pm_message_t state); extern int i915_resume(struct drm_device *dev); -- cgit v1.2.3 From 6e96e7757a0133a9a66fd9b86cd04b5e7b88122a Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Wed, 13 Jul 2011 14:38:18 -0700 Subject: drm/i915: provide module parameter description Signed-off-by: Ben Widawsky Signed-off-by: Keith Packard --- drivers/gpu/drm/i915/i915_drv.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index ef5cf036364d..ce045a8cf82c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -39,39 +39,68 @@ static int i915_modeset __read_mostly = -1; module_param_named(modeset, i915_modeset, int, 0400); +MODULE_PARM_DESC(modeset, + "Use kernel modesetting [KMS] (0=DRM_I915_KMS from .config, " + "1=on, -1=force vga console preference [default])"); unsigned int i915_fbpercrtc __always_unused = 0; module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400); int i915_panel_ignore_lid __read_mostly = 0; module_param_named(panel_ignore_lid, i915_panel_ignore_lid, int, 0600); +MODULE_PARM_DESC(panel_ignore_lid, + "Override lid status (0=autodetect [default], 1=lid open, " + "-1=lid closed)"); unsigned int i915_powersave __read_mostly = 1; module_param_named(powersave, i915_powersave, int, 0600); +MODULE_PARM_DESC(powersave, + "Enable powersavings, fbc, downclocking, etc. (default: true)"); unsigned int i915_semaphores __read_mostly = 0; module_param_named(semaphores, i915_semaphores, int, 0600); +MODULE_PARM_DESC(semaphores, + "Use semaphores for inter-ring sync (default: false)"); unsigned int i915_enable_rc6 __read_mostly = 0; module_param_named(i915_enable_rc6, i915_enable_rc6, int, 0600); +MODULE_PARM_DESC(i915_enable_rc6, + "Enable power-saving render C-state 6 (default: true)"); unsigned int i915_enable_fbc __read_mostly = 1; module_param_named(i915_enable_fbc, i915_enable_fbc, int, 0600); +MODULE_PARM_DESC(i915_enable_fbc, + "Enable frame buffer compression for power savings " + "(default: false)"); unsigned int i915_lvds_downclock __read_mostly = 0; module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400); +MODULE_PARM_DESC(lvds_downclock, + "Use panel (LVDS/eDP) downclocking for power savings " + "(default: false)"); unsigned int i915_panel_use_ssc __read_mostly = 1; module_param_named(lvds_use_ssc, i915_panel_use_ssc, int, 0600); +MODULE_PARM_DESC(lvds_use_ssc, + "Use Spread Spectrum Clock with panels [LVDS/eDP] " + "(default: true)"); int i915_vbt_sdvo_panel_type __read_mostly = -1; module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600); +MODULE_PARM_DESC(vbt_sdvo_panel_type, + "Override selection of SDVO panel mode in the VBT " + "(default: auto)"); static bool i915_try_reset __read_mostly = true; module_param_named(reset, i915_try_reset, bool, 0600); +MODULE_PARM_DESC(reset, "Attempt GPU resets (default: true)"); bool i915_enable_hangcheck __read_mostly = true; module_param_named(enable_hangcheck, i915_enable_hangcheck, bool, 0644); +MODULE_PARM_DESC(enable_hangcheck, + "Periodically check GPU activity for detecting hangs. " + "WARNING: Disabling this can cause system wide hangs. " + "(default: true)"); static struct drm_driver driver; extern int intel_agp_enabled; -- cgit v1.2.3