drm/i915: Delay allocation of stolen space for FBC
authorChris Wilson <chris@chris-wilson.co.uk>
Thu, 15 Nov 2012 11:32:20 +0000 (11:32 +0000)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Fri, 30 Nov 2012 22:29:59 +0000 (23:29 +0100)
As FBC is commonly disabled due to limitations of the chipset upon
output configurations, on many systems FBC is never enabled. For those
systems, it is advantageous to make use of the stolen memory for other
objects and so we defer allocation of the FBC chunk until we actually
require it. This increases the likelihood of that allocation failing,
but that in turns means that we are already taking advantage of the
stolen memory!

As well as delaying the allocation from driver initialisation until the
first use of FBC, we also return the stolen block after we finish using
it - allowing greater flexibility in our usage of stolen space. A side
effect of this is that we can then attempt to allocate only the required
amount of space (with a little slack to reduce reallocation rate and
avoid fragmentation).

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem_stolen.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_pm.c

index f16101fa8b4a16307a836f98efd50c52781e687c..6969c722edf6b37f4b436d5397339ea82c70adc1 100644 (file)
@@ -1571,6 +1571,8 @@ int i915_gem_evict_everything(struct drm_device *dev);
 
 /* i915_gem_stolen.c */
 int i915_gem_init_stolen(struct drm_device *dev);
+int i915_gem_stolen_setup_compression(struct drm_device *dev, int size);
+void i915_gem_stolen_cleanup_compression(struct drm_device *dev);
 void i915_gem_cleanup_stolen(struct drm_device *dev);
 
 /* i915_gem_tiling.c */
index be24312cd32fd55157ebc03a7a52ef67d305bbac..10ca473d85f2a82ee1e1b0a2f9d754cfa59aeff1 100644 (file)
@@ -88,33 +88,27 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
        return base;
 }
 
-static void i915_warn_stolen(struct drm_device *dev)
-{
-       DRM_INFO("not enough stolen space for compressed buffer, disabling\n");
-       DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n");
-}
-
-static void i915_setup_compression(struct drm_device *dev, int size)
+static int i915_setup_compression(struct drm_device *dev, int size)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb);
-       unsigned long cfb_base;
-       unsigned long ll_base = 0;
 
-       /* Just in case the BIOS is doing something questionable. */
-       intel_disable_fbc(dev);
-
-       compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0);
+       /* Try to over-allocate to reduce reallocations and fragmentation */
+       compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen,
+                                          size <<= 1, 4096, 0);
+       if (!compressed_fb)
+               compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen,
+                                                  size >>= 1, 4096, 0);
        if (compressed_fb)
                compressed_fb = drm_mm_get_block(compressed_fb, size, 4096);
        if (!compressed_fb)
                goto err;
 
-       cfb_base = dev_priv->mm.stolen_base + compressed_fb->start;
-       if (!cfb_base)
-               goto err_fb;
-
-       if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) {
+       if (HAS_PCH_SPLIT(dev))
+               I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start);
+       else if (IS_GM45(dev)) {
+               I915_WRITE(DPFC_CB_BASE, compressed_fb->start);
+       } else {
                compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen,
                                                    4096, 4096, 0);
                if (compressed_llb)
@@ -123,56 +117,68 @@ static void i915_setup_compression(struct drm_device *dev, int size)
                if (!compressed_llb)
                        goto err_fb;
 
-               ll_base = dev_priv->mm.stolen_base + compressed_llb->start;
-               if (!ll_base)
-                       goto err_llb;
+               dev_priv->compressed_llb = compressed_llb;
+
+               I915_WRITE(FBC_CFB_BASE,
+                          dev_priv->mm.stolen_base + compressed_fb->start);
+               I915_WRITE(FBC_LL_BASE,
+                          dev_priv->mm.stolen_base + compressed_llb->start);
        }
 
+       dev_priv->compressed_fb = compressed_fb;
        dev_priv->cfb_size = size;
 
-       dev_priv->compressed_fb = compressed_fb;
-       if (HAS_PCH_SPLIT(dev))
-               I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start);
-       else if (IS_GM45(dev)) {
-               I915_WRITE(DPFC_CB_BASE, compressed_fb->start);
-       } else {
-               I915_WRITE(FBC_CFB_BASE, cfb_base);
-               I915_WRITE(FBC_LL_BASE, ll_base);
-               dev_priv->compressed_llb = compressed_llb;
-       }
+       DRM_DEBUG_KMS("reserved %d bytes of contiguous stolen space for FBC\n",
+                     size);
 
-       DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n",
-                     (long)cfb_base, (long)ll_base, size >> 20);
-       return;
+       return 0;
 
-err_llb:
-       drm_mm_put_block(compressed_llb);
 err_fb:
        drm_mm_put_block(compressed_fb);
 err:
-       dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
-       i915_warn_stolen(dev);
+       return -ENOSPC;
+}
+
+int i915_gem_stolen_setup_compression(struct drm_device *dev, int size)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->mm.stolen_base == 0)
+               return -ENODEV;
+
+       if (size < dev_priv->cfb_size)
+               return 0;
+
+       /* Release any current block */
+       i915_gem_stolen_cleanup_compression(dev);
+
+       return i915_setup_compression(dev, size);
 }
 
-static void i915_cleanup_compression(struct drm_device *dev)
+void i915_gem_stolen_cleanup_compression(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       drm_mm_put_block(dev_priv->compressed_fb);
+       if (dev_priv->cfb_size == 0)
+               return;
+
+       if (dev_priv->compressed_fb)
+               drm_mm_put_block(dev_priv->compressed_fb);
+
        if (dev_priv->compressed_llb)
                drm_mm_put_block(dev_priv->compressed_llb);
+
+       dev_priv->cfb_size = 0;
 }
 
 void i915_gem_cleanup_stolen(struct drm_device *dev)
 {
-       if (I915_HAS_FBC(dev) && i915_powersave)
-               i915_cleanup_compression(dev);
+       i915_gem_stolen_cleanup_compression(dev);
 }
 
 int i915_gem_init_stolen(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size;
 
        dev_priv->mm.stolen_base = i915_stolen_to_physical(dev);
        if (dev_priv->mm.stolen_base == 0)
@@ -182,21 +188,7 @@ int i915_gem_init_stolen(struct drm_device *dev)
                      dev_priv->mm.gtt->stolen_size, dev_priv->mm.stolen_base);
 
        /* Basic memrange allocator for stolen space */
-       drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size);
-
-       /* Try to set up FBC with a reasonable compressed buffer size */
-       if (I915_HAS_FBC(dev) && i915_powersave) {
-               int cfb_size;
-
-               /* Leave 1M for line length buffer & misc. */
-
-               /* Try to get a 32M buffer... */
-               if (prealloc_size > (36*1024*1024))
-                       cfb_size = 32*1024*1024;
-               else /* fall back to 7/8 of the stolen space */
-                       cfb_size = prealloc_size * 7 / 8;
-               i915_setup_compression(dev, cfb_size);
-       }
+       drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->mm.gtt->stolen_size);
 
        return 0;
 }
index 52b6b0e811bc4c4ba86096836938b4172b26199f..d303f2a90e70d0417d7cc13d851a706b6bdd3758 100644 (file)
@@ -8655,6 +8655,9 @@ void intel_modeset_init(struct drm_device *dev)
        /* Just disable it once at startup */
        i915_disable_vga(dev);
        intel_setup_outputs(dev);
+
+       /* Just in case the BIOS is doing something questionable. */
+       intel_disable_fbc(dev);
 }
 
 static void
index f595b8d56cc3543365e5291b1d11e1d6c2881838..abfff29ed13a0c83edf91a926ae56037d6934c96 100644 (file)
@@ -440,12 +440,6 @@ void intel_update_fbc(struct drm_device *dev)
                dev_priv->no_fbc_reason = FBC_MODULE_PARAM;
                goto out_disable;
        }
-       if (intel_fb->obj->base.size > dev_priv->cfb_size) {
-               DRM_DEBUG_KMS("framebuffer too large, disabling "
-                             "compression\n");
-               dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
-               goto out_disable;
-       }
        if ((crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) ||
            (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)) {
                DRM_DEBUG_KMS("mode incompatible with compression, "
@@ -479,6 +473,14 @@ void intel_update_fbc(struct drm_device *dev)
        if (in_dbg_master())
                goto out_disable;
 
+       if (i915_gem_stolen_setup_compression(dev, intel_fb->obj->base.size)) {
+               DRM_INFO("not enough stolen space for compressed buffer (need %zd bytes), disabling\n", intel_fb->obj->base.size);
+               DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n");
+               DRM_DEBUG_KMS("framebuffer too large, disabling compression\n");
+               dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
+               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)
@@ -526,6 +528,7 @@ out_disable:
                DRM_DEBUG_KMS("unsupported config, disabling FBC\n");
                intel_disable_fbc(dev);
        }
+       i915_gem_stolen_cleanup_compression(dev);
 }
 
 static void i915_pineview_get_mem_freq(struct drm_device *dev)