#define ICL_DSSM_CDCLK_PLL_REFCLK_19_2MHz      (1 << 29)
 #define ICL_DSSM_CDCLK_PLL_REFCLK_38_4MHz      (2 << 29)
 
+#define GMD_ID_DISPLAY                         _MMIO(0x510a0)
+#define   GMD_ID_ARCH_MASK                     REG_GENMASK(31, 22)
+#define   GMD_ID_RELEASE_MASK                  REG_GENMASK(21, 14)
+#define   GMD_ID_STEP                          REG_GENMASK(5, 0)
+
 /*GEN11 chicken */
 #define _PIPEA_CHICKEN                         0x70038
 #define _PIPEB_CHICKEN                         0x71038
 #define   MTL_TRAS_MASK                        REG_GENMASK(16, 8)
 #define   MTL_TRDPRE_MASK              REG_GENMASK(7, 0)
 
+#define MTL_MEDIA_GSI_BASE             0x380000
+
 #endif /* _I915_REG_H_ */
 
 
 #include "display/intel_cdclk.h"
 #include "display/intel_de.h"
+#include "gt/intel_gt_regs.h"
 #include "intel_device_info.h"
 #include "i915_drv.h"
 #include "i915_utils.h"
        return false;
 }
 
-void intel_device_info_subplatform_init(struct drm_i915_private *i915)
+static void intel_device_info_subplatform_init(struct drm_i915_private *i915)
 {
        const struct intel_device_info *info = INTEL_INFO(i915);
        const struct intel_runtime_info *rinfo = RUNTIME_INFO(i915);
        RUNTIME_INFO(i915)->platform_mask[pi] |= mask;
 }
 
+static void ip_ver_read(struct drm_i915_private *i915, u32 offset, struct ip_version *ip)
+{
+       struct pci_dev *pdev = to_pci_dev(i915->drm.dev);
+       void __iomem *addr;
+       u32 val;
+       u8 expected_ver = ip->ver;
+       u8 expected_rel = ip->rel;
+
+       addr = pci_iomap_range(pdev, 0, offset, sizeof(u32));
+       if (drm_WARN_ON(&i915->drm, !addr))
+               return;
+
+       val = ioread32(addr);
+       pci_iounmap(pdev, addr);
+
+       ip->ver = REG_FIELD_GET(GMD_ID_ARCH_MASK, val);
+       ip->rel = REG_FIELD_GET(GMD_ID_RELEASE_MASK, val);
+       ip->step = REG_FIELD_GET(GMD_ID_STEP, val);
+
+       /* Sanity check against expected versions from device info */
+       if (IP_VER(ip->ver, ip->rel) < IP_VER(expected_ver, expected_rel))
+               drm_dbg(&i915->drm,
+                       "Hardware reports GMD IP version %u.%u (REG[0x%x] = 0x%08x) but minimum expected is %u.%u\n",
+                       ip->ver, ip->rel, offset, val, expected_ver, expected_rel);
+}
+
+/*
+ * Setup the graphics version for the current device.  This must be done before
+ * any code that performs checks on GRAPHICS_VER or DISPLAY_VER, so this
+ * function should be called very early in the driver initialization sequence.
+ *
+ * Regular MMIO access is not yet setup at the point this function is called so
+ * we peek at the appropriate MMIO offset directly.  The GMD_ID register is
+ * part of an 'always on' power well by design, so we don't need to worry about
+ * forcewake while reading it.
+ */
+static void intel_ipver_early_init(struct drm_i915_private *i915)
+{
+       struct intel_runtime_info *runtime = RUNTIME_INFO(i915);
+
+       if (!HAS_GMD_ID(i915))
+               return;
+
+       ip_ver_read(i915, i915_mmio_reg_offset(GMD_ID_GRAPHICS),
+                   &runtime->graphics.ip);
+       ip_ver_read(i915, i915_mmio_reg_offset(GMD_ID_DISPLAY),
+                   &runtime->display.ip);
+       ip_ver_read(i915, i915_mmio_reg_offset(GMD_ID_MEDIA),
+                   &runtime->media.ip);
+}
+
+/**
+ * intel_device_info_runtime_init_early - initialize early runtime info
+ * @i915: the i915 device
+ *
+ * Determine early intel_device_info fields at runtime. This function needs
+ * to be called before the MMIO has been setup.
+ */
+void intel_device_info_runtime_init_early(struct drm_i915_private *i915)
+{
+       intel_ipver_early_init(i915);
+       intel_device_info_subplatform_init(i915);
+}
+
 /**
  * intel_device_info_runtime_init - initialize runtime info
  * @dev_priv: the i915 device