drm/nouveau/kms/nv50: separate out core surface commit
authorBen Skeggs <bskeggs@redhat.com>
Fri, 4 Nov 2016 07:20:36 +0000 (17:20 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Mon, 7 Nov 2016 04:04:52 +0000 (14:04 +1000)
This commit separates the calculation of EVO state from the commit, in
order to make the same code useful for atomic modesetting.

The legacy interfaces have been wrapped on top of them.

As of this commit, we're no longer bothering to point the core surface
at a valid framebuffer.  Prior to this, we'd initially point the core
channel to the framebuffer passed in a mode_set()/mode_set_base(), and
then use the base channel for any page-flip updates, leaving the core
channel pointing at stale information.

The important thing here is to configure the core surface parameters in
such a way that EVO's error checking is satisfied.

TL;DR: The situation isn't too much different to before.

There may be brief periods of times during modesets where the (garbage)
core surface will be showing.  This issue will be resolved once support
for atomic commits has been implemented and we're able to interlock the
updates that involve multiple channels.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nv50_display.c

index 8d27ddd75b3722f92b109f4bfc8e91f9e79ac53b..8203a38737977d996ca1488df034afdd30ded107 100644 (file)
 #include <linux/dma-mapping.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
 #include <drm/drm_crtc_helper.h>
-#include <drm/drm_plane_helper.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_plane_helper.h>
 
 #include <nvif/class.h>
 #include <nvif/cl0002.h>
@@ -90,8 +91,41 @@ struct nv50_head_atom {
                } v;
        } mode;
 
+       struct {
+               bool visible;
+               u32 handle;
+               u64 offset:40;
+               u8  format;
+               u8  kind:7;
+               u8  layout:1;
+               u8  block:4;
+               u32 pitch:20;
+               u16 x;
+               u16 y;
+               u16 w;
+               u16 h;
+       } core;
+
+       struct {
+               u8  depth;
+               u8  cpp;
+               u16 x;
+               u16 y;
+               u16 w;
+               u16 h;
+       } base;
+
+       union {
+               struct {
+                       bool core:1;
+               };
+               u8 mask;
+       } clr;
+
        union {
                struct {
+                       bool core:1;
+                       bool view:1;
                        bool mode:1;
                };
                u16 mask;
@@ -737,6 +771,70 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
  * Head
  *****************************************************************************/
 
+static void
+nv50_head_core_clr(struct nv50_head *head)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->mast.base;
+       u32 *push;
+       if ((push = evo_wait(core, 2))) {
+               if (core->base.user.oclass < GF110_DISP_CORE_CHANNEL_DMA)
+                       evo_mthd(push, 0x0874 + head->base.index * 0x400, 1);
+               else
+                       evo_mthd(push, 0x0474 + head->base.index * 0x300, 1);
+               evo_data(push, 0x00000000);
+               evo_kick(push, core);
+       }
+}
+
+static void
+nv50_head_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
+{
+       struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->mast.base;
+       u32 *push;
+       if ((push = evo_wait(core, 9))) {
+               if (core->base.user.oclass < G82_DISP_CORE_CHANNEL_DMA) {
+                       evo_mthd(push, 0x0860 + head->base.index * 0x400, 1);
+                       evo_data(push, asyh->core.offset >> 8);
+                       evo_mthd(push, 0x0868 + head->base.index * 0x400, 4);
+                       evo_data(push, (asyh->core.h << 16) | asyh->core.w);
+                       evo_data(push, asyh->core.layout << 20 |
+                                      (asyh->core.pitch >> 8) << 8 |
+                                      asyh->core.block);
+                       evo_data(push, asyh->core.kind << 16 |
+                                      asyh->core.format << 8);
+                       evo_data(push, asyh->core.handle);
+                       evo_mthd(push, 0x08c0 + head->base.index * 0x400, 1);
+                       evo_data(push, (asyh->core.y << 16) | asyh->core.x);
+               } else
+               if (core->base.user.oclass < GF110_DISP_CORE_CHANNEL_DMA) {
+                       evo_mthd(push, 0x0860 + head->base.index * 0x400, 1);
+                       evo_data(push, asyh->core.offset >> 8);
+                       evo_mthd(push, 0x0868 + head->base.index * 0x400, 4);
+                       evo_data(push, (asyh->core.h << 16) | asyh->core.w);
+                       evo_data(push, asyh->core.layout << 20 |
+                                      (asyh->core.pitch >> 8) << 8 |
+                                      asyh->core.block);
+                       evo_data(push, asyh->core.format << 8);
+                       evo_data(push, asyh->core.handle);
+                       evo_mthd(push, 0x08c0 + head->base.index * 0x400, 1);
+                       evo_data(push, (asyh->core.y << 16) | asyh->core.x);
+               } else {
+                       evo_mthd(push, 0x0460 + head->base.index * 0x300, 1);
+                       evo_data(push, asyh->core.offset >> 8);
+                       evo_mthd(push, 0x0468 + head->base.index * 0x300, 4);
+                       evo_data(push, (asyh->core.h << 16) | asyh->core.w);
+                       evo_data(push, asyh->core.layout << 24 |
+                                      (asyh->core.pitch >> 8) << 8 |
+                                      asyh->core.block);
+                       evo_data(push, asyh->core.format << 8);
+                       evo_data(push, asyh->core.handle);
+                       evo_mthd(push, 0x04b0 + head->base.index * 0x300, 1);
+                       evo_data(push, (asyh->core.y << 16) | asyh->core.x);
+               }
+               evo_kick(push, core);
+       }
+}
+
 static void
 nv50_head_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
 {
@@ -777,10 +875,18 @@ nv50_head_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
        }
 }
 
+static void
+nv50_head_flush_clr(struct nv50_head *head, struct nv50_head_atom *asyh, bool y)
+{
+       if (asyh->clr.core && (!asyh->set.core || y))
+               nv50_head_core_clr(head);
+}
+
 static void
 nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh)
 {
        if (asyh->set.mode   ) nv50_head_mode    (head, asyh);
+       if (asyh->set.core   ) nv50_head_core_set(head, asyh);
 }
 
 static void
@@ -830,16 +936,58 @@ static int
 nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state)
 {
        struct nouveau_drm *drm = nouveau_drm(crtc->dev);
+       struct nv50_disp *disp = nv50_disp(crtc->dev);
        struct nv50_head *head = nv50_head(crtc);
        struct nv50_head_atom *armh = &head->arm;
        struct nv50_head_atom *asyh = nv50_head_atom(state);
 
        NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active);
+       asyh->clr.mask = 0;
        asyh->set.mask = 0;
 
        if (asyh->state.active) {
                if (asyh->state.mode_changed)
                        nv50_head_atomic_check_mode(head, asyh);
+
+               if ((asyh->core.visible = (asyh->base.cpp != 0))) {
+                       asyh->core.x = asyh->base.x;
+                       asyh->core.y = asyh->base.y;
+                       asyh->core.w = asyh->base.w;
+                       asyh->core.h = asyh->base.h;
+               } else
+               if ((asyh->core.visible = true)) {
+                       /*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
+                        *     need to allocate a dummy black surface here.
+                        */
+                       asyh->core.x = 0;
+                       asyh->core.y = 0;
+                       asyh->core.w = asyh->state.mode.hdisplay;
+                       asyh->core.h = asyh->state.mode.vdisplay;
+               }
+               asyh->core.handle = disp->mast.base.vram.handle;
+               asyh->core.offset = 0;
+               asyh->core.format = 0xcf;
+               asyh->core.kind = 0;
+               asyh->core.layout = 1;
+               asyh->core.block = 0;
+               asyh->core.pitch = ALIGN(asyh->core.w, 64) * 4;
+       } else {
+               asyh->core.visible = false;
+       }
+
+       if (!drm_atomic_crtc_needs_modeset(&asyh->state)) {
+               if (asyh->core.visible) {
+                       if (memcmp(&armh->core, &asyh->core, sizeof(asyh->core)))
+                               asyh->set.core = true;
+               } else
+               if (armh->core.visible) {
+                       asyh->clr.core = true;
+               }
+       } else {
+               asyh->clr.core = armh->core.visible;
+               asyh->set.core = asyh->core.visible;
        }
 
        memcpy(armh, asyh, sizeof(*asyh));
@@ -1057,41 +1205,31 @@ nv50_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb,
                    int x, int y, bool update)
 {
        struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb);
-       struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev);
-       u32 *push;
+       struct nv50_head *head = nv50_head(&nv_crtc->base);
+       struct nv50_head_atom *asyh = &head->asy;
+       const struct drm_format_info *info;
 
-       push = evo_wait(mast, 16);
-       if (push) {
-               if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
-                       evo_mthd(push, 0x0860 + (nv_crtc->index * 0x400), 1);
-                       evo_data(push, nvfb->nvbo->bo.offset >> 8);
-                       evo_mthd(push, 0x0868 + (nv_crtc->index * 0x400), 3);
-                       evo_data(push, (fb->height << 16) | fb->width);
-                       evo_data(push, nvfb->r_pitch);
-                       evo_data(push, nvfb->r_format);
-                       evo_mthd(push, 0x08c0 + (nv_crtc->index * 0x400), 1);
-                       evo_data(push, (y << 16) | x);
-                       if (nv50_vers(mast) > NV50_DISP_CORE_CHANNEL_DMA) {
-                               evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
-                               evo_data(push, nvfb->r_handle);
-                       }
-               } else {
-                       evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1);
-                       evo_data(push, nvfb->nvbo->bo.offset >> 8);
-                       evo_mthd(push, 0x0468 + (nv_crtc->index * 0x300), 4);
-                       evo_data(push, (fb->height << 16) | fb->width);
-                       evo_data(push, nvfb->r_pitch);
-                       evo_data(push, nvfb->r_format);
-                       evo_data(push, nvfb->r_handle);
-                       evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1);
-                       evo_data(push, (y << 16) | x);
-               }
+       info = drm_format_info(nvfb->base.pixel_format);
+       if (!info || !info->depth)
+               return -EINVAL;
 
-               if (update) {
+       asyh->base.depth = info->depth;
+       asyh->base.cpp = info->cpp[0];
+       asyh->base.x = x;
+       asyh->base.y = y;
+       asyh->base.w = nvfb->base.width;
+       asyh->base.h = nvfb->base.height;
+       nv50_head_atomic_check(&head->base.base, &asyh->state);
+       nv50_head_flush_set(head, asyh);
+
+       if (update) {
+               struct nv50_mast *core = nv50_mast(nv_crtc->base.dev);
+               u32 *push = evo_wait(core, 2);
+               if (push) {
                        evo_mthd(push, 0x0080, 1);
                        evo_data(push, 0x00000000);
+                       evo_kick(push, core);
                }
-               evo_kick(push, mast);
        }
 
        nv_crtc->fb.handle = nvfb->r_handle;
@@ -1183,28 +1321,28 @@ nv50_crtc_prepare(struct drm_crtc *crtc)
 {
        struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
        struct nv50_mast *mast = nv50_mast(crtc->dev);
+       struct nv50_head *head = nv50_head(crtc);
+       struct nv50_head_atom *asyh = &head->asy;
        u32 *push;
 
        nv50_display_flip_stop(crtc);
 
+       asyh->state.active = false;
+       nv50_head_atomic_check(&head->base.base, &asyh->state);
+       nv50_head_flush_clr(head, asyh, false);
+
        push = evo_wait(mast, 6);
        if (push) {
                if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) {
-                       evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
-                       evo_data(push, 0x00000000);
                        evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 1);
                        evo_data(push, 0x40000000);
                } else
                if (nv50_vers(mast) <  GF110_DISP_CORE_CHANNEL_DMA) {
-                       evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
-                       evo_data(push, 0x00000000);
                        evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 1);
                        evo_data(push, 0x40000000);
                        evo_mthd(push, 0x085c + (nv_crtc->index * 0x400), 1);
                        evo_data(push, 0x00000000);
                } else {
-                       evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1);
-                       evo_data(push, 0x00000000);
                        evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 1);
                        evo_data(push, 0x03000000);
                        evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1);
@@ -1227,23 +1365,17 @@ nv50_crtc_commit(struct drm_crtc *crtc)
        push = evo_wait(mast, 32);
        if (push) {
                if (nv50_vers(mast) < G82_DISP_CORE_CHANNEL_DMA) {
-                       evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
-                       evo_data(push, nv_crtc->fb.handle);
                        evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 2);
                        evo_data(push, 0xc0000000);
                        evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8);
                } else
                if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
-                       evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1);
-                       evo_data(push, nv_crtc->fb.handle);
                        evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 2);
                        evo_data(push, 0xc0000000);
                        evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8);
                        evo_mthd(push, 0x085c + (nv_crtc->index * 0x400), 1);
                        evo_data(push, mast->base.vram.handle);
                } else {
-                       evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1);
-                       evo_data(push, nv_crtc->fb.handle);
                        evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 4);
                        evo_data(push, 0x83000000);
                        evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8);
@@ -1251,8 +1383,6 @@ nv50_crtc_commit(struct drm_crtc *crtc)
                        evo_data(push, 0x00000000);
                        evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1);
                        evo_data(push, mast->base.vram.handle);
-                       evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1);
-                       evo_data(push, 0xffffff00);
                }
 
                evo_kick(push, mast);