Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+106 −83
Original line number Original line Diff line number Diff line
@@ -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 {


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)
	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)
		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,
		       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,
	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,
		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,
		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,
	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)
	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,17 +2135,25 @@ 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
nv50_display_destroy(struct drm_device *dev)
nv50_display_destroy(struct drm_device *dev)
{
{