From ad63361953938caaf85630ce0cb1bfa976938552 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 4 Nov 2016 17:20:36 +1000 Subject: [PATCH] drm/nouveau/kms/nv50: separate out core surface commit 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 --- drivers/gpu/drm/nouveau/nv50_display.c | 220 ++++++++++++++++++++----- 1 file changed, 175 insertions(+), 45 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 8d27ddd75b37..8203a3873797 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -25,10 +25,11 @@ #include #include +#include #include -#include #include #include +#include #include #include @@ -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); -- 2.30.2