drm/i915: Don't clobber the GTT when it's within stolen memory
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 5 Jun 2014 17:02:59 +0000 (20:02 +0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Wed, 9 Jul 2014 07:52:13 +0000 (09:52 +0200)
On most gen2-4 platforms the GTT can be (or maybe always is?)
inside the stolen memory region. If that's the case, reduce the
size of the stolen memory appropriately to make make sure we
don't clobber the GTT.

v2: Deal with gen4 36 bit physical address

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=80151
Acked-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: stable@vger.kernel.org
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_gem_stolen.c
drivers/gpu/drm/i915/i915_reg.h

index 62ef55b..7465ab0 100644 (file)
@@ -74,6 +74,50 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
        if (base == 0)
                return 0;
 
+       /* make sure we don't clobber the GTT if it's within stolen memory */
+       if (INTEL_INFO(dev)->gen <= 4 && !IS_G33(dev) && !IS_G4X(dev)) {
+               struct {
+                       u32 start, end;
+               } stolen[2] = {
+                       { .start = base, .end = base + dev_priv->gtt.stolen_size, },
+                       { .start = base, .end = base + dev_priv->gtt.stolen_size, },
+               };
+               u64 gtt_start, gtt_end;
+
+               gtt_start = I915_READ(PGTBL_CTL);
+               if (IS_GEN4(dev))
+                       gtt_start = (gtt_start & PGTBL_ADDRESS_LO_MASK) |
+                               (gtt_start & PGTBL_ADDRESS_HI_MASK) << 28;
+               else
+                       gtt_start &= PGTBL_ADDRESS_LO_MASK;
+               gtt_end = gtt_start + gtt_total_entries(dev_priv->gtt) * 4;
+
+               if (gtt_start >= stolen[0].start && gtt_start < stolen[0].end)
+                       stolen[0].end = gtt_start;
+               if (gtt_end > stolen[1].start && gtt_end <= stolen[1].end)
+                       stolen[1].start = gtt_end;
+
+               /* pick the larger of the two chunks */
+               if (stolen[0].end - stolen[0].start >
+                   stolen[1].end - stolen[1].start) {
+                       base = stolen[0].start;
+                       dev_priv->gtt.stolen_size = stolen[0].end - stolen[0].start;
+               } else {
+                       base = stolen[1].start;
+                       dev_priv->gtt.stolen_size = stolen[1].end - stolen[1].start;
+               }
+
+               if (stolen[0].start != stolen[1].start ||
+                   stolen[0].end != stolen[1].end) {
+                       DRM_DEBUG_KMS("GTT within stolen memory at 0x%llx-0x%llx\n",
+                                     (unsigned long long) gtt_start,
+                                     (unsigned long long) gtt_end - 1);
+                       DRM_DEBUG_KMS("Stolen memory adjusted to 0x%x-0x%x\n",
+                                     base, base + (u32) dev_priv->gtt.stolen_size - 1);
+               }
+       }
+
+
        /* Verify that nothing else uses this physical address. Stolen
         * memory should be reserved by the BIOS and hidden from the
         * kernel. So if the region is already marked as busy, something
index e691b30..a5bab61 100644 (file)
@@ -942,6 +942,9 @@ enum punit_power_well {
 /*
  * Instruction and interrupt control regs
  */
+#define PGTBL_CTL      0x02020
+#define   PGTBL_ADDRESS_LO_MASK        0xfffff000 /* bits [31:12] */
+#define   PGTBL_ADDRESS_HI_MASK        0x000000f0 /* bits [35:32] (gen4) */
 #define PGTBL_ER       0x02024
 #define RENDER_RING_BASE       0x02000
 #define BSD_RING_BASE          0x04000