From e5230c7bf7186699f1b83dcbff5a45a3267c9941 Mon Sep 17 00:00:00 2001
From: Chris Wilson <chris@chris-wilson.co.uk>
Date: Wed, 26 Feb 2014 18:01:42 +0000
Subject: [PATCH] drm/i915: Trim stolen region for reserved pages and
 conflicting BIOS setups

On a few systems we have to reserve regions inside the stolen portion
for use by the BIOS - we have to trim that out of our own allocation. In
some cases, the BIOS will have reduced the reserved region in the e820
map and so we have to adjust our own region request to suit. Either way,
we need to only use the resource that we successfully reserve for
ourselves - rather than claim one region and use another.

v2: Fix resource request bounds to be inclusive. (Jani)

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_drv.h        |  2 +-
 drivers/gpu/drm/i915/i915_gem_stolen.c | 64 ++++++++++++++++++++++------------
 drivers/gpu/drm/i915/intel_pm.c        |  9 +++--
 3 files changed, 49 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 655a45c981fd..eb31c456eb35 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1099,7 +1099,7 @@ struct i915_gem_mm {
 	struct list_head unbound_list;
 
 	/** Usable portion of the GTT for GEM */
-	unsigned long stolen_base; /* limited to low memory (32-bit) */
+	struct resource *stolen_region; /* limited to low memory (32-bit) */
 
 	/** PPGTT used for aliasing the PPGTT with the GTT */
 	struct i915_hw_ppgtt *aliasing_ppgtt;
diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c
index adc5c91f6946..984ada1b0084 100644
--- a/drivers/gpu/drm/i915/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/i915_gem_stolen.c
@@ -45,10 +45,11 @@
  * for is a boon.
  */
 
-static unsigned long i915_stolen_to_physical(struct drm_device *dev)
+static struct resource *i915_stolen_to_physical(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct resource *r;
+	unsigned long start, end;
 	u32 base;
 
 	/* Almost universally we can find the Graphics Base of Stolen Memory
@@ -184,15 +185,38 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
 	 * kernel. So if the region is already marked as busy, something
 	 * is seriously wrong.
 	 */
-	r = devm_request_mem_region(dev->dev, base, dev_priv->gtt.stolen_size,
+	start = base + PAGE_SIZE; /* leave the first page alone! */
+
+	end = base + dev_priv->gtt.stolen_size - 1;
+	if (IS_VALLEYVIEW(dev))
+		end -= 1024*1024; /* top 1M on VLV/BYT is reserved */
+
+	r = devm_request_mem_region(dev->dev, start, end-start,
 				    "Graphics Stolen Memory");
 	if (r == NULL) {
-		DRM_ERROR("conflict detected with stolen region: [0x%08x - 0x%08x]\n",
-			  base, base + (uint32_t)dev_priv->gtt.stolen_size);
-		base = 0;
+		/* Weird. BIOS has not reserved the whole region for us,
+		 * try something smaller.
+		 */
+		do {
+			start += PAGE_SIZE;
+			end -= PAGE_SIZE;
+			if (start < end)
+				break;
+
+			r = devm_request_mem_region(dev->dev, start, end-start,
+						    "Graphics Stolen Memory");
+		} while (r == NULL);
+
+		if (r == NULL)
+			DRM_ERROR("conflicting resource reservations detected with stolen region: [0x%08x - 0x%08x]\n",
+				  base, base + (uint32_t)dev_priv->gtt.stolen_size - 1);
+		else
+			DRM_INFO("conflict detected with stolen region [0x%08x - 0x%08x], reducing to [0x%08lx - 0x%08lx]\n",
+				 base, base + (uint32_t)dev_priv->gtt.stolen_size - 1,
+				 start, end);
 	}
 
-	return base;
+	return r;
 }
 
 static int i915_setup_compression(struct drm_device *dev, int size)
@@ -232,9 +256,9 @@ static int i915_setup_compression(struct drm_device *dev, int size)
 		dev_priv->fbc.compressed_llb = compressed_llb;
 
 		I915_WRITE(FBC_CFB_BASE,
-			   dev_priv->mm.stolen_base + compressed_fb->start);
+			   dev_priv->mm.stolen_region->start + compressed_fb->start);
 		I915_WRITE(FBC_LL_BASE,
-			   dev_priv->mm.stolen_base + compressed_llb->start);
+			   dev_priv->mm.stolen_region->start + compressed_llb->start);
 	}
 
 	dev_priv->fbc.compressed_fb = compressed_fb;
@@ -304,28 +328,22 @@ void i915_gem_cleanup_stolen(struct drm_device *dev)
 int i915_gem_init_stolen(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
-	int bios_reserved = 0;
+	struct resource *r;
 
 	if (dev_priv->gtt.stolen_size == 0)
 		return 0;
 
-	dev_priv->mm.stolen_base = i915_stolen_to_physical(dev);
-	if (dev_priv->mm.stolen_base == 0)
+	r = i915_stolen_to_physical(dev);
+	if (r == NULL)
 		return 0;
 
-	DRM_DEBUG_KMS("found %zd bytes of stolen memory at %08lx\n",
-		      dev_priv->gtt.stolen_size, dev_priv->mm.stolen_base);
-
-	if (IS_VALLEYVIEW(dev))
-		bios_reserved = 1024*1024; /* top 1M on VLV/BYT */
-
-	if (WARN_ON(bios_reserved > dev_priv->gtt.stolen_size))
-		return 0;
+	DRM_DEBUG_KMS("found %ld bytes of stolen memory at %08lx\n",
+		      (long)resource_size(r), (long)r->start);
 
 	/* Basic memrange allocator for stolen space */
-	drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size -
-		    bios_reserved);
+	drm_mm_init(&dev_priv->mm.stolen, 0, resource_size(r));
 
+	dev_priv->mm.stolen_region = r;
 	return 0;
 }
 
@@ -338,7 +356,7 @@ i915_pages_create_for_stolen(struct drm_device *dev,
 	struct scatterlist *sg;
 
 	DRM_DEBUG_DRIVER("offset=0x%x, size=%d\n", offset, size);
-	BUG_ON(offset > dev_priv->gtt.stolen_size - size);
+	BUG_ON(offset > resource_size(dev_priv->mm.stolen_region) - size);
 
 	/* We hide that we have no struct page backing our stolen object
 	 * by wrapping the contiguous physical allocation with a fake
@@ -358,7 +376,7 @@ i915_pages_create_for_stolen(struct drm_device *dev,
 	sg->offset = 0;
 	sg->length = size;
 
-	sg_dma_address(sg) = (dma_addr_t)dev_priv->mm.stolen_base + offset;
+	sg_dma_address(sg) = (dma_addr_t)dev_priv->mm.stolen_region->start + offset;
 	sg_dma_len(sg) = size;
 
 	return st;
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 3419ddae32c6..ac0cff40d5ec 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -3564,12 +3564,17 @@ static void valleyview_setup_pctx(struct drm_device *dev)
 	u32 pcbr;
 	int pctx_size = 24*1024;
 
+	if (dev_priv->mm.stolen_region == NULL) {
+		DRM_DEBUG("stolen space not reserved, unable to setup power saving context");
+		return;
+	}
+
 	pcbr = I915_READ(VLV_PCBR);
 	if (pcbr) {
 		/* BIOS set it up already, grab the pre-alloc'd space */
 		int pcbr_offset;
 
-		pcbr_offset = (pcbr & (~4095)) - dev_priv->mm.stolen_base;
+		pcbr_offset = (pcbr & (~4095)) - dev_priv->mm.stolen_region->start;
 		pctx = i915_gem_object_create_stolen_for_preallocated(dev_priv->dev,
 								      pcbr_offset,
 								      I915_GTT_OFFSET_NONE,
@@ -3591,7 +3596,7 @@ static void valleyview_setup_pctx(struct drm_device *dev)
 		return;
 	}
 
-	pctx_paddr = dev_priv->mm.stolen_base + pctx->stolen->start;
+	pctx_paddr = dev_priv->mm.stolen_region->start + pctx->stolen->start;
 	I915_WRITE(VLV_PCBR, pctx_paddr);
 
 out:
-- 
1.9.0

