drm/nouveau/kms/gv100: initial support
authorBen Skeggs <bskeggs@redhat.com>
Tue, 8 May 2018 10:39:48 +0000 (20:39 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 18 May 2018 05:01:46 +0000 (15:01 +1000)
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
21 files changed:
drivers/gpu/drm/nouveau/dispnv50/Kbuild
drivers/gpu/drm/nouveau/dispnv50/atom.h
drivers/gpu/drm/nouveau/dispnv50/core.c
drivers/gpu/drm/nouveau/dispnv50/core.h
drivers/gpu/drm/nouveau/dispnv50/corec37d.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv50/curs.c
drivers/gpu/drm/nouveau/dispnv50/curs.h
drivers/gpu/drm/nouveau/dispnv50/cursc37a.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv50/disp.c
drivers/gpu/drm/nouveau/dispnv50/disp.h
drivers/gpu/drm/nouveau/dispnv50/head.c
drivers/gpu/drm/nouveau/dispnv50/head.h
drivers/gpu/drm/nouveau/dispnv50/head917d.c
drivers/gpu/drm/nouveau/dispnv50/headc37d.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv50/sorc37d.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv50/wimm.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv50/wimm.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv50/wimmc37b.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv50/wndw.c
drivers/gpu/drm/nouveau/dispnv50/wndw.h
drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c [new file with mode: 0644]

index ebd18cb..849b0f4 100644 (file)
@@ -6,6 +6,7 @@ nouveau-y += dispnv50/core507d.o
 nouveau-y += dispnv50/core827d.o
 nouveau-y += dispnv50/core907d.o
 nouveau-y += dispnv50/core917d.o
+nouveau-y += dispnv50/corec37d.o
 
 nouveau-y += dispnv50/dac507d.o
 nouveau-y += dispnv50/dac907d.o
@@ -14,14 +15,20 @@ nouveau-y += dispnv50/pior507d.o
 
 nouveau-y += dispnv50/sor507d.o
 nouveau-y += dispnv50/sor907d.o
+nouveau-y += dispnv50/sorc37d.o
 
 nouveau-y += dispnv50/head.o
 nouveau-y += dispnv50/head507d.o
 nouveau-y += dispnv50/head827d.o
 nouveau-y += dispnv50/head907d.o
 nouveau-y += dispnv50/head917d.o
+nouveau-y += dispnv50/headc37d.o
+
+nouveau-y += dispnv50/wimm.o
+nouveau-y += dispnv50/wimmc37b.o
 
 nouveau-y += dispnv50/wndw.o
+nouveau-y += dispnv50/wndwc37e.o
 
 nouveau-y += dispnv50/base.o
 nouveau-y += dispnv50/base507c.o
@@ -32,6 +39,7 @@ nouveau-y += dispnv50/base917c.o
 nouveau-y += dispnv50/curs.o
 nouveau-y += dispnv50/curs507a.o
 nouveau-y += dispnv50/curs907a.o
+nouveau-y += dispnv50/cursc37a.o
 
 nouveau-y += dispnv50/oimm.o
 nouveau-y += dispnv50/oimm507b.o
index d8337e7..908feb1 100644 (file)
@@ -54,6 +54,9 @@ struct nv50_head_atom {
                u64 offset:40;
                u8 buffer:1;
                u8 mode:4;
+               u8 size:2;
+               u8 range:2;
+               u8 output_mode:2;
        } olut;
 
        struct {
@@ -77,7 +80,7 @@ struct nv50_head_atom {
                u32 handle;
                u64 offset:40;
                u8  layout:2;
-               u8  format:1;
+               u8  format:8;
        } curs;
 
        struct {
@@ -166,6 +169,9 @@ struct nv50_wndw_atom {
                        u8  buffer:1;
                        u8  enable:2;
                        u8  mode:4;
+                       u8  size:2;
+                       u8  range:2;
+                       u8  output_mode:2;
                } i;
        } xlut;
 
index f87cbaa..f3c49ad 100644 (file)
@@ -42,6 +42,7 @@ nv50_core_new(struct nouveau_drm *drm, struct nv50_core **pcore)
                int version;
                int (*new)(struct nouveau_drm *, s32, struct nv50_core **);
        } cores[] = {
+               { GV100_DISP_CORE_CHANNEL_DMA, 0, corec37d_new },
                { GP102_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
                { GP100_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
                { GM200_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
index c490d7d..8470df9 100644 (file)
@@ -44,4 +44,7 @@ extern const struct nv50_outp_func dac907d;
 extern const struct nv50_outp_func sor907d;
 
 int core917d_new(struct nouveau_drm *, s32, struct nv50_core **);
+
+int corec37d_new(struct nouveau_drm *, s32, struct nv50_core **);
+extern const struct nv50_outp_func sorc37d;
 #endif
diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c
new file mode 100644 (file)
index 0000000..b5c17c9
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "core.h"
+#include "head.h"
+
+#include <nouveau_bo.h>
+
+static void
+corec37d_update(struct nv50_core *core, u32 *interlock, bool ntfy)
+{
+       u32 *push;
+       if ((push = evo_wait(&core->chan, 9))) {
+               if (ntfy) {
+                       evo_mthd(push, 0x020c, 1);
+                       evo_data(push, 0x00001000 | NV50_DISP_CORE_NTFY);
+               }
+
+               evo_mthd(push, 0x0218, 2);
+               evo_data(push, interlock[NV50_DISP_INTERLOCK_CURS]);
+               evo_data(push, interlock[NV50_DISP_INTERLOCK_WNDW]);
+               evo_mthd(push, 0x0200, 1);
+               evo_data(push, 0x00000001);
+
+               if (ntfy) {
+                       evo_mthd(push, 0x020c, 1);
+                       evo_data(push, 0x00000000);
+               }
+               evo_kick(push, &core->chan);
+       }
+}
+
+int
+corec37d_ntfy_wait_done(struct nouveau_bo *bo, u32 offset,
+                       struct nvif_device *device)
+{
+       u32 data;
+       s64 time = nvif_msec(device, 2000ULL,
+               data = nouveau_bo_rd32(bo, offset / 4 + 0);
+               if ((data & 0xc0000000) == 0x80000000)
+                       break;
+               usleep_range(1, 2);
+       );
+       return time < 0 ? time : 0;
+}
+
+void
+corec37d_ntfy_init(struct nouveau_bo *bo, u32 offset)
+{
+       nouveau_bo_wr32(bo, offset / 4 + 0, 0x00000000);
+       nouveau_bo_wr32(bo, offset / 4 + 1, 0x00000000);
+       nouveau_bo_wr32(bo, offset / 4 + 2, 0x00000000);
+       nouveau_bo_wr32(bo, offset / 4 + 3, 0x00000000);
+}
+
+void
+corec37d_init(struct nv50_core *core)
+{
+       const u32 windows = 8; /*XXX*/
+       u32 *push, i;
+       if ((push = evo_wait(&core->chan, 2 + 6 * windows + 2))) {
+               evo_mthd(push, 0x0208, 1);
+               evo_data(push, core->chan.sync.handle);
+               for (i = 0; i < windows; i++) {
+                       evo_mthd(push, 0x1000 + (i * 0x080), 3);
+                       evo_data(push, i >> 1);
+                       evo_data(push, 0x00000017);
+                       evo_data(push, 0x00000000);
+                       evo_mthd(push, 0x1010 + (i * 0x080), 1);
+                       evo_data(push, 0x00127fff);
+               }
+               evo_mthd(push, 0x0200, 1);
+               evo_data(push, 0x00000001);
+               evo_kick(push, &core->chan);
+       }
+}
+
+static const struct nv50_core_func
+corec37d = {
+       .init = corec37d_init,
+       .ntfy_init = corec37d_ntfy_init,
+       .ntfy_wait_done = corec37d_ntfy_wait_done,
+       .update = corec37d_update,
+       .head = &headc37d,
+       .sor = &sorc37d,
+};
+
+int
+corec37d_new(struct nouveau_drm *drm, s32 oclass, struct nv50_core **pcore)
+{
+       return core507d_new_(&corec37d, drm, oclass, pcore);
+}
index fb842ed..f592087 100644 (file)
@@ -31,6 +31,7 @@ nv50_curs_new(struct nouveau_drm *drm, int head, struct nv50_wndw **pwndw)
                int version;
                int (*new)(struct nouveau_drm *, int, s32, struct nv50_wndw **);
        } curses[] = {
+               { GV100_DISP_CURSOR, 0, cursc37a_new },
                { GK104_DISP_CURSOR, 0, curs907a_new },
                { GF110_DISP_CURSOR, 0, curs907a_new },
                { GT214_DISP_CURSOR, 0, curs507a_new },
index 8edac45..23aff5f 100644 (file)
@@ -8,6 +8,7 @@ int curs507a_new_(const struct nv50_wimm_func *, struct nouveau_drm *,
                  struct nv50_wndw **);
 
 int curs907a_new(struct nouveau_drm *, int, s32, struct nv50_wndw **);
+int cursc37a_new(struct nouveau_drm *, int, s32, struct nv50_wndw **);
 
 int nv50_curs_new(struct nouveau_drm *, int head, struct nv50_wndw **);
 #endif
diff --git a/drivers/gpu/drm/nouveau/dispnv50/cursc37a.c b/drivers/gpu/drm/nouveau/dispnv50/cursc37a.c
new file mode 100644 (file)
index 0000000..23fb29d
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "curs.h"
+#include "atom.h"
+
+static void
+cursc37a_update(struct nv50_wndw *wndw, u32 *interlock)
+{
+       nvif_wr32(&wndw->wimm.base.user, 0x0200, 0x00000001);
+}
+
+static void
+cursc37a_point(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       nvif_wr32(&wndw->wimm.base.user, 0x0208, asyw->point.y << 16 |
+                                                asyw->point.x);
+}
+
+static const struct nv50_wimm_func
+cursc37a = {
+       .point = cursc37a_point,
+       .update = cursc37a_update,
+};
+
+int
+cursc37a_new(struct nouveau_drm *drm, int head, s32 oclass,
+            struct nv50_wndw **pwndw)
+{
+       return curs507a_new_(&cursc37a, drm, head, oclass,
+                            0x00000001 << head, pwndw);
+}
index 6c860e8..b83465a 100644 (file)
@@ -154,6 +154,9 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
        if (ret)
                return ret;
 
+       if (!syncbuf)
+               return 0;
+
        ret = nvif_object_init(&dmac->base.user, 0xf0000000, NV_DMA_IN_MEMORY,
                               &(struct nv_dma_v0) {
                                        .target = NV_DMA_V0_TARGET_VRAM,
@@ -2170,6 +2173,9 @@ nv50_display_create(struct drm_device *dev)
                goto out;
 
        /* create crtc objects to represent the hw heads */
+       if (disp->disp->object.oclass >= GV100_DISP)
+               crtcs = nvif_rd32(&device->object, 0x610060) & 0xff;
+       else
        if (disp->disp->object.oclass >= GF110_DISP)
                crtcs = nvif_rd32(&device->object, 0x612004) & 0xf;
        else
index a89b83f..e48c5eb 100644 (file)
@@ -36,11 +36,15 @@ struct nv50_disp_interlock {
                NV50_DISP_INTERLOCK_CURS,
                NV50_DISP_INTERLOCK_BASE,
                NV50_DISP_INTERLOCK_OVLY,
+               NV50_DISP_INTERLOCK_WNDW,
+               NV50_DISP_INTERLOCK_WIMM,
                NV50_DISP_INTERLOCK__SIZE
        } type;
        u32 data;
 };
 
+void corec37d_ntfy_init(struct nouveau_bo *, u32);
+
 struct nv50_chan {
        struct nvif_object user;
        struct nvif_device *device;
index ca83006..4f57e53 100644 (file)
@@ -475,7 +475,16 @@ nv50_head_create(struct drm_device *dev, int index)
 
        head->func = disp->core->func->head;
        head->base.index = index;
-       ret = nv50_base_new(drm, head->base.index, &wndw);
+
+       if (disp->disp->object.oclass < GV100_DISP) {
+               ret = nv50_ovly_new(drm, head->base.index, &wndw);
+               ret = nv50_base_new(drm, head->base.index, &wndw);
+       } else {
+               ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_OVERLAY,
+                                   head->base.index * 2 + 1, &wndw);
+               ret = nv50_wndw_new(drm, DRM_PLANE_TYPE_PRIMARY,
+                                   head->base.index * 2 + 0, &wndw);
+       }
        if (ret == 0)
                ret = nv50_curs_new(drm, head->base.index, &curs);
        if (ret) {
@@ -495,8 +504,6 @@ nv50_head_create(struct drm_device *dev, int index)
                        goto out;
        }
 
-       /* allocate overlay resources */
-       ret = nv50_ovly_new(drm, head->base.index, &wndw);
 out:
        if (ret)
                nv50_head_destroy(crtc);
index 8f2c3ff..37b3248 100644 (file)
@@ -71,4 +71,8 @@ void head907d_procamp(struct nv50_head *, struct nv50_head_atom *);
 void head907d_or(struct nv50_head *, struct nv50_head_atom *);
 
 extern const struct nv50_head_func head917d;
+int head917d_curs_layout(struct nv50_head *, struct nv50_wndw_atom *,
+                        struct nv50_head_atom *);
+
+extern const struct nv50_head_func headc37d;
 #endif
index 4c019a4..303df84 100644 (file)
@@ -63,7 +63,7 @@ head917d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
        }
 }
 
-static int
+int
 head917d_curs_layout(struct nv50_head *head, struct nv50_wndw_atom *asyw,
                     struct nv50_head_atom *asyh)
 {
diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c
new file mode 100644 (file)
index 0000000..989c140
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "head.h"
+#include "atom.h"
+#include "core.h"
+
+static void
+headc37d_or(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       u32 *push;
+       if ((push = evo_wait(core, 2))) {
+               /*XXX: This is a dirty hack until OR depth handling is
+                *     improved later for deep colour etc.
+                */
+               switch (asyh->or.depth) {
+               case 6: asyh->or.depth = 5; break;
+               case 5: asyh->or.depth = 4; break;
+               case 2: asyh->or.depth = 1; break;
+               case 0: asyh->or.depth = 4; break;
+               default:
+                       WARN_ON(1);
+                       break;
+               }
+
+               evo_mthd(push, 0x2004 + (head->base.index * 0x400), 1);
+               evo_data(push, 0x00000001 |
+                              asyh->or.depth << 4 |
+                              asyh->or.nvsync << 3 |
+                              asyh->or.nhsync << 2);
+               evo_kick(push, core);
+       }
+}
+
+static void
+headc37d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       u32 *push;
+       if ((push = evo_wait(core, 2))) {
+               evo_mthd(push, 0x2000 + (head->base.index * 0x400), 1);
+               evo_data(push, 0x80000000 |
+                              asyh->procamp.sat.sin << 16 |
+                              asyh->procamp.sat.cos << 4);
+               evo_kick(push, core);
+       }
+}
+
+static void
+headc37d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       u32 *push;
+       if ((push = evo_wait(core, 2))) {
+               evo_mthd(push, 0x2018 + (head->base.index * 0x0400), 1);
+               evo_data(push, asyh->dither.mode << 8 |
+                              asyh->dither.bits << 4 |
+                              asyh->dither.enable);
+               evo_kick(push, core);
+       }
+}
+
+static void
+headc37d_curs_clr(struct nv50_head *head)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       u32 *push;
+       if ((push = evo_wait(core, 4))) {
+               evo_mthd(push, 0x209c + head->base.index * 0x400, 1);
+               evo_data(push, 0x000000cf);
+               evo_mthd(push, 0x2088 + head->base.index * 0x400, 1);
+               evo_data(push, 0x00000000);
+               evo_kick(push, core);
+       }
+}
+
+static void
+headc37d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       u32 *push;
+       if ((push = evo_wait(core, 7))) {
+               evo_mthd(push, 0x209c + head->base.index * 0x400, 2);
+               evo_data(push, 0x80000000 |
+                              asyh->curs.layout << 8 |
+                              asyh->curs.format << 0);
+               evo_data(push, 0x000072ff);
+               evo_mthd(push, 0x2088 + head->base.index * 0x400, 1);
+               evo_data(push, asyh->curs.handle);
+               evo_mthd(push, 0x2090 + head->base.index * 0x400, 1);
+               evo_data(push, asyh->curs.offset >> 8);
+               evo_kick(push, core);
+       }
+}
+
+static int
+headc37d_curs_format(struct nv50_head *head, struct nv50_wndw_atom *asyw,
+                    struct nv50_head_atom *asyh)
+{
+       asyh->curs.format = asyw->image.format;
+       return 0;
+}
+
+static void
+headc37d_olut_clr(struct nv50_head *head)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       u32 *push;
+       if ((push = evo_wait(core, 2))) {
+               evo_mthd(push, 0x20ac + (head->base.index * 0x400), 1);
+               evo_data(push, 0x00000000);
+               evo_kick(push, core);
+       }
+}
+
+static void
+headc37d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       u32 *push;
+       if ((push = evo_wait(core, 4))) {
+               evo_mthd(push, 0x20a4 + (head->base.index * 0x400), 3);
+               evo_data(push, asyh->olut.output_mode << 8 |
+                              asyh->olut.range << 4 |
+                              asyh->olut.size);
+               evo_data(push, asyh->olut.offset >> 8);
+               evo_data(push, asyh->olut.handle);
+               evo_kick(push, core);
+       }
+}
+
+static void
+headc37d_olut(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       asyh->olut.mode = 2;
+       asyh->olut.size = 0;
+       asyh->olut.range = 0;
+       asyh->olut.output_mode = 1;
+}
+
+static void
+headc37d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       struct nv50_head_mode *m = &asyh->mode;
+       u32 *push;
+       if ((push = evo_wait(core, 12))) {
+               evo_mthd(push, 0x2064 + (head->base.index * 0x400), 5);
+               evo_data(push, (m->v.active  << 16) | m->h.active );
+               evo_data(push, (m->v.synce   << 16) | m->h.synce  );
+               evo_data(push, (m->v.blanke  << 16) | m->h.blanke );
+               evo_data(push, (m->v.blanks  << 16) | m->h.blanks );
+               evo_data(push, (m->v.blank2e << 16) | m->v.blank2s);
+               evo_mthd(push, 0x200c + (head->base.index * 0x400), 1);
+               evo_data(push, m->clock * 1000);
+               evo_mthd(push, 0x2028 + (head->base.index * 0x400), 1);
+               evo_data(push, m->clock * 1000);
+               /*XXX: HEAD_USAGE_BOUNDS, doesn't belong here. */
+               evo_mthd(push, 0x2030 + (head->base.index * 0x400), 1);
+               evo_data(push, 0x00000124);
+               evo_kick(push, core);
+       }
+}
+
+static void
+headc37d_view(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       u32 *push;
+       if ((push = evo_wait(core, 4))) {
+               evo_mthd(push, 0x204c + (head->base.index * 0x400), 1);
+               evo_data(push, (asyh->view.iH << 16) | asyh->view.iW);
+               evo_mthd(push, 0x2058 + (head->base.index * 0x400), 1);
+               evo_data(push, (asyh->view.oH << 16) | asyh->view.oW);
+               evo_kick(push, core);
+       }
+}
+
+const struct nv50_head_func
+headc37d = {
+       .view = headc37d_view,
+       .mode = headc37d_mode,
+       .olut = headc37d_olut,
+       .olut_set = headc37d_olut_set,
+       .olut_clr = headc37d_olut_clr,
+       .curs_layout = head917d_curs_layout,
+       .curs_format = headc37d_curs_format,
+       .curs_set = headc37d_curs_set,
+       .curs_clr = headc37d_curs_clr,
+       .dither = headc37d_dither,
+       .procamp = headc37d_procamp,
+       .or = headc37d_or,
+};
diff --git a/drivers/gpu/drm/nouveau/dispnv50/sorc37d.c b/drivers/gpu/drm/nouveau/dispnv50/sorc37d.c
new file mode 100644 (file)
index 0000000..dff0592
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "core.h"
+
+static void
+sorc37d_ctrl(struct nv50_core *core, int or, u32 ctrl,
+            struct nv50_head_atom *asyh)
+{
+       u32 *push;
+       if ((push = evo_wait(&core->chan, 2))) {
+               evo_mthd(push, 0x0300 + (or * 0x20), 1);
+               evo_data(push, ctrl);
+               evo_kick(push, &core->chan);
+       }
+}
+
+const struct nv50_outp_func
+sorc37d = {
+       .ctrl = sorc37d_ctrl,
+};
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wimm.c b/drivers/gpu/drm/nouveau/dispnv50/wimm.c
new file mode 100644 (file)
index 0000000..fc36e06
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wimm.h"
+
+#include <nvif/class.h>
+
+int
+nv50_wimm_init(struct nouveau_drm *drm, struct nv50_wndw *wndw)
+{
+       struct {
+               s32 oclass;
+               int version;
+               int (*init)(struct nouveau_drm *, s32, struct nv50_wndw *);
+       } wimms[] = {
+               { GV100_DISP_WINDOW_IMM_CHANNEL_DMA, 0, wimmc37b_init },
+               {}
+       };
+       struct nv50_disp *disp = nv50_disp(drm->dev);
+       int cid;
+
+       cid = nvif_mclass(&disp->disp->object, wimms);
+       if (cid < 0) {
+               NV_ERROR(drm, "No supported window immediate class\n");
+               return cid;
+       }
+
+       return wimms[cid].init(drm, wimms[cid].oclass, wndw);
+}
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wimm.h b/drivers/gpu/drm/nouveau/dispnv50/wimm.h
new file mode 100644 (file)
index 0000000..3630523
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __NV50_KMS_WIMM_H__
+#define __NV50_KMS_WIMM_H__
+#include "wndw.h"
+
+int nv50_wimm_init(struct nouveau_drm *drm, struct nv50_wndw *);
+
+int wimmc37b_init(struct nouveau_drm *, s32, struct nv50_wndw *);
+#endif
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wimmc37b.c b/drivers/gpu/drm/nouveau/dispnv50/wimmc37b.c
new file mode 100644 (file)
index 0000000..9103b84
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wimm.h"
+#include "atom.h"
+#include "wndw.h"
+
+#include <nvif/clc37b.h>
+
+static void
+wimmc37b_update(struct nv50_wndw *wndw, u32 *interlock)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wimm, 2))) {
+               evo_mthd(push, 0x0200, 1);
+               if (interlock[NV50_DISP_INTERLOCK_WNDW] & wndw->interlock.data)
+                       evo_data(push, 0x00000003);
+               else
+                       evo_data(push, 0x00000001);
+               evo_kick(push, &wndw->wimm);
+       }
+}
+
+static void
+wimmc37b_point(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wimm, 2))) {
+               evo_mthd(push, 0x0208, 1);
+               evo_data(push, asyw->point.y << 16 | asyw->point.x);
+               evo_kick(push, &wndw->wimm);
+       }
+}
+
+static const struct nv50_wimm_func
+wimmc37b = {
+       .point = wimmc37b_point,
+       .update = wimmc37b_update,
+};
+
+static int
+wimmc37b_init_(const struct nv50_wimm_func *func, struct nouveau_drm *drm,
+              s32 oclass, struct nv50_wndw *wndw)
+{
+       struct nvc37b_window_imm_channel_dma_v0 args = {
+               .pushbuf = 0xb0007b00 | wndw->id,
+               .index = wndw->id,
+       };
+       struct nv50_disp *disp = nv50_disp(drm->dev);
+       int ret;
+
+       ret = nv50_dmac_create(&drm->client.device, &disp->disp->object,
+                              &oclass, 0, &args, sizeof(args), 0,
+                              &wndw->wimm);
+       if (ret) {
+               NV_ERROR(drm, "wimm%04x allocation failed: %d\n", oclass, ret);
+               return ret;
+       }
+
+       wndw->immd = func;
+       return 0;
+}
+
+int
+wimmc37b_init(struct nouveau_drm *drm, s32 oclass, struct nv50_wndw *wndw)
+{
+       return wimmc37b_init_(&wimmc37b, drm, oclass, wndw);
+}
index c7c08fa..224963b 100644 (file)
@@ -20,6 +20,7 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 #include "wndw.h"
+#include "wimm.h"
 
 #include <nvif/class.h>
 #include <nvif/cl0002.h>
@@ -148,11 +149,15 @@ nv50_wndw_flush_set(struct nv50_wndw *wndw, u32 *interlock,
 
        if (asyw->set.scale) wndw->func->scale_set(wndw, asyw);
        if (asyw->set.point) {
+               if (asyw->set.point = false, asyw->set.mask)
+                       interlock[wndw->interlock.type] |= wndw->interlock.data;
+               interlock[NV50_DISP_INTERLOCK_WIMM] |= wndw->interlock.data;
+
                wndw->immd->point(wndw, asyw);
                wndw->immd->update(wndw, interlock);
+       } else {
+               interlock[wndw->interlock.type] |= wndw->interlock.data;
        }
-
-       interlock[wndw->interlock.type] |= wndw->interlock.data;
 }
 
 void
@@ -605,3 +610,32 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
        wndw->notify.func = nv50_wndw_notify;
        return 0;
 }
+
+int
+nv50_wndw_new(struct nouveau_drm *drm, enum drm_plane_type type, int index,
+             struct nv50_wndw **pwndw)
+{
+       struct {
+               s32 oclass;
+               int version;
+               int (*new)(struct nouveau_drm *, enum drm_plane_type,
+                          int, s32, struct nv50_wndw **);
+       } wndws[] = {
+               { GV100_DISP_WINDOW_CHANNEL_DMA, 0, wndwc37e_new },
+               {}
+       };
+       struct nv50_disp *disp = nv50_disp(drm->dev);
+       int cid, ret;
+
+       cid = nvif_mclass(&disp->disp->object, wndws);
+       if (cid < 0) {
+               NV_ERROR(drm, "No supported window class\n");
+               return cid;
+       }
+
+       ret = wndws[cid].new(drm, type, index, wndws[cid].oclass, pwndw);
+       if (ret)
+               return ret;
+
+       return nv50_wimm_init(drm, *pwndw);
+}
index 745304d..b0b6428 100644 (file)
@@ -87,4 +87,10 @@ struct nv50_wimm_func {
 };
 
 extern const struct nv50_wimm_func curs507a;
+
+int wndwc37e_new(struct nouveau_drm *, enum drm_plane_type, int, s32,
+                struct nv50_wndw **);
+
+int nv50_wndw_new(struct nouveau_drm *, enum drm_plane_type, int index,
+                 struct nv50_wndw **);
 #endif
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c
new file mode 100644 (file)
index 0000000..44afb0f
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wndw.h"
+#include "atom.h"
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <nouveau_bo.h>
+
+#include <nvif/clc37e.h>
+
+static void
+wndwc37e_ilut_clr(struct nv50_wndw *wndw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 2))) {
+               evo_mthd(push, 0x02b8, 1);
+               evo_data(push, 0x00000000);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+wndwc37e_ilut_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 4))) {
+               evo_mthd(push, 0x02b0, 3);
+               evo_data(push, asyw->xlut.i.output_mode << 8 |
+                              asyw->xlut.i.range << 4 |
+                              asyw->xlut.i.size);
+               evo_data(push, asyw->xlut.i.offset >> 8);
+               evo_data(push, asyw->xlut.handle);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+wndwc37e_ilut(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       asyw->xlut.i.mode = 2;
+       asyw->xlut.i.size = 0;
+       asyw->xlut.i.range = 0;
+       asyw->xlut.i.output_mode = 1;
+}
+
+static void
+wndwc37e_image_clr(struct nv50_wndw *wndw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 4))) {
+               evo_mthd(push, 0x0308, 1);
+               evo_data(push, 0x00000000);
+               evo_mthd(push, 0x0240, 1);
+               evo_data(push, 0x00000000);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+wndwc37e_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       u32 *push;
+
+       if (!(push = evo_wait(&wndw->wndw, 25)))
+               return;
+
+       evo_mthd(push, 0x0308, 1);
+       evo_data(push, asyw->image.mode << 4 | asyw->image.interval);
+       evo_mthd(push, 0x0224, 4);
+       evo_data(push, asyw->image.h << 16 | asyw->image.w);
+       evo_data(push, asyw->image.layout << 4 | asyw->image.blockh);
+       evo_data(push, asyw->image.colorspace << 8 | asyw->image.format);
+       evo_data(push, asyw->image.blocks[0] | (asyw->image.pitch[0] >> 6));
+       evo_mthd(push, 0x0240, 1);
+       evo_data(push, asyw->image.handle[0]);
+       evo_mthd(push, 0x0260, 1);
+       evo_data(push, asyw->image.offset[0] >> 8);
+       evo_mthd(push, 0x0290, 1);
+       evo_data(push, (asyw->state.src_y >> 16) << 16 |
+                      (asyw->state.src_x >> 16));
+       evo_mthd(push, 0x0298, 1);
+       evo_data(push, (asyw->state.src_h >> 16) << 16 |
+                      (asyw->state.src_w >> 16));
+       evo_mthd(push, 0x02a4, 1);
+       evo_data(push, asyw->state.crtc_h << 16 |
+                      asyw->state.crtc_w);
+
+       /*XXX: Composition-related stuff.  Need to implement properly. */
+       evo_mthd(push, 0x02ec, 1);
+       evo_data(push, (2 - (wndw->id & 1)) << 4);
+       evo_mthd(push, 0x02f4, 5);
+       evo_data(push, 0x00000011);
+       evo_data(push, 0xffff0000);
+       evo_data(push, 0xffff0000);
+       evo_data(push, 0xffff0000);
+       evo_data(push, 0xffff0000);
+       evo_kick(push, &wndw->wndw);
+}
+
+static void
+wndwc37e_ntfy_clr(struct nv50_wndw *wndw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 2))) {
+               evo_mthd(push, 0x021c, 1);
+               evo_data(push, 0x00000000);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+wndwc37e_ntfy_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 3))) {
+               evo_mthd(push, 0x021c, 2);
+               evo_data(push, asyw->ntfy.handle);
+               evo_data(push, asyw->ntfy.offset | asyw->ntfy.awaken);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+wndwc37e_sema_clr(struct nv50_wndw *wndw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 2))) {
+               evo_mthd(push, 0x0218, 1);
+               evo_data(push, 0x00000000);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+wndwc37e_sema_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 5))) {
+               evo_mthd(push, 0x020c, 4);
+               evo_data(push, asyw->sema.offset);
+               evo_data(push, asyw->sema.acquire);
+               evo_data(push, asyw->sema.release);
+               evo_data(push, asyw->sema.handle);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+wndwc37e_update(struct nv50_wndw *wndw, u32 *interlock)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 5))) {
+               evo_mthd(push, 0x0370, 2);
+               evo_data(push, interlock[NV50_DISP_INTERLOCK_CURS] << 1 |
+                              interlock[NV50_DISP_INTERLOCK_CORE]);
+               evo_data(push, interlock[NV50_DISP_INTERLOCK_WNDW]);
+               evo_mthd(push, 0x0200, 1);
+               if (interlock[NV50_DISP_INTERLOCK_WIMM] & wndw->interlock.data)
+                       evo_data(push, 0x00001001);
+               else
+                       evo_data(push, 0x00000001);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+wndwc37e_release(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw,
+                struct nv50_head_atom *asyh)
+{
+}
+
+static int
+wndwc37e_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw,
+                struct nv50_head_atom *asyh)
+{
+       return drm_atomic_helper_check_plane_state(&asyw->state, &asyh->state,
+                                                  DRM_PLANE_HELPER_NO_SCALING,
+                                                  DRM_PLANE_HELPER_NO_SCALING,
+                                                  true, true);
+}
+
+static const u32
+wndwc37e_format[] = {
+       DRM_FORMAT_C8,
+       DRM_FORMAT_YUYV,
+       DRM_FORMAT_UYVY,
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_RGB565,
+       DRM_FORMAT_XRGB1555,
+       DRM_FORMAT_ARGB1555,
+       DRM_FORMAT_XBGR2101010,
+       DRM_FORMAT_ABGR2101010,
+       DRM_FORMAT_XBGR8888,
+       DRM_FORMAT_ABGR8888,
+       DRM_FORMAT_XRGB2101010,
+       DRM_FORMAT_ARGB2101010,
+       0
+};
+
+static const struct nv50_wndw_func
+wndwc37e = {
+       .acquire = wndwc37e_acquire,
+       .release = wndwc37e_release,
+       .sema_set = wndwc37e_sema_set,
+       .sema_clr = wndwc37e_sema_clr,
+       .ntfy_set = wndwc37e_ntfy_set,
+       .ntfy_clr = wndwc37e_ntfy_clr,
+       .ntfy_reset = corec37d_ntfy_init,
+       .ntfy_wait_begun = base507c_ntfy_wait_begun,
+       .ilut = wndwc37e_ilut,
+       .xlut_set = wndwc37e_ilut_set,
+       .xlut_clr = wndwc37e_ilut_clr,
+       .image_set = wndwc37e_image_set,
+       .image_clr = wndwc37e_image_clr,
+       .update = wndwc37e_update,
+};
+
+static int
+wndwc37e_new_(const struct nv50_wndw_func *func, struct nouveau_drm *drm,
+             enum drm_plane_type type, int index, s32 oclass, u32 heads,
+             struct nv50_wndw **pwndw)
+{
+       struct nvc37e_window_channel_dma_v0 args = {
+               .pushbuf = 0xb0007e00 | index,
+               .index = index,
+       };
+       struct nv50_disp *disp = nv50_disp(drm->dev);
+       struct nv50_wndw *wndw;
+       int ret;
+
+       ret = nv50_wndw_new_(func, drm->dev, type, "wndw", index,
+                            wndwc37e_format, heads, NV50_DISP_INTERLOCK_WNDW,
+                            BIT(index), &wndw);
+       if (*pwndw = wndw, ret)
+               return ret;
+
+       ret = nv50_dmac_create(&drm->client.device, &disp->disp->object,
+                              &oclass, 0, &args, sizeof(args),
+                              disp->sync->bo.offset, &wndw->wndw);
+       if (ret) {
+               NV_ERROR(drm, "qndw%04x allocation failed: %d\n", oclass, ret);
+               return ret;
+       }
+
+       wndw->ntfy = NV50_DISP_WNDW_NTFY(wndw->id);
+       wndw->sema = NV50_DISP_WNDW_SEM0(wndw->id);
+       wndw->data = 0x00000000;
+       return 0;
+}
+
+int
+wndwc37e_new(struct nouveau_drm *drm, enum drm_plane_type type, int index,
+            s32 oclass, struct nv50_wndw **pwndw)
+{
+       return wndwc37e_new_(&wndwc37e, drm, type, index, oclass,
+                            BIT(index >> 1), pwndw);
+}