1277487Skib/* 2277487Skib * Copyright �� 2008-2012 Intel Corporation 3277487Skib * 4277487Skib * Permission is hereby granted, free of charge, to any person obtaining a 5277487Skib * copy of this software and associated documentation files (the "Software"), 6277487Skib * to deal in the Software without restriction, including without limitation 7277487Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8277487Skib * and/or sell copies of the Software, and to permit persons to whom the 9277487Skib * Software is furnished to do so, subject to the following conditions: 10277487Skib * 11277487Skib * The above copyright notice and this permission notice (including the next 12277487Skib * paragraph) shall be included in all copies or substantial portions of the 13277487Skib * Software. 14277487Skib * 15277487Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16277487Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17277487Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18277487Skib * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19277487Skib * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20277487Skib * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21277487Skib * IN THE SOFTWARE. 22277487Skib * 23277487Skib * Authors: 24277487Skib * Eric Anholt <eric@anholt.net> 25277487Skib * Chris Wilson <chris@chris-wilson.co.uk> 26277487Skib * 27277487Skib */ 28277487Skib 29277487Skib#include <sys/cdefs.h> 30277487Skib__FBSDID("$FreeBSD$"); 31277487Skib 32277487Skib#include <dev/drm2/drmP.h> 33277487Skib#include <dev/drm2/i915/i915_drm.h> 34277487Skib#include <dev/drm2/i915/i915_drv.h> 35277487Skib 36277487Skib/* 37277487Skib * The BIOS typically reserves some of the system's memory for the exclusive 38277487Skib * use of the integrated graphics. This memory is no longer available for 39277487Skib * use by the OS and so the user finds that his system has less memory 40277487Skib * available than he put in. We refer to this memory as stolen. 41277487Skib * 42277487Skib * The BIOS will allocate its framebuffer from the stolen memory. Our 43277487Skib * goal is try to reuse that object for our own fbcon which must always 44277487Skib * be available for panics. Anything else we can reuse the stolen memory 45277487Skib * for is a boon. 46277487Skib */ 47277487Skib 48296548Sdumbbellstatic unsigned long i915_stolen_to_physical(struct drm_device *dev) 49277487Skib{ 50277487Skib u32 base; 51277487Skib 52277487Skib /* On the machines I have tested the Graphics Base of Stolen Memory 53296548Sdumbbell * is unreliable, so on those compute the base by subtracting the 54296548Sdumbbell * stolen memory from the Top of Low Usable DRAM which is where the 55296548Sdumbbell * BIOS places the graphics stolen memory. 56296548Sdumbbell * 57296548Sdumbbell * On gen2, the layout is slightly different with the Graphics Segment 58296548Sdumbbell * immediately following Top of Memory (or Top of Usable DRAM). Note 59296548Sdumbbell * it appears that TOUD is only reported by 865g, so we just use the 60296548Sdumbbell * top of memory as determined by the e820 probe. 61296548Sdumbbell * 62296548Sdumbbell * XXX gen2 requires an unavailable symbol and 945gm fails with 63296548Sdumbbell * its value of TOLUD. 64277487Skib */ 65296548Sdumbbell base = 0; 66296548Sdumbbell if (INTEL_INFO(dev)->gen >= 6) { 67296548Sdumbbell /* Read Base Data of Stolen Memory Register (BDSM) directly. 68296548Sdumbbell * Note that there is also a MCHBAR miror at 0x1080c0 or 69296548Sdumbbell * we could use device 2:0x5c instead. 70296548Sdumbbell */ 71296548Sdumbbell pci_read_config_dword(dev->dev, 0xB0, &base); 72296548Sdumbbell base &= ~4095; /* lower bits used for locking register */ 73296548Sdumbbell } else if (INTEL_INFO(dev)->gen > 3 || IS_G33(dev)) { 74296548Sdumbbell /* Read Graphics Base of Stolen Memory directly */ 75296548Sdumbbell pci_read_config_dword(dev->dev, 0xA4, &base); 76296548Sdumbbell#if 0 77296548Sdumbbell } else if (IS_GEN3(dev)) { 78277487Skib u8 val; 79296548Sdumbbell /* Stolen is immediately below Top of Low Usable DRAM */ 80296548Sdumbbell pci_read_config_byte(pdev, 0x9c, &val); 81277487Skib base = val >> 3 << 27; 82296548Sdumbbell base -= dev_priv->mm.gtt->stolen_size; 83296548Sdumbbell } else { 84296548Sdumbbell /* Stolen is immediately above Top of Memory */ 85296548Sdumbbell base = max_low_pfn_mapped << PAGE_SHIFT; 86296548Sdumbbell#endif 87277487Skib } 88277487Skib 89296548Sdumbbell return base; 90277487Skib} 91277487Skib 92277487Skibstatic void i915_warn_stolen(struct drm_device *dev) 93277487Skib{ 94277487Skib DRM_INFO("not enough stolen space for compressed buffer, disabling\n"); 95277487Skib DRM_INFO("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n"); 96277487Skib} 97277487Skib 98277487Skibstatic void i915_setup_compression(struct drm_device *dev, int size) 99277487Skib{ 100277487Skib struct drm_i915_private *dev_priv = dev->dev_private; 101296548Sdumbbell struct drm_mm_node *compressed_fb, *uninitialized_var(compressed_llb); 102277487Skib unsigned long cfb_base; 103277487Skib unsigned long ll_base = 0; 104277487Skib 105277487Skib /* Just in case the BIOS is doing something questionable. */ 106277487Skib intel_disable_fbc(dev); 107277487Skib 108277487Skib compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); 109277487Skib if (compressed_fb) 110277487Skib compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); 111277487Skib if (!compressed_fb) 112277487Skib goto err; 113277487Skib 114296548Sdumbbell cfb_base = dev_priv->mm.stolen_base + compressed_fb->start; 115277487Skib if (!cfb_base) 116277487Skib goto err_fb; 117277487Skib 118277487Skib if (!(IS_GM45(dev) || HAS_PCH_SPLIT(dev))) { 119277487Skib compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen, 120277487Skib 4096, 4096, 0); 121277487Skib if (compressed_llb) 122277487Skib compressed_llb = drm_mm_get_block(compressed_llb, 123277487Skib 4096, 4096); 124277487Skib if (!compressed_llb) 125277487Skib goto err_fb; 126277487Skib 127296548Sdumbbell ll_base = dev_priv->mm.stolen_base + compressed_llb->start; 128277487Skib if (!ll_base) 129277487Skib goto err_llb; 130277487Skib } 131277487Skib 132277487Skib dev_priv->cfb_size = size; 133277487Skib 134277487Skib dev_priv->compressed_fb = compressed_fb; 135277487Skib if (HAS_PCH_SPLIT(dev)) 136277487Skib I915_WRITE(ILK_DPFC_CB_BASE, compressed_fb->start); 137277487Skib else if (IS_GM45(dev)) { 138277487Skib I915_WRITE(DPFC_CB_BASE, compressed_fb->start); 139277487Skib } else { 140277487Skib I915_WRITE(FBC_CFB_BASE, cfb_base); 141277487Skib I915_WRITE(FBC_LL_BASE, ll_base); 142277487Skib dev_priv->compressed_llb = compressed_llb; 143277487Skib } 144277487Skib 145277487Skib DRM_DEBUG_KMS("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", 146296548Sdumbbell (long)cfb_base, (long)ll_base, size >> 20); 147277487Skib return; 148277487Skib 149277487Skiberr_llb: 150277487Skib drm_mm_put_block(compressed_llb); 151277487Skiberr_fb: 152277487Skib drm_mm_put_block(compressed_fb); 153277487Skiberr: 154277487Skib dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL; 155277487Skib i915_warn_stolen(dev); 156277487Skib} 157277487Skib 158277487Skibstatic void i915_cleanup_compression(struct drm_device *dev) 159277487Skib{ 160277487Skib struct drm_i915_private *dev_priv = dev->dev_private; 161277487Skib 162277487Skib drm_mm_put_block(dev_priv->compressed_fb); 163277487Skib if (dev_priv->compressed_llb) 164277487Skib drm_mm_put_block(dev_priv->compressed_llb); 165277487Skib} 166277487Skib 167277487Skibvoid i915_gem_cleanup_stolen(struct drm_device *dev) 168277487Skib{ 169277487Skib if (I915_HAS_FBC(dev) && i915_powersave) 170277487Skib i915_cleanup_compression(dev); 171277487Skib} 172277487Skib 173277487Skibint i915_gem_init_stolen(struct drm_device *dev) 174277487Skib{ 175277487Skib struct drm_i915_private *dev_priv = dev->dev_private; 176296548Sdumbbell unsigned long prealloc_size = dev_priv->mm.gtt->stolen_size; 177277487Skib 178296548Sdumbbell dev_priv->mm.stolen_base = i915_stolen_to_physical(dev); 179296548Sdumbbell if (dev_priv->mm.stolen_base == 0) 180296548Sdumbbell return 0; 181296548Sdumbbell 182296548Sdumbbell DRM_DEBUG_KMS("found %d bytes of stolen memory at %08lx\n", 183296548Sdumbbell dev_priv->mm.gtt->stolen_size, dev_priv->mm.stolen_base); 184296548Sdumbbell 185277487Skib /* Basic memrange allocator for stolen space */ 186277487Skib drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size); 187277487Skib 188277487Skib /* Try to set up FBC with a reasonable compressed buffer size */ 189277487Skib if (I915_HAS_FBC(dev) && i915_powersave) { 190277487Skib int cfb_size; 191277487Skib 192277487Skib /* Leave 1M for line length buffer & misc. */ 193277487Skib 194277487Skib /* Try to get a 32M buffer... */ 195277487Skib if (prealloc_size > (36*1024*1024)) 196277487Skib cfb_size = 32*1024*1024; 197277487Skib else /* fall back to 7/8 of the stolen space */ 198277487Skib cfb_size = prealloc_size * 7 / 8; 199277487Skib i915_setup_compression(dev, cfb_size); 200277487Skib } 201277487Skib 202277487Skib return 0; 203277487Skib} 204