Skip to contentSkip to conversation

drm/nv50-nvd0: implement precise vblank timing support on nv50/nvc0.

RFC PATCH New Maarten Lankhorst 1 participant 1 message Aug 12, 2013

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

RFC PATCH 1/1 drm/nv50-nvd0: implement precise vblank timing support on nv50/nvc0.

Not as thoroughly tested as I would like. Newer nvd0 and kepler are unsupported, as I don't know the registers yet.

Information of the scanout position is based on Lucas Stach's original patch, with a teak to read vline twice, to prevent a race of hline with vline.

Cc Lucas Stach <[email protected]>
Cc Mario Kleiner <[email protected]>

---

drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +54 -0
index c168ae3..96268b7 100644
@@ -1285,6 +1285,57 @@ nv50_disp_intr(struct nouveau_subdev *subdev)
 	}
 }
 
+u32 nv50_disp_get_vblank_count(struct nouveau_disp *disp, int head)
+{
+	if (head < 0 || head >= 2)
+		return 0;
+
+	return nv_rd32(disp, 0x616340 + head * 0x800) >> 16;
+}
+
+int nv50_disp_get_scanoutpos(struct nouveau_disp *disp, int head, int *vpos, int *hpos)
+{
+	u32 reg, vbias, hbias, vbl_start, vbl_end, hline, vline;
+
+	if (head < 0 || head >= 2)
+		return -1;
+
+	reg = nv_rd32(disp, 0x610ae8 + head * 4);
+	vbias = reg >> 16;
+	hbias = reg & 0xffff;
+
+	vbl_start = nv_rd32(disp, 0x610af0 + head * 4) >> 16;
+	vbl_end = nv_rd32(disp, 0x610af8 + head * 4) >> 16;
+
+	reg = nv_rd32(disp, 0x616340 + head * 0x800) & 0xffff;
+	while (1) {
+		hline = nv_rd32(disp, 0x616344 + head * 0x800) & 0xffff;
+
+		vline = nv_rd32(disp, 0x616340 + head * 0x800) & 0xffff;
+		if (vline == reg)
+			break;
+
+		reg = vline;
+	}
+
+	if((vline >= vbl_start) || (vline < vbias)) {
+		/* we are in vblank so do a neg countdown */
+		vline -= vbias;
+		hline -= hbias;
+
+		if (vline > 0)
+			vline -= vbl_end;
+	} else {
+		/* apply corrective offset */
+		vline -= vbias;
+		hline -= hbias;
+	}
+
+	*vpos = vline;
+	*hpos = hline;
+	return 0;
+}
+
 static int
 nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 	       struct nouveau_oclass *oclass, void *data, u32 size,
@@ -1302,6 +1353,9 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 	nv_engine(priv)->sclass = nv50_disp_base_oclass;
 	nv_engine(priv)->cclass = &nv50_disp_cclass;
 	nv_subdev(priv)->intr = nv50_disp_intr;
+	priv->base.max_vblank_count = 0xffff;
+	priv->base.get_vblank_count = nv50_disp_get_vblank_count;
+	priv->base.get_scanoutpos = nv50_disp_get_scanoutpos;
 	INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
 	priv->sclass = nv50_disp_sclass;
 	priv->head.nr = 2;
drivers/gpu/drm/nouveau/core/engine/disp/nv50.h +3 -0
index 1ae6ceb..e3900ce 100644
@@ -144,4 +144,7 @@ extern struct nouveau_oclass nvd0_disp_cclass;
 void nvd0_disp_intr_supervisor(struct work_struct *);
 void nvd0_disp_intr(struct nouveau_subdev *);
 
+u32 nv50_disp_get_vblank_count(struct nouveau_disp *disp, int head);
+int nv50_disp_get_scanoutpos(struct nouveau_disp *disp, int head, int *vpos, int *hpos);
+
 #endif
drivers/gpu/drm/nouveau/core/engine/disp/nv84.c +3 -0
index d8c74c0..df357cf 100644
@@ -75,6 +75,9 @@ nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 	nv_engine(priv)->sclass = nv84_disp_base_oclass;
 	nv_engine(priv)->cclass = &nv50_disp_cclass;
 	nv_subdev(priv)->intr = nv50_disp_intr;
+	priv->base.max_vblank_count = 0xffff;
+	priv->base.get_vblank_count = nv50_disp_get_vblank_count;
+	priv->base.get_scanoutpos = nv50_disp_get_scanoutpos;
 	INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
 	priv->sclass = nv84_disp_sclass;
 	priv->head.nr = 2;
drivers/gpu/drm/nouveau/core/engine/disp/nv94.c +3 -0
index a66f949..38eafdf 100644
@@ -75,6 +75,9 @@ nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 	nv_engine(priv)->sclass = nv94_disp_base_oclass;
 	nv_engine(priv)->cclass = &nv50_disp_cclass;
 	nv_subdev(priv)->intr = nv50_disp_intr;
+	priv->base.max_vblank_count = 0xffff;
+	priv->base.get_vblank_count = nv50_disp_get_vblank_count;
+	priv->base.get_scanoutpos = nv50_disp_get_scanoutpos;
 	INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
 	priv->sclass = nv94_disp_sclass;
 	priv->head.nr = 2;
drivers/gpu/drm/nouveau/core/engine/disp/nva0.c +3 -0
index 6cf8eef..4f02b8d 100644
@@ -62,6 +62,9 @@ nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 	nv_engine(priv)->sclass = nva0_disp_base_oclass;
 	nv_engine(priv)->cclass = &nv50_disp_cclass;
 	nv_subdev(priv)->intr = nv50_disp_intr;
+	priv->base.max_vblank_count = 0xffff;
+	priv->base.get_vblank_count = nv50_disp_get_vblank_count;
+	priv->base.get_scanoutpos = nv50_disp_get_scanoutpos;
 	INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
 	priv->sclass = nva0_disp_sclass;
 	priv->head.nr = 2;
drivers/gpu/drm/nouveau/core/engine/disp/nva3.c +3 -0
index b754131..186f22e 100644
@@ -77,6 +77,9 @@ nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
 	nv_engine(priv)->cclass = &nv50_disp_cclass;
 	nv_subdev(priv)->intr = nv50_disp_intr;
 	INIT_WORK(&priv->supervisor, nv50_disp_intr_supervisor);
+	priv->base.max_vblank_count = 0xffff;
+	priv->base.get_vblank_count = nv50_disp_get_vblank_count;
+	priv->base.get_scanoutpos = nv50_disp_get_scanoutpos;
 	priv->sclass = nva3_disp_sclass;
 	priv->head.nr = 2;
 	priv->dac.nr = 3;
drivers/gpu/drm/nouveau/core/include/engine/disp.h +3 -0
index 4b21fab..8ba03db 100644
@@ -9,6 +9,9 @@
 struct nouveau_disp {
 	struct nouveau_engine base;
 	struct nouveau_event *vblank;
+	u32 (*get_vblank_count)(struct nouveau_disp *disp, int head);
+	int (*get_scanoutpos)(struct nouveau_disp *disp, int head, int *vpos, int *hpos);
+	u32 max_vblank_count;
 };
 
 static inline struct nouveau_disp *
drivers/gpu/drm/nouveau/nouveau_display.c +2 -2
index 2573604..edd8d07 100644
@@ -579,7 +579,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 
 	/* Emit a page flip */
 	if (nv_device(drm->device)->card_type >= NV_50) {
-		ret = nv50_display_flip_next(crtc, fb, chan, 0);
+		ret = nv50_display_flip_next(crtc, fb, chan, 1);
 		if (ret)
 			goto fail_unreserve;
 	}
@@ -634,7 +634,7 @@ nouveau_finish_page_flip(struct nouveau_channel *chan,
 
 	s = list_first_entry(&fctx->flip, struct nouveau_page_flip_state, head);
 	if (s->event)
-		drm_send_vblank_event(dev, -1, s->event);
+		drm_send_vblank_event(dev, s->crtc, s->event);
 
 	list_del(&s->head);
 	if (ps)
drivers/gpu/drm/nouveau/nouveau_drm.c +59 -1

72 lines not on this page · raw message

drivers/gpu/drm/nouveau/nv50_display.c +16 -1
index 738d7a2..df8e24a 100644
@@ -41,6 +41,7 @@
 #include <core/class.h>
 #include <engine/disp.h>
 
+#include <engine/disp.h>
 #include <subdev/timer.h>
 #include <subdev/bar.h>
 #include <subdev/fb.h>
@@ -1030,12 +1031,14 @@ nv50_crtc_commit(struct drm_crtc *crtc)
 
 	nv50_crtc_cursor_show_hide(nv_crtc, nv_crtc->cursor.visible, true);
 	nv50_display_flip_next(crtc, crtc->fb, NULL, 1);
+	drm_vblank_post_modeset(crtc->dev, nv_crtc->index);
 }
 
 static bool
 nv50_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode,
 		     struct drm_display_mode *adjusted_mode)
 {
+	drm_mode_set_crtcinfo(adjusted_mode, 0);
 	return true;
 }
 
@@ -1091,9 +1094,12 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
 		vactive = (vactive * 2) + 1;
 	}
 
+	drm_vblank_pre_modeset(crtc->dev, nv_crtc->index);
 	ret = nv50_crtc_swap_fbs(crtc, old_fb);
-	if (ret)
+	if (ret) {
+		drm_vblank_post_modeset(crtc->dev, nv_crtc->index);
 		return ret;
+	}
 
 	push = evo_wait(mast, 64);
 	if (push) {
@@ -2229,9 +2235,18 @@ nv50_display_create(struct drm_device *dev)
 	struct dcb_table *dcb = &drm->vbios.dcb;
 	struct drm_connector *connector, *tmp;
 	struct nv50_disp *disp;
+	struct nouveau_disp *dev_disp;
 	struct dcb_output *dcbe;
 	int crtcs, ret, i;
 
+	dev_disp = nouveau_disp(device);
+	if (!dev_disp) {
+		NV_ERROR(drm, "Cannot enable display engine without display support\n");
+		return -ENODEV;
+	}
+	if (dev_disp->max_vblank_count)
+		dev->max_vblank_count = dev_disp->max_vblank_count;
+
 	disp = kzalloc(sizeof(*disp), GFP_KERNEL);
 	if (!disp)
 		return -ENOMEM;