Blob Blame History Raw
From d52e948c67b263d0ceb71d734673ff8b1d4b10ce Mon Sep 17 00:00:00 2001
From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 19 May 2017 23:59:35 +1000
Subject: [PATCH] drm/nouveau/disp/nv50-: implement a common supervisor 2.0
Git-commit: d52e948c67b263d0ceb71d734673ff8b1d4b10ce
Patch-mainline: v4.13-rc1
References: bsc#1095094

This makes use of all the additional routing and state added in previous
commits, making it possible to deal with GM20x macro link routing, while
also sharing code between the NV50 and GF119 implementations.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Acked-by: Takashi Iwai <tiwai@suse.de>

---
 drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c    |   18 +++
 drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c |   66 --------------
 drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c  |  108 ++++-------------------
 drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h  |    1 
 drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h  |    1 
 5 files changed, 40 insertions(+), 154 deletions(-)

--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
@@ -411,6 +411,23 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 da
 	return ret;
 }
 
+static void
+nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior)
+{
+	struct nvkm_dp *dp = nvkm_dp(outp);
+
+	/* Prevent link from being retrained if sink sends an IRQ. */
+	atomic_set(&dp->lt.done, 0);
+	ior->dp.nr = 0;
+
+	/* Execute DisableLT script from DP Info Table. */
+	nvbios_init(&ior->disp->engine.subdev, dp->info.script[4],
+		init.outp = &dp->outp.info;
+		init.or   = ior->id;
+		init.link = ior->arm.link;
+	);
+}
+
 int
 nvkm_output_dp_train(struct nvkm_outp *outp, u32 unused)
 {
@@ -557,6 +574,7 @@ nvkm_dp_func = {
 	.dtor = nvkm_dp_dtor,
 	.init = nvkm_dp_init,
 	.fini = nvkm_dp_fini,
+	.release = nvkm_dp_release,
 };
 
 static int
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
@@ -79,44 +79,6 @@ exec_lookup(struct nv50_disp *disp, int
 }
 
 static struct nvkm_output *
-exec_script(struct nv50_disp *disp, int head, int id)
-{
-	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-	struct nvkm_device *device = subdev->device;
-	struct nvkm_bios *bios = device->bios;
-	struct nvkm_output *outp;
-	struct nvbios_outp info;
-	u8  ver, hdr, cnt, len;
-	u32 data, ctrl = 0;
-	int or;
-
-	for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
-		ctrl = nvkm_rd32(device, 0x640180 + (or * 0x20));
-		if (ctrl & (1 << head))
-			break;
-	}
-
-	if (or == 8)
-		return NULL;
-
-	outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
-	if (outp) {
-		struct nvbios_init init = {
-			.subdev = subdev,
-			.bios = bios,
-			.offset = info.script[id],
-			.outp = &outp->info,
-			.crtc = head,
-			.execute = 1,
-		};
-
-		nvbios_exec(&init);
-	}
-
-	return outp;
-}
-
-static struct nvkm_output *
 exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
 {
 	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
@@ -177,31 +139,6 @@ exec_clkcmp(struct nv50_disp *disp, int
 }
 
 static void
-gf119_disp_intr_unk2_0(struct nv50_disp *disp, int head)
-{
-	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-	struct nvkm_output *outp = exec_script(disp, head, 2);
-
-	/* see note in nv50_disp_intr_unk20_0() */
-	if (outp && outp->info.type == DCB_OUTPUT_DP) {
-		struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
-		if (!outpdp->lt.mst) {
-			struct nvbios_init init = {
-				.subdev = subdev,
-				.bios = subdev->device->bios,
-				.outp = &outp->info,
-				.crtc = head,
-				.offset = outpdp->info.script[4],
-				.execute = 1,
-			};
-
-			atomic_set(&outpdp->lt.done, 0);
-			nvbios_exec(&init);
-		}
-	}
-}
-
-static void
 gf119_disp_intr_unk2_1(struct nv50_disp *disp, int head)
 {
 	struct nvkm_device *device = disp->base.engine.subdev.device;
@@ -364,8 +301,7 @@ gf119_disp_super(struct work_struct *wor
 		list_for_each_entry(head, &disp->base.head, head) {
 			if (!(mask[head->id] & 0x00001000))
 				continue;
-			nvkm_debug(subdev, "supervisor 2.0 - head %d\n", head->id);
-			gf119_disp_intr_unk2_0(disp, head->id);
+			nv50_disp_super_2_0(disp, head);
 		}
 		nvkm_outp_route(&disp->base);
 		list_for_each_entry(head, &disp->base.head, head) {
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
@@ -226,65 +226,6 @@ exec_lookup(struct nv50_disp *disp, int
 }
 
 static struct nvkm_output *
-exec_script(struct nv50_disp *disp, int head, int id)
-{
-	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-	struct nvkm_device *device = subdev->device;
-	struct nvkm_bios *bios = device->bios;
-	struct nvkm_output *outp;
-	struct nvbios_outp info;
-	u8  ver, hdr, cnt, len;
-	u32 data, ctrl = 0;
-	u32 reg;
-	int i;
-
-	/* DAC */
-	for (i = 0; !(ctrl & (1 << head)) && i < disp->func->dac.nr; i++)
-		ctrl = nvkm_rd32(device, 0x610b5c + (i * 8));
-
-	/* SOR */
-	if (!(ctrl & (1 << head))) {
-		if (device->chipset  < 0x90 ||
-		    device->chipset == 0x92 ||
-		    device->chipset == 0xa0) {
-			reg = 0x610b74;
-		} else {
-			reg = 0x610798;
-		}
-		for (i = 0; !(ctrl & (1 << head)) && i < disp->func->sor.nr; i++)
-			ctrl = nvkm_rd32(device, reg + (i * 8));
-		i += 4;
-	}
-
-	/* PIOR */
-	if (!(ctrl & (1 << head))) {
-		for (i = 0; !(ctrl & (1 << head)) && i < disp->func->pior.nr; i++)
-			ctrl = nvkm_rd32(device, 0x610b84 + (i * 8));
-		i += 8;
-	}
-
-	if (!(ctrl & (1 << head)))
-		return NULL;
-	i--;
-
-	outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
-	if (outp) {
-		struct nvbios_init init = {
-			.subdev = subdev,
-			.bios = bios,
-			.offset = info.script[id],
-			.outp = &outp->info,
-			.crtc = head,
-			.execute = 1,
-		};
-
-		nvbios_exec(&init);
-	}
-
-	return outp;
-}
-
-static struct nvkm_output *
 exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
 {
 	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
@@ -601,38 +542,27 @@ nv50_disp_intr_unk20_1(struct nv50_disp
 		nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head, pclk);
 }
 
-static void
-nv50_disp_intr_unk20_0(struct nv50_disp *disp, int head)
+void
+nv50_disp_super_2_0(struct nv50_disp *disp, struct nvkm_head *head)
 {
-	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-	struct nvkm_output *outp = exec_script(disp, head, 2);
+	struct nvkm_outp *outp;
+	struct nvkm_ior *ior;
 
-	/* the binary driver does this outside of the supervisor handling
-	 * (after the third supervisor from a detach).  we (currently?)
-	 * allow both detach/attach to happen in the same set of
-	 * supervisor interrupts, so it would make sense to execute this
-	 * (full power down?) script after all the detach phases of the
-	 * supervisor handling.  like with training if needed from the
-	 * second supervisor, nvidia doesn't do this, so who knows if it's
-	 * entirely safe, but it does appear to work..
-	 *
-	 * without this script being run, on some configurations i've
-	 * seen, switching from DP to TMDS on a DP connector may result
-	 * in a blank screen (SOR_PWR off/on can restore it)
-	 */
-	if (outp && outp->info.type == DCB_OUTPUT_DP) {
-		struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
-		struct nvbios_init init = {
-			.subdev = subdev,
-			.bios = subdev->device->bios,
-			.outp = &outp->info,
-			.crtc = head,
-			.offset = outpdp->info.script[4],
-			.execute = 1,
-		};
+	/* Determine which OR, if any, we're detaching from the head. */
+	HEAD_DBG(head, "supervisor 2.0");
+	ior = nv50_disp_super_ior_arm(head);
+	if (!ior)
+		return;
+
+	/* Execute OffInt2 IED script. */
+	nv50_disp_super_ied_off(head, ior, 2);
 
-		atomic_set(&outpdp->lt.done, 0);
-		nvbios_exec(&init);
+	/* If we're shutting down the OR's only active head, execute
+	 * the output path's release function.
+	 */
+	if (ior->arm.head == (1 << head->id)) {
+		if ((outp = ior->arm.outp) && outp->func->release)
+			outp->func->release(outp, ior);
 	}
 }
 
@@ -695,7 +625,7 @@ nv50_disp_super(struct work_struct *work
 		list_for_each_entry(head, &disp->base.head, head) {
 			if (!(super & (0x00000080 << head->id)))
 				continue;
-			nv50_disp_intr_unk20_0(disp, head->id);
+			nv50_disp_super_2_0(disp, head);
 		}
 		nvkm_outp_route(&disp->base);
 		list_for_each_entry(head, &disp->base.head, head) {
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
@@ -28,6 +28,7 @@ struct nv50_disp {
 
 void nv50_disp_super_1(struct nv50_disp *);
 void nv50_disp_super_1_0(struct nv50_disp *, struct nvkm_head *);
+void nv50_disp_super_2_0(struct nv50_disp *, struct nvkm_head *);
 
 int nv50_disp_new_(const struct nv50_disp_func *, struct nvkm_device *,
 		   int index, int heads, struct nvkm_disp **);
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
@@ -39,6 +39,7 @@ struct nvkm_outp_func {
 	void *(*dtor)(struct nvkm_outp *);
 	void (*init)(struct nvkm_outp *);
 	void (*fini)(struct nvkm_outp *);
+	void (*release)(struct nvkm_outp *, struct nvkm_ior *);
 };
 
 #define nvkm_output nvkm_outp