Commit 60f60bf1 authored by Ben Skeggs's avatar Ben Skeggs

drm/nv50-nvc0: request and wait on notification of modeset completion

This should prevent a number of races from occuring, the most obvious of
which will be exposed when we start making use of the "display sync" evo
channel for page flipping.  The DS channel will reject any command stream
that doesn't completely agree with the current "master" state.
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent f13e435c
...@@ -78,7 +78,8 @@ enum { ...@@ -78,7 +78,8 @@ enum {
NvEvoVRAM = 0x01000000, NvEvoVRAM = 0x01000000,
NvEvoFB16 = 0x01000001, NvEvoFB16 = 0x01000001,
NvEvoFB32 = 0x01000002, NvEvoFB32 = 0x01000002,
NvEvoVRAM_LP = 0x01000003 NvEvoVRAM_LP = 0x01000003,
NvEvoSync = 0xcafe0000
}; };
#define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039 #define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039
......
...@@ -443,6 +443,42 @@ nv50_crtc_dpms(struct drm_crtc *crtc, int mode) ...@@ -443,6 +443,42 @@ nv50_crtc_dpms(struct drm_crtc *crtc, int mode)
{ {
} }
static int
nv50_crtc_wait_complete(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
struct nv50_display *disp = nv50_display(dev);
struct nouveau_channel *evo = disp->master;
u64 start;
int ret;
ret = RING_SPACE(evo, 6);
if (ret)
return ret;
BEGIN_RING(evo, 0, 0x0084, 1);
OUT_RING (evo, 0x80000000);
BEGIN_RING(evo, 0, 0x0080, 1);
OUT_RING (evo, 0);
BEGIN_RING(evo, 0, 0x0084, 1);
OUT_RING (evo, 0x00000000);
nv_wo32(disp->ntfy, 0x000, 0x00000000);
FIRE_RING (evo);
start = ptimer->read(dev);
do {
nv_wr32(dev, 0x61002c, 0x370);
nv_wr32(dev, 0x000140, 1);
if (nv_ro32(disp->ntfy, 0x000))
return 0;
} while (ptimer->read(dev) - start < 2000000000ULL);
return -EBUSY;
}
static void static void
nv50_crtc_prepare(struct drm_crtc *crtc) nv50_crtc_prepare(struct drm_crtc *crtc)
{ {
...@@ -459,23 +495,13 @@ static void ...@@ -459,23 +495,13 @@ static void
nv50_crtc_commit(struct drm_crtc *crtc) nv50_crtc_commit(struct drm_crtc *crtc)
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct nouveau_channel *evo = nv50_display(dev)->master;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
int ret;
NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index); NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
nv50_crtc_blank(nv_crtc, false); nv50_crtc_blank(nv_crtc, false);
drm_vblank_post_modeset(dev, nv_crtc->index); drm_vblank_post_modeset(dev, nv_crtc->index);
nv50_crtc_wait_complete(crtc);
ret = RING_SPACE(evo, 2);
if (ret) {
NV_ERROR(dev, "no space while committing crtc\n");
return;
}
BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
OUT_RING (evo, 0);
FIRE_RING (evo);
} }
static bool static bool
...@@ -488,7 +514,7 @@ nv50_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, ...@@ -488,7 +514,7 @@ nv50_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
static int static int
nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
struct drm_framebuffer *passed_fb, struct drm_framebuffer *passed_fb,
int x, int y, bool update, bool atomic) int x, int y, bool atomic)
{ {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct drm_device *dev = nv_crtc->base.dev; struct drm_device *dev = nv_crtc->base.dev;
...@@ -598,15 +624,6 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, ...@@ -598,15 +624,6 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
nv50_crtc_lut_load(crtc); nv50_crtc_lut_load(crtc);
} }
if (update) {
ret = RING_SPACE(evo, 2);
if (ret)
return ret;
BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
OUT_RING(evo, 0);
FIRE_RING(evo);
}
return 0; return 0;
} }
...@@ -696,14 +713,20 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, ...@@ -696,14 +713,20 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false); nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false);
nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false); nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false);
return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false, false); return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
} }
static int static int
nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb) struct drm_framebuffer *old_fb)
{ {
return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, true, false); int ret;
ret = nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
if (ret)
return ret;
return nv50_crtc_wait_complete(crtc);
} }
static int static int
...@@ -711,7 +734,13 @@ nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc, ...@@ -711,7 +734,13 @@ nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
struct drm_framebuffer *fb, struct drm_framebuffer *fb,
int x, int y, enum mode_set_atomic state) int x, int y, enum mode_set_atomic state)
{ {
return nv50_crtc_do_mode_set_base(crtc, fb, x, y, true, true); int ret;
ret = nv50_crtc_do_mode_set_base(crtc, fb, x, y, true);
if (ret)
return ret;
return nv50_crtc_wait_complete(crtc);
} }
static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = { static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = {
......
...@@ -183,7 +183,7 @@ nv50_display_init(struct drm_device *dev) ...@@ -183,7 +183,7 @@ nv50_display_init(struct drm_device *dev)
return ret; return ret;
BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2); BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2);
OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED); OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED);
OUT_RING(evo, NV50_EVO_DMA_NOTIFY_HANDLE_NONE); OUT_RING(evo, NvEvoSync);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1); BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1);
OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE); OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1); BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1);
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
struct nv50_display { struct nv50_display {
struct nouveau_channel *master; struct nouveau_channel *master;
struct nouveau_gpuobj *ntfy;
struct tasklet_struct tasklet; struct tasklet_struct tasklet;
struct { struct {
......
...@@ -203,6 +203,7 @@ nv50_evo_destroy(struct drm_device *dev) ...@@ -203,6 +203,7 @@ nv50_evo_destroy(struct drm_device *dev)
{ {
struct nv50_display *disp = nv50_display(dev); struct nv50_display *disp = nv50_display(dev);
nouveau_gpuobj_ref(NULL, &disp->ntfy);
nv50_evo_channel_del(&disp->master); nv50_evo_channel_del(&disp->master);
} }
...@@ -251,6 +252,25 @@ nv50_evo_create(struct drm_device *dev) ...@@ -251,6 +252,25 @@ nv50_evo_create(struct drm_device *dev)
if (ret) if (ret)
goto err; goto err;
/* not sure exactly what this is..
*
* the first dword of the structure is used by nvidia to wait on
* full completion of an EVO "update" command.
*
* method 0x8c on the master evo channel will fill a lot more of
* this structure with some undefined info
*/
ret = nouveau_gpuobj_new(dev, disp->master, 0x1000, 0,
NVOBJ_FLAG_ZERO_ALLOC, &disp->ntfy);
if (ret)
goto err;
ret = nv50_evo_dmaobj_new(disp->master, 0x3d, NvEvoSync, 0, 0x19,
disp->ntfy->vinst, disp->ntfy->vinst +
disp->ntfy->size, 0x00010000);
if (ret)
goto err;
/* create some default objects for the scanout memtypes we support */ /* create some default objects for the scanout memtypes we support */
if (dev_priv->card_type >= NV_C0) { if (dev_priv->card_type >= NV_C0) {
ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB32, 0xfe, 0x19, ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB32, 0xfe, 0x19,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment