return -ENODEV;
 
        if (obj->ops->mmap_offset)  {
+               if (mmap_type != I915_MMAP_TYPE_FIXED)
+                       return -ENODEV;
+
                *offset = obj->ops->mmap_offset(obj);
                return 0;
        }
 
+       if (mmap_type == I915_MMAP_TYPE_FIXED)
+               return -ENODEV;
+
        if (mmap_type != I915_MMAP_TYPE_GTT &&
            !i915_gem_object_has_struct_page(obj) &&
            !i915_gem_object_has_iomem(obj))
 {
        enum i915_mmap_type mmap_type;
 
-       if (boot_cpu_has(X86_FEATURE_PAT))
+       if (HAS_LMEM(to_i915(dev)))
+               mmap_type = I915_MMAP_TYPE_FIXED;
+       else if (boot_cpu_has(X86_FEATURE_PAT))
                mmap_type = I915_MMAP_TYPE_WC;
        else if (!i915_ggtt_has_aperture(&to_i915(dev)->ggtt))
                return -ENODEV;
                type = I915_MMAP_TYPE_UC;
                break;
 
+       case I915_MMAP_OFFSET_FIXED:
+               type = I915_MMAP_TYPE_FIXED;
+               break;
+
        default:
                return -EINVAL;
        }
                vma->vm_ops = &vm_ops_cpu;
                break;
 
+       case I915_MMAP_TYPE_FIXED:
+               GEM_WARN_ON(1);
+               fallthrough;
        case I915_MMAP_TYPE_WB:
                vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
                vma->vm_ops = &vm_ops_cpu;
 
        I915_MMAP_TYPE_WC,
        I915_MMAP_TYPE_WB,
        I915_MMAP_TYPE_UC,
+       I915_MMAP_TYPE_FIXED,
 };
 
 struct i915_mmap_offset {
 
        return 0;
 }
 
+static enum i915_mmap_type default_mapping(struct drm_i915_private *i915)
+{
+       if (HAS_LMEM(i915))
+               return I915_MMAP_TYPE_FIXED;
+
+       return I915_MMAP_TYPE_GTT;
+}
+
 static bool assert_mmap_offset(struct drm_i915_private *i915,
                               unsigned long size,
                               int expected)
        if (IS_ERR(obj))
                return expected && expected == PTR_ERR(obj);
 
-       ret = __assign_mmap_offset(obj, I915_MMAP_TYPE_GTT, &offset, NULL);
+       ret = __assign_mmap_offset(obj, default_mapping(i915), &offset, NULL);
        i915_gem_object_put(obj);
 
        return ret == expected;
                goto out;
        }
 
-       err = __assign_mmap_offset(obj, I915_MMAP_TYPE_GTT, &offset, NULL);
+       err = __assign_mmap_offset(obj, default_mapping(i915), &offset, NULL);
        if (err) {
                pr_err("Unable to insert object into reclaimed hole\n");
                goto err_obj;
 
 static bool can_mmap(struct drm_i915_gem_object *obj, enum i915_mmap_type type)
 {
+       struct drm_i915_private *i915 = to_i915(obj->base.dev);
        bool no_map;
 
+       if (HAS_LMEM(i915))
+               return type == I915_MMAP_TYPE_FIXED;
+       else if (type == I915_MMAP_TYPE_FIXED)
+               return false;
+
        if (type == I915_MMAP_TYPE_GTT &&
            !i915_ggtt_has_aperture(&to_i915(obj->base.dev)->ggtt))
                return false;
                        err = __igt_mmap(i915, obj, I915_MMAP_TYPE_GTT);
                        if (err == 0)
                                err = __igt_mmap(i915, obj, I915_MMAP_TYPE_WC);
+                       if (err == 0)
+                               err = __igt_mmap(i915, obj, I915_MMAP_TYPE_FIXED);
 
                        i915_gem_object_put(obj);
                        if (err)
        case I915_MMAP_TYPE_WB: return "wb";
        case I915_MMAP_TYPE_WC: return "wc";
        case I915_MMAP_TYPE_UC: return "uc";
+       case I915_MMAP_TYPE_FIXED: return "fixed";
        default: return "unknown";
        }
 }
                        err = __igt_mmap_access(i915, obj, I915_MMAP_TYPE_WC);
                if (err == 0)
                        err = __igt_mmap_access(i915, obj, I915_MMAP_TYPE_UC);
+               if (err == 0)
+                       err = __igt_mmap_access(i915, obj, I915_MMAP_TYPE_FIXED);
 
                i915_gem_object_put(obj);
                if (err)
                err = __igt_mmap_gpu(i915, obj, I915_MMAP_TYPE_GTT);
                if (err == 0)
                        err = __igt_mmap_gpu(i915, obj, I915_MMAP_TYPE_WC);
+               if (err == 0)
+                       err = __igt_mmap_gpu(i915, obj, I915_MMAP_TYPE_FIXED);
 
                i915_gem_object_put(obj);
                if (err)
                err = __igt_mmap_revoke(i915, obj, I915_MMAP_TYPE_GTT);
                if (err == 0)
                        err = __igt_mmap_revoke(i915, obj, I915_MMAP_TYPE_WC);
+               if (err == 0)
+                       err = __igt_mmap_revoke(i915, obj, I915_MMAP_TYPE_FIXED);
 
                i915_gem_object_put(obj);
                if (err)
 
        __u64 offset;
 };
 
+/**
+ * struct drm_i915_gem_mmap_offset - Retrieve an offset so we can mmap this buffer object.
+ *
+ * This struct is passed as argument to the `DRM_IOCTL_I915_GEM_MMAP_OFFSET` ioctl,
+ * and is used to retrieve the fake offset to mmap an object specified by &handle.
+ *
+ * The legacy way of using `DRM_IOCTL_I915_GEM_MMAP` is removed on gen12+.
+ * `DRM_IOCTL_I915_GEM_MMAP_GTT` is an older supported alias to this struct, but will behave
+ * as setting the &extensions to 0, and &flags to `I915_MMAP_OFFSET_GTT`.
+ */
 struct drm_i915_gem_mmap_offset {
-       /** Handle for the object being mapped. */
+       /** @handle: Handle for the object being mapped. */
        __u32 handle;
+       /** @pad: Must be zero */
        __u32 pad;
        /**
-        * Fake offset to use for subsequent mmap call
+        * @offset: The fake offset to use for subsequent mmap call
         *
         * This is a fixed-size type for 32/64 compatibility.
         */
        __u64 offset;
 
        /**
-        * Flags for extended behaviour.
+        * @flags: Flags for extended behaviour.
         *
-        * It is mandatory that one of the MMAP_OFFSET types
-        * (GTT, WC, WB, UC, etc) should be included.
+        * It is mandatory that one of the `MMAP_OFFSET` types
+        * should be included:
+        *
+        * - `I915_MMAP_OFFSET_GTT`: Use mmap with the object bound to GTT. (Write-Combined)
+        * - `I915_MMAP_OFFSET_WC`: Use Write-Combined caching.
+        * - `I915_MMAP_OFFSET_WB`: Use Write-Back caching.
+        * - `I915_MMAP_OFFSET_FIXED`: Use object placement to determine caching.
+        *
+        * On devices with local memory `I915_MMAP_OFFSET_FIXED` is the only valid
+        * type. On devices without local memory, this caching mode is invalid.
+        *
+        * As caching mode when specifying `I915_MMAP_OFFSET_FIXED`, WC or WB will
+        * be used, depending on the object placement on creation. WB will be used
+        * when the object can only exist in system memory, WC otherwise.
         */
        __u64 flags;
-#define I915_MMAP_OFFSET_GTT 0
-#define I915_MMAP_OFFSET_WC  1
-#define I915_MMAP_OFFSET_WB  2
-#define I915_MMAP_OFFSET_UC  3
 
-       /*
-        * Zero-terminated chain of extensions.
+#define I915_MMAP_OFFSET_GTT   0
+#define I915_MMAP_OFFSET_WC    1
+#define I915_MMAP_OFFSET_WB    2
+#define I915_MMAP_OFFSET_UC    3
+#define I915_MMAP_OFFSET_FIXED 4
+
+       /**
+        * @extensions: Zero-terminated chain of extensions.
         *
         * No current extensions defined; mbz.
         */