};
 
 struct nvif_object {
+       struct nvif_parent *parent;
        struct nvif_client *client;
        const char *name;
        u32 handle;
 
--- /dev/null
+/* SPDX-License-Identifier: MIT */
+#ifndef __NVIF_PARENT_H__
+#define __NVIF_PARENT_H__
+#include <nvif/os.h>
+struct nvif_object;
+
+struct nvif_parent {
+       const struct nvif_parent_func {
+               void (*debugf)(struct nvif_object *, const char *fmt, ...) __printf(2, 3);
+               void (*errorf)(struct nvif_object *, const char *fmt, ...) __printf(2, 3);
+       } *func;
+};
+
+static inline void
+nvif_parent_dtor(struct nvif_parent *parent)
+{
+       parent->func = NULL;
+}
+
+static inline void
+nvif_parent_ctor(const struct nvif_parent_func *func, struct nvif_parent *parent)
+{
+       parent->func = func;
+}
+#endif
 
--- /dev/null
+/* SPDX-License-Identifier: MIT */
+#ifndef __NVIF_PRINTF_H__
+#define __NVIF_PRINTF_H__
+#include <nvif/client.h>
+#include <nvif/parent.h>
+
+#define NVIF_PRINT(l,o,f,a...) do {                                                                \
+       struct nvif_object *_o = (o);                                                              \
+       struct nvif_parent *_p = _o->parent;                                                       \
+       _p->func->l(_o, "[%s/%08x:%s] "f"\n", _o->client->object.name, _o->handle, _o->name, ##a); \
+} while(0)
+
+#ifndef NVIF_DEBUG_PRINT_DISABLE
+#define NVIF_DEBUG(o,f,a...) NVIF_PRINT(debugf, (o), f, ##a)
+#else
+#define NVIF_DEBUG(o,f,a...)
+#endif
+
+#define NVIF_ERROR(o,f,a...) NVIF_PRINT(errorf, (o), f, ##a)
+#endif
 
        nouveau_bo_move_init(drm);
 }
 
+static void __printf(2, 3)
+nouveau_drm_errorf(struct nvif_object *object, const char *fmt, ...)
+{
+       struct nouveau_drm *drm = container_of(object->parent, typeof(*drm), parent);
+       struct va_format vaf;
+       va_list va;
+
+       va_start(va, fmt);
+       vaf.fmt = fmt;
+       vaf.va = &va;
+       NV_ERROR(drm, "%pV", &vaf);
+       va_end(va);
+}
+
+static void __printf(2, 3)
+nouveau_drm_debugf(struct nvif_object *object, const char *fmt, ...)
+{
+       struct nouveau_drm *drm = container_of(object->parent, typeof(*drm), parent);
+       struct va_format vaf;
+       va_list va;
+
+       va_start(va, fmt);
+       vaf.fmt = fmt;
+       vaf.va = &va;
+       NV_DEBUG(drm, "%pV", &vaf);
+       va_end(va);
+}
+
+static const struct nvif_parent_func
+nouveau_parent = {
+       .debugf = nouveau_drm_debugf,
+       .errorf = nouveau_drm_errorf,
+};
+
 static int
 nouveau_drm_device_init(struct drm_device *dev)
 {
        dev->dev_private = drm;
        drm->dev = dev;
 
+       nvif_parent_ctor(&nouveau_parent, &drm->parent);
+       drm->master.base.object.parent = &drm->parent;
+
        ret = nouveau_cli_init(drm, "DRM-master", &drm->master);
        if (ret)
                goto fail_alloc;
 fail_master:
        nouveau_cli_fini(&drm->master);
 fail_alloc:
+       nvif_parent_dtor(&drm->parent);
        kfree(drm);
        return ret;
 }
 
        nouveau_cli_fini(&drm->client);
        nouveau_cli_fini(&drm->master);
+       nvif_parent_dtor(&drm->parent);
        kfree(drm);
 }
 
 
 }
 
 #include <nvif/object.h>
+#include <nvif/parent.h>
 
 struct nouveau_drm {
+       struct nvif_parent parent;
        struct nouveau_cli master;
        struct nouveau_cli client;
        struct drm_device *dev;
 
                        return -ENOMEM;
                }
 
+               object->parent = parent->parent;
+
                args->ioctl.version = 0;
                args->ioctl.type = NVIF_IOCTL_V0_NEW;
                args->new.version = 0;