#include "intel_gtt.h"
 #include "gen8_ppgtt.h"
 
+static inline bool suspend_retains_ptes(struct i915_address_space *vm)
+{
+       return GRAPHICS_VER(vm->i915) >= 8 &&
+               !HAS_LMEM(vm->i915) &&
+               vm->is_ggtt;
+}
+
 static void i915_ggtt_color_adjust(const struct drm_mm_node *node,
                                   unsigned long color,
                                   u64 *start,
        return 0;
 }
 
+/*
+ * Return the value of the last GGTT pte cast to an u64, if
+ * the system is supposed to retain ptes across resume. 0 otherwise.
+ */
+static u64 read_last_pte(struct i915_address_space *vm)
+{
+       struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm);
+       gen8_pte_t __iomem *ptep;
+
+       if (!suspend_retains_ptes(vm))
+               return 0;
+
+       GEM_BUG_ON(GRAPHICS_VER(vm->i915) < 8);
+       ptep = (typeof(ptep))ggtt->gsm + (ggtt_total_entries(ggtt) - 1);
+       return readq(ptep);
+}
+
 /**
  * i915_ggtt_suspend_vm - Suspend the memory mappings for a GGTT or DPT VM
  * @vm: The VM to suspend the mappings for
                i915_gem_object_unlock(obj);
        }
 
-       vm->clear_range(vm, 0, vm->total);
+       if (!suspend_retains_ptes(vm))
+               vm->clear_range(vm, 0, vm->total);
+       else
+               i915_vm_to_ggtt(vm)->probed_pte = read_last_pte(vm);
 
        vm->skip_pte_rewrite = save_skip_rewrite;
 
        struct drm_mm_node *entry;
        int ret;
 
+       ggtt->pte_lost = true;
+
        /*
         * GuC requires all resources that we're sharing with it to be placed in
         * non-WOPCM memory. If GuC is not present or not in use we still need a
 {
        struct i915_vma *vma;
        bool write_domain_objs = false;
+       bool retained_ptes;
 
        drm_WARN_ON(&vm->i915->drm, !vm->is_ggtt && !vm->is_dpt);
 
-       /* First fill our portion of the GTT with scratch pages */
-       vm->clear_range(vm, 0, vm->total);
+       /*
+        * First fill our portion of the GTT with scratch pages if
+        * they were not retained across suspend.
+        */
+       retained_ptes = suspend_retains_ptes(vm) &&
+               !i915_vm_to_ggtt(vm)->pte_lost &&
+               !GEM_WARN_ON(i915_vm_to_ggtt(vm)->probed_pte != read_last_pte(vm));
+
+       if (!retained_ptes)
+               vm->clear_range(vm, 0, vm->total);
 
        /* clflush objects bound into the GGTT and rebind them. */
        list_for_each_entry(vma, &vm->bound_list, vm_link) {
                        atomic_read(&vma->flags) & I915_VMA_BIND_MASK;
 
                GEM_BUG_ON(!was_bound);
-               vma->ops->bind_vma(vm, NULL, vma->resource,
-                                  obj ? obj->cache_level : 0,
-                                  was_bound);
+               if (!retained_ptes)
+                       vma->ops->bind_vma(vm, NULL, vma->resource,
+                                          obj ? obj->cache_level : 0,
+                                          was_bound);
                if (obj) { /* only used during resume => exclusive access */
                        write_domain_objs |= fetch_and_zero(&obj->write_domain);
                        obj->read_domains |= I915_GEM_DOMAIN_GTT;
 
        intel_ggtt_restore_fences(ggtt);
 }
+
+void i915_ggtt_mark_pte_lost(struct drm_i915_private *i915, bool val)
+{
+       to_gt(i915)->ggtt->pte_lost = val;
+}
 
 
        bool do_idle_maps;
 
+       /**
+        * @pte_lost: Are ptes lost on resume?
+        *
+        * Whether the system was recently restored from hibernate and
+        * thus may have lost pte content.
+        */
+       bool pte_lost;
+
+       /**
+        * @probed_pte: Probed pte value on suspend. Re-checked on resume.
+        */
+       u64 probed_pte;
+
        int mtrr;
 
        /** Bit 6 swizzling required for X tiling */
 void i915_ggtt_suspend(struct i915_ggtt *gtt);
 void i915_ggtt_resume(struct i915_ggtt *ggtt);
 
+/**
+ * i915_ggtt_mark_pte_lost - Mark ggtt ptes as lost or clear such a marking
+ * @i915 The device private.
+ * @val whether the ptes should be marked as lost.
+ *
+ * In some cases pte content is retained across suspend, but typically lost
+ * across hibernate. Typically they should be marked as lost on
+ * hibernation restore and such marking cleared on suspend.
+ */
+void i915_ggtt_mark_pte_lost(struct drm_i915_private *i915, bool val);
+
 void
 fill_page_dma(struct drm_i915_gem_object *p, const u64 val, unsigned int count);
 
 
 #include "intel_region_ttm.h"
 #include "vlv_suspend.h"
 
+/* Intel Rapid Start Technology ACPI device name */
+static const char irst_name[] = "INT3392";
+
 static const struct drm_driver i915_drm_driver;
 
 static int i915_get_bridge_dev(struct drm_i915_private *dev_priv)
                return -ENODEV;
        }
 
+       i915_ggtt_mark_pte_lost(i915, false);
+
        if (i915->drm.switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
        if (i915->drm.switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
+       /*
+        * If IRST is enabled, or if we can't detect whether it's enabled,
+        * then we must assume we lost the GGTT page table entries, since
+        * they are not retained if IRST decided to enter S4.
+        */
+       if (!IS_ENABLED(CONFIG_ACPI) || acpi_dev_present(irst_name, NULL, -1))
+               i915_ggtt_mark_pte_lost(i915, true);
+
        return i915_drm_resume(&i915->drm);
 }
 
 
 static int i915_pm_restore(struct device *kdev)
 {
+       struct drm_i915_private *i915 = kdev_to_i915(kdev);
+
+       i915_ggtt_mark_pte_lost(i915, true);
        return i915_pm_resume(kdev);
 }