drm/nouveau/kms/nv50-: handle degamma LUT from window channels
authorBen Skeggs <bskeggs@redhat.com>
Tue, 8 May 2018 10:39:47 +0000 (20:39 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 18 May 2018 05:01:29 +0000 (15:01 +1000)
Required to eventually support DRM colour management APIs, and to
support Volta.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
17 files changed:
drivers/gpu/drm/nouveau/dispnv50/Kbuild
drivers/gpu/drm/nouveau/dispnv50/atom.h
drivers/gpu/drm/nouveau/dispnv50/base.h
drivers/gpu/drm/nouveau/dispnv50/base507c.c
drivers/gpu/drm/nouveau/dispnv50/base827c.c
drivers/gpu/drm/nouveau/dispnv50/base907c.c
drivers/gpu/drm/nouveau/dispnv50/disp.c
drivers/gpu/drm/nouveau/dispnv50/head.c
drivers/gpu/drm/nouveau/dispnv50/head.h
drivers/gpu/drm/nouveau/dispnv50/head507d.c
drivers/gpu/drm/nouveau/dispnv50/head827d.c
drivers/gpu/drm/nouveau/dispnv50/head907d.c
drivers/gpu/drm/nouveau/dispnv50/head917d.c
drivers/gpu/drm/nouveau/dispnv50/lut.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv50/lut.h [new file with mode: 0644]
drivers/gpu/drm/nouveau/dispnv50/wndw.c
drivers/gpu/drm/nouveau/dispnv50/wndw.h

index 3e53484b4589d34935b1deafbf7ac7f64bf323d1..d074bb8ecd1be5973f31daf983ed328153bb70db 100644 (file)
@@ -1,4 +1,5 @@
 nouveau-y += dispnv50/disp.o
+nouveau-y += dispnv50/lut.o
 
 nouveau-y += dispnv50/core.o
 nouveau-y += dispnv50/core507d.o
index fefb9caaf7b8a790b74904d7880abcf1c241490c..3e9e8832d0dd129b0f79ea80a67dd24483fb0c4f 100644 (file)
@@ -16,6 +16,11 @@ struct nv50_atom {
 struct nv50_head_atom {
        struct drm_crtc_state state;
 
+       struct {
+               u32 mask;
+               u32 olut;
+       } wndw;
+
        struct {
                u16 iW;
                u16 iH;
@@ -47,8 +52,9 @@ struct nv50_head_atom {
                bool visible;
                u32 handle;
                u64 offset:40;
-               u8  mode:4;
-       } ilut;
+               u8 buffer:1;
+               u8 mode:4;
+       } olut;
 
        struct {
                bool visible;
@@ -107,7 +113,7 @@ struct nv50_head_atom {
 
        union nv50_head_atom_mask {
                struct {
-                       bool ilut:1;
+                       bool olut:1;
                        bool core:1;
                        bool curs:1;
                        bool view:1;
@@ -136,6 +142,7 @@ nv50_head_atom_get(struct drm_atomic_state *state, struct drm_crtc *crtc)
 struct nv50_wndw_atom {
        struct drm_plane_state state;
 
+       struct drm_property_blob *ilut;
        bool visible;
 
        struct {
@@ -152,8 +159,14 @@ struct nv50_wndw_atom {
        } sema;
 
        struct {
-               u8 enable:2;
-       } lut;
+               u32 handle;
+               struct {
+                       u64 offset:40;
+                       u8  buffer:1;
+                       u8  enable:2;
+                       u8  mode:4;
+               } i;
+       } xlut;
 
        struct {
                u8  mode:2;
@@ -180,8 +193,8 @@ struct nv50_wndw_atom {
                struct {
                        bool ntfy:1;
                        bool sema:1;
+                       bool xlut:1;
                        bool image:1;
-                       bool lut:1;
                        bool point:1;
                };
                u8 mask;
index 71fc10369b3731fc878bba10a11f0fe600a2be2a..87ec8394b7f3c69c2b767ed209ce7387e0e07927 100644 (file)
@@ -17,8 +17,9 @@ void base507c_ntfy_reset(struct nouveau_bo *, u32);
 void base507c_ntfy_set(struct nv50_wndw *, struct nv50_wndw_atom *);
 void base507c_ntfy_clr(struct nv50_wndw *);
 int base507c_ntfy_wait_begun(struct nouveau_bo *, u32, struct nvif_device *);
+void base507c_xlut_set(struct nv50_wndw *, struct nv50_wndw_atom *);
+void base507c_xlut_clr(struct nv50_wndw *);
 void base507c_image_clr(struct nv50_wndw *);
-void base507c_lut(struct nv50_wndw *, struct nv50_wndw_atom *);
 void base507c_update(struct nv50_wndw *, u32 *);
 
 int base827c_new(struct nouveau_drm *, int, s32, struct nv50_wndw **);
index d8d35166936761b7f016027d7562a4f44d11c4b3..5d664d75b64517d3e071233947e2d9b2a6ae3a00 100644 (file)
@@ -39,17 +39,6 @@ base507c_update(struct nv50_wndw *wndw, u32 *interlock)
        }
 }
 
-void
-base507c_lut(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
-{
-       u32 *push;
-       if ((push = evo_wait(&wndw->wndw, 2))) {
-               evo_mthd(push, 0x00e0, 1);
-               evo_data(push, asyw->lut.enable << 30);
-               evo_kick(push, &wndw->wndw);
-       }
-}
-
 void
 base507c_image_clr(struct nv50_wndw *wndw)
 {
@@ -86,6 +75,28 @@ base507c_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
        }
 }
 
+void
+base507c_xlut_clr(struct nv50_wndw *wndw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 2))) {
+               evo_mthd(push, 0x00e0, 1);
+               evo_data(push, 0x00000000);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+void
+base507c_xlut_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 2))) {
+               evo_mthd(push, 0x00e0, 1);
+               evo_data(push, 0x40000000);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
 int
 base507c_ntfy_wait_begun(struct nouveau_bo *bo, u32 offset,
                         struct nvif_device *device)
@@ -177,14 +188,17 @@ base507c_acquire(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw,
        if (ret)
                return ret;
 
+       if (!wndw->func->ilut) {
+               if ((asyh->base.cpp != 1) ^ (fb->format->cpp[0] != 1))
+                       asyh->state.color_mgmt_changed = true;
+       }
+
        asyh->base.depth = fb->format->depth;
        asyh->base.cpp = fb->format->cpp[0];
        asyh->base.x = asyw->state.src.x1 >> 16;
        asyh->base.y = asyw->state.src.y1 >> 16;
        asyh->base.w = asyw->state.fb->width;
        asyh->base.h = asyw->state.fb->height;
-
-       asyw->lut.enable = 1;
        return 0;
 }
 
@@ -213,9 +227,11 @@ base507c = {
        .ntfy_set = base507c_ntfy_set,
        .ntfy_clr = base507c_ntfy_clr,
        .ntfy_wait_begun = base507c_ntfy_wait_begun,
+       .olut_core = 1,
+       .xlut_set = base507c_xlut_set,
+       .xlut_clr = base507c_xlut_clr,
        .image_set = base507c_image_set,
        .image_clr = base507c_image_clr,
-       .lut = base507c_lut,
        .update = base507c_update,
 };
 
index 240a6409329d22aef1d82cd63664a7c3c90533f2..d886858a57240b70378b8c4888672522aac58529 100644 (file)
@@ -53,9 +53,11 @@ base827c = {
        .ntfy_set = base507c_ntfy_set,
        .ntfy_clr = base507c_ntfy_clr,
        .ntfy_wait_begun = base507c_ntfy_wait_begun,
+       .olut_core = 1,
+       .xlut_set = base507c_xlut_set,
+       .xlut_clr = base507c_xlut_clr,
        .image_set = base827c_image_set,
        .image_clr = base507c_image_clr,
-       .lut = base507c_lut,
        .update = base507c_update,
 };
 
index 6c32a4e5cb7d9c1e8b9267ce4bcd5837b81dfc5d..2643592ad827cfb4cecdc8c9c529eb325fd18b9d 100644 (file)
@@ -43,6 +43,44 @@ base907c_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
        }
 }
 
+static void
+base907c_xlut_clr(struct nv50_wndw *wndw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 6))) {
+               evo_mthd(push, 0x00e0, 1);
+               evo_data(push, 0x00000000);
+               evo_mthd(push, 0x00e8, 1);
+               evo_data(push, 0x00000000);
+               evo_mthd(push, 0x00fc, 1);
+               evo_data(push, 0x00000000);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+base907c_xlut_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       u32 *push;
+       if ((push = evo_wait(&wndw->wndw, 6))) {
+               evo_mthd(push, 0x00e0, 3);
+               evo_data(push, asyw->xlut.i.enable << 30 |
+                              asyw->xlut.i.mode << 24);
+               evo_data(push, asyw->xlut.i.offset >> 8);
+               evo_data(push, 0x40000000);
+               evo_mthd(push, 0x00fc, 1);
+               evo_data(push, asyw->xlut.handle);
+               evo_kick(push, &wndw->wndw);
+       }
+}
+
+static void
+base907c_ilut(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       asyw->xlut.i.mode = 7;
+       asyw->xlut.i.enable = 2;
+}
+
 static const struct nv50_wndw_func
 base907c = {
        .acquire = base507c_acquire,
@@ -53,9 +91,12 @@ base907c = {
        .ntfy_set = base507c_ntfy_set,
        .ntfy_clr = base507c_ntfy_clr,
        .ntfy_wait_begun = base507c_ntfy_wait_begun,
+       .ilut = base907c_ilut,
+       .olut_core = true,
+       .xlut_set = base907c_xlut_set,
+       .xlut_clr = base907c_xlut_clr,
        .image_set = base907c_image_set,
        .image_clr = base507c_image_clr,
-       .lut = base507c_lut,
        .update = base507c_update,
 };
 
index 0f2020010aab08670da4b854d072feffad30023d..6c860e8b1b16f5f58d178a8922e0efe7d0366f21 100644 (file)
@@ -1971,8 +1971,19 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
        struct nv50_atom *atom = nv50_atom(state);
        struct drm_connector_state *old_connector_state, *new_connector_state;
        struct drm_connector *connector;
+       struct drm_crtc_state *new_crtc_state;
+       struct drm_crtc *crtc;
        int ret, i;
 
+       /* We need to handle colour management on a per-plane basis. */
+       for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
+               if (new_crtc_state->color_mgmt_changed) {
+                       ret = drm_atomic_add_affected_planes(state, crtc);
+                       if (ret)
+                               return ret;
+               }
+       }
+
        ret = drm_atomic_helper_check(dev, state);
        if (ret)
                return ret;
index 2eb7fdb611310394f89d4c98a7b77824ed87c146..ca83006510b7fea6d9471cca6ab5dd7f39d9788a 100644 (file)
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include "nouveau_connector.h"
-#include "nouveau_bo.h"
-
-static void
-nv50_head_lut_load(struct drm_property_blob *blob, int mode,
-                  struct nouveau_bo *nvbo)
-{
-       struct drm_color_lut *in = (struct drm_color_lut *)blob->data;
-       void __iomem *lut = (u8 *)nvbo_kmap_obj_iovirtual(nvbo);
-       const int size = blob->length / sizeof(*in);
-       int bits, shift, i;
-       u16 zero, r, g, b;
-
-       /* This can't happen.. But it shuts the compiler up. */
-       if (WARN_ON(size != 256))
-               return;
-
-       switch (mode) {
-       case 0: /* LORES. */
-       case 1: /* HIRES. */
-               bits = 11;
-               shift = 3;
-               zero = 0x0000;
-               break;
-       case 7: /* INTERPOLATE_257_UNITY_RANGE. */
-               bits = 14;
-               shift = 0;
-               zero = 0x6000;
-               break;
-       default:
-               WARN_ON(1);
-               return;
-       }
-
-       for (i = 0; i < size; i++) {
-               r = (drm_color_lut_extract(in[i].  red, bits) + zero) << shift;
-               g = (drm_color_lut_extract(in[i].green, bits) + zero) << shift;
-               b = (drm_color_lut_extract(in[i]. blue, bits) + zero) << shift;
-               writew(r, lut + (i * 0x08) + 0);
-               writew(g, lut + (i * 0x08) + 2);
-               writew(b, lut + (i * 0x08) + 4);
-       }
-
-       /* INTERPOLATE modes require a "next" entry to interpolate with,
-        * so we replicate the last entry to deal with this for now.
-        */
-       writew(r, lut + (i * 0x08) + 0);
-       writew(g, lut + (i * 0x08) + 2);
-       writew(b, lut + (i * 0x08) + 4);
-}
-
 void
 nv50_head_flush_clr(struct nv50_head *head,
                    struct nv50_head_atom *asyh, bool flush)
@@ -87,7 +37,7 @@ nv50_head_flush_clr(struct nv50_head *head,
        union nv50_head_atom_mask clr = {
                .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask),
        };
-       if (clr.ilut) head->func->ilut_clr(head);
+       if (clr.olut) head->func->olut_clr(head);
        if (clr.core) head->func->core_clr(head);
        if (clr.curs) head->func->curs_clr(head);
 }
@@ -97,16 +47,14 @@ nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
 {
        if (asyh->set.view   ) head->func->view    (head, asyh);
        if (asyh->set.mode   ) head->func->mode    (head, asyh);
-       if (asyh->set.ilut   ) {
-               struct nouveau_bo *nvbo = head->ilut.nvbo[head->ilut.next];
-               struct drm_property_blob *blob = asyh->state.gamma_lut;
-               if (blob)
-                       nv50_head_lut_load(blob, asyh->ilut.mode, nvbo);
-               asyh->ilut.offset = nvbo->bo.offset;
-               head->ilut.next ^= 1;
-               head->func->ilut_set(head, asyh);
-       }
        if (asyh->set.core   ) head->func->core_set(head, asyh);
+       if (asyh->set.olut   ) {
+               asyh->olut.offset = nv50_lut_load(&head->olut,
+                                                 asyh->olut.mode <= 1,
+                                                 asyh->olut.buffer,
+                                                 asyh->state.gamma_lut);
+               head->func->olut_set(head, asyh);
+       }
        if (asyh->set.curs   ) head->func->curs_set(head, asyh);
        if (asyh->set.base   ) head->func->base    (head, asyh);
        if (asyh->set.ovly   ) head->func->ovly    (head, asyh);
@@ -240,35 +188,37 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh,
        asyh->set.view = true;
 }
 
-static void
+static int
 nv50_head_atomic_check_lut(struct nv50_head *head,
-                          struct nv50_head_atom *armh,
                           struct nv50_head_atom *asyh)
 {
        struct nv50_disp *disp = nv50_disp(head->base.base.dev);
-
-       /* An I8 surface without an input LUT makes no sense, and
-        * EVO will throw an error if you try.
-        *
-        * Legacy clients actually cause this due to the order in
-        * which they call ioctls, so we will enable the LUT with
-        * whatever contents the buffer already contains to avoid
-        * triggering the error check.
-        */
-       if (!asyh->state.gamma_lut && asyh->base.cpp != 1) {
-               asyh->ilut.handle = 0;
-               asyh->clr.ilut = armh->ilut.visible;
-               return;
+       struct drm_property_blob *olut = asyh->state.gamma_lut;
+
+       /* Determine whether core output LUT should be enabled. */
+       if (olut) {
+               /* Check if any window(s) have stolen the core output LUT
+                * to as an input LUT for legacy gamma + I8 colour format.
+                */
+               if (asyh->wndw.olut) {
+                       /* If any window has stolen the core output LUT,
+                        * all of them must.
+                        */
+                       if (asyh->wndw.olut != asyh->wndw.mask)
+                               return -EINVAL;
+                       olut = NULL;
+               }
        }
 
-       if (disp->disp->object.oclass < GF110_DISP) {
-               asyh->ilut.mode = (asyh->base.cpp == 1) ? 0 : 1;
-               asyh->set.ilut = true;
-       } else {
-               asyh->ilut.mode = 7;
-               asyh->set.ilut = asyh->state.color_mgmt_changed;
+       if (!olut) {
+               asyh->olut.handle = 0;
+               return 0;
        }
-       asyh->ilut.handle = disp->core->chan.vram.handle;
+
+       asyh->olut.handle = disp->core->chan.vram.handle;
+       asyh->olut.buffer = !asyh->olut.buffer;
+       head->func->olut(head, asyh);
+       return 0;
 }
 
 static void
@@ -360,9 +310,13 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
                        nv50_head_atomic_check_mode(head, asyh);
 
                if (asyh->state.color_mgmt_changed ||
-                   asyh->base.cpp != armh->base.cpp)
-                       nv50_head_atomic_check_lut(head, armh, asyh);
-               asyh->ilut.visible = asyh->ilut.handle != 0;
+                   memcmp(&armh->wndw, &asyh->wndw, sizeof(asyh->wndw))) {
+                       int ret = nv50_head_atomic_check_lut(head, asyh);
+                       if (ret)
+                               return ret;
+
+                       asyh->olut.visible = asyh->olut.handle != 0;
+               }
 
                if (asyc) {
                        if (asyc->set.scaler)
@@ -373,13 +327,16 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
                                nv50_head_atomic_check_procamp(armh, asyh, asyc);
                }
 
-               if (head->func->core_calc)
+               if (head->func->core_calc) {
                        head->func->core_calc(head, asyh);
+                       if (!asyh->core.visible)
+                               asyh->olut.visible = false;
+               }
 
                asyh->set.base = armh->base.cpp != asyh->base.cpp;
                asyh->set.ovly = armh->ovly.cpp != asyh->ovly.cpp;
        } else {
-               asyh->ilut.visible = false;
+               asyh->olut.visible = false;
                asyh->core.visible = false;
                asyh->curs.visible = false;
                asyh->base.cpp = 0;
@@ -402,11 +359,19 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
                if (armh->curs.visible) {
                        asyh->clr.curs = true;
                }
+
+               if (asyh->olut.visible) {
+                       if (memcmp(&armh->olut, &asyh->olut, sizeof(asyh->olut)))
+                               asyh->set.olut = true;
+               } else
+               if (armh->olut.visible) {
+                       asyh->clr.olut = true;
+               }
        } else {
-               asyh->clr.ilut = armh->ilut.visible;
+               asyh->clr.olut = armh->olut.visible;
                asyh->clr.core = armh->core.visible;
                asyh->clr.curs = armh->curs.visible;
-               asyh->set.ilut = asyh->ilut.visible;
+               asyh->set.olut = asyh->olut.visible;
                asyh->set.core = asyh->core.visible;
                asyh->set.curs = asyh->curs.visible;
        }
@@ -438,9 +403,10 @@ nv50_head_atomic_duplicate_state(struct drm_crtc *crtc)
        if (!(asyh = kmalloc(sizeof(*asyh), GFP_KERNEL)))
                return NULL;
        __drm_atomic_helper_crtc_duplicate_state(crtc, &asyh->state);
+       asyh->wndw = armh->wndw;
        asyh->view = armh->view;
        asyh->mode = armh->mode;
-       asyh->ilut = armh->ilut;
+       asyh->olut = armh->olut;
        asyh->core = armh->core;
        asyh->curs = armh->curs;
        asyh->base = armh->base;
@@ -477,11 +443,7 @@ static void
 nv50_head_destroy(struct drm_crtc *crtc)
 {
        struct nv50_head *head = nv50_head(crtc);
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(head->ilut.nvbo); i++)
-               nouveau_bo_unmap_unpin_unref(&head->ilut.nvbo[i]);
-
+       nv50_lut_fini(&head->olut);
        drm_crtc_cleanup(crtc);
        kfree(head);
 }
@@ -505,7 +467,7 @@ nv50_head_create(struct drm_device *dev, int index)
        struct nv50_head *head;
        struct nv50_wndw *curs, *wndw;
        struct drm_crtc *crtc;
-       int ret, i;
+       int ret;
 
        head = kzalloc(sizeof(*head), GFP_KERNEL);
        if (!head)
@@ -527,10 +489,8 @@ nv50_head_create(struct drm_device *dev, int index)
        drm_crtc_helper_add(crtc, &nv50_head_help);
        drm_mode_crtc_set_gamma_size(crtc, 256);
 
-       for (i = 0; i < ARRAY_SIZE(head->ilut.nvbo); i++) {
-               ret = nouveau_bo_new_pin_map(&drm->client, 1025 * 8, 0x100,
-                                            TTM_PL_FLAG_VRAM,
-                                            &head->ilut.nvbo[i]);
+       if (head->func->olut_set) {
+               ret = nv50_lut_init(disp, &drm->client.mmu, &head->olut);
                if (ret)
                        goto out;
        }
index d00cebdbd2600674230fe17da4ff885135e815fa..0802271bc90ccb2b30cb0805c907650f00bd82f5 100644 (file)
@@ -3,16 +3,14 @@
 #define nv50_head(c) container_of((c), struct nv50_head, base.base)
 #include "disp.h"
 #include "atom.h"
+#include "lut.h"
 
 #include "nouveau_crtc.h"
 
 struct nv50_head {
        const struct nv50_head_func *func;
        struct nouveau_crtc base;
-       struct {
-               struct nouveau_bo *nvbo[2];
-               int next;
-       } ilut;
+       struct nv50_lut olut;
 };
 
 int nv50_head_create(struct drm_device *, int index);
@@ -22,8 +20,9 @@ void nv50_head_flush_clr(struct nv50_head *, struct nv50_head_atom *, bool y);
 struct nv50_head_func {
        void (*view)(struct nv50_head *, struct nv50_head_atom *);
        void (*mode)(struct nv50_head *, struct nv50_head_atom *);
-       void (*ilut_set)(struct nv50_head *, struct nv50_head_atom *);
-       void (*ilut_clr)(struct nv50_head *);
+       void (*olut)(struct nv50_head *, struct nv50_head_atom *);
+       void (*olut_set)(struct nv50_head *, struct nv50_head_atom *);
+       void (*olut_clr)(struct nv50_head *);
        void (*core_calc)(struct nv50_head *, struct nv50_head_atom *);
        void (*core_set)(struct nv50_head *, struct nv50_head_atom *);
        void (*core_clr)(struct nv50_head *);
@@ -39,6 +38,7 @@ struct nv50_head_func {
 extern const struct nv50_head_func head507d;
 void head507d_view(struct nv50_head *, struct nv50_head_atom *);
 void head507d_mode(struct nv50_head *, struct nv50_head_atom *);
+void head507d_olut(struct nv50_head *, struct nv50_head_atom *);
 void head507d_core_calc(struct nv50_head *, struct nv50_head_atom *);
 void head507d_core_clr(struct nv50_head *);
 void head507d_base(struct nv50_head *, struct nv50_head_atom *);
@@ -51,13 +51,13 @@ extern const struct nv50_head_func head827d;
 extern const struct nv50_head_func head907d;
 void head907d_view(struct nv50_head *, struct nv50_head_atom *);
 void head907d_mode(struct nv50_head *, struct nv50_head_atom *);
-void head907d_ilut_set(struct nv50_head *, struct nv50_head_atom *);
-void head907d_ilut_clr(struct nv50_head *);
+void head907d_olut(struct nv50_head *, struct nv50_head_atom *);
+void head907d_olut_set(struct nv50_head *, struct nv50_head_atom *);
+void head907d_olut_clr(struct nv50_head *);
 void head907d_core_set(struct nv50_head *, struct nv50_head_atom *);
 void head907d_core_clr(struct nv50_head *);
 void head907d_curs_set(struct nv50_head *, struct nv50_head_atom *);
 void head907d_curs_clr(struct nv50_head *);
-void head907d_base(struct nv50_head *, struct nv50_head_atom *);
 void head907d_ovly(struct nv50_head *, struct nv50_head_atom *);
 void head907d_procamp(struct nv50_head *, struct nv50_head_atom *);
 void head907d_or(struct nv50_head *, struct nv50_head_atom *);
index 5f06fa174832f0806f422fcb439f5488a3b78bdf..75575c33c5d674bfef471e7ba6ca9455d6878acd 100644 (file)
@@ -165,6 +165,7 @@ head507d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
                 * without also updating HeadSetOffsetCursor.
                 */
                asyh->set.curs = asyh->curs.visible;
+               asyh->set.olut = asyh->olut.handle != 0;
        }
 }
 
@@ -178,8 +179,8 @@ head507d_core_calc(struct nv50_head *head, struct nv50_head_atom *asyh)
                asyh->core.w = asyh->base.w;
                asyh->core.h = asyh->base.h;
        } else
-       if ((asyh->core.visible = asyh->curs.visible) ||
-           (asyh->core.visible = asyh->ilut.visible)) {
+       if ((asyh->core.visible = (asyh->ovly.cpp != 0)) ||
+           (asyh->core.visible = asyh->curs.visible)) {
                /*XXX: We need to either find some way of having the
                 *     primary base layer appear black, while still
                 *     being able to display the other layers, or we
@@ -200,30 +201,39 @@ head507d_core_calc(struct nv50_head *head, struct nv50_head_atom *asyh)
 }
 
 static void
-head507d_ilut_clr(struct nv50_head *head)
+head507d_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, 0x0840 + (head->base.index * 0x400), 1);
-               evo_data(push, 0x40000000);
+               evo_data(push, 0x00000000);
                evo_kick(push, core);
        }
 }
 
 static void
-head507d_ilut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
+head507d_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, 3))) {
                evo_mthd(push, 0x0840 + (head->base.index * 0x400), 2);
-               evo_data(push, 0x80000000 | asyh->ilut.mode << 30);
-               evo_data(push, asyh->ilut.offset >> 8);
+               evo_data(push, 0x80000000 | asyh->olut.mode << 30);
+               evo_data(push, asyh->olut.offset >> 8);
                evo_kick(push, core);
        }
 }
 
+void
+head507d_olut(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       if (asyh->base.cpp == 1)
+               asyh->olut.mode = 0;
+       else
+               asyh->olut.mode = 1;
+}
+
 void
 head507d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
 {
@@ -269,8 +279,9 @@ const struct nv50_head_func
 head507d = {
        .view = head507d_view,
        .mode = head507d_mode,
-       .ilut_set = head507d_ilut_set,
-       .ilut_clr = head507d_ilut_clr,
+       .olut = head507d_olut,
+       .olut_set = head507d_olut_set,
+       .olut_clr = head507d_olut_clr,
        .core_calc = head507d_core_calc,
        .core_set = head507d_core_set,
        .core_clr = head507d_core_clr,
index 84ce595fbe79351b40647b9766545f27ee913b7e..ddc143bac3055c332cabf28648dff6e692eecee7 100644 (file)
@@ -74,13 +74,13 @@ head827d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
 }
 
 static void
-head827d_ilut_clr(struct nv50_head *head)
+head827d_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, 4))) {
                evo_mthd(push, 0x0840 + (head->base.index * 0x400), 1);
-               evo_data(push, 0x40000000);
+               evo_data(push, 0x00000000);
                evo_mthd(push, 0x085c + (head->base.index * 0x400), 1);
                evo_data(push, 0x00000000);
                evo_kick(push, core);
@@ -88,16 +88,16 @@ head827d_ilut_clr(struct nv50_head *head)
 }
 
 static void
-head827d_ilut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
+head827d_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, 5))) {
                evo_mthd(push, 0x0840 + (head->base.index * 0x400), 2);
-               evo_data(push, 0x80000000 | asyh->ilut.mode << 30);
-               evo_data(push, asyh->ilut.offset >> 8);
+               evo_data(push, 0x80000000 | asyh->olut.mode << 30);
+               evo_data(push, asyh->olut.offset >> 8);
                evo_mthd(push, 0x085c + (head->base.index * 0x400), 1);
-               evo_data(push, asyh->ilut.handle);
+               evo_data(push, asyh->olut.handle);
                evo_kick(push, core);
        }
 }
@@ -106,8 +106,9 @@ const struct nv50_head_func
 head827d = {
        .view = head507d_view,
        .mode = head507d_mode,
-       .ilut_set = head827d_ilut_set,
-       .ilut_clr = head827d_ilut_clr,
+       .olut = head507d_olut,
+       .olut_set = head827d_olut_set,
+       .olut_clr = head827d_olut_clr,
        .core_calc = head507d_core_calc,
        .core_set = head827d_core_set,
        .core_clr = head507d_core_clr,
index 0035eccd62d61034966e33bcdcdb3c080d7760ef..0fa0159bfafb8d11ffb7c46ffdbb70afc9a154db 100644 (file)
@@ -91,7 +91,7 @@ head907d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh)
        }
 }
 
-void
+static void
 head907d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
 {
        struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
@@ -182,13 +182,13 @@ head907d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
 }
 
 void
-head907d_ilut_clr(struct nv50_head *head)
+head907d_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, 4))) {
-               evo_mthd(push, 0x0440 + (head->base.index * 0x300), 1);
-               evo_data(push, 0x03000000);
+               evo_mthd(push, 0x0448 + (head->base.index * 0x300), 1);
+               evo_data(push, 0x00000000);
                evo_mthd(push, 0x045c + (head->base.index * 0x300), 1);
                evo_data(push, 0x00000000);
                evo_kick(push, core);
@@ -196,22 +196,26 @@ head907d_ilut_clr(struct nv50_head *head)
 }
 
 void
-head907d_ilut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
+head907d_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, 7))) {
-               evo_mthd(push, 0x0440 + (head->base.index * 0x300), 4);
-               evo_data(push, 0x80000000 | asyh->ilut.mode << 24);
-               evo_data(push, asyh->ilut.offset >> 8);
-               evo_data(push, 0x00000000);
-               evo_data(push, 0x00000000);
+       if ((push = evo_wait(core, 5))) {
+               evo_mthd(push, 0x0448 + (head->base.index * 0x300), 2);
+               evo_data(push, 0x80000000 | asyh->olut.mode << 24);
+               evo_data(push, asyh->olut.offset >> 8);
                evo_mthd(push, 0x045c + (head->base.index * 0x300), 1);
-               evo_data(push, asyh->ilut.handle);
+               evo_data(push, asyh->olut.handle);
                evo_kick(push, core);
        }
 }
 
+void
+head907d_olut(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       asyh->olut.mode = 7;
+}
+
 void
 head907d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
 {
@@ -259,8 +263,9 @@ const struct nv50_head_func
 head907d = {
        .view = head907d_view,
        .mode = head907d_mode,
-       .ilut_set = head907d_ilut_set,
-       .ilut_clr = head907d_ilut_clr,
+       .olut = head907d_olut,
+       .olut_set = head907d_olut_set,
+       .olut_clr = head907d_olut_clr,
        .core_calc = head507d_core_calc,
        .core_set = head907d_core_set,
        .core_clr = head907d_core_clr,
index 5341ea3bc7b6003fb3d47821a12e258ae35f7eb8..5f654512c8c268cede60fc0208b5b75f8c0f0159 100644 (file)
@@ -36,18 +36,46 @@ head917d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
        }
 }
 
+static void
+head917d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
+       u32 bounds = 0;
+       u32 *push;
+
+       if (asyh->base.cpp) {
+               switch (asyh->base.cpp) {
+               case 8: bounds |= 0x00000500; break;
+               case 4: bounds |= 0x00000300; break;
+               case 2: bounds |= 0x00000100; break;
+               case 1: bounds |= 0x00000000; break;
+               default:
+                       WARN_ON(1);
+                       break;
+               }
+               bounds |= 0x00020001;
+       }
+
+       if ((push = evo_wait(core, 2))) {
+               evo_mthd(push, 0x04d0 + head->base.index * 0x300, 1);
+               evo_data(push, bounds);
+               evo_kick(push, core);
+       }
+}
+
 const struct nv50_head_func
 head917d = {
        .view = head907d_view,
        .mode = head907d_mode,
-       .ilut_set = head907d_ilut_set,
-       .ilut_clr = head907d_ilut_clr,
+       .olut = head907d_olut,
+       .olut_set = head907d_olut_set,
+       .olut_clr = head907d_olut_clr,
        .core_calc = head507d_core_calc,
        .core_set = head907d_core_set,
        .core_clr = head907d_core_clr,
        .curs_set = head907d_curs_set,
        .curs_clr = head907d_curs_clr,
-       .base = head907d_base,
+       .base = head917d_base,
        .ovly = head907d_ovly,
        .dither = head917d_dither,
        .procamp = head907d_procamp,
diff --git a/drivers/gpu/drm/nouveau/dispnv50/lut.c b/drivers/gpu/drm/nouveau/dispnv50/lut.c
new file mode 100644 (file)
index 0000000..a6b96ae
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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 "lut.h"
+#include "disp.h"
+
+#include <drm/drm_color_mgmt.h>
+#include <drm/drm_mode.h>
+#include <drm/drm_property.h>
+
+#include <nvif/class.h>
+
+u32
+nv50_lut_load(struct nv50_lut *lut, bool legacy, int buffer,
+             struct drm_property_blob *blob)
+{
+       struct drm_color_lut *in = (struct drm_color_lut *)blob->data;
+       void __iomem *mem = lut->mem[buffer].object.map.ptr;
+       const int size = blob->length / sizeof(*in);
+       int bits, shift, i;
+       u16 zero, r, g, b;
+       u32 addr = lut->mem[buffer].addr;
+
+       /* This can't happen.. But it shuts the compiler up. */
+       if (WARN_ON(size != 256))
+               return 0;
+
+       if (legacy) {
+               bits = 11;
+               shift = 3;
+               zero = 0x0000;
+       } else {
+               bits = 14;
+               shift = 0;
+               zero = 0x6000;
+       }
+
+       for (i = 0; i < size; i++) {
+               r = (drm_color_lut_extract(in[i].  red, bits) + zero) << shift;
+               g = (drm_color_lut_extract(in[i].green, bits) + zero) << shift;
+               b = (drm_color_lut_extract(in[i]. blue, bits) + zero) << shift;
+               writew(r, mem + (i * 0x08) + 0);
+               writew(g, mem + (i * 0x08) + 2);
+               writew(b, mem + (i * 0x08) + 4);
+       }
+
+       /* INTERPOLATE modes require a "next" entry to interpolate with,
+        * so we replicate the last entry to deal with this for now.
+        */
+       writew(r, mem + (i * 0x08) + 0);
+       writew(g, mem + (i * 0x08) + 2);
+       writew(b, mem + (i * 0x08) + 4);
+       return addr;
+}
+
+void
+nv50_lut_fini(struct nv50_lut *lut)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(lut->mem); i++)
+               nvif_mem_fini(&lut->mem[i]);
+}
+
+int
+nv50_lut_init(struct nv50_disp *disp, struct nvif_mmu *mmu,
+             struct nv50_lut *lut)
+{
+       const u32 size = disp->disp->object.oclass < GF110_DISP ? 257 : 1025;
+       int i;
+       for (i = 0; i < ARRAY_SIZE(lut->mem); i++) {
+               int ret = nvif_mem_init_map(mmu, NVIF_MEM_VRAM, size * 8,
+                                           &lut->mem[i]);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/dispnv50/lut.h b/drivers/gpu/drm/nouveau/dispnv50/lut.h
new file mode 100644 (file)
index 0000000..6d7b835
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __NV50_KMS_LUT_H__
+#define __NV50_KMS_LUT_H__
+#include <nvif/mem.h>
+struct drm_property_blob;
+struct nv50_disp;
+
+struct nv50_lut {
+       struct nvif_mem mem[2];
+};
+
+int nv50_lut_init(struct nv50_disp *, struct nvif_mmu *, struct nv50_lut *);
+void nv50_lut_fini(struct nv50_lut *);
+u32 nv50_lut_load(struct nv50_lut *, bool legacy, int buffer,
+                 struct drm_property_blob *);
+#endif
index 0f6de6049be4412f9accf9eb1f637d22589ffde3..fbaf8b7ed203aa5dd08bba6b7f7e49ab22c344be 100644 (file)
@@ -116,6 +116,7 @@ nv50_wndw_flush_clr(struct nv50_wndw *wndw, u32 *interlock, bool flush,
        };
        if (clr.sema ) wndw->func-> sema_clr(wndw);
        if (clr.ntfy ) wndw->func-> ntfy_clr(wndw);
+       if (clr.xlut ) wndw->func-> xlut_clr(wndw);
        if (clr.image) wndw->func->image_clr(wndw);
 
        interlock[wndw->interlock.type] |= wndw->interlock.data;
@@ -133,7 +134,18 @@ nv50_wndw_flush_set(struct nv50_wndw *wndw, u32 *interlock,
        if (asyw->set.sema ) wndw->func->sema_set (wndw, asyw);
        if (asyw->set.ntfy ) wndw->func->ntfy_set (wndw, asyw);
        if (asyw->set.image) wndw->func->image_set(wndw, asyw);
-       if (asyw->set.lut  ) wndw->func->lut      (wndw, asyw);
+
+       if (asyw->set.xlut ) {
+               if (asyw->ilut) {
+                       asyw->xlut.i.offset =
+                               nv50_lut_load(&wndw->ilut,
+                                             asyw->xlut.i.mode <= 1,
+                                             asyw->xlut.i.buffer,
+                                             asyw->ilut);
+               }
+               wndw->func->xlut_set(wndw, asyw);
+       }
+
        if (asyw->set.point) {
                wndw->immd->point(wndw, asyw);
                wndw->immd->update(wndw, interlock);
@@ -241,7 +253,56 @@ nv50_wndw_atomic_check_acquire(struct nv50_wndw *wndw, bool modeset,
        return wndw->func->acquire(wndw, asyw, asyh);
 }
 
-int
+static void
+nv50_wndw_atomic_check_lut(struct nv50_wndw *wndw,
+                          struct nv50_wndw_atom *armw,
+                          struct nv50_wndw_atom *asyw,
+                          struct nv50_head_atom *asyh)
+{
+       struct drm_property_blob *ilut = asyh->state.degamma_lut;
+
+       /* I8 format without an input LUT makes no sense, and the
+        * HW error-checks for this.
+        *
+        * In order to handle legacy gamma, when there's no input
+        * LUT we need to steal the output LUT and use it instead.
+        */
+       if (!ilut && asyw->state.fb->format->format == DRM_FORMAT_C8) {
+               /* This should be an error, but there's legacy clients
+                * that do a modeset before providing a gamma table.
+                *
+                * We keep the window disabled to avoid angering HW.
+                */
+               if (!(ilut = asyh->state.gamma_lut)) {
+                       asyw->visible = false;
+                       return;
+               }
+
+               if (wndw->func->ilut)
+                       asyh->wndw.olut |= BIT(wndw->id);
+       } else {
+               asyh->wndw.olut &= ~BIT(wndw->id);
+       }
+
+       /* Recalculate LUT state. */
+       memset(&asyw->xlut, 0x00, sizeof(asyw->xlut));
+       if ((asyw->ilut = wndw->func->ilut ? ilut : NULL)) {
+               wndw->func->ilut(wndw, asyw);
+               asyw->xlut.handle = wndw->wndw.vram.handle;
+               asyw->xlut.i.buffer = !asyw->xlut.i.buffer;
+               asyw->set.xlut = true;
+       }
+
+       /* Handle setting base SET_OUTPUT_LUT_LO_ENABLE_USE_CORE_LUT. */
+       if (wndw->func->olut_core &&
+           (!armw->visible || (armw->xlut.handle && !asyw->xlut.handle)))
+               asyw->set.xlut = true;
+
+       /* Can't do an immediate flip while changing the LUT. */
+       asyh->state.pageflip_flags &= ~DRM_MODE_PAGE_FLIP_ASYNC;
+}
+
+static int
 nv50_wndw_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
 {
        struct nouveau_drm *drm = nouveau_drm(plane->dev);
@@ -274,15 +335,26 @@ nv50_wndw_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
                        return PTR_ERR(harm);
        }
 
+       /* LUT configuration can potentially cause the window to be disabled. */
+       if (asyw->visible && wndw->func->xlut_set &&
+           (!armw->visible ||
+            asyh->state.color_mgmt_changed ||
+            asyw->state.fb->format->format !=
+            armw->state.fb->format->format))
+               nv50_wndw_atomic_check_lut(wndw, armw, asyw, asyh);
+
        /* Calculate new window state. */
        if (asyw->visible) {
                ret = nv50_wndw_atomic_check_acquire(wndw, modeset,
                                                     armw, asyw, asyh);
                if (ret)
                        return ret;
+
+               asyh->wndw.mask |= BIT(wndw->id);
        } else
        if (armw->visible) {
                nv50_wndw_atomic_check_release(wndw, asyw, harm);
+               harm->wndw.mask &= ~BIT(wndw->id);
        } else {
                return 0;
        }
@@ -294,9 +366,9 @@ nv50_wndw_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
        if (!asyw->visible || modeset) {
                asyw->clr.ntfy = armw->ntfy.handle != 0;
                asyw->clr.sema = armw->sema.handle != 0;
+               asyw->clr.xlut = armw->xlut.handle != 0;
                if (wndw->func->image_clr)
                        asyw->clr.image = armw->image.handle[0] != 0;
-               asyw->set.lut = wndw->func->lut && asyw->visible;
        }
 
        return 0;
@@ -381,9 +453,10 @@ nv50_wndw_atomic_duplicate_state(struct drm_plane *plane)
        __drm_atomic_helper_plane_duplicate_state(plane, &asyw->state);
        asyw->sema = armw->sema;
        asyw->ntfy = armw->ntfy;
+       asyw->ilut = NULL;
+       asyw->xlut = armw->xlut;
        asyw->image = armw->image;
        asyw->point = armw->point;
-       asyw->lut = armw->lut;
        asyw->clr.mask = 0;
        asyw->set.mask = 0;
        return &asyw->state;
@@ -417,6 +490,9 @@ nv50_wndw_destroy(struct drm_plane *plane)
        nvif_notify_fini(&wndw->notify);
        nv50_dmac_destroy(&wndw->wimm);
        nv50_dmac_destroy(&wndw->wndw);
+
+       nv50_lut_fini(&wndw->ilut);
+
        drm_plane_cleanup(&wndw->plane);
        kfree(wndw);
 }
@@ -456,6 +532,9 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
               enum nv50_disp_interlock_type interlock_type, u32 interlock_data,
               struct nv50_wndw **pwndw)
 {
+       struct nouveau_drm *drm = nouveau_drm(dev);
+       struct nvif_mmu *mmu = &drm->client.mmu;
+       struct nv50_disp *disp = nv50_disp(dev);
        struct nv50_wndw *wndw;
        int nformat;
        int ret;
@@ -484,6 +563,12 @@ nv50_wndw_new_(const struct nv50_wndw_func *func, struct drm_device *dev,
 
        drm_plane_helper_add(&wndw->plane, &nv50_wndw_helper);
 
+       if (wndw->func->ilut) {
+               ret = nv50_lut_init(disp, mmu, &wndw->ilut);
+               if (ret)
+                       return ret;
+       }
+
        wndw->notify.func = nv50_wndw_notify;
        return 0;
 }
index c26796c612f6b7c40bcededf810ce3b4bff2a3a5..223cf3f37dae3316198d430c5218abfe66769a0e 100644 (file)
@@ -3,6 +3,7 @@
 #define nv50_wndw(p) container_of((p), struct nv50_wndw, plane)
 #include "disp.h"
 #include "atom.h"
+#include "lut.h"
 
 #include <nvif/notify.h>
 
@@ -24,6 +25,8 @@ struct nv50_wndw {
 
        struct drm_plane plane;
 
+       struct nv50_lut ilut;
+
        struct nv50_dmac wndw;
        struct nv50_dmac wimm;
 
@@ -61,9 +64,12 @@ struct nv50_wndw_func {
        void (*ntfy_clr)(struct nv50_wndw *);
        int (*ntfy_wait_begun)(struct nouveau_bo *, u32 offset,
                               struct nvif_device *);
+       void (*ilut)(struct nv50_wndw *, struct nv50_wndw_atom *);
+       bool olut_core;
+       void (*xlut_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
+       void (*xlut_clr)(struct nv50_wndw *);
        void (*image_set)(struct nv50_wndw *, struct nv50_wndw_atom *);
        void (*image_clr)(struct nv50_wndw *);
-       void (*lut)(struct nv50_wndw *, struct nv50_wndw_atom *);
 
        void (*update)(struct nv50_wndw *, u32 *interlock);
 };