diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index ed682a9a9cbba2a0d79af9369a3761e39d6b5e1c..a36cb95ec798ec8a8a3e09da884cf6528d84c126 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -186,11 +186,103 @@ void i915_gem_cleanup_stolen(struct drm_device *dev) drm_mm_takedown(&dev_priv->mm.stolen); } +static void gen6_get_stolen_reserved(struct drm_i915_private *dev_priv, + unsigned long *base, unsigned long *size) +{ + uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); + + *base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK; + + switch (reg_val & GEN6_STOLEN_RESERVED_SIZE_MASK) { + case GEN6_STOLEN_RESERVED_1M: + *size = 1024 * 1024; + break; + case GEN6_STOLEN_RESERVED_512K: + *size = 512 * 1024; + break; + case GEN6_STOLEN_RESERVED_256K: + *size = 256 * 1024; + break; + case GEN6_STOLEN_RESERVED_128K: + *size = 128 * 1024; + break; + default: + *size = 1024 * 1024; + MISSING_CASE(reg_val & GEN6_STOLEN_RESERVED_SIZE_MASK); + } +} + +static void gen7_get_stolen_reserved(struct drm_i915_private *dev_priv, + unsigned long *base, unsigned long *size) +{ + uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); + + *base = reg_val & GEN7_STOLEN_RESERVED_ADDR_MASK; + + switch (reg_val & GEN7_STOLEN_RESERVED_SIZE_MASK) { + case GEN7_STOLEN_RESERVED_1M: + *size = 1024 * 1024; + break; + case GEN7_STOLEN_RESERVED_256K: + *size = 256 * 1024; + break; + default: + *size = 1024 * 1024; + MISSING_CASE(reg_val & GEN7_STOLEN_RESERVED_SIZE_MASK); + } +} + +static void gen8_get_stolen_reserved(struct drm_i915_private *dev_priv, + unsigned long *base, unsigned long *size) +{ + uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); + + *base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK; + + switch (reg_val & GEN8_STOLEN_RESERVED_SIZE_MASK) { + case GEN8_STOLEN_RESERVED_1M: + *size = 1024 * 1024; + break; + case GEN8_STOLEN_RESERVED_2M: + *size = 2 * 1024 * 1024; + break; + case GEN8_STOLEN_RESERVED_4M: + *size = 4 * 1024 * 1024; + break; + case GEN8_STOLEN_RESERVED_8M: + *size = 8 * 1024 * 1024; + break; + default: + *size = 8 * 1024 * 1024; + MISSING_CASE(reg_val & GEN8_STOLEN_RESERVED_SIZE_MASK); + } +} + +static void bdw_get_stolen_reserved(struct drm_i915_private *dev_priv, + unsigned long *base, unsigned long *size) +{ + uint32_t reg_val = I915_READ(GEN6_STOLEN_RESERVED); + unsigned long stolen_top; + + stolen_top = dev_priv->mm.stolen_base + dev_priv->gtt.stolen_size; + + *base = reg_val & GEN6_STOLEN_RESERVED_ADDR_MASK; + + /* On these platforms, the register doesn't have a size field, so the + * size is the distance between the base and the top of the stolen + * memory. We also have the genuine case where base is zero and there's + * nothing reserved. */ + if (*base == 0) + *size = 0; + else + *size = stolen_top - *base; +} + int i915_gem_init_stolen(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 tmp; - int bios_reserved = 0; + unsigned long reserved_total, reserved_base, reserved_size; + unsigned long stolen_top; mutex_init(&dev_priv->mm.stolen_lock); @@ -208,26 +300,61 @@ int i915_gem_init_stolen(struct drm_device *dev) if (dev_priv->mm.stolen_base == 0) 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 (INTEL_INFO(dev)->gen >= 8) { - tmp = I915_READ(GEN7_BIOS_RESERVED); - tmp >>= GEN8_BIOS_RESERVED_SHIFT; - tmp &= GEN8_BIOS_RESERVED_MASK; - bios_reserved = (1024*1024) << tmp; - } else if (IS_GEN7(dev)) { - tmp = I915_READ(GEN7_BIOS_RESERVED); - bios_reserved = tmp & GEN7_BIOS_RESERVED_256K ? - 256*1024 : 1024*1024; + stolen_top = dev_priv->mm.stolen_base + dev_priv->gtt.stolen_size; + + switch (INTEL_INFO(dev_priv)->gen) { + case 2: + case 3: + case 4: + case 5: + /* Assume the gen6 maximum for the older platforms. */ + reserved_size = 1024 * 1024; + reserved_base = stolen_top - reserved_size; + break; + case 6: + gen6_get_stolen_reserved(dev_priv, &reserved_base, + &reserved_size); + break; + case 7: + gen7_get_stolen_reserved(dev_priv, &reserved_base, + &reserved_size); + break; + default: + if (IS_BROADWELL(dev_priv) || IS_SKYLAKE(dev_priv)) + bdw_get_stolen_reserved(dev_priv, &reserved_base, + &reserved_size); + else + gen8_get_stolen_reserved(dev_priv, &reserved_base, + &reserved_size); + break; + } + + /* It is possible for the reserved base to be zero, but the register + * field for size doesn't have a zero option. */ + if (reserved_base == 0) { + reserved_size = 0; + reserved_base = stolen_top; } - if (WARN_ON(bios_reserved > dev_priv->gtt.stolen_size)) + if (reserved_base < dev_priv->mm.stolen_base || + reserved_base + reserved_size > stolen_top) { + DRM_DEBUG_KMS("Stolen reserved area [0x%08lx - 0x%08lx] outside stolen memory [0x%08lx - 0x%08lx]\n", + reserved_base, reserved_base + reserved_size, + dev_priv->mm.stolen_base, stolen_top); return 0; + } + + /* It is possible for the reserved area to end before the end of stolen + * memory, so just consider the start. */ + reserved_total = stolen_top - reserved_base; + + DRM_DEBUG_KMS("Memory reserved for graphics device: %luK, usable: %luK\n", + dev_priv->gtt.stolen_size >> 10, + (dev_priv->gtt.stolen_size - reserved_total) >> 10); /* Basic memrange allocator for stolen space */ drm_mm_init(&dev_priv->mm.stolen, 0, dev_priv->gtt.stolen_size - - bios_reserved); + reserved_total); return 0; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 0f67f3da0762a39135060ad367374400fa9a6243..8e46c348366bc867c7b81ed9ccdd72d7bc57c2fc 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -178,13 +178,22 @@ #define GAB_CTL 0x24000 #define GAB_CTL_CONT_AFTER_PAGEFAULT (1<<8) -#define GEN7_BIOS_RESERVED 0x1082C0 -#define GEN7_BIOS_RESERVED_1M (0 << 5) -#define GEN7_BIOS_RESERVED_256K (1 << 5) -#define GEN8_BIOS_RESERVED_SHIFT 7 -#define GEN7_BIOS_RESERVED_MASK 0x1 -#define GEN8_BIOS_RESERVED_MASK 0x3 - +#define GEN6_STOLEN_RESERVED 0x1082C0 +#define GEN6_STOLEN_RESERVED_ADDR_MASK (0xFFF << 20) +#define GEN7_STOLEN_RESERVED_ADDR_MASK (0x3FFF << 18) +#define GEN6_STOLEN_RESERVED_SIZE_MASK (3 << 4) +#define GEN6_STOLEN_RESERVED_1M (0 << 4) +#define GEN6_STOLEN_RESERVED_512K (1 << 4) +#define GEN6_STOLEN_RESERVED_256K (2 << 4) +#define GEN6_STOLEN_RESERVED_128K (3 << 4) +#define GEN7_STOLEN_RESERVED_SIZE_MASK (1 << 5) +#define GEN7_STOLEN_RESERVED_1M (0 << 5) +#define GEN7_STOLEN_RESERVED_256K (1 << 5) +#define GEN8_STOLEN_RESERVED_SIZE_MASK (3 << 7) +#define GEN8_STOLEN_RESERVED_1M (0 << 7) +#define GEN8_STOLEN_RESERVED_2M (1 << 7) +#define GEN8_STOLEN_RESERVED_4M (2 << 7) +#define GEN8_STOLEN_RESERVED_8M (3 << 7) /* VGA stuff */