nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crc.o
 nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crc907d.o
 nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crcc37d.o
+nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crcc57d.o
 
 nouveau-y += dispnv50/dac507d.o
 nouveau-y += dispnv50/dac907d.o
 
        .head = &headc57d,
        .sor = &sorc37d,
 #if IS_ENABLED(CONFIG_DEBUG_FS)
-       .crc = &crcc37d,
+       .crc = &crcc57d,
 #endif
 };
 
 
                               struct nv50_head_atom *armh)
 {
        struct nv50_atom *atom = nv50_atom(asyh->state.state);
-       struct drm_device *dev = head->base.base.dev;
-       struct nv50_disp *disp = nv50_disp(dev);
        bool changed = armh->crc.src != asyh->crc.src;
 
        if (!armh->crc.src && !asyh->crc.src) {
                return 0;
        }
 
-       /* While we don't care about entry tags, Volta+ hw always needs the
-        * controlling wndw channel programmed to a wndw that's owned by our
-        * head
-        */
-       if (asyh->crc.src && disp->disp->object.oclass >= GV100_DISP &&
-           !(BIT(asyh->crc.wndw) & asyh->wndw.owned)) {
-               if (!asyh->wndw.owned) {
-                       /* TODO: once we support flexible channel ownership,
-                        * we should write some code here to handle attempting
-                        * to "steal" a plane: e.g. take a plane that is
-                        * currently not-visible and owned by another head,
-                        * and reassign it to this head. If we fail to do so,
-                        * we shuld reject the mode outright as CRC capture
-                        * then becomes impossible.
-                        */
-                       NV_ATOMIC(nouveau_drm(dev),
-                                 "No available wndws for CRC readback\n");
-                       return -EINVAL;
-               }
-               asyh->crc.wndw = ffs(asyh->wndw.owned) - 1;
-       }
-
-       if (drm_atomic_crtc_needs_modeset(&asyh->state) || changed ||
-           armh->crc.wndw != asyh->crc.wndw) {
+       if (drm_atomic_crtc_needs_modeset(&asyh->state) || changed) {
                asyh->clr.crc = armh->crc.src && armh->state.active;
                asyh->set.crc = asyh->crc.src && asyh->state.active;
                if (changed)
        struct nouveau_encoder *outp =
                nv50_real_outp(nv50_head_atom_get_encoder(asyh));
 
-       func->set_src(head, outp->or,
-                     nv50_crc_source_type(outp, asyh->crc.src),
-                     &crc->ctx[crc->ctx_idx], asyh->crc.wndw);
+       func->set_src(head, outp->or, nv50_crc_source_type(outp, asyh->crc.src),
+                     &crc->ctx[crc->ctx_idx]);
 }
 
 void nv50_crc_atomic_clr(struct nv50_head *head)
        const struct nv50_crc_func *func =
                nv50_disp(head->base.base.dev)->core->func->crc;
 
-       func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE, NULL, 0);
+       func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE, NULL);
 }
 
 static inline int
 
 
 struct nv50_crc_atom {
        enum nv50_crc_source src;
-       /* Only used for gv100+ */
-       u8 wndw : 4;
 };
 
 struct nv50_crc_func {
-       int (*set_src)(struct nv50_head *, int or, enum nv50_crc_source_type,
-                      struct nv50_crc_notifier_ctx *, u32 wndw);
+       int (*set_src)(struct nv50_head *, int or, enum nv50_crc_source_type type,
+                      struct nv50_crc_notifier_ctx *ctx);
        int (*set_ctx)(struct nv50_head *, struct nv50_crc_notifier_ctx *);
        u32 (*get_entry)(struct nv50_head *, struct nv50_crc_notifier_ctx *,
                         enum nv50_crc_source, int idx);
 
 extern const struct nv50_crc_func crc907d;
 extern const struct nv50_crc_func crcc37d;
+extern const struct nv50_crc_func crcc57d;
 
 #else /* IS_ENABLED(CONFIG_DEBUG_FS) */
 struct nv50_crc {};
 
 } __packed;
 
 static int
-crc907d_set_src(struct nv50_head *head, int or,
-               enum nv50_crc_source_type source,
-               struct nv50_crc_notifier_ctx *ctx, u32 wndw)
+crc907d_set_src(struct nv50_head *head, int or, enum nv50_crc_source_type source,
+               struct nv50_crc_notifier_ctx *ctx)
 {
        struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
        const int i = head->base.index;
 
 #include <drm/drm_crtc.h>
 
 #include "crc.h"
+#include "crcc37d.h"
 #include "core.h"
 #include "disp.h"
 #include "head.h"
 
 #include <nvhw/class/clc37d.h>
 
-#define CRCC37D_MAX_ENTRIES 2047
-
-struct crcc37d_notifier {
-       u32 status;
-
-       /* reserved */
-       u32 :32;
-       u32 :32;
-       u32 :32;
-       u32 :32;
-       u32 :32;
-       u32 :32;
-       u32 :32;
-
-       struct crcc37d_entry {
-               u32 status[2];
-               u32 :32; /* reserved */
-               u32 compositor_crc;
-               u32 rg_crc;
-               u32 output_crc[2];
-               u32 :32; /* reserved */
-       } entries[CRCC37D_MAX_ENTRIES];
-} __packed;
-
 static int
-crcc37d_set_src(struct nv50_head *head, int or,
-               enum nv50_crc_source_type source,
-               struct nv50_crc_notifier_ctx *ctx, u32 wndw)
+crcc37d_set_src(struct nv50_head *head, int or, enum nv50_crc_source_type source,
+               struct nv50_crc_notifier_ctx *ctx)
 {
        struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
        const int i = head->base.index;
-       u32 crc_args = NVVAL(NVC37D, HEAD_SET_CRC_CONTROL, CONTROLLING_CHANNEL, wndw) |
+       u32 crc_args = NVVAL(NVC37D, HEAD_SET_CRC_CONTROL, CONTROLLING_CHANNEL, i * 4) |
                       NVDEF(NVC37D, HEAD_SET_CRC_CONTROL, EXPECT_BUFFER_COLLAPSE, FALSE) |
                       NVDEF(NVC37D, HEAD_SET_CRC_CONTROL, SECONDARY_CRC, NONE) |
                       NVDEF(NVC37D, HEAD_SET_CRC_CONTROL, CRC_DURING_SNOOZE, DISABLE);
        return 0;
 }
 
-static int
-crcc37d_set_ctx(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx)
+int crcc37d_set_ctx(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx)
 {
        struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
        const int i = head->base.index;
        return 0;
 }
 
-static u32 crcc37d_get_entry(struct nv50_head *head,
-                            struct nv50_crc_notifier_ctx *ctx,
-                            enum nv50_crc_source source, int idx)
+u32 crcc37d_get_entry(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx,
+                     enum nv50_crc_source source, int idx)
 {
        struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr;
        struct crcc37d_entry __iomem *entry = ¬ifier->entries[idx];
        return ioread32_native(crc_addr);
 }
 
-static bool crcc37d_ctx_finished(struct nv50_head *head,
-                                struct nv50_crc_notifier_ctx *ctx)
+bool crcc37d_ctx_finished(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx)
 {
        struct nouveau_drm *drm = nouveau_drm(head->base.base.dev);
        struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr;
        .set_ctx = crcc37d_set_ctx,
        .get_entry = crcc37d_get_entry,
        .ctx_finished = crcc37d_ctx_finished,
-       .flip_threshold = CRCC37D_MAX_ENTRIES - 30,
+       .flip_threshold = CRCC37D_FLIP_THRESHOLD,
        .num_entries = CRCC37D_MAX_ENTRIES,
        .notifier_len = sizeof(struct crcc37d_notifier),
 };
 
--- /dev/null
+/* SPDX-License-Identifier: MIT */
+
+#ifndef __CRCC37D_H__
+#define __CRCC37D_H__
+
+#include <linux/types.h>
+
+#include "crc.h"
+
+#define CRCC37D_MAX_ENTRIES 2047
+#define CRCC37D_FLIP_THRESHOLD (CRCC37D_MAX_ENTRIES - 30)
+
+struct crcc37d_notifier {
+       u32 status;
+
+       /* reserved */
+       u32:32;
+       u32:32;
+       u32:32;
+       u32:32;
+       u32:32;
+       u32:32;
+       u32:32;
+
+       struct crcc37d_entry {
+               u32 status[2];
+               u32:32; /* reserved */
+               u32 compositor_crc;
+               u32 rg_crc;
+               u32 output_crc[2];
+               u32:32; /* reserved */
+       } entries[CRCC37D_MAX_ENTRIES];
+} __packed;
+
+int crcc37d_set_ctx(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx);
+u32 crcc37d_get_entry(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx,
+                     enum nv50_crc_source source, int idx);
+bool crcc37d_ctx_finished(struct nv50_head *head, struct nv50_crc_notifier_ctx *ctx);
+
+#endif /* !__CRCC37D_H__ */
 
--- /dev/null
+// SPDX-License-Identifier: MIT
+
+#include "crc.h"
+#include "crcc37d.h"
+#include "core.h"
+#include "disp.h"
+#include "head.h"
+
+#include <nvif/pushc37b.h>
+
+#include <nvhw/class/clc57d.h>
+
+static int crcc57d_set_src(struct nv50_head *head, int or, enum nv50_crc_source_type source,
+                          struct nv50_crc_notifier_ctx *ctx)
+{
+       struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push;
+       const int i = head->base.index;
+       u32 crc_args = NVDEF(NVC57D, HEAD_SET_CRC_CONTROL, CONTROLLING_CHANNEL, CORE) |
+                      NVDEF(NVC57D, HEAD_SET_CRC_CONTROL, EXPECT_BUFFER_COLLAPSE, FALSE) |
+                      NVDEF(NVC57D, HEAD_SET_CRC_CONTROL, SECONDARY_CRC, NONE) |
+                      NVDEF(NVC57D, HEAD_SET_CRC_CONTROL, CRC_DURING_SNOOZE, DISABLE);
+       int ret;
+
+       switch (source) {
+       case NV50_CRC_SOURCE_TYPE_SOR:
+               crc_args |= NVDEF(NVC57D, HEAD_SET_CRC_CONTROL, PRIMARY_CRC, SOR(or));
+               break;
+       case NV50_CRC_SOURCE_TYPE_SF:
+               crc_args |= NVDEF(NVC57D, HEAD_SET_CRC_CONTROL, PRIMARY_CRC, SF);
+               break;
+       default:
+               break;
+       }
+
+       ret = PUSH_WAIT(push, 4);
+       if (ret)
+               return ret;
+
+       if (source) {
+               PUSH_MTHD(push, NVC57D, HEAD_SET_CONTEXT_DMA_CRC(i), ctx->ntfy.handle);
+               PUSH_MTHD(push, NVC57D, HEAD_SET_CRC_CONTROL(i), crc_args);
+       } else {
+               PUSH_MTHD(push, NVC57D, HEAD_SET_CRC_CONTROL(i), 0);
+               PUSH_MTHD(push, NVC57D, HEAD_SET_CONTEXT_DMA_CRC(i), 0);
+       }
+
+       return 0;
+}
+
+const struct nv50_crc_func crcc57d = {
+       .set_src = crcc57d_set_src,
+       .set_ctx = crcc37d_set_ctx,
+       .get_entry = crcc37d_get_entry,
+       .ctx_finished = crcc37d_ctx_finished,
+       .flip_threshold = CRCC37D_FLIP_THRESHOLD,
+       .num_entries = CRCC37D_MAX_ENTRIES,
+       .notifier_len = sizeof(struct crcc37d_notifier),
+};
 
 #define NVC57D_HEAD_SET_RASTER_BLANK_START(a)                                   (0x00002070 + (a)*0x00000400)
 #define NVC57D_HEAD_SET_RASTER_BLANK_START_X                                    14:0
 #define NVC57D_HEAD_SET_RASTER_BLANK_START_Y                                    30:16
+#define NVC57D_HEAD_SET_CONTEXT_DMA_CRC(a)                                      (0x00002180 + (a)*0x00000400)
+#define NVC57D_HEAD_SET_CONTEXT_DMA_CRC_HANDLE                                  31:0
+#define NVC57D_HEAD_SET_CRC_CONTROL(a)                                          (0x00002184 + (a)*0x00000400)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL                         5:0
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_0                   (0x00000000)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_1                   (0x00000001)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_2                   (0x00000002)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_3                   (0x00000003)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_4                   (0x00000004)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_5                   (0x00000005)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_6                   (0x00000006)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_7                   (0x00000007)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_8                   (0x00000008)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_9                   (0x00000009)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_10                  (0x0000000A)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_11                  (0x0000000B)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_12                  (0x0000000C)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_13                  (0x0000000D)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_14                  (0x0000000E)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_15                  (0x0000000F)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_16                  (0x00000010)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_17                  (0x00000011)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_18                  (0x00000012)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_19                  (0x00000013)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_20                  (0x00000014)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_21                  (0x00000015)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_22                  (0x00000016)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_23                  (0x00000017)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_24                  (0x00000018)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_25                  (0x00000019)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_26                  (0x0000001A)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_27                  (0x0000001B)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_28                  (0x0000001C)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_29                  (0x0000001D)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_30                  (0x0000001E)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_WIN_31                  (0x0000001F)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CONTROLLING_CHANNEL_CORE                    (0x00000020)
+#define NVC57D_HEAD_SET_CRC_CONTROL_EXPECT_BUFFER_COLLAPSE                      8:8
+#define NVC57D_HEAD_SET_CRC_CONTROL_EXPECT_BUFFER_COLLAPSE_FALSE                (0x00000000)
+#define NVC57D_HEAD_SET_CRC_CONTROL_EXPECT_BUFFER_COLLAPSE_TRUE                 (0x00000001)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC                                 19:12
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_NONE                            (0x00000000)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SF                              (0x00000030)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR(i)                          (0x00000050 +(i))
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR__SIZE_1                     8
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR0                            (0x00000050)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR1                            (0x00000051)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR2                            (0x00000052)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR3                            (0x00000053)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR4                            (0x00000054)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR5                            (0x00000055)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR6                            (0x00000056)
+#define NVC57D_HEAD_SET_CRC_CONTROL_PRIMARY_CRC_SOR7                            (0x00000057)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC                               27:20
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_NONE                          (0x00000000)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SF                            (0x00000030)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR(i)                        (0x00000050 +(i))
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR__SIZE_1                   8
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR0                          (0x00000050)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR1                          (0x00000051)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR2                          (0x00000052)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR3                          (0x00000053)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR4                          (0x00000054)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR5                          (0x00000055)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR6                          (0x00000056)
+#define NVC57D_HEAD_SET_CRC_CONTROL_SECONDARY_CRC_SOR7                          (0x00000057)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CRC_DURING_SNOOZE                           9:9
+#define NVC57D_HEAD_SET_CRC_CONTROL_CRC_DURING_SNOOZE_DISABLE                   (0x00000000)
+#define NVC57D_HEAD_SET_CRC_CONTROL_CRC_DURING_SNOOZE_ENABLE                    (0x00000001)
 #define NVC57D_HEAD_SET_OLUT_CONTROL(a)                                         (0x00002280 + (a)*0x00000400)
 #define NVC57D_HEAD_SET_OLUT_CONTROL_INTERPOLATE                                0:0
 #define NVC57D_HEAD_SET_OLUT_CONTROL_INTERPOLATE_DISABLE                        (0x00000000)