Browse Source

drm/nouveau/bus/hwsq: Implement VBLANK waiting heuristic

Avoids waiting for VBLANKS that never arrive on headless or otherwise
unconventional set-ups. Strategy taken from MEMX.

Signed-off-by: Roy Spliet <rspliet@eclipso.eu>
Tested-by: Pierre Moreau <pierre.morrow@free.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Roy Spliet 10 years ago
parent
commit
271c27665c

+ 1 - 0
drivers/gpu/drm/nouveau/include/nvkm/subdev/bus.h

@@ -14,6 +14,7 @@ int  nvkm_hwsq_fini(struct nvkm_hwsq **, bool exec);
 void nvkm_hwsq_wr32(struct nvkm_hwsq *, u32 addr, u32 data);
 void nvkm_hwsq_setf(struct nvkm_hwsq *, u8 flag, int data);
 void nvkm_hwsq_wait(struct nvkm_hwsq *, u8 flag, u8 data);
+void nvkm_hwsq_wait_vblank(struct nvkm_hwsq *);
 void nvkm_hwsq_nsec(struct nvkm_hwsq *, u32 nsec);
 
 int nv04_bus_new(struct nvkm_device *, int, struct nvkm_bus **);

+ 32 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.c

@@ -131,6 +131,38 @@ nvkm_hwsq_wait(struct nvkm_hwsq *hwsq, u8 flag, u8 data)
 	hwsq_cmd(hwsq, 3, (u8[]){ 0x5f, flag, data });
 }
 
+void
+nvkm_hwsq_wait_vblank(struct nvkm_hwsq *hwsq)
+{
+	struct nvkm_subdev *subdev = hwsq->subdev;
+	struct nvkm_device *device = subdev->device;
+	u32 heads, x, y, px = 0;
+	int i, head_sync;
+
+	heads = nvkm_rd32(device, 0x610050);
+	for (i = 0; i < 2; i++) {
+		/* Heuristic: sync to head with biggest resolution */
+		if (heads & (2 << (i << 3))) {
+			x = nvkm_rd32(device, 0x610b40 + (0x540 * i));
+			y = (x & 0xffff0000) >> 16;
+			x &= 0x0000ffff;
+			if ((x * y) > px) {
+				px = (x * y);
+				head_sync = i;
+			}
+		}
+	}
+
+	if (px == 0) {
+		nvkm_debug(subdev, "WAIT VBLANK !NO ACTIVE HEAD\n");
+		return;
+	}
+
+	nvkm_debug(subdev, "WAIT VBLANK HEAD%d\n", head_sync);
+	nvkm_hwsq_wait(hwsq, head_sync ? 0x3 : 0x1, 0x0);
+	nvkm_hwsq_wait(hwsq, head_sync ? 0x3 : 0x1, 0x1);
+}
+
 void
 nvkm_hwsq_nsec(struct nvkm_hwsq *hwsq, u32 nsec)
 {

+ 6 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/bus/hwsq.h

@@ -133,6 +133,12 @@ hwsq_wait(struct hwsq *ram, u8 flag, u8 data)
 	nvkm_hwsq_wait(ram->hwsq, flag, data);
 }
 
+static inline void
+hwsq_wait_vblank(struct hwsq *ram)
+{
+	nvkm_hwsq_wait_vblank(ram->hwsq);
+}
+
 static inline void
 hwsq_nsec(struct hwsq *ram, u32 nsec)
 {

+ 1 - 2
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramnv50.c

@@ -308,8 +308,7 @@ nv50_ram_calc(struct nvkm_ram *base, u32 freq)
 	/* Always disable this bit during reclock */
 	ram_mask(hwsq, 0x100200, 0x00000800, 0x00000000);
 
-	ram_wait(hwsq, 0x01, 0x00); /* wait for !vblank */
-	ram_wait(hwsq, 0x01, 0x01); /* wait for vblank */
+	ram_wait_vblank(hwsq);
 	ram_wr32(hwsq, 0x611200, 0x00003300);
 	ram_wr32(hwsq, 0x002504, 0x00000001); /* block fifo */
 	ram_nsec(hwsq, 8000);

+ 1 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramseq.h

@@ -11,5 +11,6 @@
 #define ram_mask(s,r,m,d)   hwsq_mask(&(s)->base, &(s)->r_##r, (m), (d))
 #define ram_setf(s,f,d)     hwsq_setf(&(s)->base, (f), (d))
 #define ram_wait(s,f,d)     hwsq_wait(&(s)->base, (f), (d))
+#define ram_wait_vblank(s)  hwsq_wait_vblank(&(s)->base)
 #define ram_nsec(s,n)       hwsq_nsec(&(s)->base, (n))
 #endif