From 46868d4272d0744ae626546d05f0a924e7704b1a Mon Sep 17 00:00:00 2001 From: Roy Spliet Date: Wed, 30 Sep 2015 00:23:51 +0100 Subject: 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 Tested-by: Pierre Moreau Signed-off-by: Ben Skeggs --- drm/nouveau/include/nvkm/subdev/bus.h | 1 + drm/nouveau/nvkm/subdev/bus/hwsq.c | 32 ++++++++++++++++++++++++++++++++ drm/nouveau/nvkm/subdev/bus/hwsq.h | 6 ++++++ drm/nouveau/nvkm/subdev/fb/ramnv50.c | 3 +-- drm/nouveau/nvkm/subdev/fb/ramseq.h | 1 + 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/drm/nouveau/include/nvkm/subdev/bus.h b/drm/nouveau/include/nvkm/subdev/bus.h index 6a04d9c07..33a057c33 100644 --- a/drm/nouveau/include/nvkm/subdev/bus.h +++ b/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 **); diff --git a/drm/nouveau/nvkm/subdev/bus/hwsq.c b/drm/nouveau/nvkm/subdev/bus/hwsq.c index 79f1cf513..2a5668938 100644 --- a/drm/nouveau/nvkm/subdev/bus/hwsq.c +++ b/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) { diff --git a/drm/nouveau/nvkm/subdev/bus/hwsq.h b/drm/nouveau/nvkm/subdev/bus/hwsq.h index 8117ec5a1..54ec3b131 100644 --- a/drm/nouveau/nvkm/subdev/bus/hwsq.h +++ b/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) { diff --git a/drm/nouveau/nvkm/subdev/fb/ramnv50.c b/drm/nouveau/nvkm/subdev/fb/ramnv50.c index d98d30699..87bde8ff2 100644 --- a/drm/nouveau/nvkm/subdev/fb/ramnv50.c +++ b/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); diff --git a/drm/nouveau/nvkm/subdev/fb/ramseq.h b/drm/nouveau/nvkm/subdev/fb/ramseq.h index 0f1f97ccd..8df7306d5 100644 --- a/drm/nouveau/nvkm/subdev/fb/ramseq.h +++ b/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 -- cgit v1.2.1