drm/nv50-/disp: move DP link training to core and train from supervisor

We need to be able to do link training for PIOR-connected ANX9805 from
the third supervisor handler (due to script ordering in the bios, can't
have the "user" call train because some settings are overwritten from
the modesetting bios scripts).

This moves link training for SOR-connected DP encoders to the second
supervisor interrupt, *before* we call the modesetting scripts (yes,
different ordering from PIOR is necessary).  This is useful since we
should now be able to remove some hacks to workaround races between
the supervisor and link training paths.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
index d22f656..a4e2112 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
@@ -911,7 +911,7 @@
 	}
 
 	data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
-	if (data) {
+	if (data && id < 0xff) {
 		data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
 		if (data) {
 			struct nvbios_init init = {
@@ -1078,8 +1078,28 @@
 	head = ffs((super & 0x00000180) >> 7) - 1;
 	if (head >= 0) {
 		u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-		u32 conf = exec_clkcmp(priv, head, 0, pclk, &outp);
+		u32 conf = exec_clkcmp(priv, head, 0xff, pclk, &outp);
 		if (conf != ~0) {
+			if (outp.location == 0 && outp.type == DCB_OUTPUT_DP) {
+				u32 soff = (ffs(outp.or) - 1) * 0x08;
+				u32 ctrl = nv_rd32(priv, 0x610798 + soff);
+				u32 datarate;
+
+				switch ((ctrl & 0x000f0000) >> 16) {
+				case 6: datarate = pclk * 30 / 8; break;
+				case 5: datarate = pclk * 24 / 8; break;
+				case 2:
+				default:
+					datarate = pclk * 18 / 8;
+					break;
+				}
+
+				nouveau_dp_train(&priv->base, priv->sor.dp,
+						 &outp, head, datarate);
+			}
+
+			exec_clkcmp(priv, head, 0, pclk, &outp);
+
 			if (outp.type == DCB_OUTPUT_ANALOG) {
 				addr = 0x614280 + (ffs(outp.or) - 1) * 0x800;
 				mask = 0xffffffff;
@@ -1128,10 +1148,9 @@
 	if (head >= 0) {
 		struct dcb_output outp;
 		u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-		if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0) {
-			if (outp.type == DCB_OUTPUT_TMDS)
+		if (exec_clkcmp(priv, head, 1, pclk, &outp) != ~0)
+			if (outp.location == 0 && outp.type == DCB_OUTPUT_TMDS)
 				nv50_disp_intr_unk40_tmds(priv, &outp);
-		}
 	}
 
 	nv_wr32(priv, 0x610030, 0x80000000);