]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/i915/i915_gem_stolen.c
Merge tag 'drm-intel-next-2013-07-12' of git://people.freedesktop.org/~danvet/drm...
[karo-tx-linux.git] / drivers / gpu / drm / i915 / i915_gem_stolen.c
index 130d1db27e288d0479804696d33e1014e1581a18..5c1a535d5072a34c62b9ab563e592e569d425acc 100644 (file)
@@ -46,6 +46,7 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct pci_dev *pdev = dev_priv->bridge_dev;
+       struct resource *r;
        u32 base;
 
        /* On the machines I have tested the Graphics Base of Stolen Memory
@@ -62,7 +63,10 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
         * its value of TOLUD.
         */
        base = 0;
-       if (INTEL_INFO(dev)->gen >= 6) {
+       if (IS_VALLEYVIEW(dev)) {
+               pci_read_config_dword(dev->pdev, 0x5c, &base);
+               base &= ~((1<<20) - 1);
+       } else if (INTEL_INFO(dev)->gen >= 6) {
                /* Read Base Data of Stolen Memory Register (BDSM) directly.
                 * Note that there is also a MCHBAR miror at 0x1080c0 or
                 * we could use device 2:0x5c instead.
@@ -85,6 +89,22 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
 #endif
        }
 
+       if (base == 0)
+               return 0;
+
+       /* 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
+        * is seriously wrong.
+        */
+       r = devm_request_mem_region(dev->dev, base, dev_priv->gtt.stolen_size,
+                                   "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;
+       }
+
        return base;
 }
 
@@ -117,7 +137,7 @@ static int i915_setup_compression(struct drm_device *dev, int size)
                if (!compressed_llb)
                        goto err_fb;
 
-               dev_priv->compressed_llb = compressed_llb;
+               dev_priv->fbc.compressed_llb = compressed_llb;
 
                I915_WRITE(FBC_CFB_BASE,
                           dev_priv->mm.stolen_base + compressed_fb->start);
@@ -125,8 +145,8 @@ static int i915_setup_compression(struct drm_device *dev, int size)
                           dev_priv->mm.stolen_base + compressed_llb->start);
        }
 
-       dev_priv->compressed_fb = compressed_fb;
-       dev_priv->cfb_size = size;
+       dev_priv->fbc.compressed_fb = compressed_fb;
+       dev_priv->fbc.size = size;
 
        DRM_DEBUG_KMS("reserved %d bytes of contiguous stolen space for FBC\n",
                      size);
@@ -136,6 +156,7 @@ static int i915_setup_compression(struct drm_device *dev, int size)
 err_fb:
        drm_mm_put_block(compressed_fb);
 err:
+       pr_info_once("drm: not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size);
        return -ENOSPC;
 }
 
@@ -143,10 +164,10 @@ 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)
+       if (!drm_mm_initialized(&dev_priv->mm.stolen))
                return -ENODEV;
 
-       if (size < dev_priv->cfb_size)
+       if (size < dev_priv->fbc.size)
                return 0;
 
        /* Release any current block */
@@ -159,22 +180,25 @@ void i915_gem_stolen_cleanup_compression(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       if (dev_priv->cfb_size == 0)
+       if (dev_priv->fbc.size == 0)
                return;
 
-       if (dev_priv->compressed_fb)
-               drm_mm_put_block(dev_priv->compressed_fb);
+       if (dev_priv->fbc.compressed_fb)
+               drm_mm_put_block(dev_priv->fbc.compressed_fb);
 
-       if (dev_priv->compressed_llb)
-               drm_mm_put_block(dev_priv->compressed_llb);
+       if (dev_priv->fbc.compressed_llb)
+               drm_mm_put_block(dev_priv->fbc.compressed_llb);
 
-       dev_priv->cfb_size = 0;
+       dev_priv->fbc.size = 0;
 }
 
 void i915_gem_cleanup_stolen(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       if (!drm_mm_initialized(&dev_priv->mm.stolen))
+               return;
+
        i915_gem_stolen_cleanup_compression(dev);
        drm_mm_takedown(&dev_priv->mm.stolen);
 }
@@ -182,6 +206,7 @@ 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;
 
        dev_priv->mm.stolen_base = i915_stolen_to_physical(dev);
        if (dev_priv->mm.stolen_base == 0)
@@ -190,8 +215,15 @@ int i915_gem_init_stolen(struct drm_device *dev)
        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;
+
        /* Basic memrange allocator for stolen space */
-       drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size);
+       drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size -
+                   bios_reserved);
 
        return 0;
 }
@@ -270,7 +302,7 @@ _i915_gem_object_create_stolen(struct drm_device *dev,
                goto cleanup;
 
        obj->has_dma_mapping = true;
-       obj->pages_pin_count = 1;
+       i915_gem_object_pin_pages(obj);
        obj->stolen = stolen;
 
        obj->base.write_domain = I915_GEM_DOMAIN_GTT;
@@ -291,7 +323,7 @@ i915_gem_object_create_stolen(struct drm_device *dev, u32 size)
        struct drm_i915_gem_object *obj;
        struct drm_mm_node *stolen;
 
-       if (dev_priv->mm.stolen_base == 0)
+       if (!drm_mm_initialized(&dev_priv->mm.stolen))
                return NULL;
 
        DRM_DEBUG_KMS("creating stolen object: size=%x\n", size);
@@ -321,8 +353,9 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_gem_object *obj;
        struct drm_mm_node *stolen;
+       int ret;
 
-       if (dev_priv->mm.stolen_base == 0)
+       if (!drm_mm_initialized(&dev_priv->mm.stolen))
                return NULL;
 
        DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%x, gtt_offset=%x, size=%x\n",
@@ -330,17 +363,21 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
 
        /* KISS and expect everything to be page-aligned */
        BUG_ON(stolen_offset & 4095);
-       BUG_ON(gtt_offset & 4095);
        BUG_ON(size & 4095);
 
        if (WARN_ON(size == 0))
                return NULL;
 
-       stolen = drm_mm_create_block(&dev_priv->mm.stolen,
-                                    stolen_offset, size,
-                                    false);
-       if (stolen == NULL) {
+       stolen = kzalloc(sizeof(*stolen), GFP_KERNEL);
+       if (!stolen)
+               return NULL;
+
+       stolen->start = stolen_offset;
+       stolen->size = size;
+       ret = drm_mm_reserve_node(&dev_priv->mm.stolen, stolen);
+       if (ret) {
                DRM_DEBUG_KMS("failed to allocate stolen space\n");
+               kfree(stolen);
                return NULL;
        }
 
@@ -351,30 +388,36 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
                return NULL;
        }
 
+       /* Some objects just need physical mem from stolen space */
+       if (gtt_offset == I915_GTT_OFFSET_NONE)
+               return obj;
+
        /* To simplify the initialisation sequence between KMS and GTT,
         * we allow construction of the stolen object prior to
         * setting up the GTT space. The actual reservation will occur
         * later.
         */
+       obj->gtt_space.start = gtt_offset;
+       obj->gtt_space.size = size;
        if (drm_mm_initialized(&dev_priv->mm.gtt_space)) {
-               obj->gtt_space = drm_mm_create_block(&dev_priv->mm.gtt_space,
-                                                    gtt_offset, size,
-                                                    false);
-               if (obj->gtt_space == NULL) {
+               ret = drm_mm_reserve_node(&dev_priv->mm.gtt_space,
+                                         &obj->gtt_space);
+               if (ret) {
                        DRM_DEBUG_KMS("failed to allocate stolen GTT space\n");
-                       drm_gem_object_unreference(&obj->base);
-                       return NULL;
+                       goto unref_out;
                }
-       } else
-               obj->gtt_space = I915_GTT_RESERVED;
+       }
 
-       obj->gtt_offset = gtt_offset;
        obj->has_global_gtt_mapping = 1;
 
-       list_add_tail(&obj->gtt_list, &dev_priv->mm.bound_list);
+       list_add_tail(&obj->global_list, &dev_priv->mm.bound_list);
        list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list);
 
        return obj;
+
+unref_out:
+       drm_gem_object_unreference(&obj->base);
+       return NULL;
 }
 
 void