struct nvkm_vma;
 struct nvkm_vm;
 
+struct nvkm_tags {
+       struct nvkm_mm_node *mn;
+       refcount_t refcount;
+};
+
 enum nvkm_memory_target {
        NVKM_MEM_TARGET_INST, /* instance memory */
        NVKM_MEM_TARGET_VRAM, /* video memory */
 struct nvkm_memory {
        const struct nvkm_memory_func *func;
        const struct nvkm_memory_ptrs *ptrs;
+       struct nvkm_tags *tags;
 };
 
 struct nvkm_memory_func {
 int nvkm_memory_new(struct nvkm_device *, enum nvkm_memory_target,
                    u64 size, u32 align, bool zero, struct nvkm_memory **);
 void nvkm_memory_del(struct nvkm_memory **);
+int nvkm_memory_tags_get(struct nvkm_memory *, struct nvkm_device *, u32 tags,
+                        void (*clear)(struct nvkm_device *, u32, u32),
+                        struct nvkm_tags **);
+void nvkm_memory_tags_put(struct nvkm_memory *, struct nvkm_device *,
+                         struct nvkm_tags **);
+
 #define nvkm_memory_target(p) (p)->func->target(p)
 #define nvkm_memory_addr(p) (p)->func->addr(p)
 #define nvkm_memory_size(p) (p)->func->size(p)
 
  * Authors: Ben Skeggs <bskeggs@redhat.com>
  */
 #include <core/memory.h>
+#include <core/mm.h>
+#include <subdev/fb.h>
 #include <subdev/instmem.h>
 
+void
+nvkm_memory_tags_put(struct nvkm_memory *memory, struct nvkm_device *device,
+                    struct nvkm_tags **ptags)
+{
+       struct nvkm_fb *fb = device->fb;
+       struct nvkm_tags *tags = *ptags;
+       if (tags) {
+               mutex_lock(&fb->subdev.mutex);
+               if (refcount_dec_and_test(&tags->refcount)) {
+                       nvkm_mm_free(&fb->tags, &tags->mn);
+                       kfree(memory->tags);
+                       memory->tags = NULL;
+               }
+               mutex_unlock(&fb->subdev.mutex);
+               *ptags = NULL;
+       }
+}
+
+int
+nvkm_memory_tags_get(struct nvkm_memory *memory, struct nvkm_device *device,
+                    u32 nr, void (*clr)(struct nvkm_device *, u32, u32),
+                    struct nvkm_tags **ptags)
+{
+       struct nvkm_fb *fb = device->fb;
+       struct nvkm_tags *tags;
+
+       mutex_lock(&fb->subdev.mutex);
+       if ((tags = memory->tags)) {
+               /* If comptags exist for the memory, but a different amount
+                * than requested, the buffer is being mapped with settings
+                * that are incompatible with existing mappings.
+                */
+               if (tags->mn && tags->mn->length != nr) {
+                       mutex_unlock(&fb->subdev.mutex);
+                       return -EINVAL;
+               }
+
+               refcount_inc(&tags->refcount);
+               *ptags = tags;
+               return 0;
+       }
+
+       if (!(tags = kmalloc(sizeof(*tags), GFP_KERNEL))) {
+               mutex_unlock(&fb->subdev.mutex);
+               return -ENOMEM;
+       }
+
+       if (!nvkm_mm_head(&fb->tags, 0, 1, nr, nr, 1, &tags->mn)) {
+               if (clr)
+                       clr(device, tags->mn->offset, tags->mn->length);
+       } else {
+               /* Failure to allocate HW comptags is not an error, the
+                * caller should fall back to an uncompressed map.
+                *
+                * As memory can be mapped in multiple places, we still
+                * need to track the allocation failure and ensure that
+                * any additional mappings remain uncompressed.
+                *
+                * This is handled by returning an empty nvkm_tags.
+                */
+               tags->mn = NULL;
+       }
+
+       refcount_set(&tags->refcount, 1);
+       mutex_unlock(&fb->subdev.mutex);
+       *ptags = tags;
+       return 0;
+}
+
 void
 nvkm_memory_ctor(const struct nvkm_memory_func *func,
                 struct nvkm_memory *memory)