drm/nv50-nvc0: request and wait on notification of modeset completion
authorBen Skeggs <bskeggs@redhat.com>
Thu, 3 Feb 2011 05:46:14 +0000 (15:46 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Thu, 24 Feb 2011 20:45:02 +0000 (06:45 +1000)
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: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nouveau_dma.h
drivers/gpu/drm/nouveau/nv50_crtc.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_display.h
drivers/gpu/drm/nouveau/nv50_evo.c

index c36f1763feaace442608126fff9c15ff49d148eb..6c9501b3226b8fff6610b56d1fd0d32b7e510a00 100644 (file)
@@ -78,7 +78,8 @@ enum {
        NvEvoVRAM       = 0x01000000,
        NvEvoFB16       = 0x01000001,
        NvEvoFB32       = 0x01000002,
-       NvEvoVRAM_LP    = 0x01000003
+       NvEvoVRAM_LP    = 0x01000003,
+       NvEvoSync       = 0xcafe0000
 };
 
 #define NV_MEMORY_TO_MEMORY_FORMAT                                    0x00000039
index bc5fa36677c17450d7462c811d0d519474e08ae7..a94aff57cc06ae423811ccedb047fba4a8b6105b 100644 (file)
@@ -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
 nv50_crtc_prepare(struct drm_crtc *crtc)
 {
@@ -459,23 +495,13 @@ static void
 nv50_crtc_commit(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
-       struct nouveau_channel *evo = nv50_display(dev)->master;
        struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
-       int ret;
 
        NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
 
        nv50_crtc_blank(nv_crtc, false);
        drm_vblank_post_modeset(dev, nv_crtc->index);
-
-       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);
+       nv50_crtc_wait_complete(crtc);
 }
 
 static bool
@@ -488,7 +514,7 @@ nv50_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
 static int
 nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
                           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 drm_device *dev = nv_crtc->base.dev;
@@ -598,15 +624,6 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *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;
 }
 
@@ -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_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
 nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
                        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
@@ -711,7 +734,13 @@ nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
                               struct drm_framebuffer *fb,
                               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 = {
index e295a17d68f4be1b122ab9c59049cdb45362c28f..09d7994ea099b23874204987e7061bd1f111ae6b 100644 (file)
@@ -183,7 +183,7 @@ nv50_display_init(struct drm_device *dev)
                return ret;
        BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2);
        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);
        OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
        BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1);
index ea37f230aee8609655b15a5ddca3fa82e1b3cb25..a51b8853a924a24341cb4deb1d2cd06edacd0c8f 100644 (file)
@@ -37,6 +37,7 @@
 
 struct nv50_display {
        struct nouveau_channel *master;
+       struct nouveau_gpuobj *ntfy;
 
        struct tasklet_struct tasklet;
        struct {
index 9703f759b717ce8fcca7b014172c95354a8ee4f3..eea96205fca26772f4451636161b1a052eea2f06 100644 (file)
@@ -203,6 +203,7 @@ nv50_evo_destroy(struct drm_device *dev)
 {
        struct nv50_display *disp = nv50_display(dev);
 
+       nouveau_gpuobj_ref(NULL, &disp->ntfy);
        nv50_evo_channel_del(&disp->master);
 }
 
@@ -251,6 +252,25 @@ nv50_evo_create(struct drm_device *dev)
        if (ret)
                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 */
        if (dev_priv->card_type >= NV_C0) {
                ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB32, 0xfe, 0x19,