Commit 9f9bdaaf authored by Ben Skeggs's avatar Ben Skeggs

drm/nv50-: prevent some races between modesetting and page flipping

nexuiz-glx + gnome-shell is able to trigger this a lot of the time.
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 42bed34c
...@@ -56,8 +56,8 @@ ...@@ -56,8 +56,8 @@
/* offsets in shared sync bo of various structures */ /* offsets in shared sync bo of various structures */
#define EVO_SYNC(c, o) ((c) * 0x0100 + (o)) #define EVO_SYNC(c, o) ((c) * 0x0100 + (o))
#define EVO_MAST_NTFY EVO_SYNC( 0, 0x00) #define EVO_MAST_NTFY EVO_SYNC( 0, 0x00)
#define EVO_FLIP_SEM0(c) EVO_SYNC((c), 0x00) #define EVO_FLIP_SEM0(c) EVO_SYNC((c) + 1, 0x00)
#define EVO_FLIP_SEM1(c) EVO_SYNC((c), 0x10) #define EVO_FLIP_SEM1(c) EVO_SYNC((c) + 1, 0x10)
#define EVO_CORE_HANDLE (0xd1500000) #define EVO_CORE_HANDLE (0xd1500000)
#define EVO_CHAN_HANDLE(t,i) (0xd15c0000 | (((t) & 0x00ff) << 8) | (i)) #define EVO_CHAN_HANDLE(t,i) (0xd15c0000 | (((t) & 0x00ff) << 8) | (i))
...@@ -341,10 +341,8 @@ struct nv50_curs { ...@@ -341,10 +341,8 @@ struct nv50_curs {
struct nv50_sync { struct nv50_sync {
struct nv50_dmac base; struct nv50_dmac base;
struct { u32 addr;
u32 offset; u32 data;
u16 value;
} sem;
}; };
struct nv50_ovly { struct nv50_ovly {
...@@ -471,13 +469,33 @@ nv50_display_crtc_sema(struct drm_device *dev, int crtc) ...@@ -471,13 +469,33 @@ nv50_display_crtc_sema(struct drm_device *dev, int crtc)
return nv50_disp(dev)->sync; return nv50_disp(dev)->sync;
} }
struct nv50_display_flip {
struct nv50_disp *disp;
struct nv50_sync *chan;
};
static bool
nv50_display_flip_wait(void *data)
{
struct nv50_display_flip *flip = data;
if (nouveau_bo_rd32(flip->disp->sync, flip->chan->addr / 4) ==
flip->chan->data);
return true;
usleep_range(1, 2);
return false;
}
void void
nv50_display_flip_stop(struct drm_crtc *crtc) nv50_display_flip_stop(struct drm_crtc *crtc)
{ {
struct nv50_sync *sync = nv50_sync(crtc); struct nouveau_device *device = nouveau_dev(crtc->dev);
struct nv50_display_flip flip = {
.disp = nv50_disp(crtc->dev),
.chan = nv50_sync(crtc),
};
u32 *push; u32 *push;
push = evo_wait(sync, 8); push = evo_wait(flip.chan, 8);
if (push) { if (push) {
evo_mthd(push, 0x0084, 1); evo_mthd(push, 0x0084, 1);
evo_data(push, 0x00000000); evo_data(push, 0x00000000);
...@@ -487,8 +505,10 @@ nv50_display_flip_stop(struct drm_crtc *crtc) ...@@ -487,8 +505,10 @@ nv50_display_flip_stop(struct drm_crtc *crtc)
evo_data(push, 0x00000000); evo_data(push, 0x00000000);
evo_mthd(push, 0x0080, 1); evo_mthd(push, 0x0080, 1);
evo_data(push, 0x00000000); evo_data(push, 0x00000000);
evo_kick(push, sync); evo_kick(push, flip.chan);
} }
nv_wait_cb(device, nv50_display_flip_wait, &flip);
} }
int int
...@@ -496,11 +516,10 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, ...@@ -496,11 +516,10 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
struct nouveau_channel *chan, u32 swap_interval) struct nouveau_channel *chan, u32 swap_interval)
{ {
struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb);
struct nv50_disp *disp = nv50_disp(crtc->dev);
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct nv50_sync *sync = nv50_sync(crtc); struct nv50_sync *sync = nv50_sync(crtc);
int head = nv_crtc->index, ret;
u32 *push; u32 *push;
int ret;
swap_interval <<= 4; swap_interval <<= 4;
if (swap_interval == 0) if (swap_interval == 0)
...@@ -510,26 +529,22 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, ...@@ -510,26 +529,22 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
if (unlikely(push == NULL)) if (unlikely(push == NULL))
return -EBUSY; return -EBUSY;
/* synchronise with the rendering channel, if necessary */ if (chan && nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) {
if (likely(chan)) {
if (nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) {
ret = RING_SPACE(chan, 8); ret = RING_SPACE(chan, 8);
if (ret) if (ret)
return ret; return ret;
BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2); BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2);
OUT_RING (chan, NvEvoSema0 + nv_crtc->index); OUT_RING (chan, NvEvoSema0 + head);
OUT_RING (chan, sync->sem.offset); OUT_RING (chan, sync->addr ^ 0x10);
BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1); BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1);
OUT_RING (chan, 0xf00d0000 | sync->sem.value); OUT_RING (chan, sync->data + 1);
BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_OFFSET, 2); BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_OFFSET, 2);
OUT_RING (chan, sync->sem.offset ^ 0x10); OUT_RING (chan, sync->addr);
OUT_RING (chan, 0x74b1e000); OUT_RING (chan, sync->data);
} else } else
if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) { if (chan && nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) {
u64 offset = nv84_fence_crtc(chan, nv_crtc->index); u64 addr = nv84_fence_crtc(chan, head) + sync->addr;
offset += sync->sem.offset;
ret = RING_SPACE(chan, 12); ret = RING_SPACE(chan, 12);
if (ret) if (ret)
return ret; return ret;
...@@ -537,39 +552,41 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, ...@@ -537,39 +552,41 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
OUT_RING (chan, chan->vram); OUT_RING (chan, chan->vram);
BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, upper_32_bits(addr ^ 0x10));
OUT_RING (chan, lower_32_bits(offset)); OUT_RING (chan, lower_32_bits(addr ^ 0x10));
OUT_RING (chan, 0xf00d0000 | sync->sem.value); OUT_RING (chan, sync->data + 1);
OUT_RING (chan, 0x00000002); OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG);
BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, upper_32_bits(addr));
OUT_RING (chan, lower_32_bits(offset ^ 0x10)); OUT_RING (chan, lower_32_bits(addr));
OUT_RING (chan, 0x74b1e000); OUT_RING (chan, sync->data);
OUT_RING (chan, 0x00000001); OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL);
} else { } else
u64 offset = nv84_fence_crtc(chan, nv_crtc->index); if (chan) {
offset += sync->sem.offset; u64 addr = nv84_fence_crtc(chan, head) + sync->addr;
ret = RING_SPACE(chan, 10); ret = RING_SPACE(chan, 10);
if (ret) if (ret)
return ret; return ret;
BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, upper_32_bits(addr ^ 0x10));
OUT_RING (chan, lower_32_bits(offset)); OUT_RING (chan, lower_32_bits(addr ^ 0x10));
OUT_RING (chan, 0xf00d0000 | sync->sem.value); OUT_RING (chan, sync->data + 1);
OUT_RING (chan, 0x00001002); OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG |
NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD);
BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, upper_32_bits(addr));
OUT_RING (chan, lower_32_bits(offset ^ 0x10)); OUT_RING (chan, lower_32_bits(addr));
OUT_RING (chan, 0x74b1e000); OUT_RING (chan, sync->data);
OUT_RING (chan, 0x00001001); OUT_RING (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL |
NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD);
} }
if (chan) {
sync->addr ^= 0x10;
sync->data++;
FIRE_RING (chan); FIRE_RING (chan);
} else { } else {
nouveau_bo_wr32(disp->sync, sync->sem.offset / 4,
0xf00d0000 | sync->sem.value);
evo_sync(crtc->dev); evo_sync(crtc->dev);
} }
...@@ -583,9 +600,9 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, ...@@ -583,9 +600,9 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
evo_data(push, 0x40000000); evo_data(push, 0x40000000);
} }
evo_mthd(push, 0x0088, 4); evo_mthd(push, 0x0088, 4);
evo_data(push, sync->sem.offset); evo_data(push, sync->addr);
evo_data(push, 0xf00d0000 | sync->sem.value); evo_data(push, sync->data++);
evo_data(push, 0x74b1e000); evo_data(push, sync->data);
evo_data(push, NvEvoSync); evo_data(push, NvEvoSync);
evo_mthd(push, 0x00a0, 2); evo_mthd(push, 0x00a0, 2);
evo_data(push, 0x00000000); evo_data(push, 0x00000000);
...@@ -613,9 +630,6 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, ...@@ -613,9 +630,6 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
evo_mthd(push, 0x0080, 1); evo_mthd(push, 0x0080, 1);
evo_data(push, 0x00000000); evo_data(push, 0x00000000);
evo_kick(push, sync); evo_kick(push, sync);
sync->sem.offset ^= 0x10;
sync->sem.value++;
return 0; return 0;
} }
...@@ -1387,7 +1401,8 @@ nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index) ...@@ -1387,7 +1401,8 @@ nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index)
if (ret) if (ret)
goto out; goto out;
head->sync.sem.offset = EVO_SYNC(1 + index, 0x00); head->sync.addr = EVO_FLIP_SEM0(index);
head->sync.data = 0x00000000;
/* allocate overlay resources */ /* allocate overlay resources */
ret = nv50_pioc_create(disp->core, NV50_DISP_OIMM_CLASS, index, ret = nv50_pioc_create(disp->core, NV50_DISP_OIMM_CLASS, index,
...@@ -2120,15 +2135,23 @@ nv50_display_fini(struct drm_device *dev) ...@@ -2120,15 +2135,23 @@ nv50_display_fini(struct drm_device *dev)
int int
nv50_display_init(struct drm_device *dev) nv50_display_init(struct drm_device *dev)
{ {
u32 *push = evo_wait(nv50_mast(dev), 32); struct nv50_disp *disp = nv50_disp(dev);
if (push) { struct drm_crtc *crtc;
u32 *push;
push = evo_wait(nv50_mast(dev), 32);
if (!push)
return -EBUSY;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nv50_sync *sync = nv50_sync(crtc);
nouveau_bo_wr32(disp->sync, sync->addr / 4, sync->data);
}
evo_mthd(push, 0x0088, 1); evo_mthd(push, 0x0088, 1);
evo_data(push, NvEvoSync); evo_data(push, NvEvoSync);
evo_kick(push, nv50_mast(dev)); evo_kick(push, nv50_mast(dev));
return 0; return 0;
}
return -EBUSY;
} }
void void
......
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